Compare commits

...

87 Commits

Author SHA1 Message Date
Jacob Reynolds 5e7dd98c67 Update rtorrent.rc.lua-example to use new network.listen.port.x commands
See #1935
2026-08-14 16:35:20 +02:00
xirvik b24db0eaa5 Do not dereference missing text nodes in the tinyxml2 backend.
Empty elements and element children crashed the parser on a null.
2026-08-14 13:16:17 +02:00
Jari Sundell 6fa139f7bf Reduce max_string_pad_size from 1<<20 to 1<<14 2026-08-14 12:46:37 +02:00
xirvik 68904ba005 Limit the padding string.rpad and string.lpad will build.
An untrusted caller could exhaust memory or the response limit.
2026-08-14 12:46:37 +02:00
xirvik b401068246 Let the choke group list own its groups.
Each choke_group.insert leaked a group, and size reported a stale copy.
2026-08-14 12:15:36 +02:00
xirvik 946a4b5c83 Check the focus iterator before dereferencing it on erase.
Erasing a download read past the end of the view.
2026-08-13 19:30:21 +02:00
xirvik a075001a73 Catch every json exception when processing a JSONRPC request.
A number overflow escaped the two handlers and killed the process.
2026-08-12 18:22:17 +02:00
xirvik 781520fef8 Close the connection when an SCGI header does not fit the buffer.
A malformed header aborted the process from the SCGI thread.
2026-08-12 15:48:39 +02:00
kunalkakade 224f06fdc2 Link against libatomic when 64-bit atomics require it. 2026-08-09 12:06:08 +02:00
xirvik 324811d6f0 Do not fail startup on unreadable session files.
The download is flagged as hash failed with a message instead of throwing.
2026-08-09 10:39:55 +02:00
Jari Sundell e51fad5bd2 Clean up comments in root.h
Removed comments regarding input history size limitations.
2026-08-09 10:19:35 +02:00
xirvik 44bb2d691f ui: bound the input history size.
Only the lower end was checked, so a large value threw bad_alloc and exited.
2026-08-09 10:19:35 +02:00
xirvik 161a36dedd Add system.sockets.<category>.min_alloc.limit.
A category can have a lower bound too, and a client needs to read it.
2026-08-08 19:39:59 +02:00
xirvik a69e44a096 Add system.sockets.<category>.max_alloc.limit.
A category can have a ceiling below the shared budget, and a client needs to read it.
2026-08-08 11:28:26 +02:00
Greg ORIOL 4dd8b1fd7e Update rtorrent.rc-example with newer network.listen 2026-08-08 10:44:41 +02:00
Greg ORIOL b5f6bb5652 Update rtorrent.rc with newer network.listen 2026-08-08 10:44:41 +02:00
kunalkakade 3ae6bbc394 Fix garbage throttle values in the status bar on 32-bit archs.
Closes #1683.
2026-08-07 15:45:47 +02:00
kunalkakade fdfca98f1c Add documentation for resolving paths. 2026-08-07 11:58:14 +02:00
kunalkakade 31196ff5f0 Add realpath variants of the commands that return paths.
Closes #1670.
2026-08-07 11:58:14 +02:00
kunalkakade cfb9e083d2 Add documentation for the string.* commands. 2026-08-07 11:35:35 +02:00
kunalkakade 5b4b607c98 Add string.* commands for text manipulation.
Closes #1366.
2026-08-07 11:35:35 +02:00
xirvik 87db1cf0f0 rpc: do not dereference a null result from the xmlrpc-c registry.
A failed call returns null, which was passed straight to xmlrpc_mem_block_contents.
2026-08-07 10:56:19 +02:00
rakshasa dd3ddf7c39 Tagged release 0.16.20. 2026-08-06 10:12:49 +02:00
rakshasa e56bffff4d Fixed socket category unit test. 2026-08-05 11:18:31 +02:00
xirvik d20c3234da Add system.sockets allocation budget commands.
They report the budget the socket manager enforces, so a client can show the limit.
2026-08-05 11:18:31 +02:00
Maks Verver dda5e4fa04 Fix deprecated command redirects (rtorrent -D) 2026-08-05 10:14:35 +02:00
Jari Sundell 9ab73352e1 Minor fixes to WaitpidQueue. 2026-08-05 00:59:33 +09:00
Jari Sundell f90a96d57a Added WaitpidQueue to handle background process reaping. 2026-08-04 19:03:26 +09:00
rakshasa 247ae7621a Tagged release 0.16.19. 2026-08-02 10:44:30 +02:00
xirvik 05831942a7 commands: don't register min_alloc/max_alloc for the generic socket category
SocketManager throws internal_error for category_generic, aborting rtorrent over RPC.
2026-07-30 12:42:24 +02:00
rakshasa ee18a1f3fc Added commit message linter. 2026-07-28 10:04:51 +02:00
Jari Sundell f52f20f1b6 Cleaned up system headers. 2026-07-26 19:53:18 +09:00
xirvik 41eef969ea commands: fix group.<name>.ratio.disable calling a removed command
group_insert() builds the disable method as "schedule_remove=...", but that
command no longer exists: the current name is schedule.remove, and the only
old-style aliases registered are schedule2 and schedule_remove2 (and those only
when deprecated commands are enabled at registration time). Every
group.<name>.ratio.disable therefore faults with

  Command "schedule_remove" does not exist.

including the built-in group.seeding.ratio.disable, so a ratio group can be
enabled but never disabled. The enable half already uses the current name.
2026-07-25 10:18:49 +02:00
fffe 1def5e8b36 Add log.print and enum.log_group methods 2026-07-22 11:08:32 +02:00
simonc56 8355870586 Mark 'd.timestamp.started' as safe in command download initialization 2026-07-20 08:21:32 +02:00
Jari Sundell ba2bc7e64d Various release bug fixes. 2026-07-19 18:53:57 +09:00
rakshasa 0c11deac50 Tagged release 0.16.18. 2026-07-17 10:15:28 +02:00
Jari Sundell f6b3ad0efd Minor cleanup of pex and other commands. 2026-07-15 19:40:13 +09:00
Jari Sundell 5d350f0bcc Use new encryption modes. 2026-07-14 17:34:14 +09:00
Jari Sundell 86fa0d195f Changing listen/dht port changes the listening/dht ports. 2026-07-12 17:19:33 +09:00
Jari Sundell 2354c9cdb5 Moved ChunkManager out of public header directory. 2026-07-10 17:54:37 +09:00
Jari Sundell 1b25bc7f56 Moved all sync/diskspace related methods to MemoryManager. 2026-07-10 02:21:08 +09:00
Jari Sundell c6bde213b4 Added runtime::MemoryManager to split out unrelated features from ChunkManager. 2026-07-09 01:56:30 +09:00
Jari Sundell 959448acda Fixed command_base t_pod align static asserts. 2026-07-08 17:15:56 +09:00
Jari Sundell 3b6da6feac Reordered http queue slots to avoid race conditions. 2026-07-07 16:34:19 +09:00
Jari Sundell ea2d22cb87 Removed unused add/remove error-event code. 2026-07-07 03:05:20 +09:00
rakshasa fa351c017d Tagged release 0.16.17. 2026-07-06 14:06:52 +02:00
Pluto Yang 1e1bdd551a Add LoongArch CPU support 2026-07-06 13:38:08 +02:00
Jari Sundell a07f57a703 Fixed fd close order in ExecFile. 2026-07-06 18:06:51 +09:00
Jari Sundell 0494ce70c8 Add http done/failed slots before starting request. 2026-07-06 17:18:39 +09:00
Xeonacid cfad36bf12 Add RISC-V cacheline fallbacks
The Linux cacheline probe first tries <linux/cache.h>, but that header is not part of the installed uapi headers on Arch Linux. When the compile probe fails, configure falls back to a host_cpu mapping and currently aborts for riscv64 with:

  Unrecognized CPU architecture (riscv64) on Linux fallback path.

Handle riscv* in both cacheline fallback maps and use a 64-byte cacheline. That matches the Linux RISC-V kernel default L1_CACHE_SHIFT value of 6 and the value reported on a riscv64 machine through getconf LEVEL1_DCACHE_LINESIZE and sysfs cache coherency_line_size.
2026-07-05 15:40:50 +02:00
rakshasa 123c327a8f Tagged release 0.16.16. 2026-07-03 16:20:27 +02:00
fffe 9884c9dd9b Add support for posix_spawn_file_actions_addclosefrom_np by @fffe 2026-07-03 20:52:26 +09:00
Jari Sundell d99cd5eb73 Added max http request size and limited redirect protocols to http/https. 2026-07-03 19:34:36 +09:00
Jari Sundell 786f1234d2 Fix background task execution. 2026-07-03 17:53:00 +09:00
Jari Sundell 23921cc97c Added new ProxyManager and support for socks5 for peer connections. 2026-06-30 19:55:45 +09:00
Jari Sundell 6558f0ad42 Fixed shutdown handling of stalled http requests. 2026-06-29 16:18:56 +09:00
simonc56 9431dd5acc mark d.complete & d.timestamp.finished as safe in command download 2026-06-28 10:11:06 +02:00
trim21 8598873627 Replace rak::regex with fnmatch from POSIX <fnmatch.h>
rak::regex was a hand-rolled glob matcher supporting only '*'
wildcards. Replace it with the standard POSIX fnmatch() which supports
the same glob patterns plus '?' and '[...]' character classes.

- src/core/manager.cc: use fnmatch for directory entry filtering
- src/command_download.cc: use fnmatch for file path pattern matching
- Makefile.am: remove rak/regex.h from EXTRA_DIST
- rak/regex.h: deleted, no longer needed
2026-06-25 09:47:11 +02:00
rakshasa ce4cd27697 Tagged release 0.16.15. 2026-06-22 10:28:54 +02:00
Jari Sundell 59fe93515e Only enable SIGCHLD on main thread. 2026-06-21 23:05:16 +09:00
Jari Sundell 8249070ff1 Remove shutdown comments for directory watch
Remove commented code regarding directory watch closure during shutdown.
2026-06-19 10:29:43 +02:00
Xirvik Support 517454b413 control: close directory watch on quick shutdown (fix SIGABRT at exit)
Control::handle_shutdown() closes m_directory_events (the inotify directory
watch) only in the normal-shutdown branch (!m_shutdownQuick). On a quick
shutdown (SIGTERM -> receive_quick_shutdown) the m_shutdownQuick branch skips
it, so the watch stays registered in the poll. ~Control() then destroys the
still-open directory_events and Event::~Event()'s assert(m_poll_event == nullptr)
(added with the 0.16.13 poll/event rework) aborts:

    main -> Control::~Control() -> ~directory_events -> ~Event() -> abort

Close it in Control::cleanup() instead, which runs on every shutdown path after
the session is saved; close() is idempotent, so the normal path that already
closed it in handle_shutdown() is unaffected.

Reproducible: configure a directory.watch.added watch, then SIGTERM the client
-> SIGABRT (core in Event::~Event); SIGINT (normal shutdown) is clean. With this
change SIGTERM exits cleanly too.
2026-06-19 10:29:43 +02:00
Jari Sundell a79aaad9ab Clean up comments in InputEvent methods
Removed commented-out code regarding error handling for stdin and event processing.
2026-06-19 09:12:22 +02:00
Xirvik Support c6de871a77 input: survive controlling-terminal/pty hangup on stdin
Since the 0.16.13 callback/poll rework, an EPOLLERR on stdin (controlling
terminal or pty hangup) reaches Poll::process(), which aborts the whole
client with an internal_error because InputEvent never registered for error
events:

    Poll::process() received error event for event not in error: input-fd:0

Register stdin for error events (insert_error) and handle event_error() by
dropping stdin from the poll set with this_thread::poll()->remove_and_close().
rtorrent then keeps running without keyboard input instead of dying.

remove() guards on the fd state (is_open()), mirroring SCgiTask: event_error()
clears the fd after remove_and_close(), and the shutdown path (Control::cleanup)
still calls remove(); without the guard the second remove_and_close() throws
'event not found' via event_mask(). insert()/remove() take the thread poll
implicitly (this_thread::poll()) instead of a Poll* argument.
2026-06-19 09:12:22 +02:00
Jari Sundell acb02379b8 Use a cache for free diskspace when sync'ing all downloads. 2026-06-18 23:45:55 +09:00
trim21 bf74686c29 fix: replace reinterpret_cast UB in command_base with aligned placement new
Replace the union-based reinterpret_cast type erasure in command_base
with an alignas char buffer + typed copy/destroy helper pointers.

set_function<T>() placement-news the correct std::function<T> type
at the buffer address, and stores per-type copy/destroy helpers so
that the copy ctor, assignment, and destructor always operate on the
actual type rather than assuming base_function.

_reinterpret_cast<T&> access of t_pod remains zero-overhead and is
now well-defined because the object was constructed at that address
as T via placement new.

Fixes #1818
2026-06-18 11:05:58 +02:00
Jari Sundell dbe7997131 Use posix_spawn for execute commands. 2026-06-18 01:01:52 +09:00
Jari Sundell d595ebf7d8 Renamed scgi socket manager category to rpc. 2026-06-17 03:43:26 +09:00
Jari Sundell 08828045f5 Added min/max alloc for SocketManager categories. 2026-06-16 22:39:39 +09:00
Jari Sundell 662d67e861 Cleaned up if/branch commands and made branch types stricter. 2026-06-15 18:55:30 +09:00
Jari Sundell d08d7de20d Added "system.sockets.<category>.{size,max_size}" commands. 2026-06-15 18:38:54 +09:00
rakshasa 83bdc271a4 Tagged release 0.16.14. 2026-06-14 09:38:10 +02:00
Jari Sundell 29edda2f15 Removed address based throttling. 2026-06-13 22:53:42 +09:00
Jari Sundell fd69baa2c1 Removed encoding name/path attribute handling. 2026-06-13 18:37:19 +09:00
Jari Sundell 1603ba0573 Use std::chrono::seconds in TrackerState. 2026-06-12 19:50:17 +09:00
trim21 19305ab662 fix: define LUA_OK for Lua 5.1 and LuaJIT compatibility
LUA_OK was introduced in Lua 5.2. Define it as 0 for compatibility
with Lua 5.1 and LuaJIT (which is based on Lua 5.1).
2026-06-12 09:59:42 +02:00
trim21 5b2e202452 fix: scheduler front()->time() -> front()->time after SchedulerHandle change
SchedulerHandle::time is now a data member instead of a method, so
front()->time() must be front()->time (no parentheses).
2026-06-11 18:31:17 +02:00
trim21 c63db9cf97 refactor: use torrent::heuristics_enum instead of choke_queue::heuristics_enum
Update to use the new top-level torrent::heuristics_enum from
torrent/download/types.h, removing the dependency on choke_queue.h
for enum values.
2026-06-10 13:02:06 +02:00
trim21 72f2197add fix: define default_resume_flags sentinel, clearing open_enable_fallocate
Replace the raw ~uint32_t sentinel with a named constant
Download::default_resume_flags that masks out the open_enable_fallocate
bit. This prevents flag_fallocate from being set on all files when
open_throw() reads resume_flags() before explicit flags are configured.

The sentinel value (~uint32_t & ~open_enable_fallocate) retains the
full range as a 'not set' marker while being safe to pass through
Download::open() without unintended fallocate.
2026-06-09 10:50:40 +02:00
fffe e96f594afc Added missing maybe_unuseds attributes and fixed pkg-config check by @fffe 2026-06-09 17:22:11 +09:00
trim21 bc8d0da70f set d.message on hash check failure
Use Download::hash_error_message() to get a descriptive error string when
hash check fails due to I/O error, instead of relying on errno which could
be zero.

Also fix Manager::receive_hashing_changed() where set_hash_failed(true) was
called without setting d.message when catching local_error during hash
check (e.g. 'too many open files').
2026-06-05 15:58:11 +02:00
Jari Sundell 75377817b4 Deprecate commands and set DHT to auto by default. 2026-06-05 21:50:08 +09:00
Jari Sundell 595617c408 Override destructor in ElementLogComplete class. 2026-06-05 10:59:54 +02:00
Jindrich Makovicka 578fe03ad6 Unregister destroyed ElementLogComplete and WindowLog 2026-06-05 10:59:54 +02:00
Xirvik efe258a137 fix(rpc): preserve c_str() stability of stored XMLRPC method names
Commit 6488131 ("Fix RPC/SCGI security and crash bugs by @sirus20x6")
replaced std::vector<std::unique_ptr<const char>> storage with
std::vector<std::string> and returned back().c_str() to the xmlrpc-c
registry as the per-method server_info pointer.

This is unsafe for any method name short enough to be SSO-stored
(<= 15 chars on libstdc++): such a string keeps its buffer inside the
std::string object itself. When a later push_back reallocates the
vector and move-constructs the existing elements into a new buffer,
the previously returned c_str() pointers — captured by xmlrpc-c at
registration time — dangle into freed memory.

Because xmlrpc-c does not dereference server_info until a call
dispatches, the failure surfaces later as nondeterministic garbage
in fault strings, e.g.

  faultString: Command "thod." does not exist.    (load.start, log.xmlrpc, log.execute)
  faultString: Command "in_rate" does not exist.  (log.add_output)
  faultString: Command ""       does not exist.   (log.open_file)
  faultString: Command "+U"     does not exist.   (method.set_key)

Long-named methods (e.g. system.client_version at 21 chars) are
heap-allocated above the SSO threshold and escape the bug because
the heap buffer's address is preserved across the vector move.

Switch the storage to std::deque<std::string>: per [deque.modifiers]
push_back does not invalidate references to existing elements, so
the std::string objects do not move and the c_str() pointers handed
to xmlrpc-c remain valid for the program's lifetime. The body of
store_command_name is unchanged.

Fixes the use-after-free; preserves the std::string-based storage
the original commit aimed for.
2026-06-05 10:55:51 +02:00
Jari Sundell 44d39713d5 Clean out old cruft in autoconf scripts. 2026-06-05 00:46:47 +09:00
97 changed files with 2928 additions and 1729 deletions
+65
View File
@@ -0,0 +1,65 @@
name: "Lint Commit Message Size"
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
check-commit-bounds:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Validate Line Count, Width, and Spacing
run: |
# Fetch commit hashes unique to this PR branch
COMMITS=$(git log --no-merges --pretty=format:"%H" origin/${{ github.base_ref }}..HEAD)
MAX_LINES=3
MAX_CHARS=90
FAILED=0
for commit in $COMMITS; do
SUBJECT=$(git log --format="%s" -n 1 $commit)
# Extract clean commit message, trimming trailing blank lines
RAW_MSG=$(git log --format="%B" -n 1 $commit)
CLEAN_MSG=$(echo "$RAW_MSG" | awk '{msg[NR]=$0} END {while(NR>0 && msg[NR]=="") NR--; for(i=1;i<=NR;i++) print msg[i]}')
# 1. Check total line count
LINE_COUNT=$(echo "$CLEAN_MSG" | wc -l)
if [ "$LINE_COUNT" -gt "$MAX_LINES" ]; then
echo "❌ Error: Commit message has too many lines ($LINE_COUNT/$MAX_LINES)."
echo " Commit: '$SUBJECT'"
FAILED=1
fi
# 2. Check for multiple consecutive blank lines
# This regex looks for 2 or more empty lines anywhere in the message
if echo "$CLEAN_MSG" | grep -Pz '(\r?\n){3,}'; then
echo "❌ Error: Commit message contains multiple consecutive line breaks."
echo " Commit: '$SUBJECT'"
FAILED=1
fi
# 3. Check maximum width of any individual line
while IFS= read -r line; do
LINE_LENGTH=${#line}
if [ "$LINE_LENGTH" -gt "$MAX_CHARS" ]; then
echo "❌ Error: Line length exceeds limit ($LINE_LENGTH / $MAX_CHARS chars)."
echo " Offending line: '$line'"
FAILED=1
fi
done <<< "$CLEAN_MSG"
done
# Fail the job if any check failed
if [ "$FAILED" -ne 0 ]; then
exit 1
fi
echo "✅ All commit messages passed style rules!"
+6
View File
@@ -37,6 +37,9 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: ubuntu-base needs: ubuntu-base
steps: steps:
- name: Update Packages
run: |
sudo apt-get update
- name: Install Dependencies - name: Install Dependencies
run: | run: |
sudo apt-get install -y \ sudo apt-get install -y \
@@ -75,6 +78,9 @@ jobs:
matrix: matrix:
config_flag: ["--with-xmlrpc-c", "--with-xmlrpc-tinyxml2"] config_flag: ["--with-xmlrpc-c", "--with-xmlrpc-tinyxml2"]
steps: steps:
- name: Update Packages
run: |
sudo apt-get update
- name: Install Dependencies - name: Install Dependencies
run: | run: |
sudo apt-get install -y \ sudo apt-get install -y \
-1
View File
@@ -7,7 +7,6 @@ nobase_dist_pkgdata_DATA = \
lua/rtorrent.lua lua/rtorrent.lua
EXTRA_DIST= \ EXTRA_DIST= \
rak/regex.h \
scripts/checks.m4 \ scripts/checks.m4 \
scripts/common.m4 \ scripts/common.m4 \
scripts/attributes.m4 scripts/attributes.m4
+8 -13
View File
@@ -1,6 +1,6 @@
m4_pattern_allow([PKG_CHECK_EXISTS]) m4_pattern_allow([PKG_CHECK_EXISTS])
AC_INIT([rtorrent],[0.16.13],[sundell.software@gmail.com]) AC_INIT([rtorrent],[0.16.20],[sundell.software@gmail.com])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIRS([scripts]) AC_CONFIG_MACRO_DIRS([scripts])
@@ -10,15 +10,11 @@ AM_PROG_AR
LT_INIT LT_INIT
AC_PROG_CXX AC_PROG_CXX
AC_DEFINE([API_VERSION], [21], [api version])
# Filter out unwanted flags added by autoconf on some systems, e.g. MacOS.
TORRENT_REMOVE_UNWANTED(CXX, $CXX, -std=c++11 -std=gnu++11)
AX_CXX_COMPILE_STDCXX(20, noext, mandatory) AX_CXX_COMPILE_STDCXX(20, noext, mandatory)
AC_SYS_LARGEFILE PKG_PROG_PKG_CONFIG
AC_DEFINE([API_VERSION], [25], [api version])
RAK_CHECK_CFLAGS RAK_CHECK_CFLAGS
RAK_CHECK_CXXFLAGS RAK_CHECK_CXXFLAGS
@@ -26,8 +22,7 @@ RAK_ENABLE_DEBUG
RAK_ENABLE_EXTRA_DEBUG RAK_ENABLE_EXTRA_DEBUG
RAK_ENABLE_WERROR RAK_ENABLE_WERROR
TORRENT_ENABLE_ARCH TORRENT_CHECK_ATOMIC
TORRENT_WITH_SYSROOT
AC_ARG_ENABLE(execinfo, AC_ARG_ENABLE(execinfo,
AS_HELP_STRING([--disable-execinfo], AS_HELP_STRING([--disable-execinfo],
@@ -54,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.13]) PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.20])
AC_LANG_PUSH(C++) AC_LANG_PUSH(C++)
TORRENT_WITH_XMLRPC_C TORRENT_WITH_XMLRPC_C
@@ -75,12 +70,12 @@ AC_DEFINE(USER_AGENT, [std::string(PACKAGE "/" VERSION)], Http user agent)
dnl Only update global build variables immediately before generating the output, dnl Only update global build variables immediately before generating the output,
dnl to avoid affecting the global build environment for other autoconf checks. dnl to avoid affecting the global build environment for other autoconf checks.
LIBS="$PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $ZLIB_LIBS $DEPENDENCIES_LIBS $LIBS" LIBS="$ATOMIC_LIBS $PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $ZLIB_LIBS $DEPENDENCIES_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS"
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS" CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS"
TORRENT_CHECK_CACHELINE TORRENT_CHECK_CACHELINE
TORRENT_CHECK_POPCOUNT TORRENT_CHECK_POSIX_SPAWN_ADDCLOSEFROM_NP
AC_CONFIG_FILES([ AC_CONFIG_FILES([
Makefile Makefile
+60
View File
@@ -0,0 +1,60 @@
# Resolving paths
Commands that return a path have `.realpath` variants that resolve it to a
canonical one, with symlinks followed and any `.` or `..` components removed.
The intent is to make paths safer to hand to an external script.
A script called through `execute` receives whatever path rtorrent gives it, and
many scripts do no sanity checking of their own, so resolving the path before it
leaves rtorrent removes a class of surprises: a download directory that is a
symlink into somewhere unexpected, or a torrent whose name walks upwards out of
the directory it is supposed to live in.
# Instead of this
execute = ~/bin/on-finished, (d.base_path)
# Pass the resolved path
execute = ~/bin/on-finished, (d.base_path.realpath.or_throw)
## Available variants
Each command below comes in an `.or_empty` and an `.or_throw` form.
| Command | Resolves |
| --- | --- |
| `d.base_path.realpath.*` | `d.base_path` |
| `d.directory.realpath.*` | `d.directory` |
| `d.tied_to_file.realpath.*` | `d.tied_to_file` |
| `d.loaded_file.realpath.*` | `d.loaded_file` |
| `f.frozen_path.realpath.*` | `f.frozen_path` |
| `session.path.realpath.*` | `session.path` |
| `directory.default.realpath.*` | `directory.default` |
A leading `~` is expanded first, exactly as it is for `execute`, so
`~/downloads` resolves the same way it would on the command line.
## Paths that do not exist
Resolving requires the path to name an existing file or directory, which is
often not the case for a download whose data has not been written yet.
The two forms differ only in what they do about it.
`.or_empty` returns an empty string:
print = (d.base_path.realpath.or_empty) # ""
This keeps `d.multicall` usable over a view that mixes started and unstarted
downloads, since a single unresolvable path does not abort the whole call.
A script receiving one of these paths should still check that it is not empty
before acting on it.
`.or_throw` raises an error instead:
print = (d.base_path.realpath.or_throw) # Could not resolve path: '...'
Prefer this one wherever an unresolvable path means the command should not run
at all, such as a single `execute` on `event.download.finished`.
Note that a download only has a file list once it has been opened, so
`d.base_path` and `f.frozen_path` are empty at `event.download.inserted` time
and their `.realpath` variants resolve nothing.
+139
View File
@@ -0,0 +1,139 @@
# String functions
The `string.*` commands manipulate and inspect text from within the
configuration file.
They are pure functions with no side effects, so they can be nested freely and
are safe to call over RPC.
Every argument is converted to its string representation before use, which means
numbers can be passed where text is expected.
Commands that count characters, such as `string.length` and `string.substr`, count
utf-8 characters rather than bytes.
## string.length
# string.length = «text»
string.length = "héllo" # 5
Returns the number of utf-8 characters in the text.
## string.equals
# string.equals = «text», «other»[, ...]
string.equals = (d.name), "first.iso", "second.iso"
Returns `1` if the first argument equals any of the following arguments,
otherwise `0`.
## string.starts_with, string.ends_with
# string.starts_with = «text», «prefix»[, ...]
# string.ends_with = «text», «tail»[, ...]
string.starts_with = (t.url), "http://", "https://"
string.ends_with = (d.name), ".iso"
Returns `1` if the text begins, or ends, with any of the given prefixes or
tails.
## string.contains, string.contains_i
# string.contains = «haystack», «needle»[, ...]
# string.contains_i = «haystack», «needle»[, ...]
string.contains = (t.url), "retracker.local"
string.contains_i = (t.url), "RETRACKER.local"
Returns `1` if the haystack contains any of the needles.
The `_i` variant compares case-insensitively, and only handles ascii.
## string.substr
# string.substr = «text»[, «position»[, «count»[, «default»]]]
string.substr = "abcdef", 2, 3 # "cde"
string.substr = "abcdef", -2 # "ef"
string.substr = "abcdef", 10, 1, "?" # "?"
Extracts a part of the text, starting at `position` and spanning `count`
characters.
The position defaults to the start of the text, and the count to the rest of it.
A negative position is relative to the end of the text.
If the position falls outside the text, the default value is returned instead,
which is the empty string unless given.
## string.split
# string.split = «text», «delimiter»
string.split = "a.b.c", "." # {"a", "b", "c"}
string.split = "abc", "" # {"a", "b", "c"}
Splits the text into a list, keeping empty fields.
An empty delimiter splits the text into its utf-8 characters.
## string.join
# string.join = «delimiter»[, «object»[, ...]]
string.join = "-", (string.split, "a.b.c", ".") # "a-b-c"
Concatenates the objects, inserting the delimiter between them.
Lists are flattened, so the result of `string.split` can be joined back
together.
## string.lpad, string.rpad
# string.lpad = «text», «length»[, «padding»]
# string.rpad = «text», «length»[, «padding»]
string.lpad = 7, 3, 0 # "007"
string.rpad = "a", 3 # "a "
Pads the text at the start, or the end, until it is `length` characters long.
The padding defaults to a single space and is repeated as needed.
Text that is already long enough is returned unchanged.
## string.strip, string.lstrip, string.rstrip
# string.strip = «text»[, «strippable»[, ...]]
# string.lstrip = «text»[, «head»[, ...]]
# string.rstrip = «text»[, «tail»[, ...]]
string.strip = " padded " # "padded"
string.strip = "//path//", "/" # "path"
Removes characters from both ends of the text, or from only the start or the
end.
The arguments after the text form a set of utf-8 characters to remove.
Without them, whitespace is removed.
## string.map
# string.map = «text», {«old», «new»}[, ...]
string.map = (d.state), {0, "stopped"}, {1, "started"}
Returns the replacement of the first pair whose `old` value equals the whole
text.
If no pair matches, the text is returned unchanged.
## string.replace
# string.replace = «text», {«old», «new»}[, ...]
string.replace = "a-b-c", {"-", "+"} # "a+b+c"
Replaces every occurrence of `old` with `new`.
The pairs are applied in order, so a later pair operates on the result of the
earlier ones.
## Example: dropping unwanted trackers
The following disables every tracker that points at `retracker.local` as soon as
a download is inserted.
method.set_key = event.download.inserted, drop_retracker, \
((t.multicall, default, "branch=(string.contains,(t.url),retracker.local),((t.disable))"))
+2 -2
View File
@@ -71,11 +71,11 @@
# Port range to use for listening. # Port range to use for listening.
# #
#network.port_range.set = 6890-6999 #network.listen.port.range.set = 6890-6999
# Start opening ports at a random position within the port range. # Start opening ports at a random position within the port range.
# #
#network.port_random.set = no #network.listen.port.random.set = no
# Set RPC type # Set RPC type
#network.rpc.use_xmlrpc.set = true #network.rpc.use_xmlrpc.set = true
+2 -2
View File
@@ -23,8 +23,8 @@ execute.throw = sh, -c, (cat,\
"\"",(cfg.watch),"/start\" ") "\"",(cfg.watch),"/start\" ")
# Listening port for incoming peer traffic (fixed; you can also randomize it) # Listening port for incoming peer traffic (fixed; you can also randomize it)
network.port_range.set = 50000-50000 network.listen.port.range.set = 50000-50000
network.port_random.set = no network.listen.port.random.set = no
# Tracker-less torrent and UDP tracker support # Tracker-less torrent and UDP tracker support
# (conservative settings for 'private' trackers, change for 'public') # (conservative settings for 'private' trackers, change for 'public')
+2 -2
View File
@@ -53,8 +53,8 @@ rc.execute.throw(
cfg.watch..'/start'})) cfg.watch..'/start'}))
-- Listening port for incoming peer traffic (fixed; you can also randomize it) -- Listening port for incoming peer traffic (fixed; you can also randomize it)
rc.network.port_range = '50000-50000' rc.network.listen.port.range = '50000-50000'
rc.network.port_random = false rc.network.listen.port.random = false
-- Tracker-less torrent and UDP tracker support -- Tracker-less torrent and UDP tracker support
-- (conservative settings for 'private' trackers, change for 'public') -- (conservative settings for 'private' trackers, change for 'public')
-109
View File
@@ -1,109 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, 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 <sundell.software@gmail.com>
// This is a hacked up whole string pattern matching. Replace with
// TR1's regex when that becomes widely available. It is intended for
// small strings.
#ifndef RAK_REGEX_H
#define RAK_REGEX_H
#include <sys/types.h>
#include <algorithm>
#include <functional>
#include <string>
#include <list>
namespace rak {
class regex {
public:
regex() {}
regex(const std::string& p) : m_pattern(p) {}
const std::string& pattern() const { return m_pattern; }
bool operator () (const std::string& p) const;
private:
std::string m_pattern;
};
// This isn't optimized, or very clean. A simple hack that should work.
inline bool
regex::operator () (const std::string& text) const {
if (m_pattern.empty() ||
text.empty() ||
(m_pattern[0] != '*' && m_pattern[0] != text[0]))
return false;
// Replace with unordered_vector?
std::list<unsigned int> paths;
paths.push_front(0);
for (std::string::const_iterator itrText = ++text.begin(), lastText = text.end(); itrText != lastText; ++itrText) {
for (std::list<unsigned int>::iterator itrPaths = paths.begin(), lastPaths = paths.end(); itrPaths != lastPaths; ) {
unsigned int next = *itrPaths + 1;
if (m_pattern[*itrPaths] != '*')
itrPaths = paths.erase(itrPaths);
else
itrPaths++;
// When we reach the end of 'm_pattern', we don't have a whole
// match of 'text'.
if (next == m_pattern.size())
continue;
// Push to the back so that '*' will match zero length strings.
if (m_pattern[next] == '*')
paths.push_back(next);
if (m_pattern[next] == *itrText)
paths.push_front(next);
}
if (paths.empty())
return false;
}
return std::find(paths.begin(), paths.end(), m_pattern.size() - 1) != paths.end();
}
}
#endif
+54 -229
View File
@@ -1,48 +1,3 @@
AC_DEFUN([TORRENT_CHECK_XFS], [
AC_MSG_CHECKING(for XFS support)
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <xfs/libxfs.h>
#include <sys/ioctl.h>
int main() {
struct xfs_flock64 l;
ioctl(0, XFS_IOC_RESVSP64, &l);
return 0;
}
])],
[
AC_DEFINE(USE_XFS, 1, Use XFS filesystem stuff.)
AC_MSG_RESULT(yes)
], [
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_WITHOUT_XFS], [
AC_ARG_WITH(xfs,
AS_HELP_STRING([--without-xfs],[do not check for XFS filesystem support]),
[
if test "$withval" = "yes"; then
TORRENT_CHECK_XFS
fi
], [
TORRENT_CHECK_XFS
])
])
AC_DEFUN([TORRENT_WITH_XFS], [
AC_ARG_WITH(xfs,
AS_HELP_STRING([--with-xfs],[check for XFS filesystem support]),
[
if test "$withval" = "yes"; then
TORRENT_CHECK_XFS
fi
])
])
AC_DEFUN([TORRENT_CHECK_EPOLL], [ AC_DEFUN([TORRENT_CHECK_EPOLL], [
AC_MSG_CHECKING(for epoll support) AC_MSG_CHECKING(for epoll support)
@@ -122,158 +77,9 @@ AC_DEFUN([TORRENT_WITHOUT_KQUEUE], [
]) ])
AC_DEFUN([TORRENT_CHECK_FALLOCATE], [
AC_MSG_CHECKING(for fallocate)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#define _GNU_SOURCE
#include <fcntl.h>
]], [[ fallocate(0, FALLOC_FL_KEEP_SIZE, 0, 0); return 0;
]])],[
AC_DEFINE(USE_FALLOCATE, 1, Linux's fallocate supported.)
AC_MSG_RESULT(yes)
],[
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_CHECK_POSIX_FALLOCATE], [
AC_MSG_CHECKING(for posix_fallocate)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <fcntl.h>
]], [[ posix_fallocate(0, 0, 0);
]])],[
AC_DEFINE(USE_POSIX_FALLOCATE, 1, posix_fallocate supported.)
AC_MSG_RESULT(yes)
],[
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_WITH_POSIX_FALLOCATE], [
AC_ARG_WITH(posix-fallocate,
AS_HELP_STRING([--with-posix-fallocate],[check for and use posix_fallocate to allocate files]),
[
if test "$withval" = "yes"; then
TORRENT_CHECK_POSIX_FALLOCATE
fi
])
])
AC_DEFUN([TORRENT_CHECK_STATVFS], [
AC_CHECK_HEADERS(sys/vfs.h sys/statvfs.h sys/statfs.h)
AC_MSG_CHECKING(for statvfs)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#if HAVE_SYS_VFS_H
#include <sys/vfs.h>
#endif
#if HAVE_SYS_STATVFS_H
#include <sys/statvfs.h>
#endif
#if HAVE_SYS_STATFS_H
#include <sys/statfs.h>
#endif
]], [[
struct statvfs s; fsblkcnt_t c;
statvfs("", &s);
fstatvfs(0, &s);
]])],[
AC_DEFINE(FS_STAT_FD, [fstatvfs(fd, &m_stat) == 0], Function to determine filesystem stats from fd)
AC_DEFINE(FS_STAT_FN, [statvfs(fn, &m_stat) == 0], Function to determine filesystem stats from filename)
AC_DEFINE(FS_STAT_STRUCT, [struct statvfs], Type of second argument to statfs function)
AC_DEFINE(FS_STAT_SIZE_TYPE, [unsigned long], Type of block size member in stat struct)
AC_DEFINE(FS_STAT_COUNT_TYPE, [fsblkcnt_t], Type of block count member in stat struct)
AC_DEFINE(FS_STAT_BLOCK_SIZE, [(m_stat.f_frsize)], Determine the block size)
AC_MSG_RESULT(ok)
have_stat_vfs=yes
],[
AC_MSG_RESULT(no)
have_stat_vfs=no
])
])
AC_DEFUN([TORRENT_CHECK_STATFS], [
AC_CHECK_HEADERS(sys/statfs.h)
AC_MSG_CHECKING(for statfs)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#if HAVE_SYS_STATFS_H
#include <sys/statfs.h>
#endif
]], [[
struct statfs s;
statfs("", &s);
fstatfs(0, &s);
]])],[
AC_DEFINE(FS_STAT_FD, [fstatfs(fd, &m_stat) == 0], Function to determine filesystem stats from fd)
AC_DEFINE(FS_STAT_FN, [statfs(fn, &m_stat) == 0], Function to determine filesystem stats from filename)
AC_DEFINE(FS_STAT_STRUCT, [struct statfs], Type of second argument to statfs function)
AC_DEFINE(FS_STAT_SIZE_TYPE, [long], Type of block size member in stat struct)
AC_DEFINE(FS_STAT_COUNT_TYPE, [long], Type of block count member in stat struct)
AC_DEFINE(FS_STAT_BLOCK_SIZE, [(m_stat.f_bsize)], Determine the block size)
AC_MSG_RESULT(ok)
have_stat_vfs=yes
],[
AC_MSG_RESULT(no)
have_stat_vfs=no
])
])
AC_DEFUN([TORRENT_DISABLED_STATFS], [
AC_DEFINE(FS_STAT_FD, [(errno = ENOSYS) == 0], Function to determine filesystem stats from fd)
AC_DEFINE(FS_STAT_FN, [(errno = ENOSYS) == 0], Function to determine filesystem stats from filename)
AC_DEFINE(FS_STAT_STRUCT, [struct {blocksize_type f_bsize; blockcount_type f_bavail;}], Type of second argument to statfs function)
AC_DEFINE(FS_STAT_SIZE_TYPE, [int], Type of block size member in stat struct)
AC_DEFINE(FS_STAT_COUNT_TYPE, [int], Type of block count member in stat struct)
AC_DEFINE(FS_STAT_BLOCK_SIZE, [(4096)], Determine the block size)
AC_MSG_RESULT(No filesystem stats available)
])
AC_DEFUN([TORRENT_WITHOUT_STATVFS], [
AC_ARG_WITH(statvfs,
AS_HELP_STRING([--without-statvfs],[don't try to use statvfs to find free diskspace]),
[
if test "$withval" = "yes"; then
TORRENT_CHECK_STATVFS
else
have_stat_vfs=no
fi
],
[
TORRENT_CHECK_STATVFS
])
])
AC_DEFUN([TORRENT_WITHOUT_STATFS], [
AC_ARG_WITH(statfs,
AS_HELP_STRING([--without-statfs],[don't try to use statfs to find free diskspace]),
[
if test "$have_stat_vfs" = "no"; then
if test "$withval" = "yes"; then
TORRENT_CHECK_STATFS
else
TORRENT_DISABLED_STATFS
fi
fi
],
[
if test "$have_stat_vfs" = "no"; then
TORRENT_CHECK_STATFS
if test "$have_stat_vfs" = "no"; then
TORRENT_DISABLED_STATFS
fi
fi
])
])
AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [ AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [
AC_ARG_WITH(address-space, AC_ARG_WITH(address-space,
AS_HELP_STRING([--with-address-space=MB],[change the default address space size [[default=1024mb]]]), AS_HELP_STRING([--with-address-space=MB],[change the default address space size [default=1024mb-or-32768mb]]),
[ [
if test ! -z $withval -a "$withval" != "yes" -a "$withval" != "no"; then if test ! -z $withval -a "$withval" != "yes" -a "$withval" != "no"; then
AC_DEFINE_UNQUOTED(DEFAULT_ADDRESS_SPACE_SIZE, [$withval]) AC_DEFINE_UNQUOTED(DEFAULT_ADDRESS_SPACE_SIZE, [$withval])
@@ -285,51 +91,48 @@ AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [
AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long)
if test $ac_cv_sizeof_long = 8; then if test $ac_cv_sizeof_long = 8; then
AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 4096, Default address space size.) AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 32768, Default address space size.)
else else
AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 1024, Default address space size.) AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 1024, Default address space size.)
fi fi
]) ])
]) ])
AC_DEFUN([TORRENT_WITH_FASTCGI], [
AC_ARG_WITH(fastcgi, AC_DEFUN([TORRENT_CHECK_ATOMIC], [
AS_HELP_STRING([--with-fastcgi=PATH],[enable FastCGI RPC support (DO NOT USE)]), AC_MSG_CHECKING([whether 64-bit atomic operations require -latomic])
AC_LANG_PUSH(C++)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <cstdint>
#include <atomic>]],
[[std::atomic<uint64_t> x(0);
return x.load();]])],
[ [
AC_MSG_CHECKING([for FastCGI (DO NOT USE)]) AC_MSG_RESULT([no])
ATOMIC_LIBS=""
],
[
save_LIBS="$LIBS"
LIBS="$LIBS -latomic"
if test "$withval" = "no"; then AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <cstdint>
AC_MSG_RESULT(no) #include <atomic>]],
[[std::atomic<uint64_t> x(0);
elif test "$withval" = "yes"; then return x.load();]])],
CXXFLAGS="$CXXFLAGS" [
LIBS="$LIBS -lfcgi" AC_MSG_RESULT([yes])
ATOMIC_LIBS="-latomic"
AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <fcgiapp.h> ],
]], [[ FCGX_Init(); ]])],[ [
AC_MSG_RESULT(ok) AC_MSG_RESULT([unsupported])
],[ AC_MSG_ERROR([Compiler target lacks proper 64-bit atomic support.])
AC_MSG_RESULT(not found)
AC_MSG_ERROR(Could not compile FastCGI test.)
]) ])
AC_DEFINE(HAVE_FASTCGI, 1, Support for FastCGI.) LIBS="$save_LIBS"
else
CXXFLAGS="$CXXFLAGS -I$withval/include"
LIBS="$LIBS -lfcgi -L$withval/lib"
AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <fcgiapp.h>
]], [[ FCGX_Init(); ]])],[
AC_MSG_RESULT(ok)
],[
AC_MSG_RESULT(not found)
AC_MSG_ERROR(Could not compile FastCGI test.)
])
AC_DEFINE(HAVE_FASTCGI, 1, Support for FastCGI.)
fi
]) ])
AC_LANG_POP(C++)
AC_SUBST([ATOMIC_LIBS])
]) ])
@@ -374,6 +177,7 @@ AC_DEFUN([TORRENT_WITH_XMLRPC_C], [
]) ])
]) ])
AC_DEFUN([TORRENT_WITH_TINYXML2], [ AC_DEFUN([TORRENT_WITH_TINYXML2], [
AC_MSG_CHECKING(for tinyxml2) AC_MSG_CHECKING(for tinyxml2)
@@ -387,6 +191,7 @@ AC_DEFUN([TORRENT_WITH_TINYXML2], [
]) ])
]) ])
AC_DEFUN([TORRENT_WITH_LUA], [ AC_DEFUN([TORRENT_WITH_LUA], [
AC_ARG_WITH(lua, AC_ARG_WITH(lua,
AS_HELP_STRING([--with-lua],[enable LUA support]), AS_HELP_STRING([--with-lua],[enable LUA support]),
@@ -441,6 +246,7 @@ AC_DEFUN([TORRENT_WITH_INOTIFY], [
AC_LANG_POP(C++) AC_LANG_POP(C++)
]) ])
AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [ AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [
AC_CHECK_HEADERS(pthread.h) AC_CHECK_HEADERS(pthread.h)
@@ -472,6 +278,7 @@ AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [
]) ])
]) ])
AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [ AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [
AC_MSG_CHECKING([for pthread_setname_no]) AC_MSG_CHECKING([for pthread_setname_no])
@@ -491,6 +298,24 @@ AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [
]) ])
AC_DEFUN([TORRENT_CHECK_POSIX_SPAWN_ADDCLOSEFROM_NP], [
AC_MSG_CHECKING(for posix_spawn_file_actions_addclosefrom_np)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#define _GNU_SOURCE
#include <spawn.h>
]], [[
posix_spawn_file_actions_t actions;
posix_spawn_file_actions_addclosefrom_np(&actions, 3);
]])],[
AC_DEFINE(HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP, 1, [Define if posix_spawn_file_actions_addclosefrom_np is available.])
AC_MSG_RESULT(yes)
],[
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_WITH_SYSTEMD], [ AC_DEFUN([TORRENT_WITH_SYSTEMD], [
AC_ARG_WITH(systemd, AC_ARG_WITH(systemd,
AS_HELP_STRING([--with-systemd],[enable systemd socket activation support [[default=no]]]), AS_HELP_STRING([--with-systemd],[enable systemd socket activation support [[default=no]]]),
+102 -127
View File
@@ -1,67 +1,3 @@
AC_DEFUN([TORRENT_WITH_SYSROOT], [
AC_ARG_WITH(sysroot,
AS_HELP_STRING([--with-sysroot=PATH],
[compile and link with a specific sysroot]),
[
AC_MSG_CHECKING(for sysroot)
if test "$withval" = "no"; then
AC_MSG_RESULT(no)
elif test "$withval" = "yes"; then
AC_MSG_RESULT(not a path)
AC_MSG_ERROR(The sysroot option must point to a directory, like f.ex "/Developer/SDKs/MacOSX10.4u.sdk".)
else
AC_MSG_RESULT($withval)
CXXFLAGS="$CXXFLAGS -isysroot $withval"
LDFLAGS="$LDFLAGS -Wl,-syslibroot,$withval"
fi
])
])
AC_DEFUN([TORRENT_REMOVE_UNWANTED],
[
AC_REQUIRE([AC_PROG_GREP])
values_to_check=`for i in $2; do echo $i; done`
unwanted_values=`for i in $3; do echo $i; done`
if test -z "${unwanted_values}"; then
$1="$2"
else
result=`echo "${values_to_check}" | $GREP -Fvx -- "${unwanted_values}" | $GREP -v '^$'`
# join with spaces, squeeze repeats, and trim trailing space
$1=$(printf '%s\n' "$result" | tr '\n' ' ' | sed 's/ */ /g; s/ *$//')
fi
])
AC_DEFUN([TORRENT_ENABLE_ARCH], [
AC_ARG_ENABLE(arch,
AS_HELP_STRING([--enable-arch=ARCH],
[comma seprated list of architectures to compile for]),
[
AC_MSG_CHECKING(for target architectures)
if test "$enableval" = "yes"; then
AC_MSG_ERROR(no arch supplied)
elif test "$enableval" = "no"; then
AC_MSG_RESULT(using default)
else
AC_MSG_RESULT($enableval)
for i in `IFS=,; echo $enableval`; do
CFLAGS="$CFLAGS -march=$i"
CXXFLAGS="$CXXFLAGS -march=$i"
LDFLAGS="$LDFLAGS -march=$i"
done
fi
])
])
AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [ AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [
AC_LANG_PUSH(C++) AC_LANG_PUSH(C++)
AC_MSG_CHECKING(signedness of mincore parameter) AC_MSG_CHECKING(signedness of mincore parameter)
@@ -90,7 +26,7 @@ AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [
AC_MSG_RESULT(signed) AC_MSG_RESULT(signed)
], ],
[ [
AC_MSG_ERROR([failed, do *not* attempt fix this with --disable-mincore unless you are running Win32.]) AC_MSG_ERROR([failed, do *not* attempt fix this with --disable-mincore unless you are running Win32 or OpenBSD.])
]) ])
]) ])
@@ -144,20 +80,6 @@ AC_DEFUN([TORRENT_CHECK_POSIX_FADVISE], [
]) ])
]) ])
AC_DEFUN([TORRENT_CHECK_POPCOUNT], [
AC_MSG_CHECKING(for __builtin_popcount)
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
int f() { return __builtin_popcount(0); }
])],
[
AC_MSG_RESULT(yes)
AC_DEFINE(USE_BUILTIN_POPCOUNT, 1, Use __builtin_popcount.)
], [
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_CHECK_CACHELINE], [ AC_DEFUN([TORRENT_CHECK_CACHELINE], [
AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_CANONICAL_HOST])
AC_MSG_CHECKING([for target cacheline size]) AC_MSG_CHECKING([for target cacheline size])
@@ -196,6 +118,14 @@ AC_DEFUN([TORRENT_CHECK_CACHELINE], [
AC_MSG_RESULT([linux fallback enterprise 128 bytes]) AC_MSG_RESULT([linux fallback enterprise 128 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 128, [Fallback 128-byte alignment for Linux enterprise hardware.]) AC_DEFINE([LT_SMP_CACHE_BYTES], 128, [Fallback 128-byte alignment for Linux enterprise hardware.])
;; ;;
riscv32*|riscv64*)
AC_MSG_RESULT([linux fallback RISC-V 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux RISC-V hardware.])
;;
loongarch32*|loongarch64*)
AC_MSG_RESULT([linux fallback LoongArch 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux LoongArch hardware.])
;;
*) *)
AC_MSG_RESULT([unrecognized CPU arch on Linux header fallback]) AC_MSG_RESULT([unrecognized CPU arch on Linux header fallback])
AC_MSG_FAILURE([Unrecognized CPU architecture ($host_cpu) on Linux fallback path. Aborting build.]) AC_MSG_FAILURE([Unrecognized CPU architecture ($host_cpu) on Linux fallback path. Aborting build.])
@@ -227,74 +157,119 @@ AC_DEFUN([TORRENT_CHECK_CACHELINE], [
esac esac
]) ])
AC_DEFUN([TORRENT_CHECK_ALIGNED], [
AC_MSG_CHECKING(the byte alignment)
AC_RUN_IFELSE([AC_LANG_SOURCE([ AC_DEFUN([TORRENT_CHECK_CUSTOM_ENDIAN64], [
#include <inttypes.h> AC_CHECK_HEADERS([endian.h sys/endian.h libkern/OSByteOrder.h])
int main() { AC_C_BIGENDIAN
char buf@<:@8@:>@ = { 0, 0, 0, 0, 1, 0, 0, 0 };
int i; AH_TEMPLATE([CUSTOM_ENDIAN_HEADER], [The system header to include for endian conversions.])
for (i = 1; i < 4; ++i) AH_TEMPLATE([custom_ntohll], [Convert 64-bit integer from network to host byte order.])
if (*(uint32_t*)(buf + i) == 0) return -1; AH_TEMPLATE([custom_htonll], [Convert 64-bit integer from host to network byte order.])
return 0;
} dnl 1. Initialize our header variable tracking
endian_header_file='<stdint.h>'
dnl 2. Test if native be64toh works natively via <endian.h> (Standard Linux/glibc)
AC_MSG_CHECKING([for native be64toh via endian.h])
AC_LINK_IFELSE([AC_LANG_SOURCE([
#include <stdint.h>
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif
int main() { uint64_t x = be64toh(1); return 0; }
])],
[has_native_be64=yes; endian_header_file='<endian.h>'],
[has_native_be64=no]
)
AC_MSG_RESULT([$has_native_be64])
dnl 3. If missing, test if it works via <sys/endian.h> (True BSD systems like FreeBSD)
if test "$has_native_be64" = "no"; then
AC_MSG_CHECKING([for native be64toh via sys/endian.h])
AC_LINK_IFELSE([AC_LANG_SOURCE([
#include <stdint.h>
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
int main() { uint64_t x = be64toh(1); return 0; }
])], ])],
[ [has_native_be64=yes; endian_header_file='<sys/endian.h>'],
AC_MSG_RESULT(none needed) [has_native_be64=no]
], [ )
AC_DEFINE(USE_ALIGNED, 1, Require byte alignment) AC_MSG_RESULT([$has_native_be64])
AC_MSG_RESULT(required) fi
])
dnl 4. Route definitions cleanly based on actual testing results
if test "$has_native_be64" = "yes"; then
dnl Linux or true BSD environment
AC_DEFINE_UNQUOTED([CUSTOM_ENDIAN_HEADER], [$endian_header_file], [The header containing native endian macros.])
AC_DEFINE([custom_ntohll(x)], [(be64toh(x))], [Use native platform be64toh])
AC_DEFINE([custom_htonll(x)], [(htobe64(x))], [Use native platform htobe64])
else
dnl Fallback block: Check if we are on macOS using Apple's optimized hardware libraries
if test "$ac_cv_header_libkern_OSByteOrder_h" = "yes"; then
AC_DEFINE([CUSTOM_ENDIAN_HEADER], [<libkern/OSByteOrder.h>], [The header containing native endian macros.])
AC_DEFINE([custom_ntohll(x)], [(OSSwapBigToHostInt64(x))], [Map missing be64toh to macOS native swap])
AC_DEFINE([custom_htonll(x)], [(OSSwapHostToBigInt64(x))], [Map missing htobe64 to macOS native swap])
else
dnl Fallback for platforms with no built-in architecture macros (compiler built-ins)
AC_DEFINE([CUSTOM_ENDIAN_HEADER], [<stdint.h>], [The header containing native endian macros.])
if test "$ac_cv_c_bigendian" = "yes"; then
AC_DEFINE([custom_ntohll(x)], [((uint64_t)(x))], [Fallback 64-bit conversion.])
AC_DEFINE([custom_htonll(x)], [((uint64_t)(x))], [Fallback 64-bit conversion.])
else
AC_DEFINE([custom_ntohll(x)], [(__builtin_bswap64((uint64_t)(x)))], [Fallback 64-bit conversion.])
AC_DEFINE([custom_htonll(x)], [(__builtin_bswap64((uint64_t)(x)))], [Fallback 64-bit conversion.])
fi
fi
fi
]) ])
AC_DEFUN([TORRENT_ENABLE_ALIGNED], [ AC_DEFUN([TORRENT_ENABLE_INSTRUMENTATION], [
AC_ARG_ENABLE(aligned,
AS_HELP_STRING([--enable-aligned],
[enable alignment safe code [[default=check]]]),
[
if test "$enableval" = "yes"; then
AC_DEFINE(USE_ALIGNED, 1, Require byte alignment)
fi
],[
TORRENT_CHECK_ALIGNED
])
])
AC_DEFUN([TORRENT_DISABLE_INSTRUMENTATION], [
AC_MSG_CHECKING([if instrumentation should be included]) AC_MSG_CHECKING([if instrumentation should be included])
AC_ARG_ENABLE(instrumentation, AC_ARG_ENABLE(instrumentation,
AS_HELP_STRING([--disable-instrumentation], AS_HELP_STRING([--enable-instrumentation],
[disable instrumentation [[default=enabled]]]), [enable instrumentation [[default=disabled]]]),
[ [
if test "$enableval" = "yes"; then if test "$enableval" = "yes"; then
AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation) AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation)
AC_MSG_RESULT(yes) AC_MSG_RESULT(yes)
else else
AC_MSG_RESULT(no) AC_MSG_RESULT(no)
fi fi
],[ ],[
AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation) AC_MSG_RESULT(no)
AC_MSG_RESULT(yes)
]) ])
]) ])
AC_DEFUN([TORRENT_ENABLE_INTERRUPT_SOCKET], [ AC_DEFUN([TORRENT_ENABLE_CUSTOM_STACK_SIZE], [
AC_ARG_ENABLE(interrupt-socket, AC_ARG_ENABLE([pthread-setstacksize],
AS_HELP_STRING([--enable-interrupt-socket], [AS_HELP_STRING([--enable-pthread-setstacksize], [explicitly set pthread stack size (auto-enabled for musl)])],
[enable interrupt socket [[default=no]]]), [enable_pthread_setstacksize=$enableval],
[ [enable_pthread_setstacksize=auto])
if test "$enableval" = "yes"; then
AC_DEFINE(USE_INTERRUPT_SOCKET, 1, Use interrupt socket instead of pthread_kill) AC_CANONICAL_HOST
fi if test "x$enable_pthread_setstacksize" = "xauto"; then
] case "$host" in
) *-musl*)
enable_pthread_setstacksize=yes
;;
*)
enable_pthread_setstacksize=no
;;
esac
fi
if test "x$enable_pthread_setstacksize" = "xyes"; then
AC_DEFINE([USE_PTHREAD_SETSTACKSIZE], [1], [Use explicit pthread stack size])
AC_DEFINE([DEFAULT_PTHREAD_STACKSIZE], [(8 * 1024 * 1024)], [Default pthread stack size in bytes])
fi
]) ])
AC_DEFUN([TORRENT_DISABLE_IPV6], [ AC_DEFUN([TORRENT_DISABLE_IPV6], [
AC_ARG_ENABLE(ipv6, AC_ARG_ENABLE(ipv6,
AS_HELP_STRING([--enable-ipv6], AS_HELP_STRING([--enable-ipv6],
+3
View File
@@ -174,6 +174,8 @@ libsub_root_a_SOURCES = \
utils/list_focus.h \ utils/list_focus.h \
utils/lockfile.cc \ utils/lockfile.cc \
utils/lockfile.h \ utils/lockfile.h \
utils/waitpid_queue.cc \
utils/waitpid_queue.h \
utils/watch_ready_queue.cc \ utils/watch_ready_queue.cc \
utils/watch_ready_queue.h \ utils/watch_ready_queue.h \
\ \
@@ -192,6 +194,7 @@ libsub_root_a_SOURCES = \
command_throttle.cc \ command_throttle.cc \
command_tracker.cc \ command_tracker.cc \
command_scheduler.cc \ command_scheduler.cc \
command_string.cc \
command_ui.cc \ command_ui.cc \
control.cc \ control.cc \
control.h \ control.h \
+39 -19
View File
@@ -5,11 +5,10 @@
#include <functional> #include <functional>
#include <netdb.h> #include <netdb.h>
#include <unistd.h> #include <unistd.h>
#include <rak/regex.h> #include <fnmatch.h>
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/throttle.h> #include <torrent/throttle.h>
#include <torrent/tracker/tracker.h> #include <torrent/tracker/tracker.h>
#include <torrent/connection_manager.h>
#include <torrent/data/download_data.h> #include <torrent/data/download_data.h>
#include <torrent/data/file.h> #include <torrent/data/file.h>
#include <torrent/data/file_list.h> #include <torrent/data/file_list.h>
@@ -164,8 +163,8 @@ apply_d_connection_type(core::Download* download, const std::string& name) {
torrent::Object torrent::Object
apply_d_choke_heuristics(core::Download* download, const std::string& name, bool is_down) { apply_d_choke_heuristics(core::Download* download, const std::string& name, bool is_down) {
torrent::Download::HeuristicType t = torrent::heuristics_enum t =
(torrent::Download::HeuristicType)torrent::option_find_string(torrent::OPTION_CHOKE_HEURISTICS, name.c_str()); static_cast<torrent::heuristics_enum>(torrent::option_find_string(torrent::OPTION_CHOKE_HEURISTICS, name.c_str()));
if (is_down) if (is_down)
download->download()->set_download_choke_heuristic(t); download->download()->set_download_choke_heuristic(t);
@@ -341,7 +340,7 @@ f_multicall(core::Download* download, const torrent::Object::list_type& args) {
// parsing and searching command map for every single call. // parsing and searching command map for every single call.
torrent::Object resultRaw = torrent::Object::create_list(); torrent::Object resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list(); torrent::Object::list_type& result = resultRaw.as_list();
std::vector<rak::regex> regex_list; std::vector<std::string> regex_list;
bool use_regex = true; bool use_regex = true;
@@ -355,7 +354,7 @@ f_multicall(core::Download* download, const torrent::Object::list_type& args) {
for (const auto& file : *download->file_list()) { for (const auto& file : *download->file_list()) {
if (use_regex && if (use_regex &&
std::none_of(regex_list.begin(), regex_list.end(), [&file](const auto& r) { return r(file->path()->as_string()); })) std::none_of(regex_list.begin(), regex_list.end(), [&file](const auto& pattern) { return fnmatch(pattern.c_str(), file->path()->as_string().c_str(), 0) == 0; }))
continue; continue;
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list(); torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
@@ -379,18 +378,19 @@ t_multicall(core::Download* download, const torrent::Object::list_type& args) {
// Add some pre-parsing of the commands, so we don't spend time // Add some pre-parsing of the commands, so we don't spend time
// parsing and searching command map for every single call. // parsing and searching command map for every single call.
torrent::Object result_raw = torrent::Object::create_list();
torrent::Object::list_type& result = result_raw.as_list(); auto result_raw = torrent::Object::create_list();
auto& result = result_raw.as_list();
for (uint32_t idx = 0, last = download->tracker_list_size(); idx < last; idx++) { for (uint32_t idx = 0, last = download->tracker_list_size(); idx < last; idx++) {
auto& row = result.insert(result.end(), torrent::Object::create_list())->as_list(); auto& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
auto tracker = download->tracker_controller().at(idx); auto tracker = download->tracker_controller().at(idx);
if (!tracker.is_valid()) if (!tracker.is_valid())
continue; continue;
for (torrent::Object::list_const_iterator cItr = ++args.begin(); cItr != args.end(); cItr++) { for (auto cItr = ++args.begin(); cItr != args.end(); cItr++) {
const std::string& cmd = cItr->as_string(); auto& cmd = cItr->as_string();
row.push_back(rpc::parse_command(rpc::make_target(&tracker), cmd.c_str(), cmd.c_str() + cmd.size()).first); row.push_back(rpc::parse_command(rpc::make_target(&tracker), cmd.c_str(), cmd.c_str() + cmd.size()).first);
} }
@@ -409,13 +409,13 @@ p_multicall(core::Download* download, const torrent::Object::list_type& args) {
// Add some pre-parsing of the commands, so we don't spend time // Add some pre-parsing of the commands, so we don't spend time
// parsing and searching command map for every single call. // parsing and searching command map for every single call.
torrent::Object resultRaw = torrent::Object::create_list(); auto resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list(); auto& result = resultRaw.as_list();
for (const auto& connection : *download->connection_list()) { for (const auto& connection : *download->connection_list()) {
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list(); torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
for (torrent::Object::list_const_iterator cItr = ++args.begin(); cItr != args.end(); cItr++) { for (auto cItr = ++args.begin(); cItr != args.end(); cItr++) {
const std::string& cmd = cItr->as_string(); const std::string& cmd = cItr->as_string();
row.push_back(rpc::parse_command(rpc::make_target(connection), cmd.c_str(), cmd.c_str() + cmd.size()).first); row.push_back(rpc::parse_command(rpc::make_target(connection), cmd.c_str(), cmd.c_str() + cmd.size()).first);
@@ -655,6 +655,8 @@ initialize_command_download() {
CMD2_DL("d.base_path.as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_as_binary(); }); CMD2_DL("d.base_path.as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_as_binary(); });
CMD2_DL("d.base_path.or_base64", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_base64(); }); CMD2_DL("d.base_path.or_base64", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_base64(); });
CMD2_DL("d.base_path.or_as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_as_binary(); }); CMD2_DL("d.base_path.or_as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_as_binary(); });
CMD2_DL("d.base_path.realpath.or_empty", [](auto* download, auto) { return resolve_path(retrieve_d_base_path(download).str()); });
CMD2_DL("d.base_path.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(retrieve_d_base_path(download).str()); });
CMD2_DL("d.base_filename", [](auto* download, auto) { return retrieve_d_base_filename(download).str(); }); CMD2_DL("d.base_filename", [](auto* download, auto) { return retrieve_d_base_filename(download).str(); });
CMD2_DL("d.base_filename.hex", [](auto* download, auto) { return retrieve_d_base_filename(download).object_hex(); }); CMD2_DL("d.base_filename.hex", [](auto* download, auto) { return retrieve_d_base_filename(download).object_hex(); });
CMD2_DL("d.base_filename.base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_base64(); }); CMD2_DL("d.base_filename.base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_base64(); });
@@ -771,6 +773,11 @@ initialize_command_download() {
CMD2_DL_VAR_STRING_PUBLIC("d.tied_to_file", "rtorrent", "tied_to_file"); CMD2_DL_VAR_STRING_PUBLIC("d.tied_to_file", "rtorrent", "tied_to_file");
CMD2_DL_VAR_STRING("d.loaded_file", "rtorrent", "loaded_file"); CMD2_DL_VAR_STRING("d.loaded_file", "rtorrent", "loaded_file");
CMD2_DL("d.tied_to_file.realpath.or_empty", [](auto* download, auto) { return resolve_path(rpc::convert_to_string(download_get_variable(download, "rtorrent", "tied_to_file"))); });
CMD2_DL("d.tied_to_file.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(rpc::convert_to_string(download_get_variable(download, "rtorrent", "tied_to_file"))); });
CMD2_DL("d.loaded_file.realpath.or_empty", [](auto* download, auto) { return resolve_path(rpc::convert_to_string(download_get_variable(download, "rtorrent", "loaded_file"))); });
CMD2_DL("d.loaded_file.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(rpc::convert_to_string(download_get_variable(download, "rtorrent", "loaded_file"))); });
// The "state_changed" variable is required to be a valid unix time // The "state_changed" variable is required to be a valid unix time
// value, it indicates the last time the torrent changed its state, // value, it indicates the last time the torrent changed its state,
// resume/pause. // resume/pause.
@@ -781,15 +788,15 @@ initialize_command_download() {
CMD2_DL_TIMESTAMP("d.timestamp.started", "rtorrent", "timestamp.started"); CMD2_DL_TIMESTAMP("d.timestamp.started", "rtorrent", "timestamp.started");
CMD2_DL_TIMESTAMP("d.timestamp.finished", "rtorrent", "timestamp.finished"); 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 ("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("d.connection_current.set", std::bind(&apply_d_connection_type, std::placeholders::_1, std::placeholders::_2)); 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_leech", "rtorrent", "connection_leech");
CMD2_DL_VAR_STRING("d.connection_seed", "rtorrent", "connection_seed"); 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_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_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"); CMD2_DL_VAR_STRING("d.up.choke_heuristics.leech", "rtorrent", "choke_heuristics.up.leech");
@@ -844,7 +851,7 @@ initialize_command_download() {
CMD2_DL ("d.bytes_done", CMD2_ON_DL(bytes_done)); 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.ratio", std::bind(&retrieve_d_ratio, std::placeholders::_1));
CMD2_DL ("d.chunks_hashed", CMD2_ON_DL(chunks_hashed)); 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_files", CMD2_ON_FL(size_files));
CMD2_DL ("d.size_bytes", CMD2_ON_FL(size_bytes)); CMD2_DL ("d.size_bytes", CMD2_ON_FL(size_bytes));
@@ -878,6 +885,8 @@ initialize_command_download() {
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_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", std::bind(&apply_d_directory, std::placeholders::_1, std::placeholders::_2));
CMD2_DL ("d.directory_base", CMD2_ON_FL(root_dir)); CMD2_DL ("d.directory_base", CMD2_ON_FL(root_dir));
CMD2_DL_STRING_V("d.directory_base.set", std::bind(&core::Download::set_root_directory, std::placeholders::_1, std::placeholders::_2)); CMD2_DL_STRING_V("d.directory_base.set", std::bind(&core::Download::set_root_directory, std::placeholders::_1, std::placeholders::_2));
@@ -912,6 +921,14 @@ initialize_command_download() {
rpc::rpc.mark_safe("d.local_id_html"); rpc::rpc.mark_safe("d.local_id_html");
rpc::rpc.mark_safe("d.bitfield"); rpc::rpc.mark_safe("d.bitfield");
rpc::rpc.mark_safe("d.base_path"); rpc::rpc.mark_safe("d.base_path");
rpc::rpc.mark_safe("d.base_path.realpath.or_empty");
rpc::rpc.mark_safe("d.base_path.realpath.or_throw");
rpc::rpc.mark_safe("d.directory.realpath.or_empty");
rpc::rpc.mark_safe("d.directory.realpath.or_throw");
rpc::rpc.mark_safe("d.tied_to_file.realpath.or_empty");
rpc::rpc.mark_safe("d.tied_to_file.realpath.or_throw");
rpc::rpc.mark_safe("d.loaded_file.realpath.or_empty");
rpc::rpc.mark_safe("d.loaded_file.realpath.or_throw");
rpc::rpc.mark_safe("d.base_path.hex"); rpc::rpc.mark_safe("d.base_path.hex");
rpc::rpc.mark_safe("d.base_path.base64"); rpc::rpc.mark_safe("d.base_path.base64");
rpc::rpc.mark_safe("d.base_path.base64_as_binary"); rpc::rpc.mark_safe("d.base_path.base64_as_binary");
@@ -973,6 +990,9 @@ initialize_command_download() {
rpc::rpc.mark_safe("d.size_chunks"); rpc::rpc.mark_safe("d.size_chunks");
rpc::rpc.mark_safe("d.size_pex"); rpc::rpc.mark_safe("d.size_pex");
rpc::rpc.mark_safe("d.completed_bytes"); rpc::rpc.mark_safe("d.completed_bytes");
rpc::rpc.mark_safe("d.complete");
rpc::rpc.mark_safe("d.timestamp.started");
rpc::rpc.mark_safe("d.timestamp.finished");
rpc::rpc.mark_safe("d.bytes_done"); rpc::rpc.mark_safe("d.bytes_done");
rpc::rpc.mark_safe("d.peers_accounted"); rpc::rpc.mark_safe("d.peers_accounted");
rpc::rpc.mark_safe("d.chunks_hashed"); rpc::rpc.mark_safe("d.chunks_hashed");
+16 -10
View File
@@ -444,16 +444,20 @@ initialize_command_dynamic() {
CMD2_ANY ("catch", std::bind(&cmd_catch, std::placeholders::_1, std::placeholders::_2)); CMD2_ANY ("catch", std::bind(&cmd_catch, std::placeholders::_1, std::placeholders::_2));
CMD2_ANY ("strings.choke_heuristics", std::bind(&torrent::option_list_strings, torrent::OPTION_CHOKE_HEURISTICS)); CMD2_ANY_STRING ("enum.log_group", [](auto, const auto& str) { return torrent::option_find_string_str(torrent::OPTION_LOG_GROUP, str); });
CMD2_ANY ("strings.choke_heuristics.upload", std::bind(&torrent::option_list_strings, torrent::OPTION_CHOKE_HEURISTICS_UPLOAD));
CMD2_ANY ("strings.choke_heuristics.download", std::bind(&torrent::option_list_strings, torrent::OPTION_CHOKE_HEURISTICS_DOWNLOAD)); CMD2_ANY ("strings.choke_heuristics", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS); });
CMD2_ANY ("strings.connection_type", std::bind(&torrent::option_list_strings, torrent::OPTION_CONNECTION_TYPE)); CMD2_ANY ("strings.choke_heuristics.upload", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_UPLOAD); });
CMD2_ANY ("strings.encryption", std::bind(&torrent::option_list_strings, torrent::OPTION_ENCRYPTION)); CMD2_ANY ("strings.choke_heuristics.download", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_DOWNLOAD); });
CMD2_ANY ("strings.ip_filter", std::bind(&torrent::option_list_strings, torrent::OPTION_IP_FILTER)); CMD2_ANY ("strings.connection_type", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CONNECTION_TYPE); });
CMD2_ANY ("strings.ip_tos", std::bind(&torrent::option_list_strings, torrent::OPTION_IP_TOS)); CMD2_ANY ("strings.encryption", [](auto, auto) { return torrent::Object::create_list(); });
CMD2_ANY ("strings.log_group", std::bind(&torrent::option_list_strings, torrent::OPTION_LOG_GROUP)); CMD2_ANY ("strings.encryption.handshake", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_HANDSHAKE); });
CMD2_ANY ("strings.tracker_event", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_EVENT)); CMD2_ANY ("strings.encryption.stream", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_STREAM); });
CMD2_ANY ("strings.tracker_mode", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_MODE)); CMD2_ANY ("strings.ip_filter", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_FILTER); });
CMD2_ANY ("strings.ip_tos", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_TOS); });
CMD2_ANY ("strings.log_group", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_LOG_GROUP); });
CMD2_ANY ("strings.tracker_event", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_TRACKER_EVENT); });
CMD2_ANY ("strings.tracker_mode", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_TRACKER_MODE); });
// clang-format on // clang-format on
#ifdef HAVE_XMLRPC_TINYXML2 #ifdef HAVE_XMLRPC_TINYXML2
@@ -468,6 +472,8 @@ initialize_command_dynamic() {
rpc::rpc.mark_safe("method.rlookup"); rpc::rpc.mark_safe("method.rlookup");
rpc::rpc.mark_safe("catch"); rpc::rpc.mark_safe("catch");
rpc::rpc.mark_safe("enum.log_group");
rpc::rpc.mark_safe("strings.choke_heuristics"); rpc::rpc.mark_safe("strings.choke_heuristics");
rpc::rpc.mark_safe("strings.choke_heuristics.upload"); rpc::rpc.mark_safe("strings.choke_heuristics.upload");
rpc::rpc.mark_safe("strings.choke_heuristics.download"); rpc::rpc.mark_safe("strings.choke_heuristics.download");
+37 -36
View File
@@ -168,12 +168,14 @@ torrent::Object
apply_close_low_diskspace(int64_t arg, uint32_t skip_priority) { apply_close_low_diskspace(int64_t arg, uint32_t skip_priority) {
bool closed = false; bool closed = false;
torrent::FileList::cache_list cache;
for (auto download : *control->core()->download_list()) { for (auto download : *control->core()->download_list()) {
if (!download->is_downloading()) if (!download->is_downloading())
continue; continue;
if (download->priority() >= skip_priority) if (download->priority() >= skip_priority)
continue; continue;
if (download->file_list()->free_diskspace() >= (uint64_t)arg) if (download->file_list()->free_diskspace(cache) >= (uint64_t)arg)
continue; continue;
control->core()->download_list()->close(download); control->core()->download_list()->close(download);
@@ -256,11 +258,12 @@ torrent::Object
d_multicall_filtered(const torrent::Object::list_type& args) { d_multicall_filtered(const torrent::Object::list_type& args) {
if (args.size() < 2) if (args.size() < 2)
throw torrent::input_error("d.multicall.filtered requires at least 2 arguments."); throw torrent::input_error("d.multicall.filtered requires at least 2 arguments.");
torrent::Object::list_const_iterator arg = args.begin();
auto arg = args.begin();
// Find the given view // Find the given view
core::ViewManager* viewManager = control->view_manager(); auto* viewManager = control->view_manager();
core::ViewManager::iterator view_itr = viewManager->find(arg->as_string().empty() ? "default" : arg->as_string()); auto view_itr = viewManager->find(arg->as_string().empty() ? "default" : arg->as_string());
if (view_itr == viewManager->end()) if (view_itr == viewManager->end())
throw torrent::input_error("Could not find view '" + arg->as_string() + "'."); throw torrent::input_error("Could not find view '" + arg->as_string() + "'.");
@@ -270,8 +273,9 @@ d_multicall_filtered(const torrent::Object::list_type& args) {
(*view_itr)->filter_by(*++arg, dlist); (*view_itr)->filter_by(*++arg, dlist);
// Generate result by iterating over all items // Generate result by iterating over all items
torrent::Object resultRaw = torrent::Object::create_list(); auto resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list(); auto& result = resultRaw.as_list();
++arg; // skip to first command ++arg; // skip to first command
for (const auto& item : dlist) { for (const auto& item : dlist) {
@@ -329,42 +333,39 @@ directory_watch_ready(const torrent::Object::list_type& args) {
void void
initialize_command_events() { initialize_command_events() {
CMD2_ANY_STRING ("on_ratio", std::bind(&apply_on_ratio, std::placeholders::_2)); CMD2_ANY_STRING ("on_ratio", [](auto, auto& args) { return apply_on_ratio(args); });
CMD2_ANY ("start_tied", std::bind(&apply_start_tied)); CMD2_ANY ("start_tied", [](auto, auto) { return apply_start_tied(); });
CMD2_ANY ("stop_untied", std::bind(&apply_stop_untied)); CMD2_ANY ("stop_untied", [](auto, auto) { return apply_stop_untied(); });
CMD2_ANY ("close_untied", std::bind(&apply_close_untied)); CMD2_ANY ("close_untied", [](auto, auto) { return apply_close_untied(); });
CMD2_ANY ("remove_untied", std::bind(&apply_remove_untied)); CMD2_ANY ("remove_untied", [](auto, auto) { return apply_remove_untied(); });
// TODO: Deprecate schedule2 in the future. CMD2_ANY_LIST ("schedule", [](auto, auto& args) { return apply_schedule(args); });
CMD2_ANY_LIST ("schedule", std::bind(&apply_schedule, std::placeholders::_2)); CMD2_ANY_STRING_V("schedule.remove", [](auto, auto& str) { return control->command_scheduler()->erase_str(str); });
CMD2_ANY_LIST ("schedule2", std::bind(&apply_schedule, std::placeholders::_2));
CMD2_ANY_STRING_V("schedule.remove", std::bind(&rpc::CommandScheduler::erase_str, control->command_scheduler(), std::placeholders::_2));
CMD2_ANY_STRING_V("schedule_remove2", std::bind(&rpc::CommandScheduler::erase_str, control->command_scheduler(), std::placeholders::_2));
CMD2_ANY_STRING_V("import", std::bind(&apply_import, std::placeholders::_2)); CMD2_ANY_STRING_V("import", [](auto, auto& str) { return apply_import(str); });
CMD2_ANY_STRING_V("try_import", std::bind(&apply_try_import, std::placeholders::_2)); CMD2_ANY_STRING_V("try_import", [](auto, auto& str) { return apply_try_import(str); });
CMD2_ANY_LIST ("load.normal", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_quiet | core::Manager::create_tied)); CMD2_ANY_LIST ("load.normal", [](auto, auto& args) { return apply_load(args, core::Manager::create_quiet | core::Manager::create_tied); });
CMD2_ANY_LIST ("load.verbose", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_tied)); CMD2_ANY_LIST ("load.verbose", [](auto, auto& args) { return apply_load(args, core::Manager::create_tied); });
CMD2_ANY_LIST ("load.start", std::bind(&apply_load, std::placeholders::_2, CMD2_ANY_LIST ("load.start", [](auto, auto& args) { return apply_load(args, core::Manager::create_quiet | core::Manager::create_tied | core::Manager::create_start); });
core::Manager::create_quiet | core::Manager::create_tied | core::Manager::create_start)); CMD2_ANY_LIST ("load.start_verbose", [](auto, auto& args) { return apply_load(args, core::Manager::create_tied | core::Manager::create_start); });
CMD2_ANY_LIST ("load.start_verbose", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_tied | core::Manager::create_start)); CMD2_ANY_LIST ("load.raw", [](auto, auto& args) { return apply_load(args, core::Manager::create_quiet | core::Manager::create_raw_data); });
CMD2_ANY_LIST ("load.raw", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_quiet | core::Manager::create_raw_data)); CMD2_ANY_LIST ("load.raw_verbose", [](auto, auto& args) { return apply_load(args, core::Manager::create_raw_data); });
CMD2_ANY_LIST ("load.raw_verbose", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_raw_data)); CMD2_ANY_LIST ("load.raw_start", [](auto, auto& args) { return apply_load(args, core::Manager::create_quiet | core::Manager::create_start | core::Manager::create_raw_data); });
CMD2_ANY_LIST ("load.raw_start", std::bind(&apply_load, std::placeholders::_2, CMD2_ANY_LIST ("load.raw_start_verbose", [](auto, auto& args) { return apply_load(args, core::Manager::create_start | core::Manager::create_raw_data); });
core::Manager::create_quiet | core::Manager::create_start | core::Manager::create_raw_data));
CMD2_ANY_LIST ("load.raw_start_verbose", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_start | core::Manager::create_raw_data));
CMD2_ANY_VALUE ("close_low_diskspace", std::bind(&apply_close_low_diskspace, std::placeholders::_2, 99)); CMD2_ANY_VALUE ("close_low_diskspace", [](auto, auto& arg) { return apply_close_low_diskspace(arg, 99); });
CMD2_ANY_VALUE ("close_low_diskspace.normal", std::bind(&apply_close_low_diskspace, std::placeholders::_2, 3)); CMD2_ANY_VALUE ("close_low_diskspace.normal", [](auto, auto& arg) { return apply_close_low_diskspace(arg, 3); });
CMD2_ANY_LIST ("download_list", std::bind(&apply_download_list, std::placeholders::_2)); CMD2_ANY_LIST ("download_list", [](auto, auto& args) { return apply_download_list(args); });
CMD2_ANY_LIST ("d.multicall2", std::bind(&d_multicall, std::placeholders::_2));
CMD2_ANY_LIST ("d.multicall.filtered", std::bind(&d_multicall_filtered, std::placeholders::_2));
CMD2_ANY_LIST ("directory.watch.added", std::bind(&directory_watch_added, std::placeholders::_2)); // TODO: Deprecate d.multicall2. (6/2026)
CMD2_ANY_LIST ("directory.watch.ready", std::bind(&directory_watch_ready, std::placeholders::_2)); CMD2_ANY_LIST ("d.multicall", [](auto, auto& args) { return d_multicall(args); });
CMD2_ANY_LIST ("d.multicall.filtered", [](auto, auto& args) { return d_multicall_filtered(args); });
CMD2_ANY_LIST ("directory.watch.added", [](auto, auto& args) { return directory_watch_added(args); });
CMD2_ANY_LIST ("directory.watch.ready", [](auto, auto& args) { return directory_watch_ready(args); });
rpc::rpc.mark_safe("start_tied"); rpc::rpc.mark_safe("start_tied");
rpc::rpc.mark_safe("stop_untied"); rpc::rpc.mark_safe("stop_untied");
@@ -374,6 +375,6 @@ initialize_command_events() {
rpc::rpc.mark_safe("close_low_diskspace"); rpc::rpc.mark_safe("close_low_diskspace");
rpc::rpc.mark_safe("close_low_diskspace.normal"); rpc::rpc.mark_safe("close_low_diskspace.normal");
rpc::rpc.mark_safe("download_list"); rpc::rpc.mark_safe("download_list");
rpc::rpc.mark_safe("d.multicall2"); rpc::rpc.mark_safe("d.multicall");
rpc::rpc.mark_safe("d.multicall.filtered"); rpc::rpc.mark_safe("d.multicall.filtered");
} }
+4
View File
@@ -116,6 +116,8 @@ initialize_command_file() {
CMD2_FILE("f.frozen_path.as_binary", [](auto* file, auto) { return file->frozen_path().object_as_binary(); }); CMD2_FILE("f.frozen_path.as_binary", [](auto* file, auto) { return file->frozen_path().object_as_binary(); });
CMD2_FILE("f.frozen_path.or_base64", [](auto* file, auto) { return file->frozen_path().object_utf8_or_base64(); }); CMD2_FILE("f.frozen_path.or_base64", [](auto* file, auto) { return file->frozen_path().object_utf8_or_base64(); });
CMD2_FILE("f.frozen_path.or_as_binary", [](auto* file, auto) { return file->frozen_path().object_utf8_or_as_binary(); }); CMD2_FILE("f.frozen_path.or_as_binary", [](auto* file, auto) { return file->frozen_path().object_utf8_or_as_binary(); });
CMD2_FILE("f.frozen_path.realpath.or_empty", [](auto* file, auto) { return resolve_path(file->frozen_path().str()); });
CMD2_FILE("f.frozen_path.realpath.or_throw", [](auto* file, auto) { return resolve_path_or_throw(file->frozen_path().str()); });
CMD2_FILE("f.match_depth_prev", std::bind(&torrent::File::match_depth_prev, std::placeholders::_1)); CMD2_FILE("f.match_depth_prev", std::bind(&torrent::File::match_depth_prev, std::placeholders::_1));
CMD2_FILE("f.match_depth_next", std::bind(&torrent::File::match_depth_next, std::placeholders::_1)); CMD2_FILE("f.match_depth_next", std::bind(&torrent::File::match_depth_next, std::placeholders::_1));
@@ -129,6 +131,8 @@ initialize_command_file() {
rpc::rpc.mark_safe("f.path_components"); rpc::rpc.mark_safe("f.path_components");
rpc::rpc.mark_safe("f.path_depth"); rpc::rpc.mark_safe("f.path_depth");
rpc::rpc.mark_safe("f.frozen_path"); rpc::rpc.mark_safe("f.frozen_path");
rpc::rpc.mark_safe("f.frozen_path.realpath.or_empty");
rpc::rpc.mark_safe("f.frozen_path.realpath.or_throw");
rpc::rpc.mark_safe("f.frozen_path.hex"); rpc::rpc.mark_safe("f.frozen_path.hex");
rpc::rpc.mark_safe("f.frozen_path.base64"); rpc::rpc.mark_safe("f.frozen_path.base64");
rpc::rpc.mark_safe("f.frozen_path.base64_as_binary"); rpc::rpc.mark_safe("f.frozen_path.base64_as_binary");
+41 -42
View File
@@ -1,5 +1,7 @@
#include "config.h" #include "config.h"
#include <memory>
#include <torrent/download/resource_manager.h> #include <torrent/download/resource_manager.h>
#include <torrent/download/choke_group.h> #include <torrent/download/choke_group.h>
#include <torrent/download/choke_queue.h> #include <torrent/download/choke_queue.h>
@@ -111,7 +113,7 @@ apply_cg_all_update_balance(bool is_up) {
// //
#else #else
std::vector<torrent::choke_group*> cg_list_hack; std::vector<std::unique_ptr<torrent::choke_group>> cg_list_hack;
int64_t int64_t
cg_get_index(const torrent::Object& raw_args) { cg_get_index(const torrent::Object& raw_args) {
@@ -121,7 +123,7 @@ cg_get_index(const torrent::Object& raw_args) {
if (arg.is_string()) { if (arg.is_string()) {
if (!rpc::parse_whole_value_nothrow(arg.as_string().c_str(), &index)) { if (!rpc::parse_whole_value_nothrow(arg.as_string().c_str(), &index)) {
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](torrent::choke_group* cg) { return arg.as_string() == cg->name(); }); auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](const auto& cg) { return arg.as_string() == cg->name(); });
if (itr == cg_list_hack.end()) if (itr == cg_list_hack.end())
throw torrent::input_error("Choke group not found."); throw torrent::input_error("Choke group not found.");
@@ -149,7 +151,7 @@ cg_get_group(const torrent::Object& raw_args) {
if ((size_t)index >= cg_list_hack.size()) if ((size_t)index >= cg_list_hack.size())
throw torrent::input_error("Choke group not found."); throw torrent::input_error("Choke group not found.");
return cg_list_hack.at(index); return cg_list_hack.at(index).get();
} }
int64_t cg_d_group(core::Download* download) { return download->group(); } int64_t cg_d_group(core::Download* download) { return download->group(); }
@@ -162,7 +164,7 @@ torrent::Object
apply_cg_list() { apply_cg_list() {
torrent::Object::list_type result; torrent::Object::list_type result;
for (auto itr : cg_list_hack) for (const auto& itr : cg_list_hack)
result.push_back(itr->name()); result.push_back(itr->name());
return torrent::Object::from_list(result); return torrent::Object::from_list(result);
@@ -180,21 +182,21 @@ apply_cg_insert(const std::string& arg) {
if (rpc::parse_whole_value_nothrow(arg.c_str(), &dummy)) if (rpc::parse_whole_value_nothrow(arg.c_str(), &dummy))
throw torrent::input_error("Cannot use a value string as choke group name."); throw torrent::input_error("Cannot use a value string as choke group name.");
if (arg.empty() || std::any_of(cg_list_hack.begin(), cg_list_hack.end(), [&arg](auto cg) { return arg == cg->name(); })) if (arg.empty() || std::any_of(cg_list_hack.begin(), cg_list_hack.end(), [&arg](const auto& cg) { return arg == cg->name(); }))
throw torrent::input_error("Duplicate name for choke group."); throw torrent::input_error("Duplicate name for choke group.");
cg_list_hack.push_back(new torrent::choke_group()); cg_list_hack.push_back(std::make_unique<torrent::choke_group>());
cg_list_hack.back()->set_name(arg); cg_list_hack.back()->set_name(arg);
cg_list_hack.back()->up_queue()->set_heuristics(torrent::choke_queue::HEURISTICS_UPLOAD_LEECH); cg_list_hack.back()->up_queue()->set_heuristics(torrent::HEURISTICS_UPLOAD_LEECH);
cg_list_hack.back()->down_queue()->set_heuristics(torrent::choke_queue::HEURISTICS_DOWNLOAD_LEECH); cg_list_hack.back()->down_queue()->set_heuristics(torrent::HEURISTICS_DOWNLOAD_LEECH);
return torrent::Object(); return torrent::Object();
} }
torrent::Object torrent::Object
apply_cg_index_of(const std::string& arg) { apply_cg_index_of(const std::string& arg) {
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](torrent::choke_group* cg) { return arg == cg->name(); }); auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](const auto& cg) { return arg == cg->name(); });
if (itr == cg_list_hack.end()) if (itr == cg_list_hack.end())
throw torrent::input_error("Choke group not found."); throw torrent::input_error("Choke group not found.");
@@ -206,7 +208,7 @@ torrent::Object
apply_cg_all_update_balance(bool is_up) { apply_cg_all_update_balance(bool is_up) {
LT_LOG_SUBSYSTEM("apply update balance: hack is_up:%i", (int)is_up); LT_LOG_SUBSYSTEM("apply update balance: hack is_up:%i", (int)is_up);
for (auto itr : cg_list_hack) { for (const auto& itr : cg_list_hack) {
if (is_up) if (is_up)
itr->up_queue()->balance(); itr->up_queue()->balance();
else else
@@ -337,54 +339,51 @@ options.
void void
initialize_command_groups() { initialize_command_groups() {
CMD2_ANY ("choke_group.list", std::bind(&apply_cg_list)); CMD_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_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2));
#if USE_CHOKE_GROUP #if USE_CHOKE_GROUP
CMD2_ANY ("choke_group.size", std::bind(&torrent::ResourceManager::group_size, torrent::resource_manager())); CMD_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_STRING ("choke_group.index_of", std::bind(&torrent::ResourceManager::group_index_of, torrent::resource_manager(), std::placeholders::_2));
#else #else
apply_cg_insert("default"); apply_cg_insert("default");
CMD2_ANY ("choke_group.size", std::bind(&std::vector<torrent::choke_group*>::size, cg_list_hack)); CMD_ANY ("choke_group.size", [](auto, auto) { return (int64_t)cg_list_hack.size(); });
CMD2_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2)); CMD_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2));
#endif #endif
// Commands specific for a group. Supports as the first argument the // Commands specific for a group. Supports as the first argument the
// name, the index or a negative index. // 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, 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()); });
std::bind(&torrent::choke_group::tracker_mode, CG_GROUP_AT()))); CMD_ANY_LIST ("choke_group.tracker.mode.set", [](auto, auto arg) { return apply_cg_tracker_mode_set(arg); });
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)); CMD_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)); CMD_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())); CMD_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())); CMD_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))); CMD_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))); CMD_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)); CMD_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))); CMD_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))); CMD_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))); CMD_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, 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()); });
std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::up_queue)))); CMD_ANY_LIST ("choke_group.up.heuristics.set", [](auto, auto arg) { return apply_cg_heuristics_set(arg, true); });
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))); CMD_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))); CMD_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)); CMD_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))); CMD_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))); CMD_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))); CMD_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, 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()); });
std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::down_queue)))); CMD_ANY_LIST ("choke_group.down.heuristics.set", [](auto, auto arg) { return apply_cg_heuristics_set(arg, false); });
CMD2_ANY_LIST ("choke_group.down.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, false));
rpc::rpc.mark_safe("choke_group.list"); rpc::rpc.mark_safe("choke_group.list");
rpc::rpc.mark_safe("choke_group.size"); rpc::rpc.mark_safe("choke_group.size");
+2
View File
@@ -19,6 +19,7 @@ void initialize_command_groups();
void initialize_command_throttle(); 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_ui(); void initialize_command_ui();
void void
@@ -37,4 +38,5 @@ initialize_commands() {
initialize_command_throttle(); initialize_command_throttle();
initialize_command_tracker(); initialize_command_tracker();
initialize_command_scheduler(); initialize_command_scheduler();
initialize_command_string();
} }
+19 -1
View File
@@ -7,6 +7,25 @@
void initialize_commands(); 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: // New std::function based command_base helper functions:
// //
@@ -115,7 +134,6 @@ void initialize_commands();
#define CMD2_REDIRECT_TRACKER(from_key, to_key) \ #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); 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: // Conversion of return types:
// //
+1 -1
View File
@@ -353,7 +353,7 @@ apply_ipv4_filter_dump() {
inet_ntop(AF_INET, &net_start, start_str, INET_ADDRSTRLEN); inet_ntop(AF_INET, &net_start, start_str, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &net_end, end_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); result.push_back((std::string)buffer);
+152 -114
View File
@@ -8,10 +8,11 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/chunk_manager.h>
#include <torrent/data/file_manager.h> #include <torrent/data/file_manager.h>
#include <torrent/data/chunk_utils.h> #include <torrent/data/chunk_utils.h>
#include <torrent/runtime/runtime.h> #include <torrent/runtime/runtime.h>
#include <torrent/runtime/memory_manager.h>
#include <torrent/runtime/socket_manager.h>
#include <torrent/utils/chrono.h> #include <torrent/utils/chrono.h>
#include <torrent/utils/option_strings.h> #include <torrent/utils/option_strings.h>
@@ -114,9 +115,9 @@ group_insert(const torrent::Object::list_type& args) {
const std::string& view = check_name(post_increment(itr, last)->as_string()); const std::string& view = check_name(post_increment(itr, last)->as_string());
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.enable", "simple", rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.enable", "simple",
"schedule2=group." + name + ".ratio,5,60,on_ratio=" + name)); "schedule=group." + name + ".ratio,5,60,on_ratio=" + name));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.disable", "simple", rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.disable", "simple",
"schedule_remove2=group." + name + ".ratio")); "schedule.remove=group." + name + ".ratio"));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.command", "simple", rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.command", "simple",
"d.try_close= ;d.ignore_commands.set=1")); "d.try_close= ;d.ignore_commands.set=1"));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".view", "string", view)); rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".view", "string", view));
@@ -133,19 +134,6 @@ group_insert(const torrent::Object::list_type& args) {
rpc::rpc.mark_safe("group." + name + ".ratio.upload"); rpc::rpc.mark_safe("group." + name + ".ratio.upload");
rpc::rpc.mark_safe("group." + name + ".ratio.upload.set"); rpc::rpc.mark_safe("group." + name + ".ratio.upload.set");
if (rpc::call_command_value("method.use_intermediate") == 3) {
// Cleaned up in 0.16.1:
CMD2_REDIRECT_MUTABLE("group2." + name + ".view", "group." + name + ".view");
CMD2_REDIRECT_MUTABLE("group2." + name + ".view.set", "group." + name + ".view.set");
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.min", "group." + name + ".ratio.min");
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.min.set", "group." + name + ".ratio.min.set");
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.max", "group." + name + ".ratio.max");
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.max.set", "group." + name + ".ratio.max.set");
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.upload", "group." + name + ".ratio.upload");
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.upload.set", "group." + name + ".ratio.upload.set");
}
return name; return name;
} }
@@ -200,142 +188,166 @@ cmd_file_append(const torrent::Object::list_type& args) {
void void
initialize_command_local() { initialize_command_local() {
core::DownloadList* dList = control->core()->download_list(); core::DownloadList* dList = control->core()->download_list();
torrent::ChunkManager* chunkManager = torrent::chunk_manager();
torrent::FileManager* fileManager = torrent::file_manager(); torrent::FileManager* fileManager = torrent::file_manager();
CMD2_ANY ("system.hostname", std::bind(&system_hostname)); CMD_ANY ("system.hostname", std::bind(&system_hostname));
CMD2_ANY ("system.pid", std::bind(&getpid)); CMD_ANY ("system.pid", std::bind(&getpid));
CMD2_VAR_C_STRING("system.api_version", (int64_t)API_VERSION); CMD_VAR_C_STRING("system.api_version", (int64_t)API_VERSION);
CMD2_VAR_C_STRING("system.client_version", PACKAGE_VERSION); CMD_VAR_C_STRING("system.client_version", PACKAGE_VERSION);
CMD2_VAR_C_STRING("system.library_version", torrent::runtime::version()); CMD_VAR_C_STRING("system.library_version", torrent::runtime::version());
CMD2_VAR_VALUE ("system.file.allocate", 0); CMD_VAR_VALUE ("system.file.allocate", 0);
CMD2_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30); CMD_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
CMD2_VAR_VALUE ("system.file.split_size", -1); CMD_VAR_VALUE ("system.file.split_size", -1);
CMD2_VAR_STRING ("system.file.split_suffix", ".part"); 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())); (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); CMD_VAR_BOOL ("file.prioritize_toc", 0);
CMD2_VAR_LIST ("file.prioritize_toc.first"); CMD_VAR_LIST ("file.prioritize_toc.first");
CMD2_VAR_LIST ("file.prioritize_toc.last"); CMD_VAR_LIST ("file.prioritize_toc.last");
CMD2_ANY ("system.files.advise_random", std::bind(&FM_t::advise_random, fileManager)); CMD_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)); CMD_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)); CMD_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)); CMD_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(); }); CMD_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); }); CMD_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)); CMD_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)); CMD_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_ANY ("system.files.failed_counter", std::bind(&FM_t::files_failed_counter, fileManager));
CMD2_ANY_STRING ("system.env", std::bind(&system_env, std::placeholders::_2)); CMD_ANY_STRING ("system.env", [](auto, auto& str) { return system_env(str); });
CMD2_ANY ("system.time", []([[maybe_unused]] auto t, [[maybe_unused]] auto o) -> torrent::Object { CMD_ANY ("system.time", [](auto, auto) { return torrent::this_thread::cached_seconds().count(); });
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(); });
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();
});
CMD2_ANY_VALUE_V ("system.umask.set", std::bind(&umask, std::placeholders::_2)); CMD_ANY_VALUE_V ("system.umask.set", [](auto, auto& value) { return ::umask(value); });
CMD2_VAR_BOOL ("system.daemon", false); CMD_VAR_BOOL ("system.daemon", false);
CMD2_ANY_V ("system.shutdown.normal", std::bind(&Control::receive_normal_shutdown, control)); CMD_ANY_V ("system.shutdown.normal", [](auto, auto) { control->receive_normal_shutdown(); });
CMD2_ANY_V ("system.shutdown.quick", std::bind(&Control::receive_quick_shutdown, control)); CMD_ANY_V ("system.shutdown.quick", [](auto, auto) { control->receive_quick_shutdown(); });
CMD2_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
CMD2_ANY ("system.cwd", std::bind(&system_get_cwd)); CMD_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
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)); CMD_ANY ("system.cwd", [](auto, auto) { return system_get_cwd(); });
CMD2_ANY_VALUE_V ("pieces.sync.always_safe.set", std::bind(&CM_t::set_safe_sync, chunkManager, std::placeholders::_2)); CMD_ANY_STRING ("system.cwd.set", [](auto, auto& str) { return system_set_cwd(str); });
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)); CMD_ANY ("system.sockets.size", [](auto, auto) { return torrent::runtime::socket_manager()->size(); });
CMD2_ANY_VALUE_V ("pieces.preload.type.set", std::bind(&CM_t::set_preload_type, chunkManager, std::placeholders::_2)); CMD_ANY ("system.sockets.max_size", [](auto, auto) { return torrent::runtime::socket_manager()->max_size(); });
CMD2_ANY ("pieces.preload.min_size", std::bind(&CM_t::preload_min_size, chunkManager)); CMD_ANY_VALUE_V ("system.sockets.max_size.set", [](auto, auto& value) { return torrent::runtime::socket_manager()->set_max_size_and_adjust(value); });
CMD2_ANY_VALUE_V ("pieces.preload.min_size.set", std::bind(&CM_t::set_preload_min_size, chunkManager, std::placeholders::_2)); CMD_ANY_V ("system.sockets.adjust_alloc", [](auto, auto) { torrent::runtime::socket_manager()->adjust_allocation(); });
CMD2_ANY ("pieces.preload.min_rate", std::bind(&CM_t::preload_required_rate, chunkManager)); CMD_ANY ("system.sockets.reserved_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->reserved_allocation(); });
CMD2_ANY_VALUE_V ("pieces.preload.min_rate.set", std::bind(&CM_t::set_preload_required_rate, chunkManager, std::placeholders::_2)); CMD_ANY ("system.sockets.available_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->available_allocation(); });
CMD2_ANY ("pieces.memory.current", std::bind(&CM_t::memory_usage, chunkManager)); for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
CMD2_ANY ("pieces.memory.sync_queue", std::bind(&CM_t::sync_queue_memory_usage, chunkManager)); auto category = static_cast<torrent::runtime::socket_manager_category_t>(i);
CMD2_ANY ("pieces.memory.block_count", std::bind(&CM_t::memory_block_count, chunkManager)); auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
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)); 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); });
CMD2_ANY ("pieces.hash.queue_size", std::bind(&torrent::main_thread::hash_queue_size)); if (i == 0) {
CMD2_VAR_BOOL ("pieces.hash.on_completion", true); CMD_ANY (category_name + ".min_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->generic_min_allocation(); });
continue;
}
CMD2_VAR_STRING ("directory.default", "./"); 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, value); });
CMD_ANY_VALUE_V(category_name + ".max_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_max_allocation(category, value); });
}
CMD2_VAR_STRING ("session.name", ""); CMD_ANY ("pieces.sync.always_safe", [](auto, auto) { return torrent::runtime::memory_manager()->safe_sync(); });
CMD2_ANY ("session.path", [](auto, auto) { return session_thread::manager()->path(); }); CMD_ANY_VALUE_V ("pieces.sync.always_safe.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_safe_sync(value); });
CMD2_ANY_STRING_V("session.path.set", [](auto, auto& str) { return session_thread::manager()->set_path(str); }); CMD_ANY ("pieces.sync.safe_free_diskspace", [](auto, auto) { return torrent::runtime::memory_manager()->sync_safe_free_diskspace(); });
CMD2_ANY ("session.use_lock", [](auto, auto) { return session_thread::manager()->use_lock(); }); CMD_ANY ("pieces.sync.timeout", [](auto, auto) { return torrent::runtime::memory_manager()->timeout_sync().count(); });
CMD2_ANY_VALUE_V ("session.use_lock.set", [](auto, auto& value) { return session_thread::manager()->set_use_lock(value); }); CMD_ANY_VALUE_V ("pieces.sync.timeout.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_timeout_sync(value); });
CMD2_VAR_BOOL ("session.on_completion", true); // CMD_ANY ("pieces.sync.timeout_safe", [](auto, auto) { return torrent::runtime::memory_manager()->timeout_safe_sync(); });
// CMD_ANY_VALUE_V ("pieces.sync.timeout_safe.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_timeout_safe_sync(value); });
CMD_ANY ("pieces.sync.timeout_safe", [](auto, auto) { return 0; });
CMD_ANY_VALUE_V ("pieces.sync.timeout_safe.set", [](auto, auto) { });
CMD_ANY ("pieces.sync.queue_size", [](auto, auto) { return torrent::runtime::memory_manager()->sync_queue_block_count(); });
CMD2_ANY_V ("session.save", [dList](auto, auto) { return dList->session_save(); }); CMD_ANY ("pieces.preload.type", [](auto, auto) { return torrent::runtime::memory_manager()->preload_type(); });
CMD_ANY_VALUE_V ("pieces.preload.type.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_preload_type(value); });
CMD_ANY ("pieces.preload.min_size", [](auto, auto) { return torrent::runtime::memory_manager()->preload_min_size(); });
CMD_ANY_VALUE_V ("pieces.preload.min_size.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_preload_min_size(value); });
CMD_ANY ("pieces.preload.min_rate", [](auto, auto) { return torrent::runtime::memory_manager()->preload_required_rate(); });
CMD_ANY_VALUE_V ("pieces.preload.min_rate.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_preload_required_rate(value); });
CMD2_ANY ("magnet.path", [](auto, auto) { return control->core()->magnet_path(); }); CMD_ANY ("pieces.stats_preloaded", [](auto, auto) { return torrent::runtime::memory_manager()->stats_preloaded(); });
CMD2_ANY_STRING_V("magnet.path.set", [](auto, auto& str) { return control->core()->set_magnet_path(str); }); CMD_ANY ("pieces.stats_not_preloaded", [](auto, auto) { return torrent::runtime::memory_manager()->stats_not_preloaded(); });
CMD_ANY ("pieces.stats.total_size", std::bind(&apply_pieces_stats_total_size));
CMD_ANY ("pieces.memory.current", [](auto, auto) { return torrent::runtime::memory_manager()->memory_usage(); });
CMD_ANY ("pieces.memory.sync_queue", [](auto, auto) { return torrent::runtime::memory_manager()->sync_queue_memory_usage(); });
CMD_ANY ("pieces.memory.block_count", [](auto, auto) { return torrent::runtime::memory_manager()->memory_block_count(); });
CMD_ANY ("pieces.memory.max", [](auto, auto) { return torrent::runtime::memory_manager()->max_memory_usage(); });
CMD_ANY_VALUE_V ("pieces.memory.max.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_max_memory_usage(value); });
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_ANY ("directory.default.realpath.or_empty", [](auto, auto) { return resolve_path(rpc::call_command_string("directory.default")); });
CMD_ANY ("directory.default.realpath.or_throw", [](auto, auto) { return resolve_path_or_throw(rpc::call_command_string("directory.default")); });
CMD_VAR_STRING ("session.name", "");
CMD_ANY ("session.path", [](auto, auto) { return session_thread::manager()->path(); });
CMD_ANY ("session.path.realpath.or_empty", [](auto, auto) { return resolve_path(session_thread::manager()->path()); });
CMD_ANY ("session.path.realpath.or_throw", [](auto, auto) { return resolve_path_or_throw(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 #ifdef HAVE_LUA
rpc::LuaEngine* lua_engine = control->lua_engine(); 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)); CMD_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.str", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, rpc::LuaEngine::flag_string));
#endif #endif
#define CMD2_EXECUTE(key, flags) \ #define CMD_EXECUTE(key, flags) \
CMD2_ANY(key, std::bind(&rpc::ExecFile::execute_object, &rpc::execFile, std::placeholders::_2, 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); CMD_EXECUTE ("execute", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
CMD2_EXECUTE ("execute2", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw); CMD_EXECUTE ("execute.throw", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
CMD2_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);
CMD2_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);
CMD2_EXECUTE ("execute.nothrow", rpc::ExecFile::flag_expand_tilde); CMD_EXECUTE ("execute.nothrow.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_background);
CMD2_EXECUTE ("execute.nothrow.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_background); CMD_EXECUTE ("execute.raw", rpc::ExecFile::flag_throw);
CMD2_EXECUTE ("execute.raw", rpc::ExecFile::flag_throw); CMD_EXECUTE ("execute.raw.bg", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background);
CMD2_EXECUTE ("execute.raw.bg", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background); CMD_EXECUTE ("execute.raw_nothrow", 0);
CMD2_EXECUTE ("execute.raw_nothrow", 0); CMD_EXECUTE ("execute.raw_nothrow.bg", rpc::ExecFile::flag_background);
CMD2_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);
CMD2_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);
CMD2_EXECUTE ("execute.capture_nothrow", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
CMD2_ANY_LIST ("file.append", std::bind(&cmd_file_append, std::placeholders::_2));
// TODO: Convert to new command types: // TODO: Convert to new command types:
*rpc::command_base::argument(0) = "placeholder.0"; *rpc::command_base::argument(0) = "placeholder.0";
*rpc::command_base::argument(1) = "placeholder.1"; *rpc::command_base::argument(1) = "placeholder.1";
*rpc::command_base::argument(2) = "placeholder.2"; *rpc::command_base::argument(2) = "placeholder.2";
*rpc::command_base::argument(3) = "placeholder.3"; *rpc::command_base::argument(3) = "placeholder.3";
CMD2_ANY_P("argument.0", std::bind(&rpc::command_base::argument_ref, 0)); CMD_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)); CMD_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)); CMD_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.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.api_version");
rpc::rpc.mark_safe("system.client_version"); rpc::rpc.mark_safe("system.client_version");
@@ -344,8 +356,34 @@ initialize_command_local() {
rpc::rpc.mark_safe("system.file.split_size"); rpc::rpc.mark_safe("system.file.split_size");
rpc::rpc.mark_safe("system.file.split_suffix"); 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_throw");
rpc::rpc.mark_safe("directory.default.realpath.or_empty");
rpc::rpc.mark_safe("directory.default.realpath.or_throw");
rpc::rpc.mark_safe("session.use_lock"); rpc::rpc.mark_safe("session.use_lock");
rpc::rpc.mark_safe("session.on_completion"); rpc::rpc.mark_safe("session.on_completion");
+29
View File
@@ -1,6 +1,7 @@
#include "config.h" #include "config.h"
#include <fcntl.h> #include <fcntl.h>
#include <iterator>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <torrent/data/chunk_utils.h> #include <torrent/data/chunk_utils.h>
@@ -15,6 +16,7 @@
#include "core/download.h" #include "core/download.h"
#include "core/download_list.h" #include "core/download_list.h"
#include "core/manager.h" #include "core/manager.h"
#include "rpc/parse.h"
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
torrent::Object torrent::Object
@@ -38,6 +40,32 @@ apply_log_add_output(const torrent::Object::list_type& args) {
return torrent::Object(); return torrent::Object();
} }
torrent::Object
apply_log_print(const torrent::Object::list_type& args) {
if (args.size() < 2)
throw torrent::input_error("Invalid number of arguments.");
torrent::Object::value_type group;
if (args.front().is_value())
group = args.front().as_value();
else if (args.front().is_string())
group = torrent::option_find_string_str(torrent::OPTION_LOG_GROUP, args.front().as_string());
else
throw torrent::input_error("Invalid log group.");
if (group < 0 || group >= torrent::LOG_GROUP_MAX_SIZE)
throw torrent::input_error("Invalid log group.");
std::string message;
for (auto itr = std::next(args.begin()); itr != args.end(); ++itr)
rpc::print_object_std(&message, &*itr, 0);
torrent::log_groups[group].internal_print(message);
return torrent::Object();
}
// TODO: Deprecated. // TODO: Deprecated.
torrent::Object torrent::Object
apply_log(const torrent::Object::string_type& arg, int logType) { apply_log(const torrent::Object::string_type& arg, int logType) {
@@ -112,6 +140,7 @@ initialize_command_logging() {
CMD2_ANY_STRING_V("log.close", std::bind(&torrent::log_close_output_str, std::placeholders::_2)); CMD2_ANY_STRING_V("log.close", std::bind(&torrent::log_close_output_str, std::placeholders::_2));
CMD2_ANY_LIST ("log.add_output", std::bind(&apply_log_add_output, std::placeholders::_2)); CMD2_ANY_LIST ("log.add_output", std::bind(&apply_log_add_output, std::placeholders::_2));
CMD2_ANY_LIST ("log.print", std::bind(&apply_log_print, std::placeholders::_2));
CMD2_ANY_STRING ("log.execute", std::bind(&apply_log, std::placeholders::_2, 0)); CMD2_ANY_STRING ("log.execute", std::bind(&apply_log, std::placeholders::_2, 0));
CMD2_ANY_STRING ("log.vmmap.dump", std::bind(&log_vmmap_dump, std::placeholders::_2)); CMD2_ANY_STRING ("log.vmmap.dump", std::bind(&log_vmmap_dump, std::placeholders::_2));
+197 -100
View File
@@ -9,7 +9,10 @@
#include <torrent/download/resource_manager.h> #include <torrent/download/resource_manager.h>
#include <torrent/net/http_stack.h> #include <torrent/net/http_stack.h>
#include <torrent/net/socket_address.h> #include <torrent/net/socket_address.h>
#include <torrent/runtime/client_config.h>
#include <torrent/runtime/network_config.h> #include <torrent/runtime/network_config.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/runtime/proxy_manager.h>
#include <torrent/runtime/runtime.h> #include <torrent/runtime/runtime.h>
#include <torrent/runtime/socket_manager.h> #include <torrent/runtime/socket_manager.h>
#include <torrent/tracker/tracker.h> #include <torrent/tracker/tracker.h>
@@ -32,21 +35,115 @@
#endif #endif
torrent::Object torrent::Object
apply_encryption(const torrent::Object::list_type& args) { listen_port_range() {
uint32_t options_mask = torrent::runtime::NetworkConfig::encryption_none; auto port_range = torrent::runtime::client_config()->listen_port_range();
for (const auto& arg : args) { return std::to_string(port_range.first) + "-" + std::to_string(port_range.second);
uint32_t opt = torrent::option_find_string(torrent::OPTION_ENCRYPTION, arg.as_string().c_str()); }
if (opt == torrent::runtime::NetworkConfig::encryption_none) void
options_mask = torrent::runtime::NetworkConfig::encryption_none; set_listen_port_range(const std::string& arg) {
else unsigned int port_first{}, port_last{};
options_mask |= opt;
if (std::sscanf(arg.c_str(), "%i-%i", &port_first, &port_last) != 2)
throw torrent::input_error("Invalid port_range argument.");
if (port_first >= (1 << 16) || port_last >= (1 << 16))
throw torrent::input_error("Port range out-of-bounds.");
torrent::runtime::client_config()->set_listen_port_range(port_first, port_last);
}
torrent::Object
get_encryption() {
auto encryption_modes = torrent::runtime::network_config()->encryption_modes();
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_HANDSHAKE, encryption_modes.first) + "," +
torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_STREAM, encryption_modes.second);
}
torrent::Object
get_handshake_encryption() {
auto encryption_modes = torrent::runtime::network_config()->encryption_modes();
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_MODE, encryption_modes.first);
}
torrent::Object
get_stream_encryption() {
auto encryption_modes = torrent::runtime::network_config()->encryption_modes();
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_MODE, encryption_modes.second);
}
torrent::Object
apply_obsolete_encryption(const torrent::Object::list_type& args) {
torrent::encryption_mode handshake_mode{torrent::ENCRYPTION_MODE_ALLOW};
torrent::encryption_mode stream_mode{torrent::ENCRYPTION_MODE_ALLOW};
for (auto& itr : args) {
auto arg = itr.as_string();
if (arg == "none") {
handshake_mode = torrent::ENCRYPTION_MODE_DENY;
stream_mode = torrent::ENCRYPTION_MODE_DENY;
break;
} else if (arg == "allow_incoming") {
} else if (arg == "try_outgoing") {
} else if (arg == "require") {
handshake_mode = torrent::ENCRYPTION_MODE_REQUIRE;
} else if (arg == "require_RC4" || arg == "require_rc4") {
handshake_mode = torrent::ENCRYPTION_MODE_REQUIRE;
stream_mode = torrent::ENCRYPTION_MODE_REQUIRE;
break;
} else if (arg == "enable_retry") {
} else if (arg == "prefer_plaintext") {
} else {
throw torrent::input_error("Invalid encryption option: '" + arg + "'");
}
} }
torrent::runtime::network_config()->set_encryption_options(options_mask); lt_log_print(torrent::LOG_WARN, "Obsolete encryption options used, use 'handshake_{deny,allow,prefer,require}, stream_{deny,allow,prefer,require}' instead.");
return torrent::Object(); torrent::runtime::network_config()->set_encryption_modes(handshake_mode, stream_mode);
return {};
}
torrent::Object
apply_encryption(const torrent::Object::list_type& args) {
if (args.empty())
throw torrent::input_error("No encryption options specified.");
torrent::encryption_mode encryption_mode, handshake_mode, stream_mode;
if (args.size() == 1) {
try {
encryption_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_MODE, args.front().as_string()));
} catch (torrent::input_error& e) {
return apply_obsolete_encryption(args);
}
torrent::runtime::network_config()->set_encryption_modes(encryption_mode, encryption_mode);
return {};
}
if (args.size() != 2)
return apply_obsolete_encryption(args);
try {
handshake_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_HANDSHAKE, args.front().as_string()));
stream_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_STREAM, args.back().as_string()));
} catch (torrent::input_error& e) {
return apply_obsolete_encryption(args);
}
torrent::runtime::network_config()->set_encryption_modes(handshake_mode, stream_mode);
return {};
} }
torrent::Object torrent::Object
@@ -61,8 +158,6 @@ apply_tos(const torrent::Object::string_type& arg) {
return torrent::Object(); return torrent::Object();
} }
torrent::Object apply_encoding_list(const std::string& arg) { torrent::encoding_list()->push_back(arg); return torrent::Object(); }
void void
initialize_rpc_handlers() { initialize_rpc_handlers() {
rpc::rpc.initialize_handlers(); rpc::rpc.initialize_handlers();
@@ -221,106 +316,107 @@ initialize_command_network() {
auto http_stack = torrent::net_thread::http_stack(); auto http_stack = torrent::net_thread::http_stack();
auto nw_config = torrent::runtime::network_config(); auto nw_config = torrent::runtime::network_config();
CMD2_ANY_STRING ("encoding.add", std::bind(&apply_encoding_list, std::placeholders::_2)); CMD_ANY ("network.listen.port", [](auto, auto) { return torrent::runtime::network_manager()->listen_port(); });
CMD_ANY_VALUE_V ("network.listen.port.set", [](auto, auto& value) { return torrent::runtime::network_manager()->set_listen_port(value); });
CMD_ANY ("network.listen.port.random", [](auto, auto) { return torrent::runtime::client_config()->listen_port_random(); });
CMD_ANY_VALUE_V ("network.listen.port.random.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_listen_port_random(value); });
CMD_ANY ("network.listen.port.range", [](auto, auto) { return listen_port_range(); });
CMD_ANY_STRING_V("network.listen.port.range.set", [](auto, auto& value) { return set_listen_port_range(value); });
CMD_ANY ("network.listen.backlog", [](auto, auto) { return torrent::runtime::network_config()->listen_backlog(); });
CMD_ANY_VALUE_V ("network.listen.backlog.set", [](auto, auto& value) { return torrent::runtime::network_config()->set_listen_backlog(value); });
// Isn't port_open used? CMD_ANY ("protocol.pex", [](auto, auto) { return torrent::runtime::client_config()->is_pex_enabled(); });
CMD2_VAR_BOOL ("network.port_open", true); CMD_ANY_VALUE_V ("protocol.pex.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_pex_enabled(value); });
CMD2_VAR_BOOL ("network.port_random", true);
CMD2_VAR_STRING ("network.port_range", "6881-6999");
CMD2_ANY ("network.listen.port", [](auto, auto) { return torrent::runtime::listen_port(); }); CMD_ANY_LIST ("protocol.encryption", [](auto, auto) { return get_encryption(); });
CMD2_ANY ("network.listen.backlog", [nw_config](auto, auto) { return nw_config->listen_backlog(); }); CMD_ANY_LIST ("protocol.encryption.set", [](auto, auto& args) { return apply_encryption(args); });
CMD2_ANY_VALUE_V ("network.listen.backlog.set", [nw_config](auto, auto& value) { return nw_config->set_listen_backlog(value); }); CMD_ANY_LIST ("protocol.encryption.handshake", [](auto, auto) { return get_handshake_encryption(); });
CMD_ANY_LIST ("protocol.encryption.stream", [](auto, auto) { return get_stream_encryption(); });
CMD2_VAR_BOOL ("protocol.pex", true); CMD_VAR_STRING ("protocol.connection.leech", "leech");
CMD2_ANY_LIST ("protocol.encryption.set", [](auto, auto& args) { return apply_encryption(args); }); CMD_VAR_STRING ("protocol.connection.seed", "seed");
CMD2_VAR_STRING ("protocol.connection.leech", "leech"); CMD_VAR_STRING ("protocol.choke_heuristics.up.leech", "upload_leech");
CMD2_VAR_STRING ("protocol.connection.seed", "seed"); CMD_VAR_STRING ("protocol.choke_heuristics.up.seed", "upload_leech");
CMD_VAR_STRING ("protocol.choke_heuristics.down.leech", "download_leech");
CMD_VAR_STRING ("protocol.choke_heuristics.down.seed", "download_leech");
CMD2_VAR_STRING ("protocol.choke_heuristics.up.leech", "upload_leech"); CMD_ANY ("network.http.cacert", [http_stack](auto, auto) { return http_stack->http_cacert(); });
CMD2_VAR_STRING ("protocol.choke_heuristics.up.seed", "upload_leech"); CMD_ANY_STRING_V("network.http.cacert.set", [http_stack](auto, auto& str) { return http_stack->set_http_cacert(str); });
CMD2_VAR_STRING ("protocol.choke_heuristics.down.leech", "download_leech"); CMD_ANY ("network.http.capath", [http_stack](auto, auto) { return http_stack->http_capath(); });
CMD2_VAR_STRING ("protocol.choke_heuristics.down.seed", "download_leech"); CMD_ANY_STRING_V("network.http.capath.set", [http_stack](auto, auto& str) { return http_stack->set_http_capath(str); });
CMD_ANY ("network.http.dns_cache_timeout", [http_stack](auto, auto) { return http_stack->dns_timeout(); });
CMD_ANY_VALUE_V ("network.http.dns_cache_timeout.set", [http_stack](auto, auto& value) { return http_stack->set_dns_timeout(value); });
CMD_ANY ("network.http.current_open", [http_stack](auto, auto) { return http_stack->size(); });
CMD_ANY ("network.http.max_cache_connections", [http_stack](auto, auto) { return http_stack->max_cache_connections(); });
CMD_ANY_VALUE_V ("network.http.max_cache_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_cache_connections(value); });
CMD_ANY ("network.http.max_host_connections", [http_stack](auto, auto) { return http_stack->max_host_connections(); });
CMD_ANY_VALUE_V ("network.http.max_host_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_host_connections(value); });
CMD_ANY ("network.http.max_total_connections", [http_stack](auto, auto) { return http_stack->max_total_connections(); });
CMD2_ANY ("network.http.cacert", [http_stack](auto, auto) { return http_stack->http_cacert(); }); CMD_ANY ("network.http.ssl_verify_host", [http_stack](auto, auto) { return http_stack->ssl_verify_host(); });
CMD2_ANY_STRING_V("network.http.cacert.set", [http_stack](auto, auto& str) { return http_stack->set_http_cacert(str); }); CMD_ANY_VALUE_V ("network.http.ssl_verify_host.set", [http_stack](auto, auto& value) { return http_stack->set_ssl_verify_host(value); });
CMD2_ANY ("network.http.capath", [http_stack](auto, auto) { return http_stack->http_capath(); }); CMD_ANY ("network.http.ssl_verify_peer", [http_stack](auto, auto) { return http_stack->ssl_verify_peer(); });
CMD2_ANY_STRING_V("network.http.capath.set", [http_stack](auto, auto& str) { return http_stack->set_http_capath(str); }); CMD_ANY_VALUE_V ("network.http.ssl_verify_peer.set", [http_stack](auto, auto& value) { return http_stack->set_ssl_verify_peer(value); });
CMD2_ANY ("network.http.dns_cache_timeout", [http_stack](auto, auto) { return http_stack->dns_timeout(); });
CMD2_ANY_VALUE_V ("network.http.dns_cache_timeout.set", [http_stack](auto, auto& value) { return http_stack->set_dns_timeout(value); });
CMD2_ANY ("network.http.current_open", [http_stack](auto, auto) { return http_stack->size(); });
CMD2_ANY ("network.http.max_cache_connections", [http_stack](auto, auto) { return http_stack->max_cache_connections(); });
CMD2_ANY_VALUE_V ("network.http.max_cache_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_cache_connections(value); });
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(); });
CMD2_ANY_VALUE_V ("network.http.ssl_verify_host.set", [http_stack](auto, auto& value) { return http_stack->set_ssl_verify_host(value); });
CMD2_ANY ("network.http.ssl_verify_peer", [http_stack](auto, auto) { return http_stack->ssl_verify_peer(); });
CMD2_ANY_VALUE_V ("network.http.ssl_verify_peer.set", [http_stack](auto, auto& value) { return http_stack->set_ssl_verify_peer(value); });
CMD2_ANY ("network.send_buffer.size", [nw_config](auto, auto) { return nw_config->send_buffer_size(); }); CMD_ANY ("network.send_buffer.size", [nw_config](auto, auto) { return nw_config->send_buffer_size(); });
CMD2_ANY_VALUE_V ("network.send_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_send_buffer_size(value); }); CMD_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(); }); CMD_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); }); CMD_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); }); CMD_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(); }); CMD_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); }); CMD_ANY_STRING_V("network.bind_address.set", [nw_config](auto, auto& str) { return nw_config->set_bind_address_str(str); });
CMD2_ANY ("network.bind_address.ipv4", [nw_config](auto, auto) { return nw_config->bind_inet_address_str(); }); CMD_ANY ("network.bind_address.ipv4", [nw_config](auto, auto) { return nw_config->bind_inet_address_str(); });
CMD2_ANY_STRING_V("network.bind_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet_address_str(str); }); CMD_ANY_STRING_V("network.bind_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet_address_str(str); });
CMD2_ANY ("network.bind_address.ipv6", [nw_config](auto, auto) { return nw_config->bind_inet6_address_str(); }); CMD_ANY ("network.bind_address.ipv6", [nw_config](auto, auto) { return nw_config->bind_inet6_address_str(); });
CMD2_ANY_STRING_V("network.bind_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet6_address_str(str); }); CMD_ANY_STRING_V("network.bind_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet6_address_str(str); });
CMD2_ANY ("network.local_address", [nw_config](auto, auto) { return nw_config->local_address_best_match_str(); }); CMD_ANY ("network.local_address", [nw_config](auto, auto) { return nw_config->local_address_best_match_str(); });
CMD2_ANY_STRING_V("network.local_address.set", [nw_config](auto, auto& str) { return nw_config->set_local_address_str(str); }); CMD_ANY_STRING_V("network.local_address.set", [nw_config](auto, auto& str) { return nw_config->set_local_address_str(str); });
CMD2_ANY ("network.local_address.ipv4", [nw_config](auto, auto) { return nw_config->local_inet_address_str(); }); CMD_ANY ("network.local_address.ipv4", [nw_config](auto, auto) { return nw_config->local_inet_address_str(); });
CMD2_ANY_STRING_V("network.local_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet_address_str(str); }); CMD_ANY_STRING_V("network.local_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet_address_str(str); });
CMD2_ANY ("network.local_address.ipv6", [nw_config](auto, auto) { return nw_config->local_inet6_address_str(); }); CMD_ANY ("network.local_address.ipv6", [nw_config](auto, auto) { return nw_config->local_inet6_address_str(); });
CMD2_ANY_STRING_V("network.local_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet6_address_str(str); }); CMD_ANY_STRING_V("network.local_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet6_address_str(str); });
CMD2_ANY ("network.proxy_address", [nw_config](auto, auto) { return nw_config->proxy_address_str(); }); CMD_ANY ("network.proxy.global", [](auto, auto) { return torrent::runtime::proxy_manager()->proxy_url(); });
CMD2_ANY_STRING_V("network.proxy_address.set", [](auto, auto& str) { return control->core()->set_proxy_address(str); }); CMD_ANY_STRING_V("network.proxy.global.set", [](auto, auto& str) { return torrent::runtime::proxy_manager()->set_proxy_url(str); });
CMD_ANY ("network.proxy.http", [](auto, auto) { return torrent::runtime::proxy_manager()->http_proxy_url(); });
CMD_ANY_STRING_V("network.proxy.http.set", [](auto, auto& str) { return torrent::runtime::proxy_manager()->set_http_proxy_url(str); });
CMD2_ANY ("network.open_files", [file_manager](auto, auto) { return file_manager->open_files(); }); CMD_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(); }); CMD_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); }); CMD_ANY ("network.total_handshakes", [](auto, auto) { return torrent::runtime::total_handshakes(); });
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)); CMD_ANY_STRING ("network.scgi.open_port", [](auto, auto& arg) { return apply_scgi(arg, 1); });
CMD2_ANY_STRING ("network.scgi.open_local", std::bind(&apply_scgi, std::placeholders::_2, 2)); CMD_ANY_STRING ("network.scgi.open_local", [](auto, auto& arg) { return apply_scgi(arg, 2); });
CMD2_VAR_BOOL ("network.scgi.dont_route", false); CMD_VAR_BOOL ("network.scgi.dont_route", false);
CMD2_ANY ("network.scgi.open_systemd", [](auto, auto) { return apply_scgi_systemd(); }); CMD_ANY ("network.scgi.open_systemd", [](auto, auto) { return apply_scgi_systemd(); });
CMD2_ANY ("network.scgi.use_gzip", [](const auto&, const auto&) { return rpc::rpc.scgi_allow_compression(); }); CMD_ANY ("network.scgi.use_gzip", [](auto, auto) { return rpc::rpc.scgi_allow_compression(); });
CMD2_ANY_VALUE_V ("network.scgi.use_gzip.set", [](const auto&, const auto& arg) { return rpc::rpc.set_scgi_allow_compression(arg); }); CMD_ANY_VALUE_V ("network.scgi.use_gzip.set", [](auto, auto& arg) { return rpc::rpc.set_scgi_allow_compression(arg); });
CMD2_ANY ("network.scgi.gzip.min_size", [](const auto&, const auto&) { return rpc::rpc.scgi_min_compress_size(); }); CMD_ANY ("network.scgi.gzip.min_size", [](auto, auto) { return rpc::rpc.scgi_min_compress_size(); });
CMD2_ANY_VALUE_V ("network.scgi.gzip.min_size.set", [](const auto&, const auto& arg) { return rpc::rpc.set_scgi_min_compress_size(arg); }); CMD_ANY_VALUE_V ("network.scgi.gzip.min_size.set", [](auto, auto& arg) { return rpc::rpc.set_scgi_min_compress_size(arg); });
CMD2_ANY_STRING ("network.xmlrpc.dialect.set", [](const auto&, const auto& arg) { return apply_xmlrpc_dialect(arg); }) CMD_ANY_STRING ("network.xmlrpc.dialect.set", [](auto, auto& arg) { return apply_xmlrpc_dialect(arg); })
CMD2_ANY ("network.xmlrpc.size_limit", [](const auto&, const auto&) { return rpc::rpc.size_limit(); }); CMD_ANY ("network.xmlrpc.size_limit", [](auto, auto) { return rpc::rpc.size_limit(); });
CMD2_ANY_VALUE_V ("network.xmlrpc.size_limit.set", [](const auto&, const auto& arg) { return rpc::rpc.set_size_limit(arg); }); CMD_ANY_VALUE_V ("network.xmlrpc.size_limit.set", [](auto, auto& arg) { return rpc::rpc.set_size_limit(arg); });
CMD2_VAR_BOOL ("network.rpc.use_xmlrpc", true); CMD_ANY ("network.rpc.use_xmlrpc", [](auto, auto) { return rpc::rpc.use_xmlrpc(); });
CMD2_VAR_BOOL ("network.rpc.use_jsonrpc", true); CMD_ANY_VALUE_V ("network.rpc.use_xmlrpc.set", [](auto, auto& arg) { return rpc::rpc.set_use_xmlrpc(arg); });
CMD_ANY ("network.rpc.use_jsonrpc", [](auto, auto) { return rpc::rpc.use_jsonrpc(); });
CMD_ANY_VALUE_V ("network.rpc.use_jsonrpc.set", [](auto, auto& arg) { return rpc::rpc.set_use_jsonrpc(arg); });
CMD2_ANY ("network.block.ipv4", [nw_config](auto, auto) { return nw_config->is_block_ipv4(); }); CMD_ANY ("network.block.ipv4", [nw_config](auto, auto) { return nw_config->is_block_ipv4(); });
CMD2_ANY_VALUE_V ("network.block.ipv4.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4(value); }); CMD_ANY_VALUE_V ("network.block.ipv4.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4(value); });
CMD2_ANY ("network.block.ipv6", [nw_config](auto, auto) { return nw_config->is_block_ipv6(); }); CMD_ANY ("network.block.ipv6", [nw_config](auto, auto) { return nw_config->is_block_ipv6(); });
CMD2_ANY_VALUE_V ("network.block.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv6(value); }); CMD_ANY_VALUE_V ("network.block.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv6(value); });
CMD2_ANY ("network.block.ipv4in6", [nw_config](auto, auto) { return nw_config->is_block_ipv4in6(); }); CMD_ANY ("network.block.ipv4in6", [nw_config](auto, auto) { return nw_config->is_block_ipv4in6(); });
CMD2_ANY_VALUE_V ("network.block.ipv4in6.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4in6(value); }); CMD_ANY_VALUE_V ("network.block.ipv4in6.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4in6(value); });
CMD2_ANY ("network.block.outgoing", [nw_config](auto, auto) { return nw_config->is_block_outgoing(); }); CMD_ANY ("network.block.outgoing", [nw_config](auto, auto) { return nw_config->is_block_outgoing(); });
CMD2_ANY_VALUE_V ("network.block.outgoing.set", [nw_config](auto, auto& value) { return nw_config->set_block_outgoing(value); }); CMD_ANY_VALUE_V ("network.block.outgoing.set", [nw_config](auto, auto& value) { return nw_config->set_block_outgoing(value); });
CMD2_ANY ("network.prefer.ipv6", [nw_config](auto, auto) { return nw_config->is_prefer_ipv6(); }); CMD_ANY ("network.prefer.ipv6", [nw_config](auto, auto) { return nw_config->is_prefer_ipv6(); });
CMD2_ANY_VALUE_V ("network.prefer.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_prefer_ipv6(value); }); CMD_ANY_VALUE_V ("network.prefer.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_prefer_ipv6(value); });
rpc::rpc.mark_safe("network.port_open"); rpc::rpc.mark_safe("network.port_open");
rpc::rpc.mark_safe("network.port_random"); rpc::rpc.mark_safe("network.port_random");
@@ -333,10 +429,9 @@ initialize_command_network() {
rpc::rpc.mark_safe("network.http.max_host_connections"); rpc::rpc.mark_safe("network.http.max_host_connections");
rpc::rpc.mark_safe("network.http.max_total_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.open_files");
rpc::rpc.mark_safe("network.max_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.send_buffer.size");
rpc::rpc.mark_safe("network.receive_buffer.size"); rpc::rpc.mark_safe("network.receive_buffer.size");
@@ -344,11 +439,13 @@ initialize_command_network() {
rpc::rpc.mark_safe("network.local_address"); rpc::rpc.mark_safe("network.local_address");
rpc::rpc.mark_safe("network.xmlrpc.size_limit"); rpc::rpc.mark_safe("network.xmlrpc.size_limit");
rpc::rpc.mark_safe("network.open_sockets"); rpc::rpc.mark_safe("network.open_sockets");
rpc::rpc.mark_safe("network.http.cacert"); rpc::rpc.mark_safe("network.http.cacert");
rpc::rpc.mark_safe("network.http.capath"); rpc::rpc.mark_safe("network.http.capath");
rpc::rpc.mark_safe("network.http.proxy_address"); rpc::rpc.mark_safe("network.proxy.global");
rpc::rpc.mark_safe("network.proxy_address"); rpc::rpc.mark_safe("network.proxy.http");
rpc::rpc.mark_safe("network.scgi.dont_route"); rpc::rpc.mark_safe("network.scgi.dont_route");
rpc::rpc.mark_safe("protocol.pex"); rpc::rpc.mark_safe("protocol.pex");
rpc::rpc.mark_safe("network.rpc.use_xmlrpc"); rpc::rpc.mark_safe("network.rpc.use_xmlrpc");
+365
View File
@@ -0,0 +1,365 @@
#include "config.h"
#include <algorithm>
#include <cctype>
#include <iterator>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <torrent/exceptions.h>
#include <torrent/object.h>
#include "rpc/parse.h"
#include "rpc/rpc_manager.h"
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
namespace {
constexpr int64_t max_string_pad_size = 1 << 14;
const std::string whitespace_characters = " \t\n\r\f\v";
// The byte offset of every utf-8 character in 'text', terminated by the offset
// past the last character. Bytes that are not valid utf-8 lead bytes are
// treated as single characters.
std::vector<size_t>
utf8_offsets(const std::string& text) {
std::vector<size_t> offsets;
for (size_t i = 0; i < text.size(); i++)
if (i == 0 || (static_cast<unsigned char>(text[i]) & 0xC0) != 0x80)
offsets.push_back(i);
offsets.push_back(text.size());
return offsets;
}
int64_t
utf8_length(const std::string& text) {
int64_t result = 0;
for (size_t i = 0; i < text.size(); i++)
if (i == 0 || (static_cast<unsigned char>(text[i]) & 0xC0) != 0x80)
result++;
return result;
}
std::string
utf8_substr(const std::string& text, const std::vector<size_t>& offsets, size_t first, size_t last) {
return text.substr(offsets[first], offsets[last] - offsets[first]);
}
std::set<std::string>
utf8_character_set(const std::string& text) {
auto offsets = utf8_offsets(text);
std::set<std::string> result;
for (size_t i = 0; i + 1 < offsets.size(); i++)
result.insert(utf8_substr(text, offsets, i, i + 1));
return result;
}
std::string
ascii_lowercase(std::string text) {
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char c) { return std::tolower(c); });
return text;
}
// A 'max_count' of zero means the command accepts any number of arguments.
void
verify_argument_count(const char* name, const torrent::Object::list_type& args, size_t min_count, size_t max_count) {
if (args.size() < min_count || (max_count != 0 && args.size() > max_count))
throw torrent::input_error(std::string(name) + ": invalid number of arguments.");
}
const torrent::Object&
argument_at(const torrent::Object::list_type& args, size_t index) {
return *std::next(args.begin(), index);
}
void
flatten_argument(const torrent::Object& arg, std::vector<std::string>* dest) {
if (!arg.is_list()) {
dest->push_back(rpc::convert_to_string(arg));
return;
}
for (const auto& child : arg.as_list())
flatten_argument(child, dest);
}
// The {old, new} pairs shared by 'string.map' and 'string.replace'.
std::pair<std::string, std::string>
argument_to_pair(const char* name, const torrent::Object& arg) {
if (!arg.is_list() || arg.as_list().size() != 2)
throw torrent::input_error(std::string(name) + ": arguments after the text must be {old, new} pairs.");
return {rpc::convert_to_string(arg.as_list().front()), rpc::convert_to_string(arg.as_list().back())};
}
// Compares the first argument against every remaining argument, returning true
// as soon as one of them matches.
torrent::Object
apply_string_predicate(const char* name, const torrent::Object::list_type& args, bool (*predicate)(const std::string&, const std::string&)) {
verify_argument_count(name, args, 2, 0);
auto text = rpc::convert_to_string(args.front());
for (auto itr = std::next(args.begin()); itr != args.end(); itr++)
if (predicate(text, rpc::convert_to_string(*itr)))
return int64_t(1);
return int64_t(0);
}
bool
text_equals(const std::string& text, const std::string& other) {
return text == other;
}
bool
text_starts_with(const std::string& text, const std::string& prefix) {
return text.size() >= prefix.size() && text.compare(0, prefix.size(), prefix) == 0;
}
bool
text_ends_with(const std::string& text, const std::string& tail) {
return text.size() >= tail.size() && text.compare(text.size() - tail.size(), tail.size(), tail) == 0;
}
bool
text_contains(const std::string& text, const std::string& needle) {
return text.find(needle) != std::string::npos;
}
bool
text_contains_i(const std::string& text, const std::string& needle) {
return ascii_lowercase(text).find(ascii_lowercase(needle)) != std::string::npos;
}
torrent::Object
apply_string_length(const torrent::Object::list_type& args) {
verify_argument_count("string.length", args, 1, 1);
return utf8_length(rpc::convert_to_string(args.front()));
}
torrent::Object
apply_string_substr(const torrent::Object::list_type& args) {
verify_argument_count("string.substr", args, 1, 4);
auto text = rpc::convert_to_string(args.front());
auto offsets = utf8_offsets(text);
auto text_length = static_cast<int64_t>(offsets.size() - 1);
auto position = args.size() > 1 ? rpc::convert_to_value(argument_at(args, 1)) : 0;
auto fallback = args.size() > 3 ? rpc::convert_to_string(argument_at(args, 3)) : std::string();
// Negative positions are relative to the end of the string.
if (position < 0)
position += text_length;
if (position < 0 || position > text_length)
return fallback;
auto last = text_length;
if (args.size() > 2) {
auto count = rpc::convert_to_value(argument_at(args, 2));
if (count < 0)
throw torrent::input_error("string.substr: the character count cannot be negative.");
last = std::min(text_length, position + std::min(count, text_length));
}
return utf8_substr(text, offsets, position, last);
}
torrent::Object
apply_string_split(const torrent::Object::list_type& args) {
verify_argument_count("string.split", args, 2, 2);
auto text = rpc::convert_to_string(args.front());
auto delim = rpc::convert_to_string(args.back());
auto result = torrent::Object::create_list();
// An empty delimiter splits the text into its utf-8 characters.
if (delim.empty()) {
auto offsets = utf8_offsets(text);
for (size_t i = 0; i + 1 < offsets.size(); i++)
result.as_list().push_back(utf8_substr(text, offsets, i, i + 1));
return result;
}
size_t first = 0;
while (true) {
auto pos = text.find(delim, first);
if (pos == std::string::npos) {
result.as_list().push_back(text.substr(first));
return result;
}
result.as_list().push_back(text.substr(first, pos - first));
first = pos + delim.size();
}
}
torrent::Object
apply_string_join(const torrent::Object::list_type& args) {
verify_argument_count("string.join", args, 1, 0);
auto delim = rpc::convert_to_string(args.front());
std::vector<std::string> parts;
for (auto itr = std::next(args.begin()); itr != args.end(); itr++)
flatten_argument(*itr, &parts);
std::string result;
for (auto itr = parts.begin(); itr != parts.end(); itr++) {
if (itr != parts.begin())
result += delim;
result += *itr;
}
return result;
}
torrent::Object
apply_string_pad(const char* name, const torrent::Object::list_type& args, bool pad_start) {
verify_argument_count(name, args, 2, 3);
auto text = rpc::convert_to_string(args.front());
auto pad_size = rpc::convert_to_value(argument_at(args, 1));
auto padding = args.size() > 2 ? rpc::convert_to_string(argument_at(args, 2)) : std::string(" ");
auto text_length = utf8_length(text);
if (pad_size <= text_length || padding.empty())
return text;
if (pad_size - text_length > max_string_pad_size)
throw torrent::input_error(std::string(name) + ": padding is too large.");
auto padding_offsets = utf8_offsets(padding);
auto padding_length = static_cast<int64_t>(padding_offsets.size() - 1);
std::string result;
result.reserve(pad_size - text_length);
for (int64_t i = 0; i < pad_size - text_length; i++) {
auto index = static_cast<size_t>(i % padding_length);
result += utf8_substr(padding, padding_offsets, index, index + 1);
}
return pad_start ? result + text : text + result;
}
torrent::Object
apply_string_strip(const char* name, const torrent::Object::list_type& args, bool strip_start, bool strip_end) {
verify_argument_count(name, args, 1, 0);
auto text = rpc::convert_to_string(args.front());
std::string strippable;
for (auto itr = std::next(args.begin()); itr != args.end(); itr++)
strippable += rpc::convert_to_string(*itr);
if (args.size() == 1)
strippable = whitespace_characters;
auto characters = utf8_character_set(strippable);
auto offsets = utf8_offsets(text);
size_t first = 0;
size_t last = offsets.size() - 1;
while (strip_start && first < last && characters.count(utf8_substr(text, offsets, first, first + 1)) != 0)
first++;
while (strip_end && last > first && characters.count(utf8_substr(text, offsets, last - 1, last)) != 0)
last--;
return utf8_substr(text, offsets, first, last);
}
torrent::Object
apply_string_map(const torrent::Object::list_type& args) {
verify_argument_count("string.map", args, 2, 0);
auto text = rpc::convert_to_string(args.front());
for (auto itr = std::next(args.begin()); itr != args.end(); itr++) {
auto pair = argument_to_pair("string.map", *itr);
if (text == pair.first)
return pair.second;
}
return text;
}
torrent::Object
apply_string_replace(const torrent::Object::list_type& args) {
verify_argument_count("string.replace", args, 2, 0);
auto text = rpc::convert_to_string(args.front());
for (auto itr = std::next(args.begin()); itr != args.end(); itr++) {
auto pair = argument_to_pair("string.replace", *itr);
if (pair.first.empty())
throw torrent::input_error("string.replace: the replaced text cannot be empty.");
for (auto pos = text.find(pair.first); pos != std::string::npos; pos = text.find(pair.first, pos + pair.second.size()))
text.replace(pos, pair.first.size(), pair.second);
}
return text;
}
}
void
initialize_command_string() {
// clang-format off
CMD2_ANY_LIST("string.length", [](auto, const auto& args) { return apply_string_length(args); });
CMD2_ANY_LIST("string.substr", [](auto, const auto& args) { return apply_string_substr(args); });
CMD2_ANY_LIST("string.split", [](auto, const auto& args) { return apply_string_split(args); });
CMD2_ANY_LIST("string.join", [](auto, const auto& args) { return apply_string_join(args); });
CMD2_ANY_LIST("string.map", [](auto, const auto& args) { return apply_string_map(args); });
CMD2_ANY_LIST("string.replace", [](auto, const auto& args) { return apply_string_replace(args); });
CMD2_ANY_LIST("string.equals", [](auto, const auto& args) { return apply_string_predicate("string.equals", args, &text_equals); });
CMD2_ANY_LIST("string.starts_with", [](auto, const auto& args) { return apply_string_predicate("string.starts_with", args, &text_starts_with); });
CMD2_ANY_LIST("string.ends_with", [](auto, const auto& args) { return apply_string_predicate("string.ends_with", args, &text_ends_with); });
CMD2_ANY_LIST("string.contains", [](auto, const auto& args) { return apply_string_predicate("string.contains", args, &text_contains); });
CMD2_ANY_LIST("string.contains_i", [](auto, const auto& args) { return apply_string_predicate("string.contains_i", args, &text_contains_i); });
CMD2_ANY_LIST("string.lpad", [](auto, const auto& args) { return apply_string_pad("string.lpad", args, true); });
CMD2_ANY_LIST("string.rpad", [](auto, const auto& args) { return apply_string_pad("string.rpad", args, false); });
CMD2_ANY_LIST("string.strip", [](auto, const auto& args) { return apply_string_strip("string.strip", args, true, true); });
CMD2_ANY_LIST("string.lstrip", [](auto, const auto& args) { return apply_string_strip("string.lstrip", args, true, false); });
CMD2_ANY_LIST("string.rstrip", [](auto, const auto& args) { return apply_string_strip("string.rstrip", args, false, true); });
// clang-format on
for (const auto name : {"string.length", "string.substr", "string.split", "string.join", "string.map", "string.replace",
"string.equals", "string.starts_with", "string.ends_with", "string.contains", "string.contains_i",
"string.lpad", "string.rpad", "string.strip", "string.lstrip", "string.rstrip"})
rpc::rpc.mark_safe(name);
}
+17 -30
View File
@@ -70,33 +70,35 @@ parse_address_range(const torrent::Object::list_type& args, torrent::Object::lis
torrent::Object torrent::Object
apply_throttle(const torrent::Object::list_type& args, bool up) { apply_throttle(const torrent::Object::list_type& args, bool up) {
torrent::Object::list_const_iterator argItr = args.begin(); auto arg_itr = args.begin();
if (argItr == args.end()) if (arg_itr == args.end())
throw torrent::input_error("Missing throttle name."); throw torrent::input_error("Missing throttle name.");
const std::string& name = argItr->as_string(); const std::string& name = arg_itr->as_string();
if (name.empty() || name == "NULL") if (name.empty() || name == "NULL")
throw torrent::input_error("Invalid throttle name '" + name + "'."); throw torrent::input_error("Invalid throttle name '" + name + "'.");
if (++argItr == args.end() || argItr->as_string().empty()) if (++arg_itr == args.end() || arg_itr->as_string().empty())
throw torrent::input_error("Missing throttle rate for '" + name + "'."); throw torrent::input_error("Missing throttle rate for '" + name + "'.");
int64_t rate; int64_t rate;
rpc::parse_whole_value_nothrow(argItr->as_string().c_str(), &rate); rpc::parse_whole_value_nothrow(arg_itr->as_string().c_str(), &rate);
if (rate < 0) if (rate < 0)
throw torrent::input_error("Throttle rate must be non-negative."); throw torrent::input_error("Throttle rate must be non-negative.");
core::ThrottleMap::iterator itr = control->core()->throttles().find(name); auto itr = control->core()->throttles().find(name);
if (itr == control->core()->throttles().end())
itr = control->core()->throttles().insert(std::make_pair(name, torrent::ThrottlePair(NULL, NULL))).first;
torrent::Throttle*& throttle = up ? itr->second.first : itr->second.second; if (itr == control->core()->throttles().end())
if (rate != 0 && throttle == NULL) itr = control->core()->throttles().insert(std::make_pair(name, core::ThrottlePair(nullptr, nullptr))).first;
auto*& throttle = up ? itr->second.first : itr->second.second;
if (rate != 0 && throttle == nullptr)
throttle = (up ? torrent::up_throttle_global() : torrent::down_throttle_global())->create_slave(); throttle = (up ? torrent::up_throttle_global() : torrent::down_throttle_global())->create_slave();
if (throttle != NULL) if (throttle != nullptr)
throttle->set_max_rate(rate * 1024); throttle->set_max_rate(rate * 1024);
return torrent::Object(); return torrent::Object();
@@ -109,10 +111,10 @@ static const int throttle_info_rate = (1 << 3);
torrent::Object torrent::Object
retrieve_throttle_info(const torrent::Object::string_type& name, int flags) { retrieve_throttle_info(const torrent::Object::string_type& name, int flags) {
core::ThrottleMap::iterator itr = control->core()->throttles().find(name); auto itr = control->core()->throttles().find(name);
torrent::ThrottlePair throttles = itr == control->core()->throttles().end() ? torrent::ThrottlePair(NULL, NULL) : itr->second; auto throttles = (itr == control->core()->throttles().end()) ? core::ThrottlePair(nullptr, nullptr) : itr->second;
torrent::Throttle* throttle = flags & throttle_info_down ? throttles.second : throttles.first; auto* throttle = flags & throttle_info_down ? throttles.second : throttles.first;
torrent::Throttle* global = flags & throttle_info_down ? torrent::down_throttle_global() : torrent::up_throttle_global(); auto* global = flags & throttle_info_down ? torrent::down_throttle_global() : torrent::up_throttle_global();
if (throttle == NULL && name.empty()) if (throttle == NULL && name.empty())
throttle = global; throttle = global;
@@ -127,20 +129,6 @@ retrieve_throttle_info(const torrent::Object::string_type& name, int flags) {
return (int64_t)throttle->max_rate(); return (int64_t)throttle->max_rate();
} }
torrent::Object
apply_address_throttle(const torrent::Object::list_type& args) {
if (args.size() < 2 || args.size() > 3)
throw torrent::input_error("Incorrect number of arguments.");
std::pair<uint32_t, uint32_t> range = parse_address_range(args, ++args.begin());
core::ThrottleMap::iterator throttleItr = control->core()->throttles().find(args.begin()->as_string().c_str());
if (throttleItr == control->core()->throttles().end())
throw torrent::input_error("Throttle not found.");
control->core()->set_address_throttle(range.first, range.second, throttleItr->second);
return torrent::Object();
}
torrent::Object torrent::Object
throttle_update(const char* variable, int64_t value) { throttle_update(const char* variable, int64_t value) {
rpc::commands.call_command(variable, value); rpc::commands.call_command(variable, value);
@@ -198,7 +186,6 @@ initialize_command_throttle() {
// than kB. // than kB.
CMD2_ANY_LIST ("throttle.up", std::bind(&apply_throttle, std::placeholders::_2, true)); CMD2_ANY_LIST ("throttle.up", std::bind(&apply_throttle, std::placeholders::_2, true));
CMD2_ANY_LIST ("throttle.down", std::bind(&apply_throttle, std::placeholders::_2, false)); CMD2_ANY_LIST ("throttle.down", std::bind(&apply_throttle, std::placeholders::_2, false));
CMD2_ANY_LIST ("throttle.ip", std::bind(&apply_address_throttle, std::placeholders::_2));
CMD2_ANY_STRING ("throttle.up.max", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_up | throttle_info_max)); CMD2_ANY_STRING ("throttle.up.max", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_up | throttle_info_max));
CMD2_ANY_STRING ("throttle.up.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_up | throttle_info_rate)); CMD2_ANY_STRING ("throttle.up.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_up | throttle_info_rate));
+41 -43
View File
@@ -81,73 +81,71 @@ apply_enable_trackers(int64_t arg) {
void void
initialize_command_tracker() { initialize_command_tracker() {
CMD2_TRACKER ("t.is_busy", std::bind(&torrent::tracker::Tracker::is_requesting, std::placeholders::_1)); CMD2_TRACKER ("t.is_busy", [](auto* tracker, auto) { return tracker->is_requesting(); });
CMD2_TRACKER ("t.is_enabled", std::bind(&torrent::tracker::Tracker::is_enabled, std::placeholders::_1)); CMD2_TRACKER ("t.is_enabled", [](auto* tracker, auto) { return tracker->is_enabled(); });
CMD2_TRACKER ("t.is_extra_tracker", std::bind(&torrent::tracker::Tracker::is_extra_tracker, std::placeholders::_1)); CMD2_TRACKER ("t.is_extra_tracker", [](auto* tracker, auto) { return tracker->is_extra_tracker(); });
CMD2_TRACKER ("t.is_open", std::bind(&torrent::tracker::Tracker::is_requesting, std::placeholders::_1)); CMD2_TRACKER ("t.is_open", [](auto* tracker, auto) { return tracker->is_requesting(); });
CMD2_TRACKER ("t.is_scrapable", std::bind(&torrent::tracker::Tracker::is_scrapable, std::placeholders::_1)); CMD2_TRACKER ("t.is_scrapable", [](auto* tracker, auto) { return tracker->is_scrapable(); });
CMD2_TRACKER ("t.is_usable", std::bind(&torrent::tracker::Tracker::is_usable, std::placeholders::_1)); CMD2_TRACKER ("t.is_usable", [](auto* tracker, auto) { return tracker->is_usable(); });
// TODO: Deprecate. // TODO: Deprecate.
CMD2_TRACKER ("t.can_scrape", std::bind(&torrent::tracker::Tracker::is_scrapable, std::placeholders::_1)); CMD2_TRACKER ("t.can_scrape", [](auto* tracker, auto) { return tracker->is_scrapable(); });
CMD2_TRACKER_V ("t.enable", std::bind(&torrent::tracker::Tracker::enable, std::placeholders::_1)); CMD2_TRACKER_V ("t.enable", [](auto* tracker, auto) { tracker->enable(); });
CMD2_TRACKER_V ("t.disable", std::bind(&torrent::tracker::Tracker::disable, std::placeholders::_1)); CMD2_TRACKER_V ("t.disable", [](auto* tracker, auto) { tracker->disable(); });
CMD2_TRACKER_VALUE_V("t.is_enabled.set", std::bind(&tracker_set_enabled, std::placeholders::_1, std::placeholders::_2)); CMD2_TRACKER_VALUE_V("t.is_enabled.set", [](auto* tracker, auto& value) { return tracker_set_enabled(tracker, value); });
CMD2_TRACKER ("t.url", std::bind(&torrent::tracker::Tracker::url, std::placeholders::_1)); CMD2_TRACKER ("t.url", [](auto* tracker, auto) { return tracker->url(); });
CMD2_TRACKER ("t.group", std::bind(&torrent::tracker::Tracker::group, std::placeholders::_1)); CMD2_TRACKER ("t.group", [](auto* tracker, auto) { return tracker->group(); });
CMD2_TRACKER ("t.type", std::bind(&torrent::tracker::Tracker::type, std::placeholders::_1)); CMD2_TRACKER ("t.type", [](auto* tracker, auto) { return tracker->type(); });
CMD2_TRACKER ("t.id", std::bind(&torrent::tracker::Tracker::tracker_id, std::placeholders::_1)); CMD2_TRACKER ("t.id", [](auto* tracker, auto) { return tracker->tracker_id(); });
CMD2_TRACKER ("t.latest_event", [](auto* tracker, auto) { return tracker->state().latest_event(); }); CMD2_TRACKER ("t.latest_event", [](auto* tracker, auto) { return tracker->state().latest_event(); });
CMD2_TRACKER ("t.latest_new_peers", [](auto* tracker, auto) { return tracker->state().latest_new_peers(); }); CMD2_TRACKER ("t.latest_new_peers", [](auto* tracker, auto) { return tracker->state().latest_new_peers(); });
CMD2_TRACKER ("t.latest_sum_peers", [](auto* tracker, auto) { return tracker->state().latest_sum_peers(); }); CMD2_TRACKER ("t.latest_sum_peers", [](auto* tracker, auto) { return tracker->state().latest_sum_peers(); });
CMD2_TRACKER ("t.normal_interval", [](auto* tracker, auto) { return tracker->state().normal_interval(); }); CMD2_TRACKER ("t.normal_interval", [](auto* tracker, auto) { return tracker->state().normal_interval().count(); });
CMD2_TRACKER ("t.min_interval", [](auto* tracker, auto) { return tracker->state().min_interval(); }); CMD2_TRACKER ("t.min_interval", [](auto* tracker, auto) { return tracker->state().min_interval().count(); });
CMD2_TRACKER ("t.activity_time_next", [](auto* tracker, auto) { return tracker->state().activity_time_next(); }); CMD2_TRACKER ("t.activity_time_next", [](auto* tracker, auto) { return tracker->state().activity_time_next().count(); });
CMD2_TRACKER ("t.activity_time_last", [](auto* tracker, auto) { return tracker->state().activity_time_last(); }); CMD2_TRACKER ("t.activity_time_last", [](auto* tracker, auto) { return tracker->state().activity_time_last().count(); });
CMD2_TRACKER ("t.success_time_next", [](auto* tracker, auto) { return tracker->state().success_time_next(); }); CMD2_TRACKER ("t.success_time_next", [](auto* tracker, auto) { return tracker->state().success_time_next().count(); });
CMD2_TRACKER ("t.success_time_last", [](auto* tracker, auto) { return tracker->state().success_time_last(); }); CMD2_TRACKER ("t.success_time_last", [](auto* tracker, auto) { return tracker->state().success_time_last().count(); });
CMD2_TRACKER ("t.success_counter", [](auto* tracker, auto) { return tracker->state().success_counter(); }); CMD2_TRACKER ("t.success_counter", [](auto* tracker, auto) { return tracker->state().success_counter(); });
CMD2_TRACKER ("t.failed_time_next", [](auto* tracker, auto) { return tracker->state().failed_time_next(); }); CMD2_TRACKER ("t.failed_time_next", [](auto* tracker, auto) { return tracker->state().failed_time_next().count(); });
CMD2_TRACKER ("t.failed_time_last", [](auto* tracker, auto) { return tracker->state().failed_time_last(); }); CMD2_TRACKER ("t.failed_time_last", [](auto* tracker, auto) { return tracker->state().failed_time_last().count(); });
CMD2_TRACKER ("t.failed_counter", [](auto* tracker, auto) { return tracker->state().failed_counter(); }); CMD2_TRACKER ("t.failed_counter", [](auto* tracker, auto) { return tracker->state().failed_counter(); });
CMD2_TRACKER ("t.scrape_time_last", [](auto* tracker, auto) { return tracker->state().scrape_time_last(); }); CMD2_TRACKER ("t.scrape_time_last", [](auto* tracker, auto) { return tracker->state().scrape_time_last().count(); });
CMD2_TRACKER ("t.scrape_counter", [](auto* tracker, auto) { return tracker->state().scrape_counter(); }); CMD2_TRACKER ("t.scrape_counter", [](auto* tracker, auto) { return tracker->state().scrape_counter(); });
CMD2_TRACKER ("t.scrape_complete", [](auto* tracker, auto) { return tracker->state().scrape_complete(); }); CMD2_TRACKER ("t.scrape_complete", [](auto* tracker, auto) { return tracker->state().scrape_complete(); });
CMD2_TRACKER ("t.scrape_incomplete", [](auto* tracker, auto) { return tracker->state().scrape_incomplete(); }); CMD2_TRACKER ("t.scrape_incomplete", [](auto* tracker, auto) { return tracker->state().scrape_incomplete(); });
CMD2_TRACKER ("t.scrape_downloaded", [](auto* tracker, auto) { return tracker->state().scrape_downloaded(); }); CMD2_TRACKER ("t.scrape_downloaded", [](auto* tracker, auto) { return tracker->state().scrape_downloaded(); });
CMD2_ANY_VALUE ("trackers.enable", std::bind(&apply_enable_trackers, int64_t(1))); CMD2_ANY_VALUE ("trackers.enable", [](auto, auto) { return apply_enable_trackers(1); });
CMD2_ANY_VALUE ("trackers.disable", std::bind(&apply_enable_trackers, int64_t(0))); CMD2_ANY_VALUE ("trackers.disable", [](auto, auto) { return apply_enable_trackers(0); });
CMD2_VAR_BOOL ("trackers.delay_scrape", false); CMD2_VAR_BOOL ("trackers.delay_scrape", false);
CMD2_VAR_VALUE ("trackers.numwant", -1); CMD2_VAR_VALUE ("trackers.numwant", -1);
CMD2_ANY ("trackers.use_udp", [](auto, auto) { return true; }); CMD2_ANY ("trackers.use_udp", [](auto, auto) { return true; });
CMD2_ANY_VALUE_V ("trackers.use_udp.set", [](auto, auto) { CMD2_ANY_VALUE_V ("trackers.use_udp.set", [](auto, auto) {
lt_log_print(torrent::LOG_ERROR, "trackers.use_udp.set is no longer supported", 0); lt_log_print(torrent::LOG_ERROR, "trackers.use_udp.set is no longer supported", 0);
}) })
auto dht_manager = control->dht_manager(); CMD2_ANY_STRING_V ("dht.mode.set", [](auto, auto& str) { return control->dht_manager()->set_mode_by_user(str); });
CMD2_ANY ("dht.port", [](auto, auto) { return torrent::runtime::network_manager()->dht_controller()->port(); });
CMD2_ANY_STRING_V ("dht.mode.set", std::bind(&core::DhtManager::set_mode, control->dht_manager(), std::placeholders::_2));
CMD2_ANY ("dht.port", [](auto, auto) { return torrent::runtime::network_manager()->dht_controller()->port(); });
CMD2_ANY_VALUE_V ("dht.port.set", [](auto, auto) { CMD2_ANY_VALUE_V ("dht.port.set", [](auto, auto) {
lt_log_print(torrent::LOG_DHT_ERROR, "dht.port.set is no longer supported, use dht.override_port.set", 0); lt_log_print(torrent::LOG_DHT_ERROR, "dht.port.set is no longer supported, use dht.override_port.set", 0);
}); });
CMD2_ANY ("dht.override_port", std::bind(&torrent::runtime::NetworkConfig::override_dht_port, torrent::runtime::network_config())); CMD2_ANY ("dht.override_port", [](auto, auto) { return torrent::runtime::network_config()->override_dht_port(); });
CMD2_ANY_VALUE_V ("dht.override_port.set", std::bind(&torrent::runtime::NetworkConfig::set_override_dht_port, torrent::runtime::network_config(), std::placeholders::_2)); CMD2_ANY_VALUE_V ("dht.override_port.set", [](auto, auto& value) { return torrent::runtime::network_manager()->set_dht_port(value); });
CMD2_ANY_STRING ("dht.add_node", std::bind(&apply_dht_add_node, std::placeholders::_2)); CMD2_ANY_STRING ("dht.add_node", [](auto, auto& str) { return apply_dht_add_node(str); });
CMD2_ANY ("dht.statistics", std::bind(&core::DhtManager::dht_statistics, dht_manager)); CMD2_ANY ("dht.statistics", [](auto, auto) { return control->dht_manager()->dht_statistics(); });
rpc::rpc.mark_safe("t.url"); rpc::rpc.mark_safe("t.url");
rpc::rpc.mark_safe("t.group"); rpc::rpc.mark_safe("t.group");
+63 -58
View File
@@ -472,11 +472,14 @@ apply_to_throttle(const torrent::Object& rawArgs) {
// if (cond1) { branch1 } else if (cond2) { branch2 } else { branch3 } // if (cond1) { branch1 } else if (cond2) { branch2 } else { branch3 }
// <cond1>,<branch1>,<cond2>,<branch2>,<branch3> // <cond1>,<branch1>,<cond2>,<branch2>,<branch3>
torrent::Object torrent::Object
apply_if(rpc::target_type target, const torrent::Object& rawArgs, int flags) { apply_if(rpc::target_type target, const torrent::Object& raw_args, int flags) {
const torrent::Object::list_type& args = rawArgs.as_list(); auto& args = raw_args.as_list();
torrent::Object::list_const_iterator itr = args.begin(); 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; torrent::Object tmp;
const torrent::Object* conditional; const torrent::Object* conditional;
@@ -500,40 +503,44 @@ apply_if(rpc::target_type target, const torrent::Object& rawArgs, int flags) {
result = false; result = false;
break; break;
default: 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++; itr++;
if (result) if (!result && itr != args.end())
break; itr++;
itr++;
} }
if (itr == args.end()) if (itr == args.end())
return torrent::Object(); return torrent::Object();
if (flags & 0x1 && itr->is_string()) { if (flags & 0x1) {
return rpc::parse_command(target, itr->as_string().c_str(), itr->as_string().c_str() + itr->as_string().size()).first; 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()) { if (itr->is_dict_key())
return rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target); return rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target);
} else if (flags & 0x1 && itr->is_list()) { if (itr->is_list()) {
// Move this into a special function or something. Also, might be for (const auto& cmd_itr : itr->as_list()) {
// nice to have a parse_command function that takes list if (cmd_itr.is_string())
// iterator... rpc::parse_command(target, cmd_itr.as_string().c_str(), cmd_itr.as_string().c_str() + cmd_itr.as_string().size());
for (const auto& cmdItr : itr->as_list()) else if (cmd_itr.is_dict_key())
if (cmdItr.is_string()) rpc::commands.call_command(cmd_itr.as_dict_key().c_str(), cmd_itr.as_dict_obj(), target);
rpc::parse_command(target, cmdItr.as_string().c_str(), cmdItr.as_string().c_str() + cmdItr.as_string().size());
return torrent::Object(); else
throw torrent::input_error("Invalid command type in branch list.");
}
} else { return torrent::Object();
return *itr; }
throw torrent::input_error("Invalid command type in branch.");
} }
return *itr;
} }
torrent::Object torrent::Object
@@ -795,61 +802,59 @@ apply_set_color(int color_id, const torrent::Object::string_type& color_str) {
void void
initialize_command_ui() { initialize_command_ui() {
CMD2_VAR_STRING("keys.layout", "qwerty"); CMD2_VAR_STRING ("keys.layout", "qwerty");
CMD2_ANY_STRING("view.add", object_convert_void(std::bind(&core::ViewManager::insert_throw, control->view_manager(), std::placeholders::_2))); CMD2_ANY_STRING ("view.add", object_convert_void(std::bind(&core::ViewManager::insert_throw, control->view_manager(), std::placeholders::_2)));
CMD2_ANY_L ("view.list", std::bind(&apply_view_list)); CMD2_ANY_L ("view.list", std::bind(&apply_view_list));
CMD2_ANY_LIST("view.set", std::bind(&apply_view_set, std::placeholders::_2)); CMD2_ANY_LIST ("view.set", std::bind(&apply_view_set, std::placeholders::_2));
CMD2_ANY_LIST ("view.filter", std::bind(&apply_view_event, &core::ViewManager::set_filter, std::placeholders::_2)); CMD2_ANY_LIST ("view.filter", std::bind(&apply_view_event, &core::ViewManager::set_filter, std::placeholders::_2));
CMD2_ANY_LIST ("view.filter_on", std::bind(&apply_view_filter_on, std::placeholders::_2)); CMD2_ANY_LIST ("view.filter_on", std::bind(&apply_view_filter_on, std::placeholders::_2));
CMD2_ANY_LIST ("view.filter.temp", std::bind(&apply_view_event, &core::ViewManager::set_filter_temp, std::placeholders::_2)); CMD2_ANY_LIST ("view.filter.temp", std::bind(&apply_view_event, &core::ViewManager::set_filter_temp, std::placeholders::_2));
CMD2_VAR_STRING("view.filter.temp.excluded", "default,started,stopped"); CMD2_VAR_STRING ("view.filter.temp.excluded", "default,started,stopped");
CMD2_VAR_BOOL ("view.filter.temp.log", 0); CMD2_VAR_BOOL ("view.filter.temp.log", 0);
CMD2_ANY_LIST("view.sort", std::bind(&apply_view_sort, std::placeholders::_2)); CMD2_ANY_LIST ("view.sort", std::bind(&apply_view_sort, std::placeholders::_2));
CMD2_ANY_LIST("view.sort_new", std::bind(&apply_view_event, &core::ViewManager::set_sort_new, std::placeholders::_2)); CMD2_ANY_LIST ("view.sort_new", std::bind(&apply_view_event, &core::ViewManager::set_sort_new, std::placeholders::_2));
CMD2_ANY_LIST("view.sort_current", std::bind(&apply_view_event, &core::ViewManager::set_sort_current, std::placeholders::_2)); CMD2_ANY_LIST ("view.sort_current", std::bind(&apply_view_event, &core::ViewManager::set_sort_current, std::placeholders::_2));
CMD2_ANY_LIST("view.event_added", std::bind(&apply_view_event, &core::ViewManager::set_event_added, std::placeholders::_2)); CMD2_ANY_LIST ("view.event_added", std::bind(&apply_view_event, &core::ViewManager::set_event_added, std::placeholders::_2));
CMD2_ANY_LIST("view.event_removed", std::bind(&apply_view_event, &core::ViewManager::set_event_removed, std::placeholders::_2)); CMD2_ANY_LIST ("view.event_removed", std::bind(&apply_view_event, &core::ViewManager::set_event_removed, std::placeholders::_2));
// Cleanup and add . to view. CMD2_ANY_STRING ("view.size", std::bind(&cmd_view_size, std::placeholders::_2));
CMD2_ANY_STRING ("view.size_not_visible", std::bind(&cmd_view_size_not_visible, std::placeholders::_2));
CMD2_ANY_STRING("view.size", std::bind(&cmd_view_size, std::placeholders::_2)); CMD2_ANY_STRING ("view.persistent", std::bind(&cmd_view_persistent, std::placeholders::_2));
CMD2_ANY_STRING("view.size_not_visible", std::bind(&cmd_view_size_not_visible, std::placeholders::_2));
CMD2_ANY_STRING("view.persistent", std::bind(&cmd_view_persistent, std::placeholders::_2));
CMD2_ANY_STRING_V("view.filter_all", std::bind(&core::View::filter, std::bind(&core::ViewManager::find_ptr_throw, control->view_manager(), std::placeholders::_2))); CMD2_ANY_STRING_V("view.filter_all", std::bind(&core::View::filter, std::bind(&core::ViewManager::find_ptr_throw, control->view_manager(), std::placeholders::_2)));
CMD2_DL_STRING ("view.filter_download", std::bind(&cmd_view_filter_download, std::placeholders::_1, std::placeholders::_2)); CMD2_DL_STRING ("view.filter_download", std::bind(&cmd_view_filter_download, std::placeholders::_1, std::placeholders::_2));
CMD2_DL_STRING ("view.set_visible", std::bind(&cmd_view_set_visible, std::placeholders::_1, std::placeholders::_2)); CMD2_DL_STRING ("view.set_visible", std::bind(&cmd_view_set_visible, std::placeholders::_1, std::placeholders::_2));
CMD2_DL_STRING ("view.set_not_visible", std::bind(&cmd_view_set_not_visible, std::placeholders::_1, std::placeholders::_2)); CMD2_DL_STRING ("view.set_not_visible", std::bind(&cmd_view_set_not_visible, std::placeholders::_1, std::placeholders::_2));
// Commands that affect the default rtorrent UI. // Commands that affect the default rtorrent UI.
CMD2_DL ("ui.unfocus_download", std::bind(&cmd_ui_unfocus_download, std::placeholders::_1)); CMD2_DL ("ui.unfocus_download", std::bind(&cmd_ui_unfocus_download, std::placeholders::_1));
CMD2_ANY ("ui.current_view", std::bind(&cmd_ui_current_view)); CMD2_ANY ("ui.current_view", std::bind(&cmd_ui_current_view));
CMD2_ANY_STRING("ui.current_view.set", std::bind(&cmd_ui_set_view, std::placeholders::_2)); CMD2_ANY_STRING ("ui.current_view.set", std::bind(&cmd_ui_set_view, std::placeholders::_2));
CMD2_ANY ("ui.input.history.size", std::bind(&ui::Root::get_input_history_size, control->ui())); CMD2_ANY ("ui.input.history.size", std::bind(&ui::Root::get_input_history_size, control->ui()));
CMD2_ANY_VALUE_V("ui.input.history.size.set", std::bind(&ui::Root::set_input_history_size, control->ui(), std::placeholders::_2)); CMD2_ANY_VALUE_V ("ui.input.history.size.set", std::bind(&ui::Root::set_input_history_size, control->ui(), std::placeholders::_2));
CMD2_ANY_V ("ui.input.history.clear", std::bind(&ui::Root::clear_input_history, control->ui())); CMD2_ANY_V ("ui.input.history.clear", std::bind(&ui::Root::clear_input_history, control->ui()));
CMD2_VAR_VALUE ("ui.throttle.global.step.small", 5); CMD2_VAR_VALUE ("ui.throttle.global.step.small", 5);
CMD2_VAR_VALUE ("ui.throttle.global.step.medium", 50); CMD2_VAR_VALUE ("ui.throttle.global.step.medium", 50);
CMD2_VAR_VALUE ("ui.throttle.global.step.large", 500); CMD2_VAR_VALUE ("ui.throttle.global.step.large", 500);
CMD2_VAR_VALUE ("ui.focus.page_size", 0); CMD2_VAR_VALUE ("ui.focus.page_size", 0);
CMD2_ANY_LIST ("ui.status.throttle.up.set", std::bind(&cmd_status_throttle_names, true, std::placeholders::_2)); CMD2_ANY_LIST ("ui.status.throttle.up.set", std::bind(&cmd_status_throttle_names, true, std::placeholders::_2));
CMD2_ANY_LIST ("ui.status.throttle.down.set", std::bind(&cmd_status_throttle_names, false, std::placeholders::_2)); CMD2_ANY_LIST ("ui.status.throttle.down.set", std::bind(&cmd_status_throttle_names, false, std::placeholders::_2));
CMD2_ANY ("ui.keymap.style", std::bind(&ui::Root::keymap_style, control->ui())); CMD2_ANY ("ui.keymap.style", std::bind(&ui::Root::keymap_style, control->ui()));
CMD2_ANY_STRING_V("ui.keymap.style.set", std::bind(&ui::Root::set_keymap_style, control->ui(), std::placeholders::_2)); CMD2_ANY_STRING_V("ui.keymap.style.set", std::bind(&ui::Root::set_keymap_style, control->ui(), std::placeholders::_2));
// TODO: Add 'option_string' for rtorrent-specific options. // TODO: Add 'option_string' for rtorrent-specific options.
CMD2_VAR_STRING("ui.torrent_list.layout", "full"); CMD2_VAR_STRING ("ui.torrent_list.layout", "full");
// Move. // Move.
CMD2_ANY("print", &apply_print); CMD2_ANY("print", &apply_print);
+28 -10
View File
@@ -6,6 +6,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <torrent/net/http_stack.h> #include <torrent/net/http_stack.h>
#include <torrent/runtime/network_manager.h> #include <torrent/runtime/network_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/utils/directory_events.h> #include <torrent/utils/directory_events.h>
#include "core/dht_manager.h" #include "core/dht_manager.h"
@@ -46,7 +47,8 @@ Control::Control()
m_inputStdin->slot_pressed(std::bind(&input::Manager::pressed, m_input.get(), std::placeholders::_1)); m_inputStdin->slot_pressed(std::bind(&input::Manager::pressed, m_input.get(), std::placeholders::_1));
m_task_shutdown.slot() = std::bind(&Control::handle_shutdown, this); m_task_shutdown.slot() = [this] { handle_shutdown(); };
m_task_shutdown_clear_requests.slot() = [this] { handle_shutdown_clear_requests(); };
m_commandScheduler->set_slot_error_message([this](const std::string& msg) { m_core->push_log_std(msg); }); m_commandScheduler->set_slot_error_message([this](const std::string& msg) { m_core->push_log_std(msg); });
} }
@@ -68,15 +70,12 @@ Control::initialize() {
display::Window::slot_unschedule([this](display::Window* w) { m_display->unschedule(w); }); display::Window::slot_unschedule([this](display::Window* w) { m_display->unschedule(w); });
display::Window::slot_adjust([this]() { m_display->adjust_layout(); }); display::Window::slot_adjust([this]() { m_display->adjust_layout(); });
torrent::net_thread::http_stack()->set_user_agent(USER_AGENT);
m_core->listen_open();
m_core->set_hashing_view(*m_view_manager->find_throw("hashing")); m_core->set_hashing_view(*m_view_manager->find_throw("hashing"));
m_ui->init(this); m_ui->init(this);
if(!display::Canvas::daemon()) if(!display::Canvas::daemon())
m_inputStdin->insert(torrent::this_thread::poll()); m_inputStdin->insert();
} }
void void
@@ -84,9 +83,12 @@ Control::cleanup() {
rpc::rpc.cleanup(); rpc::rpc.cleanup();
torrent::this_thread::scheduler()->erase(&m_task_shutdown); torrent::this_thread::scheduler()->erase(&m_task_shutdown);
torrent::this_thread::scheduler()->erase(&m_task_shutdown_clear_requests);
if(!display::Canvas::daemon()) if(!display::Canvas::daemon())
m_inputStdin->remove(torrent::this_thread::poll()); m_inputStdin->remove();
m_directory_events->close();
if (scgi_thread::thread()->is_active()) if (scgi_thread::thread()->is_active())
scgi_thread::thread()->stop_thread_wait(); scgi_thread::thread()->stop_thread_wait();
@@ -111,7 +113,7 @@ Control::cleanup_exception() {
bool bool
Control::is_shutdown_completed() { Control::is_shutdown_completed() {
if (!m_shutdownQuick) if (!m_shutdown_quick)
return false; return false;
// Tracker requests can be disowned, so wait for these to // Tracker requests can be disowned, so wait for these to
@@ -135,8 +137,9 @@ Control::handle_shutdown() {
if (scgi_thread::thread()->is_active()) if (scgi_thread::thread()->is_active())
scgi_thread::thread()->stop_thread_wait(); scgi_thread::thread()->stop_thread_wait();
if (!m_shutdownQuick) { if (!m_shutdown_quick) {
torrent::runtime::network_manager()->listen_close(); torrent::runtime::network_manager()->listen_close();
torrent::runtime::shutdown();
m_directory_events->close(); m_directory_events->close();
m_core->shutdown(false); m_core->shutdown(false);
@@ -145,9 +148,24 @@ Control::handle_shutdown() {
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown, 5s); torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown, 5s);
} else { } else {
torrent::runtime::quick_shutdown();
m_core->shutdown(true); m_core->shutdown(true);
} }
m_shutdownQuick = true; if (!m_task_shutdown_clear_requests.is_scheduled())
m_shutdownReceived = false; torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown_clear_requests, 10s);
m_shutdown_quick = true;
m_shutdown_received = false;
}
void
Control::handle_shutdown_clear_requests() {
torrent::net_thread::http_stack()->clear_requests();
// Use 5s for the initial wait to ensure trackers get a chance to finish both IPv4 and IPv6 requests.
if (m_clear_requests_count++ == 0)
torrent::this_thread::scheduler()->wait_for(&m_task_shutdown_clear_requests, 5s);
else
torrent::this_thread::scheduler()->wait_for(&m_task_shutdown_clear_requests, 1s);
} }
+14 -8
View File
@@ -6,7 +6,7 @@
#include <memory> #include <memory>
#include <sys/types.h> #include <sys/types.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/utils/scheduler.h> #include <torrent/system/scheduler.h>
namespace ui { namespace ui {
class Root; class Root;
@@ -48,17 +48,18 @@ public:
~Control(); ~Control();
bool is_shutdown_completed(); bool is_shutdown_completed();
bool is_shutdown_received() { return m_shutdownReceived; } bool is_shutdown_received() { return m_shutdown_received; }
bool is_shutdown_started() { return m_shutdownQuick; } bool is_shutdown_started() { return m_shutdown_quick; }
void initialize(); void initialize();
void cleanup(); void cleanup();
void cleanup_exception(); void cleanup_exception();
void handle_shutdown(); void handle_shutdown();
void handle_shutdown_clear_requests();
void receive_normal_shutdown() { m_shutdownReceived = true; } void receive_normal_shutdown() { m_shutdown_received = true; }
void receive_quick_shutdown() { m_shutdownReceived = true; m_shutdownQuick = true; } void receive_quick_shutdown() { m_shutdown_received = true; m_shutdown_quick = true; }
core::Manager* core() { return m_core.get(); } core::Manager* core() { return m_core.get(); }
core::ViewManager* view_manager() { return m_view_manager.get(); } core::ViewManager* view_manager() { return m_view_manager.get(); }
@@ -106,10 +107,15 @@ private:
mode_t m_umask; mode_t m_umask;
std::string m_workingDirectory; std::string m_workingDirectory;
torrent::utils::SchedulerEntry m_task_shutdown; torrent::system::SchedulerEntry m_task_shutdown;
torrent::system::SchedulerEntry m_task_shutdown_clear_requests;
std::atomic<bool> m_shutdownReceived{}; int m_clear_requests_count{};
std::atomic<bool> m_shutdownQuick{};
align_cacheline
std::atomic<bool> m_shutdown_received{};
std::atomic<bool> m_shutdown_quick{};
}; };
#endif #endif
+42 -9
View File
@@ -8,6 +8,7 @@
#include <torrent/object_stream.h> #include <torrent/object_stream.h>
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/runtime/network_manager.h> #include <torrent/runtime/network_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/tracker/dht_controller.h> #include <torrent/tracker/dht_controller.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
@@ -140,17 +141,32 @@ DhtManager::save_dht_cache() {
} }
void void
DhtManager::set_mode(const std::string& arg) { DhtManager::set_mode_by_user(const std::string& arg) {
int i; unsigned int mode = [arg]() {
for (i = 0; i < dht_settings_num; i++) { for (int i = 0; i < dht_settings_num; i++) {
if (arg == dht_settings[i]) { if (arg == dht_settings[i])
m_start = i; return i;
break; }
}
throw torrent::input_error("Invalid dht mode: " + arg);
}();
m_set_by_user = true;
if (!torrent::runtime::is_network_initialized()) {
m_start = mode;
return;
} }
if (i == dht_settings_num) set_mode_directly(mode);
throw torrent::input_error("Invalid argument."); }
void
DhtManager::set_mode_directly(unsigned int mode) {
if (mode >= dht_settings_num)
throw torrent::input_error("Invalid dht mode.");
m_start = mode;
if (m_start == dht_off) if (m_start == dht_off)
stop_dht(); stop_dht();
@@ -158,6 +174,23 @@ DhtManager::set_mode(const std::string& arg) {
start_dht(); start_dht();
} }
void
DhtManager::set_auto_if_untouched_and_has_session() {
if (m_set_by_user) {
set_mode_directly(m_start);
return;
}
if (rpc::call_command_string("session.path").empty()) {
LT_LOG("DHT auto-start disabled, session path not set.", 0);
return;
}
set_mode_directly(dht_auto);
LT_LOG("DHT auto-start enabled.", 0);
}
void void
DhtManager::update() { DhtManager::update() {
if (!torrent::runtime::network_manager()->is_dht_active()) if (!torrent::runtime::network_manager()->is_dht_active())
+15 -11
View File
@@ -2,12 +2,17 @@
#define RTORRENT_CORE_DHT_MANAGER_H #define RTORRENT_CORE_DHT_MANAGER_H
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/utils/scheduler.h> #include <torrent/system/scheduler.h>
namespace core { namespace core {
class DhtManager { class DhtManager {
public: public:
static constexpr int dht_disable = 0;
static constexpr int dht_off = 1;
static constexpr int dht_auto = 2;
static constexpr int dht_on = 3;
~DhtManager(); ~DhtManager();
void load_dht_cache(); void load_dht_cache();
@@ -18,16 +23,14 @@ public:
void stop_dht(); void stop_dht();
void auto_start() { if (m_start == dht_auto) start_dht(); } void auto_start() { if (m_start == dht_auto) start_dht(); }
void set_mode(const std::string& arg); void set_mode_by_user(const std::string& arg);
void set_mode_directly(unsigned int mode);
void set_auto_if_untouched_and_has_session();
private: private:
static const int dht_disable = 0; static constexpr int dht_settings_num = 4;
static const int dht_off = 1; static const char* dht_settings[dht_settings_num];
static const int dht_auto = 2;
static const int dht_on = 3;
static const int dht_settings_num = 4;
static const char* dht_settings[dht_settings_num];
void update(); void update();
bool log_statistics(bool force); bool log_statistics(bool force);
@@ -39,10 +42,11 @@ private:
uint64_t m_dhtPrevBytesUp; uint64_t m_dhtPrevBytesUp;
uint64_t m_dhtPrevBytesDown; uint64_t m_dhtPrevBytesDown;
torrent::utils::SchedulerEntry m_update_timeout; torrent::system::SchedulerEntry m_update_timeout;
torrent::utils::SchedulerEntry m_stop_timeout; torrent::system::SchedulerEntry m_stop_timeout;
bool m_warned{}; bool m_warned{};
bool m_set_by_user{};
int m_start{dht_off}; int m_start{dht_off};
}; };
+4 -3
View File
@@ -88,15 +88,16 @@ Download::distributed_copies() const {
} }
void void
Download::set_throttle_name(const std::string& throttleName) { Download::set_throttle_name(const std::string& name) {
if (m_download.info()->is_active()) if (m_download.info()->is_active())
throw torrent::input_error("Cannot set throttle on active download."); throw torrent::input_error("Cannot set throttle on active download.");
torrent::ThrottlePair throttles = control->core()->get_throttle(throttleName); auto throttles = control->core()->get_throttle(name);
m_download.set_upload_throttle(throttles.first); m_download.set_upload_throttle(throttles.first);
m_download.set_download_throttle(throttles.second); m_download.set_download_throttle(throttles.second);
m_download.bencode()->get_key("rtorrent").insert_key("throttle_name", throttleName); m_download.bencode()->get_key("rtorrent").insert_key("throttle_name", name);
} }
void void
+4 -2
View File
@@ -22,6 +22,8 @@ public:
typedef torrent::ConnectionList connection_list_type; typedef torrent::ConnectionList connection_list_type;
typedef download_type::ConnectionType connection_type; typedef download_type::ConnectionType connection_type;
static constexpr uint32_t default_resume_flags = ~uint32_t{} & ~torrent::Download::open_enable_fallocate;
static const int variable_hashing_stopped = 0; static const int variable_hashing_stopped = 0;
static const int variable_hashing_initial = 1; static const int variable_hashing_initial = 1;
static const int variable_hashing_last = 2; static const int variable_hashing_last = 2;
@@ -78,7 +80,7 @@ public:
void set_root_directory(const std::string& path); void set_root_directory(const std::string& path);
void set_throttle_name(const std::string& throttleName); void set_throttle_name(const std::string& name);
bool operator == (const std::string& str) const; bool operator == (const std::string& str) const;
@@ -100,7 +102,7 @@ private:
download_type m_download; download_type m_download;
bool m_hashFailed{}; bool m_hashFailed{};
std::string m_message; std::string m_message;
uint32_t m_resumeFlags{~uint32_t{}}; uint32_t m_resumeFlags{default_resume_flags};
unsigned int m_group{}; unsigned int m_group{};
}; };
+61 -28
View File
@@ -15,6 +15,7 @@
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/data/file_utils.h> #include <torrent/data/file_utils.h>
#include <torrent/net/http_stack.h> #include <torrent/net/http_stack.h>
#include <torrent/runtime/client_config.h>
#include <torrent/utils/string_manip.h> #include <torrent/utils/string_manip.h>
#include "control.h" #include "control.h"
@@ -34,8 +35,10 @@ is_network_uri(const std::string& uri) {
std::strncmp(uri.c_str(), "ftp://", 6) == 0; std::strncmp(uri.c_str(), "ftp://", 6) == 0;
} }
static constexpr const char* session_invalid_message = "Session data is invalid, ignoring it";
static std::unique_ptr<torrent::Object> static std::unique_ptr<torrent::Object>
download_factory_load_stream(const char* filename) { download_factory_load_stream(const char* filename, bool* is_invalid) {
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())
@@ -44,8 +47,10 @@ download_factory_load_stream(const char* filename) {
auto obj = std::make_unique<torrent::Object>(); auto obj = std::make_unique<torrent::Object>();
stream >> *obj; stream >> *obj;
if (!stream.good()) if (!stream.good() || !obj->is_map()) {
*is_invalid = true;
return std::unique_ptr<torrent::Object>(); return std::unique_ptr<torrent::Object>();
}
return obj; return obj;
} }
@@ -104,40 +109,42 @@ DownloadFactory::receive_load() {
throw torrent::internal_error("DownloadFactory::load*() called on an object with m_stream != NULL"); throw torrent::internal_error("DownloadFactory::load*() called on an object with m_stream != NULL");
if (is_network_uri(m_uri)) { if (is_network_uri(m_uri)) {
// Http handling here.
m_stream.reset(new std::stringstream); m_stream.reset(new std::stringstream);
HttpQueue::iterator itr = m_manager->http_queue()->insert(m_uri, m_stream); auto done_fn = [this]() { receive_loaded(); };
auto failed_fn = [this](const std::string& error) { receive_failed(error); };
itr->add_done_slot(torrent::this_thread::thread(), [this]() { receive_loaded(); }); m_manager->http_queue()->insert(m_uri, m_stream, done_fn, failed_fn);
itr->add_failed_slot(torrent::this_thread::thread(), [this](const std::string& error) { receive_failed(error); });
m_variables["tied_to_file"] = (int64_t)false; m_variables["tied_to_file"] = (int64_t)false;
return;
}
} else if (is_magnet_uri(m_uri)) { if (is_magnet_uri(m_uri)) {
// DEBUG: Use m_object. // DEBUG: Use m_object.
m_stream.reset(new std::stringstream()); m_stream.reset(new std::stringstream());
*m_stream << "d10:magnet-uri" << m_uri.length() << ":" << m_uri << "e"; *m_stream << "d10:magnet-uri" << m_uri.length() << ":" << m_uri << "e";
m_variables["tied_to_file"] = (int64_t)false; m_variables["tied_to_file"] = (int64_t)false;
receive_loaded();
} else {
std::fstream stream(expand_path(m_uri).c_str(), std::ios::in | std::ios::binary);
if (!stream.is_open())
return receive_failed("Could not open file");
m_object = new torrent::Object;
stream >> *m_object;
if (!stream.good())
return receive_failed("Reading torrent file failed");
m_isFile = true;
receive_loaded(); receive_loaded();
return;
} }
std::fstream stream(expand_path(m_uri).c_str(), std::ios::in | std::ios::binary);
if (!stream.is_open())
return receive_failed("Could not open file");
m_object = new torrent::Object;
stream >> *m_object;
if (!stream.good())
return receive_failed("Reading torrent file failed");
m_isFile = true;
receive_loaded();
} }
void void
@@ -158,8 +165,13 @@ DownloadFactory::receive_commit() {
void void
DownloadFactory::receive_success() { DownloadFactory::receive_success() {
auto rtorrent_object = download_factory_load_stream((expand_path(m_uri) + ".rtorrent").c_str()); bool session_invalid = false;
auto libtorrent_resume_object = download_factory_load_stream((expand_path(m_uri) + ".libtorrent_resume").c_str());
auto rtorrent_object = download_factory_load_stream((expand_path(m_uri) + ".rtorrent").c_str(), &session_invalid);
auto 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;
@@ -181,6 +193,14 @@ DownloadFactory::receive_success() {
return; return;
} }
if (session_invalid) {
download->set_hash_failed(true);
download->set_message(session_invalid_message);
if (m_printLog)
m_manager->push_log_std(std::string(session_invalid_message) + ": \"" + m_uri + "\"");
}
torrent::Object* root = download->bencode(); torrent::Object* root = download->bencode();
if (download->download()->info()->is_meta_download()) { if (download->download()->info()->is_meta_download()) {
@@ -269,11 +289,24 @@ DownloadFactory::receive_success() {
if (!m_session && m_variables["tied_to_file"].as_value()) if (!m_session && m_variables["tied_to_file"].as_value())
rpc::call_command("d.tied_to_file.set", m_uri.empty() ? m_variables["tied_file"] : m_uri, rpc::make_target(download)); rpc::call_command("d.tied_to_file.set", m_uri.empty() ? m_variables["tied_file"] : m_uri, rpc::make_target(download));
rpc::call_command("d.peer_exchange.set", rpc::call_command_value("protocol.pex"), rpc::make_target(download)); rpc::call_command("d.peer_exchange.set", torrent::runtime::client_config()->is_pex_enabled(), rpc::make_target(download));
torrent::resume_load_addresses(*download->download(), resumeObject); try {
torrent::resume_load_file_priorities(*download->download(), resumeObject); torrent::resume_load_addresses(*download->download(), resumeObject);
torrent::resume_load_tracker_settings(*download->download(), resumeObject); torrent::resume_load_file_priorities(*download->download(), resumeObject);
torrent::resume_load_tracker_settings(*download->download(), resumeObject);
} catch (const torrent::input_error& e) {
std::string msg = std::string(session_invalid_message) + ": " + e.what();
lt_log_print(torrent::LOG_ERROR, "%s: %s", msg.c_str(), m_uri.c_str());
if (m_printLog)
m_manager->push_log_std(msg + ": \"" + m_uri + "\"");
download->set_hash_failed(true);
download->set_message(msg);
}
// The action of inserting might cause the torrent to be // The action of inserting might cause the torrent to be
// opened/started or such. Figure out a nicer way of handling this. // opened/started or such. Figure out a nicer way of handling this.
+4 -4
View File
@@ -9,7 +9,7 @@
#include <iosfwd> #include <iosfwd>
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/utils/scheduler.h> #include <torrent/system/scheduler.h>
#include "http_queue.h" #include "http_queue.h"
@@ -79,9 +79,9 @@ private:
command_list_type m_commands; command_list_type m_commands;
torrent::Object::map_type m_variables; torrent::Object::map_type m_variables;
slot_void m_slot_finished; slot_void m_slot_finished;
torrent::utils::SchedulerEntry m_task_load; torrent::system::SchedulerEntry m_task_load;
torrent::utils::SchedulerEntry m_task_commit; torrent::system::SchedulerEntry m_task_commit;
}; };
bool is_network_uri(const std::string& uri); bool is_network_uri(const std::string& uri);
+11 -7
View File
@@ -331,7 +331,7 @@ DownloadList::resume(Download* download, int flags) {
// We need to make sure the flags aren't reset if someone decideds // We need to make sure the flags aren't reset if someone decideds
// to call resume() while it is hashing, etc. // to call resume() while it is hashing, etc.
if (download->resume_flags() == ~uint32_t()) if (download->resume_flags() == Download::default_resume_flags)
download->set_resume_flags(flags); download->set_resume_flags(flags);
// Manual or end-of-download rehashing clears the resume data so // Manual or end-of-download rehashing clears the resume data so
@@ -404,7 +404,7 @@ DownloadList::resume(Download* download, int flags) {
download->set_priority(download->priority()); download->set_priority(download->priority());
download->download()->start(download->resume_flags()); download->download()->start(download->resume_flags());
download->set_resume_flags(~uint32_t()); download->set_resume_flags(Download::default_resume_flags);
DL_TRIGGER_EVENT(download, "event.download.resumed"); DL_TRIGGER_EVENT(download, "event.download.resumed");
@@ -421,7 +421,7 @@ DownloadList::pause(Download* download, int flags) {
try { try {
download->set_resume_flags(~uint32_t()); download->set_resume_flags(Download::default_resume_flags);
rpc::parse_command_single(rpc::make_target(download), "view.set_not_visible=active"); rpc::parse_command_single(rpc::make_target(download), "view.set_not_visible=active");
@@ -492,7 +492,11 @@ DownloadList::hash_done(Download* download) {
if (!download->is_hash_checked()) { if (!download->is_hash_checked()) {
download->set_hash_failed(true); download->set_hash_failed(true);
auto& msg = download->download()->hash_error_message();
if (!msg.empty())
download->set_message(msg);
DL_TRIGGER_EVENT(download, "event.download.hash_failed"); DL_TRIGGER_EVENT(download, "event.download.hash_failed");
return; return;
} }
@@ -656,8 +660,8 @@ DownloadList::confirm_finished(Download* download) {
if (find(infohash) == end()) if (find(infohash) == end())
return; return;
// if (download->resume_flags() != ~uint32_t()) // if (download->resume_flags() != Download::default_resume_flags)
// throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != ~uint32_t()."); // throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != Download::default_resume_flags.");
// See #1292. // See #1292.
// //
@@ -667,7 +671,7 @@ DownloadList::confirm_finished(Download* download) {
// //
// TODO: Add a check when setting the flags to see if the torrent is // TODO: Add a check when setting the flags to see if the torrent is
// being hashed. // being hashed.
download->set_resume_flags(~uint32_t()); download->set_resume_flags(Download::default_resume_flags);
if (!download->is_active() && rpc::call_command_value("d.state", rpc::make_target(download)) == 1) if (!download->is_active() && rpc::call_command_value("d.state", rpc::make_target(download)) == 1)
resume(download, resume(download,
+9 -5
View File
@@ -9,17 +9,21 @@
namespace core { namespace core {
HttpQueue::iterator HttpQueue::iterator
HttpQueue::insert(const std::string& url, std::shared_ptr<std::ostream> stream) { HttpQueue::insert(const std::string& url, std::shared_ptr<std::ostream> stream,
std::function<void()> done_fn, std::function<void(const std::string&)> failed_fn) {
auto itr = base_type::insert(end(), torrent::net::HttpGet(url, stream)); auto itr = base_type::insert(end(), torrent::net::HttpGet(url, stream));
itr->set_max_file_size(15 << 20);
itr->set_redirect_only_http_https();
for (auto& slot : m_signal_insert) for (auto& slot : m_signal_insert)
slot(*itr); slot(*itr);
itr->add_done_slot(torrent::this_thread::thread(), [this, itr]() { erase(itr); }); itr->add_done_slot(torrent::this_thread::thread(), std::move(done_fn));
itr->add_failed_slot(torrent::this_thread::thread(), [this, itr](auto) { erase(itr); }); itr->add_done_slot(torrent::this_thread::thread(), [this, itr]() { erase(itr); });
// TODO: Downloading http torrents doesn't seem to work. itr->add_failed_slot(torrent::this_thread::thread(), std::move(failed_fn));
// TODO: Quitting no longer works. itr->add_failed_slot(torrent::this_thread::thread(), [this, itr](auto) { erase(itr); });
torrent::net_thread::http_stack()->start_get(*itr); torrent::net_thread::http_stack()->start_get(*itr);
+2 -1
View File
@@ -39,7 +39,8 @@ public:
// //
// Consider adding a flag to indicate whetever HttpQueue should // Consider adding a flag to indicate whetever HttpQueue should
// delete the stream. // delete the stream.
iterator insert(const std::string& url, std::shared_ptr<std::ostream> stream); iterator insert(const std::string& url, std::shared_ptr<std::ostream> stream,
std::function<void()> done_fn, std::function<void(const std::string&)> failed_fn);
void erase(iterator itr); void erase(iterator itr);
void clear(); void clear();
+13 -89
View File
@@ -8,10 +8,9 @@
#include <sstream> #include <sstream>
#include <unistd.h> #include <unistd.h>
#include <sys/select.h> #include <sys/select.h>
#include <rak/regex.h> #include <fnmatch.h>
#include <torrent/utils/resume.h> #include <torrent/utils/resume.h>
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/connection_manager.h>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/object_stream.h> #include <torrent/object_stream.h>
#include <torrent/throttle.h> #include <torrent/throttle.h>
@@ -34,6 +33,8 @@
#include "core/http_queue.h" #include "core/http_queue.h"
#include "core/view.h" #include "core/view.h"
#include <torrent/runtime/client_config.h>
namespace core { namespace core {
const int Manager::create_start; const int Manager::create_start;
@@ -87,10 +88,10 @@ Manager::set_hashing_view(View* v) {
m_hashingView->signal_changed().push_back(std::bind(&Manager::receive_hashing_changed, this)); m_hashingView->signal_changed().push_back(std::bind(&Manager::receive_hashing_changed, this));
} }
torrent::ThrottlePair ThrottlePair
Manager::get_throttle(const std::string& name) { Manager::get_throttle(const std::string& name) {
ThrottleMap::const_iterator itr = m_throttles.find(name); auto itr = m_throttles.find(name);
torrent::ThrottlePair throttles = (itr == m_throttles.end() ? torrent::ThrottlePair(nullptr, nullptr) : itr->second); auto throttles = (itr == m_throttles.end() ? ThrottlePair(nullptr, nullptr) : itr->second);
if (throttles.first == nullptr) if (throttles.first == nullptr)
throttles.first = torrent::up_throttle_global(); throttles.first = torrent::up_throttle_global();
@@ -101,20 +102,6 @@ Manager::get_throttle(const std::string& name) {
return throttles; return throttles;
} }
void
Manager::set_address_throttle(uint32_t begin, uint32_t end, torrent::ThrottlePair throttles) {
m_addressThrottles.set_merge(begin, end, throttles);
torrent::connection_manager()->address_throttle() = std::bind(&core::Manager::get_address_throttle, control->core(), std::placeholders::_1);
}
torrent::ThrottlePair
Manager::get_address_throttle(const sockaddr* addr) {
if (addr->sa_family != AF_INET)
return torrent::ThrottlePair(nullptr, nullptr);
return m_addressThrottles.get(ntohl(reinterpret_cast<const sockaddr_in*>(addr)->sin_addr.s_addr), torrent::ThrottlePair(nullptr, nullptr));
}
int64_t int64_t
Manager::retrieve_throttle_value(const torrent::Object::string_type& name, bool rate, bool up) { Manager::retrieve_throttle_value(const torrent::Object::string_type& name, bool rate, bool up) {
ThrottleMap::iterator itr = throttles().find(name); ThrottleMap::iterator itr = throttles().find(name);
@@ -166,76 +153,12 @@ Manager::cleanup() {
void void
Manager::shutdown(bool force) { Manager::shutdown(bool force) {
if (!force) if (!force) {
for (auto d : *m_download_list) for (auto d : *m_download_list)
m_download_list->pause_default(d); m_download_list->pause_default(d);
else } else {
for (auto d : *m_download_list) for (auto d : *m_download_list)
m_download_list->close_quick(d); m_download_list->close_quick(d);
}
void
Manager::listen_open() {
// This stuff really should be moved outside of manager, make it
// part of the init script.
if (!rpc::call_command_value("network.port_open"))
return;
int portFirst, portLast;
torrent::Object portRange = rpc::call_command("network.port_range");
if (!portRange.is_string())
throw torrent::input_error("Invalid port_range argument type.");
if (std::sscanf(portRange.as_string().c_str(), "%i-%i", &portFirst, &portLast) != 2)
throw torrent::input_error("Invalid port_range argument.");
if (portFirst > portLast || portLast >= (1 << 16))
throw torrent::input_error("Invalid port range.");
if (rpc::call_command_value("network.port_random")) {
int boundary = portFirst + random() % (portLast - portFirst + 1);
if (torrent::runtime::network_manager()->listen_open(boundary, portLast) ||
torrent::runtime::network_manager()->listen_open(portFirst, boundary))
return;
} else {
if (torrent::runtime::network_manager()->listen_open(portFirst, portLast))
return;
}
throw torrent::input_error("Could not open/bind port for listening: " + std::string(std::strerror(errno)));
}
void
Manager::set_proxy_address(const std::string& addr) {
int port;
torrent::sa_unique_ptr sa;
std::string buf(addr.length() + 1, '\0');
int err = std::sscanf(addr.c_str(), "%[^:]:%i", buf.data(), &port);
if (err <= 0)
throw torrent::input_error("Could not parse proxy address.");
if (err == 1)
port = 80;
try {
sa = torrent::sa_copy(torrent::sa_lookup_address(buf, AF_INET).get());
} catch (torrent::input_error& e) {
throw torrent::input_error("Could not resolve proxy address: " + std::string(e.what()));
}
try {
torrent::sa_set_port(sa.get(), port);
torrent::runtime::network_config()->set_proxy_address(sa.get());
} catch (torrent::input_error& e) {
throw e;
} }
} }
@@ -407,9 +330,9 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
// Might be an idea to use depth-first search instead. // Might be an idea to use depth-first search instead.
for (; first != last; ++first) { for (; first != last; ++first) {
rak::regex r(*first); const std::string& pattern = *first;
if (r.pattern().empty()) if (pattern.empty())
continue; continue;
// Special case for ".."? // Special case for ".."?
@@ -417,8 +340,8 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
for (auto& itr : currentCache) { for (auto& itr : currentCache) {
// Only include filenames starting with '.' if the pattern // Only include filenames starting with '.' if the pattern
// starts with the same. // starts with the same.
itr.update((r.pattern()[0] != '.') ? utils::Directory::update_hide_dot : 0); itr.update((pattern[0] != '.') ? utils::Directory::update_hide_dot : 0);
itr.erase(std::remove_if(itr.begin(), itr.end(), [r](const utils::directory_entry& entry) { return !r(entry.s_name); }), itr.end()); itr.erase(std::remove_if(itr.begin(), itr.end(), [&pattern](const utils::directory_entry& entry) { return fnmatch(pattern.c_str(), entry.s_name.c_str(), 0) != 0; }), itr.end());
for (const auto& cache : itr) for (const auto& cache : itr)
nextCache.push_back(path_expand_transform(itr.path() + (itr.path() == "/" ? "" : "/"), cache)); nextCache.push_back(path_expand_transform(itr.path() + (itr.path() == "/" ? "" : "/"), cache));
@@ -513,6 +436,7 @@ Manager::receive_hashing_changed() {
} else { } else {
(*itr)->set_hash_failed(true); (*itr)->set_hash_failed(true);
(*itr)->set_message("Hashing failed: " + std::string(e.what()));
lt_log_print(torrent::LOG_TORRENT_ERROR, "Hashing failed: %s", e.what()); lt_log_print(torrent::LOG_TORRENT_ERROR, "Hashing failed: %s", e.what());
} }
} }
+10 -21
View File
@@ -5,7 +5,6 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <torrent/utils/log_buffer.h> #include <torrent/utils/log_buffer.h>
#include <torrent/connection_manager.h>
#include <torrent/object.h> #include <torrent/object.h>
#include "download_list.h" #include "download_list.h"
@@ -23,7 +22,8 @@ namespace core {
class HttpQueue; class HttpQueue;
typedef std::map<std::string, torrent::ThrottlePair> ThrottleMap; using ThrottlePair = std::pair<torrent::Throttle*, torrent::Throttle*>;
using ThrottleMap = std::map<std::string, ThrottlePair>;
class View; class View;
@@ -37,32 +37,24 @@ public:
bool is_download_shutdown_completed(); bool is_download_shutdown_completed();
DownloadList* download_list() { return m_download_list.get(); } DownloadList* download_list() { return m_download_list.get(); }
FileStatusCache* file_status_cache() { return m_file_status_cache.get(); } FileStatusCache* file_status_cache() { return m_file_status_cache.get(); }
HttpQueue* http_queue() { return m_http_queue.get(); } HttpQueue* http_queue() { return m_http_queue.get(); }
View* hashing_view() { return m_hashingView; } View* hashing_view() { return m_hashingView; }
void set_hashing_view(View* v); void set_hashing_view(View* v);
torrent::log_buffer* log_important() { return m_log_important.get(); } auto* log_important() { return m_log_important.get(); }
torrent::log_buffer* log_complete() { return m_log_complete.get(); } auto* log_complete() { return m_log_complete.get(); }
ThrottleMap& throttles() { return m_throttles; } ThrottleMap& throttles() { return m_throttles; }
torrent::ThrottlePair get_throttle(const std::string& name); ThrottlePair get_throttle(const std::string& name);
int64_t retrieve_throttle_value(const torrent::Object::string_type& name, bool rate, bool up); int64_t retrieve_throttle_value(const torrent::Object::string_type& name, bool rate, bool up);
// Use custom throttle for the given range of IP addresses.
void set_address_throttle(uint32_t begin, uint32_t end, torrent::ThrottlePair throttles);
torrent::ThrottlePair get_address_throttle(const sockaddr* addr);
void cleanup(); void cleanup();
void listen_open();
void set_proxy_address(const std::string& addr);
const std::string& magnet_path(); const std::string& magnet_path();
void set_magnet_path(const std::string& path); void set_magnet_path(const std::string& path);
@@ -87,8 +79,6 @@ public:
void try_create_download_from_meta_download(torrent::Object* bencode, const std::string& metafile); void try_create_download_from_meta_download(torrent::Object* bencode, const std::string& metafile);
private: private:
typedef RangeMap<uint32_t, torrent::ThrottlePair> AddressThrottleMap;
void create_http(const std::string& uri); void create_http(const std::string& uri);
void create_final(std::istream* s); void create_final(std::istream* s);
@@ -104,7 +94,6 @@ private:
View* m_hashingView{}; View* m_hashingView{};
ThrottleMap m_throttles; ThrottleMap m_throttles;
AddressThrottleMap m_addressThrottles;
torrent::log_buffer_ptr m_log_important; torrent::log_buffer_ptr m_log_important;
torrent::log_buffer_ptr m_log_complete; torrent::log_buffer_ptr m_log_complete;
+3 -3
View File
@@ -18,7 +18,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/utils/scheduler.h> #include <torrent/system/scheduler.h>
#include "globals.h" #include "globals.h"
@@ -153,8 +153,8 @@ private:
std::chrono::microseconds m_last_changed{}; std::chrono::microseconds m_last_changed{};
signal_void m_signal_changed; signal_void m_signal_changed;
torrent::utils::SchedulerEntry m_delay_changed; torrent::system::SchedulerEntry m_delay_changed;
}; };
} // namespace core } // namespace core
+6 -9
View File
@@ -132,8 +132,7 @@ Canvas::resize_term(std::pair<int, int> dim) {
inline unsigned int inline unsigned int
Canvas::get_x() { Canvas::get_x() {
int x; [[maybe_unused]] int x, y;
[[maybe_unused]] int y;
if (!m_daemon) { if (!m_daemon) {
getyx(m_window, y, x); getyx(m_window, y, x);
} else { } else {
@@ -144,7 +143,7 @@ Canvas::get_x() {
inline unsigned int inline unsigned int
Canvas::get_y() { Canvas::get_y() {
int x, y; [[maybe_unused]] int x, y;
if (!m_daemon) { if (!m_daemon) {
getyx(m_window, y, x); getyx(m_window, y, x);
} else { } else {
@@ -155,8 +154,7 @@ Canvas::get_y() {
inline unsigned int inline unsigned int
Canvas::width() { Canvas::width() {
int x; [[maybe_unused]] int x, y;
[[maybe_unused]] int y;
if (!m_daemon) { if (!m_daemon) {
getmaxyx(m_window, y, x); getmaxyx(m_window, y, x);
} else { } else {
@@ -167,7 +165,7 @@ Canvas::width() {
inline unsigned int inline unsigned int
Canvas::height() { Canvas::height() {
int x, y; [[maybe_unused]] int x, y;
if (!m_daemon) { if (!m_daemon) {
getmaxyx(m_window, y, x); getmaxyx(m_window, y, x);
} else { } else {
@@ -257,8 +255,7 @@ Canvas::set_default_attributes(int attr) {
inline int inline int
Canvas::get_screen_width() { Canvas::get_screen_width() {
int x; [[maybe_unused]] int x, y;
[[maybe_unused]] int y;
if (!m_daemon) { if (!m_daemon) {
getmaxyx(stdscr, y, x); getmaxyx(stdscr, y, x);
} else { } else {
@@ -269,7 +266,7 @@ Canvas::get_screen_width() {
inline int inline int
Canvas::get_screen_height() { Canvas::get_screen_height() {
int x, y; [[maybe_unused]] int x, y;
if (!m_daemon) { if (!m_daemon) {
getmaxyx(stdscr, y, x); getmaxyx(stdscr, y, x);
} else { } else {
+6 -2
View File
@@ -80,10 +80,14 @@ Manager::schedule_update(std::chrono::microseconds min_interval) {
return; return;
} }
if (m_task_update.is_scheduled() && m_task_update.time() <= m_scheduler.front()->time()) auto cached = torrent::this_thread::cached_time();
m_scheduler.external_set_cached_time(cached);
auto next = cached + m_scheduler.next_timeout(cached);
if (m_task_update.is_scheduled() && m_task_update.time_or_zero() <= next)
return; return;
torrent::this_thread::scheduler()->update_wait_until(&m_task_update, std::max(m_scheduler.front()->time(), m_time_last_update + min_interval)); torrent::this_thread::scheduler()->update_wait_until(&m_task_update, std::max(next, m_time_last_update + min_interval));
} }
} }
+4 -4
View File
@@ -1,7 +1,7 @@
#ifndef RTORRENT_DISPLAY_MANAGER_H #ifndef RTORRENT_DISPLAY_MANAGER_H
#define RTORRENT_DISPLAY_MANAGER_H #define RTORRENT_DISPLAY_MANAGER_H
#include <torrent/utils/scheduler.h> #include <torrent/system/scheduler.h>
#include "display/frame.h" #include "display/frame.h"
@@ -31,9 +31,9 @@ private:
bool m_force_redraw{false}; bool m_force_redraw{false};
Frame m_root_frame; Frame m_root_frame;
std::chrono::microseconds m_time_last_update{}; std::chrono::microseconds m_time_last_update{};
torrent::utils::ExternalScheduler m_scheduler; torrent::system::ExternalScheduler m_scheduler;
torrent::utils::SchedulerEntry m_task_update; torrent::system::SchedulerEntry m_task_update;
}; };
} }
+2 -2
View File
@@ -345,7 +345,7 @@ print_status_info(char* first, char* last) {
if (!torrent::up_throttle_global()->is_throttled()) { if (!torrent::up_throttle_global()->is_throttled()) {
first = print_buffer(first, last, "[Throttle off"); first = print_buffer(first, last, "[Throttle off");
} else { } else {
first = print_buffer(first, last, "[Throttle %3i", torrent::up_throttle_global()->max_rate() / 1024); first = print_buffer(first, last, "[Throttle %3i", (int)(torrent::up_throttle_global()->max_rate() / 1024));
if (!throttle_up_names.empty()) if (!throttle_up_names.empty())
first = print_status_throttle_limit(first, last, true, throttle_up_names); first = print_status_throttle_limit(first, last, true, throttle_up_names);
@@ -354,7 +354,7 @@ print_status_info(char* first, char* last) {
if (!torrent::down_throttle_global()->is_throttled()) { if (!torrent::down_throttle_global()->is_throttled()) {
first = print_buffer(first, last, " / off KB]"); first = print_buffer(first, last, " / off KB]");
} else { } else {
first = print_buffer(first, last, " / %3i", torrent::down_throttle_global()->max_rate() / 1024); first = print_buffer(first, last, " / %3i", (int)(torrent::down_throttle_global()->max_rate() / 1024));
if (!throttle_down_names.empty()) if (!throttle_down_names.empty())
first = print_status_throttle_limit(first, last, false, throttle_down_names); first = print_status_throttle_limit(first, last, false, throttle_down_names);
+2 -2
View File
@@ -2,7 +2,7 @@
#define RTORRENT_WINDOW_BASE_H #define RTORRENT_WINDOW_BASE_H
#include <functional> #include <functional>
#include <torrent/utils/scheduler.h> #include <torrent/system/scheduler.h>
#include <torrent/system/thread.h> #include <torrent/system/thread.h>
#include "canvas.h" #include "canvas.h"
@@ -99,7 +99,7 @@ protected:
extent_type m_max_width; extent_type m_max_width;
extent_type m_max_height; extent_type m_max_height;
torrent::utils::SchedulerEntry m_task_update; torrent::system::SchedulerEntry m_task_update;
}; };
// Return a range with a distance of no more than __distance and // Return a range with a distance of no more than __distance and
+1 -1
View File
@@ -47,7 +47,7 @@ private:
signal_curl_get::iterator m_conn_insert; signal_curl_get::iterator m_conn_insert;
signal_curl_get::iterator m_conn_erase; signal_curl_get::iterator m_conn_erase;
torrent::utils::SchedulerEntry m_task_deactivate; torrent::system::SchedulerEntry m_task_deactivate;
}; };
} }
+1
View File
@@ -26,6 +26,7 @@ WindowLog::WindowLog(torrent::log_buffer* l) :
} }
WindowLog::~WindowLog() { WindowLog::~WindowLog() {
m_log->lock_and_set_update_slot(nullptr);
torrent::this_thread::scheduler()->erase(&m_task_update); torrent::this_thread::scheduler()->erase(&m_task_update);
} }
+6 -4
View File
@@ -1,8 +1,8 @@
#ifndef RTORRENT_DISPLAY_WINDOW_LOG_H #ifndef RTORRENT_DISPLAY_WINDOW_LOG_H
#define RTORRENT_DISPLAY_WINDOW_LOG_H #define RTORRENT_DISPLAY_WINDOW_LOG_H
#include <torrent/system/scheduler.h>
#include <torrent/utils/log_buffer.h> #include <torrent/utils/log_buffer.h>
#include <torrent/utils/scheduler.h>
#include "window.h" #include "window.h"
@@ -22,10 +22,12 @@ public:
private: private:
inline iterator find_older(); inline iterator find_older();
torrent::log_buffer* m_log; torrent::log_buffer* m_log;
torrent::utils::SchedulerEntry m_task_update; torrent::system::SchedulerEntry m_task_update;
align_cacheline std::atomic<bool> m_log_updating{}; align_cacheline
std::atomic<bool> m_log_updating{};
}; };
} }
+30
View File
@@ -3,6 +3,7 @@
#include "globals.h" #include "globals.h"
#include <cstdlib> #include <cstdlib>
#include <stdlib.h>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
rpc::ip_table_list ip_tables; rpc::ip_table_list ip_tables;
@@ -28,3 +29,32 @@ expand_path(const std::string& path) {
return path; return path;
} }
// Resolves a path to a canonical one with no symlinks or relative components,
// so it can safely be handed to an external script. Returns an empty string if
// the path does not name an existing file or directory.
std::string
resolve_path(const std::string& path) {
if (path.empty())
return std::string();
char* resolved = ::realpath(expand_path(path).c_str(), nullptr);
if (resolved == nullptr)
return std::string();
std::string result(resolved);
std::free(resolved);
return result;
}
std::string
resolve_path_or_throw(const std::string& path) {
auto result = resolve_path(path);
if (result.empty())
throw torrent::input_error("Could not resolve path: '" + path + "'.");
return result;
}
+2
View File
@@ -11,6 +11,8 @@ extern rpc::ip_table_list ip_tables;
extern Control* control; extern Control* control;
std::string expand_path(const std::string& path); std::string expand_path(const std::string& path);
std::string resolve_path(const std::string& path);
std::string resolve_path_or_throw(const std::string& path);
namespace rpc { namespace rpc {
class SCgi; class SCgi;
+14 -6
View File
@@ -2,20 +2,26 @@
#include "input_event.h" #include "input_event.h"
#include <torrent/common.h>
#include <torrent/system/poll.h>
#include "display/attributes.h" #include "display/attributes.h"
namespace input { namespace input {
void void
InputEvent::insert(torrent::system::Poll* p) { InputEvent::insert() {
p->open(this); torrent::this_thread::poll()->open(this);
p->insert_read(this); torrent::this_thread::poll()->insert_read(this);
} }
void void
InputEvent::remove(torrent::system::Poll* p) { InputEvent::remove() {
p->remove_read(this); if (!is_open())
p->close(this); return;
torrent::this_thread::poll()->remove_and_close(this);
set_file_descriptor(-1);
} }
void void
@@ -32,6 +38,8 @@ InputEvent::event_write() {
void void
InputEvent::event_error() { InputEvent::event_error() {
torrent::this_thread::poll()->remove_and_close(this);
set_file_descriptor(-1);
} }
} }
+5 -7
View File
@@ -2,22 +2,20 @@
#define RTORRENT_INPUT_INPUT_EVENT_H #define RTORRENT_INPUT_INPUT_EVENT_H
#include <functional> #include <functional>
#include <torrent/system/event.h>
#include <torrent/event.h>
#include <torrent/system/poll.h>
namespace input { namespace input {
class InputEvent : public torrent::Event { class InputEvent : public torrent::system::Event {
public: public:
typedef std::function<void (int)> slot_int; typedef std::function<void (int)> slot_int;
InputEvent(int fd) { m_fileDesc = fd; } InputEvent(int fd) { set_file_descriptor(fd); }
const char* type_name() const override { return "input"; } const char* type_name() const override { return "input"; }
void insert(torrent::system::Poll* p); void insert();
void remove(torrent::system::Poll* p); void remove();
void event_read() override; void event_read() override;
void event_write() override; void event_write() override;
+132 -88
View File
@@ -11,6 +11,9 @@
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/data/chunk_utils.h> #include <torrent/data/chunk_utils.h>
#include <torrent/net/fd.h> #include <torrent/net/fd.h>
#include <torrent/net/http_stack.h>
#include <torrent/runtime/memory_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/utils/chrono.h> #include <torrent/utils/chrono.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
@@ -112,8 +115,16 @@ main(int argc, char** argv) {
// TODO: Create a fake thread object for initializing other processes and enabling logging. // TODO: Create a fake thread object for initializing other processes and enabling logging.
torrent::initialize_main_thread(); 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. // 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(SIGSEGV, std::bind(&do_panic, SIGSEGV));
SignalHandler::set_handler(SIGILL, std::bind(&do_panic, SIGILL)); SignalHandler::set_handler(SIGILL, std::bind(&do_panic, SIGILL));
SignalHandler::set_handler(SIGFPE, std::bind(&do_panic, SIGFPE)); SignalHandler::set_handler(SIGFPE, std::bind(&do_panic, SIGFPE));
@@ -141,8 +152,8 @@ main(int argc, char** argv) {
SignalHandler::set_sigaction_handler(SIGBUS, &handle_sigbus); SignalHandler::set_sigaction_handler(SIGBUS, &handle_sigbus);
torrent::log_add_group_output(torrent::LOG_NOTICE, "important"); torrent::log_add_group_output(torrent::LOG_NOTICE, "important");
torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "important"); torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "important");
torrent::log_add_group_output(torrent::LOG_INFO, "complete"); torrent::log_add_group_output(torrent::LOG_INFO, "complete");
torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "complete"); torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "complete");
@@ -156,6 +167,8 @@ main(int argc, char** argv) {
scgi::ThreadScgi::create_thread(); scgi::ThreadScgi::create_thread();
session::ThreadSession::create_thread(); session::ThreadSession::create_thread();
SignalHandler::set_unblock(SIGCHLD);
// Initialize option handlers after libtorrent to ensure // Initialize option handlers after libtorrent to ensure
// torrent::ConnectionManager* are valid etc. // torrent::ConnectionManager* are valid etc.
initialize_commands(); initialize_commands();
@@ -281,14 +294,12 @@ main(int argc, char** argv) {
"view.filter = leeching,((and,((d.state)),((not,((d.complete))))))\n" "view.filter = leeching,((and,((d.state)),((not,((d.complete))))))\n"
"view.filter_on = leeching,event.download.resumed,event.download.paused,event.download.finished\n" "view.filter_on = leeching,event.download.resumed,event.download.paused,event.download.finished\n"
"schedule2 = view.main,10,10,((view.sort,main,20))\n" "schedule = view.main,10,10,((view.sort,main,20))\n"
"schedule2 = view.name,10,10,((view.sort,name,20))\n" "schedule = view.name,10,10,((view.sort,name,20))\n"
"schedule2 = session_save,1200,1200,((session.save))\n" "schedule = session_save,1200,1200,((session.save))\n"
"schedule2 = low_diskspace,5,60,((close_low_diskspace,500M))\n" "schedule = low_diskspace,5,60,((close_low_diskspace,500M))\n"
"schedule2 = prune_file_status,3600,86400,((system.file_status_cache.prune))\n" "schedule = prune_file_status,3600,86400,((system.file_status_cache.prune))\n"
"protocol.encryption.set=allow_incoming,prefer_plaintext,enable_retry\n"
"ui.color.focus.set=reverse\n" "ui.color.focus.set=reverse\n"
); );
@@ -296,101 +307,130 @@ main(int argc, char** argv) {
// Functions that might not get depracted as they are nice for // Functions that might not get depracted as they are nice for
// configuration files, and thus might do with just some // configuration files, and thus might do with just some
// cleanup. // cleanup.
CMD2_REDIRECT("upload_rate", "throttle.global_up.max_rate.set_kb"); CMD_REDIRECT("upload_rate", "throttle.global_up.max_rate.set_kb");
CMD2_REDIRECT("download_rate", "throttle.global_down.max_rate.set_kb"); CMD_REDIRECT("download_rate", "throttle.global_down.max_rate.set_kb");
CMD2_REDIRECT("ratio.enable", "group.seeding.ratio.enable"); CMD_REDIRECT("ratio.enable", "group.seeding.ratio.enable");
CMD2_REDIRECT("ratio.disable", "group.seeding.ratio.disable"); CMD_REDIRECT("ratio.disable", "group.seeding.ratio.disable");
CMD2_REDIRECT("ratio.min", "group.seeding.ratio.min"); CMD_REDIRECT("ratio.min", "group.seeding.ratio.min");
CMD2_REDIRECT("ratio.max", "group.seeding.ratio.max"); CMD_REDIRECT("ratio.max", "group.seeding.ratio.max");
CMD2_REDIRECT("ratio.upload", "group.seeding.ratio.upload"); CMD_REDIRECT("ratio.upload", "group.seeding.ratio.upload");
CMD2_REDIRECT("ratio.min.set", "group.seeding.ratio.min.set"); CMD_REDIRECT("ratio.min.set", "group.seeding.ratio.min.set");
CMD2_REDIRECT("ratio.max.set", "group.seeding.ratio.max.set"); CMD_REDIRECT("ratio.max.set", "group.seeding.ratio.max.set");
CMD2_REDIRECT("ratio.upload.set", "group.seeding.ratio.upload.set"); CMD_REDIRECT("ratio.upload.set", "group.seeding.ratio.upload.set");
CMD2_REDIRECT("encryption", "protocol.encryption.set"); CMD_REDIRECT("encryption", "protocol.encryption.set");
CMD2_REDIRECT("encoding_list", "encoding.add");
CMD2_REDIRECT("connection_leech", "protocol.connection.leech.set"); CMD_REDIRECT("check_hash", "pieces.hash.on_completion.set");
CMD2_REDIRECT("connection_seed", "protocol.connection.seed.set");
CMD2_REDIRECT("min_peers", "throttle.min_peers.normal.set"); CMD_REDIRECT("connection_leech", "protocol.connection.leech.set");
CMD2_REDIRECT("max_peers", "throttle.max_peers.normal.set"); CMD_REDIRECT("connection_seed", "protocol.connection.seed.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"); CMD_REDIRECT("min_peers", "throttle.min_peers.normal.set");
CMD2_REDIRECT("max_uploads", "throttle.max_uploads.set"); CMD_REDIRECT("max_peers", "throttle.max_peers.normal.set");
CMD2_REDIRECT("min_downloads", "throttle.min_downloads.set"); CMD_REDIRECT("min_peers_seed", "throttle.min_peers.seed.set");
CMD2_REDIRECT("max_downloads", "throttle.max_downloads.set"); CMD_REDIRECT("max_peers_seed", "throttle.max_peers.seed.set");
CMD2_REDIRECT("max_uploads_div", "throttle.max_uploads.div.set"); CMD_REDIRECT("min_uploads", "throttle.min_uploads.set");
CMD2_REDIRECT("max_uploads_global", "throttle.max_uploads.global.set"); CMD_REDIRECT("max_uploads", "throttle.max_uploads.set");
CMD2_REDIRECT("max_downloads_div", "throttle.max_downloads.div.set"); CMD_REDIRECT("min_downloads", "throttle.min_downloads.set");
CMD2_REDIRECT("max_downloads_global", "throttle.max_downloads.global.set"); CMD_REDIRECT("max_downloads", "throttle.max_downloads.set");
CMD2_REDIRECT("max_memory_usage", "pieces.memory.max.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");
CMD2_REDIRECT("bind", "network.bind_address.set"); CMD_REDIRECT("directory", "directory.default.set");
CMD2_REDIRECT("ip", "network.local_address.set"); CMD_REDIRECT("session", "session.path.set");
CMD2_REDIRECT("port_range", "network.port_range.set");
CMD2_REDIRECT("dht", "dht.mode.set"); CMD_REDIRECT_NO_EXPORT("port_range", "network.listen.port.range.set");
CMD_REDIRECT_NO_EXPORT("scgi_port", "network.scgi.open_port");
CMD_REDIRECT_NO_EXPORT("scgi_local", "network.scgi.open_local");
CMD2_REDIRECT("port_random", "network.port_random.set"); CMD_REDIRECT("to_gm_time", "convert.gm_time");
CMD2_REDIRECT("proxy_address", "network.proxy_address.set"); 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");
CMD2_REDIRECT("scgi_port", "network.scgi.open_port"); // TODO: Deprecate these at some point a while after 1.1 release.
CMD2_REDIRECT("scgi_local", "network.scgi.open_local");
CMD2_REDIRECT("directory", "directory.default.set"); CMD_REDIRECT("d.multicall2", "d.multicall");
CMD2_REDIRECT("session", "session.path.set"); 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");
CMD_REDIRECT("network.http.proxy_address", "network.proxy.http");
CMD_REDIRECT("network.http.proxy_address.set", "network.proxy.http.set");
CMD2_REDIRECT("check_hash", "pieces.hash.on_completion.set"); rpc::rpc.mark_safe("d.multicall2");
rpc::rpc.mark_safe("network.max_open_sockets");
rpc::rpc.mark_safe("network.http.proxy_address");
CMD2_REDIRECT("key_layout", "keys.layout.set"); 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_REDIRECT("to_gm_time", "convert.gm_time"); CMD2_ANY_VALUE_V("network.max_open_files.set", [](auto, auto) {
CMD2_REDIRECT("to_gm_date", "convert.gm_date"); lt_log_print(torrent::LOG_WARN, "network.max_open_files.set is deprecated, use system.sockets.files.min_alloc.set instead.");
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");
CMD2_REDIRECT("torrent_list_layout", "ui.torrent_list.layout.set");
// Deprecate:
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");
// Deprecated commands. Don't use these anymore.
//
// It has been so long that we now re-create these commands with the new (old by now) command
// call style, where the first argument is the target.
// if (rpc::call_command_value("method.use_intermediate") == 1) { // if (rpc::call_command_value("method.use_intermediate") == 1) {
// CMD2_REDIRECT("execute", "execute2");
// CMD2_REDIRECT("schedule", "schedule2");
// CMD2_REDIRECT("schedule_remove", "schedule_remove2");
// } else if (rpc::call_command_value("method.use_intermediate") == 2) { // } else if (rpc::call_command_value("method.use_intermediate") == 2) {
// Allow for use in config files, etc, just don't export it. // Allow for use in config files, etc, just don't export it.
// CMD2_REDIRECT_NO_EXPORT("execute", "execute2");
// CMD2_REDIRECT_NO_EXPORT("schedule", "schedule2");
// CMD2_REDIRECT_NO_EXPORT("schedule_remove", "schedule_remove2");
// } // }
// if (rpc::call_command_value("method.use_deprecated") == 1) { if (rpc::call_command_value("method.use_deprecated") == 1) {
// } CMD_REDIRECT("execute2", "execute");
CMD_REDIRECT("schedule2", "schedule");
CMD_REDIRECT("schedule_remove2", "schedule.remove");
CMD_REDIRECT("bind", "network.bind_address.set");
CMD_REDIRECT("ip", "network.local_address.set");
// TODO: Check if dht is on by default.
CMD_REDIRECT("dht", "dht.mode.set");
CMD_REDIRECT("port_random", "network.listen.port.random.set");
CMD_REDIRECT("proxy_address", "network.proxy.http.set");
CMD_REDIRECT("key_layout", "keys.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");
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.
CMD_REDIRECT("max_memory_usage", "pieces.memory.max.set");
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();
});
CMD_ANY("network.port_open", [](auto, auto) {
lt_log_print(torrent::LOG_WARN, "The 'network.port_open' command is deprecated and does nothing.");
return torrent::Object();
});
CMD_ANY("network.port_open.set", [](auto, auto) {
lt_log_print(torrent::LOG_WARN, "The 'network.port_open.set' command is deprecated and does nothing.");
return torrent::Object();
});
CMD_REDIRECT("network.port_random", "network.listen.port.random");
CMD_REDIRECT("network.port_random.set", "network.listen.port.random.set");
CMD_REDIRECT("network.port_range", "network.listen.port.range");
CMD_REDIRECT("network.port_range.set", "network.listen.port.range.set");
}
{ {
auto fd = torrent::fd_open_family(torrent::fd_flag_stream, AF_INET6); auto fd = torrent::fd_open_family(torrent::fd_flag_stream, AF_INET6);
@@ -417,20 +457,24 @@ main(int argc, char** argv) {
}); });
LT_LOG("seeded srandom and srand48 (seed:%u)", random_seed); LT_LOG("seeded srandom and srand48 (seed:%u)", random_seed);
LT_LOG("max memory usage: %" PRIu64, torrent::runtime::memory_manager()->max_memory_usage());
control->initialize(); control->initialize();
control->ui()->load_input_history(); control->ui()->load_input_history();
// Load session torrents and perform scheduled tasks to ensure torrent::net_thread::http_stack()->set_user_agent(USER_AGENT);
// session torrents are loaded before arg torrents. torrent::runtime::initialize_network();
// Load session torrents and perform scheduled tasks to ensure session torrents are loaded
// before arg torrents.
control->dht_manager()->set_auto_if_untouched_and_has_session();
control->dht_manager()->load_dht_cache(); control->dht_manager()->load_dht_cache();
load_session_torrents(session_thread::manager()->path()); load_session_torrents(session_thread::manager()->path());
load_arg_torrents(argv + firstArg, argv + argc); load_arg_torrents(argv + firstArg, argv + argc);
// Make sure we update the display before any scheduled tasks can // Make sure we update the display before any scheduled tasks can run, so that loading of
// run, so that loading of torrents doesn't look like it hangs on // torrents doesn't look like it hangs on startup.
// startup.
control->display()->adjust_layout(); control->display()->adjust_layout();
control->display()->receive_update(); control->display()->receive_update();
+31 -31
View File
@@ -6,31 +6,31 @@
#include "command.h" #include "command.h"
#define COMMAND_BASE_TEMPLATE_DEFINE(func_name) \ #define COMMAND_BASE_TEMPLATE_DEFINE(func_name) \
template const torrent::Object func_name<target_type>(command_base* rawCommand, target_type target, const torrent::Object& args); \ template const torrent::Object func_name<target_type>(command_base* command_raw, target_type target, const torrent::Object& args); \
template const torrent::Object func_name<core::Download*>(command_base* rawCommand, target_type target, const torrent::Object& args); \ template const torrent::Object func_name<core::Download*>(command_base* command_raw, target_type target, const torrent::Object& args); \
template const torrent::Object func_name<torrent::Peer*>(command_base* rawCommand, target_type target, const torrent::Object& args); \ template const torrent::Object func_name<torrent::Peer*>(command_base* command_raw, target_type target, const torrent::Object& args); \
template const torrent::Object func_name<torrent::tracker::Tracker*>(command_base* rawCommand, target_type target, const torrent::Object& args); \ template const torrent::Object func_name<torrent::tracker::Tracker*>(command_base* command_raw, target_type target, const torrent::Object& args); \
template const torrent::Object func_name<torrent::File*>(command_base* rawCommand, target_type target, const torrent::Object& args); \ template const torrent::Object func_name<torrent::File*>(command_base* command_raw, target_type target, const torrent::Object& args); \
template const torrent::Object func_name<torrent::FileListIterator*>(command_base* rawCommand, target_type target, const torrent::Object& args); template const torrent::Object func_name<torrent::FileListIterator*>(command_base* command_raw, target_type target, const torrent::Object& args);
namespace rpc { namespace rpc {
template <typename T> const torrent::Object template <typename T> const torrent::Object
command_base_call(command_base* rawCommand, target_type target, const torrent::Object& args) { command_base_call(command_base* command_raw, target_type target, const torrent::Object& args) {
if (!is_target_compatible<T>(target)) if (!is_target_compatible<T>(target))
throw torrent::input_error("Target of wrong type to command."); throw torrent::input_error("Target of wrong type to generic command.");
return command_base::_call<typename command_function<T>::type, T>(rawCommand, target, args); return command_base::_call<typename command_function<T>::type, T>(command_raw, target, args);
} }
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call); COMMAND_BASE_TEMPLATE_DEFINE(command_base_call);
template <typename T> const torrent::Object template <typename T> const torrent::Object
command_base_call_value_base(command_base* rawCommand, target_type target, const torrent::Object& rawArgs, int base, int unit) { command_base_call_value_base(command_base* command_raw, target_type target, const torrent::Object& args_raw, int base, int unit) {
if (!is_target_compatible<T>(target)) if (!is_target_compatible<T>(target))
throw torrent::input_error("Target of wrong type to command."); throw torrent::input_error("Target of wrong type to value command.");
const torrent::Object& arg = convert_to_single_argument(rawArgs); auto& arg = convert_to_single_argument(args_raw);
if (arg.type() == torrent::Object::TYPE_STRING) { if (arg.type() == torrent::Object::TYPE_STRING) {
torrent::Object::value_type val; torrent::Object::value_type val;
@@ -38,55 +38,55 @@ command_base_call_value_base(command_base* rawCommand, target_type target, const
if (!parse_whole_value_nothrow(arg.as_string().c_str(), &val, base, unit)) if (!parse_whole_value_nothrow(arg.as_string().c_str(), &val, base, unit))
throw torrent::input_error("Not a value."); throw torrent::input_error("Not a value.");
return command_base::_call<typename command_value_function<T>::type, T>(rawCommand, target, val); return command_base::_call<typename command_value_function<T>::type, T>(command_raw, target, val);
} }
return command_base::_call<typename command_value_function<T>::type, T>(rawCommand, target, unit * arg.as_value()); return command_base::_call<typename command_value_function<T>::type, T>(command_raw, target, unit * arg.as_value());
} }
template <typename T> const torrent::Object template <typename T> const torrent::Object
command_base_call_value(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) { command_base_call_value(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
return command_base_call_value_base<T>(rawCommand, target, rawArgs, 0, 1); return command_base_call_value_base<T>(command_raw, target, args_raw, 0, 1);
} }
template <typename T> const torrent::Object template <typename T> const torrent::Object
command_base_call_value_kb(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) { command_base_call_value_kb(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
return command_base_call_value_base<T>(rawCommand, target, rawArgs, 0, 1024); return command_base_call_value_base<T>(command_raw, target, args_raw, 0, 1024);
} }
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_value); COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_value);
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_value_kb); COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_value_kb);
template <typename T> const torrent::Object template <typename T> const torrent::Object
command_base_call_string(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) { command_base_call_string(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
if (!is_target_compatible<T>(target)) if (!is_target_compatible<T>(target))
throw torrent::input_error("Target of wrong type to command."); throw torrent::input_error("Target of wrong type to string command.");
const torrent::Object& arg = convert_to_single_argument(rawArgs); auto& arg = convert_to_single_argument(args_raw);
if (arg.type() == torrent::Object::TYPE_RAW_STRING) if (arg.type() == torrent::Object::TYPE_RAW_STRING)
return command_base::_call<typename command_string_function<T>::type, T>(rawCommand, target, arg.as_raw_string().as_string()); return command_base::_call<typename command_string_function<T>::type, T>(command_raw, target, arg.as_raw_string().as_string());
return command_base::_call<typename command_string_function<T>::type, T>(rawCommand, target, arg.as_string()); return command_base::_call<typename command_string_function<T>::type, T>(command_raw, target, arg.as_string());
} }
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_string); COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_string);
template <typename T> const torrent::Object template <typename T> const torrent::Object
command_base_call_list(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) { command_base_call_list(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
if (!is_target_compatible<T>(target)) if (!is_target_compatible<T>(target))
throw torrent::input_error("Target of wrong type to command."); throw torrent::input_error("Target of wrong type to list command.");
if (rawArgs.type() != torrent::Object::TYPE_LIST) { if (args_raw.type() != torrent::Object::TYPE_LIST) {
torrent::Object::list_type arg; torrent::Object::list_type arg;
if (!rawArgs.is_empty())
arg.push_back(rawArgs);
return command_base::_call<typename command_list_function<T>::type, T>(rawCommand, target, arg); if (!args_raw.is_empty())
arg.push_back(args_raw);
return command_base::_call<typename command_list_function<T>::type, T>(command_raw, target, arg);
} }
return command_base::_call<typename command_list_function<T>::type, T>(rawCommand, target, rawArgs.as_list()); return command_base::_call<typename command_list_function<T>::type, T>(command_raw, target, args_raw.as_list());
} }
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_list); COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_list);
+80 -51
View File
@@ -3,6 +3,7 @@
#include <functional> #include <functional>
#include <limits> #include <limits>
#include <new>
#include <torrent/common.h> #include <torrent/common.h>
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/data/file_list_iterator.h> #include <torrent/data/file_list_iterator.h>
@@ -28,7 +29,6 @@ struct target_wrapper<void> {
typedef no_type* cleaned_type; typedef no_type* cleaned_type;
}; };
// Since c++0x isn't out yet...
template <typename T1, typename T2, typename T3> template <typename T1, typename T2, typename T3>
struct rt_triple : private std::pair<T1, T2> { struct rt_triple : private std::pair<T1, T2> {
typedef std::pair<T1, T2> base_type; typedef std::pair<T1, T2> base_type;
@@ -55,15 +55,11 @@ struct rt_triple : private std::pair<T1, T2> {
base_type(src.first, src.second), third(src.third) {} 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; class command_base;
typedef const torrent::Object (*command_base_call_type)(command_base*, target_type, const torrent::Object&); using target_type = rt_triple<int, void*, void*>;
typedef std::function<torrent::Object (target_type, const torrent::Object&)> base_function; using base_function = std::function<torrent::Object (target_type, const torrent::Object&)>;
using command_base_call_type = const torrent::Object (command_base*, target_type, const torrent::Object&);
template <typename tmpl> struct command_base_is_valid {}; template <typename tmpl> struct command_base_is_valid {};
template <command_base_call_type tmpl_func> struct command_base_is_type {}; template <command_base_call_type tmpl_func> struct command_base_is_type {};
@@ -87,17 +83,18 @@ public:
typedef const torrent::Object (*download_pair_slot) (command_base*, core::Download*, core::Download*, const torrent::Object&); typedef const torrent::Object (*download_pair_slot) (command_base*, core::Download*, core::Download*, const torrent::Object&);
static const int target_generic = 0; static constexpr int target_generic = 0;
static const int target_any = 1; static constexpr int target_any = 1;
static const int target_download = 2; static constexpr int target_download = 2;
static const int target_peer = 3; static constexpr int target_peer = 3;
static const int target_tracker = 4; static constexpr int target_tracker = 4;
static const int target_file = 5; static constexpr int target_file = 5;
static const int target_file_itr = 6; static constexpr int target_file_itr = 6;
static constexpr int target_download_pair = 7;
static const int target_download_pair = 7; static constexpr unsigned int max_arguments = 10;
static const unsigned int max_arguments = 10; static constexpr std::size_t optimal_alignment = std::max(alignof(std::max_align_t), alignof(base_function));
struct stack_type { struct stack_type {
torrent::Object* begin() { return reinterpret_cast<torrent::Object*>(buffer); } torrent::Object* begin() { return reinterpret_cast<torrent::Object*>(buffer); }
@@ -114,10 +111,48 @@ public:
char buffer[sizeof(torrent::Object) * max_arguments]; char buffer[sizeof(torrent::Object) * max_arguments];
}; };
command_base() { new (&_pod<base_function>()) base_function(); } command_base() : m_copy_helper(nullptr), m_dest_helper(nullptr) {}
command_base(const command_base& src) { new (&_pod<base_function>()) base_function(src._pod<base_function>()); }
~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(unsigned int index) { return current_stack.begin() + index; }
static torrent::Object& argument_ref(unsigned int index) { return *(current_stack.begin() + index); } static torrent::Object& argument_ref(unsigned int index) { return *(current_stack.begin() + index); }
@@ -132,55 +167,51 @@ public:
static void pop_stack(stack_type* stack, torrent::Object* last_stack); static void pop_stack(stack_type* stack, torrent::Object* last_stack);
template <typename T> 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(optimal_alignment >= alignof(T), "t_pod alignment insufficient for type");
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> 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) { 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> tmpl& _pod() { return reinterpret_cast<tmpl&>(t_pod); }
template <typename tmpl> const tmpl& _pod() const { return reinterpret_cast<const tmpl&>(t_pod); } template <typename tmpl> const tmpl& _pod() const { return reinterpret_cast<const tmpl&>(t_pod); }
template <typename Func, typename T, typename Args> template <typename Func, typename T, typename Args>
static const torrent::Object _call(command_base* cmd, target_type target, Args 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: protected:
// For use by functions that need to use placeholders to arguments using copy_fn_t = void (*)(void* dest, const void* src);
// within commands. E.d. callable command strings where one of the using dest_fn_t = void (*)(void* ptr);
// arguments within the command needs to be supplied by the caller.
#ifdef HAVE_CXX11 alignas(optimal_alignment) char t_pod[sizeof(base_function)];
union {
base_function t_pod; copy_fn_t m_copy_helper;
// char t_pod[sizeof(base_function)]; dest_fn_t m_dest_helper;
};
#else
union {
char t_pod[sizeof(base_function)];
};
#endif
}; };
template <typename T1 = void, typename T2 = void> template <typename T1 = void, typename T2 = void>
struct target_type_id { struct target_type_id {
// Nothing here, so we cause an error.
}; };
template <typename T> inline bool template <typename T> inline bool
is_target_compatible(const target_type& target) { return target.first == target_type_id<T>::value; } 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; } inline bool is_target_pair(const target_type& target) { return target.first >= command_base::target_download_pair; }
template <typename T> inline T template <typename T> inline T
@@ -203,7 +234,7 @@ command_base::_call(command_base* cmd, target_type target, Args args) {
#define COMMAND_BASE_TEMPLATE_TYPE(func_type, func_parm) \ #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 <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<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<core::Download*>::type> { static const int value = 1; }; \
template <> struct command_base_is_valid<func_type<torrent::Peer*>::type> { static const int value = 1; }; \ template <> struct command_base_is_valid<func_type<torrent::Peer*>::type> { static const int value = 1; }; \
@@ -211,8 +242,6 @@ command_base::_call(command_base* cmd, target_type target, Args args) {
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::File*>::type> { static const int value = 1; }; \
template <> struct command_base_is_valid<func_type<torrent::FileListIterator*>::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_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_value_function, torrent::Object (T, const torrent::Object::value_type&));
COMMAND_BASE_TEMPLATE_TYPE(command_string_function, torrent::Object (T, const std::string&)); COMMAND_BASE_TEMPLATE_TYPE(command_string_function, torrent::Object (T, const std::string&));
@@ -220,7 +249,7 @@ COMMAND_BASE_TEMPLATE_TYPE(command_list_function, torrent::Object (T, const to
#define COMMAND_BASE_TEMPLATE_CALL(func_name, func_type) \ #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 <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<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<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; }; \ template <> struct command_base_is_type<func_name<torrent::Peer*> > { static const int value = 1; typedef func_type<torrent::Peer*>::type type; }; \
+2 -2
View File
@@ -5,7 +5,7 @@
#include <functional> #include <functional>
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/utils/scheduler.h> #include <torrent/system/scheduler.h>
namespace rpc { namespace rpc {
@@ -43,7 +43,7 @@ private:
uint32_t m_interval{}; uint32_t m_interval{};
std::chrono::microseconds m_time_scheduled; std::chrono::microseconds m_time_scheduled;
torrent::utils::SchedulerEntry m_task; torrent::system::SchedulerEntry m_task;
// Flags for various things. // Flags for various things.
}; };
+53 -124
View File
@@ -1,16 +1,22 @@
#include "config.h" #include "config.h"
#include <cerrno> #include "rpc/exec_file.h"
#include <cstring>
#include <fcntl.h>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <torrent/system/thread.h>
#include "exec_file.h" // #include <cassert>
#include "parse.h" // #include <cerrno>
// #include <cstring>
// #include <fcntl.h>
// #include <spawn.h>
// #include <string>
#include <unistd.h>
// #include <sys/types.h>
#include <sys/uio.h>
// #include <torrent/net/fd.h>
#include <torrent/system/thread.h>
#include <torrent/system/spawn_process.h>
#include <torrent/system/types.h>
#include "rpc/parse.h"
namespace rpc { namespace rpc {
@@ -18,133 +24,56 @@ namespace rpc {
int int
ExecFile::execute(const char* file, char* const* argv, int flags) { ExecFile::execute(const char* file, char* const* argv, int flags) {
// Write the executed command and its parameters to the log fd. torrent::system::SpawnProcess spawn_process;
[[maybe_unused]] int result;
spawn_process.set_log_fd(m_log_fd);
spawn_process.set_background(flags & flag_background);
spawn_process.set_capture_output(flags & flag_capture);
if (m_log_fd != -1) { if (m_log_fd != -1) {
for (char* const* itr = argv; *itr != NULL; itr++) { std::vector<struct iovec> iovecs;
if (itr == argv) iovecs.reserve(32);
result = write(m_log_fd, "\n---\n", sizeof("\n---\n")); iovecs.push_back({const_cast<char*>("\n---\n"), 5});
else
result = write(m_log_fd, " ", 1);
result = write(m_log_fd, *itr, std::strlen(*itr)); for (auto* itr = argv; *itr != nullptr; itr++) {
if (itr != argv)
iovecs.push_back({const_cast<char*>(" "), 1});
iovecs.push_back({*itr, std::strlen(*itr)});
} }
result = write(m_log_fd, "\n---\n", sizeof("\n---\n")); iovecs.push_back({const_cast<char*>("\n---\n"), 5});
[[maybe_unused]] int result = ::writev(m_log_fd, iovecs.data(), iovecs.size());
} }
int pipeFd[2]; int spawn_status = spawn_process.execute(file, argv);
if ((flags & flag_capture) && pipe(pipeFd))
throw torrent::input_error("ExecFile::execute(...) Pipe creation failed.");
pid_t childPid = fork();
if (childPid == -1) {
if (flags & flag_capture) {
::close(pipeFd[0]);
::close(pipeFd[1]);
}
throw torrent::input_error("ExecFile::execute(...) Fork failed.");
}
if (childPid == 0) {
if (flags & flag_background) {
pid_t detached_pid = fork();
if (detached_pid == -1)
_exit(-1);
if (detached_pid != 0) {
if (m_log_fd != -1)
result = write(m_log_fd, "\n--- Background task ---\n", sizeof("\n--- Background task ---\n"));
_exit(0);
}
m_log_fd = -1;
flags &= ~flag_capture;
}
int devNull = open("/dev/null", O_RDWR);
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);
result = execvp(file, argv);
_exit(result);
}
if (flags & flag_capture) {
m_capture = std::string();
::close(pipeFd[1]);
char buffer[4096];
ssize_t length;
do {
length = read(pipeFd[0], buffer, sizeof(buffer));
if (length > 0)
m_capture += std::string(buffer, length);
} while (length > 0);
::close(pipeFd[0]);
if (spawn_status != 0) {
if (m_log_fd != -1) { if (m_log_fd != -1) {
result = write(m_log_fd, "Captured output:\n", sizeof("Captured output:\n")); auto prefix = "\n--- posix_spawn failed: ";
result = write(m_log_fd, m_capture.data(), m_capture.length()); auto errno_str = torrent::system::errno_enum_str(spawn_status) + " ---\n";
struct iovec iovecs[2] = {
{const_cast<char*>(prefix), std::strlen(prefix)},
{const_cast<char*>(errno_str.c_str()), errno_str.size()},
};
[[maybe_unused]] int result = ::writev(m_log_fd, iovecs, 2);
} }
throw torrent::input_error("ExecFile::execute() posix_spawn failed: " + torrent::system::errno_enum_str(spawn_status));
} }
int status; if (flags & flag_background) {
m_waitpid_queue.close_pid(spawn_process.child_pid());
while (waitpid(childPid, &status, 0) == -1) { return 0;
switch (errno) {
case EINTR:
continue;
case ECHILD:
throw torrent::internal_error("ExecFile::execute(...) waitpid failed with ECHILD, child process not found.");
case EINVAL:
throw torrent::internal_error("ExecFile::execute(...) waitpid failed with EINVAL.");
default:
throw torrent::internal_error("ExecFile::execute(...) waitpid failed with unexpected error: " + std::string(std::strerror(errno)));
}
};
// Check return value?
if (m_log_fd != -1) {
if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
result = write(m_log_fd, "\n--- Success ---\n", sizeof("\n--- Success ---\n"));
else
result = write(m_log_fd, "\n--- Error ---\n", sizeof("\n--- Error ---\n"));
} }
return status; if (flags & flag_capture)
m_capture = spawn_process.capture_child_output();
return spawn_process.wait_for_child();
} }
torrent::Object torrent::Object
@@ -204,4 +133,4 @@ ExecFile::execute_object(const torrent::Object& rawArgs, int flags) {
return torrent::Object((int64_t)status); return torrent::Object((int64_t)status);
} }
} } // namespace rpc
+4
View File
@@ -3,6 +3,8 @@
#include <torrent/object.h> #include <torrent/object.h>
#include "utils/waitpid_queue.h"
namespace rpc { namespace rpc {
class ExecFile { class ExecFile {
@@ -24,6 +26,8 @@ public:
private: private:
int m_log_fd{-1}; int m_log_fd{-1};
std::string m_capture; std::string m_capture;
utils::WaitpidQueue m_waitpid_queue;
}; };
} }
+2 -5
View File
@@ -258,11 +258,8 @@ JsonRpc::process(const char* in_buffer, uint32_t length, slot_write callback) {
return callback(response_str.c_str(), response_str.size()); return callback(response_str.c_str(), response_str.size());
} catch (json::parse_error& e) { } catch (json::exception& e) {
auto err_str = json_error(JSONRPC_PARSE_ERROR, e.what(), nullptr).dump(-1, ' ', false, json::error_handler_t::replace); // Exception strings may contain invalid UTF-8, hence the ::replace
return callback(err_str.c_str(), err_str.size());
} catch (json::type_error& e) {
// Type errors may be caused by invalid UTF-8 strings in exception strings, hence the ::replace
auto err_str = json_error(JSONRPC_PARSE_ERROR, e.what(), nullptr).dump(-1, ' ', false, json::error_handler_t::replace); auto err_str = json_error(JSONRPC_PARSE_ERROR, e.what(), nullptr).dump(-1, ' ', false, json::error_handler_t::replace);
return callback(err_str.c_str(), err_str.size()); return callback(err_str.c_str(), err_str.size());
} }
+4
View File
@@ -10,6 +10,10 @@
#ifdef HAVE_LUA #ifdef HAVE_LUA
#include <lua.hpp> #include <lua.hpp>
// LUA_OK was introduced in Lua 5.2; define for Lua 5.1 and LuaJIT2.0.
#ifndef LUA_OK
#define LUA_OK 0
#endif
#endif #endif
#include <torrent/object.h> #include <torrent/object.h>
+6 -37
View File
@@ -113,25 +113,20 @@ bool
RpcManager::process(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback) { RpcManager::process(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback) {
switch (type) { switch (type) {
case RPCType::XML: case RPCType::XML:
// TODO: 'network.rpc.use_xmlrpc' should be a bool in RpcManager, not a command variable. if (!m_xmlrpc.is_valid() || !m_use_xmlrpc) {
if (m_xmlrpc.is_valid() && rpc::call_command_value("network.rpc.use_xmlrpc")) {
return m_xmlrpc.process(in_buffer, length, callback);
} else {
const std::string response = "<?xml version=\"1.0\"?><methodResponse><fault><value><struct><member><name>faultCode</name><value><i8>-501</i8></value></member><member><name>faultString</name><value><string>XML-RPC not supported</string></value></member></struct></value></fault></methodResponse>"; const std::string response = "<?xml version=\"1.0\"?><methodResponse><fault><value><struct><member><name>faultCode</name><value><i8>-501</i8></value></member><member><name>faultString</name><value><string>XML-RPC not supported</string></value></member></struct></value></fault></methodResponse>";
return callback(response.c_str(), response.size()); return callback(response.c_str(), response.size());
} }
break;
return m_xmlrpc.process(in_buffer, length, callback);
case RPCType::JSON: case RPCType::JSON:
if (rpc::call_command_value("network.rpc.use_jsonrpc")) { if (!m_use_jsonrpc) {
return m_jsonrpc.process(in_buffer, length, callback);
} else {
const std::string response = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32601,\"message\":\"JSON-RPC not supported\"},\"id\":null}"; const std::string response = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32601,\"message\":\"JSON-RPC not supported\"},\"id\":null}";
return callback(response.c_str(), response.size()); return callback(response.c_str(), response.size());
} }
break;
return m_jsonrpc.process(in_buffer, length, callback);
default: default:
throw torrent::input_error("invalid parameters: unknown RPC type"); throw torrent::input_error("invalid parameters: unknown RPC type");
@@ -172,32 +167,6 @@ RpcManager::cleanup() {
m_jsonrpc.cleanup(); m_jsonrpc.cleanup();
} }
bool
RpcManager::is_type_enabled(RPCType type) const {
switch (type) {
case RPCType::XML:
return m_is_xmlrpc_enabled;
case RPCType::JSON:
return m_is_jsonrpc_enabled;
default:
throw torrent::input_error("invalid parameters: unknown RPC type");
}
}
void
RpcManager::set_type_enabled(RPCType type, bool enabled) {
switch (type) {
case RPCType::XML:
m_is_xmlrpc_enabled = enabled;
break;
case RPCType::JSON:
m_is_jsonrpc_enabled = enabled;
break;
default:
throw torrent::input_error("invalid parameters: unknown RPC type");
}
}
void void
RpcManager::insert_command(const char* name, const char* parm, const char* doc) { RpcManager::insert_command(const char* name, const char* parm, const char* doc) {
m_xmlrpc.insert_command(name, parm, doc); m_xmlrpc.insert_command(name, parm, doc);
+13 -4
View File
@@ -67,8 +67,11 @@ public:
int dialect() { return m_xmlrpc.dialect(); } int dialect() { return m_xmlrpc.dialect(); }
void set_dialect(int dialect) { m_xmlrpc.set_dialect(dialect); } void set_dialect(int dialect) { m_xmlrpc.set_dialect(dialect); }
bool is_type_enabled(RPCType type) const; bool use_xmlrpc() const;
void set_type_enabled(RPCType type, bool enabled); void set_use_xmlrpc(bool v);
bool use_jsonrpc() const;
void set_use_jsonrpc(bool v);
bool process(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback); bool process(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback);
bool process_untrusted(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback); bool process_untrusted(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback);
@@ -102,8 +105,9 @@ private:
JsonRpc m_jsonrpc; JsonRpc m_jsonrpc;
bool m_handlers_initialized{}; bool m_handlers_initialized{};
bool m_is_jsonrpc_enabled{true};
bool m_is_xmlrpc_enabled{true}; bool m_use_jsonrpc{true};
bool m_use_xmlrpc{true};
std::atomic<bool> m_scgi_allow_compression{true}; std::atomic<bool> m_scgi_allow_compression{true};
std::atomic<unsigned int> m_scgi_min_compress_size{1000}; std::atomic<unsigned int> m_scgi_min_compress_size{1000};
@@ -116,6 +120,11 @@ private:
extern RpcManager rpc; extern RpcManager rpc;
inline bool RpcManager::use_xmlrpc() const { return m_use_xmlrpc; }
inline void RpcManager::set_use_xmlrpc(bool v) { m_use_xmlrpc = v; }
inline bool RpcManager::use_jsonrpc() const { return m_use_jsonrpc; }
inline void RpcManager::set_use_jsonrpc(bool v) { m_use_jsonrpc = v; }
} // namespace rpc } // namespace rpc
#endif #endif
+4 -5
View File
@@ -48,7 +48,7 @@ SCgi::open_port(sockaddr* sa, unsigned int length, bool dont_route) {
open(reinterpret_cast<sockaddr*>(sa), length); 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 void
@@ -72,7 +72,7 @@ SCgi::open_named(const std::string& filename) {
open(reinterpret_cast<sockaddr*>(sa), offsetof(struct sockaddr_un, sun_path) + filename.size() + 1); 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; m_path = filename;
} }
@@ -86,7 +86,7 @@ SCgi::open_fd(int fd) {
// fd is already bound and listening; no bind()/listen() needed. // 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 void
@@ -111,7 +111,6 @@ SCgi::activate() {
torrent::this_thread::poll()->open(this); torrent::this_thread::poll()->open(this);
torrent::this_thread::poll()->insert_read(this); torrent::this_thread::poll()->insert_read(this);
torrent::this_thread::poll()->insert_error(this);
} }
// TODO: This should close the fd to avoid reuse. // TODO: This should close the fd to avoid reuse.
@@ -183,7 +182,7 @@ SCgi::event_read() {
task->cancel_open(); 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) if (!result)
break; break;
+2 -2
View File
@@ -3,13 +3,13 @@
#include <array> #include <array>
#include <memory> #include <memory>
#include <torrent/event.h> #include <torrent/system/event.h>
#include "rpc/scgi_task.h" #include "rpc/scgi_task.h"
namespace rpc { namespace rpc {
class SCgi : public torrent::Event { class SCgi : public torrent::system::Event {
public: public:
static const int max_tasks = 100; static const int max_tasks = 100;
+11 -8
View File
@@ -27,7 +27,7 @@ namespace rpc {
SCgiTask::SCgiTask() SCgiTask::SCgiTask()
: m_callback_id(torrent::system::make_callback_id()) { : m_callback_id(torrent::system::make_callback_id()) {
set_file_descriptor(-1); reset_file_descriptor();
} }
void void
@@ -49,7 +49,6 @@ SCgiTask::open(SCgi* parent, int fd) {
torrent::this_thread::poll()->open(this); torrent::this_thread::poll()->open(this);
torrent::this_thread::poll()->insert_read(this); torrent::this_thread::poll()->insert_read(this);
torrent::this_thread::poll()->insert_error(this);
auto lock = std::lock_guard<std::mutex>(m_result_mutex); auto lock = std::lock_guard<std::mutex>(m_result_mutex);
@@ -65,7 +64,7 @@ SCgiTask::cancel_open() {
torrent::this_thread::poll()->remove_and_close(this); torrent::this_thread::poll()->remove_and_close(this);
torrent::fd_close(file_descriptor()); torrent::fd_close(file_descriptor());
set_file_descriptor(-1); reset_file_descriptor();
}; };
void void
@@ -79,7 +78,7 @@ SCgiTask::close() {
torrent::this_thread::poll()->remove_and_close(this); torrent::this_thread::poll()->remove_and_close(this);
torrent::fd_close(file_descriptor()); torrent::fd_close(file_descriptor());
set_file_descriptor(-1); reset_file_descriptor();
}); });
// The callbacks are guaranteed to be finished/canceled at this point. // The callbacks are guaranteed to be finished/canceled at this point.
@@ -95,10 +94,14 @@ SCgiTask::event_read() {
if (m_content_length == 0) if (m_content_length == 0)
read_length--; read_length--;
if (read_length <= 0) if (read_length <= 0) {
throw torrent::internal_error("SCgiTask::event_read() no space in buffer for event_read."); if (m_content_length == 0)
return close();
int bytes = ::recv(m_fileDesc, m_buffer.data() + m_position, read_length, 0); throw torrent::internal_error("SCgiTask::event_read() no space in buffer for event_read.");
}
int bytes = ::recv(file_descriptor(), m_buffer.data() + m_position, read_length, 0);
if (bytes <= 0) { if (bytes <= 0) {
if (bytes == 0 || !(errno == EAGAIN || errno == EINTR)) if (bytes == 0 || !(errno == EAGAIN || errno == EINTR))
@@ -181,7 +184,7 @@ event_read_failed:
void void
SCgiTask::event_write() { SCgiTask::event_write() {
int bytes = ::send(m_fileDesc, m_buffer.data() + m_position, m_buffer.size() - m_position, 0); int bytes = ::send(file_descriptor(), m_buffer.data() + m_position, m_buffer.size() - m_position, 0);
if (bytes == -1) { if (bytes == -1) {
if (!(errno == EAGAIN || errno == EINTR)) if (!(errno == EAGAIN || errno == EINTR))
+4 -4
View File
@@ -4,13 +4,13 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <vector> #include <vector>
#include <torrent/event.h> #include <torrent/system/event.h>
namespace rpc { namespace rpc {
class SCgi; class SCgi;
class SCgiTask : public torrent::Event { 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;
@@ -22,8 +22,8 @@ public:
const char* type_name() const override { return "scgi-task"; } const char* type_name() const override { return "scgi-task"; }
bool is_open() const { return m_fileDesc != -1; } bool is_open() const { return file_descriptor() != -1; }
bool is_available() const { return m_fileDesc == -1; } bool is_available() const { return file_descriptor() == -1; }
void open(SCgi* parent, int fd); void open(SCgi* parent, int fd);
void cancel_open(); void cancel_open();
+2 -1
View File
@@ -1,3 +1,4 @@
#include <deque>
#include "config.h" #include "config.h"
#include "xmlrpc.h" #include "xmlrpc.h"
@@ -9,7 +10,7 @@
namespace rpc { namespace rpc {
std::vector<std::string> XmlRpc::m_command_names; std::deque<std::string> XmlRpc::m_command_names;
const char* const char*
XmlRpc::store_command_name(const char* name) { XmlRpc::store_command_name(const char* name) {
+2 -1
View File
@@ -1,6 +1,7 @@
#ifndef RTORRENT_RPC_XMLRPC_H #ifndef RTORRENT_RPC_XMLRPC_H
#define RTORRENT_RPC_XMLRPC_H #define RTORRENT_RPC_XMLRPC_H
#include <deque>
#include <functional> #include <functional>
#include <torrent/common.h> #include <torrent/common.h>
#include <torrent/hash_string.h> #include <torrent/hash_string.h>
@@ -61,7 +62,7 @@ public:
private: private:
static const char* store_command_name(const char* name); static const char* store_command_name(const char* name);
static std::vector<std::string> m_command_names; static std::deque<std::string> m_command_names;
slot_download m_slotFindDownload; slot_download m_slotFindDownload;
slot_file m_slotFindFile; slot_file m_slotFindFile;
+5
View File
@@ -451,6 +451,11 @@ XmlRpc::process(const char* inBuffer, uint32_t length, slot_write slotWrite) {
if (local_env.fault_occurred && local_env.fault_code == XMLRPC_INTERNAL_ERROR) if (local_env.fault_occurred && local_env.fault_code == XMLRPC_INTERNAL_ERROR)
throw torrent::internal_error("Internal error in XMLRPC."); throw torrent::internal_error("Internal error in XMLRPC.");
if (memblock == nullptr) {
xmlrpc_env_clean(&local_env);
return false;
}
bool result = slotWrite((const char*)xmlrpc_mem_block_contents(memblock), bool result = slotWrite((const char*)xmlrpc_mem_block_contents(memblock),
xmlrpc_mem_block_size(memblock)); xmlrpc_mem_block_size(memblock));
+26 -5
View File
@@ -49,13 +49,23 @@ element_access(const tinyxml2::XMLElement* elem, std::initializer_list<std::stri
return result; return result;
} }
const char*
element_text_value(const tinyxml2::XMLNode* node) {
auto text = node->ToText();
if (text == nullptr)
throw rpc_error(XMLRPC_TYPE_ERROR, "expected a text value");
return text->Value();
}
long long long long
element_to_int(const tinyxml2::XMLNode* elem) { element_to_int(const tinyxml2::XMLNode* elem) {
char* pos; char* pos;
if (elem->FirstChild() == nullptr) { if (elem->FirstChild() == nullptr) {
throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse empty integer"); throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse empty integer");
} }
auto str = elem->FirstChild()->ToText()->Value(); auto str = element_text_value(elem->FirstChild());
auto result = std::strtoll(str, &pos, 10); auto result = std::strtoll(str, &pos, 10);
if (pos == str || *pos != '\0') if (pos == str || *pos != '\0')
throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse integer value"); throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse integer value");
@@ -85,7 +95,7 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
if (child_element == nullptr) if (child_element == nullptr)
return torrent::Object(""); return torrent::Object("");
return torrent::Object(child_element->ToText()->Value()); return torrent::Object(element_text_value(child_element));
} else if (std::strncmp(root_type, "int", sizeof("int")) == 0 || } else if (std::strncmp(root_type, "int", sizeof("int")) == 0 ||
std::strncmp(root_type, "i4", sizeof("i4")) == 0 || std::strncmp(root_type, "i4", sizeof("i4")) == 0 ||
@@ -98,7 +108,7 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
if (child_element == nullptr) if (child_element == nullptr)
throw rpc_error(XMLRPC_TYPE_ERROR, "empty boolean element"); throw rpc_error(XMLRPC_TYPE_ERROR, "empty boolean element");
auto boolean_text = std::string(child_element->ToText()->Value()); auto boolean_text = std::string(element_text_value(child_element));
if (boolean_text == "1") if (boolean_text == "1")
return torrent::Object((int64_t)1); return torrent::Object((int64_t)1);
@@ -129,7 +139,12 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
if (name_element == nullptr) if (name_element == nullptr)
throw rpc_error(XMLRPC_PARSE_ERROR, "struct member missing name element"); throw rpc_error(XMLRPC_PARSE_ERROR, "struct member missing name element");
map[name_element->GetText()] = std::move(xml_value_to_object(child->FirstChildElement("value"))); auto name_text = name_element->GetText();
if (name_text == nullptr)
throw rpc_error(XMLRPC_PARSE_ERROR, "struct member has an empty name element");
map[name_text] = std::move(xml_value_to_object(child->FirstChildElement("value")));
} }
return map_raw; return map_raw;
@@ -140,7 +155,7 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
if (child_element == nullptr) if (child_element == nullptr)
return torrent::Object(""); return torrent::Object("");
return torrent::Object(utils::decode_base64(utils::remove_newlines(child_element->ToText()->Value()))); return torrent::Object(utils::decode_base64(utils::remove_newlines(element_text_value(child_element))));
} else { } else {
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received unsupported value type: " + std::string(root_type)); throw rpc_error(XMLRPC_INTERNAL_ERROR, "received unsupported value type: " + std::string(root_type));
@@ -300,6 +315,9 @@ process_document(const tinyxml2::XMLDocument* doc, tinyxml2::XMLPrinter* printer
if (doc->FirstChildElement("methodCall")->FirstChildElement("methodName") == nullptr) if (doc->FirstChildElement("methodCall")->FirstChildElement("methodName") == nullptr)
throw rpc_error(XMLRPC_PARSE_ERROR, "methodName element not found"); throw rpc_error(XMLRPC_PARSE_ERROR, "methodName element not found");
auto method_name = doc->FirstChildElement("methodCall")->FirstChildElement("methodName")->GetText(); auto method_name = doc->FirstChildElement("methodCall")->FirstChildElement("methodName")->GetText();
if (method_name == nullptr)
throw rpc_error(XMLRPC_PARSE_ERROR, "methodName element is empty");
torrent::Object result; torrent::Object result;
// Add a shim here for system.multicall to allow better code reuse, and // Add a shim here for system.multicall to allow better code reuse, and
@@ -310,6 +328,9 @@ process_document(const tinyxml2::XMLDocument* doc, tinyxml2::XMLPrinter* printer
auto parent_elements = element_access(doc->RootElement(), {"params", "param", "value", "array", "data"}); auto parent_elements = element_access(doc->RootElement(), {"params", "param", "value", "array", "data"});
for (auto child = parent_elements->FirstChildElement("value"); child; child = child->NextSiblingElement("value")) { for (auto child = parent_elements->FirstChildElement("value"); child; child = child->NextSiblingElement("value")) {
auto sub_method_name = element_access(child, {"struct", "member", "value", "string"})->GetText(); auto sub_method_name = element_access(child, {"struct", "member", "value", "string"})->GetText();
if (sub_method_name == nullptr)
throw rpc_error(XMLRPC_PARSE_ERROR, "multicall methodName element is empty");
// If sub_params ends up a nullptr at the end of this if-chian, // If sub_params ends up a nullptr at the end of this if-chian,
// execute_command will turn it into an empty list // execute_command will turn it into an empty list
auto sub_params = element_access(child, {"struct", "member"}); auto sub_params = element_access(child, {"struct", "member"});
+1 -1
View File
@@ -38,7 +38,7 @@ parse_main_options(int argc, char** argv) {
optionParser.insert_option('b', [](auto& arg) { rpc::call_command_set_string("network.bind_address.set", arg); }); optionParser.insert_option('b', [](auto& arg) { rpc::call_command_set_string("network.bind_address.set", arg); });
optionParser.insert_option('d', [](auto& arg) { rpc::call_command_set_string("directory.default.set", arg); }); optionParser.insert_option('d', [](auto& arg) { rpc::call_command_set_string("directory.default.set", arg); });
optionParser.insert_option('i', [](auto& arg) { rpc::call_command_set_string("ip", arg); }); optionParser.insert_option('i', [](auto& arg) { rpc::call_command_set_string("ip", arg); });
optionParser.insert_option('p', [](auto& arg) { rpc::call_command_set_string("network.port_range.set", arg); }); optionParser.insert_option('p', [](auto& arg) { rpc::call_command_set_string("network.listen.port.range.set", arg); });
optionParser.insert_option('s', [](auto& arg) { rpc::call_command_set_string("session", arg); }); optionParser.insert_option('s', [](auto& arg) { rpc::call_command_set_string("session", arg); });
optionParser.insert_option('O', [](auto& arg) { rpc::parse_command_single_std(arg); }); optionParser.insert_option('O', [](auto& arg) { rpc::parse_command_single_std(arg); });
+41
View File
@@ -42,6 +42,7 @@ SignalHandler::set_handler(unsigned int signum, slot_void slot) {
throw std::logic_error("SignalHandler::set_handler(...) received an empty slot."); throw std::logic_error("SignalHandler::set_handler(...) received an empty slot.");
struct sigaction sa; struct sigaction sa;
sigemptyset(&sa.sa_mask); sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; sa.sa_flags = SA_RESTART;
sa.sa_handler = &SignalHandler::caught; sa.sa_handler = &SignalHandler::caught;
@@ -52,6 +53,46 @@ SignalHandler::set_handler(unsigned int signum, slot_void slot) {
m_handlers[signum] = slot; m_handlers[signum] = slot;
} }
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_sigchild_ignore() {
struct sigaction sa;
sa.sa_handler = SIG_IGN;
sa.sa_flags = SA_NOCLDWAIT | SA_RESTART;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGCHLD, &sa, NULL) == -1)
throw std::logic_error("Could not set sigaction (ignore) for SIGCHLD: " + std::string(std::strerror(errno)));
}
void void
SignalHandler::set_sigaction_handler(unsigned int signum, handler_slot slot) { SignalHandler::set_sigaction_handler(unsigned int signum, handler_slot slot) {
if (signum >= HIGHEST_SIGNAL) if (signum >= HIGHEST_SIGNAL)
+5 -34
View File
@@ -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 <sundell.software@gmail.com>
#ifndef RTORRENT_SIGNAL_HANDLER_H #ifndef RTORRENT_SIGNAL_HANDLER_H
#define RTORRENT_SIGNAL_HANDLER_H #define RTORRENT_SIGNAL_HANDLER_H
@@ -56,6 +22,11 @@ public:
static void set_ignore(unsigned int signum); static void set_ignore(unsigned int signum);
static void set_handler(unsigned int signum, slot_void slot); 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_sigchild_ignore();
static void set_sigaction_handler(unsigned int signum, handler_slot slot); static void set_sigaction_handler(unsigned int signum, handler_slot slot);
static const char* as_string(unsigned int signum); static const char* as_string(unsigned int signum);
+4 -8
View File
@@ -3,8 +3,6 @@
#include <cassert> #include <cassert>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/chunk_manager.h>
#include <torrent/connection_manager.h>
#include <torrent/throttle.h> #include <torrent/throttle.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/data/file_list.h> #include <torrent/data/file_list.h>
@@ -32,10 +30,10 @@
namespace ui { namespace ui {
Download::Download(core::Download* d) : Download::Download(core::Download* d)
m_download(d) { : m_download(d) {
m_windowDownloadStatus = new WDownloadStatus(d); m_windowDownloadStatus = std::make_unique<WDownloadStatus>(d);
m_windowDownloadStatus->set_bottom(true); m_windowDownloadStatus->set_bottom(true);
m_uiArray[DISPLAY_MENU] = create_menu(); m_uiArray[DISPLAY_MENU] = create_menu();
@@ -61,8 +59,6 @@ Download::~Download() {
assert(!is_active() && "ui::Download::~Download() called on an active object."); assert(!is_active() && "ui::Download::~Download() called on an active object.");
std::for_each(m_uiArray, m_uiArray + DISPLAY_MAX_SIZE, [](ElementBase* eb) { delete eb; }); std::for_each(m_uiArray, m_uiArray + DISPLAY_MAX_SIZE, [](ElementBase* eb) { delete eb; });
delete m_windowDownloadStatus;
} }
inline ElementBase* inline ElementBase*
@@ -172,7 +168,7 @@ Download::activate(display::Frame* frame, [[maybe_unused]] bool focus) {
m_frame = frame; m_frame = frame;
m_frame->initialize_row(2); m_frame->initialize_row(2);
m_frame->frame(1)->initialize_window(m_windowDownloadStatus); m_frame->frame(1)->initialize_window(m_windowDownloadStatus.get());
m_windowDownloadStatus->set_active(true); m_windowDownloadStatus->set_active(true);
activate_display_menu(DISPLAY_PEER_LIST); activate_display_menu(DISPLAY_PEER_LIST);
+1 -35
View File
@@ -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 <sundell.software@gmail.com>
#ifndef RTORRENT_UI_DOWNLOAD_H #ifndef RTORRENT_UI_DOWNLOAD_H
#define RTORRENT_UI_DOWNLOAD_H #define RTORRENT_UI_DOWNLOAD_H
@@ -113,7 +79,7 @@ private:
bool m_focusDisplay{}; bool m_focusDisplay{};
WDownloadStatus* m_windowDownloadStatus; std::unique_ptr<WDownloadStatus> m_windowDownloadStatus;
}; };
} }
+1 -1
View File
@@ -89,7 +89,7 @@ DownloadList::unfocus_download(core::Download* d) {
if (m_state == DISPLAY_DOWNLOAD && d == static_cast<Download*>(m_uiArray[DISPLAY_DOWNLOAD])->download()) if (m_state == DISPLAY_DOWNLOAD && d == static_cast<Download*>(m_uiArray[DISPLAY_DOWNLOAD])->download())
activate_display(DISPLAY_DOWNLOAD_LIST); activate_display(DISPLAY_DOWNLOAD_LIST);
if (*current_view()->focus() == d && current_view()->focus() < current_view()->end_visible()) if (current_view()->focus() < current_view()->end_visible() && *current_view()->focus() == d)
current_view()->next_focus(); current_view()->next_focus();
} }
+4
View File
@@ -26,6 +26,10 @@ ElementLogComplete::ElementLogComplete(torrent::log_buffer* l) :
}); });
} }
ElementLogComplete::~ElementLogComplete() {
m_log->lock_and_set_update_slot(nullptr);
}
void void
ElementLogComplete::activate(display::Frame* frame, [[maybe_unused]] bool focus) { ElementLogComplete::activate(display::Frame* frame, [[maybe_unused]] bool focus) {
if (is_active()) if (is_active())
+7 -4
View File
@@ -15,12 +15,13 @@ namespace ui {
class ElementLogComplete : public ElementBase { class ElementLogComplete : public ElementBase {
public: public:
typedef display::WindowLogComplete WLogComplete; using WLogComplete = display::WindowLogComplete;
ElementLogComplete(torrent::log_buffer* l); ElementLogComplete(torrent::log_buffer* l);
~ElementLogComplete() override;
void activate(display::Frame* frame, bool focus = true); void activate(display::Frame* frame, bool focus = true) override;
void disable(); void disable() override;
display::Window* window(); display::Window* window();
@@ -31,7 +32,9 @@ private:
torrent::log_buffer* m_log; torrent::log_buffer* m_log;
align_cacheline std::atomic<bool> m_log_updating{}; align_cacheline
std::atomic<bool> m_log_updating{};
}; };
} }
+4 -2
View File
@@ -4,6 +4,7 @@
#include <fstream> #include <fstream>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <string.h> #include <string.h>
#include <torrent/throttle.h> #include <torrent/throttle.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
@@ -354,8 +355,9 @@ Root::reset_input_history_attributes(ui::DownloadList::Input type) {
void void
Root::set_input_history_size(int size) { Root::set_input_history_size(int size) {
if (size < 1) if (size < 1 || size > max_input_history_size)
throw torrent::input_error("Invalid input history size."); throw torrent::input_error("Input history size must be between 1 and " +
std::to_string(max_input_history_size) + ".");
for (auto& [entry, category] : m_input_history) { for (auto& [entry, category] : m_input_history) {
// Reserve the latest input history entries if new size is smaller than original. // Reserve the latest input history entries if new size is smaller than original.
+2
View File
@@ -54,6 +54,8 @@ public:
typedef std::vector<std::string> InputHistoryCategory; typedef std::vector<std::string> InputHistoryCategory;
typedef std::map<int, InputHistoryCategory> InputHistory; typedef std::map<int, InputHistoryCategory> InputHistory;
static constexpr int max_input_history_size = 4096;
Root(); Root();
void init(Control* c); void init(Control* c);
+107
View File
@@ -0,0 +1,107 @@
#include "config.h"
#include "utils/waitpid_queue.h"
#include <sys/wait.h>
#include <torrent/exceptions.h>
namespace utils {
WaitpidQueue::WaitpidQueue() {
m_worker = std::async(std::launch::async, [this]() {
auto wait_time = 50ms;
while (true) {
if (!m_queue.empty()) {
auto start_time = std::chrono::steady_clock::now();
while (std::chrono::steady_clock::now() - start_time < wait_time) {
if (m_should_shutdown.load(std::memory_order_acquire))
return;
std::this_thread::sleep_for(50ms);
if (m_wakeup_worker.load(std::memory_order_acquire))
break;
}
} else {
m_wakeup_worker.wait(false, std::memory_order_acquire);
}
// Adds a small delay to allow new processes to finish if they're quickly spawned and
// terminated.
std::this_thread::sleep_for(50ms);
std::set<pid_t> queue;
{
std::lock_guard<std::mutex> guard(m_mutex);
if (m_should_shutdown)
return;
if (m_queue.empty())
throw torrent::internal_error("WaitpidQueue worker thread woke up but queue is empty.");
queue = m_queue;
m_wakeup_worker.store(false, std::memory_order_release);
}
wait_time = std::min(10 * 1000ms, wait_time * 2);
for (auto pid : queue) {
if (::waitpid(pid, nullptr, WNOHANG) == 0)
continue;
{
std::lock_guard<std::mutex> guard(m_mutex);
if (m_queue.erase(pid) != 1)
throw torrent::internal_error("WaitpidQueue worker thread could not find pid in queue.");
}
wait_time = std::max(50ms, wait_time / 2);
m_remaining.fetch_sub(1, std::memory_order_release);
m_remaining.notify_all();
}
}
});
}
// We don't wait for the worker thread to finish as waitpid isn't needed to be called on shutdown.
WaitpidQueue::~WaitpidQueue() {
{
std::lock_guard<std::mutex> guard(m_mutex);
m_should_shutdown = true;
}
m_wakeup_worker.store(true, std::memory_order_release);
m_wakeup_worker.notify_all();
}
void
WaitpidQueue::close_pid(pid_t pid) {
if (pid < 0)
throw torrent::internal_error("WaitpidQueue::close_pid() called with invalid pid.");
m_remaining.fetch_add(1, std::memory_order_acquire);
{
std::lock_guard<std::mutex> guard(m_mutex);
m_queue.insert(pid);
}
m_wakeup_worker.store(true, std::memory_order_release);
m_wakeup_worker.notify_all();
}
void
WaitpidQueue::wait_for(uint32_t max_remaining) {
while (m_remaining.load(std::memory_order_acquire) > max_remaining)
m_remaining.wait(max_remaining, std::memory_order_acquire);
}
} // namespace torrent::utils
+46
View File
@@ -0,0 +1,46 @@
#ifndef RTORRENT_UTILS_WAITPID_QUEUE_H
#define RTORRENT_UTILS_WAITPID_QUEUE_H
#include <future>
#include <set>
#include <torrent/system/common.h>
namespace utils {
class WaitpidQueue {
public:
WaitpidQueue();
~WaitpidQueue();
uint32_t size() const;
void close_pid(int pid);
void wait_for(uint32_t max_remaining);
// TODO: Add a signal handler to tell the worker thread to wake up. Use a counter to batch wakeups.
private:
WaitpidQueue(const WaitpidQueue&) = delete;
WaitpidQueue& operator=(const WaitpidQueue&) = delete;
std::future<void> m_worker;
align_cacheline
std::mutex m_mutex;
std::set<pid_t> m_queue;
align_cacheline
std::atomic<bool> m_wakeup_worker{};
std::atomic<bool> m_should_shutdown{};
std::atomic<uint32_t> m_remaining{};
};
inline uint32_t WaitpidQueue::size() const { return m_remaining.load(std::memory_order_acquire); }
} // namespace utils
#endif
+7 -5
View File
@@ -9,7 +9,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <torrent/utils/scheduler.h> #include <torrent/system/scheduler.h>
namespace utils { namespace utils {
@@ -49,10 +49,12 @@ private:
void update_status(Entry* entry); void update_status(Entry* entry);
void schedule(); void schedule();
std::map<std::string, Entry> m_entries; std::map<std::string, Entry> m_entries;
std::vector<Entry*> m_entry_queue; std::vector<Entry*> m_entry_queue;
torrent::utils::SchedulerEntry m_task_process;
bool m_active{true}; bool m_active{true};
torrent::system::SchedulerEntry m_task_process;
}; };
} }
+6
View File
@@ -49,6 +49,12 @@ 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_local.h \
src/test_command_path.cc \
src/test_command_path.h \
src/test_command_string.cc \
src/test_command_string.h \
src/test_watch_ready_queue.cc \ src/test_watch_ready_queue.cc \
src/test_watch_ready_queue.h src/test_watch_ready_queue.h
+22 -8
View File
@@ -2,9 +2,10 @@
#define LIBTORRENT_HELPERS_MOCK_COMPARE_H #define LIBTORRENT_HELPERS_MOCK_COMPARE_H
#include <algorithm> #include <algorithm>
#include <map>
#include <type_traits> #include <type_traits>
#include <torrent/event.h>
#include <torrent/net/socket_address.h> #include <torrent/net/socket_address.h>
#include <torrent/system/event.h>
// Compare arguments to mock functions with what is expected. The lhs // Compare arguments to mock functions with what is expected. The lhs
// are the expected arguments, rhs are the ones called with. // are the expected arguments, rhs are the ones called with.
@@ -13,13 +14,13 @@ template <typename Arg>
inline bool mock_compare_arg(Arg lhs, Arg rhs) { return lhs == rhs; } inline bool mock_compare_arg(Arg lhs, Arg rhs) { return lhs == rhs; }
template <int I, typename A, typename... Args> template <int I, typename A, typename... Args>
typename std::enable_if<I == 1, int>::type std::enable_if_t<I == 1, int>
mock_compare_tuple(const std::tuple<A, Args...>& lhs, const std::tuple<Args...>& rhs) { mock_compare_tuple(const std::tuple<A, Args...>& lhs, const std::tuple<Args...>& rhs) {
return mock_compare_arg(std::get<I>(lhs), std::get<I - 1>(rhs)) ? 0 : 1; return mock_compare_arg(std::get<I>(lhs), std::get<I - 1>(rhs)) ? 0 : 1;
} }
template <int I, typename A, typename... Args> template <int I, typename A, typename... Args>
typename std::enable_if<1 < I, int>::type std::enable_if_t<1 < I, int>
mock_compare_tuple(const std::tuple<A, Args...>& lhs, const std::tuple<Args...>& rhs) { mock_compare_tuple(const std::tuple<A, Args...>& lhs, const std::tuple<Args...>& rhs) {
auto res = mock_compare_tuple<I - 1>(lhs, rhs); auto res = mock_compare_tuple<I - 1>(lhs, rhs);
@@ -70,24 +71,37 @@ void mock_compare_add(T* v) {
// Specialize: // Specialize:
// //
constexpr int mock_compare_gt_two_int = -0xFD30;
template <>
inline bool mock_compare_arg<int>(int lhs, int rhs) {
if (lhs == mock_compare_gt_two_int)
return rhs > 2;
if (rhs == mock_compare_gt_two_int)
return lhs > 2;
return lhs == rhs;
}
template <> template <>
inline bool mock_compare_arg<sockaddr*>(sockaddr* lhs, sockaddr* rhs) { inline bool mock_compare_arg<sockaddr*>(sockaddr* lhs, sockaddr* rhs) {
return lhs != nullptr && rhs != nullptr && torrent::sa_equal(lhs, rhs); return lhs != nullptr && rhs != nullptr && torrent::sa_equal(lhs, rhs);
} }
template <> template <>
inline bool mock_compare_arg<const sockaddr*>(const sockaddr* lhs, const sockaddr* rhs) { inline bool mock_compare_arg<const sockaddr*>(const sockaddr* lhs, const sockaddr* rhs) {
return lhs != nullptr && rhs != nullptr && torrent::sa_equal(lhs, rhs); return lhs != nullptr && rhs != nullptr && torrent::sa_equal(lhs, rhs);
} }
template <> template <>
inline bool mock_compare_arg<torrent::Event*>(torrent::Event* lhs, torrent::Event* rhs) { inline bool mock_compare_arg<torrent::system::Event*>(torrent::system::Event* lhs, torrent::system::Event* rhs) {
if (mock_compare_map<torrent::Event>::is_key(lhs)) { if (mock_compare_map<torrent::system::Event>::is_key(lhs)) {
if (!mock_compare_map<torrent::Event>::has_value(rhs)) { if (!mock_compare_map<torrent::system::Event>::has_value(rhs)) {
mock_compare_map<torrent::Event>::values[lhs] = rhs; mock_compare_map<torrent::system::Event>::values[lhs] = rhs;
return true; return true;
} }
return mock_compare_map<torrent::Event>::has_key(lhs) && mock_compare_map<torrent::Event>::get(lhs) == rhs; return mock_compare_map<torrent::system::Event>::has_key(lhs) && mock_compare_map<torrent::system::Event>::get(lhs) == rhs;
} }
return lhs == rhs; return lhs == rhs;
+5 -66
View File
@@ -6,9 +6,9 @@
#include <iostream> #include <iostream>
#include <unistd.h> #include <unistd.h>
#include "torrent/event.h"
#include "torrent/net/socket_address.h" #include "torrent/net/socket_address.h"
#include "torrent/net/fd.h" #include "torrent/net/fd.h"
#include "torrent/system/event.h"
#include "torrent/utils/log.h" #include "torrent/utils/log.h"
#include "torrent/utils/random.h" #include "torrent/utils/random.h"
@@ -32,7 +32,7 @@ mock_clear(bool ignore_assert) {
MOCK_CLEANUP_MAP(torrent::random_uniform_uint16); MOCK_CLEANUP_MAP(torrent::random_uniform_uint16);
MOCK_CLEANUP_MAP(torrent::random_uniform_uint32); MOCK_CLEANUP_MAP(torrent::random_uniform_uint32);
mock_compare_map<torrent::Event>::values.clear(); mock_compare_map<torrent::system::Event>::values.clear();
} }
} // namespace } // namespace
@@ -55,7 +55,9 @@ mock_redirect_defaults([[maybe_unused]] mock_redirect_flags flags) {
mock_redirect(torrent::fd__close, std::function<int(int fildes)>([](int fildes) { return ::close(fildes); })); mock_redirect(torrent::fd__close, std::function<int(int fildes)>([](int fildes) { return ::close(fildes); }));
mock_redirect(torrent::fd__fcntl_int, std::function<int(int fildes, int cmd, int arg)>([](int fildes, int cmd, int arg) { return ::fcntl(fildes, cmd, arg); })); mock_redirect(torrent::fd__fcntl_int, std::function<int(int fildes, int cmd, int arg)>([](int fildes, int cmd, int arg) { return ::fcntl(fildes, cmd, arg); }));
mock_redirect(torrent::fd__setsockopt_int, std::function<int(int socket, int level, int option_name, int option_value)>([](int socket, int level, int option_name, int option_value) { return ::setsockopt(socket, level, option_name, &option_value, sizeof(int)); })); mock_redirect(torrent::fd__setsockopt_int, std::function<int(int socket, int level, int option_name, int option_value)>([](int socket, int level, int option_name, int option_value) {
return ::setsockopt(socket, level, option_name, &option_value, sizeof(int));
}));
mock_redirect(torrent::fd__socket, std::function<int(int domain, int type, int protocol)>([](int domain, int type, int protocol) { return ::socket(domain, type, protocol); })); mock_redirect(torrent::fd__socket, std::function<int(int domain, int type, int protocol)>([](int domain, int type, int protocol) { return ::socket(domain, type, protocol); }));
} }
@@ -112,69 +114,6 @@ int fd__socket(int domain, int type, int protocol) {
return mock_call<int>(__func__, &torrent::fd__socket, domain, type, protocol); return mock_call<int>(__func__, &torrent::fd__socket, domain, type, protocol);
} }
//
// Mock functions for 'torrent/common.h':
//
namespace this_thread {
void event_open(Event* event) {
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
return mock_call<void>(__func__, &torrent::this_thread::event_open, event);
}
void event_open_and_count(Event* event) {
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
return mock_call<void>(__func__, &torrent::this_thread::event_open_and_count, event);
}
void event_close_and_count(Event* event) {
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
return mock_call<void>(__func__, &torrent::this_thread::event_close_and_count, event);
}
void event_closed_and_count(Event* event) {
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
return mock_call<void>(__func__, &torrent::this_thread::event_closed_and_count, event);
}
void event_insert_read(Event* event) {
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
return mock_call<void>(__func__, &torrent::this_thread::event_insert_read, event);
}
void event_insert_write(Event* event) {
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
return mock_call<void>(__func__, &torrent::this_thread::event_insert_write, event);
}
void event_insert_error(Event* event) {
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
return mock_call<void>(__func__, &torrent::this_thread::event_insert_error, event);
}
void event_remove_read(Event* event) {
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
return mock_call<void>(__func__, &torrent::this_thread::event_remove_read, event);
}
void event_remove_write(Event* event) {
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
return mock_call<void>(__func__, &torrent::this_thread::event_remove_write, event);
}
void event_remove_error(Event* event) {
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
return mock_call<void>(__func__, &torrent::this_thread::event_remove_error, event);
}
void event_remove_and_close(Event* event) {
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
return mock_call<void>(__func__, &torrent::this_thread::event_remove_and_close, event);
}
}
// //
// Mock functions for 'torrent/utils/random.h': // Mock functions for 'torrent/utils/random.h':
// //
-1
View File
@@ -8,7 +8,6 @@
#include "torrent/exceptions.h" #include "torrent/exceptions.h"
#include "torrent/net/resolver.h" #include "torrent/net/resolver.h"
#include "torrent/utils/log.h" #include "torrent/utils/log.h"
#include "torrent/utils/scheduler.h"
std::unique_ptr<TestMainThread> std::unique_ptr<TestMainThread>
TestMainThread::create() { TestMainThread::create() {
-17
View File
@@ -169,20 +169,3 @@ TestParseOptions::test_flag_libtorrent() {
FLAG_LT_LOG_ASSERT_ERROR("resume_data|rpc_dump"); FLAG_LT_LOG_ASSERT_ERROR("resume_data|rpc_dump");
} }
#define FLAGS_LT_ENCRYPTION_ASSERT(flags, result) \
CPPUNIT_ASSERT(rpc::parse_option_flags(flags, std::bind(&torrent::option_find_string_str, torrent::OPTION_ENCRYPTION, std::placeholders::_1)) == (result))
#define FLAGS_LT_ENCRYPTION_ASSERT_ERROR(flags) \
ASSERT_CATCH_INPUT_ERROR(rpc::parse_option_flags(flags, std::bind(&torrent::option_find_string_str, torrent::OPTION_ENCRYPTION, std::placeholders::_1)))
void
TestParseOptions::test_flags_libtorrent() {
FLAGS_LT_ENCRYPTION_ASSERT("", torrent::runtime::NetworkConfig::encryption_none);
FLAGS_LT_ENCRYPTION_ASSERT("none", torrent::runtime::NetworkConfig::encryption_none);
FLAGS_LT_ENCRYPTION_ASSERT("require_rc4", torrent::runtime::NetworkConfig::encryption_require_RC4);
FLAGS_LT_ENCRYPTION_ASSERT("require_RC4", torrent::runtime::NetworkConfig::encryption_require_RC4);
FLAGS_LT_ENCRYPTION_ASSERT("require_RC4 | enable_retry", torrent::runtime::NetworkConfig::encryption_require_RC4 | torrent::runtime::NetworkConfig::encryption_enable_retry);
FLAGS_LT_ENCRYPTION_ASSERT_ERROR("require_");
}
-2
View File
@@ -14,7 +14,6 @@ class TestParseOptions : public test_fixture {
CPPUNIT_TEST(test_flags_print_flags); CPPUNIT_TEST(test_flags_print_flags);
CPPUNIT_TEST(test_flag_libtorrent); CPPUNIT_TEST(test_flag_libtorrent);
CPPUNIT_TEST(test_flags_libtorrent);
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
@@ -30,5 +29,4 @@ public:
void test_flags_print_flags(); void test_flags_print_flags();
void test_flag_libtorrent(); void test_flag_libtorrent();
void test_flags_libtorrent();
}; };
+54
View File
@@ -0,0 +1,54 @@
#include "config.h"
#include "test/src/test_command_local.h"
#include <torrent/torrent.h>
#include <torrent/runtime/socket_manager.h>
#include <torrent/utils/option_strings.h>
#include "control.h"
#include "globals.h"
#include "rpc/parse_commands.h"
CPPUNIT_TEST_SUITE_REGISTRATION(TestCommandLocal);
void initialize_command_local();
void
TestCommandLocal::setUp() {
torrent::initialize_main_thread();
torrent::initialize();
if (control == nullptr)
control = new Control;
if (!rpc::commands.has("system.sockets.size"))
initialize_command_local();
}
void
TestCommandLocal::tearDown() {
torrent::cleanup();
}
void
TestCommandLocal::test_socket_category_commands() {
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++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);
for (const auto suffix : {".size", ".max_size", ".min_alloc", ".max_alloc"})
if (rpc::commands.has(name + suffix))
CPPUNIT_ASSERT_NO_THROW(rpc::commands.call(name + suffix));
const bool has_allocation = category != torrent::runtime::category_generic;
CPPUNIT_ASSERT(rpc::commands.has(name + ".size"));
CPPUNIT_ASSERT(rpc::commands.has(name + ".max_size"));
CPPUNIT_ASSERT(rpc::commands.has(name + ".min_alloc"));
CPPUNIT_ASSERT_EQUAL(has_allocation, rpc::commands.has(name + ".max_alloc"));
CPPUNIT_ASSERT_EQUAL(has_allocation, rpc::commands.has(name + ".min_alloc.set"));
CPPUNIT_ASSERT_EQUAL(has_allocation, rpc::commands.has(name + ".max_alloc.set"));
}
}
+15
View File
@@ -0,0 +1,15 @@
#include "test/helpers/test_fixture.h"
class TestCommandLocal : public test_fixture {
CPPUNIT_TEST_SUITE(TestCommandLocal);
CPPUNIT_TEST(test_socket_category_commands);
CPPUNIT_TEST_SUITE_END();
public:
void setUp();
void tearDown();
void test_socket_category_commands();
};
+116
View File
@@ -0,0 +1,116 @@
#include "config.h"
#include "test/src/test_command_path.h"
#include <cstdlib>
#include <sys/stat.h>
#include <unistd.h>
#include <torrent/exceptions.h>
#include <torrent/torrent.h>
#include "control.h"
#include "globals.h"
#include "rpc/parse_commands.h"
CPPUNIT_TEST_SUITE_REGISTRATION(TestCommandPath);
void initialize_command_local();
void
TestCommandPath::setUp() {
char temp_dir[] = "/tmp/rtorrent_test_path_XXXXXX";
CPPUNIT_ASSERT(mkdtemp(temp_dir) != nullptr);
// The temporary directory itself may sit behind a symlink, as /tmp does on
// macOS, so resolve it up front to keep the expected values exact.
m_temp_dir = resolve_path(temp_dir);
CPPUNIT_ASSERT(!m_temp_dir.empty());
CPPUNIT_ASSERT_EQUAL(0, mkdir((m_temp_dir + "/data").c_str(), 0755));
CPPUNIT_ASSERT_EQUAL(0, symlink((m_temp_dir + "/data").c_str(), (m_temp_dir + "/link").c_str()));
}
void
TestCommandPath::tearDown() {
unlink((m_temp_dir + "/link").c_str());
rmdir((m_temp_dir + "/data").c_str());
rmdir(m_temp_dir.c_str());
}
void
TestCommandPath::test_resolves_symlink() {
CPPUNIT_ASSERT_EQUAL(m_temp_dir + "/data", resolve_path(m_temp_dir + "/link"));
// A path that lies below a symlinked directory is resolved as well.
CPPUNIT_ASSERT_EQUAL(0, mkdir((m_temp_dir + "/data/below").c_str(), 0755));
CPPUNIT_ASSERT_EQUAL(m_temp_dir + "/data/below", resolve_path(m_temp_dir + "/link/below"));
rmdir((m_temp_dir + "/data/below").c_str());
}
void
TestCommandPath::test_removes_relative_components() {
CPPUNIT_ASSERT_EQUAL(m_temp_dir, resolve_path(m_temp_dir + "/data/.."));
CPPUNIT_ASSERT_EQUAL(m_temp_dir + "/data", resolve_path(m_temp_dir + "/./data"));
CPPUNIT_ASSERT_EQUAL(m_temp_dir + "/data", resolve_path(m_temp_dir + "/data/"));
// Trailing slashes and duplicated separators collapse.
CPPUNIT_ASSERT_EQUAL(m_temp_dir + "/data", resolve_path(m_temp_dir + "//data//"));
}
void
TestCommandPath::test_expands_tilde() {
const char* home = std::getenv("HOME");
if (home == nullptr || *home == '\0')
return;
CPPUNIT_ASSERT_EQUAL(resolve_path(home), resolve_path("~"));
CPPUNIT_ASSERT_THROW(resolve_path("~root/somewhere"), torrent::input_error);
}
void
TestCommandPath::test_missing_path_throws() {
CPPUNIT_ASSERT_THROW(resolve_path_or_throw(""), torrent::input_error);
CPPUNIT_ASSERT_THROW(resolve_path_or_throw(m_temp_dir + "/does_not_exist"), torrent::input_error);
CPPUNIT_ASSERT_EQUAL(m_temp_dir + "/data", resolve_path_or_throw(m_temp_dir + "/link"));
}
void
TestCommandPath::test_missing_path_is_empty() {
CPPUNIT_ASSERT_EQUAL(std::string(), resolve_path(""));
CPPUNIT_ASSERT_EQUAL(std::string(), resolve_path(m_temp_dir + "/does_not_exist"));
CPPUNIT_ASSERT_EQUAL(std::string(), resolve_path(m_temp_dir + "/does_not_exist/below"));
// A dangling symlink does not name an existing path either.
CPPUNIT_ASSERT_EQUAL(0, symlink((m_temp_dir + "/gone").c_str(), (m_temp_dir + "/dangling").c_str()));
CPPUNIT_ASSERT_EQUAL(std::string(), resolve_path(m_temp_dir + "/dangling"));
unlink((m_temp_dir + "/dangling").c_str());
}
void
TestCommandPath::test_commands() {
torrent::initialize_main_thread();
torrent::initialize();
if (control == nullptr)
control = new Control;
if (!rpc::commands.has("directory.default.realpath.or_empty"))
initialize_command_local();
rpc::commands.call_command("directory.default.set", m_temp_dir + "/link");
CPPUNIT_ASSERT_EQUAL(m_temp_dir + "/link", rpc::commands.call_command("directory.default", torrent::Object()).as_string());
CPPUNIT_ASSERT_EQUAL(m_temp_dir + "/data", rpc::commands.call_command("directory.default.realpath.or_empty", torrent::Object()).as_string());
CPPUNIT_ASSERT_EQUAL(m_temp_dir + "/data", rpc::commands.call_command("directory.default.realpath.or_throw", torrent::Object()).as_string());
// A directory that has not been created yet resolves to nothing, and the
// or_throw variant reports it instead.
rpc::commands.call_command("directory.default.set", m_temp_dir + "/missing");
CPPUNIT_ASSERT_EQUAL(std::string(), rpc::commands.call_command("directory.default.realpath.or_empty", torrent::Object()).as_string());
CPPUNIT_ASSERT_THROW(rpc::commands.call_command("directory.default.realpath.or_throw", torrent::Object()), torrent::input_error);
torrent::cleanup();
}
+30
View File
@@ -0,0 +1,30 @@
#include "test/helpers/test_fixture.h"
#include <string>
class TestCommandPath : public test_fixture {
CPPUNIT_TEST_SUITE(TestCommandPath);
CPPUNIT_TEST(test_resolves_symlink);
CPPUNIT_TEST(test_removes_relative_components);
CPPUNIT_TEST(test_expands_tilde);
CPPUNIT_TEST(test_missing_path_is_empty);
CPPUNIT_TEST(test_missing_path_throws);
CPPUNIT_TEST(test_commands);
CPPUNIT_TEST_SUITE_END();
public:
void setUp();
void tearDown();
void test_resolves_symlink();
void test_removes_relative_components();
void test_expands_tilde();
void test_missing_path_is_empty();
void test_missing_path_throws();
void test_commands();
private:
std::string m_temp_dir;
};
+261
View File
@@ -0,0 +1,261 @@
#include "config.h"
#include "test/src/test_command_string.h"
#include "rpc/parse_commands.h"
CPPUNIT_TEST_SUITE_REGISTRATION(TestCommandString);
void initialize_command_string();
namespace {
torrent::Object
args(std::initializer_list<torrent::Object> objects) {
auto result = torrent::Object::create_list();
for (const auto& object : objects)
result.as_list().push_back(object);
return result;
}
std::string
call_string(const char* key, std::initializer_list<torrent::Object> objects) {
return rpc::commands.call_command(key, args(objects)).as_string();
}
int64_t
call_value(const char* key, std::initializer_list<torrent::Object> objects) {
return rpc::commands.call_command(key, args(objects)).as_value();
}
torrent::Object::list_type
call_list(const char* key, std::initializer_list<torrent::Object> objects) {
return rpc::commands.call_command(key, args(objects)).as_list();
}
// Runs a command the way a line in the configuration file would.
torrent::Object
parse(const char* command) {
return rpc::parse_command_single(rpc::make_target(), command);
}
}
void
TestCommandString::setUp() {
if (!rpc::commands.has("string.length"))
initialize_command_string();
}
void
TestCommandString::tearDown() {
}
void
TestCommandString::test_length() {
CPPUNIT_ASSERT_EQUAL(int64_t(0), call_value("string.length", {""}));
CPPUNIT_ASSERT_EQUAL(int64_t(3), call_value("string.length", {"abc"}));
// The length is counted in utf-8 characters, not bytes.
CPPUNIT_ASSERT_EQUAL(int64_t(5), call_value("string.length", {"héllo"}));
CPPUNIT_ASSERT_EQUAL(int64_t(3), call_value("string.length", {"日本語"}));
// Values are converted to their string representation.
CPPUNIT_ASSERT_EQUAL(int64_t(4), call_value("string.length", {int64_t(1234)}));
}
void
TestCommandString::test_equals() {
CPPUNIT_ASSERT_EQUAL(int64_t(1), call_value("string.equals", {"abc", "abc"}));
CPPUNIT_ASSERT_EQUAL(int64_t(0), call_value("string.equals", {"abc", "abd"}));
CPPUNIT_ASSERT_EQUAL(int64_t(0), call_value("string.equals", {"abc", "ab"}));
CPPUNIT_ASSERT_EQUAL(int64_t(1), call_value("string.equals", {"abc", "x", "abc"}));
CPPUNIT_ASSERT_EQUAL(int64_t(0), call_value("string.equals", {"abc", "x", "y"}));
CPPUNIT_ASSERT_EQUAL(int64_t(1), call_value("string.equals", {int64_t(42), "42"}));
}
void
TestCommandString::test_starts_with() {
CPPUNIT_ASSERT_EQUAL(int64_t(1), call_value("string.starts_with", {"abcdef", "abc"}));
CPPUNIT_ASSERT_EQUAL(int64_t(1), call_value("string.starts_with", {"abcdef", ""}));
CPPUNIT_ASSERT_EQUAL(int64_t(0), call_value("string.starts_with", {"abcdef", "bcd"}));
CPPUNIT_ASSERT_EQUAL(int64_t(0), call_value("string.starts_with", {"ab", "abc"}));
CPPUNIT_ASSERT_EQUAL(int64_t(1), call_value("string.starts_with", {"abcdef", "x", "ab"}));
}
void
TestCommandString::test_ends_with() {
CPPUNIT_ASSERT_EQUAL(int64_t(1), call_value("string.ends_with", {"abcdef", "def"}));
CPPUNIT_ASSERT_EQUAL(int64_t(1), call_value("string.ends_with", {"abcdef", ""}));
CPPUNIT_ASSERT_EQUAL(int64_t(0), call_value("string.ends_with", {"abcdef", "cde"}));
CPPUNIT_ASSERT_EQUAL(int64_t(0), call_value("string.ends_with", {"ef", "def"}));
CPPUNIT_ASSERT_EQUAL(int64_t(1), call_value("string.ends_with", {"a.torrent", ".rar", ".torrent"}));
}
void
TestCommandString::test_contains() {
CPPUNIT_ASSERT_EQUAL(int64_t(1), call_value("string.contains", {"abcdef", "cde"}));
CPPUNIT_ASSERT_EQUAL(int64_t(0), call_value("string.contains", {"abcdef", "ace"}));
CPPUNIT_ASSERT_EQUAL(int64_t(1), call_value("string.contains", {"abcdef", "x", "bcd"}));
CPPUNIT_ASSERT_EQUAL(int64_t(0), call_value("string.contains", {"retracker.local", "RETRACKER"}));
CPPUNIT_ASSERT_EQUAL(int64_t(1), call_value("string.contains_i", {"retracker.local", "RETRACKER"}));
CPPUNIT_ASSERT_EQUAL(int64_t(1), call_value("string.contains_i", {"RETRACKER.LOCAL", "retracker"}));
CPPUNIT_ASSERT_EQUAL(int64_t(0), call_value("string.contains_i", {"abcdef", "xyz"}));
}
void
TestCommandString::test_substr() {
CPPUNIT_ASSERT_EQUAL(std::string("abcdef"), call_string("string.substr", {"abcdef"}));
CPPUNIT_ASSERT_EQUAL(std::string("cdef"), call_string("string.substr", {"abcdef", int64_t(2)}));
CPPUNIT_ASSERT_EQUAL(std::string("cde"), call_string("string.substr", {"abcdef", int64_t(2), int64_t(3)}));
CPPUNIT_ASSERT_EQUAL(std::string("cdef"), call_string("string.substr", {"abcdef", int64_t(2), int64_t(100)}));
CPPUNIT_ASSERT_EQUAL(std::string(""), call_string("string.substr", {"abcdef", int64_t(2), int64_t(0)}));
// Negative positions are relative to the end of the string.
CPPUNIT_ASSERT_EQUAL(std::string("ef"), call_string("string.substr", {"abcdef", int64_t(-2)}));
CPPUNIT_ASSERT_EQUAL(std::string("e"), call_string("string.substr", {"abcdef", int64_t(-2), int64_t(1)}));
// Out-of-bounds positions return the default value.
CPPUNIT_ASSERT_EQUAL(std::string(""), call_string("string.substr", {"abcdef", int64_t(10)}));
CPPUNIT_ASSERT_EQUAL(std::string("n/a"), call_string("string.substr", {"abcdef", int64_t(10), int64_t(1), "n/a"}));
CPPUNIT_ASSERT_EQUAL(std::string("n/a"), call_string("string.substr", {"abcdef", int64_t(-10), int64_t(1), "n/a"}));
// Positions and counts are in utf-8 characters, not bytes.
CPPUNIT_ASSERT_EQUAL(std::string(""), call_string("string.substr", {"日本語", int64_t(1), int64_t(1)}));
CPPUNIT_ASSERT_EQUAL(std::string("本語"), call_string("string.substr", {"日本語", int64_t(-2)}));
}
void
TestCommandString::test_split() {
auto parts = call_list("string.split", {"a,b,c", ","});
CPPUNIT_ASSERT_EQUAL(size_t(3), parts.size());
CPPUNIT_ASSERT_EQUAL(std::string("a"), parts.front().as_string());
CPPUNIT_ASSERT_EQUAL(std::string("c"), parts.back().as_string());
// Empty fields are preserved.
CPPUNIT_ASSERT_EQUAL(size_t(3), call_list("string.split", {"a,,b", ","}).size());
CPPUNIT_ASSERT_EQUAL(size_t(1), call_list("string.split", {"abc", ","}).size());
// A multi-character delimiter is matched as a whole.
CPPUNIT_ASSERT_EQUAL(size_t(2), call_list("string.split", {"a::b", "::"}).size());
// An empty delimiter splits the text into its utf-8 characters.
auto characters = call_list("string.split", {"日本語", ""});
CPPUNIT_ASSERT_EQUAL(size_t(3), characters.size());
CPPUNIT_ASSERT_EQUAL(std::string(""), characters.front().as_string());
}
void
TestCommandString::test_join() {
CPPUNIT_ASSERT_EQUAL(std::string("a, b"), call_string("string.join", {", ", "a", "b"}));
CPPUNIT_ASSERT_EQUAL(std::string("ab"), call_string("string.join", {"", "a", "b"}));
CPPUNIT_ASSERT_EQUAL(std::string(""), call_string("string.join", {", "}));
CPPUNIT_ASSERT_EQUAL(std::string("a"), call_string("string.join", {", ", "a"}));
CPPUNIT_ASSERT_EQUAL(std::string("1-2"), call_string("string.join", {"-", int64_t(1), int64_t(2)}));
// Lists are flattened, so the output of string.split can be joined again.
CPPUNIT_ASSERT_EQUAL(std::string("a-b-c"), call_string("string.join", {"-", args({"a", "b"}), "c"}));
}
void
TestCommandString::test_pad() {
CPPUNIT_ASSERT_EQUAL(std::string(" 7"), call_string("string.lpad", {"7", int64_t(3)}));
CPPUNIT_ASSERT_EQUAL(std::string("7 "), call_string("string.rpad", {"7", int64_t(3)}));
CPPUNIT_ASSERT_EQUAL(std::string("007"), call_string("string.lpad", {"7", int64_t(3), "0"}));
CPPUNIT_ASSERT_EQUAL(std::string("700"), call_string("string.rpad", {"7", int64_t(3), "0"}));
// Text that is already long enough is returned unchanged.
CPPUNIT_ASSERT_EQUAL(std::string("abcd"), call_string("string.lpad", {"abcd", int64_t(2)}));
CPPUNIT_ASSERT_EQUAL(std::string("abcd"), call_string("string.rpad", {"abcd", int64_t(4)}));
// Multi-character padding is repeated, and an empty padding is a no-op.
CPPUNIT_ASSERT_EQUAL(std::string("axyx"), call_string("string.rpad", {"a", int64_t(4), "xy"}));
CPPUNIT_ASSERT_EQUAL(std::string("a"), call_string("string.rpad", {"a", int64_t(4), ""}));
// Padding is counted in utf-8 characters, not bytes.
CPPUNIT_ASSERT_EQUAL(std::string("00日"), call_string("string.lpad", {"", int64_t(3), "0"}));
}
void
TestCommandString::test_strip() {
CPPUNIT_ASSERT_EQUAL(std::string("a b"), call_string("string.strip", {" a b "}));
CPPUNIT_ASSERT_EQUAL(std::string("a"), call_string("string.strip", {"\t\n a \r\n"}));
CPPUNIT_ASSERT_EQUAL(std::string("a "), call_string("string.lstrip", {" a "}));
CPPUNIT_ASSERT_EQUAL(std::string(" a"), call_string("string.rstrip", {" a "}));
CPPUNIT_ASSERT_EQUAL(std::string("a"), call_string("string.strip", {"xxaxx", "x"}));
CPPUNIT_ASSERT_EQUAL(std::string("a"), call_string("string.strip", {"/x/a/x/", "/", "x"}));
CPPUNIT_ASSERT_EQUAL(std::string(""), call_string("string.strip", {"aaa", "a"}));
CPPUNIT_ASSERT_EQUAL(std::string(" a "), call_string("string.strip", {" a ", ""}));
// The strippable argument is a set of utf-8 characters.
CPPUNIT_ASSERT_EQUAL(std::string("a"), call_string("string.strip", {"日a日", ""}));
}
void
TestCommandString::test_map() {
CPPUNIT_ASSERT_EQUAL(std::string("b"), call_string("string.map", {"a", args({"a", "b"})}));
CPPUNIT_ASSERT_EQUAL(std::string("c"), call_string("string.map", {"c", args({"a", "b"})}));
// Only whole-string matches are replaced.
CPPUNIT_ASSERT_EQUAL(std::string("ab"), call_string("string.map", {"ab", args({"a", "b"})}));
// The first matching pair wins.
CPPUNIT_ASSERT_EQUAL(std::string("y"), call_string("string.map", {"x", args({"a", "b"}), args({"x", "y"}), args({"x", "z"})}));
}
void
TestCommandString::test_replace() {
CPPUNIT_ASSERT_EQUAL(std::string("a+b+c"), call_string("string.replace", {"a-b-c", args({"-", "+"})}));
CPPUNIT_ASSERT_EQUAL(std::string("abc"), call_string("string.replace", {"a-b-c", args({"-", ""})}));
CPPUNIT_ASSERT_EQUAL(std::string("a-b-c"), call_string("string.replace", {"a-b-c", args({"x", "y"})}));
// Pairs are applied in order, left to right.
CPPUNIT_ASSERT_EQUAL(std::string("xby"), call_string("string.replace", {"abc", args({"a", "x"}), args({"c", "y"})}));
// A replacement that contains the replaced text terminates.
CPPUNIT_ASSERT_EQUAL(std::string("aaaa"), call_string("string.replace", {"aa", args({"a", "aa"})}));
}
void
TestCommandString::test_invalid_arguments() {
CPPUNIT_ASSERT_THROW(rpc::commands.call_command("string.length", torrent::Object()), torrent::input_error);
CPPUNIT_ASSERT_THROW(call_string("string.length", {"a", "b"}), torrent::input_error);
CPPUNIT_ASSERT_THROW(call_value("string.equals", {"a"}), torrent::input_error);
CPPUNIT_ASSERT_THROW(call_value("string.contains", {"a"}), torrent::input_error);
CPPUNIT_ASSERT_THROW(call_list("string.split", {"a"}), torrent::input_error);
CPPUNIT_ASSERT_THROW(call_string("string.lpad", {"a"}), torrent::input_error);
// The character count of string.substr cannot be negative.
CPPUNIT_ASSERT_THROW(call_string("string.substr", {"abc", int64_t(0), int64_t(-1)}), torrent::input_error);
// Both string.map and string.replace require {old, new} pairs.
CPPUNIT_ASSERT_THROW(call_string("string.map", {"a", "b"}), torrent::input_error);
CPPUNIT_ASSERT_THROW(call_string("string.replace", {"a", args({"a", "b", "c"})}), torrent::input_error);
CPPUNIT_ASSERT_THROW(call_string("string.replace", {"a", args({"", "b"})}), torrent::input_error);
}
void
TestCommandString::test_config_syntax() {
CPPUNIT_ASSERT_EQUAL(int64_t(3), parse("string.length=abc").as_value());
CPPUNIT_ASSERT_EQUAL(int64_t(1), parse("string.contains=retracker.local,retracker").as_value());
CPPUNIT_ASSERT_EQUAL(int64_t(1), parse("string.starts_with=udp://tracker.example.com,http://,udp://").as_value());
CPPUNIT_ASSERT_EQUAL(std::string("cde"), parse("string.substr=abcdef,2,3").as_string());
CPPUNIT_ASSERT_EQUAL(std::string("padded"), parse("string.strip=\" padded \"").as_string());
// The {old, new} pairs are written as a block in the configuration file.
CPPUNIT_ASSERT_EQUAL(std::string("a+b+c"), parse("string.replace=a-b-c,{-,+}").as_string());
CPPUNIT_ASSERT_EQUAL(std::string("y"), parse("string.map=x,{a,b},{x,y}").as_string());
// Commands nest, so a split can be joined back together.
CPPUNIT_ASSERT_EQUAL(std::string("a-b-c"), parse("string.join=-,(string.split,a.b.c,.)").as_string());
}
+41
View File
@@ -0,0 +1,41 @@
#include "test/helpers/test_fixture.h"
class TestCommandString : public test_fixture {
CPPUNIT_TEST_SUITE(TestCommandString);
CPPUNIT_TEST(test_length);
CPPUNIT_TEST(test_equals);
CPPUNIT_TEST(test_starts_with);
CPPUNIT_TEST(test_ends_with);
CPPUNIT_TEST(test_contains);
CPPUNIT_TEST(test_substr);
CPPUNIT_TEST(test_split);
CPPUNIT_TEST(test_join);
CPPUNIT_TEST(test_pad);
CPPUNIT_TEST(test_strip);
CPPUNIT_TEST(test_map);
CPPUNIT_TEST(test_replace);
CPPUNIT_TEST(test_invalid_arguments);
CPPUNIT_TEST(test_config_syntax);
CPPUNIT_TEST_SUITE_END();
public:
void setUp();
void tearDown();
void test_length();
void test_equals();
void test_starts_with();
void test_ends_with();
void test_contains();
void test_substr();
void test_split();
void test_join();
void test_pad();
void test_strip();
void test_map();
void test_replace();
void test_invalid_arguments();
void test_config_syntax();
};