Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rtorrent for openSUSE:Factory checked in at 2026-07-01 16:38:30 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rtorrent (Old) and /work/SRC/openSUSE:Factory/.rtorrent.new.11887 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rtorrent" Wed Jul 1 16:38:30 2026 rev:34 rq:1362739 version:0.16.15 Changes: -------- --- /work/SRC/openSUSE:Factory/rtorrent/rtorrent.changes 2026-06-15 19:45:47.653323772 +0200 +++ /work/SRC/openSUSE:Factory/.rtorrent.new.11887/rtorrent.changes 2026-07-01 16:39:10.406356070 +0200 @@ -1,0 +2,15 @@ +Tue Jun 30 21:07:56 UTC 2026 - Jan Engelhardt <[email protected]> + +- Update to release 0.16.15 + * Deprecated commands: network.open_sockets, + network.max_open_sockets, network.max_open_sockets.set, + network.http.max_total_connections.set, + network.max_open_files.set + * Global network socket commands (e.g., + network.max_open_sockets) are now deprecated. Socket + allocation is managed granularly via Socket Categories: + generic, http, internal, rpc, and files. + (e.g. new commands: system.sockets.<category>.size, .max_size, + .min_alloc/.max_alloc) + +------------------------------------------------------------------- Old: ---- rtorrent-0.16.14.tar.gz New: ---- rtorrent-0.16.15.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rtorrent.spec ++++++ --- /var/tmp/diff_new_pack.cqdYWD/_old 2026-07-01 16:39:11.154382106 +0200 +++ /var/tmp/diff_new_pack.cqdYWD/_new 2026-07-01 16:39:11.158382245 +0200 @@ -17,7 +17,7 @@ Name: rtorrent -Version: 0.16.14 +Version: 0.16.15 Release: 0 Summary: Console-based BitTorrent client License: SUSE-GPL-2.0+-with-openssl-exception ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.cqdYWD/_old 2026-07-01 16:39:11.202383777 +0200 +++ /var/tmp/diff_new_pack.cqdYWD/_new 2026-07-01 16:39:11.206383916 +0200 @@ -1,5 +1,5 @@ -mtime: 1781442982 -commit: ed7ff080c03459460d03b561a84bc3c9983f0d6c836b908206e3978fcc8d8562 +mtime: 1782854052 +commit: 6e53d80fc4ed98c92abed75aab664a0b23cf717c6e65d31daeff000d98782cd3 url: https://src.opensuse.org/jengelh/rtorrent revision: master ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-06-30 23:14:12.000000000 +0200 @@ -0,0 +1 @@ +.osc ++++++ rtorrent-0.16.14.tar.gz -> rtorrent-0.16.15.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/configure new/rtorrent-0.16.15/configure --- old/rtorrent-0.16.14/configure 2026-06-14 10:19:35.000000000 +0200 +++ new/rtorrent-0.16.15/configure 2026-06-22 11:17:06.000000000 +0200 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.72 for rtorrent 0.16.14. +# Generated by GNU Autoconf 2.72 for rtorrent 0.16.15. # # Report bugs to <[email protected]>. # @@ -614,8 +614,8 @@ # Identity of this package. PACKAGE_NAME='rtorrent' PACKAGE_TARNAME='rtorrent' -PACKAGE_VERSION='0.16.14' -PACKAGE_STRING='rtorrent 0.16.14' +PACKAGE_VERSION='0.16.15' +PACKAGE_STRING='rtorrent 0.16.15' PACKAGE_BUGREPORT='[email protected]' PACKAGE_URL='' @@ -1421,7 +1421,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -'configure' configures rtorrent 0.16.14 to adapt to many kinds of systems. +'configure' configures rtorrent 0.16.15 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1492,7 +1492,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of rtorrent 0.16.14:";; + short | recursive ) echo "Configuration of rtorrent 0.16.15:";; esac cat <<\_ACEOF @@ -1644,7 +1644,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -rtorrent configure 0.16.14 +rtorrent configure 0.16.15 generated by GNU Autoconf 2.72 Copyright (C) 2023 Free Software Foundation, Inc. @@ -2273,7 +2273,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by rtorrent $as_me 0.16.14, which was +It was created by rtorrent $as_me 0.16.15, which was generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw @@ -3966,7 +3966,7 @@ # Define the identity of the package. PACKAGE='rtorrent' - VERSION='0.16.14' + VERSION='0.16.15' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -18708,7 +18708,7 @@ fi -printf "%s\n" "#define API_VERSION 22" >>confdefs.h +printf "%s\n" "#define API_VERSION 23" >>confdefs.h @@ -21491,19 +21491,19 @@ fi pkg_failed=no -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent >= 0.16.14" >&5 -printf %s "checking for libtorrent >= 0.16.14... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent >= 0.16.15" >&5 +printf %s "checking for libtorrent >= 0.16.15... " >&6; } if test -n "$DEPENDENCIES_CFLAGS"; then pkg_cv_DEPENDENCIES_CFLAGS="$DEPENDENCIES_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent >= 0.16.14\""; } >&5 - ($PKG_CONFIG --exists --print-errors "libtorrent >= 0.16.14") 2>&5 + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent >= 0.16.15\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libtorrent >= 0.16.15") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_DEPENDENCIES_CFLAGS=`$PKG_CONFIG --cflags "libtorrent >= 0.16.14" 2>/dev/null` + pkg_cv_DEPENDENCIES_CFLAGS=`$PKG_CONFIG --cflags "libtorrent >= 0.16.15" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -21515,12 +21515,12 @@ pkg_cv_DEPENDENCIES_LIBS="$DEPENDENCIES_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent >= 0.16.14\""; } >&5 - ($PKG_CONFIG --exists --print-errors "libtorrent >= 0.16.14") 2>&5 + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent >= 0.16.15\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libtorrent >= 0.16.15") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_DEPENDENCIES_LIBS=`$PKG_CONFIG --libs "libtorrent >= 0.16.14" 2>/dev/null` + pkg_cv_DEPENDENCIES_LIBS=`$PKG_CONFIG --libs "libtorrent >= 0.16.15" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -21541,14 +21541,14 @@ _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - DEPENDENCIES_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent >= 0.16.14" 2>&1` + DEPENDENCIES_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent >= 0.16.15" 2>&1` else - DEPENDENCIES_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent >= 0.16.14" 2>&1` + DEPENDENCIES_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent >= 0.16.15" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$DEPENDENCIES_PKG_ERRORS" >&5 - as_fn_error $? "Package requirements (libtorrent >= 0.16.14) were not met: + as_fn_error $? "Package requirements (libtorrent >= 0.16.15) were not met: $DEPENDENCIES_PKG_ERRORS @@ -23494,7 +23494,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by rtorrent $as_me 0.16.14, which was +This file was extended by rtorrent $as_me 0.16.15, which was generated by GNU Autoconf 2.72. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -23562,7 +23562,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -rtorrent config.status 0.16.14 +rtorrent config.status 0.16.15 configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/configure.ac new/rtorrent-0.16.15/configure.ac --- old/rtorrent-0.16.14/configure.ac 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/configure.ac 2026-06-22 11:01:51.000000000 +0200 @@ -1,6 +1,6 @@ m4_pattern_allow([PKG_CHECK_EXISTS]) -AC_INIT([rtorrent],[0.16.14],[[email protected]]) +AC_INIT([rtorrent],[0.16.15],[[email protected]]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIRS([scripts]) @@ -14,7 +14,7 @@ PKG_PROG_PKG_CONFIG -AC_DEFINE([API_VERSION], [22], [api version]) +AC_DEFINE([API_VERSION], [23], [api version]) RAK_CHECK_CFLAGS RAK_CHECK_CXXFLAGS @@ -47,7 +47,7 @@ PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"]) PKG_CHECK_MODULES([ZLIB], [zlib]) -PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.14]) +PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.15]) AC_LANG_PUSH(C++) TORRENT_WITH_XMLRPC_C diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/command_download.cc new/rtorrent-0.16.15/src/command_download.cc --- old/rtorrent-0.16.14/src/command_download.cc 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/command_download.cc 2026-06-22 11:01:51.000000000 +0200 @@ -780,15 +780,15 @@ CMD2_DL_TIMESTAMP("d.timestamp.started", "rtorrent", "timestamp.started"); CMD2_DL_TIMESTAMP("d.timestamp.finished", "rtorrent", "timestamp.finished"); - CMD2_DL ("d.connection_current", std::bind(&torrent::option_as_string, torrent::OPTION_CONNECTION_TYPE, CMD2_ON_DL(connection_type))); - CMD2_DL_STRING("d.connection_current.set", std::bind(&apply_d_connection_type, std::placeholders::_1, std::placeholders::_2)); + CMD2_DL ("d.connection_current", [](auto* d, auto) { return torrent::option_to_c_str_or_throw(torrent::OPTION_CONNECTION_TYPE, d->download()->connection_type()); }); + CMD2_DL_STRING_V("d.connection_current.set", [](auto* d, auto arg) { apply_d_connection_type(d, arg); }); CMD2_DL_VAR_STRING("d.connection_leech", "rtorrent", "connection_leech"); CMD2_DL_VAR_STRING("d.connection_seed", "rtorrent", "connection_seed"); - CMD2_DL ("d.up.choke_heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, CMD2_ON_DL(upload_choke_heuristic))); + CMD2_DL ("d.up.choke_heuristics", [](auto* d, auto) { return torrent::option_to_c_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, d->download()->upload_choke_heuristic()); }); CMD2_DL_STRING("d.up.choke_heuristics.set", std::bind(&apply_d_choke_heuristics, std::placeholders::_1, std::placeholders::_2, false)); - CMD2_DL ("d.down.choke_heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, CMD2_ON_DL(download_choke_heuristic))); + CMD2_DL ("d.down.choke_heuristics", [](auto* d, auto) { return torrent::option_to_c_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, d->download()->download_choke_heuristic()); }); CMD2_DL_STRING("d.down.choke_heuristics.set", std::bind(&apply_d_choke_heuristics, std::placeholders::_1, std::placeholders::_2, true)); CMD2_DL_VAR_STRING("d.up.choke_heuristics.leech", "rtorrent", "choke_heuristics.up.leech"); @@ -843,7 +843,7 @@ CMD2_DL ("d.bytes_done", CMD2_ON_DL(bytes_done)); CMD2_DL ("d.ratio", std::bind(&retrieve_d_ratio, std::placeholders::_1)); CMD2_DL ("d.chunks_hashed", CMD2_ON_DL(chunks_hashed)); - CMD2_DL ("d.free_diskspace", CMD2_ON_FL(free_diskspace)); + CMD2_DL ("d.free_diskspace", [](auto* download, auto) { return download->file_list()->free_diskspace_no_cache(); }); CMD2_DL ("d.size_files", CMD2_ON_FL(size_files)); CMD2_DL ("d.size_bytes", CMD2_ON_FL(size_bytes)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/command_events.cc new/rtorrent-0.16.15/src/command_events.cc --- old/rtorrent-0.16.14/src/command_events.cc 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/command_events.cc 2026-06-22 11:01:51.000000000 +0200 @@ -168,12 +168,14 @@ apply_close_low_diskspace(int64_t arg, uint32_t skip_priority) { bool closed = false; + torrent::FileList::cache_list cache; + for (auto download : *control->core()->download_list()) { if (!download->is_downloading()) continue; if (download->priority() >= skip_priority) continue; - if (download->file_list()->free_diskspace() >= (uint64_t)arg) + if (download->file_list()->free_diskspace(cache) >= (uint64_t)arg) continue; control->core()->download_list()->close(download); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/command_groups.cc new/rtorrent-0.16.15/src/command_groups.cc --- old/rtorrent-0.16.14/src/command_groups.cc 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/command_groups.cc 2026-06-22 11:01:51.000000000 +0200 @@ -337,54 +337,51 @@ void initialize_command_groups() { - CMD2_ANY ("choke_group.list", std::bind(&apply_cg_list)); - CMD2_ANY_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2)); + CMD_ANY ("choke_group.list", std::bind(&apply_cg_list)); + CMD_ANY_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2)); #if USE_CHOKE_GROUP - CMD2_ANY ("choke_group.size", std::bind(&torrent::ResourceManager::group_size, torrent::resource_manager())); - CMD2_ANY_STRING ("choke_group.index_of", std::bind(&torrent::ResourceManager::group_index_of, torrent::resource_manager(), std::placeholders::_2)); + CMD_ANY ("choke_group.size", std::bind(&torrent::ResourceManager::group_size, torrent::resource_manager())); + CMD_ANY_STRING ("choke_group.index_of", std::bind(&torrent::ResourceManager::group_index_of, torrent::resource_manager(), std::placeholders::_2)); #else apply_cg_insert("default"); - CMD2_ANY ("choke_group.size", std::bind(&std::vector<torrent::choke_group*>::size, cg_list_hack)); - CMD2_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2)); + CMD_ANY ("choke_group.size", std::bind(&std::vector<torrent::choke_group*>::size, cg_list_hack)); + CMD_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2)); #endif // Commands specific for a group. Supports as the first argument the // name, the index or a negative index. - CMD2_ANY ("choke_group.general.size", std::bind(&torrent::choke_group::size, CG_GROUP_AT())); + CMD_ANY ("choke_group.general.size", std::bind(&torrent::choke_group::size, CG_GROUP_AT())); - CMD2_ANY ("choke_group.tracker.mode", std::bind(&torrent::option_as_string, torrent::OPTION_TRACKER_MODE, - std::bind(&torrent::choke_group::tracker_mode, CG_GROUP_AT()))); - CMD2_ANY_LIST ("choke_group.tracker.mode.set", std::bind(&apply_cg_tracker_mode_set, std::placeholders::_2)); - - CMD2_ANY ("choke_group.all.up.update_balance", std::bind(&apply_cg_all_update_balance, true)); - CMD2_ANY ("choke_group.all.down.update_balance", std::bind(&apply_cg_all_update_balance, false)); - - CMD2_ANY ("choke_group.up.rate", std::bind(&torrent::choke_group::up_rate, CG_GROUP_AT())); - CMD2_ANY ("choke_group.down.rate", std::bind(&torrent::choke_group::down_rate, CG_GROUP_AT())); - - CMD2_ANY ("choke_group.up.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::up_queue))); - CMD2_ANY ("choke_group.up.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::up_queue))); - CMD2_ANY_LIST ("choke_group.up.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, true)); - - CMD2_ANY ("choke_group.up.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::up_queue))); - CMD2_ANY ("choke_group.up.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::up_queue))); - CMD2_ANY ("choke_group.up.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::up_queue))); - CMD2_ANY ("choke_group.up.heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, - std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::up_queue)))); - CMD2_ANY_LIST ("choke_group.up.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, true)); - - CMD2_ANY ("choke_group.down.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::down_queue))); - CMD2_ANY ("choke_group.down.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::down_queue))); - CMD2_ANY_LIST ("choke_group.down.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, false)); - - CMD2_ANY ("choke_group.down.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::down_queue))); - CMD2_ANY ("choke_group.down.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::down_queue))); - CMD2_ANY ("choke_group.down.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::down_queue))); - CMD2_ANY ("choke_group.down.heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, - std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::down_queue)))); - CMD2_ANY_LIST ("choke_group.down.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, false)); + CMD_ANY ("choke_group.tracker.mode", [](auto, auto arg) { return torrent::option_to_str_or_throw(torrent::OPTION_TRACKER_MODE, cg_get_group(arg)->tracker_mode()); }); + CMD_ANY_LIST ("choke_group.tracker.mode.set", [](auto, auto arg) { return apply_cg_tracker_mode_set(arg); }); + + CMD_ANY ("choke_group.all.up.update_balance", std::bind(&apply_cg_all_update_balance, true)); + CMD_ANY ("choke_group.all.down.update_balance", std::bind(&apply_cg_all_update_balance, false)); + + CMD_ANY ("choke_group.up.rate", std::bind(&torrent::choke_group::up_rate, CG_GROUP_AT())); + CMD_ANY ("choke_group.down.rate", std::bind(&torrent::choke_group::down_rate, CG_GROUP_AT())); + + CMD_ANY ("choke_group.up.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::up_queue))); + CMD_ANY ("choke_group.up.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::up_queue))); + CMD_ANY_LIST ("choke_group.up.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, true)); + + CMD_ANY ("choke_group.up.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::up_queue))); + CMD_ANY ("choke_group.up.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::up_queue))); + CMD_ANY ("choke_group.up.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::up_queue))); + CMD_ANY ("choke_group.up.heuristics", [](auto, auto arg) { return torrent::option_to_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, cg_get_group(arg)->up_queue()->heuristics()); }); + CMD_ANY_LIST ("choke_group.up.heuristics.set", [](auto, auto arg) { return apply_cg_heuristics_set(arg, true); }); + + CMD_ANY ("choke_group.down.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::down_queue))); + CMD_ANY ("choke_group.down.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::down_queue))); + CMD_ANY_LIST ("choke_group.down.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, false)); + + CMD_ANY ("choke_group.down.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::down_queue))); + CMD_ANY ("choke_group.down.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::down_queue))); + CMD_ANY ("choke_group.down.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::down_queue))); + CMD_ANY ("choke_group.down.heuristics", [](auto, auto arg) { return torrent::option_to_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, cg_get_group(arg)->down_queue()->heuristics()); }); + CMD_ANY_LIST ("choke_group.down.heuristics.set", [](auto, auto arg) { return apply_cg_heuristics_set(arg, false); }); rpc::rpc.mark_safe("choke_group.list"); rpc::rpc.mark_safe("choke_group.size"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/command_helpers.h new/rtorrent-0.16.15/src/command_helpers.h --- old/rtorrent-0.16.14/src/command_helpers.h 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/command_helpers.h 2026-06-22 11:01:51.000000000 +0200 @@ -8,6 +8,25 @@ void initialize_commands(); // +// Aliases with CMD_* for the below +// + +#define CMD_ANY(key, slot) CMD2_ANY(key, slot) +#define CMD_ANY_P(key, slot) CMD2_ANY_P(key, slot) +#define CMD_REDIRECT(key, slot) CMD2_REDIRECT(key, slot) +#define CMD_REDIRECT_NO_EXPORT(key, slot) CMD2_REDIRECT_NO_EXPORT(key, slot) +#define CMD_ANY_STRING(key, slot) CMD2_ANY_STRING(key, slot) +#define CMD_ANY_STRING_V(key, slot) CMD2_ANY_STRING_V(key, slot) +#define CMD_ANY_LIST(key, slot) CMD2_ANY_LIST(key, slot) +#define CMD_VAR_VALUE(key, value) CMD2_VAR_VALUE(key, value) +#define CMD_VAR_BOOL(key, value) CMD2_VAR_BOOL(key, value) +#define CMD_VAR_STRING(key, value) CMD2_VAR_STRING(key, value) +#define CMD_VAR_C_STRING(key, value) CMD2_VAR_C_STRING(key, value) +#define CMD_VAR_LIST(key) CMD2_VAR_LIST(key) +#define CMD_ANY_V(key, slot) CMD2_ANY_V(key, slot) +#define CMD_ANY_VALUE_V(key, slot) CMD2_ANY_VALUE_V(key, slot) + +// // New std::function based command_base helper functions: // @@ -115,7 +134,6 @@ #define CMD2_REDIRECT_TRACKER(from_key, to_key) \ rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_tracker_target | rpc::CommandMap::flag_dont_delete); - // // Conversion of return types: // diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/command_ip.cc new/rtorrent-0.16.15/src/command_ip.cc --- old/rtorrent-0.16.14/src/command_ip.cc 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/command_ip.cc 2026-06-22 11:01:51.000000000 +0200 @@ -353,7 +353,7 @@ inet_ntop(AF_INET, &net_start, start_str, INET_ADDRSTRLEN); inet_ntop(AF_INET, &net_end, end_str, INET_ADDRSTRLEN); - snprintf(buffer, 64, "%s-%s %s", start_str, end_str, torrent::option_as_string(torrent::OPTION_IP_FILTER, value)); + snprintf(buffer, 64, "%s-%s %s", start_str, end_str, torrent::option_to_c_str_or_throw(torrent::OPTION_IP_FILTER, value)); result.push_back((std::string)buffer); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/command_local.cc new/rtorrent-0.16.15/src/command_local.cc --- old/rtorrent-0.16.14/src/command_local.cc 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/command_local.cc 2026-06-22 11:01:51.000000000 +0200 @@ -12,6 +12,7 @@ #include <torrent/data/file_manager.h> #include <torrent/data/chunk_utils.h> #include <torrent/runtime/runtime.h> +#include <torrent/runtime/socket_manager.h> #include <torrent/utils/chrono.h> #include <torrent/utils/option_strings.h> @@ -191,139 +192,155 @@ torrent::FileManager* fileManager = torrent::file_manager(); if (rpc::call_command_value("method.use_deprecated") == 1) { - CMD2_ANY_LIST ("file.append", std::bind(&cmd_file_append, std::placeholders::_2)); + CMD_ANY_LIST ("file.append", std::bind(&cmd_file_append, std::placeholders::_2)); } - CMD2_ANY ("system.hostname", std::bind(&system_hostname)); - CMD2_ANY ("system.pid", std::bind(&getpid)); + CMD_ANY ("system.hostname", std::bind(&system_hostname)); + CMD_ANY ("system.pid", std::bind(&getpid)); - CMD2_VAR_C_STRING("system.api_version", (int64_t)API_VERSION); - CMD2_VAR_C_STRING("system.client_version", PACKAGE_VERSION); - CMD2_VAR_C_STRING("system.library_version", torrent::runtime::version()); - - CMD2_VAR_VALUE ("system.file.allocate", 0); - CMD2_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30); - CMD2_VAR_VALUE ("system.file.split_size", -1); - CMD2_VAR_STRING ("system.file.split_suffix", ".part"); + 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"); - CMD2_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size, + CMD_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size, (utils::FileStatusCache::base_type*)control->core()->file_status_cache())); - CMD2_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache())); + CMD_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache())); - CMD2_VAR_BOOL ("file.prioritize_toc", 0); - CMD2_VAR_LIST ("file.prioritize_toc.first"); - CMD2_VAR_LIST ("file.prioritize_toc.last"); - - CMD2_ANY ("system.files.advise_random", std::bind(&FM_t::advise_random, fileManager)); - CMD2_ANY_VALUE_V ("system.files.advise_random.set", std::bind(&FM_t::set_advise_random, fileManager, std::placeholders::_2)); - CMD2_ANY ("system.files.advise_random.hashing", std::bind(&FM_t::advise_random_hashing, fileManager)); - CMD2_ANY_VALUE_V ("system.files.advise_random.hashing.set", std::bind(&FM_t::set_advise_random_hashing, fileManager, std::placeholders::_2)); - CMD2_ANY ("system.files.session.fdatasync", [](auto, auto) { return session_thread::manager()->use_fsyncdisk(); }); - CMD2_ANY_VALUE_V ("system.files.session.fdatasync.set", [](auto, auto& value) { return session_thread::manager()->set_use_fsyncdisk(value); }); - - CMD2_ANY ("system.files.opened_counter", std::bind(&FM_t::files_opened_counter, fileManager)); - CMD2_ANY ("system.files.closed_counter", std::bind(&FM_t::files_closed_counter, fileManager)); - CMD2_ANY ("system.files.failed_counter", std::bind(&FM_t::files_failed_counter, fileManager)); + CMD_VAR_BOOL ("file.prioritize_toc", 0); + CMD_VAR_LIST ("file.prioritize_toc.first"); + CMD_VAR_LIST ("file.prioritize_toc.last"); - CMD2_ANY_STRING ("system.env", std::bind(&system_env, std::placeholders::_2)); + CMD_ANY ("system.files.advise_random", std::bind(&FM_t::advise_random, fileManager)); + CMD_ANY_VALUE_V ("system.files.advise_random.set", std::bind(&FM_t::set_advise_random, fileManager, std::placeholders::_2)); + CMD_ANY ("system.files.advise_random.hashing", std::bind(&FM_t::advise_random_hashing, fileManager)); + CMD_ANY_VALUE_V ("system.files.advise_random.hashing.set", std::bind(&FM_t::set_advise_random_hashing, fileManager, std::placeholders::_2)); + 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); }); - CMD2_ANY ("system.time", []([[maybe_unused]] auto t, [[maybe_unused]] auto o) -> torrent::Object { - return torrent::this_thread::cached_seconds().count(); - }); - CMD2_ANY ("system.time_seconds", []([[maybe_unused]] auto t, [[maybe_unused]] auto o) -> torrent::Object { - return torrent::utils::cast_seconds(torrent::utils::time_since_epoch()).count(); - }); - CMD2_ANY ("system.time_usec", []([[maybe_unused]] auto t, [[maybe_unused]] auto o) -> torrent::Object { - return torrent::utils::time_since_epoch().count(); - }); + CMD_ANY ("system.files.opened_counter", std::bind(&FM_t::files_opened_counter, fileManager)); + CMD_ANY ("system.files.closed_counter", std::bind(&FM_t::files_closed_counter, fileManager)); + CMD_ANY ("system.files.failed_counter", std::bind(&FM_t::files_failed_counter, fileManager)); + + 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"); - CMD2_ANY_VALUE_V ("system.umask.set", std::bind(&umask, std::placeholders::_2)); + CMD_ANY ("system.cwd", [](auto, auto) { return system_get_cwd(); }); + CMD_ANY_STRING ("system.cwd.set", [](auto, auto& str) { return system_set_cwd(str); }); - CMD2_VAR_BOOL ("system.daemon", false); + 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(value); }); + CMD_ANY_V ("system.sockets.adjust_alloc", [](auto, auto) { torrent::runtime::socket_manager()->adjust_allocation(); }); - CMD2_ANY_V ("system.shutdown.normal", std::bind(&Control::receive_normal_shutdown, control)); - CMD2_ANY_V ("system.shutdown.quick", std::bind(&Control::receive_quick_shutdown, control)); - CMD2_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal"); - - CMD2_ANY ("system.cwd", std::bind(&system_get_cwd)); - CMD2_ANY_STRING ("system.cwd.set", std::bind(&system_set_cwd, std::placeholders::_2)); - - CMD2_ANY ("pieces.sync.always_safe", std::bind(&CM_t::safe_sync, chunkManager)); - CMD2_ANY_VALUE_V ("pieces.sync.always_safe.set", std::bind(&CM_t::set_safe_sync, chunkManager, std::placeholders::_2)); - CMD2_ANY ("pieces.sync.safe_free_diskspace", std::bind(&CM_t::safe_free_diskspace, chunkManager)); - CMD2_ANY ("pieces.sync.timeout", std::bind(&CM_t::timeout_sync, chunkManager)); - CMD2_ANY_VALUE_V ("pieces.sync.timeout.set", std::bind(&CM_t::set_timeout_sync, chunkManager, std::placeholders::_2)); - CMD2_ANY ("pieces.sync.timeout_safe", std::bind(&CM_t::timeout_safe_sync, chunkManager)); - CMD2_ANY_VALUE_V ("pieces.sync.timeout_safe.set", std::bind(&CM_t::set_timeout_safe_sync, chunkManager, std::placeholders::_2)); - CMD2_ANY ("pieces.sync.queue_size", std::bind(&CM_t::sync_queue_size, chunkManager)); - - CMD2_ANY ("pieces.preload.type", std::bind(&CM_t::preload_type, chunkManager)); - CMD2_ANY_VALUE_V ("pieces.preload.type.set", std::bind(&CM_t::set_preload_type, chunkManager, std::placeholders::_2)); - CMD2_ANY ("pieces.preload.min_size", std::bind(&CM_t::preload_min_size, chunkManager)); - CMD2_ANY_VALUE_V ("pieces.preload.min_size.set", std::bind(&CM_t::set_preload_min_size, chunkManager, std::placeholders::_2)); - CMD2_ANY ("pieces.preload.min_rate", std::bind(&CM_t::preload_required_rate, chunkManager)); - CMD2_ANY_VALUE_V ("pieces.preload.min_rate.set", std::bind(&CM_t::set_preload_required_rate, chunkManager, std::placeholders::_2)); - - CMD2_ANY ("pieces.memory.current", std::bind(&CM_t::memory_usage, chunkManager)); - CMD2_ANY ("pieces.memory.sync_queue", std::bind(&CM_t::sync_queue_memory_usage, chunkManager)); - CMD2_ANY ("pieces.memory.block_count", std::bind(&CM_t::memory_block_count, chunkManager)); - CMD2_ANY ("pieces.memory.max", std::bind(&CM_t::max_memory_usage, chunkManager)); - CMD2_ANY_VALUE_V ("pieces.memory.max.set", std::bind(&CM_t::set_max_memory_usage, chunkManager, std::placeholders::_2)); - CMD2_ANY ("pieces.stats_preloaded", std::bind(&CM_t::stats_preloaded, chunkManager)); - CMD2_ANY ("pieces.stats_not_preloaded", std::bind(&CM_t::stats_not_preloaded, chunkManager)); - - CMD2_ANY ("pieces.stats.total_size", std::bind(&apply_pieces_stats_total_size)); - - CMD2_ANY ("pieces.hash.queue_size", std::bind(&torrent::main_thread::hash_queue_size)); - CMD2_VAR_BOOL ("pieces.hash.on_completion", true); - - CMD2_VAR_STRING ("directory.default", "./"); - - CMD2_VAR_STRING ("session.name", ""); - CMD2_ANY ("session.path", [](auto, auto) { return session_thread::manager()->path(); }); - CMD2_ANY_STRING_V("session.path.set", [](auto, auto& str) { return session_thread::manager()->set_path(str); }); - CMD2_ANY ("session.use_lock", [](auto, auto) { return session_thread::manager()->use_lock(); }); - CMD2_ANY_VALUE_V ("session.use_lock.set", [](auto, auto& value) { return session_thread::manager()->set_use_lock(value); }); - CMD2_VAR_BOOL ("session.on_completion", true); + 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); - CMD2_ANY_V ("session.save", [dList](auto, auto) { return dList->session_save(); }); + 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); }); + 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); }); - CMD2_ANY ("magnet.path", [](auto, auto) { return control->core()->magnet_path(); }); - CMD2_ANY_STRING_V("magnet.path.set", [](auto, auto& str) { return control->core()->set_magnet_path(str); }); + if (i == 0) + continue; + + CMD_ANY_VALUE_V(category_name + ".min_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_min_allocation(category, value); }); + CMD_ANY_VALUE_V(category_name + ".max_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_max_allocation(category, value); }); + } + + CMD_ANY ("pieces.sync.always_safe", std::bind(&CM_t::safe_sync, chunkManager)); + CMD_ANY_VALUE_V ("pieces.sync.always_safe.set", std::bind(&CM_t::set_safe_sync, chunkManager, std::placeholders::_2)); + CMD_ANY ("pieces.sync.safe_free_diskspace", std::bind(&CM_t::safe_free_diskspace, chunkManager)); + CMD_ANY ("pieces.sync.timeout", std::bind(&CM_t::timeout_sync, chunkManager)); + CMD_ANY_VALUE_V ("pieces.sync.timeout.set", std::bind(&CM_t::set_timeout_sync, chunkManager, std::placeholders::_2)); + CMD_ANY ("pieces.sync.timeout_safe", std::bind(&CM_t::timeout_safe_sync, chunkManager)); + CMD_ANY_VALUE_V ("pieces.sync.timeout_safe.set", std::bind(&CM_t::set_timeout_safe_sync, chunkManager, std::placeholders::_2)); + CMD_ANY ("pieces.sync.queue_size", std::bind(&CM_t::sync_queue_size, chunkManager)); + + CMD_ANY ("pieces.preload.type", std::bind(&CM_t::preload_type, chunkManager)); + CMD_ANY_VALUE_V ("pieces.preload.type.set", std::bind(&CM_t::set_preload_type, chunkManager, std::placeholders::_2)); + CMD_ANY ("pieces.preload.min_size", std::bind(&CM_t::preload_min_size, chunkManager)); + CMD_ANY_VALUE_V ("pieces.preload.min_size.set", std::bind(&CM_t::set_preload_min_size, chunkManager, std::placeholders::_2)); + CMD_ANY ("pieces.preload.min_rate", std::bind(&CM_t::preload_required_rate, chunkManager)); + CMD_ANY_VALUE_V ("pieces.preload.min_rate.set", std::bind(&CM_t::set_preload_required_rate, chunkManager, std::placeholders::_2)); + + CMD_ANY ("pieces.memory.current", std::bind(&CM_t::memory_usage, chunkManager)); + CMD_ANY ("pieces.memory.sync_queue", std::bind(&CM_t::sync_queue_memory_usage, chunkManager)); + CMD_ANY ("pieces.memory.block_count", std::bind(&CM_t::memory_block_count, chunkManager)); + CMD_ANY ("pieces.memory.max", std::bind(&CM_t::max_memory_usage, chunkManager)); + CMD_ANY_VALUE_V ("pieces.memory.max.set", std::bind(&CM_t::set_max_memory_usage, chunkManager, std::placeholders::_2)); + CMD_ANY ("pieces.stats_preloaded", std::bind(&CM_t::stats_preloaded, chunkManager)); + CMD_ANY ("pieces.stats_not_preloaded", std::bind(&CM_t::stats_not_preloaded, chunkManager)); + + CMD_ANY ("pieces.stats.total_size", std::bind(&apply_pieces_stats_total_size)); + + CMD_ANY ("pieces.hash.queue_size", std::bind(&torrent::main_thread::hash_queue_size)); + CMD_VAR_BOOL ("pieces.hash.on_completion", true); + + CMD_VAR_STRING ("directory.default", "./"); + + CMD_VAR_STRING ("session.name", ""); + CMD_ANY ("session.path", [](auto, auto) { return session_thread::manager()->path(); }); + CMD_ANY_STRING_V("session.path.set", [](auto, auto& str) { return session_thread::manager()->set_path(str); }); + CMD_ANY ("session.use_lock", [](auto, auto) { return session_thread::manager()->use_lock(); }); + CMD_ANY_VALUE_V ("session.use_lock.set", [](auto, auto& value) { return session_thread::manager()->set_use_lock(value); }); + CMD_VAR_BOOL ("session.on_completion", true); + + CMD_ANY_V ("session.save", [dList](auto, auto) { return dList->session_save(); }); + + CMD_ANY ("magnet.path", [](auto, auto) { return control->core()->magnet_path(); }); + CMD_ANY_STRING_V("magnet.path.set", [](auto, auto& str) { return control->core()->set_magnet_path(str); }); #ifdef HAVE_LUA rpc::LuaEngine* lua_engine = control->lua_engine(); - CMD2_ANY ("lua.execute", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, 0)); - CMD2_ANY ("lua.execute.str", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, rpc::LuaEngine::flag_string)); + CMD_ANY ("lua.execute", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, 0)); + CMD_ANY ("lua.execute.str", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, rpc::LuaEngine::flag_string)); #endif -#define CMD2_EXECUTE(key, flags) \ - CMD2_ANY(key, std::bind(&rpc::ExecFile::execute_object, &rpc::execFile, std::placeholders::_2, flags)); +#define CMD_EXECUTE(key, flags) \ + CMD_ANY(key, std::bind(&rpc::ExecFile::execute_object, &rpc::execFile, std::placeholders::_2, flags)); - CMD2_EXECUTE ("execute", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw); - CMD2_EXECUTE ("execute.throw", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw); - CMD2_EXECUTE ("execute.throw.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background); - CMD2_EXECUTE ("execute.nothrow", rpc::ExecFile::flag_expand_tilde); - CMD2_EXECUTE ("execute.nothrow.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_background); - CMD2_EXECUTE ("execute.raw", rpc::ExecFile::flag_throw); - CMD2_EXECUTE ("execute.raw.bg", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background); - CMD2_EXECUTE ("execute.raw_nothrow", 0); - CMD2_EXECUTE ("execute.raw_nothrow.bg", rpc::ExecFile::flag_background); - CMD2_EXECUTE ("execute.capture", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture); - CMD2_EXECUTE ("execute.capture_nothrow", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture); + CMD_EXECUTE ("execute", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw); + CMD_EXECUTE ("execute.throw", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw); + CMD_EXECUTE ("execute.throw.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background); + CMD_EXECUTE ("execute.nothrow", rpc::ExecFile::flag_expand_tilde); + CMD_EXECUTE ("execute.nothrow.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_background); + CMD_EXECUTE ("execute.raw", rpc::ExecFile::flag_throw); + CMD_EXECUTE ("execute.raw.bg", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background); + CMD_EXECUTE ("execute.raw_nothrow", 0); + CMD_EXECUTE ("execute.raw_nothrow.bg", rpc::ExecFile::flag_background); + CMD_EXECUTE ("execute.capture", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture); + CMD_EXECUTE ("execute.capture_nothrow", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture); // TODO: Convert to new command types: *rpc::command_base::argument(0) = "placeholder.0"; *rpc::command_base::argument(1) = "placeholder.1"; *rpc::command_base::argument(2) = "placeholder.2"; *rpc::command_base::argument(3) = "placeholder.3"; - CMD2_ANY_P("argument.0", std::bind(&rpc::command_base::argument_ref, 0)); - CMD2_ANY_P("argument.1", std::bind(&rpc::command_base::argument_ref, 1)); - CMD2_ANY_P("argument.2", std::bind(&rpc::command_base::argument_ref, 2)); - CMD2_ANY_P("argument.3", std::bind(&rpc::command_base::argument_ref, 3)); + CMD_ANY_P("argument.0", std::bind(&rpc::command_base::argument_ref, 0)); + CMD_ANY_P("argument.1", std::bind(&rpc::command_base::argument_ref, 1)); + CMD_ANY_P("argument.2", std::bind(&rpc::command_base::argument_ref, 2)); + CMD_ANY_P("argument.3", std::bind(&rpc::command_base::argument_ref, 3)); - CMD2_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"); @@ -332,6 +349,18 @@ 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"); + + 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"); + 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("session.path"); rpc::rpc.mark_safe("session.use_lock"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/command_network.cc new/rtorrent-0.16.15/src/command_network.cc --- old/rtorrent-0.16.14/src/command_network.cc 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/command_network.cc 2026-06-22 11:01:51.000000000 +0200 @@ -251,7 +251,6 @@ CMD2_ANY ("network.http.max_host_connections", [http_stack](auto, auto) { return http_stack->max_host_connections(); }); CMD2_ANY_VALUE_V ("network.http.max_host_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_host_connections(value); }); CMD2_ANY ("network.http.max_total_connections", [http_stack](auto, auto) { return http_stack->max_total_connections(); }); - CMD2_ANY_VALUE_V ("network.http.max_total_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_total_connections(value); }); CMD2_ANY ("network.http.proxy_address", [http_stack](auto, auto) { return http_stack->http_proxy(); }); CMD2_ANY_STRING_V("network.http.proxy_address.set", [http_stack](auto, auto& str) { return http_stack->set_http_proxy(str); }); CMD2_ANY ("network.http.ssl_verify_host", [http_stack](auto, auto) { return http_stack->ssl_verify_host(); }); @@ -263,7 +262,7 @@ CMD2_ANY_VALUE_V ("network.send_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_send_buffer_size(value); }); CMD2_ANY ("network.receive_buffer.size", [nw_config](auto, auto) { return nw_config->receive_buffer_size(); }); CMD2_ANY_VALUE_V ("network.receive_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_receive_buffer_size(value); }); - CMD2_ANY_STRING ("network.tos.set", [](auto, auto& str) { return apply_tos(str); }); + CMD2_ANY_STRING ("network.tos.set", [](auto, auto& str) { return apply_tos(str); }); CMD2_ANY ("network.bind_address", [nw_config](auto, auto) { return nw_config->bind_address_best_match_str(); }); CMD2_ANY_STRING_V("network.bind_address.set", [nw_config](auto, auto& str) { return nw_config->set_bind_address_str(str); }); @@ -284,11 +283,7 @@ CMD2_ANY ("network.open_files", [file_manager](auto, auto) { return file_manager->open_files(); }); CMD2_ANY ("network.max_open_files", [file_manager](auto, auto) { return file_manager->max_open_files(); }); - CMD2_ANY_VALUE_V ("network.max_open_files.set", [file_manager](auto, auto& value) { return file_manager->set_max_open_files(value); }); CMD2_ANY ("network.total_handshakes", [](auto, auto) { return torrent::runtime::total_handshakes(); }); - CMD2_ANY ("network.open_sockets", [](auto, auto) { return torrent::runtime::socket_manager()->size(); }); - CMD2_ANY ("network.max_open_sockets", [](auto, auto) { return torrent::runtime::socket_manager()->max_size(); }); - CMD2_ANY_VALUE_V ("network.max_open_sockets.set", [](auto, auto& value) { return torrent::runtime::socket_manager()->set_max_size_and_adjust(value); }); CMD2_ANY_STRING ("network.scgi.open_port", std::bind(&apply_scgi, std::placeholders::_2, 1)); CMD2_ANY_STRING ("network.scgi.open_local", std::bind(&apply_scgi, std::placeholders::_2, 2)); @@ -329,10 +324,9 @@ rpc::rpc.mark_safe("network.http.max_host_connections"); rpc::rpc.mark_safe("network.http.max_total_connections"); + rpc::rpc.mark_safe("network.total_handshakes"); rpc::rpc.mark_safe("network.open_files"); rpc::rpc.mark_safe("network.max_open_files"); - rpc::rpc.mark_safe("network.max_open_sockets"); - rpc::rpc.mark_safe("network.total_handshakes"); rpc::rpc.mark_safe("network.send_buffer.size"); rpc::rpc.mark_safe("network.receive_buffer.size"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/command_ui.cc new/rtorrent-0.16.15/src/command_ui.cc --- old/rtorrent-0.16.14/src/command_ui.cc 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/command_ui.cc 2026-06-22 11:01:51.000000000 +0200 @@ -472,11 +472,14 @@ // if (cond1) { branch1 } else if (cond2) { branch2 } else { branch3 } // <cond1>,<branch1>,<cond2>,<branch2>,<branch3> torrent::Object -apply_if(rpc::target_type target, const torrent::Object& rawArgs, int flags) { - const torrent::Object::list_type& args = rawArgs.as_list(); - torrent::Object::list_const_iterator itr = args.begin(); +apply_if(rpc::target_type target, const torrent::Object& raw_args, int flags) { + auto& args = raw_args.as_list(); + auto itr = args.begin(); - while (itr != args.end() && itr != --args.end()) { + if (args.empty()) + throw torrent::input_error("Empty argument list to " + std::string((flags & 0x1) ? "branch" : "if") + "."); + + { torrent::Object tmp; const torrent::Object* conditional; @@ -500,40 +503,44 @@ result = false; break; default: - throw torrent::input_error("Type not supported by 'if'."); + throw torrent::input_error("Type not supported by " + std::string((flags & 0x1) ? "branch" : "if") + "."); }; itr++; - if (result) - break; - - itr++; + if (!result && itr != args.end()) + itr++; } if (itr == args.end()) return torrent::Object(); - if (flags & 0x1 && itr->is_string()) { - return rpc::parse_command(target, itr->as_string().c_str(), itr->as_string().c_str() + itr->as_string().size()).first; + if (flags & 0x1) { + if (itr->is_string()) + return rpc::parse_command(target, itr->as_string().c_str(), itr->as_string().c_str() + itr->as_string().size()).first; - } else if (flags & 0x1 && itr->is_dict_key()) { - return rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target); + if (itr->is_dict_key()) + return rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target); - } else if (flags & 0x1 && itr->is_list()) { - // Move this into a special function or something. Also, might be - // nice to have a parse_command function that takes list - // iterator... - - for (const auto& cmdItr : itr->as_list()) - if (cmdItr.is_string()) - rpc::parse_command(target, cmdItr.as_string().c_str(), cmdItr.as_string().c_str() + cmdItr.as_string().size()); + if (itr->is_list()) { + for (const auto& cmd_itr : itr->as_list()) { + if (cmd_itr.is_string()) + rpc::parse_command(target, cmd_itr.as_string().c_str(), cmd_itr.as_string().c_str() + cmd_itr.as_string().size()); - return torrent::Object(); + else if (cmd_itr.is_dict_key()) + rpc::commands.call_command(cmd_itr.as_dict_key().c_str(), cmd_itr.as_dict_obj(), target); + + else + throw torrent::input_error("Invalid command type in branch list."); + } - } else { - return *itr; + return torrent::Object(); + } + + throw torrent::input_error("Invalid command type in branch."); } + + return *itr; } torrent::Object diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/control.cc new/rtorrent-0.16.15/src/control.cc --- old/rtorrent-0.16.14/src/control.cc 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/control.cc 2026-06-22 11:01:51.000000000 +0200 @@ -76,7 +76,7 @@ m_ui->init(this); if(!display::Canvas::daemon()) - m_inputStdin->insert(torrent::this_thread::poll()); + m_inputStdin->insert(); } void @@ -86,7 +86,9 @@ torrent::this_thread::scheduler()->erase(&m_task_shutdown); if(!display::Canvas::daemon()) - m_inputStdin->remove(torrent::this_thread::poll()); + m_inputStdin->remove(); + + m_directory_events->close(); if (scgi_thread::thread()->is_active()) scgi_thread::thread()->stop_thread_wait(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/input/input_event.cc new/rtorrent-0.16.15/src/input/input_event.cc --- old/rtorrent-0.16.14/src/input/input_event.cc 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/input/input_event.cc 2026-06-22 11:01:51.000000000 +0200 @@ -2,20 +2,27 @@ #include "input_event.h" +#include <torrent/common.h> +#include <torrent/system/poll.h> + #include "display/attributes.h" namespace input { void -InputEvent::insert(torrent::system::Poll* p) { - p->open(this); - p->insert_read(this); +InputEvent::insert() { + torrent::this_thread::poll()->open(this); + torrent::this_thread::poll()->insert_read(this); + torrent::this_thread::poll()->insert_error(this); } void -InputEvent::remove(torrent::system::Poll* p) { - p->remove_read(this); - p->close(this); +InputEvent::remove() { + if (!is_open()) + return; + + torrent::this_thread::poll()->remove_and_close(this); + set_file_descriptor(-1); } void @@ -32,6 +39,8 @@ void InputEvent::event_error() { + torrent::this_thread::poll()->remove_and_close(this); + set_file_descriptor(-1); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/input/input_event.h new/rtorrent-0.16.15/src/input/input_event.h --- old/rtorrent-0.16.14/src/input/input_event.h 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/input/input_event.h 2026-06-22 11:01:51.000000000 +0200 @@ -4,7 +4,6 @@ #include <functional> #include <torrent/event.h> -#include <torrent/system/poll.h> namespace input { @@ -16,8 +15,8 @@ const char* type_name() const override { return "input"; } - void insert(torrent::system::Poll* p); - void remove(torrent::system::Poll* p); + void insert(); + void remove(); void event_read() override; void event_write() override; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/main.cc new/rtorrent-0.16.15/src/main.cc --- old/rtorrent-0.16.14/src/main.cc 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/main.cc 2026-06-22 11:01:51.000000000 +0200 @@ -112,8 +112,16 @@ // TODO: Create a fake thread object for initializing other processes and enabling logging. torrent::initialize_main_thread(); + // Block SIGCHLD until all threads are created, then unblock on main-thread, to avoid SIGCHLD + // interrupting other threads. + // + // This means only main-thread can fork and wait for child processes. + + SignalHandler::set_block(SIGALRM); + SignalHandler::set_block(SIGPIPE); + SignalHandler::set_block(SIGCHLD); + // All signal handlers must restore errno if they return. - SignalHandler::set_ignore(SIGPIPE); SignalHandler::set_handler(SIGSEGV, std::bind(&do_panic, SIGSEGV)); SignalHandler::set_handler(SIGILL, std::bind(&do_panic, SIGILL)); SignalHandler::set_handler(SIGFPE, std::bind(&do_panic, SIGFPE)); @@ -156,6 +164,8 @@ scgi::ThreadScgi::create_thread(); session::ThreadSession::create_thread(); + SignalHandler::set_unblock(SIGCHLD); + // Initialize option handlers after libtorrent to ensure // torrent::ConnectionManager* are valid etc. initialize_commands(); @@ -296,56 +306,71 @@ // Functions that might not get depracted as they are nice for // configuration files, and thus might do with just some // cleanup. - CMD2_REDIRECT("upload_rate", "throttle.global_up.max_rate.set_kb"); - CMD2_REDIRECT("download_rate", "throttle.global_down.max_rate.set_kb"); + CMD_REDIRECT("upload_rate", "throttle.global_up.max_rate.set_kb"); + CMD_REDIRECT("download_rate", "throttle.global_down.max_rate.set_kb"); - CMD2_REDIRECT("ratio.enable", "group.seeding.ratio.enable"); - CMD2_REDIRECT("ratio.disable", "group.seeding.ratio.disable"); - CMD2_REDIRECT("ratio.min", "group.seeding.ratio.min"); - CMD2_REDIRECT("ratio.max", "group.seeding.ratio.max"); - CMD2_REDIRECT("ratio.upload", "group.seeding.ratio.upload"); - CMD2_REDIRECT("ratio.min.set", "group.seeding.ratio.min.set"); - CMD2_REDIRECT("ratio.max.set", "group.seeding.ratio.max.set"); - CMD2_REDIRECT("ratio.upload.set", "group.seeding.ratio.upload.set"); - - CMD2_REDIRECT("encryption", "protocol.encryption.set"); - - CMD2_REDIRECT("check_hash", "pieces.hash.on_completion.set"); - - CMD2_REDIRECT("connection_leech", "protocol.connection.leech.set"); - CMD2_REDIRECT("connection_seed", "protocol.connection.seed.set"); - - CMD2_REDIRECT("min_peers", "throttle.min_peers.normal.set"); - CMD2_REDIRECT("max_peers", "throttle.max_peers.normal.set"); - CMD2_REDIRECT("min_peers_seed", "throttle.min_peers.seed.set"); - CMD2_REDIRECT("max_peers_seed", "throttle.max_peers.seed.set"); - - CMD2_REDIRECT("min_uploads", "throttle.min_uploads.set"); - CMD2_REDIRECT("max_uploads", "throttle.max_uploads.set"); - CMD2_REDIRECT("min_downloads", "throttle.min_downloads.set"); - CMD2_REDIRECT("max_downloads", "throttle.max_downloads.set"); - - CMD2_REDIRECT("max_uploads_div", "throttle.max_uploads.div.set"); - CMD2_REDIRECT("max_uploads_global", "throttle.max_uploads.global.set"); - CMD2_REDIRECT("max_downloads_div", "throttle.max_downloads.div.set"); - CMD2_REDIRECT("max_downloads_global", "throttle.max_downloads.global.set"); - - CMD2_REDIRECT("directory", "directory.default.set"); - CMD2_REDIRECT("session", "session.path.set"); - - CMD2_REDIRECT("scgi_port", "network.scgi.open_port"); - CMD2_REDIRECT("scgi_local", "network.scgi.open_local"); - - CMD2_REDIRECT("to_gm_time", "convert.gm_time"); - CMD2_REDIRECT("to_gm_date", "convert.gm_date"); - CMD2_REDIRECT("to_time", "convert.time"); - CMD2_REDIRECT("to_date", "convert.date"); - CMD2_REDIRECT("to_elapsed_time", "convert.elapsed_time"); - CMD2_REDIRECT("to_kb", "convert.kb"); - CMD2_REDIRECT("to_mb", "convert.mb"); - CMD2_REDIRECT("to_xb", "convert.xb"); - CMD2_REDIRECT("to_throttle", "convert.throttle"); + CMD_REDIRECT("ratio.enable", "group.seeding.ratio.enable"); + CMD_REDIRECT("ratio.disable", "group.seeding.ratio.disable"); + CMD_REDIRECT("ratio.min", "group.seeding.ratio.min"); + CMD_REDIRECT("ratio.max", "group.seeding.ratio.max"); + CMD_REDIRECT("ratio.upload", "group.seeding.ratio.upload"); + CMD_REDIRECT("ratio.min.set", "group.seeding.ratio.min.set"); + CMD_REDIRECT("ratio.max.set", "group.seeding.ratio.max.set"); + CMD_REDIRECT("ratio.upload.set", "group.seeding.ratio.upload.set"); + + CMD_REDIRECT("encryption", "protocol.encryption.set"); + + CMD_REDIRECT("check_hash", "pieces.hash.on_completion.set"); + + CMD_REDIRECT("connection_leech", "protocol.connection.leech.set"); + CMD_REDIRECT("connection_seed", "protocol.connection.seed.set"); + + CMD_REDIRECT("min_peers", "throttle.min_peers.normal.set"); + CMD_REDIRECT("max_peers", "throttle.max_peers.normal.set"); + CMD_REDIRECT("min_peers_seed", "throttle.min_peers.seed.set"); + CMD_REDIRECT("max_peers_seed", "throttle.max_peers.seed.set"); + + CMD_REDIRECT("min_uploads", "throttle.min_uploads.set"); + CMD_REDIRECT("max_uploads", "throttle.max_uploads.set"); + CMD_REDIRECT("min_downloads", "throttle.min_downloads.set"); + CMD_REDIRECT("max_downloads", "throttle.max_downloads.set"); + + CMD_REDIRECT("max_uploads_div", "throttle.max_uploads.div.set"); + CMD_REDIRECT("max_uploads_global", "throttle.max_uploads.global.set"); + CMD_REDIRECT("max_downloads_div", "throttle.max_downloads.div.set"); + CMD_REDIRECT("max_downloads_global", "throttle.max_downloads.global.set"); + + CMD_REDIRECT("directory", "directory.default.set"); + CMD_REDIRECT("session", "session.path.set"); + + CMD_REDIRECT("scgi_port", "network.scgi.open_port"); + CMD_REDIRECT("scgi_local", "network.scgi.open_local"); + + CMD_REDIRECT("to_gm_time", "convert.gm_time"); + CMD_REDIRECT("to_gm_date", "convert.gm_date"); + CMD_REDIRECT("to_time", "convert.time"); + CMD_REDIRECT("to_date", "convert.date"); + CMD_REDIRECT("to_elapsed_time", "convert.elapsed_time"); + CMD_REDIRECT("to_kb", "convert.kb"); + CMD_REDIRECT("to_mb", "convert.mb"); + CMD_REDIRECT("to_xb", "convert.xb"); + CMD_REDIRECT("to_throttle", "convert.throttle"); + + // TODO: Deprecate these at some point a while after 1.1 release. + + CMD_REDIRECT("network.open_sockets", "system.sockets.size"); + CMD_REDIRECT("network.max_open_sockets", "system.sockets.max_size"); + CMD_REDIRECT("network.max_open_sockets.set", "system.sockets.max_size.set"); + rpc::rpc.mark_safe("network.max_open_sockets"); + + CMD2_ANY_VALUE_V("network.http.max_total_connections.set", [](auto, auto) { + lt_log_print(torrent::LOG_WARN, "network.http.max_total_connections.set is deprecated, use system.sockets.http.min_alloc.set instead."); + }); + + CMD2_ANY_VALUE_V("network.max_open_files.set", [](auto, auto) { + lt_log_print(torrent::LOG_WARN, "network.max_open_files.set is deprecated, use system.sockets.files.min_alloc.set instead."); + }); // if (rpc::call_command_value("method.use_intermediate") == 1) { @@ -354,41 +379,41 @@ // } if (rpc::call_command_value("method.use_deprecated") == 1) { - CMD2_REDIRECT("execute2", "execute"); - CMD2_REDIRECT("schedule2", "schedule"); - CMD2_REDIRECT("schedule_remove2", "schedule.remove"); + CMD_REDIRECT("execute2", "execute"); + CMD_REDIRECT("schedule2", "schedule"); + CMD_REDIRECT("schedule_remove2", "schedule.remove"); // TODO: Remove file.append when cleaning these up. - CMD2_REDIRECT("bind", "network.bind_address.set"); - CMD2_REDIRECT("ip", "network.local_address.set"); - CMD2_REDIRECT("port_range", "network.port_range.set"); + CMD_REDIRECT("bind", "network.bind_address.set"); + CMD_REDIRECT("ip", "network.local_address.set"); + CMD_REDIRECT("port_range", "network.port_range.set"); // TODO: Check if dht is on by default. - CMD2_REDIRECT("dht", "dht.mode.set"); + CMD_REDIRECT("dht", "dht.mode.set"); - CMD2_REDIRECT("port_random", "network.port_random.set"); - CMD2_REDIRECT("proxy_address", "network.proxy_address.set"); + CMD_REDIRECT("port_random", "network.port_random.set"); + CMD_REDIRECT("proxy_address", "network.proxy_address.set"); - CMD2_REDIRECT("key_layout", "keys.layout.set"); + CMD_REDIRECT("key_layout", "keys.layout.set"); - CMD2_REDIRECT("torrent_list_layout", "ui.torrent_list.layout.set"); + CMD_REDIRECT("torrent_list_layout", "ui.torrent_list.layout.set"); CMD2_VAR_STRING("dht.throttle.name", "deprecated"); rpc::rpc.mark_safe("dht.throttle.name"); - CMD2_REDIRECT("network.http.max_open", "network.http.max_total_connections"); - CMD2_REDIRECT("network.http.max_open.set", "network.http.max_total_connections.set"); + CMD_REDIRECT("network.http.max_open", "network.http.max_total_connections"); + CMD_REDIRECT("network.http.max_open.set", "network.http.max_total_connections.set"); // Users should check their setups to see if they need to modify their use of these options. - CMD2_REDIRECT("max_memory_usage", "pieces.memory.max.set"); - CMD2_REDIRECT("encoding_list", "encoding.add"); + CMD_REDIRECT("max_memory_usage", "pieces.memory.max.set"); + CMD_REDIRECT("encoding_list", "encoding.add"); - CMD2_ANY_STRING_V("encoding.add", [](auto, auto) { + CMD_ANY_STRING_V("encoding.add", [](auto, auto) { lt_log_print(torrent::LOG_WARN, "The 'encoding.add' command is deprecated and does nothing."); }); - CMD2_ANY_LIST ("throttle.ip", []( auto, auto) { + CMD_ANY_LIST("throttle.ip", []( auto, auto) { lt_log_print(torrent::LOG_WARN, "The 'throttle.ip' command is deprecated and does nothing."); return torrent::Object(); }); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/rpc/command.h new/rtorrent-0.16.15/src/rpc/command.h --- old/rtorrent-0.16.14/src/rpc/command.h 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/rpc/command.h 2026-06-22 11:01:51.000000000 +0200 @@ -3,6 +3,7 @@ #include <functional> #include <limits> +#include <new> #include <torrent/common.h> #include <torrent/object.h> #include <torrent/data/file_list_iterator.h> @@ -28,7 +29,6 @@ typedef no_type* cleaned_type; }; -// Since c++0x isn't out yet... template <typename T1, typename T2, typename T3> struct rt_triple : private std::pair<T1, T2> { typedef std::pair<T1, T2> base_type; @@ -55,9 +55,6 @@ base_type(src.first, src.second), third(src.third) {} }; -// Since it gets used so many places we might as well put it in the -// rpc namespace. -//typedef std::pair<int, void*> target_type; typedef rt_triple<int, void*, void*> target_type; class command_base; @@ -114,10 +111,48 @@ char buffer[sizeof(torrent::Object) * max_arguments]; }; - command_base() { new (&_pod<base_function>()) base_function(); } - command_base(const command_base& src) { new (&_pod<base_function>()) base_function(src._pod<base_function>()); } + command_base() : m_copy_helper(nullptr), m_dest_helper(nullptr) {} - ~command_base() { _pod<base_function>().~base_function(); } + command_base(const command_base& src) { + m_copy_helper = src.m_copy_helper; + m_dest_helper = src.m_dest_helper; + if (src.m_copy_helper) + src.m_copy_helper(t_pod, src.t_pod); + } + + command_base& operator=(const command_base& src) { + if (this != &src) { + if (m_dest_helper) m_dest_helper(t_pod); + m_copy_helper = src.m_copy_helper; + m_dest_helper = src.m_dest_helper; + if (src.m_copy_helper) + src.m_copy_helper(t_pod, src.t_pod); + } + return *this; + } + + command_base(command_base&& src) noexcept { + m_copy_helper = src.m_copy_helper; + m_dest_helper = src.m_dest_helper; + if (src.m_copy_helper) + src.m_copy_helper(t_pod, src.t_pod); + } + + command_base& operator=(command_base&& src) noexcept { + if (this != &src) { + if (m_dest_helper) m_dest_helper(t_pod); + m_copy_helper = src.m_copy_helper; + m_dest_helper = src.m_dest_helper; + if (src.m_copy_helper) + src.m_copy_helper(t_pod, src.t_pod); + } + return *this; + } + + ~command_base() { + if (m_dest_helper) + m_dest_helper(t_pod); + } static torrent::Object* argument(unsigned int index) { return current_stack.begin() + index; } static torrent::Object& argument_ref(unsigned int index) { return *(current_stack.begin() + index); } @@ -132,55 +167,52 @@ static void pop_stack(stack_type* stack, torrent::Object* last_stack); template <typename T> - void set_function(T s, [[maybe_unused]] int value = command_base_is_valid<T>::value) { _pod<T>() = s; } + void set_function(T s, [[maybe_unused]] int value = command_base_is_valid<T>::value) { + static_assert(sizeof(T) <= sizeof(t_pod), "t_pod storage overflow"); + static_assert(alignof(std::max_align_t) % alignof(T) == 0, "t_pod alignment insufficient for type"); + static_assert(alignof(std::max_align_t) >= alignof(T), "t_pod structural capacity mismatch"); + + if (m_dest_helper) + m_dest_helper(t_pod); + + ::new (t_pod) T(std::move(s)); + + m_copy_helper = [](void* dest, const void* src) { + ::new (dest) T(*static_cast<const T*>(src)); + }; + m_dest_helper = [](void* ptr) { + static_cast<T*>(ptr)->~T(); + }; + } template <command_base_call_type T> void set_function_2(typename command_base_is_type<T>::type s, [[maybe_unused]] int value = command_base_is_valid<typename command_base_is_type<T>::type>::value) { - _pod<typename command_base_is_type<T>::type>() = s; + set_function<typename command_base_is_type<T>::type>(std::move(s)); } - // The std::function object in GCC is castable between types with a - // pointer to a struct of ctor/dtor/calls for non-POD slots. As such - // it should be safe to cast between different std::function - // template types, yet what the C++0x standard will say about this I - // have no idea atm. template <typename tmpl> tmpl& _pod() { return reinterpret_cast<tmpl&>(t_pod); } template <typename tmpl> const tmpl& _pod() const { return reinterpret_cast<const tmpl&>(t_pod); } template <typename Func, typename T, typename Args> static const torrent::Object _call(command_base* cmd, target_type target, Args args); - command_base& operator = (const command_base& src) { - _pod<base_function>() = src._pod<base_function>(); - return *this; - } - protected: - // For use by functions that need to use placeholders to arguments - // within commands. E.d. callable command strings where one of the - // arguments within the command needs to be supplied by the caller. - -#ifdef HAVE_CXX11 - union { - base_function t_pod; - // char t_pod[sizeof(base_function)]; - }; -#else - union { - char t_pod[sizeof(base_function)]; - }; -#endif + using copy_fn_t = void (*)(void* dest, const void* src); + using dest_fn_t = void (*)(void* ptr); + + alignas(std::max_align_t) char t_pod[sizeof(base_function)]; + + copy_fn_t m_copy_helper; + dest_fn_t m_dest_helper; }; template <typename T1 = void, typename T2 = void> struct target_type_id { - // Nothing here, so we cause an error. }; template <typename T> inline bool is_target_compatible(const target_type& target) { return target.first == target_type_id<T>::value; } -// Splitting pairs into separate targets. inline bool is_target_pair(const target_type& target) { return target.first >= command_base::target_download_pair; } template <typename T> inline T @@ -203,7 +235,7 @@ #define COMMAND_BASE_TEMPLATE_TYPE(func_type, func_parm) \ template <typename T, int proper = target_type_id<T>::proper_type> struct func_type { typedef std::function<func_parm> type; }; \ - \ + \ template <> struct command_base_is_valid<func_type<target_type>::type> { static const int value = 1; }; \ template <> struct command_base_is_valid<func_type<core::Download*>::type> { static const int value = 1; }; \ template <> struct command_base_is_valid<func_type<torrent::Peer*>::type> { static const int value = 1; }; \ @@ -211,8 +243,6 @@ template <> struct command_base_is_valid<func_type<torrent::File*>::type> { static const int value = 1; }; \ template <> struct command_base_is_valid<func_type<torrent::FileListIterator*>::type> { static const int value = 1; }; -// template <typename Q> struct command_base_is_valid<typename func_type<Q>::type > { static const int value = 1; }; - COMMAND_BASE_TEMPLATE_TYPE(command_function, torrent::Object (T, const torrent::Object&)); COMMAND_BASE_TEMPLATE_TYPE(command_value_function, torrent::Object (T, const torrent::Object::value_type&)); COMMAND_BASE_TEMPLATE_TYPE(command_string_function, torrent::Object (T, const std::string&)); @@ -220,7 +250,7 @@ #define COMMAND_BASE_TEMPLATE_CALL(func_name, func_type) \ template <typename T> const torrent::Object func_name(command_base* rawCommand, target_type target, const torrent::Object& args); \ - \ + \ template <> struct command_base_is_type<func_name<target_type> > { static const int value = 1; typedef func_type<target_type>::type type; }; \ template <> struct command_base_is_type<func_name<core::Download*> > { static const int value = 1; typedef func_type<core::Download*>::type type; }; \ template <> struct command_base_is_type<func_name<torrent::Peer*> > { static const int value = 1; typedef func_type<torrent::Peer*>::type type; }; \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/rpc/exec_file.cc new/rtorrent-0.16.15/src/rpc/exec_file.cc --- old/rtorrent-0.16.14/src/rpc/exec_file.cc 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/rpc/exec_file.cc 2026-06-22 11:01:51.000000000 +0200 @@ -3,6 +3,7 @@ #include <cerrno> #include <cstring> #include <fcntl.h> +#include <spawn.h> #include <string> #include <unistd.h> #include <sys/types.h> @@ -12,6 +13,9 @@ #include "exec_file.h" #include "parse.h" +// Standard POSIX environment pointer +extern char** environ; + namespace rpc { // TODO: Access fd through torrent logging? @@ -34,86 +38,92 @@ result = write(m_log_fd, "\n---\n", sizeof("\n---\n")); } - int pipeFd[2]; + int pipe_fd[2]; - if ((flags & flag_capture) && pipe(pipeFd)) + if ((flags & flag_capture) && pipe(pipe_fd)) throw torrent::input_error("ExecFile::execute(...) Pipe creation failed."); - pid_t childPid = fork(); + auto clean_fn = [pipe_fd, flags]() { + if (flags & flag_capture) { + ::close(pipe_fd[0]); + ::close(pipe_fd[1]); + } + }; + + posix_spawn_file_actions_t actions{}; - if (childPid == -1) { - if (flags & flag_capture) { - ::close(pipeFd[0]); - ::close(pipeFd[1]); - } - throw torrent::input_error("ExecFile::execute(...) Fork failed."); + if (posix_spawn_file_actions_init(&actions) != 0) { + clean_fn(); + throw torrent::internal_error("ExecFile::execute(...) posix_spawn_file_actions_init failed."); } - if (childPid == 0) { - if (flags & flag_background) { - pid_t detached_pid = fork(); + // Handle standard input redirection (/dev/null), posix_spawn_file_actions_addopen handles opening + // and dup2 natively + if (posix_spawn_file_actions_addopen(&actions, 0, "/dev/null", O_RDWR, 0) != 0) { + // Fallback if open fails inside action setup + posix_spawn_file_actions_addclose(&actions, 0); + } - if (detached_pid == -1) - _exit(-1); + // Handle standard output redirection + if (flags & flag_capture) { + posix_spawn_file_actions_adddup2(&actions, pipe_fd[1], 1); - if (detached_pid != 0) { - if (m_log_fd != -1) - result = write(m_log_fd, "\n--- Background task ---\n", sizeof("\n--- Background task ---\n")); + // Ensure the write end of the pipe is closed in the child after duplicating. + posix_spawn_file_actions_addclose(&actions, pipe_fd[1]); + posix_spawn_file_actions_addclose(&actions, pipe_fd[0]); - _exit(0); - } + } else if (m_log_fd != -1) { + posix_spawn_file_actions_adddup2(&actions, m_log_fd, 1); - m_log_fd = -1; - flags &= ~flag_capture; - } + } else { + posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_RDWR, 0); + } + + if (m_log_fd != -1) { + posix_spawn_file_actions_adddup2(&actions, m_log_fd, 2); + } else { + posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_RDWR, 0); + } - int devNull = open("/dev/null", O_RDWR); + posix_spawnattr_t attr; + posix_spawnattr_init(&attr); - if (devNull != -1) - dup2(devNull, 0); - else - ::close(0); - - if (flags & flag_capture) - dup2(pipeFd[1], 1); - else if (m_log_fd != -1) - dup2(m_log_fd, 1); - else if (devNull != -1) - dup2(devNull, 1); - else - ::close(1); - - if (m_log_fd != -1) - dup2(m_log_fd, 2); - else if (devNull != -1) - dup2(devNull, 2); - else - ::close(2); - - // Close all fd's. - for (int i = 3, last = sysconf(_SC_OPEN_MAX); i != last; i++) - ::close(i); + // If you are using standard close-on-exec (O_CLOEXEC) across rtorrent, posix_spawn honors it + // automatically. If you want to explicitly enforce a clean slate, modern systems support + // POSIX_SPAWN_CLOEXEC_DEFAULT. - result = execvp(file, argv); +#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT + posix_spawnattr_setflags(&attr, POSIX_SPAWN_CLOEXEC_DEFAULT); +#endif - _exit(result); + pid_t child_pid{}; + + int spawn_status = posix_spawnp(&child_pid, file, &actions, &attr, argv, environ); + + posix_spawn_file_actions_destroy(&actions); + posix_spawnattr_destroy(&attr); + + if (spawn_status != 0) { + clean_fn(); + throw torrent::input_error("ExecFile::execute(...) posix_spawn failed: " + std::string(std::strerror(spawn_status))); } if (flags & flag_capture) { m_capture = std::string(); - ::close(pipeFd[1]); + ::close(pipe_fd[1]); char buffer[4096]; ssize_t length; do { - length = read(pipeFd[0], buffer, sizeof(buffer)); + length = read(pipe_fd[0], buffer, sizeof(buffer)); if (length > 0) m_capture += std::string(buffer, length); + } while (length > 0); - ::close(pipeFd[0]); + ::close(pipe_fd[0]); if (m_log_fd != -1) { result = write(m_log_fd, "Captured output:\n", sizeof("Captured output:\n")); @@ -123,7 +133,7 @@ int status; - while (waitpid(childPid, &status, 0) == -1) { + while (waitpid(child_pid, &status, 0) == -1) { switch (errno) { case EINTR: continue; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/rpc/scgi.cc new/rtorrent-0.16.15/src/rpc/scgi.cc --- old/rtorrent-0.16.14/src/rpc/scgi.cc 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/rpc/scgi.cc 2026-06-22 11:01:51.000000000 +0200 @@ -48,7 +48,7 @@ open(reinterpret_cast<sockaddr*>(sa), length); - torrent::runtime::socket_manager()->register_event_or_throw(this, torrent::runtime::category_scgi, []() {}); + torrent::runtime::socket_manager()->register_event_or_throw(this, torrent::runtime::category_rpc, []() {}); } void @@ -72,7 +72,7 @@ open(reinterpret_cast<sockaddr*>(sa), offsetof(struct sockaddr_un, sun_path) + filename.size() + 1); - torrent::runtime::socket_manager()->register_event_or_throw(this, torrent::runtime::category_scgi, []() {}); + torrent::runtime::socket_manager()->register_event_or_throw(this, torrent::runtime::category_rpc, []() {}); m_path = filename; } @@ -86,7 +86,7 @@ // fd is already bound and listening; no bind()/listen() needed. - torrent::runtime::socket_manager()->register_event_or_throw(this, torrent::runtime::category_scgi, []() {}); + torrent::runtime::socket_manager()->register_event_or_throw(this, torrent::runtime::category_rpc, []() {}); } void @@ -183,7 +183,7 @@ task->cancel_open(); }; - bool result = torrent::runtime::socket_manager()->open_event_or_cleanup(m_current->get(), torrent::runtime::category_scgi, open_func, cleanup_func); + bool result = torrent::runtime::socket_manager()->open_event_or_cleanup(m_current->get(), torrent::runtime::category_rpc, open_func, cleanup_func); if (!result) break; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/signal_handler.cc new/rtorrent-0.16.15/src/signal_handler.cc --- old/rtorrent-0.16.14/src/signal_handler.cc 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/signal_handler.cc 2026-06-22 11:01:51.000000000 +0200 @@ -42,6 +42,7 @@ throw std::logic_error("SignalHandler::set_handler(...) received an empty slot."); struct sigaction sa; + sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sa.sa_handler = &SignalHandler::caught; @@ -53,6 +54,34 @@ } void +SignalHandler::set_block(unsigned int signum) { + if (signum >= HIGHEST_SIGNAL) + throw std::logic_error("SignalHandler::set_block(...) received invalid signal value."); + + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, signum); + + if (pthread_sigmask(SIG_BLOCK, &mask, NULL) == -1) + throw std::logic_error("Could not block signal: " + std::string(std::strerror(errno))); +} + +void +SignalHandler::set_unblock(unsigned int signum) { + if (signum >= HIGHEST_SIGNAL) + throw std::logic_error("SignalHandler::set_unblock(...) received invalid signal value."); + + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, signum); + + if (pthread_sigmask(SIG_UNBLOCK, &mask, NULL) == -1) + throw std::logic_error("Could not unblock signal: " + std::string(std::strerror(errno))); +} + +void SignalHandler::set_sigaction_handler(unsigned int signum, handler_slot slot) { if (signum >= HIGHEST_SIGNAL) throw std::logic_error("SignalHandler::set_handler(...) received invalid signal value."); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.14/src/signal_handler.h new/rtorrent-0.16.15/src/signal_handler.h --- old/rtorrent-0.16.14/src/signal_handler.h 2026-06-14 10:04:12.000000000 +0200 +++ new/rtorrent-0.16.15/src/signal_handler.h 2026-06-22 11:01:51.000000000 +0200 @@ -1,37 +1,3 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, Jari Sundell -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// In addition, as a special exception, the copyright holders give -// permission to link the code of portions of this program with the -// OpenSSL library under certain conditions as described in each -// individual source file, and distribute linked combinations -// including the two. -// -// You must obey the GNU General Public License in all respects for -// all of the code used other than OpenSSL. If you modify file(s) -// with this exception, you may extend this exception to your version -// of the file(s), but you are not obligated to do so. If you do not -// wish to do so, delete this exception statement from your version. -// If you delete this exception statement from all source files in the -// program, then also delete it here. -// -// Contact: Jari Sundell <[email protected]> - - #ifndef RTORRENT_SIGNAL_HANDLER_H #define RTORRENT_SIGNAL_HANDLER_H @@ -56,6 +22,9 @@ static void set_ignore(unsigned int signum); static void set_handler(unsigned int signum, slot_void slot); + static void set_block(unsigned int signum); + static void set_unblock(unsigned int signum); + static void set_sigaction_handler(unsigned int signum, handler_slot slot); static const char* as_string(unsigned int signum);
