Compare commits

...

24 Commits

Author SHA1 Message Date
rakshasa f9b9629235 Merge branch 'master' into feature/ipc-worker 2026-09-15 14:40:49 +02:00
noctuum 282c86597a Fix a use after free in the xmlrpc-c index path
The string was freed before it was tested, and tmp was never released.
2026-09-14 11:32:11 +02:00
noctuum c41e63248a Check the flush and sync on session file writes
good() was checked before close(), where the data is actually flushed.
2026-09-14 11:10:12 +02:00
noctuum 641626029a Fix a one byte write past address_copy
The index bound was the buffer size, not the last writable index.
2026-09-14 10:53:12 +02:00
noctuum 0c507c581f Release the zlib deflate state in gzip_compress_to_vector.
deflateEnd was never called, leaking 168 kB per compressed response.
2026-09-13 10:43:16 +02:00
noctuum af0b600b54 Add unit tests for ipv4_range_parse.
Covers single addresses, explicit ranges and the cidr mask boundaries.
2026-09-13 10:05:39 +02:00
noctuum c984a69fd1 Fix cidr /32 ip filter entries covering everything above the address.
Shifting end_mask by the full type width is undefined behavior.
2026-09-13 10:05:39 +02:00
Jakob Breivik Grimstveit 20002ce937 Fix false invalid session error on empty URI
Avoid triggering 'Session data is invalid' when loading downloads with an empty URI.
2026-09-11 12:05:59 +02:00
rakshasa 3caa31113d Bumped scgi max content length to 64mb. 2026-09-06 14:50:19 +02:00
Nicolas PARLANT 0373d227c1 add missing headers for libcxx-23
Signed-off-by: Nicolas PARLANT <nicolas.parlant@parhuet.fr>
2026-09-03 09:59:12 +02:00
rakshasa a8be7e443a Tagged release 0.16.22. 2026-09-02 09:39:57 +02:00
aeiouaeiouaeiouaeiouaeiouaeiou f327523a07 gzip.h: fix build with Clang <17
Signed-off-by: aeiouaeiouaeiouaeiouaeiouaeiou <aeioudev@outlook.com>
2026-08-30 11:18:25 +02:00
Jari Sundell 2804be2c9c Optionally sanitize download and file names by replacing slash with underscore. 2026-08-29 18:31:16 +09:00
rakshasa cc1fcbe3e5 Merge branch 'master' into feature/ipc-worker 2026-08-16 14:29:32 +02:00
rakshasa 877e5f6ed3 Stuff. 2026-08-09 22:49:49 +02:00
rakshasa ee64f8ea50 Merge branch 'master' into feature/ipc-worker 2026-08-09 13:13:20 +02:00
rakshasa bf1265d954 Stuff. 2026-08-09 10:44:29 +02:00
rakshasa 6c3c77a2b7 Stuff. 2026-08-07 11:58:33 +02:00
rakshasa 120fcb0273 Merge branch 'master' into feature/ipc-worker 2026-08-06 11:03:44 +02:00
rakshasa c36956813d Merge branch 'master' into feature/ipc-worker 2026-08-04 18:00:04 +02:00
rakshasa 05563b4c9b Stuff. 2026-07-28 10:56:25 +02:00
rakshasa a544f8ad68 Merge branch 'master' into feature/ipc-worker 2026-07-28 10:33:57 +02:00
rakshasa b35413ea71 Merge branch 'master' into feature/ipc-worker 2026-07-26 13:03:33 +02:00
rakshasa f9f19fb041 Stuff. 2026-07-26 09:14:39 +02:00
28 changed files with 398 additions and 254 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
m4_pattern_allow([PKG_CHECK_EXISTS]) m4_pattern_allow([PKG_CHECK_EXISTS])
AC_INIT([rtorrent],[0.16.21],[sundell.software@gmail.com]) AC_INIT([rtorrent],[0.16.22],[sundell.software@gmail.com])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIRS([scripts]) AC_CONFIG_MACRO_DIRS([scripts])
@@ -49,7 +49,7 @@ fi
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"]) PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
PKG_CHECK_MODULES([ZLIB], [zlib]) PKG_CHECK_MODULES([ZLIB], [zlib])
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.21]) PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.22])
AC_LANG_PUSH(C++) AC_LANG_PUSH(C++)
TORRENT_WITH_XMLRPC_C TORRENT_WITH_XMLRPC_C
+1
View File
@@ -195,6 +195,7 @@ libsub_root_a_SOURCES = \
command_tracker.cc \ command_tracker.cc \
command_scheduler.cc \ command_scheduler.cc \
command_string.cc \ command_string.cc \
command_system.cc \
command_ui.cc \ command_ui.cc \
control.cc \ control.cc \
control.h \ control.h \
+5 -17
View File
@@ -142,16 +142,6 @@ apply_d_delete_tied(core::Download* download) {
return torrent::Object(); return torrent::Object();
} }
void
apply_d_directory(core::Download* download, const std::string& name) {
if (!download->file_list()->is_multi_file())
download->set_root_directory(name);
else if (name.empty() || *name.rbegin() == '/')
download->set_root_directory(name + download->info()->name().str());
else
download->set_root_directory(name + "/" + download->info()->name().str());
}
torrent::Object torrent::Object
apply_d_connection_type(core::Download* download, const std::string& name) { apply_d_connection_type(core::Download* download, const std::string& name) {
torrent::Download::ConnectionType t = torrent::Download::ConnectionType t =
@@ -884,12 +874,11 @@ initialize_command_download() {
CMD2_DL_LIST ("d.tracker.insert", std::bind(&download_tracker_insert, std::placeholders::_1, std::placeholders::_2)); CMD2_DL_LIST ("d.tracker.insert", std::bind(&download_tracker_insert, std::placeholders::_1, std::placeholders::_2));
CMD2_DL_VALUE_V ("d.tracker.send_scrape", [](auto download, uint64_t arg) { download->tracker_controller().scrape_request(arg); }); CMD2_DL_VALUE_V ("d.tracker.send_scrape", [](auto download, uint64_t arg) { download->tracker_controller().scrape_request(arg); });
CMD2_DL ("d.directory", CMD2_ON_FL(root_dir)); CMD2_DL ("d.directory", CMD2_ON_FL(root_dir));
CMD2_DL ("d.directory.realpath.or_empty", [](auto* download, auto) { return resolve_path(download->file_list()->root_dir()); }); CMD2_DL ("d.directory.realpath.or_empty", [](auto* download, auto) { return resolve_path(download->file_list()->root_dir()); });
CMD2_DL ("d.directory.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(download->file_list()->root_dir()); }); CMD2_DL ("d.directory.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(download->file_list()->root_dir()); });
CMD2_DL_STRING_V("d.directory.set", std::bind(&apply_d_directory, std::placeholders::_1, std::placeholders::_2)); CMD2_DL_STRING_V("d.directory.set", [](auto* download, auto arg) { download->set_directory(arg); });
CMD2_DL ("d.directory_base", CMD2_ON_FL(root_dir)); CMD2_DL_STRING_V("d.directory.base.set", [](auto* download, auto arg) { download->set_base_directory(arg); });
CMD2_DL_STRING_V("d.directory_base.set", std::bind(&core::Download::set_root_directory, std::placeholders::_1, std::placeholders::_2));
CMD2_DL ("d.priority", std::bind(&core::Download::priority, std::placeholders::_1)); CMD2_DL ("d.priority", std::bind(&core::Download::priority, std::placeholders::_1));
CMD2_DL ("d.priority_str", std::bind(&retrieve_d_priority_str, std::placeholders::_1)); CMD2_DL ("d.priority_str", std::bind(&retrieve_d_priority_str, std::placeholders::_1));
@@ -950,7 +939,6 @@ initialize_command_download() {
rpc::rpc.mark_safe("d.name.or_base64"); rpc::rpc.mark_safe("d.name.or_base64");
rpc::rpc.mark_safe("d.name.or_as_binary"); rpc::rpc.mark_safe("d.name.or_as_binary");
rpc::rpc.mark_safe("d.directory"); rpc::rpc.mark_safe("d.directory");
rpc::rpc.mark_safe("d.directory_base");
rpc::rpc.mark_safe("d.creation_date"); rpc::rpc.mark_safe("d.creation_date");
rpc::rpc.mark_safe("d.load_date"); rpc::rpc.mark_safe("d.load_date");
rpc::rpc.mark_safe("d.up.rate"); rpc::rpc.mark_safe("d.up.rate");
+2
View File
@@ -20,6 +20,7 @@ void initialize_command_throttle();
void initialize_command_tracker(); void initialize_command_tracker();
void initialize_command_scheduler(); void initialize_command_scheduler();
void initialize_command_string(); void initialize_command_string();
void initialize_command_system();
void initialize_command_ui(); void initialize_command_ui();
void void
@@ -39,4 +40,5 @@ initialize_commands() {
initialize_command_tracker(); initialize_command_tracker();
initialize_command_scheduler(); initialize_command_scheduler();
initialize_command_string(); initialize_command_string();
initialize_command_system();
} }
+4 -1
View File
@@ -131,7 +131,7 @@ ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address
// copy everything up to '#' to address_copy and work from there // copy everything up to '#' to address_copy and work from there
while(address[address_start_index] != '#' && address[address_start_index] != '\r' && while(address[address_start_index] != '#' && address[address_start_index] != '\r' &&
address[address_start_index] != '\n' && address[address_start_index] != '\0' && address[address_start_index] != '\n' && address[address_start_index] != '\0' &&
address_start_index < 4096 ) { address_start_index < 4095 ) {
address_copy[address_start_index] = address[address_start_index]; address_copy[address_start_index] = address[address_start_index];
address_start_index++; address_start_index++;
@@ -206,6 +206,9 @@ ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address
if (mask_bits == 0) { if (mask_bits == 0) {
mask = 0; mask = 0;
end_mask = ~(uint32_t)0; end_mask = ~(uint32_t)0;
} else if (mask_bits == 32) {
mask = ~(uint32_t)0;
end_mask = 0;
} else { } else {
mask = (~mask) << (32-mask_bits); mask = (~mask) << (32-mask_bits);
end_mask = (~end_mask) >> mask_bits; end_mask = (~end_mask) >> mask_bits;
+3 -184
View File
@@ -1,35 +1,16 @@
#include "config.h" #include "config.h"
#include <cerrno>
#include <fcntl.h>
#include <functional>
#include <limits>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <torrent/torrent.h>
#include <torrent/data/file_manager.h>
#include <torrent/data/chunk_utils.h>
#include <torrent/runtime/runtime.h>
#include <torrent/runtime/memory_manager.h> #include <torrent/runtime/memory_manager.h>
#include <torrent/runtime/socket_manager.h>
#include <torrent/utils/chrono.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "command_helpers.h"
#include "control.h"
#include "core/download.h" #include "core/download.h"
#include "core/download_list.h"
#include "core/manager.h" #include "core/manager.h"
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
#include "rpc/lua.h"
#include "rpc/scgi.h" #include "rpc/scgi.h"
#include "session/session_manager.h" #include "session/session_manager.h"
#include "utils/file_status_cache.h"
#include "globals.h"
#include "rpc/lua.h"
#include "control.h"
#include "command_helpers.h"
typedef torrent::ChunkManager CM_t; typedef torrent::ChunkManager CM_t;
typedef torrent::FileManager FM_t; typedef torrent::FileManager FM_t;
@@ -45,49 +26,6 @@ apply_pieces_stats_total_size() {
return size; return size;
} }
torrent::Object
system_env(const torrent::Object::string_type& arg) {
if (arg.empty())
throw torrent::input_error("system.env: Missing variable name.");
char* val = getenv(arg.c_str());
return std::string(val ? val : "");
}
torrent::Object
system_hostname() {
char buffer[1024];
if (gethostname(buffer, 1023) == -1)
throw torrent::input_error("Unable to read hostname.");
// if (shorten)
// *std::find(buffer, buffer + 1023, '.') = '\0';
return std::string(buffer);
}
torrent::Object
system_get_cwd() {
char* buffer = getcwd(NULL, 0);
if (buffer == NULL)
throw torrent::input_error("Unable to read cwd.");
torrent::Object result = torrent::Object(std::string(buffer));
free(buffer);
return result;
}
torrent::Object
system_set_cwd(const torrent::Object::string_type& rawArgs) {
if (::chdir(rawArgs.c_str()) != 0)
throw torrent::input_error("Could not change current working directory.");
return torrent::Object();
}
inline torrent::Object::list_const_iterator inline torrent::Object::list_const_iterator
post_increment(torrent::Object::list_const_iterator& itr, const torrent::Object::list_const_iterator& last) { post_increment(torrent::Object::list_const_iterator& itr, const torrent::Object::list_const_iterator& last) {
if (itr == last) if (itr == last)
@@ -187,97 +125,10 @@ cmd_file_append(const torrent::Object::list_type& args) {
return torrent::Object(); return torrent::Object();
} }
uint32_t
checked_socket_value(int64_t value, const char* label) {
if (value < 0 || value > std::numeric_limits<uint32_t>::max())
throw torrent::input_error(std::string("Invalid ") + label + " value.");
return static_cast<uint32_t>(value);
}
void void
initialize_command_local() { initialize_command_local() {
core::DownloadList* dList = control->core()->download_list(); core::DownloadList* dList = control->core()->download_list();
CMD_ANY ("system.hostname", std::bind(&system_hostname));
CMD_ANY ("system.pid", std::bind(&getpid));
CMD_VAR_C_STRING("system.api_version", (int64_t)API_VERSION);
CMD_VAR_C_STRING("system.client_version", PACKAGE_VERSION);
CMD_VAR_C_STRING("system.library_version", torrent::runtime::version());
CMD_VAR_VALUE ("system.file.allocate", 0);
CMD_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
CMD_VAR_VALUE ("system.file.split_size", -1);
CMD_VAR_STRING ("system.file.split_suffix", ".part");
CMD_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size,
(utils::FileStatusCache::base_type*)control->core()->file_status_cache()));
CMD_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache()));
CMD_VAR_BOOL ("file.prioritize_toc", 0);
CMD_VAR_LIST ("file.prioritize_toc.first");
CMD_VAR_LIST ("file.prioritize_toc.last");
CMD_ANY ("system.files.advise_random", [](auto, auto) { return torrent::file_manager()->advise_random(); });
CMD_ANY_VALUE_V ("system.files.advise_random.set", [](auto, auto& value) { return torrent::file_manager()->set_advise_random(value); });
CMD_ANY ("system.files.advise_random.hashing", [](auto, auto) { return torrent::file_manager()->advise_random_hashing(); });
CMD_ANY_VALUE_V ("system.files.advise_random.hashing.set", [](auto, auto& value) { return torrent::file_manager()->set_advise_random_hashing(value); });
CMD_ANY ("system.files.session.fdatasync", [](auto, auto) { return session_thread::manager()->use_fsyncdisk(); });
CMD_ANY_VALUE_V ("system.files.session.fdatasync.set", [](auto, auto& value) { return session_thread::manager()->set_use_fsyncdisk(value); });
CMD_ANY ("system.files.close_idle", [](auto, auto) { return torrent::file_manager()->close_idle_timeout().count(); });
CMD_ANY_VALUE_V ("system.files.close_idle.set", [](auto, auto& value) { return torrent::file_manager()->set_close_idle_timeout(value * 1s); });
CMD_ANY ("system.files.opened_counter", [](auto, auto) { return torrent::file_manager()->files_opened_counter(); });
CMD_ANY ("system.files.closed_counter", [](auto, auto) { return torrent::file_manager()->files_closed_counter(); });
CMD_ANY ("system.files.failed_counter", [](auto, auto) { return torrent::file_manager()->files_failed_counter(); });
CMD_ANY_STRING ("system.env", [](auto, auto& str) { return system_env(str); });
CMD_ANY ("system.time", [](auto, auto) { return torrent::this_thread::cached_seconds().count(); });
CMD_ANY ("system.time_seconds", [](auto, auto) { return torrent::utils::cast_seconds(torrent::utils::time_since_epoch()).count(); });
CMD_ANY ("system.time_usec", [](auto, auto) { return torrent::utils::time_since_epoch().count(); });
CMD_ANY_VALUE_V ("system.umask.set", [](auto, auto& value) { return ::umask(value); });
CMD_VAR_BOOL ("system.daemon", false);
CMD_ANY_V ("system.shutdown.normal", [](auto, auto) { control->receive_normal_shutdown(); });
CMD_ANY_V ("system.shutdown.quick", [](auto, auto) { control->receive_quick_shutdown(); });
CMD_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
CMD_ANY ("system.cwd", [](auto, auto) { return system_get_cwd(); });
CMD_ANY_STRING ("system.cwd.set", [](auto, auto& str) { return system_set_cwd(str); });
CMD_ANY ("system.sockets.size", [](auto, auto) { return torrent::runtime::socket_manager()->size(); });
CMD_ANY ("system.sockets.max_size", [](auto, auto) { return torrent::runtime::socket_manager()->max_size(); });
CMD_ANY_VALUE_V ("system.sockets.max_size.set", [](auto, auto& value) { return torrent::runtime::socket_manager()->set_max_size_and_adjust(checked_socket_value(value, "socket max size")); });
CMD_ANY_V ("system.sockets.adjust_alloc", [](auto, auto) { torrent::runtime::socket_manager()->adjust_allocation(); });
CMD_ANY ("system.sockets.reserved_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->reserved_allocation(); });
CMD_ANY ("system.sockets.available_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->available_allocation(); });
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
auto category = static_cast<torrent::runtime::socket_manager_category_t>(i);
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
CMD_ANY (category_name + ".size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_managed_size(category); });
CMD_ANY (category_name + ".max_size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_size(category); });
if (i == 0) {
CMD_ANY (category_name + ".min_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->generic_min_allocation(); });
continue;
}
CMD_ANY (category_name + ".max_alloc.limit", [category](auto, auto) { return torrent::runtime::socket_manager()->category_alloc_limit(category); });
CMD_ANY (category_name + ".min_alloc.limit", [category](auto, auto) { return torrent::runtime::socket_manager()->category_alloc_minimum(category); });
CMD_ANY (category_name + ".min_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_min_allocation(category); });
CMD_ANY (category_name + ".max_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_allocation(category); });
CMD_ANY_VALUE_V(category_name + ".min_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_min_allocation(category, checked_socket_value(value, "socket min alloc")); });
CMD_ANY_VALUE_V(category_name + ".max_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_max_allocation(category, checked_socket_value(value, "socket max alloc")); });
}
CMD_ANY ("pieces.sync.always_safe", [](auto, auto) { return torrent::runtime::memory_manager()->safe_sync(); }); CMD_ANY ("pieces.sync.always_safe", [](auto, auto) { return torrent::runtime::memory_manager()->safe_sync(); });
CMD_ANY_VALUE_V ("pieces.sync.always_safe.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_safe_sync(value); }); CMD_ANY_VALUE_V ("pieces.sync.always_safe.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_safe_sync(value); });
CMD_ANY ("pieces.sync.safe_free_diskspace", [](auto, auto) { return torrent::runtime::memory_manager()->sync_safe_free_diskspace(); }); CMD_ANY ("pieces.sync.safe_free_diskspace", [](auto, auto) { return torrent::runtime::memory_manager()->sync_safe_free_diskspace(); });
@@ -361,38 +212,6 @@ initialize_command_local() {
CMD_ANY_LIST ("group.insert", std::bind(&group_insert, std::placeholders::_2)); CMD_ANY_LIST ("group.insert", std::bind(&group_insert, std::placeholders::_2));
rpc::rpc.mark_safe("system.api_version");
rpc::rpc.mark_safe("system.client_version");
rpc::rpc.mark_safe("system.library_version");
rpc::rpc.mark_safe("system.time");
rpc::rpc.mark_safe("system.time_seconds");
rpc::rpc.mark_safe("system.time_usec");
rpc::rpc.mark_safe("system.file.max_size");
rpc::rpc.mark_safe("system.file.split_size");
rpc::rpc.mark_safe("system.file.split_suffix");
rpc::rpc.mark_safe("system.sockets.size");
rpc::rpc.mark_safe("system.sockets.max_size");
rpc::rpc.mark_safe("system.sockets.reserved_alloc");
rpc::rpc.mark_safe("system.sockets.available_alloc");
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
rpc::rpc.mark_safe(category_name + ".size");
rpc::rpc.mark_safe(category_name + ".max_size");
if (i == 0) {
rpc::rpc.mark_safe(category_name + ".min_alloc");
continue;
}
rpc::rpc.mark_safe(category_name + ".max_alloc.limit");
rpc::rpc.mark_safe(category_name + ".min_alloc.limit");
rpc::rpc.mark_safe(category_name + ".min_alloc");
rpc::rpc.mark_safe(category_name + ".max_alloc");
}
rpc::rpc.mark_safe("directory.default"); rpc::rpc.mark_safe("directory.default");
rpc::rpc.mark_safe("session.path"); rpc::rpc.mark_safe("session.path");
rpc::rpc.mark_safe("session.path.realpath.or_empty"); rpc::rpc.mark_safe("session.path.realpath.or_empty");
+195
View File
@@ -0,0 +1,195 @@
#include "config.h"
#include <unistd.h>
#include <sys/stat.h>
#include <torrent/data/file_manager.h>
#include <torrent/runtime/client_config.h>
#include <torrent/runtime/memory_manager.h>
#include <torrent/runtime/socket_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/utils/chrono.h>
#include <torrent/utils/option_strings.h>
#include "command_helpers.h"
#include "control.h"
#include "globals.h"
#include "core/manager.h"
#include "session/session_manager.h"
#include "utils/file_status_cache.h"
torrent::Object
system_env(const torrent::Object::string_type& arg) {
if (arg.empty())
throw torrent::input_error("system.env: Missing variable name.");
char* val = getenv(arg.c_str());
return std::string(val ? val : "");
}
torrent::Object
system_hostname() {
char buffer[1024];
if (gethostname(buffer, 1023) == -1)
throw torrent::input_error("Unable to read hostname.");
// if (shorten)
// *std::find(buffer, buffer + 1023, '.') = '\0';
return std::string(buffer);
}
torrent::Object
system_get_cwd() {
char* buffer = getcwd(NULL, 0);
if (buffer == NULL)
throw torrent::input_error("Unable to read cwd.");
torrent::Object result = torrent::Object(std::string(buffer));
free(buffer);
return result;
}
torrent::Object
system_set_cwd(const torrent::Object::string_type& rawArgs) {
if (::chdir(rawArgs.c_str()) != 0)
throw torrent::input_error("Could not change current working directory.");
return torrent::Object();
}
uint32_t
checked_socket_value(int64_t value, const char* label) {
if (value < 0 || value > std::numeric_limits<uint32_t>::max())
throw torrent::input_error(std::string("Invalid ") + label + " value.");
return static_cast<uint32_t>(value);
}
void
initialize_command_system() {
CMD_ANY ("system.hostname", std::bind(&system_hostname));
CMD_ANY ("system.pid", std::bind(&getpid));
CMD_VAR_C_STRING("system.api_version", (int64_t)API_VERSION);
CMD_VAR_C_STRING("system.client_version", PACKAGE_VERSION);
CMD_VAR_C_STRING("system.library_version", torrent::runtime::version());
CMD_ANY ("system.torrent_name.use_sanitized", [](auto, auto) { return torrent::runtime::client_config()->torrent_name_use_sanitized(); });
CMD_ANY_VALUE_V ("system.torrent_name.use_sanitized.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_torrent_name_use_sanitized(value); });
CMD_VAR_VALUE ("system.file.allocate", 0);
CMD_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
CMD_VAR_VALUE ("system.file.split_size", -1);
CMD_VAR_STRING ("system.file.split_suffix", ".part");
CMD_ANY ("system.file_name.replace_slash", [](auto, auto) { return torrent::runtime::client_config()->file_name_replace_slash(); });
CMD_ANY_STRING_V("system.file_name.replace_slash.set", [](auto, auto& str) { return torrent::runtime::client_config()->set_file_name_replace_slash(str); });
CMD_ANY ("system.file_status_cache.size", [](auto, auto) { return control->core()->file_status_cache()->size(); });
CMD_ANY_V ("system.file_status_cache.prune", [](auto, auto) { return control->core()->file_status_cache()->prune(); });
CMD_VAR_BOOL ("file.prioritize_toc", 0);
CMD_VAR_LIST ("file.prioritize_toc.first");
CMD_VAR_LIST ("file.prioritize_toc.last");
CMD_ANY ("system.files.advise_random", [](auto, auto) { return torrent::file_manager()->advise_random(); });
CMD_ANY_VALUE_V ("system.files.advise_random.set", [](auto, auto& value) { return torrent::file_manager()->set_advise_random(value); });
CMD_ANY ("system.files.advise_random.hashing", [](auto, auto) { return torrent::file_manager()->advise_random_hashing(); });
CMD_ANY_VALUE_V ("system.files.advise_random.hashing.set", [](auto, auto& value) { return torrent::file_manager()->set_advise_random_hashing(value); });
CMD_ANY ("system.files.session.fdatasync", [](auto, auto) { return session_thread::manager()->use_fsyncdisk(); });
CMD_ANY_VALUE_V ("system.files.session.fdatasync.set", [](auto, auto& value) { return session_thread::manager()->set_use_fsyncdisk(value); });
CMD_ANY ("system.files.close_idle", [](auto, auto) { return torrent::file_manager()->close_idle_timeout().count(); });
CMD_ANY_VALUE_V ("system.files.close_idle.set", [](auto, auto& value) { return torrent::file_manager()->set_close_idle_timeout(value * 1s); });
CMD_ANY ("system.files.opened_counter", [](auto, auto) { return torrent::file_manager()->files_opened_counter(); });
CMD_ANY ("system.files.closed_counter", [](auto, auto) { return torrent::file_manager()->files_closed_counter(); });
CMD_ANY ("system.files.failed_counter", [](auto, auto) { return torrent::file_manager()->files_failed_counter(); });
CMD_ANY_STRING ("system.env", [](auto, auto& str) { return system_env(str); });
CMD_ANY ("system.time", [](auto, auto) { return torrent::this_thread::cached_seconds().count(); });
CMD_ANY ("system.time_seconds", [](auto, auto) { return torrent::utils::cast_seconds(torrent::utils::time_since_epoch()).count(); });
CMD_ANY ("system.time_usec", [](auto, auto) { return torrent::utils::time_since_epoch().count(); });
CMD_ANY_VALUE_V ("system.umask.set", [](auto, auto& value) { return ::umask(value); });
CMD_VAR_BOOL ("system.daemon", false);
CMD_ANY_V ("system.shutdown.normal", [](auto, auto) { control->receive_normal_shutdown(); });
CMD_ANY_V ("system.shutdown.quick", [](auto, auto) { control->receive_quick_shutdown(); });
CMD_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
CMD_ANY ("system.cwd", [](auto, auto) { return system_get_cwd(); });
CMD_ANY_STRING ("system.cwd.set", [](auto, auto& str) { return system_set_cwd(str); });
CMD_ANY ("system.sockets.size", [](auto, auto) { return torrent::runtime::socket_manager()->size(); });
CMD_ANY ("system.sockets.max_size", [](auto, auto) { return torrent::runtime::socket_manager()->max_size(); });
CMD_ANY_VALUE_V ("system.sockets.max_size.set", [](auto, auto& value) {
return torrent::runtime::socket_manager()->set_max_size_and_adjust(checked_socket_value(value, "socket max size"));
});
CMD_ANY_V ("system.sockets.adjust_alloc", [](auto, auto) { torrent::runtime::socket_manager()->adjust_allocation(); });
CMD_ANY ("system.sockets.reserved_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->reserved_allocation(); });
CMD_ANY ("system.sockets.available_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->available_allocation(); });
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
auto category = static_cast<torrent::runtime::socket_manager_category_t>(i);
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
CMD_ANY (category_name + ".size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_managed_size(category); });
CMD_ANY (category_name + ".max_size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_size(category); });
if (i == 0) {
CMD_ANY (category_name + ".min_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->generic_min_allocation(); });
continue;
}
CMD_ANY (category_name + ".max_alloc.limit", [category](auto, auto) { return torrent::runtime::socket_manager()->category_alloc_limit(category); });
CMD_ANY (category_name + ".min_alloc.limit", [category](auto, auto) { return torrent::runtime::socket_manager()->category_alloc_minimum(category); });
CMD_ANY (category_name + ".min_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_min_allocation(category); });
CMD_ANY (category_name + ".max_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_allocation(category); });
CMD_ANY_VALUE_V(category_name + ".min_alloc.set", [category](auto, auto& value) {
torrent::runtime::socket_manager()->set_category_min_allocation(category, checked_socket_value(value, "socket min alloc"));
});
CMD_ANY_VALUE_V(category_name + ".max_alloc.set", [category](auto, auto& value) {
torrent::runtime::socket_manager()->set_category_max_allocation(category, checked_socket_value(value, "socket max alloc"));
});
}
rpc::rpc.mark_safe("system.api_version");
rpc::rpc.mark_safe("system.client_version");
rpc::rpc.mark_safe("system.library_version");
rpc::rpc.mark_safe("system.time");
rpc::rpc.mark_safe("system.time_seconds");
rpc::rpc.mark_safe("system.time_usec");
rpc::rpc.mark_safe("system.file.max_size");
rpc::rpc.mark_safe("system.file.split_size");
rpc::rpc.mark_safe("system.file.split_suffix");
rpc::rpc.mark_safe("system.sockets.size");
rpc::rpc.mark_safe("system.sockets.max_size");
rpc::rpc.mark_safe("system.sockets.reserved_alloc");
rpc::rpc.mark_safe("system.sockets.available_alloc");
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
rpc::rpc.mark_safe(category_name + ".size");
rpc::rpc.mark_safe(category_name + ".max_size");
if (i == 0) {
rpc::rpc.mark_safe(category_name + ".min_alloc");
continue;
}
rpc::rpc.mark_safe(category_name + ".max_alloc.limit");
rpc::rpc.mark_safe(category_name + ".min_alloc.limit");
rpc::rpc.mark_safe(category_name + ".min_alloc");
rpc::rpc.mark_safe(category_name + ".max_alloc");
}
}
+2
View File
@@ -118,4 +118,6 @@ private:
std::atomic<bool> m_shutdown_quick{}; std::atomic<bool> m_shutdown_quick{};
}; };
extern Control* control;
#endif #endif
+12 -1
View File
@@ -101,7 +101,18 @@ Download::set_throttle_name(const std::string& name) {
} }
void void
Download::set_root_directory(const std::string& path) { Download::set_directory(const std::string& path) {
if (!m_download.file_list()->is_multi_file())
return set_base_directory(path);
if (path.empty() || *path.rbegin() == '/')
return set_base_directory(path + m_download.info()->name_sanitized());
set_base_directory(path + "/" + m_download.info()->name_sanitized());
}
void
Download::set_base_directory(const std::string& path) {
// If the download is open, hashed and has completed chunks make // If the download is open, hashed and has completed chunks make
// sure to verify that the download files are still present. // sure to verify that the download files are still present.
// //
+2 -1
View File
@@ -79,7 +79,8 @@ public:
uint32_t resume_flags() { return m_resumeFlags; } uint32_t resume_flags() { return m_resumeFlags; }
void set_resume_flags(uint32_t flags) { m_resumeFlags = flags; } void set_resume_flags(uint32_t flags) { m_resumeFlags = flags; }
void set_root_directory(const std::string& path); void set_directory(const std::string& path);
void set_base_directory(const std::string& path);
void set_throttle_name(const std::string& name); void set_throttle_name(const std::string& name);
+18 -4
View File
@@ -7,6 +7,7 @@
#include <functional> #include <functional>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <sys/stat.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/utils/resume.h> #include <torrent/utils/resume.h>
#include <torrent/object.h> #include <torrent/object.h>
@@ -39,6 +40,11 @@ static constexpr const char* session_invalid_message = "Session data is invalid,
static std::unique_ptr<torrent::Object> static std::unique_ptr<torrent::Object>
download_factory_load_stream(const char* filename, bool* is_invalid) { download_factory_load_stream(const char* filename, bool* is_invalid) {
struct stat sb;
if (stat(filename, &sb) != 0 || !S_ISREG(sb.st_mode))
return std::unique_ptr<torrent::Object>();
std::fstream stream(filename, std::ios::in | std::ios::binary); std::fstream stream(filename, std::ios::in | std::ios::binary);
if (!stream.is_open()) if (!stream.is_open())
@@ -167,11 +173,19 @@ void
DownloadFactory::receive_success() { DownloadFactory::receive_success() {
bool session_invalid = false; bool session_invalid = false;
auto rtorrent_object = download_factory_load_stream((expand_path(m_uri) + ".rtorrent").c_str(), &session_invalid); std::unique_ptr<torrent::Object> rtorrent_object;
auto libtorrent_resume_object = download_factory_load_stream((expand_path(m_uri) + ".libtorrent_resume").c_str(), &session_invalid); std::unique_ptr<torrent::Object> libtorrent_resume_object;
if (session_invalid) if (m_session) {
lt_log_print(torrent::LOG_ERROR, "%s: %s", session_invalid_message, m_uri.c_str()); if (m_uri.empty())
throw torrent::input_error("Session torrent URI is empty.");
rtorrent_object = download_factory_load_stream((expand_path(m_uri) + ".rtorrent").c_str(), &session_invalid);
libtorrent_resume_object = download_factory_load_stream((expand_path(m_uri) + ".libtorrent_resume").c_str(), &session_invalid);
if (session_invalid)
lt_log_print(torrent::LOG_ERROR, "%s: %s", session_invalid_message, m_uri.c_str());
}
uint32_t tracker_key; uint32_t tracker_key;
+1 -2
View File
@@ -11,8 +11,7 @@ namespace input {
void void
InputEvent::insert() { InputEvent::insert() {
torrent::this_thread::poll()->open(this); torrent::this_thread::poll()->open_and_insert_read(this);
torrent::this_thread::poll()->insert_read(this);
} }
void void
+22 -15
View File
@@ -112,8 +112,9 @@ main(int argc, char** argv) {
torrent::log_initialize(); torrent::log_initialize();
// TODO: Create a fake thread object for initializing other processes and enabling logging. torrent::runtime::initialize_worker_process_and_main_thread([argc, argv]() {
torrent::initialize_main_thread(); parse_config_file(argc, argv, [](auto& path) { parse_config_file_comments("worker", path); });
});
// Block SIGCHLD until all threads are created, then unblock on main-thread, to avoid SIGCHLD // Block SIGCHLD until all threads are created, then unblock on main-thread, to avoid SIGCHLD
// interrupting other threads. // interrupting other threads.
@@ -125,9 +126,9 @@ main(int argc, char** argv) {
SignalHandler::set_block(SIGCHLD); SignalHandler::set_block(SIGCHLD);
// All signal handlers must restore errno if they return. // All signal handlers must restore errno if they return.
SignalHandler::set_handler(SIGSEGV, std::bind(&do_panic, SIGSEGV)); SignalHandler::set_handler(SIGSEGV, []() { do_panic(SIGSEGV); });
SignalHandler::set_handler(SIGILL, std::bind(&do_panic, SIGILL)); SignalHandler::set_handler(SIGILL, []() { do_panic(SIGILL); });
SignalHandler::set_handler(SIGFPE, std::bind(&do_panic, SIGFPE)); SignalHandler::set_handler(SIGFPE, []() { do_panic(SIGFPE); });
// Limited list of commands with the following format: // Limited list of commands with the following format:
// //
@@ -136,19 +137,17 @@ main(int argc, char** argv) {
// # do:log.open_file=system,/usr/rakshasa/system.log // # do:log.open_file=system,/usr/rakshasa/system.log
// # do:log.add_output=system,system // # do:log.add_output=system,system
// //
parse_config_file(argc, argv, [](auto& path) { // # do-worker:log.open_file=system,/usr/rakshasa/system-worker.log
if (path.empty()) // # do-worker:log.add_output=system,system
return; //
parse_config_file(argc, argv, [](auto& path) { parse_config_file_comments("", path); });
parse_config_file_comments(path);
});
control = new Control; control = new Control;
SignalHandler::set_handler(SIGINT, std::bind(&Control::receive_normal_shutdown, control)); SignalHandler::set_handler(SIGINT, []() { control->receive_normal_shutdown(); });
SignalHandler::set_handler(SIGHUP, std::bind(&Control::receive_normal_shutdown, control)); SignalHandler::set_handler(SIGHUP, []() { control->receive_normal_shutdown(); });
SignalHandler::set_handler(SIGTERM, std::bind(&Control::receive_quick_shutdown, control)); SignalHandler::set_handler(SIGTERM, []() { control->receive_quick_shutdown(); });
SignalHandler::set_handler(SIGWINCH, std::bind(&display::Manager::force_redraw, control->display())); SignalHandler::set_handler(SIGWINCH, []() { control->display()->force_redraw(); });
SignalHandler::set_sigaction_handler(SIGBUS, &handle_sigbus); SignalHandler::set_sigaction_handler(SIGBUS, &handle_sigbus);
@@ -379,6 +378,12 @@ main(int argc, char** argv) {
lt_log_print(torrent::LOG_WARN, "network.max_open_files.set is deprecated, use system.sockets.files.min_alloc.set instead."); lt_log_print(torrent::LOG_WARN, "network.max_open_files.set is deprecated, use system.sockets.files.min_alloc.set instead.");
}); });
// TODO: Keep d.directory_base for a while as it is widely used.
CMD_REDIRECT("d.directory_base", "d.directory");
CMD_REDIRECT("d.directory_base.set", "d.directory.base.set");
rpc::rpc.mark_safe("d.directory_base");
// if (rpc::call_command_value("method.use_intermediate") == 1) { // if (rpc::call_command_value("method.use_intermediate") == 1) {
// } else if (rpc::call_command_value("method.use_intermediate") == 2) { // } else if (rpc::call_command_value("method.use_intermediate") == 2) {
@@ -463,6 +468,8 @@ main(int argc, char** argv) {
control->ui()->load_input_history(); control->ui()->load_input_history();
torrent::net_thread::http_stack()->set_user_agent(USER_AGENT); torrent::net_thread::http_stack()->set_user_agent(USER_AGENT);
torrent::runtime::initialize_worker_process();
torrent::runtime::initialize_network(); torrent::runtime::initialize_network();
// Load session torrents and perform scheduled tasks to ensure session torrents are loaded // Load session torrents and perform scheduled tasks to ensure session torrents are loaded
+1 -1
View File
@@ -108,6 +108,6 @@ OptionParser::call_int_pair(slot_int_pair slot, const std::string& arg) {
if (a < 0 || b < 0) if (a < 0 || b < 0)
throw std::runtime_error("Invalid argument, \"" + arg + "\" should be positive numbers"); throw std::runtime_error("Invalid argument, \"" + arg + "\" should be positive numbers");
slot(a, b); slot(a, b);
} }
+1 -2
View File
@@ -109,8 +109,7 @@ void
SCgi::activate() { SCgi::activate() {
assert(torrent::this_thread::thread() == scgi_thread::thread()); assert(torrent::this_thread::thread() == scgi_thread::thread());
torrent::this_thread::poll()->open(this); torrent::this_thread::poll()->open_and_insert_read(this);
torrent::this_thread::poll()->insert_read(this);
} }
// TODO: This should close the fd to avoid reuse. // TODO: This should close the fd to avoid reuse.
+1 -2
View File
@@ -50,8 +50,7 @@ SCgiTask::open(SCgi* parent, int fd) {
// m_trusted=false into the next reuse, given that the // m_trusted=false into the next reuse, given that the
// UNTRUSTED_CONNECTION=0 parse branch is a no-op. // UNTRUSTED_CONNECTION=0 parse branch is a no-op.
torrent::this_thread::poll()->open(this); torrent::this_thread::poll()->open_and_insert_read(this);
torrent::this_thread::poll()->insert_read(this);
torrent::this_thread::scheduler()->update_wait_for_ceil_seconds(&m_task_timeout, timeout_request); torrent::this_thread::scheduler()->update_wait_for_ceil_seconds(&m_task_timeout, timeout_request);
+1 -1
View File
@@ -16,7 +16,7 @@ class SCgiTask : public torrent::system::Event {
public: public:
static constexpr int default_buffer_size = 8191; static constexpr int default_buffer_size = 8191;
static constexpr int max_header_size = 2000; static constexpr int max_header_size = 2000;
static constexpr int max_content_size = (2 << 23); static constexpr int max_content_size = (1 << 26);
static constexpr auto timeout_request = std::chrono::seconds(60); static constexpr auto timeout_request = std::chrono::seconds(60);
+3 -1
View File
@@ -77,16 +77,18 @@ xmlrpc_list_entry_to_value(xmlrpc_env* env, xmlrpc_value* src, int index) {
{ {
const char* str; const char* str;
xmlrpc_read_string(env, tmp, &str); xmlrpc_read_string(env, tmp, &str);
xmlrpc_DECREF(tmp);
if (env->fault_occurred) if (env->fault_occurred)
throw xmlrpc_error_c(env); throw xmlrpc_error_c(env);
const char* end = str; const char* end = str;
int64_t v3 = ::strtoll(str, (char**)&end, 0); int64_t v3 = ::strtoll(str, (char**)&end, 0);
bool invalid = *str == '\0' || *end != '\0';
::free((void*)str); ::free((void*)str);
if (*str == '\0' || *end != '\0') if (invalid)
throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Invalid index."); throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Invalid index.");
return v3; return v3;
+13 -3
View File
@@ -127,8 +127,12 @@ save_stream(const std::string& path, bool use_fsyncdisk, const std::stringstream
if (!output.good()) if (!output.good())
throw torrent::storage_error("failed to write stream to file : " + path); throw torrent::storage_error("failed to write stream to file : " + path);
// The data only reaches the kernel here, so this is where a full disk is seen.
output.close(); output.close();
if (!output.good())
throw torrent::storage_error("failed to flush stream to file : " + path);
// Ensure that the new file is actually written to the disk // Ensure that the new file is actually written to the disk
int fd = ::open(path.c_str(), O_WRONLY); int fd = ::open(path.c_str(), O_WRONLY);
@@ -137,13 +141,19 @@ save_stream(const std::string& path, bool use_fsyncdisk, const std::stringstream
if (use_fsyncdisk) { if (use_fsyncdisk) {
#ifdef __APPLE__ #ifdef __APPLE__
::fsync(fd); int sync_result = ::fsync(fd);
#else #else
::fdatasync(fd); int sync_result = ::fdatasync(fd);
#endif #endif
if (sync_result == -1) {
::close(fd);
throw torrent::storage_error("failed to sync file to disk : " + path);
}
} }
::close(fd); if (::close(fd) == -1)
throw torrent::storage_error("failed to close file descriptor : " + path);
} }
} // namespace anonymous } // namespace anonymous
+9 -5
View File
@@ -108,18 +108,22 @@ config_comment_log(const std::string& command, const std::string& raw_args) {
throw torrent::input_error("Unknown log command: " + command); throw torrent::input_error("Unknown log command: " + command);
} }
// Call special commands in the format "# do:command=args" in the config file. // Call special commands in the format "# do:command=args" or "# do-<category>:command=args" in the config file.
void void
parse_config_file_comments(const std::string& path) { parse_config_file_comments(const std::string& category, const std::string& path) {
if (path.empty())
return;
std::fstream file(path, std::ios::in); std::fstream file(path, std::ios::in);
if (!file.is_open()) if (!file.is_open())
return; return;
std::string line; std::string line;
std::string prefix = category.empty() ? "# do:" : "# do-" + category + ":";
while (std::getline(file, line)) { while (std::getline(file, line)) {
if (line.size() <= 5 || line.compare(0, 5, "# do:") != 0) if (line.size() <= prefix.size() || line.compare(0, prefix.size(), prefix) != 0)
continue; continue;
auto equal_pos = line.find('='); auto equal_pos = line.find('=');
@@ -127,8 +131,8 @@ parse_config_file_comments(const std::string& path) {
if (equal_pos == std::string::npos) if (equal_pos == std::string::npos)
throw torrent::input_error("Invalid command in config file comment: " + line); throw torrent::input_error("Invalid command in config file comment: " + line);
std::string command = line.substr(5, equal_pos - 5); auto command = line.substr(prefix.size(), equal_pos - prefix.size());
std::string args = line.substr(equal_pos + 1); auto args = line.substr(equal_pos + 1);
if (command.empty()) if (command.empty())
throw torrent::input_error("Invalid command in config file comment: " + line); throw torrent::input_error("Invalid command in config file comment: " + line);
+2 -1
View File
@@ -3,10 +3,11 @@
#include <functional> #include <functional>
#include <string> #include <string>
#include <vector>
int parse_main_options(int argc, char** argv); int parse_main_options(int argc, char** argv);
void parse_config_file(int argc, char** argv, std::function<void (const std::string&)> parse_fn); void parse_config_file(int argc, char** argv, std::function<void (const std::string&)> parse_fn);
void parse_config_file_comments(const std::string& path); void parse_config_file_comments(const std::string& category, const std::string& path);
void load_session_torrents(const std::string& path); void load_session_torrents(const std::string& path);
void load_arg_torrents(char** first, char** last); void load_arg_torrents(char** first, char** last);
+4
View File
@@ -5,6 +5,8 @@
#include <zlib.h> #include <zlib.h>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include "utils/functional.h"
namespace utils { namespace utils {
void void
@@ -21,6 +23,8 @@ gzip_compress_to_vector(const char* buffer, unsigned int length, std::vector<cha
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, window_bits | gzip_encoding, gzip_level, Z_DEFAULT_STRATEGY) != Z_OK) if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, window_bits | gzip_encoding, gzip_level, Z_DEFAULT_STRATEGY) != Z_OK)
throw torrent::internal_error("gzip_compress_to_vector(...) could not initialize gzip deflate."); throw torrent::internal_error("gzip_compress_to_vector(...) could not initialize gzip deflate.");
scope_guard guard([&zs]() { deflateEnd(&zs); });
auto max_response_size = deflateBound(&zs, length); auto max_response_size = deflateBound(&zs, length);
output.resize(offset + max_response_size); output.resize(offset + max_response_size);
+1
View File
@@ -2,6 +2,7 @@
#define RTORRENT_UTILS_GZIP_H #define RTORRENT_UTILS_GZIP_H
#include <functional> #include <functional>
#include <vector>
namespace utils { namespace utils {
+4 -2
View File
@@ -51,8 +51,10 @@ rtorrent_Test_Rpc_SOURCES = $(rtorrent_Test_Common) \
rtorrent_Test_Src_SOURCES = $(rtorrent_Test_Common) \ rtorrent_Test_Src_SOURCES = $(rtorrent_Test_Common) \
src/test_command_dynamic.cc \ src/test_command_dynamic.cc \
src/test_command_dynamic.h \ src/test_command_dynamic.h \
src/test_command_local.cc \ src/test_command_ip.cc \
src/test_command_local.h \ src/test_command_ip.h \
src/test_command_system.cc \
src/test_command_system.h \
src/test_command_path.cc \ src/test_command_path.cc \
src/test_command_path.h \ src/test_command_path.h \
src/test_command_string.cc \ src/test_command_string.cc \
+55
View File
@@ -0,0 +1,55 @@
#include "config.h"
#include "test/src/test_command_ip.h"
#include <cstdint>
CPPUNIT_TEST_SUITE_REGISTRATION(TestCommandIp);
bool ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address_end);
static uint32_t
ipv4(uint32_t a, uint32_t b, uint32_t c, uint32_t d) {
return (a << 24) | (b << 16) | (c << 8) | d;
}
#define RANGE_ASSERT(address, expected_start, expected_end) \
{ \
uint32_t start = 0; \
uint32_t end = 0; \
\
CPPUNIT_ASSERT(ipv4_range_parse(address, &start, &end)); \
CPPUNIT_ASSERT_EQUAL(expected_start, start); \
CPPUNIT_ASSERT_EQUAL(expected_end, end); \
}
void
TestCommandIp::test_single_address() {
RANGE_ASSERT("10.1.2.3", ipv4(10, 1, 2, 3), ipv4(10, 1, 2, 3));
}
void
TestCommandIp::test_explicit_range() {
RANGE_ASSERT("10.1.2.3-10.1.2.9", ipv4(10, 1, 2, 3), ipv4(10, 1, 2, 9));
}
void
TestCommandIp::test_cidr() {
RANGE_ASSERT("10.0.0.0/8", ipv4(10, 0, 0, 0), ipv4(10, 255, 255, 255));
RANGE_ASSERT("10.1.2.0/24", ipv4(10, 1, 2, 0), ipv4(10, 1, 2, 255));
RANGE_ASSERT("10.1.2.128/25", ipv4(10, 1, 2, 128), ipv4(10, 1, 2, 255));
RANGE_ASSERT("10.1.2.3/31", ipv4(10, 1, 2, 2), ipv4(10, 1, 2, 3));
}
void
TestCommandIp::test_cidr_zero_mask() {
RANGE_ASSERT("0.0.0.0/0", ipv4(0, 0, 0, 0), ipv4(255, 255, 255, 255));
RANGE_ASSERT("10.1.2.3/0", ipv4(0, 0, 0, 0), ipv4(255, 255, 255, 255));
}
void
TestCommandIp::test_cidr_full_mask() {
RANGE_ASSERT("10.1.2.3/32", ipv4(10, 1, 2, 3), ipv4(10, 1, 2, 3));
RANGE_ASSERT("0.0.0.0/32", ipv4(0, 0, 0, 0), ipv4(0, 0, 0, 0));
RANGE_ASSERT("255.255.255.255/32", ipv4(255, 255, 255, 255), ipv4(255, 255, 255, 255));
}
+25
View File
@@ -0,0 +1,25 @@
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
// ipv4_range_parse is a pure function, so this does not use test_fixture and
// the mock and logging setup that comes with it.
class TestCommandIp : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(TestCommandIp);
CPPUNIT_TEST(test_single_address);
CPPUNIT_TEST(test_explicit_range);
CPPUNIT_TEST(test_cidr);
CPPUNIT_TEST(test_cidr_zero_mask);
CPPUNIT_TEST(test_cidr_full_mask);
CPPUNIT_TEST_SUITE_END();
public:
void test_single_address();
void test_explicit_range();
void test_cidr();
void test_cidr_zero_mask();
void test_cidr_full_mask();
};
@@ -1,6 +1,6 @@
#include "config.h" #include "config.h"
#include "test/src/test_command_local.h" #include "test/src/test_command_system.h"
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/runtime/socket_manager.h> #include <torrent/runtime/socket_manager.h>
@@ -10,12 +10,12 @@
#include "globals.h" #include "globals.h"
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
CPPUNIT_TEST_SUITE_REGISTRATION(TestCommandLocal); CPPUNIT_TEST_SUITE_REGISTRATION(TestCommandSystem);
void initialize_command_local(); void initialize_command_system();
void void
TestCommandLocal::setUp() { TestCommandSystem::setUp() {
torrent::initialize_main_thread(); torrent::initialize_main_thread();
torrent::initialize(); torrent::initialize();
@@ -23,16 +23,16 @@ TestCommandLocal::setUp() {
control = new Control; control = new Control;
if (!rpc::commands.has("system.sockets.size")) if (!rpc::commands.has("system.sockets.size"))
initialize_command_local(); initialize_command_system();
} }
void void
TestCommandLocal::tearDown() { TestCommandSystem::tearDown() {
torrent::cleanup(); torrent::cleanup();
} }
void void
TestCommandLocal::test_socket_category_commands() { TestCommandSystem::test_socket_category_commands() {
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) { for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
auto category = static_cast<torrent::runtime::socket_manager_category_t>(i); auto category = static_cast<torrent::runtime::socket_manager_category_t>(i);
auto name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i); auto name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
@@ -1,7 +1,7 @@
#include "test/helpers/test_fixture.h" #include "test/helpers/test_fixture.h"
class TestCommandLocal : public test_fixture { class TestCommandSystem : public test_fixture {
CPPUNIT_TEST_SUITE(TestCommandLocal); CPPUNIT_TEST_SUITE(TestCommandSystem);
CPPUNIT_TEST(test_socket_category_commands); CPPUNIT_TEST(test_socket_category_commands);