Compare commits

...

100 Commits

Author SHA1 Message Date
rakshasa 9507ac17b3 Stuff. 2026-08-04 11:08:52 +02:00
rakshasa 49d45c1088 Stuff. 2026-08-04 11:04:20 +02:00
rakshasa 5ab973504a Stuff. 2026-08-04 10:47:23 +02:00
rakshasa ebe28e07b1 Stuff. 2026-08-04 10:40:43 +02:00
rakshasa 06a391f9d8 Stuff. 2026-08-03 09:11:23 +02: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
Xirvik Support 49625ab5e5 rpc: close SCGI task on EPIPE to stop event_write() busy-loop
When an SCGI client closes the connection before rtorrent finishes sending
the response, send() in SCgiTask::event_write() returns -1 with errno EPIPE.
EPIPE was grouped with EAGAIN/EINTR as a non-fatal retry-later condition, so
the task was not closed and its descriptor stayed registered for EPOLLOUT. A
broken socket is reported writable immediately, so epoll_wait() returns it on
every iteration and the SCGI thread spins at 100% CPU on one core
indefinitely. The dead connection fd is also leaked (stays ESTAB).

EPIPE is terminal here, not retryable: the peer is gone and the response can
never be delivered. Close the task on EPIPE, matching event_read(), which
already closes on any recv() error other than EAGAIN/EINTR.

Reproduction: open the SCGI socket, send a complete RPC request, then
shutdown(SHUT_RDWR)/close before reading the reply. Stock: the rtorrent-scgi
thread goes to 100% CPU and the connection leaks. With this change: CPU stays
at 0% and the descriptor is closed.
2026-06-04 10:41:19 +02:00
Jari Sundell cc15e9308a Fixed xmlrpc-c build errors and added better github workflow for unit-tests. 2026-06-04 17:16:47 +09:00
Jari Sundell a5a96236df Moved base64 transform and validation functions. 2026-06-04 06:37:01 +09:00
Jari Sundell f1cfe8ad72 Added hex to Object and string_utf8. 2026-06-04 04:22:16 +09:00
rakshasa d0fc48cf7b Tagged release 0.16.13. 2026-06-03 10:18:25 +02:00
trim21 5725eb3773 fix: move --without-ncurses logic to scripts/checks.m4 per review
- Add TORRENT_WITHOUT_NCURSES macro in scripts/checks.m4
- Clear CURSES_LIBS/CFLAGS in the macro instead of if/else in configure.ac
- Restore simple LIBS/CFLAGS lines in configure.ac
- Add missing set_escdelay stub
2026-06-01 20:33:39 +02:00
trim21 ca706c21c0 feat: add --without-ncurses option for daemon-only builds
Add a dummy curses stub header that provides all ncurses types, macros,
and no-op function stubs. When configured with --without-ncurses, the
build uses this stub instead of linking to the real ncurses library.

This allows rtorrent to be built for daemon-only usage (e.g. with
ruTorrent or Flood) without requiring ncurses to be installed.

Closes #1613
2026-06-01 20:33:39 +02:00
Jari Sundell 14edd68653 Removed SignalBitfield and replaced it with callbacks. 2026-06-01 22:40:22 +09:00
Jari Sundell 50a609ade9 Fixed cacheline size check. 2026-06-01 20:11:02 +09:00
Jari Sundell 2cf8f2351b Fixed github workflow. 2026-05-31 00:37:22 +09:00
Jari Sundell add305f90c Added support base64 support for non-UTF8 strings. 2026-05-30 00:25:16 +09:00
Jari Sundell 880510073a Updated LUA autoconf script. 2026-05-27 21:37:09 +09:00
rakshasa 0989b2218d Various cleanup and fixes to SocketManager categories. 2026-05-27 12:38:52 +02:00
Trim21 3d7e6cb077 Added per-category allocation limits to SocketManager by @trim21 2026-05-27 18:16:02 +09:00
Jari Sundell 64881317c6 Fix RPC/SCGI security and crash bugs by @sirus20x6 2026-05-26 17:27:10 +09:00
rakshasa a5ecd89a54 Removed unnessesary configure check from PR. 2026-05-25 18:21:31 +02:00
tonimelisma 84229a85be Add directory.watch.ready for safe torrent watch folders 2026-05-25 18:21:31 +02:00
Jari Sundell e03ab27cf0 Added intermediate requesting flag for trackers to prevent premature deletion. 2026-05-26 00:43:15 +09:00
Jari Sundell e41b066555 Converted callbacks to new interface. 2026-05-25 20:13:45 +09:00
Jari Sundell 8318133c79 Added a new callback mechanism and use it for trackers. 2026-05-23 23:43:45 +09:00
rakshasa 088bf40bdf Removed obsolete use of trackers.use_udp. 2026-05-18 16:35:20 +02:00
rakshasa 2f7a984992 Tagged release 0.16.12. 2026-05-18 15:25:56 +02:00
Jari Sundell 840a57915e Deprecate trackers.use_udp as it is no longer needed. 2026-05-17 00:59:03 +09:00
Jari Sundell 5e211c3a28 Allow for quick shutdown after sending the first UDP announce packet. 2026-05-15 20:58:17 +09:00
Jari Sundell 797a194abc Cleaned up torrent header files. 2026-05-14 19:24:38 +09:00
Trim21 501853cfbc Delete repro-scgi-content-type.py 2026-05-11 11:42:36 +02:00
trim21 b0ef95592b fix SCGI parse_headers: defer content-type body peek until body received
detect_content_type() peeked at m_buffer[m_body] to infer JSON vs XML
when no CONTENT_TYPE header was provided.  When the TCP header segment
arrives without any body bytes, m_body equals m_position and the peek
reads the null terminator padding byte — not the actual '{' or '[' —
causing JSON requests to be incorrectly classified as XML and fail.

Fix:
 - Remove the body peek from detect_content_type(); defer it to after
   the full body is confirmed present in event_read().
 - Add a m_content_type_set flag to distinguish header-provided type
   from auto-detected type.
2026-05-11 11:42:36 +02:00
Miroslav Marchev 26a7e9f545 Update to version 11.0.0 2026-05-10 09:25:02 +02:00
Miroslav Marchev 0ed10984b3 Update to version 11.0.0 2026-05-10 09:25:02 +02:00
Miroslav Marchev 5f09bb74de Updated to version 3.12.0 2026-05-10 09:25:02 +02:00
Jari Sundell c368b2de1e Fix SCGI event_read: return on partial header read instead of closing connection @trim21 2026-05-10 16:01:45 +09:00
Jari Sundell 71ac256cb5 Moved socket counter to runtime::SocketManager. 2026-05-10 02:11:35 +09:00
rakshasa b05b65bd65 Enable SCGI compression by default and set min size to 1000. 2026-05-09 12:24:24 +02:00
Jari Sundell f05b48e228 Moved NetworkConfig to runtime. 2026-05-05 16:17:24 +09:00
Xirvik 1279bd9f7a Move d.save_full_session to run after user-registered inserted_new handlers
Previously d.save_full_session was inside the 1_prepare handler for
event.download.inserted_new. Since 0.16.6 moved session saving to a
separate thread, save_full_download() snapshots the bencode synchronously
when called and queues the prebuilt streams for async write.

If user-registered handlers (e.g. seedingtime plugin's addtime setter
running d.set_custom=addtime) sort lexicographically AFTER 1_prepare, they
modify the bencode AFTER the snapshot has already been taken. The first
on-disk .rtorrent then lacks those custom fields. The next periodic
resume save catches up, but if rtorrent restarts before that, the data
is lost — manifesting as blank Finished/SeedingTime columns in ruTorrent.

Fix: split the inserted_new key into two — 1_prepare keeps view
visibility setup, ~_save_full runs d.save_full_session last (~ prefix
is ASCII 0x7E, sorts after all alphanumerics, matching the existing
~_delete_tied precedent on event.download.erased). The snapshot then
includes any custom fields written by user handlers.
2026-05-05 08:40:13 +02:00
Xirvik 981184574d Reset SCgiTask m_trusted on connection reuse
SCgiTask objects are pre-allocated in a pool (scgi.cc) and reused across
SCGI connections. SCgiTask::open() did not reset m_trusted, so when a
task that had handled an untrusted connection (m_trusted=false) was
reused for a new connection, m_trusted stayed false unless the new
connection explicitly sent UNTRUSTED_CONNECTION=1.

The header parser only set m_trusted=false on value 1 and was a no-op
on value 0 (the comment said "default is trusted, so do nothing") —
which is wrong for a reused task that is no longer in default state.

This caused intermittent rejection of trusted commands (e.g. ruTorrent
calling execute.capture for UID detection) with "Command X is not allowed
for untrusted connections", producing cascading plugin failures and
"ruTorrent cannot determine the UID of rTorrent user" in the web UI.

Fix:
- SCgiTask::open() resets m_trusted=true to default.
- parse_headers explicitly sets m_trusted=true on UNTRUSTED_CONNECTION=0,
  so the value sent on the wire is authoritative regardless of pool
  reuse semantics.

Verified on gb4 with rtorrent 0.16.11 + this fix: 30/30 trusted calls
succeed, 30/30 untrusted correctly blocked, 30/30 trusted-after-untrusted
batch all succeed (previously 70%+ would fail in the same scenario).
2026-05-04 10:06:48 +02:00
104 changed files with 3520 additions and 2285 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!"
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
permissions: permissions:
pull-requests: write pull-requests: write
# OPTIONAL: auto-closing conversations requires the `contents` permission # OPTIONAL: auto-closing conversations requires the `contents` permission
contents: write contents: read
steps: steps:
- name: Sleep for 30 seconds - name: Sleep for 30 seconds
run: sleep 30s run: sleep 30s
+69 -27
View File
@@ -4,9 +4,8 @@ on: pull_request
jobs: jobs:
unit-tests: ubuntu-base:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Update Packages - name: Update Packages
run: | run: |
@@ -23,43 +22,86 @@ jobs:
- name: Build libtorrent - name: Build libtorrent
run: | run: |
cd libtorrent cd libtorrent
libtoolize autoreconf -fiv
aclocal -I scripts
autoconf -i
autoheader
automake --add-missing
./configure ./configure
make make
sudo make install sudo make install DESTDIR=$GITHUB_WORKSPACE/libtorrent-dist
cd .. - name: Upload Build Artifacts
rm -rf libtorrent uses: actions/upload-artifact@v4
- uses: actions/checkout@v4
with: with:
ref: ${{ github.event.pull_request.head.sha }} name: installed-libtorrent
fetch-depth: 0 path: libtorrent-dist/
- name: Fetch base branch
unit-tests-default:
runs-on: ubuntu-22.04
needs: ubuntu-base
steps:
- name: Update Packages
run: | run: |
git remote add upstream "https://github.com/${{ github.event.pull_request.base.repo.full_name }}" sudo apt-get update
git fetch --no-tags --no-recurse-submodules upstream "${{ github.event.pull_request.base.ref }}" - name: Install Dependencies
run: |
sudo apt-get install -y \
libcppunit-dev \
zlib1g-dev \
libcurl4-openssl-dev
- name: Download Libtorrent Artifacts
uses: actions/download-artifact@v4
with:
name: installed-libtorrent
path: ./libtorrent-dist
- name: Move Artifacts to System Path
run: |
# Elevate permissions with sudo to safely place the files
sudo cp -r ./libtorrent-dist/usr/local/* /usr/local/
rm -rf ./libtorrent-dist
- uses: actions/checkout@v4
- name: Configure Project - name: Configure Project
run: | run: |
libtoolize autoreconf -fiv
aclocal -I scripts
autoconf -i
autoheader
automake --add-missing
./configure ./configure
- name: Build Project
run: |
make make
- name: Run Unit Tests
run: |
ls /usr/local/lib/ ls /usr/local/lib/
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" make check
make check
- name: Archive test/test-suite.log - name: Archive test/test-suite.log
if: success() || failure() if: success() || failure()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: test-suite.log name: test-suite.log
path: test/test-suite.log path: test/test-suite.log
unit-tests-variants:
runs-on: ubuntu-22.04
needs: unit-tests-default
strategy:
matrix:
config_flag: ["--with-xmlrpc-c", "--with-xmlrpc-tinyxml2"]
steps:
- name: Update Packages
run: |
sudo apt-get update
- name: Install Dependencies
run: |
sudo apt-get install -y \
libcppunit-dev \
zlib1g-dev \
libcurl4-openssl-dev \
libxmlrpc-c++8-dev
- name: Download Libtorrent Artifacts
uses: actions/download-artifact@v4
with:
name: installed-libtorrent
path: ./libtorrent-dist
- name: Move Artifacts to System Path
run: |
sudo cp -r ./libtorrent-dist/usr/local/* /usr/local/
rm -rf ./libtorrent-dist
- uses: actions/checkout@v4
- name: Configure Project
run: |
autoreconf -fiv
./configure ${{ matrix.config_flag }}
make
ls /usr/local/lib/
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" make check
+2
View File
@@ -18,6 +18,7 @@
.libs .libs
Makefile Makefile
aclocal.m4 aclocal.m4
actmp.*
ar-lib ar-lib
autom4te.cache autom4te.cache
compile compile
@@ -69,4 +70,5 @@ TAGS
########################### ###########################
src/rtorrent src/rtorrent
test/rtorrent_Test* test/rtorrent_Test*
test/test-suite.log.tmp
test-driver test-driver
-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
+19 -17
View File
@@ -1,6 +1,6 @@
m4_pattern_allow([PKG_CHECK_EXISTS]) m4_pattern_allow([PKG_CHECK_EXISTS])
AC_INIT([rtorrent],[0.16.11],[sundell.software@gmail.com]) AC_INIT([rtorrent],[0.16.19],[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
AX_CXX_COMPILE_STDCXX(20, noext, mandatory)
AC_DEFINE([API_VERSION], [20], [api version]) PKG_PROG_PKG_CONFIG
# Filter out unwanted flags added by autoconf on some systems, e.g. MacOS. AC_DEFINE([API_VERSION], [24], [api version])
TORRENT_REMOVE_UNWANTED(CXX, $CXX, -std=c++11 -std=gnu++11)
AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory])
AC_SYS_LARGEFILE
RAK_CHECK_CFLAGS RAK_CHECK_CFLAGS
RAK_CHECK_CXXFLAGS RAK_CHECK_CXXFLAGS
@@ -26,9 +22,6 @@ RAK_ENABLE_DEBUG
RAK_ENABLE_EXTRA_DEBUG RAK_ENABLE_EXTRA_DEBUG
RAK_ENABLE_WERROR RAK_ENABLE_WERROR
TORRENT_ENABLE_ARCH
TORRENT_WITH_SYSROOT
AC_ARG_ENABLE(execinfo, AC_ARG_ENABLE(execinfo,
AS_HELP_STRING([--disable-execinfo], AS_HELP_STRING([--disable-execinfo],
[disable libexecinfo [[default=enable]]]), [disable libexecinfo [[default=enable]]]),
@@ -41,20 +34,28 @@ AC_ARG_ENABLE(execinfo,
]) ])
AX_PTHREAD([], AC_MSG_ERROR([requires pthread])) AX_PTHREAD([], AC_MSG_ERROR([requires pthread]))
AX_WITH_CURSES
if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then TORRENT_WITHOUT_NCURSES
AC_MSG_ERROR([requires either NcursesW or Ncurses library])
if test "x$with_ncurses" != xno; then
AX_WITH_CURSES
if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
AC_MSG_ERROR([requires either NcursesW or Ncurses library])
fi
fi 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.11]) PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.19])
AC_LANG_PUSH(C++) AC_LANG_PUSH(C++)
TORRENT_WITH_XMLRPC_C TORRENT_WITH_XMLRPC_C
AC_LANG_POP(C++) AC_LANG_POP(C++)
dnl We don't have LuaJIT support, and the script breaks if not disabled.
AM_CONDITIONAL([LUAJIT], [false])
TORRENT_WITH_LUA TORRENT_WITH_LUA
TORRENT_WITH_TINYXML2 TORRENT_WITH_TINYXML2
TORRENT_WITH_SYSTEMD TORRENT_WITH_SYSTEMD
@@ -63,7 +64,7 @@ if test ${with_xmlrpc_c+y} && test ${with_xmlrpc_tinyxml2+y}; then
AC_MSG_ERROR([--with-xmlrpc-c and --with-xmlrpc-tinyxml2 cannot be used together. Please choose only one]) AC_MSG_ERROR([--with-xmlrpc-c and --with-xmlrpc-tinyxml2 cannot be used together. Please choose only one])
fi fi
AC_DEFINE(USER_AGENT, [std::string(PACKAGE "/" VERSION "/") + torrent::version()], Http user agent) 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.
@@ -71,7 +72,8 @@ 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_POPCOUNT() TORRENT_CHECK_CACHELINE
TORRENT_CHECK_POSIX_SPAWN_ADDCLOSEFROM_NP
AC_CONFIG_FILES([ AC_CONFIG_FILES([
Makefile Makefile
+5 -1
View File
@@ -46,8 +46,12 @@
#session.path.set = ./session #session.path.set = ./session
# Watch a directory for new torrents, and stop those that have been # Watch a directory for new torrents, and stop those that have been
# deleted. # deleted. Use directory.watch.ready for network shares or other watch
# directories where files may be copied or written directly into place.
# Do not mix directory.watch.ready and directory.watch.added on the same
# watch directory.
# #
#directory.watch.ready = ./watch, load.start
#schedule2 = watch_directory,5,5,load.start=./watch/*.torrent #schedule2 = watch_directory,5,5,load.start=./watch/*.torrent
# Close torrents when disk-space is low. # Close torrents when disk-space is low.
-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
+124 -16
View File
@@ -10,8 +10,8 @@
# #
# Check for baseline language coverage in the compiler for the specified # Check for baseline language coverage in the compiler for the specified
# version of the C++ standard. If necessary, add switches to CXX and # version of the C++ standard. If necessary, add switches to CXX and
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) # CXXCPP to enable support. VERSION may be '11', '14', '17', '20', or
# or '14' (for the C++14 standard). # '23' for the respective C++ standard version.
# #
# The second argument, if specified, indicates whether you insist on an # The second argument, if specified, indicates whether you insist on an
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
@@ -36,13 +36,15 @@
# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com> # Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
# Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com> # Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
# Copyright (c) 2020 Jason Merrill <jason@redhat.com> # Copyright (c) 2020 Jason Merrill <jason@redhat.com>
# Copyright (c) 2021, 2024 Jörn Heusipp <osmanx@problemloesungsmaschine.de>
# Copyright (c) 2015, 2022, 2023, 2024 Olly Betts
# #
# Copying and distribution of this file, with or without modification, are # Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice # permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any # and this notice are preserved. This file is offered as-is, without any
# warranty. # warranty.
#serial 12 #serial 25
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
dnl (serial version number 13). dnl (serial version number 13).
@@ -51,6 +53,8 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
[$1], [14], [ax_cxx_compile_alternatives="14 1y"], [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
[$1], [17], [ax_cxx_compile_alternatives="17 1z"], [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
[$1], [20], [ax_cxx_compile_alternatives="20"],
[$1], [23], [ax_cxx_compile_alternatives="23"],
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$2], [], [], m4_if([$2], [], [],
[$2], [ext], [], [$2], [ext], [],
@@ -102,9 +106,18 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
dnl HP's aCC needs +std=c++11 according to: dnl HP's aCC needs +std=c++11 according to:
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
dnl Cray's crayCC needs "-h std=c++11" dnl Cray's crayCC needs "-h std=c++11"
dnl MSVC needs -std:c++NN for C++17 and later (default is C++14)
for alternative in ${ax_cxx_compile_alternatives}; do for alternative in ${ax_cxx_compile_alternatives}; do
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}" MSVC; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) if test x"$switch" = xMSVC; then
dnl AS_TR_SH maps both `:` and `=` to `_` so -std:c++17 would collide
dnl with -std=c++17. We suffix the cache variable name with _MSVC to
dnl avoid this.
switch=-std:c++${alternative}
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_${switch}_MSVC])
else
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
fi
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar, $cachevar,
[ac_save_CXX="$CXX" [ac_save_CXX="$CXX"
@@ -148,23 +161,44 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
dnl Test body for checking C++11 support dnl Test body for checking C++11 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11 [_AX_CXX_COMPILE_STDCXX_testbody_new_in_11]
) )
dnl Test body for checking C++14 support dnl Test body for checking C++14 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11 [_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14]
) )
dnl Test body for checking C++17 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11 [_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17]
) )
dnl Test body for checking C++20 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_20],
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
_AX_CXX_COMPILE_STDCXX_testbody_new_in_20]
)
dnl Test body for checking C++23 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_23],
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
_AX_CXX_COMPILE_STDCXX_testbody_new_in_20
_AX_CXX_COMPILE_STDCXX_testbody_new_in_23]
)
dnl Tests for new features in C++11 dnl Tests for new features in C++11
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
@@ -176,7 +210,21 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
#error "This is not a C++ compiler" #error "This is not a C++ compiler"
#elif __cplusplus < 201103L // MSVC always sets __cplusplus to 199711L in older versions; newer versions
// only set it correctly if /Zc:__cplusplus is specified as well as a
// /std:c++NN switch:
//
// https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
//
// The value __cplusplus ought to have is available in _MSVC_LANG since
// Visual Studio 2015 Update 3:
//
// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros
//
// This was also the first MSVC version to support C++14 so we can't use the
// value of either __cplusplus or _MSVC_LANG to quickly rule out MSVC having
// C++11 or C++14 support, but we can check _MSVC_LANG for C++17 and later.
#elif __cplusplus < 201103L && !defined _MSC_VER
#error "This is not a C++11 compiler" #error "This is not a C++11 compiler"
@@ -467,7 +515,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
#error "This is not a C++ compiler" #error "This is not a C++ compiler"
#elif __cplusplus < 201402L #elif __cplusplus < 201402L && !defined _MSC_VER
#error "This is not a C++14 compiler" #error "This is not a C++14 compiler"
@@ -591,7 +639,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
#error "This is not a C++ compiler" #error "This is not a C++ compiler"
#elif __cplusplus < 201703L #elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 201703L
#error "This is not a C++17 compiler" #error "This is not a C++17 compiler"
@@ -957,6 +1005,66 @@ namespace cxx17
} // namespace cxx17 } // namespace cxx17
#endif // __cplusplus < 201703L #endif // (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 201703L
]])
dnl Tests for new features in C++20
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_20], [[
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202002L
#error "This is not a C++20 compiler"
#else
#include <version>
namespace cxx20
{
// As C++20 supports feature test macros in the standard, there is no
// immediate need to actually test for feature availability on the
// Autoconf side.
} // namespace cxx20
#endif // (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202002L
]])
dnl Tests for new features in C++23
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_23], [[
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202302L
#error "This is not a C++23 compiler"
#else
#include <version>
namespace cxx23
{
// As C++23 supports feature test macros in the standard, there is no
// immediate need to actually test for feature availability on the
// Autoconf side.
} // namespace cxx23
#endif // (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202302L
]]) ]])
+83 -34
View File
@@ -19,7 +19,7 @@
# header is checked to match the Lua interpreter version exactly. When # header is checked to match the Lua interpreter version exactly. When
# searching for Lua libraries, the version number is used as a suffix. # searching for Lua libraries, the version number is used as a suffix.
# This is done with the goal of supporting multiple Lua installs (5.1, # This is done with the goal of supporting multiple Lua installs (5.1,
# 5.2, and 5.3 side-by-side). # 5.2, 5.3, and 5.4 side-by-side).
# #
# A note on compatibility with previous versions: This file has been # A note on compatibility with previous versions: This file has been
# mostly rewritten for serial 18. Most developers should be able to use # mostly rewritten for serial 18. Most developers should be able to use
@@ -49,6 +49,14 @@
# interpreter. If LUA is blank, the user's path is searched for an # interpreter. If LUA is blank, the user's path is searched for an
# suitable interpreter. # suitable interpreter.
# #
# Optionally a LUAJIT option may be set ahead of time to look for and
# validate a LuaJIT install instead of PUC Lua. Usage might look like:
#
# AC_ARG_WITH(luajit, [AS_HELP_STRING([--with-luajit],
# [Prefer LuaJIT over PUC Lua, even if the latter is newer. Default: no])
# ])
# AM_CONDITIONAL([LUAJIT], [test "x$with_luajit" != 'xno'])
#
# If MINIMUM-VERSION is supplied, then only Lua interpreters with a # If MINIMUM-VERSION is supplied, then only Lua interpreters with a
# version number greater or equal to MINIMUM-VERSION will be accepted. If # version number greater or equal to MINIMUM-VERSION will be accepted. If
# TOO-BIG-VERSION is also supplied, then only Lua interpreters with a # TOO-BIG-VERSION is also supplied, then only Lua interpreters with a
@@ -82,7 +90,7 @@
# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES. # appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES.
# #
# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is # If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is
# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT- # performed, otherwise ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-
# FOUND is blank, then it will default to printing an error. To prevent # FOUND is blank, then it will default to printing an error. To prevent
# the default behavior, give ':' as an action. # the default behavior, give ':' as an action.
# #
@@ -152,6 +160,7 @@
# #
# LICENSE # LICENSE
# #
# Copyright (c) 2023 Caleb Maclennan <caleb@alerque.com>
# Copyright (c) 2015 Reuben Thomas <rrt@sc3d.org> # Copyright (c) 2015 Reuben Thomas <rrt@sc3d.org>
# Copyright (c) 2014 Tim Perkins <tprk77@gmail.com> # Copyright (c) 2014 Tim Perkins <tprk77@gmail.com>
# #
@@ -181,7 +190,7 @@
# modified version of the Autoconf Macro, you may extend this special # modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well. # exception to the GPL to apply to your modified version as well.
#serial 42 #serial 47
dnl ========================================================================= dnl =========================================================================
dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION], dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION],
@@ -197,13 +206,14 @@ AC_DEFUN([AX_PROG_LUA],
AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1]) AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1])
dnl Find a Lua interpreter. dnl Find a Lua interpreter.
m4_define_default([_AX_LUA_INTERPRETER_LIST], AM_COND_IF([LUAJIT],
[lua lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua50]) [_ax_lua_interpreter_list='luajit luajit-2.1.0-beta3 luajit-2.0.5 luajit-2.0.4 luajit-2.0.3'],
[_ax_lua_interpreter_list='lua lua5.4 lua54 lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua5.0 lua50'])
m4_if([$1], [], m4_if([$1], [],
[ dnl No version check is needed. Find any Lua interpreter. [ dnl No version check is needed. Find any Lua interpreter.
AS_IF([test "x$LUA" = 'x'], AS_IF([test "x$LUA" = 'x'],
[AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])]) [AC_PATH_PROGS([LUA], [$_ax_lua_interpreter_list], [:])])
ax_display_LUA='lua' ax_display_LUA='lua'
AS_IF([test "x$LUA" != 'x:'], AS_IF([test "x$LUA" != 'x:'],
@@ -242,7 +252,7 @@ AC_DEFUN([AX_PROG_LUA],
[_ax_check_text="for a Lua interpreter with version >= $1, < $2"]) [_ax_check_text="for a Lua interpreter with version >= $1, < $2"])
AC_CACHE_CHECK([$_ax_check_text], AC_CACHE_CHECK([$_ax_check_text],
[ax_cv_pathless_LUA], [ax_cv_pathless_LUA],
[ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do [ for ax_cv_pathless_LUA in $_ax_lua_interpreter_list none; do
test "x$ax_cv_pathless_LUA" = 'xnone' && break test "x$ax_cv_pathless_LUA" = 'xnone' && break
_AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue]) _AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue])
_AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break]) _AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break])
@@ -268,13 +278,25 @@ AC_DEFUN([AX_PROG_LUA],
ax_cv_lua_version=[`$LUA -e ' ax_cv_lua_version=[`$LUA -e '
-- return a version number in X.Y format -- return a version number in X.Y format
local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)") local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)")
print(ver)'`] print(ver or "")'`]
]) ])
AS_IF([test "x$ax_cv_lua_version" = 'x'], AS_IF([test "x$ax_cv_lua_version" = 'x'],
[AC_MSG_ERROR([invalid Lua version number])]) [AC_MSG_ERROR([invalid Lua version number])])
AC_SUBST([LUA_VERSION], [$ax_cv_lua_version]) AC_SUBST([LUA_VERSION], [$ax_cv_lua_version])
AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`]) AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`])
AM_COND_IF([LUAJIT], [
AC_CACHE_CHECK([for $ax_display_LUA jit version], [ax_cv_luajit_version],
[ ax_cv_luajit_version=[`$LUA -e '
local _, _, ver = string.find(jit and jit.version, "(%d+%..+)")
print(ver or "")'`]
])
AS_IF([test "x$ax_cv_luajit_version" = 'x'],
[AC_MSG_ERROR([invalid Lua jit version number])])
AC_SUBST([LUAJIT_VERSION], [$ax_cv_luajit_version])
AC_SUBST([LUAJIT_SHORT_VERSION], [$(echo "$LUAJIT_VERSION" | $SED 's|\.|§|;s|\..*||;s|§|.|')])
])
dnl The following check is not supported: dnl The following check is not supported:
dnl At times (like when building shared libraries) you may want to know dnl At times (like when building shared libraries) you may want to know
dnl which OS platform Lua thinks this is. dnl which OS platform Lua thinks this is.
@@ -464,33 +486,50 @@ AC_DEFUN([AX_LUA_HEADERS],
AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION]) AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION])
]) ])
AM_COND_IF([LUAJIT],[
dnl Check for LUAJIT_VERSION.
AC_MSG_CHECKING([if LUAJIT_VERSION is defined])
AS_IF([test "x$LUAJIT_VERSION" != 'x'],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([cannot check Lua jit headers without knowing LUAJIT_VERSION])
])
])
dnl Make LUA_INCLUDE a precious variable. dnl Make LUA_INCLUDE a precious variable.
AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1]) AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1])
dnl Some default directories to search. dnl Some default directories to search.
LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'` AM_COND_IF([LUAJIT],
m4_define_default([_AX_LUA_INCLUDE_LIST], [_ax_lua_include_list="
[ /usr/include/lua$LUA_VERSION \ /usr/include/luajit-$LUAJIT_VERSION
/usr/include/lua-$LUA_VERSION \ /usr/include/luajit-$LUAJIT_SHORT_VERSION
/usr/include/lua/$LUA_VERSION \ /usr/local/include/luajit-$LUAJIT_VERSION
/usr/include/lua$LUA_SHORT_VERSION \ /usr/local/include/luajit-$LUAJIT_SHORT_VERSION"],
/usr/local/include/lua$LUA_VERSION \ [_ax_lua_include_list="
/usr/local/include/lua-$LUA_VERSION \ /usr/include/lua$LUA_VERSION
/usr/local/include/lua/$LUA_VERSION \ /usr/include/lua-$LUA_VERSION
/usr/local/include/lua$LUA_SHORT_VERSION \ /usr/include/lua/$LUA_VERSION
]) /usr/include/lua$LUA_SHORT_VERSION
/usr/local/include/lua$LUA_VERSION
/usr/local/include/lua-$LUA_VERSION
/usr/local/include/lua/$LUA_VERSION
/usr/local/include/lua$LUA_SHORT_VERSION"])
dnl Try to find the headers. dnl Try to find the headers.
_ax_lua_saved_cppflags=$CPPFLAGS _ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h]) AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
AM_COND_IF([LUAJIT], [AC_CHECK_HEADERS([luajit.h])])
CPPFLAGS=$_ax_lua_saved_cppflags CPPFLAGS=$_ax_lua_saved_cppflags
dnl Try some other directories if LUA_INCLUDE was not set. dnl Try some other directories if LUA_INCLUDE was not set.
AS_IF([test "x$LUA_INCLUDE" = 'x' && AS_IF([test "x$LUA_INCLUDE" = 'x' &&
test "x$ac_cv_header_lua_h" != 'xyes'], test "x$ac_cv_header_lua_h" != 'xyes' ||
test "x$with_luajit" != 'xno' &&
test "x$ac_cv_header_luajit_h" != 'xyes'],
[ dnl Try some common include paths. [ dnl Try some common include paths.
for _ax_include_path in _AX_LUA_INCLUDE_LIST; do for _ax_include_path in $_ax_lua_include_list; do
test ! -d "$_ax_include_path" && continue test ! -d "$_ax_include_path" && continue
AC_MSG_CHECKING([for Lua headers in]) AC_MSG_CHECKING([for Lua headers in])
@@ -500,10 +539,12 @@ AC_DEFUN([AX_LUA_HEADERS],
AS_UNSET([ac_cv_header_lualib_h]) AS_UNSET([ac_cv_header_lualib_h])
AS_UNSET([ac_cv_header_lauxlib_h]) AS_UNSET([ac_cv_header_lauxlib_h])
AS_UNSET([ac_cv_header_luaconf_h]) AS_UNSET([ac_cv_header_luaconf_h])
AS_UNSET([ac_cv_header_luajit_h])
_ax_lua_saved_cppflags=$CPPFLAGS _ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$_ax_include_path" CPPFLAGS="$CPPFLAGS -I$_ax_include_path"
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h]) AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
AM_COND_IF([LUAJIT], [AC_CHECK_HEADERS([luajit.h])])
CPPFLAGS=$_ax_lua_saved_cppflags CPPFLAGS=$_ax_lua_saved_cppflags
AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'], AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
@@ -621,18 +662,26 @@ AC_DEFUN([AX_LUA_LIBS],
dnl Try to find the Lua libs. dnl Try to find the Lua libs.
_ax_lua_saved_libs=$LIBS _ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB" LIBS="$LIBS $LUA_LIB"
AC_SEARCH_LIBS([lua_load], AM_COND_IF([LUAJIT],
[ lua$LUA_VERSION \ [AC_SEARCH_LIBS([lua_load],
lua$LUA_SHORT_VERSION \ [ luajit$LUA_VERSION \
lua-$LUA_VERSION \ luajit$LUA_SHORT_VERSION \
lua-$LUA_SHORT_VERSION \ luajit-$LUA_VERSION \
:liblua-$LUA_VERSION.so.0 \ luajit-$LUA_SHORT_VERSION \
:liblua-$LUA_SHORT_VERSION.so.0 \ luajit],
lua \ [_ax_found_lua_libs='yes'],
], [_ax_found_lua_libs='no'],
[_ax_found_lua_libs='yes'], [$_ax_lua_extra_libs])],
[_ax_found_lua_libs='no'], [AC_SEARCH_LIBS([lua_load],
[$_ax_lua_extra_libs]) [ lua$LUA_VERSION \
lua$LUA_SHORT_VERSION \
lua-$LUA_VERSION \
lua-$LUA_SHORT_VERSION \
lua \
],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'],
[$_ax_lua_extra_libs])])
LIBS=$_ax_lua_saved_libs LIBS=$_ax_lua_saved_libs
AS_IF([test "x$ac_cv_search_lua_load" != 'xno' && AS_IF([test "x$ac_cv_search_lua_load" != 'xno' &&
+100 -127
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)
@@ -54,6 +9,8 @@ AC_DEFUN([TORRENT_CHECK_EPOLL], [
} }
])], ])],
[ [
use_epoll=yes
AC_DEFINE(USE_EPOLL, 1, Use epoll.) AC_DEFINE(USE_EPOLL, 1, Use epoll.)
AC_MSG_RESULT(yes) AC_MSG_RESULT(yes)
], [ ], [
@@ -86,6 +43,8 @@ AC_DEFUN([TORRENT_CHECK_KQUEUE], [
} }
])], ])],
[ [
use_kqueue=yes
AC_DEFINE(USE_KQUEUE, 1, Use kqueue.) AC_DEFINE(USE_KQUEUE, 1, Use kqueue.)
AC_MSG_RESULT(yes) AC_MSG_RESULT(yes)
], [ ], [
@@ -118,48 +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(HAVE_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_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])
@@ -171,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])
]) ])
@@ -260,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)
@@ -273,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]),
@@ -281,29 +200,45 @@ AC_DEFUN([TORRENT_WITH_LUA], [
AC_MSG_RESULT(no) AC_MSG_RESULT(no)
else else
AX_PROG_LUA AX_PROG_LUA
AX_LUA_LIBS
AX_LUA_HEADERS # 1. Override AX_LUA_LIBS default crash behavior
AC_DEFINE(HAVE_LUA, 1, Use LUA.) AX_LUA_LIBS([have_lua_libs=yes], [have_lua_libs=no])
AC_DEFINE(LUA_DATADIR, [PACKAGE_DATADIR "/lua"], [LUA data directory])
LIBS="$LIBS $LUA_LIB" # 2. Override AX_LUA_HEADERS default crash behavior
CXXFLAGS="$CXXFLAGS $LUA_INCLUDE" AX_LUA_HEADERS([have_lua_headers=yes], [have_lua_headers=no])
# 3. Only inject if both checks completely pass
if test "x$have_lua_libs" = "xyes" && test "x$have_lua_headers" = "xyes"; then
AC_DEFINE(HAVE_LUA, 1, Use LUA.)
AC_DEFINE(LUA_DATADIR, [PACKAGE_DATADIR "/lua"], [LUA data directory])
LIBS="$LIBS $LUA_LIB"
CXXFLAGS="$CXXFLAGS $LUA_INCLUDE"
else
# Throw fatal error ONLY if user strictly ran --with-lua=yes
if test "$withval" = "yes"; then
AC_MSG_ERROR([Lua support explicitly requested, but compatible Lua 5.3 libs/headers were not found.])
else
AC_MSG_WARN([Lua 5.3 libs or headers missing. Proceeding without Lua support.])
fi
fi
fi fi
],[ ],[
AC_MSG_RESULT(ignored) AC_MSG_RESULT(ignored)
]) ])
]) ])
AC_DEFUN([TORRENT_WITH_INOTIFY], [ AC_DEFUN([TORRENT_WITH_INOTIFY], [
AC_LANG_PUSH(C++) AC_LANG_PUSH(C++)
AC_CHECK_HEADERS([sys/inotify.h mcheck.h]) AC_CHECK_HEADERS([sys/inotify.h])
AC_MSG_CHECKING([whether sys/inotify.h actually works]) AC_MSG_CHECKING([whether sys/inotify.h actually works])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([ AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <sys/inotify.h> #include <sys/inotify.h>
int main(int,const char**) { return (-1 == inotify_init()); }]) int main(int,const char**) { return (-1 == inotify_init()); }])
],[ ],[
AC_DEFINE(HAVE_INOTIFY, 1, [sys/inotify.h exists and works correctly]) AC_DEFINE(USE_INOTIFY, 1, [sys/inotify.h exists and works correctly])
AC_MSG_RESULT(yes)], AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(failed)] [AC_MSG_RESULT(failed)]
) )
@@ -311,12 +246,14 @@ 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)
AC_MSG_CHECKING(for pthread_setname_np type) AC_MSG_CHECKING(for pthread_setname_np type)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#define _GNU_SOURCE
#include <pthread.h> #include <pthread.h>
#include <sys/types.h> #include <sys/types.h>
]], [[ ]], [[
@@ -341,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])
@@ -360,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]]]),
@@ -375,3 +331,20 @@ AC_DEFUN([TORRENT_WITH_SYSTEMD], [
fi fi
]) ])
]) ])
AC_DEFUN([TORRENT_WITHOUT_NCURSES], [
AC_ARG_WITH([ncurses],
[AS_HELP_STRING([--without-ncurses], [build without ncurses (daemon-only mode)])],
[with_ncurses=$withval],
[with_ncurses=yes])
if test "x$with_ncurses" = xno; then
AC_DEFINE([HAVE_NO_NCURSES], [1], [Define to 1 if building without ncurses])
CURSES_LIBS=""
CURSES_CFLAGS=""
CURSES_LIB=""
fi
AM_CONDITIONAL([NO_NCURSES], [test "x$with_ncurses" = xno])
])
+169 -145
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,108 +80,196 @@ 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_MSG_CHECKING(for cacheline) AC_REQUIRE([AC_CANONICAL_HOST])
AC_MSG_CHECKING([for target cacheline size])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([ case "$host_os" in
#include <stdlib.h> linux*)
#include <linux/cache.h> # REGION: Linux Kernel Extraction Loop
void* vptr __cacheline_aligned; AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
void f() { posix_memalign(&vptr, SMP_CACHE_BYTES, 42); } #include <stdlib.h>
])], #include <linux/cache.h>
[ void* vptr;
AC_MSG_RESULT(found builtin) void f() {
dnl Need to fix this so that it uses the stuff defined by the system. int res = posix_memalign(&vptr, SMP_CACHE_BYTES, 42);
(void)res;
}
]])],[
# We need an explicit variable fallback condition inside AC_COMPUTE_INT
AC_COMPUTE_INT([torrent_cv_cacheline_size], [SMP_CACHE_BYTES], [#include <linux/cache.h>], [torrent_cv_cacheline_size=0])
AC_DEFINE(LT_SMP_CACHE_BYTES, 128, Largest L1 cache size we know of should work on all archs.) if test "$torrent_cv_cacheline_size" -gt 0; then
], [ AC_MSG_RESULT([linux builtin ($torrent_cv_cacheline_size bytes)])
AC_MSG_RESULT(using default 128 bytes) AC_DEFINE_UNQUOTED([LT_SMP_CACHE_BYTES], [$torrent_cv_cacheline_size], [System-defined Linux L1 SMP cacheline size.])
AC_DEFINE(LT_SMP_CACHE_BYTES, 128, Largest L1 cache size we know of should work on all archs.) else
]) # Handle scenarios where macro maps to a complex runtime expression or fails
]) AC_MSG_RESULT([failed to parse SMP_CACHE_BYTES value])
AC_MSG_FAILURE([Linux kernel headers found, but cacheline constant could not be computed at compile-time.])
AC_DEFUN([TORRENT_CHECK_ALIGNED], [
AC_MSG_CHECKING(the byte alignment)
AC_RUN_IFELSE([AC_LANG_SOURCE([
#include <inttypes.h>
int main() {
char buf@<:@8@:>@ = { 0, 0, 0, 0, 1, 0, 0, 0 };
int i;
for (i = 1; i < 4; ++i)
if (*(uint32_t*)(buf + i) == 0) return -1;
return 0;
}
])],
[
AC_MSG_RESULT(none needed)
], [
AC_DEFINE(USE_ALIGNED, 1, Require byte alignment)
AC_MSG_RESULT(required)
])
])
AC_DEFUN([TORRENT_ENABLE_ALIGNED], [
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 fi
],[ ],[
TORRENT_CHECK_ALIGNED # Explicitly validate the CPU type even on Linux if the header check fails
]) case "$host_cpu" in
x86_64*|amd64*|i386*|i486*|i586*|i686*)
AC_MSG_RESULT([linux fallback x86 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux x86 hardware.])
;;
arm*|aarch64*|powerpc*|ppc*|s390x*)
AC_MSG_RESULT([linux fallback enterprise 128 bytes])
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_FAILURE([Unrecognized CPU architecture ($host_cpu) on Linux fallback path. Aborting build.])
;;
esac
])
;;
*)
# REGION: Cross-Platform Strict Hardware Mapping (macOS, FreeBSD, OpenBSD, NetBSD)
case "$host_cpu" in
x86_64*|amd64*|i386*|i486*|i586*|i686*)
# Explicit x86 desktop hardware baseline block
AC_MSG_RESULT([$host_os ($host_cpu) standard x86 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Standard 64-byte alignment for stable x86 hardware layout.])
;;
arm*|aarch64*|powerpc*|ppc*|s390x*)
# Explicit modern enterprise and Apple Silicon hardware baseline block
AC_MSG_RESULT([$host_os ($host_cpu) stable enterprise 128 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 128, [Optimized 128-byte alignment for newer high-performance chipsets.])
;;
*)
# STRICT ENFORCEMENT: Fail the build immediately if the CPU isn't explicitly known
AC_MSG_RESULT([unrecognized architecture])
AC_MSG_FAILURE([The target CPU architecture ($host_cpu) is unrecognized. Aborting configuration to prevent fatal runtime false-sharing or memory misalignment errors.])
;;
esac
;;
esac
]) ])
AC_DEFUN([TORRENT_DISABLE_INSTRUMENTATION], [ AC_DEFUN([TORRENT_CHECK_CUSTOM_ENDIAN64], [
AC_CHECK_HEADERS([endian.h sys/endian.h libkern/OSByteOrder.h])
AC_C_BIGENDIAN
AH_TEMPLATE([CUSTOM_ENDIAN_HEADER], [The system header to include for endian conversions.])
AH_TEMPLATE([custom_ntohll], [Convert 64-bit integer from network to host byte order.])
AH_TEMPLATE([custom_htonll], [Convert 64-bit integer from host to network byte order.])
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>'],
[has_native_be64=no]
)
AC_MSG_RESULT([$has_native_be64])
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_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],
+8
View File
@@ -174,6 +174,10 @@ 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.h \
\ \
command_download.cc \ command_download.cc \
command_dynamic.cc \ command_dynamic.cc \
@@ -203,4 +207,8 @@ libsub_root_a_SOURCES = \
signal_handler.h signal_handler.h
if NO_NCURSES
libsub_root_a_SOURCES += display/curses_stub.cc display/curses_stub.h
endif
AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -DPACKAGE_DATADIR=\"$(pkgdatadir)\" AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -DPACKAGE_DATADIR=\"$(pkgdatadir)\"
+102 -67
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>
@@ -18,6 +17,7 @@
#include <torrent/net/types.h> #include <torrent/net/types.h>
#include <torrent/peer/connection_list.h> #include <torrent/peer/connection_list.h>
#include <torrent/peer/peer_list.h> #include <torrent/peer/peer_list.h>
#include <torrent/system/callbacks.h>
#include <torrent/utils/file_stat.h> #include <torrent/utils/file_stat.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h> #include <torrent/utils/option_strings.h>
@@ -32,31 +32,34 @@
#include "control.h" #include "control.h"
#include "command_helpers.h" #include "command_helpers.h"
std::string torrent::string_utf8
retrieve_d_base_path(core::Download* download) { retrieve_d_base_path(core::Download* download) {
if (download->file_list()->is_multi_file()) if (download->file_list()->is_multi_file())
return download->file_list()->frozen_root_dir(); return download->file_list()->frozen_root_dir();
else
return download->file_list()->empty() ? std::string() : download->file_list()->at(0)->frozen_path(); if (download->file_list()->empty())
return {};
return download->file_list()->at(0)->frozen_path();
} }
std::string torrent::string_utf8
retrieve_d_base_filename(core::Download* download) { retrieve_d_base_filename(core::Download* download) {
const std::string* base; torrent::string_utf8 base_path;
if (download->file_list()->is_multi_file()) if (download->file_list()->is_multi_file())
base = &download->file_list()->frozen_root_dir(); base_path = download->file_list()->frozen_root_dir();
else if (download->file_list()->empty()) else if (!download->file_list()->empty())
return std::string(); base_path = download->file_list()->at(0)->frozen_path();
else else
base = &download->file_list()->at(0)->frozen_path(); return {};
std::string::size_type split = base->rfind('/'); auto split = base_path.str().rfind('/');
if (split == std::string::npos) if (split == std::string::npos)
return *base; return base_path;
else
return base->substr(split + 1); return torrent::string_utf8::from_string(base_path.str().substr(split + 1));
} }
torrent::Object torrent::Object
@@ -144,9 +147,9 @@ apply_d_directory(core::Download* download, const std::string& name) {
if (!download->file_list()->is_multi_file()) if (!download->file_list()->is_multi_file())
download->set_root_directory(name); download->set_root_directory(name);
else if (name.empty() || *name.rbegin() == '/') else if (name.empty() || *name.rbegin() == '/')
download->set_root_directory(name + download->info()->name()); download->set_root_directory(name + download->info()->name().str());
else else
download->set_root_directory(name + "/" + download->info()->name()); download->set_root_directory(name + "/" + download->info()->name().str());
} }
torrent::Object torrent::Object
@@ -160,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);
@@ -271,16 +274,6 @@ retrieve_d_custom_map(core::Download* download, bool keys_only, const torrent::O
return result; return result;
} }
torrent::Object
retrieve_d_bitfield(core::Download* download) {
const torrent::Bitfield* bitField = download->download()->file_list()->bitfield();
if (bitField->empty())
return torrent::Object("");
return torrent::Object(torrent::utils::transform_to_hex_str(*bitField));
}
void void
apply_d_add_peer(core::Download* download, const std::string& arg) { apply_d_add_peer(core::Download* download, const std::string& arg) {
int port, ret; int port, ret;
@@ -305,8 +298,10 @@ apply_d_add_peer(core::Download* download, const std::string& arg) {
assert(std::this_thread::get_id() == torrent::main_thread::thread_id()); assert(std::this_thread::get_id() == torrent::main_thread::thread_id());
auto callback_id = torrent::system::make_callback_id();
// Currently discarding SOCK_STREAM. // Currently discarding SOCK_STREAM.
torrent::this_thread::resolver()->resolve_preferred(NULL, host, AF_UNSPEC, AF_INET, [download, port](torrent::c_sa_shared_ptr sa, int err) { torrent::this_thread::resolver()->resolve_preferred(callback_id, host, AF_UNSPEC, AF_INET, [download, port](torrent::c_sa_shared_ptr sa, int err) {
if (sa == nullptr) { if (sa == nullptr) {
lt_log_print(torrent::LOG_TORRENT_WARN, "could not resolve hostname for added peer: %s", gai_strerror(err)); lt_log_print(torrent::LOG_TORRENT_WARN, "could not resolve hostname for added peer: %s", gai_strerror(err));
return; return;
@@ -345,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;
@@ -359,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();
@@ -383,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);
} }
@@ -413,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);
@@ -648,30 +644,48 @@ void cg_d_group_set(core::Download* download, const torrent::Objec
void void
initialize_command_download() { initialize_command_download() {
CMD2_DL("d.hash", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->hash()); }); CMD2_DL("d.hash", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->hash()); });
CMD2_DL("d.local_id", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->local_id()); }); CMD2_DL("d.local_id", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->local_id()); });
CMD2_DL("d.local_id_html", [](auto* download, auto) { return torrent::utils::copy_escape_html_str(download->info()->local_id()); }); CMD2_DL("d.local_id_html", [](auto* download, auto) { return torrent::utils::copy_escape_html_str(download->info()->local_id()); });
CMD2_DL("d.bitfield", std::bind(&retrieve_d_bitfield, std::placeholders::_1)); CMD2_DL("d.bitfield", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(*download->download()->file_list()->bitfield()); });
CMD2_DL("d.base_path", std::bind(&retrieve_d_base_path, std::placeholders::_1)); CMD2_DL("d.base_path", [](auto* download, auto) { return retrieve_d_base_path(download).str(); });
CMD2_DL("d.base_filename", std::bind(&retrieve_d_base_filename, std::placeholders::_1)); CMD2_DL("d.base_path.hex", [](auto* download, auto) { return retrieve_d_base_path(download).object_hex(); });
CMD2_DL("d.base_path.base64", [](auto* download, auto) { return retrieve_d_base_path(download).object_base64(); });
CMD2_DL("d.base_path.base64_as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_base64_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_as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_as_binary(); });
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.base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_base64(); });
CMD2_DL("d.base_filename.base64_as_binary", [](auto* download, auto) { return retrieve_d_base_filename(download).object_base64_as_binary(); });
CMD2_DL("d.base_filename.as_binary", [](auto* download, auto) { return retrieve_d_base_filename(download).object_as_binary(); });
CMD2_DL("d.base_filename.or_base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_utf8_or_base64(); });
CMD2_DL("d.base_filename.or_as_binary", [](auto* download, auto) { return retrieve_d_base_filename(download).object_utf8_or_as_binary(); });
CMD2_DL("d.name", CMD2_ON_INFO(name)); CMD2_DL("d.name", [](auto* download, auto) { return download->info()->name().str(); });
CMD2_DL("d.creation_date", CMD2_ON_INFO(creation_date)); CMD2_DL("d.name.hex", [](auto* download, auto) { return download->info()->name().object_hex(); });
CMD2_DL("d.load_date", CMD2_ON_INFO(load_date)); CMD2_DL("d.name.base64", [](auto* download, auto) { return download->info()->name().object_base64(); });
CMD2_DL("d.name.base64_as_binary", [](auto* download, auto) { return download->info()->name().object_base64_as_binary(); });
CMD2_DL("d.name.as_binary", [](auto* download, auto) { return download->info()->name().object_as_binary(); });
CMD2_DL("d.name.or_base64", [](auto* download, auto) { return download->info()->name().object_utf8_or_base64(); });
CMD2_DL("d.name.or_as_binary", [](auto* download, auto) { return download->info()->name().object_utf8_or_as_binary(); });
CMD2_DL("d.creation_date", [](auto* download, auto) { return download->info()->creation_date(); });
CMD2_DL("d.load_date", [](auto* download, auto) { return download->info()->load_date(); });
// //
// Network related: // Network related:
// //
CMD2_DL ("d.up.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(up_rate))); CMD2_DL ("d.up.rate", [](auto* download, auto) { return download->info()->up_rate()->rate(); });
CMD2_DL ("d.up.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(up_rate))); CMD2_DL ("d.up.total", [](auto* download, auto) { return download->info()->up_rate()->total(); });
CMD2_DL ("d.down.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(down_rate))); CMD2_DL ("d.down.rate", [](auto* download, auto) { return download->info()->down_rate()->rate(); });
CMD2_DL ("d.down.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(down_rate))); CMD2_DL ("d.down.total", [](auto* download, auto) { return download->info()->down_rate()->total(); });
CMD2_DL ("d.skip.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(skip_rate))); CMD2_DL ("d.skip.rate", [](auto* download, auto) { return download->info()->skip_rate()->rate(); });
CMD2_DL ("d.skip.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(skip_rate))); CMD2_DL ("d.skip.total", [](auto* download, auto) { return download->info()->skip_rate()->total(); });
CMD2_DL ("d.peer_exchange", CMD2_ON_INFO(is_pex_enabled)); CMD2_DL ("d.peer_exchange", [](auto* download, auto) { return download->info()->is_pex_enabled(); });
CMD2_DL_VALUE_V ("d.peer_exchange.set", std::bind(&torrent::Download::set_pex_enabled, CMD2_BIND_DL, std::placeholders::_2)); CMD2_DL_VALUE_V ("d.peer_exchange.set", [](auto* download, auto value) { download->download()->set_pex_enabled(value); });
CMD2_DL_LIST ("d.create_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 0)); CMD2_DL_LIST ("d.create_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 0));
CMD2_DL_LIST ("d.delete_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 1)); CMD2_DL_LIST ("d.delete_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 1));
@@ -687,16 +701,16 @@ initialize_command_download() {
// Control functinos: // Control functinos:
// //
CMD2_DL ("d.is_open", CMD2_ON_INFO(is_open)); CMD2_DL ("d.is_open", [](auto* download, auto) { return download->info()->is_open(); });
CMD2_DL ("d.is_active", CMD2_ON_INFO(is_active)); CMD2_DL ("d.is_active", [](auto* download, auto) { return download->info()->is_active(); });
CMD2_DL ("d.is_hash_checked", std::bind(&torrent::Download::is_hash_checked, CMD2_BIND_DL)); CMD2_DL ("d.is_hash_checked", std::bind(&torrent::Download::is_hash_checked, CMD2_BIND_DL));
CMD2_DL ("d.is_hash_checking", std::bind(&torrent::Download::is_hash_checking, CMD2_BIND_DL)); CMD2_DL ("d.is_hash_checking", std::bind(&torrent::Download::is_hash_checking, CMD2_BIND_DL));
CMD2_DL ("d.is_multi_file", std::bind(&torrent::FileList::is_multi_file, CMD2_BIND_FL)); CMD2_DL ("d.is_multi_file", std::bind(&torrent::FileList::is_multi_file, CMD2_BIND_FL));
CMD2_DL ("d.is_private", CMD2_ON_INFO(is_private)); CMD2_DL ("d.is_private", [](auto* download, auto) { return download->info()->is_private(); });
CMD2_DL ("d.is_pex_active", CMD2_ON_INFO(is_pex_active)); CMD2_DL ("d.is_pex_active", [](auto* download, auto) { return download->info()->is_pex_active(); });
CMD2_DL ("d.is_partially_done", CMD2_ON_DATA(is_partially_done)); CMD2_DL ("d.is_partially_done", CMD2_ON_DATA(is_partially_done));
CMD2_DL ("d.is_not_partially_done", CMD2_ON_DATA(is_not_partially_done)); CMD2_DL ("d.is_not_partially_done", CMD2_ON_DATA(is_not_partially_done));
CMD2_DL ("d.is_meta", CMD2_ON_INFO(is_meta_download)); CMD2_DL ("d.is_meta", [](auto* download, auto) { return download->info()->is_meta_download(); });
CMD2_DL_V ("d.resume", std::bind(&core::DownloadList::resume_default, control->core()->download_list(), std::placeholders::_1)); CMD2_DL_V ("d.resume", std::bind(&core::DownloadList::resume_default, control->core()->download_list(), std::placeholders::_1));
CMD2_DL_V ("d.pause", std::bind(&core::DownloadList::pause_default, control->core()->download_list(), std::placeholders::_1)); CMD2_DL_V ("d.pause", std::bind(&core::DownloadList::pause_default, control->core()->download_list(), std::placeholders::_1));
@@ -767,15 +781,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");
@@ -830,7 +844,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));
@@ -898,8 +912,26 @@ 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.hex");
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.as_binary");
rpc::rpc.mark_safe("d.base_path.or_base64");
rpc::rpc.mark_safe("d.base_path.or_as_binary");
rpc::rpc.mark_safe("d.base_filename"); rpc::rpc.mark_safe("d.base_filename");
rpc::rpc.mark_safe("d.base_filename.hex");
rpc::rpc.mark_safe("d.base_filename.base64");
rpc::rpc.mark_safe("d.base_filename.base64_as_binary");
rpc::rpc.mark_safe("d.base_filename.as_binary");
rpc::rpc.mark_safe("d.base_filename.or_base64");
rpc::rpc.mark_safe("d.base_filename.or_as_binary");
rpc::rpc.mark_safe("d.name"); rpc::rpc.mark_safe("d.name");
rpc::rpc.mark_safe("d.name.hex");
rpc::rpc.mark_safe("d.name.base64");
rpc::rpc.mark_safe("d.name.base64_as_binary");
rpc::rpc.mark_safe("d.name.as_binary");
rpc::rpc.mark_safe("d.name.or_base64");
rpc::rpc.mark_safe("d.name.or_as_binary");
rpc::rpc.mark_safe("d.directory"); rpc::rpc.mark_safe("d.directory");
rpc::rpc.mark_safe("d.directory_base"); rpc::rpc.mark_safe("d.directory_base");
rpc::rpc.mark_safe("d.creation_date"); rpc::rpc.mark_safe("d.creation_date");
@@ -941,6 +973,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");
+61 -39
View File
@@ -2,6 +2,8 @@
#include <functional> #include <functional>
#include <cstdio> #include <cstdio>
#include <string>
#include <vector>
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/hash_string.h> #include <torrent/hash_string.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
@@ -19,6 +21,7 @@
#include "rpc/command_scheduler.h" #include "rpc/command_scheduler.h"
#include "rpc/parse.h" #include "rpc/parse.h"
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
#include "utils/watch_ready_queue.h"
torrent::Object torrent::Object
apply_on_ratio(const torrent::Object& rawArgs) { apply_on_ratio(const torrent::Object& rawArgs) {
@@ -165,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);
@@ -253,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() + "'.");
@@ -267,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) {
@@ -291,59 +298,74 @@ call_watch_command(const std::string& command, const std::string& path) {
} }
torrent::Object torrent::Object
directory_watch_added(const torrent::Object::list_type& args) { directory_watch(const torrent::Object::list_type& args, int flags) {
if (args.size() != 2) if (args.size() != 2)
throw torrent::input_error("Too few arguments."); throw torrent::input_error("Too few arguments.");
auto& path = args.front().as_string(); auto& path = args.front().as_string();
auto& command = args.back().as_string(); auto& command = args.back().as_string();
std::string expanded_path = expand_path(path);
if (!control->directory_events()->open()) if (!control->directory_events()->open())
throw torrent::input_error("Could not open inotify:" + std::string(std::strerror(errno))); throw torrent::input_error("Could not open inotify:" + std::string(std::strerror(errno)));
control->directory_events()->notify_on(path.c_str(), torrent::watch_descriptor::slot_string slot =
torrent::directory_events::flag_on_added | torrent::directory_events::flag_on_updated, flags == torrent::directory_events::flag_on_ready ?
std::bind(&call_watch_command, command, std::placeholders::_1)); torrent::watch_descriptor::slot_string([command](const auto& arg) { control->watch_ready_queue()->push(command, arg); }) :
torrent::watch_descriptor::slot_string(std::bind(&call_watch_command, command, std::placeholders::_1));
control->directory_events()->notify_on(expanded_path.c_str(), flags, slot);
return torrent::Object(); return torrent::Object();
} }
torrent::Object
directory_watch_added(const torrent::Object::list_type& args) {
return directory_watch(args,
torrent::directory_events::flag_on_added |
torrent::directory_events::flag_on_updated);
}
torrent::Object
directory_watch_ready(const torrent::Object::list_type& args) {
return directory_watch(args,
torrent::directory_events::flag_on_ready);
}
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 ("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");
@@ -353,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");
} }
+50 -23
View File
@@ -24,24 +24,34 @@ apply_f_path(torrent::File* file) {
if (file->path()->empty()) if (file->path()->empty())
return std::string(); return std::string();
torrent::Object resultRaw(*file->path()->begin()); torrent::Object result_raw(file->path()->begin()->str());
torrent::Object::string_type& result = resultRaw.as_string(); auto& result = result_raw.as_string();
for (torrent::Path::const_iterator itr = ++file->path()->begin(), last = file->path()->end(); itr != last; itr++) for (torrent::Path::const_iterator itr = ++file->path()->begin(), last = file->path()->end(); itr != last; itr++)
result += '/' + *itr; result += '/' + itr->str();
return resultRaw; return result_raw;
} }
torrent::Object torrent::Object
apply_f_path_components(torrent::File* file) { apply_f_path_components(torrent::File* file, int output_type) {
torrent::Object resultRaw = torrent::Object::create_list(); auto result_raw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list(); auto& result = result_raw.as_list();
for (const auto& itr : *file->path()) for (const auto& itr : *file->path()) {
result.push_back(itr); switch (output_type) {
case 0: result.push_back(itr.str()); break;
case 1: result.push_back(itr.object_hex()); break;
case 2: result.push_back(itr.object_base64()); break;
case 3: result.push_back(itr.object_base64_as_binary()); break;
case 4: result.push_back(itr.object_as_binary()); break;
case 5: result.push_back(itr.object_utf8_or_base64()); break;
case 6: result.push_back(itr.object_utf8_or_as_binary()); break;
default: throw torrent::input_error("apply_f_path_components(): invalid output type");
};
}
return resultRaw; return result_raw;
} }
torrent::Object torrent::Object
@@ -57,7 +67,7 @@ apply_fi_filename_last(torrent::FileListIterator* itr) {
if (itr->depth() >= itr->file()->path()->size()) if (itr->depth() >= itr->file()->path()->size())
return "ERROR"; return "ERROR";
return itr->file()->path()->at(itr->depth()); return itr->file()->path()->at(itr->depth()).str();
} }
void void
@@ -80,21 +90,32 @@ initialize_command_file() {
CMD2_FILE_V("f.prioritize_last.enable", std::bind(&torrent::File::set_flags, std::placeholders::_1, torrent::File::flag_prioritize_last)); CMD2_FILE_V("f.prioritize_last.enable", std::bind(&torrent::File::set_flags, std::placeholders::_1, torrent::File::flag_prioritize_last));
CMD2_FILE_V("f.prioritize_last.disable", std::bind(&torrent::File::unset_flags, std::placeholders::_1, torrent::File::flag_prioritize_last)); CMD2_FILE_V("f.prioritize_last.disable", std::bind(&torrent::File::unset_flags, std::placeholders::_1, torrent::File::flag_prioritize_last));
CMD2_FILE("f.size_bytes", std::bind(&torrent::File::size_bytes, std::placeholders::_1)); CMD2_FILE("f.size_bytes", [](auto* file, auto) { return file->size_bytes(); });
CMD2_FILE("f.size_chunks", std::bind(&torrent::File::size_chunks, std::placeholders::_1)); CMD2_FILE("f.size_chunks", [](auto* file, auto) { return file->size_chunks(); });
CMD2_FILE("f.completed_chunks", std::bind(&torrent::File::completed_chunks, std::placeholders::_1)); CMD2_FILE("f.completed_chunks", [](auto* file, auto) { return file->completed_chunks(); });
CMD2_FILE("f.offset", std::bind(&torrent::File::offset, std::placeholders::_1)); CMD2_FILE("f.offset", [](auto* file, auto) { return file->offset(); });
CMD2_FILE("f.range_first", std::bind(&torrent::File::range_first, std::placeholders::_1)); CMD2_FILE("f.range_first", [](auto* file, auto) { return file->range_first(); });
CMD2_FILE("f.range_second", std::bind(&torrent::File::range_second, std::placeholders::_1)); CMD2_FILE("f.range_second", [](auto* file, auto) { return file->range_second(); });
CMD2_FILE("f.priority", std::bind(&torrent::File::priority, std::placeholders::_1)); CMD2_FILE("f.priority", [](auto* file, auto) { return file->priority(); });
CMD2_FILE_VALUE_V("f.priority.set", std::bind(&apply_f_set_priority, std::placeholders::_1, std::placeholders::_2)); CMD2_FILE_VALUE_V("f.priority.set", [](auto* file, auto v) { apply_f_set_priority(file, v); });
CMD2_FILE("f.path", std::bind(&apply_f_path, std::placeholders::_1)); CMD2_FILE("f.path", [](auto* file, auto) { return apply_f_path(file); });
CMD2_FILE("f.path_components", std::bind(&apply_f_path_components, std::placeholders::_1)); CMD2_FILE("f.path_components", [](auto* file, auto) { return apply_f_path_components(file, 0); });
CMD2_FILE("f.path_depth", std::bind(&apply_f_path_depth, std::placeholders::_1)); CMD2_FILE("f.path_components.hex", [](auto* file, auto) { return apply_f_path_components(file, 1); });
CMD2_FILE("f.frozen_path", std::bind(&torrent::File::frozen_path, std::placeholders::_1)); CMD2_FILE("f.path_components.base64", [](auto* file, auto) { return apply_f_path_components(file, 2); });
CMD2_FILE("f.path_components.base64_as_binary", [](auto* file, auto) { return apply_f_path_components(file, 3); });
CMD2_FILE("f.path_components.as_binary", [](auto* file, auto) { return apply_f_path_components(file, 4); });
CMD2_FILE("f.path_components.or_base64", [](auto* file, auto) { return apply_f_path_components(file, 5); });
CMD2_FILE("f.path_components.or_as_binary", [](auto* file, auto) { return apply_f_path_components(file, 6); });
CMD2_FILE("f.path_depth", [](auto* file, auto) { return apply_f_path_depth(file); });
CMD2_FILE("f.frozen_path", [](auto* file, auto) { return file->frozen_path().str(); });
CMD2_FILE("f.frozen_path.hex", [](auto* file, auto) { return file->frozen_path().object_hex(); });
CMD2_FILE("f.frozen_path.base64", [](auto* file, auto) { return file->frozen_path().object_base64(); });
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_as_binary", [](auto* file, auto) { return file->frozen_path().object_utf8_or_as_binary(); });
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));
@@ -108,6 +129,12 @@ 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.hex");
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.as_binary");
rpc::rpc.mark_safe("f.frozen_path.or_base64");
rpc::rpc.mark_safe("f.frozen_path.or_as_binary");
rpc::rpc.mark_safe("f.offset"); rpc::rpc.mark_safe("f.offset");
rpc::rpc.mark_safe("f.size_bytes"); rpc::rpc.mark_safe("f.size_bytes");
rpc::rpc.mark_safe("f.size_chunks"); rpc::rpc.mark_safe("f.size_chunks");
+31 -34
View File
@@ -186,8 +186,8 @@ apply_cg_insert(const std::string& arg) {
cg_list_hack.push_back(new torrent::choke_group()); cg_list_hack.push_back(new 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();
} }
@@ -337,54 +337,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", std::bind(&std::vector<torrent::choke_group*>::size, cg_list_hack));
CMD2_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2)); CMD_ANY_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");
+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);
+133 -114
View File
@@ -8,9 +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/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>
@@ -113,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));
@@ -132,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;
} }
@@ -199,142 +188,156 @@ 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::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));
CMD2_ANY_VALUE_V ("pieces.preload.min_rate.set", std::bind(&CM_t::set_preload_required_rate, chunkManager, std::placeholders::_2));
CMD2_ANY ("pieces.memory.current", std::bind(&CM_t::memory_usage, chunkManager)); 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); continue;
CMD2_VAR_STRING ("directory.default", "./"); 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_VAR_STRING ("session.name", "");
CMD_ANY ("session.path", [](auto, auto) { return session_thread::manager()->path(); });
CMD_ANY_STRING_V("session.path.set", [](auto, auto& str) { return session_thread::manager()->set_path(str); });
CMD_ANY ("session.use_lock", [](auto, auto) { return session_thread::manager()->use_lock(); });
CMD_ANY_VALUE_V ("session.use_lock.set", [](auto, auto& value) { return session_thread::manager()->set_use_lock(value); });
CMD_VAR_BOOL ("session.on_completion", true);
CMD_ANY_V ("session.save", [dList](auto, auto) { return dList->session_save(); });
CMD_ANY ("magnet.path", [](auto, auto) { return control->core()->magnet_path(); });
CMD_ANY_STRING_V("magnet.path.set", [](auto, auto& str) { return control->core()->set_magnet_path(str); });
#ifdef HAVE_LUA #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");
@@ -343,6 +346,22 @@ 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");
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)
continue;
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.use_lock"); rpc::rpc.mark_safe("session.use_lock");
+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));
+204 -106
View File
@@ -8,8 +8,13 @@
#include <torrent/data/file_manager.h> #include <torrent/data/file_manager.h>
#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/network_config.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_manager.h>
#include <torrent/runtime/proxy_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/runtime/socket_manager.h>
#include <torrent/tracker/tracker.h> #include <torrent/tracker/tracker.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h> #include <torrent/utils/option_strings.h>
@@ -30,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::net::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::net::NetworkConfig::encryption_none) void
options_mask = torrent::net::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::config::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
@@ -54,13 +153,11 @@ apply_tos(const torrent::Object::string_type& arg) {
if (!rpc::parse_whole_value_nothrow(arg.c_str(), &value, 16, 1)) if (!rpc::parse_whole_value_nothrow(arg.c_str(), &value, 16, 1))
value = torrent::option_find_string(torrent::OPTION_IP_TOS, arg.c_str()); value = torrent::option_find_string(torrent::OPTION_IP_TOS, arg.c_str());
torrent::config::network_config()->set_priority(value); torrent::runtime::network_config()->set_priority(value);
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();
@@ -215,111 +312,111 @@ apply_xmlrpc_dialect(const std::string& arg) {
void void
initialize_command_network() { initialize_command_network() {
auto cm = torrent::connection_manager(); auto file_manager = torrent::file_manager();
auto file_manager = torrent::file_manager(); 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::config::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::total_handshakes(); });
CMD2_ANY ("network.open_sockets", [cm](auto, auto) { return cm->size(); });
CMD2_ANY ("network.max_open_sockets", [cm](auto, auto) { return cm->max_size(); });
CMD2_ANY_VALUE_V ("network.max_open_sockets.set", [cm](auto, auto& value) { return cm->set_max_size(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");
@@ -332,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");
@@ -343,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");
+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));
+50 -55
View File
@@ -3,9 +3,10 @@
#include <cassert> #include <cassert>
#include <cstdio> #include <cstdio>
#include <netdb.h> #include <netdb.h>
#include <torrent/net/network_config.h>
#include <torrent/net/resolver.h> #include <torrent/net/resolver.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/network_manager.h> #include <torrent/runtime/network_manager.h>
#include <torrent/system/callbacks.h>
#include <torrent/tracker/dht_controller.h> #include <torrent/tracker/dht_controller.h>
#include <torrent/tracker/tracker.h> #include <torrent/tracker/tracker.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
@@ -48,8 +49,10 @@ apply_dht_add_node(const std::string& arg) {
// TODO: Move this lookup to DhtController. // TODO: Move this lookup to DhtController.
auto callback_id = torrent::system::make_callback_id();
// Currently discarding SOCK_STREAM. // Currently discarding SOCK_STREAM.
torrent::this_thread::resolver()->resolve_specific(nullptr, host_str, PF_INET, [host_str, port](torrent::c_sa_shared_ptr sa, int err) { torrent::this_thread::resolver()->resolve_specific(callback_id, host_str, PF_INET, [host_str, port](torrent::c_sa_shared_ptr sa, int err) {
if (sa == nullptr) { if (sa == nullptr) {
lt_log_print(torrent::LOG_DHT_ERROR, "dht.add_node : could not resolve host : %s (%s)", gai_strerror(err), host_str.c_str()); lt_log_print(torrent::LOG_DHT_ERROR, "dht.add_node : could not resolve host : %s (%s)", gai_strerror(err), host_str.c_str());
return; return;
@@ -68,16 +71,6 @@ apply_enable_trackers(int64_t arg) {
for (auto download : *control->core()->download_list()) for (auto download : *control->core()->download_list())
download->tracker_controller().for_each([](auto& tracker) { tracker.disable(); }); download->tracker_controller().for_each([](auto& tracker) { tracker.disable(); });
} else if (rpc::call_command_value("trackers.use_udp") == 0) {
for (auto download : *control->core()->download_list()) {
download->tracker_controller().for_each([](auto& tracker) {
if (tracker.type() == torrent::TRACKER_UDP)
tracker.disable();
else
tracker.enable();
});
}
} else { } else {
for (auto download : *control->core()->download_list()) for (auto download : *control->core()->download_list())
download->tracker_controller().for_each([](auto& tracker) { tracker.enable(); }); download->tracker_controller().for_each([](auto& tracker) { tracker.enable(); });
@@ -88,69 +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_busy, 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_busy, 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_counter(); }); CMD2_TRACKER ("t.scrape_counter", [](auto* tracker, auto) { return tracker->state().scrape_counter(); });
CMD2_TRACKER ("t.scrape_complete", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_complete(); }); CMD2_TRACKER ("t.scrape_complete", [](auto* tracker, auto) { return tracker->state().scrape_complete(); });
CMD2_TRACKER ("t.scrape_incomplete", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_incomplete(); }); CMD2_TRACKER ("t.scrape_incomplete", [](auto* tracker, auto) { return tracker->state().scrape_incomplete(); });
CMD2_TRACKER ("t.scrape_downloaded", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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_VAR_BOOL ("trackers.use_udp", true);
auto dht_manager = control->dht_manager(); CMD2_ANY ("trackers.use_udp", [](auto, auto) { return true; });
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);
})
CMD2_ANY_STRING_V ("dht.mode.set", std::bind(&core::DhtManager::set_mode, control->dht_manager(), std::placeholders::_2)); 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 ("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::net::NetworkConfig::override_dht_port, torrent::config::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::net::NetworkConfig::set_override_dht_port, torrent::config::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);
+33 -12
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"
@@ -27,6 +28,7 @@
#include "rpc/object_storage.h" #include "rpc/object_storage.h"
#include "session/session_manager.h" #include "session/session_manager.h"
#include "ui/root.h" #include "ui/root.h"
#include "utils/watch_ready_queue.h"
Control::Control() Control::Control()
: m_ui(new ui::Root()), : m_ui(new ui::Root()),
@@ -36,7 +38,8 @@ Control::Control()
m_commandScheduler(new rpc::CommandScheduler()), m_commandScheduler(new rpc::CommandScheduler()),
m_objectStorage(new rpc::object_storage()), m_objectStorage(new rpc::object_storage()),
m_lua_engine(new rpc::LuaEngine()), m_lua_engine(new rpc::LuaEngine()),
m_directory_events(new torrent::directory_events()) { m_directory_events(new torrent::directory_events()),
m_watch_ready_queue(new utils::WatchReadyQueue()) {
m_core = std::make_unique<core::Manager>(); m_core = std::make_unique<core::Manager>();
m_view_manager = std::make_unique<core::ViewManager>(); m_view_manager = std::make_unique<core::ViewManager>();
@@ -44,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); });
} }
@@ -66,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
@@ -82,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();
@@ -109,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
@@ -121,18 +125,21 @@ Control::is_shutdown_completed() {
if (torrent::net_thread::http_stack()->size() != 0 || !core()->http_queue()->empty()) if (torrent::net_thread::http_stack()->size() != 0 || !core()->http_queue()->empty())
return false; return false;
return torrent::is_inactive(); return core()->is_download_shutdown_completed();
} }
void void
Control::handle_shutdown() { Control::handle_shutdown() {
m_watch_ready_queue->shutdown();
rpc::commands.call_catch("event.system.shutdown", rpc::make_target(), "shutdown", "System shutdown event action failed: "); rpc::commands.call_catch("event.system.shutdown", rpc::make_target(), "shutdown", "System shutdown event action failed: ");
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);
@@ -141,10 +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);
}
+18 -6
View File
@@ -38,23 +38,28 @@ namespace torrent {
class directory_events; class directory_events;
} }
namespace utils {
class WatchReadyQueue;
}
class Control { class Control {
public: public:
Control(); Control();
~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(); }
@@ -70,6 +75,7 @@ public:
rpc::LuaEngine* lua_engine() { return m_lua_engine.get(); } rpc::LuaEngine* lua_engine() { return m_lua_engine.get(); }
torrent::directory_events* directory_events() { return m_directory_events.get(); } torrent::directory_events* directory_events() { return m_directory_events.get(); }
utils::WatchReadyQueue* watch_ready_queue() { return m_watch_ready_queue.get(); }
uint64_t tick() const { return m_tick; } uint64_t tick() const { return m_tick; }
void inc_tick() { m_tick++; } void inc_tick() { m_tick++; }
@@ -94,6 +100,7 @@ private:
std::unique_ptr<rpc::object_storage> m_objectStorage; std::unique_ptr<rpc::object_storage> m_objectStorage;
std::unique_ptr<rpc::LuaEngine> m_lua_engine; std::unique_ptr<rpc::LuaEngine> m_lua_engine;
std::unique_ptr<torrent::directory_events> m_directory_events; std::unique_ptr<torrent::directory_events> m_directory_events;
std::unique_ptr<utils::WatchReadyQueue> m_watch_ready_queue;
uint64_t m_tick{}; uint64_t m_tick{};
@@ -101,9 +108,14 @@ private:
std::string m_workingDirectory; std::string m_workingDirectory;
torrent::utils::SchedulerEntry m_task_shutdown; torrent::utils::SchedulerEntry m_task_shutdown;
torrent::utils::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())
+12 -8
View File
@@ -8,6 +8,11 @@ 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);
@@ -43,6 +46,7 @@ private:
torrent::utils::SchedulerEntry m_stop_timeout; torrent::utils::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};
}; };
+5 -19
View File
@@ -32,21 +32,6 @@ Download::~Download() {
m_download = download_type(); m_download = download_type();
} }
void
Download::enable_udp_trackers(bool state) {
for (int idx = 0, end = m_download.tracker_controller().size(); idx < end; ++idx) {
auto tracker = m_download.tracker_controller().at(idx);
if (tracker.type() != torrent::TRACKER_UDP)
continue;
if (state)
tracker.enable();
else
tracker.disable();
}
}
uint32_t uint32_t
Download::priority() { Download::priority() {
return bencode()->get_key("rtorrent").get_key_value("priority"); return bencode()->get_key("rtorrent").get_key_value("priority");
@@ -103,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
@@ -131,7 +117,7 @@ Download::set_root_directory(const std::string& path) {
(file_list->is_multi_file() ? (file_list->is_multi_file() ?
!file_list->is_root_dir_created() : !file_list->is_root_dir_created() :
!file_stat.update(file_list->front()->frozen_path()))) { !file_stat.update(file_list->front()->frozen_path().str()))) {
set_message("Cannot change the directory of an open download after the files have been moved."); set_message("Cannot change the directory of an open download after the files have been moved.");
rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(this)); rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(this));
+4 -4
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;
@@ -70,8 +72,6 @@ public:
const std::string& message() const { return m_message; } const std::string& message() const { return m_message; }
void set_message(const std::string& msg) { m_message = msg; } void set_message(const std::string& msg) { m_message = msg; }
void enable_udp_trackers(bool state);
uint32_t priority(); uint32_t priority();
void set_priority(uint32_t p); void set_priority(uint32_t p);
@@ -80,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;
@@ -102,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{};
}; };
+24 -24
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"
@@ -104,40 +105,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([this]() { receive_loaded(); }); m_manager->http_queue()->insert(m_uri, m_stream, done_fn, failed_fn);
itr->add_failed_slot([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
@@ -238,9 +241,6 @@ DownloadFactory::receive_success() {
rpc::call_command("d.peers_max.set", rpc::call_command("throttle.max_peers.seed"), rpc::make_target(download)); rpc::call_command("d.peers_max.set", rpc::call_command("throttle.max_peers.seed"), rpc::make_target(download));
} }
if (!rpc::call_command_value("trackers.use_udp"))
download->enable_udp_trackers(false);
// Skip forcing trackers to scrape when rtorrent starts // Skip forcing trackers to scrape when rtorrent starts
if (m_initLoad && rpc::call_command_value("trackers.delay_scrape")) if (m_initLoad && rpc::call_command_value("trackers.delay_scrape"))
download->set_resume_flags(torrent::Download::start_skip_tracker); download->set_resume_flags(torrent::Download::start_skip_tracker);
@@ -272,7 +272,7 @@ 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); torrent::resume_load_addresses(*download->download(), resumeObject);
torrent::resume_load_file_priorities(*download->download(), resumeObject); torrent::resume_load_file_priorities(*download->download(), resumeObject);
+17 -8
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,
@@ -683,8 +687,10 @@ DownloadList::process_meta_download(Download* download) {
rpc::call_command("d.stop", torrent::Object(), rpc::make_target(download)); rpc::call_command("d.stop", torrent::Object(), rpc::make_target(download));
rpc::call_command("d.close", torrent::Object(), rpc::make_target(download)); rpc::call_command("d.close", torrent::Object(), rpc::make_target(download));
std::string metafile = (*download->file_list()->begin())->frozen_path(); std::string metafile = (*download->file_list()->begin())->frozen_path().str();
std::fstream file(metafile.c_str(), std::ios::in | std::ios::binary); std::fstream file(metafile.c_str(), std::ios::in | std::ios::binary);
if (!file.is_open()) { if (!file.is_open()) {
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not read download metadata."); lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not read download metadata.");
return; return;
@@ -692,11 +698,13 @@ DownloadList::process_meta_download(Download* download) {
torrent::Object* bencode = new torrent::Object(torrent::Object::create_map()); torrent::Object* bencode = new torrent::Object(torrent::Object::create_map());
file >> bencode->insert_key("info", torrent::Object()); file >> bencode->insert_key("info", torrent::Object());
if (file.fail()) { if (file.fail()) {
delete bencode; delete bencode;
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download, the input is not a valid torrent."); lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download, the input is not a valid torrent.");
return; return;
} }
file.close(); file.close();
// Steal the keys we still need. The old download has no use for them. // Steal the keys we still need. The old download has no use for them.
@@ -707,6 +715,7 @@ DownloadList::process_meta_download(Download* download) {
bencode->insert_key("announce-list", torrent::Object()).swap(download->bencode()->get_key("announce-list")); bencode->insert_key("announce-list", torrent::Object()).swap(download->bencode()->get_key("announce-list"));
erase_ptr(download); erase_ptr(download);
control->core()->try_create_download_from_meta_download(bencode, metafile); control->core()->try_create_download_from_meta_download(bencode, metafile);
} }
+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([this, itr]() { erase(itr); }); itr->add_done_slot(torrent::this_thread::thread(), std::move(done_fn));
itr->add_failed_slot([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();
+28 -91
View File
@@ -8,17 +8,15 @@
#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/error.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>
#include <torrent/net/http_stack.h> #include <torrent/net/http_stack.h>
#include <torrent/net/network_config.h>
#include <torrent/net/socket_address.h> #include <torrent/net/socket_address.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/network_manager.h> #include <torrent/runtime/network_manager.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/utils/string_manip.h> #include <torrent/utils/string_manip.h>
@@ -35,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;
@@ -65,6 +65,20 @@ Manager::~Manager() {
torrent::Throttle::destroy_throttle(m_throttles["NULL"].first); torrent::Throttle::destroy_throttle(m_throttles["NULL"].first);
} }
bool
Manager::is_download_shutdown_completed() {
for (const auto& download : *download_list()) {
// TODO: Why is this being checked?
// if (download->tracker_controller().is_active())
// return false;
if (download->tracker_controller().has_active_trackers_not_dht_scrape_disownable())
return false;
}
return true;
}
void void
Manager::set_hashing_view(View* v) { Manager::set_hashing_view(View* v) {
if (v == nullptr || m_hashingView != nullptr) if (v == nullptr || m_hashingView != nullptr)
@@ -74,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();
@@ -88,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);
@@ -153,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::config::network_config()->set_proxy_address(sa.get());
} catch (torrent::input_error& e) {
throw e;
} }
} }
@@ -394,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 ".."?
@@ -404,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));
@@ -500,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());
} }
} }
+12 -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;
@@ -35,32 +35,26 @@ public:
Manager(); Manager();
~Manager(); ~Manager();
DownloadList* download_list() { return m_download_list.get(); } bool is_download_shutdown_completed();
FileStatusCache* file_status_cache() { return m_file_status_cache.get(); }
HttpQueue* http_queue() { return m_http_queue.get(); } DownloadList* download_list() { return m_download_list.get(); }
FileStatusCache* file_status_cache() { return m_file_status_cache.get(); }
View* hashing_view() { return m_hashingView; } HttpQueue* http_queue() { return m_http_queue.get(); }
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);
@@ -85,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);
@@ -102,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 -1
View File
@@ -4,7 +4,9 @@
#include <string> #include <string>
#include <vector> #include <vector>
#if defined(HAVE_NCURSESW_CURSES_H) #if defined(HAVE_NO_NCURSES)
#include "curses_stub.h"
#elif defined(HAVE_NCURSESW_CURSES_H)
#include <ncursesw/curses.h> #include <ncursesw/curses.h>
#elif defined(HAVE_NCURSESW_H) #elif defined(HAVE_NCURSESW_H)
#include <ncursesw.h> #include <ncursesw.h>
+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 {
+4
View File
@@ -4,7 +4,11 @@
#include <array> #include <array>
#include <map> #include <map>
#if defined(HAVE_NO_NCURSES)
#include "curses_stub.h"
#else
#include <curses.h> #include <curses.h>
#endif
namespace display { namespace display {
+6
View File
@@ -0,0 +1,6 @@
// Dummy ncurses stub: provides the stdscr symbol expected by the codebase.
#include "curses_stub.h"
// Define stdscr as a null pointer; all ncurses calls are no-ops in this stub.
WINDOW* stdscr = nullptr;
+108
View File
@@ -0,0 +1,108 @@
// Dummy ncurses stub header for building rtorrent without ncurses.
// Provides types, macros, and no-op function stubs so the codebase
// compiles without a real curses library. All display functions become
// no-ops; use with system.daemon.set = true.
#ifndef RTORRENT_DISPLAY_CURSES_STUB_H
#define RTORRENT_DISPLAY_CURSES_STUB_H
#include <cstdarg>
#include <cstdio>
#include <cstddef>
// Opaque window type
typedef void* WINDOW;
// Character type used by curses
typedef unsigned int chtype;
typedef unsigned int attr_t;
// The standard screen
extern WINDOW* stdscr;
// Boolean constants and return codes
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef ERR
#define ERR (-1)
#endif
// Attribute constants
#define A_NORMAL 0
#define A_STANDOUT (1U << 8)
#define A_UNDERLINE (1U << 9)
#define A_REVERSE (1U << 10)
#define A_BLINK (1U << 11)
#define A_DIM (1U << 12)
#define A_BOLD (1U << 13)
// Color support
#define COLORS 0
#define COLOR_BLACK 0
#define COLOR_RED 1
#define COLOR_GREEN 2
#define COLOR_YELLOW 3
#define COLOR_BLUE 4
#define COLOR_MAGENTA 5
#define COLOR_CYAN 6
#define COLOR_WHITE 7
#define COLOR_PAIR(n) 0
// Key constants
#define KEY_DOWN 0x102
#define KEY_UP 0x103
#define KEY_LEFT 0x104
#define KEY_RIGHT 0x105
#define KEY_HOME 0x106
#define KEY_BACKSPACE 0x107
#define KEY_DC 0x14a
#define KEY_ENTER 0x157
#define KEY_NPAGE 0x152
#define KEY_PPAGE 0x153
#define KEY_END 0x168
#define KEY_MOUSE 0x199
#define KEY_RESIZE 0x19a
// Stubbed functions
inline WINDOW* initscr() { return nullptr; }
inline int endwin() { return 0; }
inline WINDOW* newwin(int, int, int, int) { return nullptr; }
inline int delwin(WINDOW*) { return 0; }
inline int wresize(WINDOW*, int, int) { return 0; }
inline int mvwin(WINDOW*, int, int) { return 0; }
inline int wnoutrefresh(WINDOW*) { return 0; }
inline int redrawwin(WINDOW*) { return 0; }
inline int werase(WINDOW*) { return 0; }
inline int wmove(WINDOW*, int, int) { return 0; }
inline int vw_printw(WINDOW*, char*, va_list) { return 0; }
inline int waddch(WINDOW*, const chtype) { return 0; }
inline int mvwaddch(WINDOW*, int, int, const chtype) { return 0; }
inline int mvwchgat(WINDOW*, int, int, int, attr_t, short, const void*) { return 0; }
inline int wattrset(WINDOW*, int) { return 0; }
inline int wattr_get(WINDOW*, attr_t*, short*, void*) { return 0; }
inline int wattr_set(WINDOW*, attr_t, short, void*) { return 0; }
inline int doupdate() { return 0; }
inline int start_color() { return 0; }
inline int use_default_colors() { return 0; }
inline int raw() { return 0; }
inline int noraw() { return 0; }
inline int noecho() { return 0; }
inline int nodelay(WINDOW*, bool) { return 0; }
inline int keypad(WINDOW*, bool) { return 0; }
inline int curs_set(int) { return 0; }
inline int init_pair(short, short, short) { return 0; }
inline int pair_content(short, short*, short*) { return 0; }
inline int resizeterm(int, int) { return 0; }
inline int getch() { return ERR; }
inline int set_escdelay(int) { return 0; }
// getyx / getmaxyx are macros in real ncurses; stub them as no-ops
#define getyx(w, y, x) do { (y) = 0; (x) = 0; } while (0)
#define getmaxyx(w, y, x) do { (y) = 24; (x) = 80; } while (0)
#endif // RTORRENT_DISPLAY_CURSES_STUB_H
+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));
} }
} }
+10 -9
View File
@@ -7,7 +7,6 @@
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/connection_manager.h>
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/throttle.h> #include <torrent/throttle.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
@@ -16,9 +15,11 @@
#include <torrent/data/file_manager.h> #include <torrent/data/file_manager.h>
#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/network_config.h>
#include <torrent/net/socket_address.h> #include <torrent/net/socket_address.h>
#include <torrent/peer/client_info.h> #include <torrent/peer/client_info.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/runtime.h>
#include <torrent/runtime/socket_manager.h>
#include "control.h" #include "control.h"
#include "globals.h" #include "globals.h"
@@ -154,7 +155,7 @@ print_download_status(char* first, char* last, core::Download* d) {
} else if (d->tracker_controller().has_active_trackers_not_scrape()) { } else if (d->tracker_controller().has_active_trackers_not_scrape()) {
auto tracker = d->tracker_controller().find_if([](const auto& t) { auto tracker = d->tracker_controller().find_if([](const auto& t) {
return t.is_busy_not_scrape(); return t.is_requesting_not_scrape();
}); });
if (tracker.is_valid()) { if (tracker.is_valid()) {
@@ -376,7 +377,7 @@ print_status_info(char* first, char* last) {
first = print_buffer(first, last, " KB]"); first = print_buffer(first, last, " KB]");
first = print_buffer(first, last, " [Port: %i]", (unsigned int)torrent::runtime::listen_port()); first = print_buffer(first, last, " [Port: %i]", (unsigned int)torrent::runtime::listen_port());
auto local_address = torrent::config::network_config()->local_address_best_match(); auto local_address = torrent::runtime::network_config()->local_address_best_match();
if (!torrent::sa_is_any(local_address.get())) { if (!torrent::sa_is_any(local_address.get())) {
first = print_buffer(first, last, " [Local "); first = print_buffer(first, last, " [Local ");
@@ -387,8 +388,8 @@ print_status_info(char* first, char* last) {
if (first > last) if (first > last)
throw torrent::internal_error("print_status_info(...) wrote past end of the buffer."); throw torrent::internal_error("print_status_info(...) wrote past end of the buffer.");
auto bind_inet_address = torrent::config::network_config()->bind_inet_address(); auto bind_inet_address = torrent::runtime::network_config()->bind_inet_address();
auto bind_inet6_address = torrent::config::network_config()->bind_inet6_address(); auto bind_inet6_address = torrent::runtime::network_config()->bind_inet6_address();
bool show_bind_inet = !torrent::sa_is_any(bind_inet_address.get()); bool show_bind_inet = !torrent::sa_is_any(bind_inet_address.get());
bool show_bind_inet6 = !torrent::sa_is_any(bind_inet6_address.get()); bool show_bind_inet6 = !torrent::sa_is_any(bind_inet6_address.get());
@@ -426,9 +427,9 @@ print_status_extra(char* first, char* last) {
torrent::net_thread::http_stack()->max_total_connections()); torrent::net_thread::http_stack()->max_total_connections());
first = print_buffer(first, last, " [S %i/%i/%i]", first = print_buffer(first, last, " [S %i/%i/%i]",
torrent::total_handshakes(), torrent::runtime::total_handshakes(),
torrent::connection_manager()->size(), torrent::runtime::socket_manager()->size(),
torrent::connection_manager()->max_size()); torrent::runtime::socket_manager()->max_size());
first = print_buffer(first, last, " [F %i/%i]", first = print_buffer(first, last, " [F %i/%i]",
torrent::file_manager()->open_files(), torrent::file_manager()->open_files(),
+11 -4
View File
@@ -135,8 +135,11 @@ WindowFileList::redraw() {
m_canvas->print(0, pos, "%*c%-*s", 16, ' ', filenameWidth, "EMPTY"); m_canvas->print(0, pos, "%*c%-*s", 16, ' ', filenameWidth, "EMPTY");
} else if (itr.is_entering()) { } else if (itr.is_entering()) {
m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\', if (itr.depth() < itr.file()->path()->size())
itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN"); m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\',
wstring_width(itr.file()->path()->at(itr.depth()).str(), filenameWidth - itr.depth() - 1).c_str());
else
m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\', L"UNKNOWN");
} else if (itr.is_leaving()) { } else if (itr.is_leaving()) {
m_canvas->print(0, pos, "%*c %-*s", 16 + (itr.depth() - 1), '/', filenameWidth - (itr.depth() - 1), ""); m_canvas->print(0, pos, "%*c %-*s", 16 + (itr.depth() - 1), '/', filenameWidth - (itr.depth() - 1), "");
@@ -166,8 +169,12 @@ WindowFileList::redraw() {
else else
m_canvas->print(8, pos, "%5.1f T", (double)val / (int64_t(1) << 40)); m_canvas->print(8, pos, "%5.1f T", (double)val / (int64_t(1) << 40));
m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|', if (itr.depth() < itr.file()->path()->size())
itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN"); m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|',
wstring_width(itr.file()->path()->at(itr.depth()).str(), filenameWidth - itr.depth() - 1).c_str());
else
m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|', L"UNKNOWN");
} else { } else {
m_canvas->print(0, pos, "BORK BORK"); m_canvas->print(0, pos, "BORK BORK");
+12 -7
View File
@@ -2,6 +2,7 @@
#include <ctime> #include <ctime>
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/system/callbacks.h>
#include <torrent/system/thread.h> #include <torrent/system/thread.h>
#include "canvas.h" #include "canvas.h"
@@ -14,16 +15,18 @@ WindowLog::WindowLog(torrent::log_buffer* l) :
Window(new Canvas, 0, 0, 0, extent_full, extent_static), Window(new Canvas, 0, 0, 0, extent_full, extent_static),
m_log(l) { m_log(l) {
m_task_update.slot() = std::bind(&WindowLog::receive_update, this); m_task_update.slot() = [this]() { receive_update(); };
unsigned int signal_index = torrent::main_thread::thread()->signal_bitfield()->add_signal(std::bind(&WindowLog::receive_update, this)); m_log->lock_and_set_update_slot([this]() {
if (m_log_updating.exchange(true) == true)
return;
m_log->lock_and_set_update_slot([signal_index]() { torrent::main_thread::callback([this]() { receive_update(); });
torrent::main_thread::thread()->send_event_signal(signal_index, false); });
});
} }
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);
} }
@@ -52,11 +55,13 @@ WindowLog::redraw() {
// gets updated. // gets updated.
void void
WindowLog::receive_update() { WindowLog::receive_update() {
m_log_updating = false;
if (!is_active()) if (!is_active())
return; return;
iterator itr = find_older(); auto itr = find_older();
extent_type height = std::min(std::distance(itr, (iterator)m_log->end()), (std::iterator_traits<iterator>::difference_type)10); auto height = std::min(std::distance(itr, (iterator)m_log->end()), (std::iterator_traits<iterator>::difference_type)10);
if (height != m_max_height) { if (height != m_max_height) {
m_min_height = height != 0 ? 1 : 0; m_min_height = height != 0 ? 1 : 0;
+2
View File
@@ -24,6 +24,8 @@ private:
torrent::log_buffer* m_log; torrent::log_buffer* m_log;
torrent::utils::SchedulerEntry m_task_update; torrent::utils::SchedulerEntry m_task_update;
align_cacheline std::atomic<bool> m_log_updating{};
}; };
} }
+3 -3
View File
@@ -51,9 +51,9 @@ WindowTrackerList::redraw() {
if (pos < m_canvas->height()) { if (pos < m_canvas->height()) {
const char* state; const char* state;
if (tracker.is_busy_not_scrape()) if (tracker.is_requesting_not_scrape())
state = "req "; state = "req ";
else if (tracker.is_busy()) else if (tracker.is_requesting())
state = "scr "; state = "scr ";
else else
state = " "; state = " ";
@@ -79,7 +79,7 @@ WindowTrackerList::redraw() {
m_canvas->set_attr(4, pos - 1, m_canvas->width(), is_focused() ? A_REVERSE : A_BOLD, COLOR_PAIR(0)); m_canvas->set_attr(4, pos - 1, m_canvas->width(), is_focused() ? A_REVERSE : A_BOLD, COLOR_PAIR(0));
} }
if (tracker.is_busy()) { if (tracker.is_requesting()) {
m_canvas->set_attr(0, pos - 2, 4, A_REVERSE, COLOR_PAIR(0)); m_canvas->set_attr(0, pos - 2, 4, A_REVERSE, COLOR_PAIR(0));
m_canvas->set_attr(0, pos - 1, 4, A_REVERSE, COLOR_PAIR(0)); m_canvas->set_attr(0, pos - 1, 4, A_REVERSE, COLOR_PAIR(0));
} }
+7 -9
View File
@@ -25,13 +25,11 @@ namespace scgi_thread {
torrent::system::Thread* thread(); torrent::system::Thread* thread();
std::thread::id thread_id(); std::thread::id thread_id();
void callback(void* target, std::function<void ()>&& fn); void callback_interrupt(torrent::system::callback_id& id, std::function<void ()>&& fn);
void cancel_callback(void* target);
void cancel_callback_and_wait(void* target);
rpc::SCgi* scgi(); rpc::SCgi* scgi();
void set_scgi(rpc::SCgi* scgi); void set_scgi(rpc::SCgi* scgi);
void set_rpc_log(const std::string& filename); void set_rpc_log(const std::string& filename);
} // namespace torrent::scgi_thread } // namespace torrent::scgi_thread
@@ -41,9 +39,9 @@ namespace session_thread {
torrent::system::Thread* thread(); torrent::system::Thread* thread();
std::thread::id thread_id(); std::thread::id thread_id();
void callback(void* target, std::function<void ()>&& fn); void callback(std::function<void ()>&& fn);
void cancel_callback(void* target); void callback(torrent::system::callback_id& id, std::function<void ()>&& fn);
void cancel_callback_and_wait(void* target); void cancel_callback(torrent::system::callback_id& id);
session::SessionManager* manager(); session::SessionManager* manager();
std::string session_path(); std::string session_path();
+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::net::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::net::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/net/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::net::Poll* p); void insert();
void remove(torrent::net::Poll* p); void remove();
void event_read() override; void event_read() override;
void event_write() override; void event_write() override;
+143 -100
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,7 +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();
SignalHandler::set_ignore(SIGPIPE); // Block SIGCHLD until all threads are created, then unblock on main-thread, to avoid SIGCHLD
// interrupting other threads.
//
// This means only main-thread can fork and wait for child processes.
SignalHandler::set_block(SIGALRM);
SignalHandler::set_block(SIGPIPE);
SignalHandler::set_block(SIGCHLD);
// All signal handlers must restore errno if they return.
SignalHandler::set_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));
@@ -140,18 +152,8 @@ main(int argc, char** argv) {
SignalHandler::set_sigaction_handler(SIGBUS, &handle_sigbus); SignalHandler::set_sigaction_handler(SIGBUS, &handle_sigbus);
// SIGUSR1 is used for interrupting polling, forcing the target torrent::log_add_group_output(torrent::LOG_NOTICE, "important");
// thread to process new non-socket events. torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "important");
//
// LibTorrent uses sockets for this purpose on Solaris and other
// platforms that do not properly pass signals to the target
// threads. Use '--enable-interrupt-socket' when configuring
// LibTorrent to enable this workaround.
if (torrent::system::Thread::should_handle_sigusr1())
SignalHandler::set_handler(SIGUSR1, []() {});
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_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");
@@ -160,11 +162,13 @@ main(int argc, char** argv) {
initialize_rpc_slots(); initialize_rpc_slots();
torrent::initialize(); torrent::initialize();
torrent::set_main_thread_slots(std::bind(&client_perform)); torrent::main_thread::set_client_callback(&client_perform);
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();
@@ -209,7 +213,14 @@ main(int argc, char** argv) {
"method.insert = event.download.hash_queued,multi|rlookup|static\n" "method.insert = event.download.hash_queued,multi|rlookup|static\n"
"method.set_key = event.download.inserted, 1_send_scrape, ((d.tracker.send_scrape,30))\n" "method.set_key = event.download.inserted, 1_send_scrape, ((d.tracker.send_scrape,30))\n"
"method.set_key = event.download.inserted_new, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) ),(d.save_full_session)}\n" "method.set_key = event.download.inserted_new, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) )}\n"
// d.save_full_session runs LAST (~ prefix is ASCII 0x7E, sorts after all alphanumerics,
// matching the existing ~_delete_tied precedent on event.download.erased) so the on-disk
// snapshot includes custom fields written by other inserted_new handlers (addtime,
// seedingtime, etc.). Was previously inside 1_prepare alongside view setup, which
// snapshotted bencode before later handlers ran, so d.set_custom=addtime did not reach
// .rtorrent until the next periodic resume save — a window where rtorrent restart lost them.
"method.set_key = event.download.inserted_new, ~_save_full, ((d.save_full_session))\n"
"method.set_key = event.download.inserted_session, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) )}\n" "method.set_key = event.download.inserted_session, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) )}\n"
"method.set_key = event.download.inserted, 1_prioritize_toc, \"branch=file.prioritize_toc=,{\\\"f.multicall=(file.prioritize_toc.first),f.prioritize_first.enable=\\\",\\\"f.multicall=(file.prioritize_toc.last),f.prioritize_last.enable=\\\",d.update_priorities=}\"\n" "method.set_key = event.download.inserted, 1_prioritize_toc, \"branch=file.prioritize_toc=,{\\\"f.multicall=(file.prioritize_toc.first),f.prioritize_first.enable=\\\",\\\"f.multicall=(file.prioritize_toc.last),f.prioritize_last.enable=\\\",d.update_priorities=}\"\n"
@@ -283,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"
); );
@@ -298,101 +307,131 @@ 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");
CMD_REDIRECT("port_range", "network.port_range.set");
// TODO: Check if dht is on by default.
CMD_REDIRECT("dht", "dht.mode.set");
CMD_REDIRECT("port_random", "network.port_random.set");
CMD_REDIRECT("proxy_address", "network.proxy_address.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);
@@ -419,20 +458,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; }; \
+51 -179
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,190 +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;
}
torrent::Object
ExecFile::execute_object(const torrent::Object& rawArgs, int flags) {
char* argsBuffer[max_args];
char** argsCurrent = argsBuffer;
// Size of value strings are less than 24.
char valueBuffer[buffer_size+1];
char* valueCurrent = valueBuffer;
if (rawArgs.is_list()) {
const torrent::Object::list_type& args = rawArgs.as_list();
if (args.empty())
throw torrent::input_error("Too few arguments.");
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++, argsCurrent++) {
if (argsCurrent == argsBuffer + max_args - 1)
throw torrent::input_error("Too many arguments.");
if (itr->is_string() && (!(flags & flag_expand_tilde) || *itr->as_string().c_str() != '~')) {
*argsCurrent = const_cast<char*>(itr->as_string().c_str());
} else {
*argsCurrent = valueCurrent;
valueCurrent = print_object(valueCurrent, valueBuffer + buffer_size, &*itr, flags) + 1;
if (valueCurrent >= valueBuffer + buffer_size)
throw torrent::input_error("Overflowed execute arg buffer.");
}
}
} else {
const torrent::Object::string_type& args = rawArgs.as_string();
if ((flags & flag_expand_tilde) && args.c_str()[0] == '~') {
*argsCurrent = valueCurrent;
valueCurrent = print_object(valueCurrent, valueBuffer + buffer_size, &rawArgs, flags) + 1;
} else {
*argsCurrent = const_cast<char*>(args.c_str());
}
argsCurrent++;
}
*argsCurrent = NULL;
int status = execute(argsBuffer[0], argsBuffer, flags);
if ((flags & flag_throw) && status != 0)
throw torrent::input_error("Bad return code.");
if (flags & flag_capture) if (flags & flag_capture)
return m_capture; m_capture = spawn_process.capture_child_output();
return torrent::Object((int64_t)status); return spawn_process.wait_for_child();
} }
} } // 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;
}; };
} }
+34 -5
View File
@@ -6,6 +6,7 @@
#include <string> #include <string>
#include <torrent/common.h> #include <torrent/common.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/utils/string_manip.h>
#include "rpc/rpc_manager.h" #include "rpc/rpc_manager.h"
#include "rpc/command.h" #include "rpc/command.h"
@@ -33,24 +34,29 @@ json_to_object(const json& value) {
case json::value_t::number_unsigned: case json::value_t::number_unsigned:
case json::value_t::number_integer: case json::value_t::number_integer:
return torrent::Object(value.get<int64_t>()); return torrent::Object(value.get<int64_t>());
case json::value_t::boolean: case json::value_t::boolean:
return value.get<bool>() ? torrent::Object(int64_t(1)) : torrent::Object(int64_t(0)); return value.get<bool>() ? torrent::Object(int64_t(1)) : torrent::Object(int64_t(0));
case json::value_t::string: case json::value_t::string:
return torrent::Object(value.get<std::string>()); return torrent::Object(value.get<std::string>());
case json::value_t::array: { case json::value_t::array: {
auto array_raw = torrent::Object::create_list(); auto array_raw = torrent::Object::create_list();
auto& array = array_raw.as_list(); auto& array = array_raw.as_list();
for (const auto& entry : value) {
for (const auto& entry : value)
array.push_back(json_to_object(entry)); array.push_back(json_to_object(entry));
}
return array_raw; return array_raw;
} }
case json::value_t::object: { case json::value_t::object: {
auto map_raw = torrent::Object::create_map(); auto map_raw = torrent::Object::create_map();
auto& map = map_raw.as_map(); auto& map = map_raw.as_map();
for (const auto& entry : value.items()) {
for (const auto& entry : value.items())
map[entry.key()] = json_to_object(entry.value()); map[entry.key()] = json_to_object(entry.value());
}
return map_raw; return map_raw;
} }
case json::value_t::number_float: case json::value_t::number_float:
@@ -61,17 +67,38 @@ json_to_object(const json& value) {
} }
} }
// Note: Only throw rpc_errors here.
json json
object_to_json(const torrent::Object& object) noexcept { object_to_json(const torrent::Object& object) {
switch (object.type()) { switch (object.type()) {
case torrent::Object::TYPE_VALUE: case torrent::Object::TYPE_VALUE:
return object.as_value(); return object.as_value();
case torrent::Object::TYPE_STRING: case torrent::Object::TYPE_STRING:
if (object.flags() & torrent::Object::flag_as_binary) {
// We should optimize our imported json library to support copying base64 strings directly.
if (object.flags() & torrent::Object::flag_base64) {
auto binary_data = torrent::utils::transform_from_base64_unsafe(object.as_string());
if (!binary_data.has_value())
throw rpc_error(JSONRPC_INTERNAL_ERROR, "invalid base64 string in base64-as-binary object");
return json::binary(*binary_data);
}
return json::binary({object.as_string().begin(), object.as_string().end()});
}
return object.as_string(); return object.as_string();
case torrent::Object::TYPE_LIST: { case torrent::Object::TYPE_LIST: {
json result = json::array(); json result = json::array();
for (const auto& obj : object.as_list()) for (const auto& obj : object.as_list())
result.push_back(object_to_json(obj)); result.push_back(object_to_json(obj));
return result; return result;
} }
case torrent::Object::TYPE_MAP: { case torrent::Object::TYPE_MAP: {
@@ -135,7 +162,9 @@ jsonrpc_call_command(const std::string& method, const json& params) {
try { try {
const auto& result = rpc::commands.call_command(itr, params_object, target); const auto& result = rpc::commands.call_command(itr, params_object, target);
return object_to_json(result); return object_to_json(result);
} catch (untrusted_error& e) { } catch (untrusted_error& e) {
throw rpc_error(JSONRPC_METHOD_NOT_FOUND_ERROR, e.what()); throw rpc_error(JSONRPC_METHOD_NOT_FOUND_ERROR, e.what());
} }
+5 -1
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>
@@ -211,7 +215,7 @@ object_to_target(const torrent::Object& obj, int call_flags, rpc::target_type* t
auto tracker = new torrent::tracker::Tracker(rpc.slot_find_tracker()(download, std::stoi(std::string(index)))); auto tracker = new torrent::tracker::Tracker(rpc.slot_find_tracker()(download, std::stoi(std::string(index))));
*deleter = [tracker]() { delete tracker; }; *deleter = [tracker]() { delete tracker; };
*target = rpc::make_target(command_base::target_tracker, target); *target = rpc::make_target(command_base::target_tracker, tracker);
} }
break; break;
+234 -135
View File
@@ -1,9 +1,9 @@
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
/****************************************************************************\ /****************************************************************************\
@@ -34,10 +34,10 @@
// #include <nlohmann/adl_serializer.hpp> // #include <nlohmann/adl_serializer.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -47,10 +47,10 @@
// #include <nlohmann/detail/abi_macros.hpp> // #include <nlohmann/detail/abi_macros.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -59,15 +59,15 @@
#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK #ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
#if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
#if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3 #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0
#warning "Already included a different version of the library!" #warning "Already included a different version of the library!"
#endif #endif
#endif #endif
#endif #endif
#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) #define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) #define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum) #define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum)
#ifndef JSON_DIAGNOSTICS #ifndef JSON_DIAGNOSTICS
#define JSON_DIAGNOSTICS 0 #define JSON_DIAGNOSTICS 0
@@ -160,10 +160,10 @@
// #include <nlohmann/detail/conversions/from_json.hpp> // #include <nlohmann/detail/conversions/from_json.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -186,10 +186,10 @@
// #include <nlohmann/detail/exceptions.hpp> // #include <nlohmann/detail/exceptions.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -206,10 +206,10 @@
// #include <nlohmann/detail/value_t.hpp> // #include <nlohmann/detail/value_t.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -222,10 +222,10 @@
// #include <nlohmann/detail/macro_scope.hpp> // #include <nlohmann/detail/macro_scope.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -234,10 +234,10 @@
// #include <nlohmann/detail/meta/detected.hpp> // #include <nlohmann/detail/meta/detected.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -247,10 +247,10 @@
// #include <nlohmann/detail/meta/void_t.hpp> // #include <nlohmann/detail/meta/void_t.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -334,10 +334,10 @@ NLOHMANN_JSON_NAMESPACE_END
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2016 - 2021 Evan Nemerson <evan@nemerson.com> // SPDX-FileCopyrightText: 2016 - 2021 Evan Nemerson <evan@nemerson.com>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -2398,15 +2398,20 @@ JSON_HEDLEY_DIAGNOSTIC_POP
// C++ language standard detection // C++ language standard detection
// if the user manually specified the used c++ version this is skipped // if the user manually specified the used c++ version this is skipped
#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) #if !defined(JSON_HAS_CPP_23) && !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) #if (defined(__cplusplus) && __cplusplus > 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG > 202002L)
#define JSON_HAS_CPP_23
#define JSON_HAS_CPP_20 #define JSON_HAS_CPP_20
#define JSON_HAS_CPP_17 #define JSON_HAS_CPP_17
#define JSON_HAS_CPP_14 #define JSON_HAS_CPP_14
#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 #elif (defined(__cplusplus) && __cplusplus > 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L)
#define JSON_HAS_CPP_20
#define JSON_HAS_CPP_17 #define JSON_HAS_CPP_17
#define JSON_HAS_CPP_14 #define JSON_HAS_CPP_14
#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) #elif (defined(__cplusplus) && __cplusplus > 201402L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
#define JSON_HAS_CPP_17
#define JSON_HAS_CPP_14
#elif (defined(__cplusplus) && __cplusplus > 201103L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
#define JSON_HAS_CPP_14 #define JSON_HAS_CPP_14
#endif #endif
// the cpp 11 flag is always specified because it is the minimal required version // the cpp 11 flag is always specified because it is the minimal required version
@@ -2767,88 +2772,140 @@ JSON_HEDLEY_DIAGNOSTIC_POP
@brief macro @brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE @def NLOHMANN_DEFINE_TYPE_INTRUSIVE
@since version 3.9.0 @since version 3.9.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/ */
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ #define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
/*! /*!
@brief macro @brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT @def NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
@since version 3.11.0 @since version 3.11.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/ */
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
/*! /*!
@brief macro @brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE @def NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE
@since version 3.11.x @since version 3.11.3
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/ */
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
/*! /*!
@brief macro @brief macro
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
@since version 3.9.0 @since version 3.9.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/ */
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
/*! /*!
@brief macro @brief macro
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT
@since version 3.11.0 @since version 3.11.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/ */
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
/*! /*!
@brief macro @brief macro
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE
@since version 3.11.x @since version 3.11.3
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/ */
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
/*! /*!
@brief macro @brief macro
@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE @def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE
@since version 3.11.x @since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/ */
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Type, BaseType, ...) \ #define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Type, BaseType, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \ #define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \ #define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
/*! /*!
@brief macro @brief macro
@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE @def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE
@since version 3.11.x @since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/ */
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(Type, BaseType, ...) \ #define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(Type, BaseType, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \ #define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \ #define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
// inspired from https://stackoverflow.com/a/26745591 // inspired from https://stackoverflow.com/a/26745591
// allows to call any std function as if (e.g. with begin): // allows calling any std function as if (e.g., with begin):
// using std::begin; begin(x); // using std::begin; begin(x);
// //
// it allows using the detected idiom to retrieve the return type // it allows using the detected idiom to retrieve the return type
@@ -3009,10 +3066,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/string_escape.hpp> // #include <nlohmann/detail/string_escape.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -3084,10 +3141,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/position_t.hpp> // #include <nlohmann/detail/input/position_t.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -3126,10 +3183,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/cpp_future.hpp> // #include <nlohmann/detail/meta/cpp_future.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2018 The Abseil Authors // SPDX-FileCopyrightText: 2018 The Abseil Authors
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -3300,10 +3357,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/type_traits.hpp> // #include <nlohmann/detail/meta/type_traits.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -3317,10 +3374,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/iterator_traits.hpp> // #include <nlohmann/detail/iterators/iterator_traits.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -3385,10 +3442,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/call_std/begin.hpp> // #include <nlohmann/detail/meta/call_std/begin.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -3405,10 +3462,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/call_std/end.hpp> // #include <nlohmann/detail/meta/call_std/end.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -3429,10 +3486,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/json_fwd.hpp> // #include <nlohmann/json_fwd.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
@@ -4280,10 +4337,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/string_concat.hpp> // #include <nlohmann/detail/string_concat.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -4534,16 +4591,34 @@ class exception : public std::exception
{ {
return concat(a, '/', detail::escape(b)); return concat(a, '/', detail::escape(b));
}); });
return concat('(', str, ") ");
return concat('(', str, ") ", get_byte_positions(leaf_element));
#else #else
static_cast<void>(leaf_element); return get_byte_positions(leaf_element);
return "";
#endif #endif
} }
private: private:
/// an exception object as storage for error messages /// an exception object as storage for error messages
std::runtime_error m; std::runtime_error m;
#if JSON_DIAGNOSTIC_POSITIONS
template<typename BasicJsonType>
static std::string get_byte_positions(const BasicJsonType* leaf_element)
{
if ((leaf_element->start_pos() != std::string::npos) && (leaf_element->end_pos() != std::string::npos))
{
return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") ");
}
return "";
}
#else
template<typename BasicJsonType>
static std::string get_byte_positions(const BasicJsonType* leaf_element)
{
static_cast<void>(leaf_element);
return "";
}
#endif
}; };
/// @brief exception indicating a parse error /// @brief exception indicating a parse error
@@ -4682,10 +4757,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/identity_tag.hpp> // #include <nlohmann/detail/meta/identity_tag.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -4706,10 +4781,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/std_fs.hpp> // #include <nlohmann/detail/meta/std_fs.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -5158,6 +5233,12 @@ std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<
return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...); return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);
} }
template<typename BasicJsonType>
std::tuple<> from_json_tuple_impl_base(BasicJsonType& /*unused*/, index_sequence<> /*unused*/)
{
return {};
}
template < typename BasicJsonType, class A1, class A2 > template < typename BasicJsonType, class A1, class A2 >
std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/) std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
{ {
@@ -5243,7 +5324,12 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p)
{ {
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
} }
p = *j.template get_ptr<const typename BasicJsonType::string_t*>(); const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
#ifdef JSON_HAS_CPP_20
p = std_fs::path(std::u8string_view(reinterpret_cast<const char8_t*>(s.data()), s.size()));
#else
p = std_fs::u8path(s); // accepts UTF-8 encoded std::string in C++17, deprecated in C++20
#endif
} }
#endif #endif
@@ -5278,10 +5364,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/conversions/to_json.hpp> // #include <nlohmann/detail/conversions/to_json.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -5304,10 +5390,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/iteration_proxy.hpp> // #include <nlohmann/detail/iterators/iteration_proxy.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -5328,10 +5414,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/string_utils.hpp> // #include <nlohmann/detail/string_utils.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -5987,6 +6073,13 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<
j = { std::get<Idx>(t)... }; j = { std::get<Idx>(t)... };
} }
template<typename BasicJsonType, typename Tuple>
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/)
{
using array_t = typename BasicJsonType::array_t;
j = array_t();
}
template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0> template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
inline void to_json(BasicJsonType& j, const T& t) inline void to_json(BasicJsonType& j, const T& t)
{ {
@@ -5997,7 +6090,12 @@ inline void to_json(BasicJsonType& j, const T& t)
template<typename BasicJsonType> template<typename BasicJsonType>
inline void to_json(BasicJsonType& j, const std_fs::path& p) inline void to_json(BasicJsonType& j, const std_fs::path& p)
{ {
j = p.string(); #ifdef JSON_HAS_CPP_20
const std::u8string s = p.u8string();
j = std::string(s.begin(), s.end());
#else
j = p.u8string(); // returns std::string in C++17
#endif
} }
#endif #endif
@@ -6072,10 +6170,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/byte_container_with_subtype.hpp> // #include <nlohmann/byte_container_with_subtype.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -6184,10 +6282,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/hash.hpp> // #include <nlohmann/detail/hash.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -6317,10 +6415,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/binary_reader.hpp> // #include <nlohmann/detail/input/binary_reader.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -6346,10 +6444,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/input_adapters.hpp> // #include <nlohmann/detail/input/input_adapters.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -6901,10 +6999,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/json_sax.hpp> // #include <nlohmann/detail/input/json_sax.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -6920,10 +7018,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/lexer.hpp> // #include <nlohmann/detail/input/lexer.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -8171,7 +8269,7 @@ scan_number_done:
// we are done scanning a number) // we are done scanning a number)
unget(); unget();
char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
errno = 0; errno = 0;
// try to parse integers first and fall back to floats // try to parse integers first and fall back to floats
@@ -9544,10 +9642,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/is_sax.hpp> // #include <nlohmann/detail/meta/is_sax.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -12752,10 +12850,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/parser.hpp> // #include <nlohmann/detail/input/parser.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -13281,10 +13379,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/internal_iterator.hpp> // #include <nlohmann/detail/iterators/internal_iterator.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -13294,10 +13392,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/primitive_iterator.hpp> // #include <nlohmann/detail/iterators/primitive_iterator.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -13453,10 +13551,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/iter_impl.hpp> // #include <nlohmann/detail/iterators/iter_impl.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -14224,10 +14322,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp> // #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -14359,10 +14457,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/json_custom_base_class.hpp> // #include <nlohmann/detail/json_custom_base_class.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -14401,10 +14499,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/json_pointer.hpp> // #include <nlohmann/detail/json_pointer.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -14634,7 +14732,7 @@ class json_pointer
} }
const char* p = s.c_str(); const char* p = s.c_str();
char* p_end = nullptr; char* p_end = nullptr; // NOLINT(misc-const-correctness)
errno = 0; // strtoull doesn't reset errno errno = 0; // strtoull doesn't reset errno
const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
if (p == p_end // invalid input or empty string if (p == p_end // invalid input or empty string
@@ -15156,7 +15254,7 @@ class json_pointer
// iterate array and use index as reference string // iterate array and use index as reference string
for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i) for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i)
{ {
flatten(detail::concat(reference_string, '/', std::to_string(i)), flatten(detail::concat<string_t>(reference_string, '/', std::to_string(i)),
value.m_data.m_value.array->operator[](i), result); value.m_data.m_value.array->operator[](i), result);
} }
} }
@@ -15175,7 +15273,7 @@ class json_pointer
// iterate object and use keys as reference string // iterate object and use keys as reference string
for (const auto& element : *value.m_data.m_value.object) for (const auto& element : *value.m_data.m_value.object)
{ {
flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result); flatten(detail::concat<string_t>(reference_string, '/', detail::escape(element.first)), element.second, result);
} }
} }
break; break;
@@ -15396,10 +15494,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/json_ref.hpp> // #include <nlohmann/detail/json_ref.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -15490,10 +15588,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/output/binary_writer.hpp> // #include <nlohmann/detail/output/binary_writer.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -15516,10 +15614,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/output/output_adapters.hpp> // #include <nlohmann/detail/output/output_adapters.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -17496,11 +17594,11 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/output/serializer.hpp> // #include <nlohmann/detail/output/serializer.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2008 - 2009 Björn Hoehrmann <bjoern@hoehrmann.de> // SPDX-FileCopyrightText: 2008 - 2009 Björn Hoehrmann <bjoern@hoehrmann.de>
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -17521,11 +17619,11 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/conversions/to_chars.hpp> // #include <nlohmann/detail/conversions/to_chars.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2009 Florian Loitsch <https://florian.loitsch.com/> // SPDX-FileCopyrightText: 2009 Florian Loitsch <https://florian.loitsch.com/>
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -19617,10 +19715,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/ordered_map.hpp> // #include <nlohmann/ordered_map.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -20168,7 +20266,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{ {
basic_json result; basic_json result;
result["copyright"] = "(C) 2013-2023 Niels Lohmann"; result["copyright"] = "(C) 2013-2025 Niels Lohmann";
result["name"] = "JSON for Modern C++"; result["name"] = "JSON for Modern C++";
result["url"] = "https://github.com/nlohmann/json"; result["url"] = "https://github.com/nlohmann/json";
result["version"]["string"] = result["version"]["string"] =
@@ -20433,7 +20531,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
object = nullptr; // silence warning, see #821 object = nullptr; // silence warning, see #821
if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
{ {
JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.11.3", nullptr)); // LCOV_EXCL_LINE JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE
} }
break; break;
} }
@@ -25220,10 +25318,10 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC
// #include <nlohmann/detail/macro_unscope.hpp> // #include <nlohmann/detail/macro_unscope.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -25254,6 +25352,7 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC
#undef JSON_HAS_CPP_14 #undef JSON_HAS_CPP_14
#undef JSON_HAS_CPP_17 #undef JSON_HAS_CPP_17
#undef JSON_HAS_CPP_20 #undef JSON_HAS_CPP_20
#undef JSON_HAS_CPP_23
#undef JSON_HAS_FILESYSTEM #undef JSON_HAS_FILESYSTEM
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
#undef JSON_HAS_THREE_WAY_COMPARISON #undef JSON_HAS_THREE_WAY_COMPARISON
@@ -25265,10 +25364,10 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC
// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp> // #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3 // | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json // |_____|_____|_____|_|___| https://github.com/nlohmann/json
// //
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me> // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
+1 -1
View File
@@ -17,7 +17,7 @@ namespace rpc {
struct object_storage_node { struct object_storage_node {
torrent::Object object; torrent::Object object;
char flags; unsigned int flags;
}; };
typedef std::unordered_map<fixed_key_type<64>, object_storage_node, hash_fixed_key_type> object_storage_base_type; typedef std::unordered_map<fixed_key_type<64>, object_storage_node, hash_fixed_key_type> object_storage_base_type;
+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);
+15 -6
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,11 +105,12 @@ 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};
std::atomic<bool> m_scgi_allow_compression{false}; bool m_use_jsonrpc{true};
std::atomic<unsigned int> m_scgi_min_compress_size{0}; bool m_use_xmlrpc{true};
std::atomic<bool> m_scgi_allow_compression{true};
std::atomic<unsigned int> m_scgi_min_compress_size{1000};
slot_download m_slot_find_download; slot_download m_slot_find_download;
slot_file m_slot_find_file; slot_file m_slot_find_file;
@@ -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
+50 -58
View File
@@ -5,12 +5,11 @@
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <sys/un.h> #include <sys/un.h>
#include <torrent/connection_manager.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/net/fd.h> #include <torrent/net/fd.h>
#include <torrent/net/poll.h>
#include <torrent/net/socket_address.h> #include <torrent/net/socket_address.h>
#include <torrent/system/poll.h>
#include <torrent/runtime/socket_manager.h> #include <torrent/runtime/socket_manager.h>
#include "control.h" #include "control.h"
@@ -34,64 +33,60 @@ SCgi::~SCgi() {
void void
SCgi::open_port(sockaddr* sa, unsigned int length, bool dont_route) { SCgi::open_port(sockaddr* sa, unsigned int length, bool dont_route) {
torrent::runtime::socket_manager()->open_event_or_throw(this, [&]() { int fd = torrent::fd_open_family(torrent::fd_flag_stream | torrent::fd_flag_nonblock | torrent::fd_flag_reuse_address,
int fd = torrent::fd_open_family(torrent::fd_flag_stream | torrent::fd_flag_nonblock | torrent::fd_flag_reuse_address, sa->sa_family);
sa->sa_family);
if (fd == -1) if (fd == -1)
throw torrent::resource_error("Could not open socket for listening: " + std::string(std::strerror(errno))); throw torrent::resource_error("Could not open socket for listening: " + std::string(std::strerror(errno)));
if (dont_route && !torrent::fd_set_dont_route(fd, true)) { if (dont_route && !torrent::fd_set_dont_route(fd, true)) {
torrent::fd_close(fd); torrent::fd_close(fd);
throw torrent::resource_error("Could not set socket option IP_DONTROUTE: " + std::string(std::strerror(errno))); throw torrent::resource_error("Could not set socket option IP_DONTROUTE: " + std::string(std::strerror(errno)));
} }
set_file_descriptor(fd); set_file_descriptor(fd);
open(reinterpret_cast<sockaddr*>(sa), length); open(reinterpret_cast<sockaddr*>(sa), length);
});
torrent::connection_manager()->inc_socket_count(); torrent::runtime::socket_manager()->register_event_or_throw(this, torrent::runtime::category_rpc, []() {});
} }
void void
SCgi::open_named(const std::string& filename) { SCgi::open_named(const std::string& filename) {
if (filename.empty() || filename.size() > 4096) if (filename.empty() || filename.size() >= sizeof(sockaddr_un::sun_path))
throw torrent::resource_error("Invalid filename length."); throw torrent::resource_error("Invalid filename length.");
auto buffer = std::make_unique<char[]>(sizeof(sockaddr_un) + filename.size() + 1); auto buffer = std::make_unique<char[]>(sizeof(sockaddr_un) + filename.size() + 1);
sockaddr_un* sa = reinterpret_cast<sockaddr_un*>(buffer.get()); sockaddr_un* sa = reinterpret_cast<sockaddr_un*>(buffer.get());
sa->sun_family = AF_LOCAL; sa->sun_family = AF_LOCAL;
std::memcpy(sa->sun_path, filename.c_str(), filename.size() + 1); std::memcpy(sa->sun_path, filename.c_str(), filename.size() + 1);
torrent::runtime::socket_manager()->open_event_or_throw(this, [&]() { int fd = torrent::fd_open_local(torrent::fd_flag_stream | torrent::fd_flag_nonblock | torrent::fd_flag_reuse_address);
int fd = torrent::fd_open_local(torrent::fd_flag_stream | torrent::fd_flag_nonblock | torrent::fd_flag_reuse_address);
if (fd == -1) if (fd == -1)
throw torrent::resource_error("Could not open socket for listening: " + std::string(std::strerror(errno))); throw torrent::resource_error("Could not open socket for listening: " + std::string(std::strerror(errno)));
set_file_descriptor(fd); set_file_descriptor(fd);
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::connection_manager()->inc_socket_count(); torrent::runtime::socket_manager()->register_event_or_throw(this, torrent::runtime::category_rpc, []() {});
m_path = filename; m_path = filename;
} }
void void
SCgi::open_fd(int fd) { SCgi::open_fd(int fd) {
torrent::runtime::socket_manager()->open_event_or_throw(this, [&]() { if (!torrent::fd_set_nonblock(fd))
if (!torrent::fd_set_nonblock(fd)) throw torrent::resource_error("Could not set non-blocking on systemd fd: " +
throw torrent::resource_error("Could not set non-blocking on systemd fd: " + std::string(std::strerror(errno)));
std::string(std::strerror(errno))); set_file_descriptor(fd);
set_file_descriptor(fd);
// fd is already bound and listening; no bind()/listen() needed.
});
torrent::connection_manager()->inc_socket_count(); // fd is already bound and listening; no bind()/listen() needed.
torrent::runtime::socket_manager()->register_event_or_throw(this, torrent::runtime::category_rpc, []() {});
} }
void void
@@ -116,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.
@@ -132,15 +126,13 @@ SCgi::stop() {
itr->close(); itr->close();
} }
torrent::runtime::socket_manager()->close_event_or_throw(this, [this]() { torrent::runtime::socket_manager()->unregister_event_or_throw(this, [this]() {
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); set_file_descriptor(-1);
}); });
torrent::connection_manager()->dec_socket_count();
if (!m_path.empty()) if (!m_path.empty())
::unlink(m_path.c_str()); ::unlink(m_path.c_str());
} }
@@ -159,38 +151,38 @@ SCgi::event_read() {
if (m_current == m_tasks.end()) if (m_current == m_tasks.end())
m_current = std::find_if(m_tasks.begin(), prev, [](const auto& task) { return !task->is_open(); }); m_current = std::find_if(m_tasks.begin(), prev, [](const auto& task) { return !task->is_open(); });
int fd = torrent::fd_accept(file_descriptor());
if (fd == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
return;
// Force a new event_read() call just to be sure we don't enter an infinite loop.
if (errno == ECONNABORTED)
return;
throw torrent::resource_error("Listener port accept() failed: " + std::string(std::strerror(errno)));
}
if (m_current == prev) { if (m_current == prev) {
// TODO: Currently just close, although we should remove ourselves from read. torrent::fd_close(fd);
int fd = torrent::fd_accept(file_descriptor());
if (fd != -1)
torrent::fd_close(fd);
continue; continue;
} }
auto open_func = [this, task = m_current->get()]() { auto open_func = [this, fd, task = m_current->get()]() {
int fd = torrent::fd_accept(file_descriptor());
if (fd == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
return;
// Force a new event_read() call just to be sure we don't enter an infinite loop.
if (errno == ECONNABORTED)
return;
throw torrent::resource_error("Listener port accept() failed: " + std::string(std::strerror(errno)));
}
task->open(this, fd); task->open(this, fd);
}; };
auto cleanup_func = [task = m_current->get()]() { auto cleanup_func = [fd, task = m_current->get()](bool opened) {
if (!opened) {
torrent::fd_close(fd);
return;
}
task->cancel_open(); task->cancel_open();
}; };
bool result = torrent::runtime::socket_manager()->open_event_or_cleanup(m_current->get(), 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;
+42 -26
View File
@@ -11,10 +11,10 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/net/fd.h> #include <torrent/net/fd.h>
#include <torrent/net/poll.h>
#include <torrent/runtime/socket_manager.h> #include <torrent/runtime/socket_manager.h>
#include <torrent/system/callbacks.h>
#include <torrent/system/poll.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/system/thread.h>
#include "control.h" #include "control.h"
#include "globals.h" #include "globals.h"
@@ -24,6 +24,12 @@
namespace rpc { namespace rpc {
SCgiTask::SCgiTask()
: m_callback_id(torrent::system::make_callback_id()) {
reset_file_descriptor();
}
void void
SCgiTask::open(SCgi* parent, int fd) { SCgiTask::open(SCgi* parent, int fd) {
set_file_descriptor(fd); set_file_descriptor(fd);
@@ -34,11 +40,15 @@ SCgiTask::open(SCgi* parent, int fd) {
m_content_length = 0; m_content_length = 0;
m_content_type = XML; m_content_type = XML;
m_content_type_set = false;
m_accepts_compression = false; m_accepts_compression = false;
m_trusted = true; // SCgiTask is pooled and reused; reset trust to default
// so a prior untrusted connection does not leak its
// m_trusted=false into the next reuse, given that the
// UNTRUSTED_CONNECTION=0 parse branch is a no-op.
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);
@@ -54,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
@@ -62,14 +72,13 @@ SCgiTask::close() {
if (!is_open()) if (!is_open())
return; return;
torrent::main_thread::thread()->cancel_callback_and_wait(this); torrent::system::cancel_callback_and_wait(m_callback_id, scgi_thread::thread(), torrent::main_thread::thread());
torrent::system::Thread::self()->cancel_callback(this);
torrent::runtime::socket_manager()->close_event_or_throw(this, [this]() { torrent::runtime::socket_manager()->close_event_or_throw(this, [this]() {
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.
@@ -81,14 +90,14 @@ SCgiTask::close() {
void void
SCgiTask::event_read() { SCgiTask::event_read() {
int read_length = m_buffer.size() - m_position; int read_length = m_buffer.size() - m_position;
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."); throw torrent::internal_error("SCgiTask::event_read() no space in buffer for event_read.");
int bytes = ::recv(m_fileDesc, m_buffer.data() + m_position, read_length, 0); 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))
@@ -116,6 +125,9 @@ SCgiTask::event_read() {
if (current == m_buffer.data()) if (current == m_buffer.data())
return; return;
if (current == m_buffer.data() + m_position)
return;
if (*current != ':' || header_size < 17 || header_size > max_header_size) if (*current != ':' || header_size < 17 || header_size > max_header_size)
goto event_read_failed; goto event_read_failed;
@@ -153,6 +165,11 @@ SCgiTask::event_read() {
lt_log_print_dump(torrent::LOG_RPC_DUMP, m_buffer.data() + m_body, m_content_length, "scgi", "RPC read.", 0); lt_log_print_dump(torrent::LOG_RPC_DUMP, m_buffer.data() + m_body, m_content_length, "scgi", "RPC read.", 0);
if (!m_content_type_set) {
if (m_buffer[m_body] == '{' || m_buffer[m_body] == '[')
m_content_type = ContentType::JSON;
}
receive_call(m_buffer.data() + m_body, m_content_length); receive_call(m_buffer.data() + m_body, m_content_length);
return; return;
@@ -163,10 +180,10 @@ 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 || errno == EPIPE)) if (!(errno == EAGAIN || errno == EINTR))
close(); close();
return; return;
@@ -231,7 +248,8 @@ SCgiTask::parse_headers(const char* current, unsigned int header_length) {
if (std::strncmp(value, "1", 1+1) == 0) if (std::strncmp(value, "1", 1+1) == 0)
m_trusted = false; m_trusted = false;
else if (std::strncmp(value, "0", 1+1) == 0) else if (std::strncmp(value, "0", 1+1) == 0)
; // Default is trusted, so do nothing. m_trusted = true; // Explicit reset (open() also resets, but be defensive in case
// future refactors stop pooling tasks or skip the open() reset).
else else
return false; return false;
} }
@@ -285,19 +303,17 @@ scgi_match_content_type(const std::string& content_type, const char* type) {
bool bool
SCgiTask::detect_content_type(const std::string& content_type) { SCgiTask::detect_content_type(const std::string& content_type) {
if (content_type.empty()) { if (content_type.empty()) {
// If no CONTENT_TYPE was supplied, peek at the body to check if it's JSON // Defer body-peek detection until the full body is received.
// { is a single request object, while [ is a batch array // event_read() will auto-detect from the first body byte after
// confirming m_position >= m_body + m_content_length.
if (m_buffer[m_body] == '{' || m_buffer[m_body] == '[')
m_content_type = ContentType::JSON;
else
m_content_type = ContentType::XML;
} else if (scgi_match_content_type(content_type, "application/json")) { } else if (scgi_match_content_type(content_type, "application/json")) {
m_content_type = ContentType::JSON; m_content_type = ContentType::JSON;
m_content_type_set = true;
} else if (scgi_match_content_type(content_type, "text/xml")) { } else if (scgi_match_content_type(content_type, "text/xml")) {
m_content_type = ContentType::XML; m_content_type = ContentType::XML;
m_content_type_set = true;
} else { } else {
// If the content type is not JSON or XML, we don't know how to handle it. // If the content type is not JSON or XML, we don't know how to handle it.
@@ -309,7 +325,7 @@ SCgiTask::detect_content_type(const std::string& content_type) {
void void
SCgiTask::receive_call(const char* buffer, uint32_t length) { SCgiTask::receive_call(const char* buffer, uint32_t length) {
assert(torrent::system::Thread::self() == scgi_thread::thread()); assert(torrent::this_thread::thread() == scgi_thread::thread());
RpcManager::RPCType rpc_type; RpcManager::RPCType rpc_type;
@@ -336,7 +352,7 @@ SCgiTask::receive_call(const char* buffer, uint32_t length) {
m_result_mutex.lock(); m_result_mutex.lock();
m_result_mutex.unlock(); m_result_mutex.unlock();
scgi_thread::thread()->callback_interrupt_polling(this, [this]() { scgi_thread::callback_interrupt(m_callback_id, [this]() {
if (!is_open()) if (!is_open())
return; return;
@@ -356,7 +372,7 @@ SCgiTask::receive_call(const char* buffer, uint32_t length) {
m_result_mutex.lock(); m_result_mutex.lock();
m_result_mutex.unlock(); m_result_mutex.unlock();
torrent::main_thread::thread()->callback_interrupt_polling(this, [this, rpc_type, buffer, length, result_callback]() { torrent::main_thread::callback_interrupt(m_callback_id, [this, rpc_type, buffer, length, result_callback]() {
// Memory barrier for the input data. // Memory barrier for the input data.
// std::atomic_thread_fence(std::memory_order_acquire); // std::atomic_thread_fence(std::memory_order_acquire);
m_result_mutex.lock(); m_result_mutex.lock();
@@ -371,7 +387,7 @@ SCgiTask::receive_call(const char* buffer, uint32_t length) {
void void
SCgiTask::receive_write(const char* buffer, uint32_t length) { SCgiTask::receive_write(const char* buffer, uint32_t length) {
assert(torrent::system::Thread::self() == torrent::main_thread::thread()); assert(torrent::this_thread::thread() == torrent::main_thread::thread());
if (buffer == nullptr || length > (100 << 20)) if (buffer == nullptr || length > (100 << 20))
throw torrent::internal_error("SCgiTask::receive_write(...) received bad input."); throw torrent::internal_error("SCgiTask::receive_write(...) received bad input.");
+8 -6
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;
@@ -18,12 +18,12 @@ public:
enum ContentType { XML, JSON }; enum ContentType { XML, JSON };
SCgiTask() { m_fileDesc = -1; } SCgiTask();
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();
@@ -54,7 +54,8 @@ private:
void plaintext_response(const char* buffer, uint32_t content_length); void plaintext_response(const char* buffer, uint32_t content_length);
void gzip_response(const char* buffer, uint32_t content_length); void gzip_response(const char* buffer, uint32_t content_length);
SCgi* m_parent{}; SCgi* m_parent{};
torrent::system::callback_id m_callback_id;
std::mutex m_result_mutex; std::mutex m_result_mutex;
@@ -67,6 +68,7 @@ private:
bool m_accepts_compression{}; bool m_accepts_compression{};
bool m_trusted{true}; bool m_trusted{true};
bool m_content_type_set{false};
}; };
} }
+71 -79
View File
@@ -27,7 +27,7 @@ distribution.
#include "tinyxml2.h" #include "tinyxml2.h"
#include <new> // yes, this one new style header, is in the Android SDK. #include <new> // yes, this one new style header, is in the Android SDK.
#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) #if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) || defined(__CC_ARM)
# include <stddef.h> # include <stddef.h>
# include <stdarg.h> # include <stdarg.h>
#else #else
@@ -239,13 +239,13 @@ char* StrPair::ParseName( char* p )
if ( !p || !(*p) ) { if ( !p || !(*p) ) {
return 0; return 0;
} }
if ( !XMLUtil::IsNameStartChar( (unsigned char) *p ) ) { if ( !XMLUtil::IsNameStartChar( static_cast<unsigned char>(*p) ) ) {
return 0; return 0;
} }
char* const start = p; char* const start = p;
++p; ++p;
while ( *p && XMLUtil::IsNameChar( (unsigned char) *p ) ) { while ( *p && XMLUtil::IsNameChar( static_cast<unsigned char>(*p) ) ) {
++p; ++p;
} }
@@ -472,102 +472,94 @@ void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length
} }
const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) const char* XMLUtil::GetCharacterRef(const char* p, char* value, int* length)
{ {
// Presume an entity, and pull it out. // Assume an entity, and pull it out.
*length = 0; *length = 0;
if ( *(p+1) == '#' && *(p+2) ) { static const uint32_t MAX_CODE_POINT = 0x10FFFF;
unsigned long ucs = 0;
TIXMLASSERT( sizeof( ucs ) >= 4 ); if (*(p + 1) == '#' && *(p + 2)) {
uint32_t ucs = 0;
ptrdiff_t delta = 0; ptrdiff_t delta = 0;
unsigned mult = 1; uint32_t mult = 1;
static const char SEMICOLON = ';'; static const char SEMICOLON = ';';
if ( *(p+2) == 'x' ) { bool hex = false;
uint32_t radix = 10;
const char* q = 0;
char terminator = '#';
if (*(p + 2) == 'x') {
// Hexadecimal. // Hexadecimal.
const char* q = p+3; hex = true;
if ( !(*q) ) { radix = 16;
return 0; terminator = 'x';
}
q = strchr( q, SEMICOLON ); q = p + 3;
if ( !q ) {
return 0;
}
TIXMLASSERT( *q == SEMICOLON );
delta = q-p;
--q;
while ( *q != 'x' ) {
unsigned int digit = 0;
if ( *q >= '0' && *q <= '9' ) {
digit = *q - '0';
}
else if ( *q >= 'a' && *q <= 'f' ) {
digit = *q - 'a' + 10;
}
else if ( *q >= 'A' && *q <= 'F' ) {
digit = *q - 'A' + 10;
}
else {
return 0;
}
TIXMLASSERT( digit < 16 );
TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
const unsigned int digitScaled = mult * digit;
TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
ucs += digitScaled;
TIXMLASSERT( mult <= UINT_MAX / 16 );
mult *= 16;
--q;
}
} }
else { else {
// Decimal. // Decimal.
const char* q = p+2; q = p + 2;
if ( !(*q) ) { }
if (!(*q)) {
return 0;
}
q = strchr(q, SEMICOLON);
if (!q) {
return 0;
}
TIXMLASSERT(*q == SEMICOLON);
delta = q - p;
--q;
while (*q != terminator) {
uint32_t digit = 0;
if (*q >= '0' && *q <= '9') {
digit = *q - '0';
}
else if (hex && (*q >= 'a' && *q <= 'f')) {
digit = *q - 'a' + 10;
}
else if (hex && (*q >= 'A' && *q <= 'F')) {
digit = *q - 'A' + 10;
}
else {
return 0; return 0;
} }
TIXMLASSERT(digit < radix);
q = strchr( q, SEMICOLON ); const unsigned int digitScaled = mult * digit;
ucs += digitScaled;
if ( !q ) { mult *= radix;
return 0;
// Security check: could a value exist that is out of range?
// Easily; limit to the MAX_CODE_POINT, which also allows for a
// bunch of leading zeroes.
if (mult > MAX_CODE_POINT) {
mult = MAX_CODE_POINT;
} }
TIXMLASSERT( *q == SEMICOLON );
delta = q-p;
--q; --q;
}
while ( *q != '#' ) { // Out of range:
if ( *q >= '0' && *q <= '9' ) { if (ucs > MAX_CODE_POINT) {
const unsigned int digit = *q - '0'; return 0;
TIXMLASSERT( digit < 10 );
TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
const unsigned int digitScaled = mult * digit;
TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
ucs += digitScaled;
}
else {
return 0;
}
TIXMLASSERT( mult <= UINT_MAX / 10 );
mult *= 10;
--q;
}
} }
// convert the UCS to UTF-8 // convert the UCS to UTF-8
ConvertUTF32ToUTF8( ucs, value, length ); ConvertUTF32ToUTF8(ucs, value, length);
if (length == 0) {
// If length is 0, there was an error. (Security? Bad input?)
// Fail safely.
return 0;
}
return p + delta + 1; return p + delta + 1;
} }
return p+1; return p + 1;
} }
void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
{ {
TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
@@ -705,7 +697,7 @@ bool XMLUtil::ToInt64(const char* str, int64_t* value)
bool XMLUtil::ToUnsigned64(const char* str, uint64_t* value) { bool XMLUtil::ToUnsigned64(const char* str, uint64_t* value) {
unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llu unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llu
if(TIXML_SSCANF(str, IsPrefixHex(str) ? "%llx" : "%llu", &v) == 1) { if(TIXML_SSCANF(str, IsPrefixHex(str) ? "%llx" : "%llu", &v) == 1) {
*value = (uint64_t)v; *value = static_cast<uint64_t>(v);
return true; return true;
} }
return false; return false;
@@ -1982,7 +1974,7 @@ char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
} }
// attribute. // attribute.
if (XMLUtil::IsNameStartChar( (unsigned char) *p ) ) { if (XMLUtil::IsNameStartChar( static_cast<unsigned char>(*p) ) ) {
XMLAttribute* attrib = CreateAttribute(); XMLAttribute* attrib = CreateAttribute();
TIXMLASSERT( attrib ); TIXMLASSERT( attrib );
attrib->_parseLineNum = _document->_parseCurLineNum; attrib->_parseLineNum = _document->_parseCurLineNum;
+2 -2
View File
@@ -98,11 +98,11 @@ distribution.
/* Versioning, past 1.0.14: /* Versioning, past 1.0.14:
http://semver.org/ http://semver.org/
*/ */
static const int TIXML2_MAJOR_VERSION = 10; static const int TIXML2_MAJOR_VERSION = 11;
static const int TIXML2_MINOR_VERSION = 0; static const int TIXML2_MINOR_VERSION = 0;
static const int TIXML2_PATCH_VERSION = 0; static const int TIXML2_PATCH_VERSION = 0;
#define TINYXML2_MAJOR_VERSION 10 #define TINYXML2_MAJOR_VERSION 11
#define TINYXML2_MINOR_VERSION 0 #define TINYXML2_MINOR_VERSION 0
#define TINYXML2_PATCH_VERSION 0 #define TINYXML2_PATCH_VERSION 0
+8 -6
View File
@@ -1,3 +1,4 @@
#include <deque>
#include "config.h" #include "config.h"
#include "xmlrpc.h" #include "xmlrpc.h"
@@ -9,20 +10,21 @@
namespace rpc { namespace rpc {
std::vector<std::unique_ptr<const char>> 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) {
if (::strnlen(name, 8192) == 8192) if (::strnlen(name, 1024 + 1) > 1024)
throw torrent::input_error("XMLRPC command name too long, limit is 8192 characters."); throw torrent::input_error("XMLRPC command name too long, limit is 8192 characters.");
for (const auto& itr : m_command_names) { for (const auto& itr : m_command_names) {
if (::strcmp(itr.get(), name) == 0) if (itr == name)
return itr.get(); return itr.c_str();
} }
m_command_names.push_back(std::unique_ptr<const char>(::strdup(name))); m_command_names.push_back(name);
return m_command_names.back().get();
return m_command_names.back().c_str();
} }
#ifndef HAVE_XMLRPC_C #ifndef HAVE_XMLRPC_C
+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::unique_ptr<const char>> 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;
+17 -2
View File
@@ -272,12 +272,27 @@ object_to_xmlrpc(xmlrpc_env* env, const torrent::Object& object) {
#ifdef XMLRPC_HAVE_I8 #ifdef XMLRPC_HAVE_I8
if (rpc.dialect() != XmlRpc::dialect_generic) if (rpc.dialect() != XmlRpc::dialect_generic)
return xmlrpc_i8_new(env, object.as_value()); return xmlrpc_i8_new(env, object.as_value());
#else
return xmlrpc_int_new(env, object.as_value());
#endif #endif
return xmlrpc_int_new(env, object.as_value());
case torrent::Object::TYPE_STRING: case torrent::Object::TYPE_STRING:
{ {
if (object.flags() & torrent::Object::flag_as_binary) {
// This causes decode-and-reencode for base64, as XMLRPC-C doesn't allow us to pass base64 strings.
if (object.flags() & torrent::Object::flag_base64) {
auto binary_data = torrent::utils::transform_from_base64_unsafe(object.as_string());
if (!binary_data.has_value())
throw torrent::input_error("invalid base64 string in base64-as-binary object");
return xmlrpc_base64_new(env, binary_data->size(), reinterpret_cast<const unsigned char*>(binary_data->data()));
}
return xmlrpc_base64_new(env, object.as_string().size(), reinterpret_cast<const unsigned char*>(object.as_string().c_str()));
}
#ifdef XMLRPC_HAVE_I8 #ifdef XMLRPC_HAVE_I8
// The versions that support I8 do implicit utf-8 validation. // The versions that support I8 do implicit utf-8 validation.
xmlrpc_value* result = xmlrpc_string_new(env, object.as_string().c_str()); xmlrpc_value* result = xmlrpc_string_new(env, object.as_string().c_str());
+59 -12
View File
@@ -13,6 +13,7 @@
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/utils/string_manip.h>
#include "parse_commands.h" #include "parse_commands.h"
#include "rpc/tinyxml2/tinyxml2.h" #include "rpc/tinyxml2/tinyxml2.h"
@@ -66,56 +67,85 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
if (elem == nullptr) { if (elem == nullptr) {
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received null element to convert"); throw rpc_error(XMLRPC_INTERNAL_ERROR, "received null element to convert");
} }
if (std::strncmp(elem->Value(), "value", sizeof("value")) != 0) { if (std::strncmp(elem->Value(), "value", sizeof("value")) != 0) {
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received non-value element to convert"); throw rpc_error(XMLRPC_INTERNAL_ERROR, "received non-value element to convert");
} }
auto root_element = elem->FirstChild(); auto root_element = elem->FirstChild();
if (root_element == nullptr)
throw rpc_error(XMLRPC_TYPE_ERROR, "empty value element");
auto root_type = root_element->Value(); auto root_type = root_element->Value();
if (std::strncmp(root_type, "string", sizeof("string")) == 0) { if (std::strncmp(root_type, "string", sizeof("string")) == 0) {
auto child_element = root_element->FirstChild(); auto child_element = root_element->FirstChild();
if (child_element == nullptr) {
if (child_element == nullptr)
return torrent::Object(""); return torrent::Object("");
}
return torrent::Object(child_element->ToText()->Value()); return torrent::Object(child_element->ToText()->Value());
} 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 ||
std::strncmp(root_type, "i8", sizeof("i8")) == 0) { std::strncmp(root_type, "i8", sizeof("i8")) == 0) {
return torrent::Object(element_to_int(root_element)); return torrent::Object(element_to_int(root_element));
} else if (std::strncmp(root_type, "boolean", sizeof("boolean")) == 0) { } else if (std::strncmp(root_type, "boolean", sizeof("boolean")) == 0) {
auto boolean_text = std::string(root_element->FirstChild()->ToText()->Value()); auto child_element = root_element->FirstChild();
if (boolean_text == "1") {
if (child_element == nullptr)
throw rpc_error(XMLRPC_TYPE_ERROR, "empty boolean element");
auto boolean_text = std::string(child_element->ToText()->Value());
if (boolean_text == "1")
return torrent::Object((int64_t)1); return torrent::Object((int64_t)1);
} else if (boolean_text == "0") { else if (boolean_text == "0")
return torrent::Object((int64_t)0); return torrent::Object((int64_t)0);
}
throw rpc_error(XMLRPC_TYPE_ERROR, "unknown boolean value: " + boolean_text); throw rpc_error(XMLRPC_TYPE_ERROR, "unknown boolean value: " + boolean_text);
} else if (std::strncmp(root_type, "array", sizeof("array")) == 0) { } else if (std::strncmp(root_type, "array", sizeof("array")) == 0) {
auto array_raw = torrent::Object::create_list(); auto array_raw = torrent::Object::create_list();
auto& array = array_raw.as_list(); auto& array = array_raw.as_list();
auto data_element = root_element->ToElement()->FirstChildElement("data"); auto data_element = root_element->ToElement()->FirstChildElement("data");
if (data_element == nullptr) if (data_element == nullptr)
throw rpc_error(XMLRPC_PARSE_ERROR, "could not find expected data element in array"); throw rpc_error(XMLRPC_PARSE_ERROR, "could not find expected data element in array");
for (auto child = data_element->FirstChildElement("value"); child; child = child->NextSiblingElement("value")) {
for (auto child = data_element->FirstChildElement("value"); child; child = child->NextSiblingElement("value"))
array.push_back(xml_value_to_object(child)); array.push_back(xml_value_to_object(child));
}
return array_raw; return array_raw;
} else if (std::strncmp(root_type, "struct", sizeof("struct")) == 0) { } else if (std::strncmp(root_type, "struct", sizeof("struct")) == 0) {
auto map_raw = torrent::Object::create_map(); auto map_raw = torrent::Object::create_map();
auto& map = map_raw.as_map(); auto& map = map_raw.as_map();
for (auto child = root_element->FirstChildElement("member"); child; child = child->NextSiblingElement("member")) { for (auto child = root_element->FirstChildElement("member"); child; child = child->NextSiblingElement("member")) {
auto key = child->FirstChildElement("name")->GetText(); auto name_element = child->FirstChildElement("name");
map[key] = std::move(xml_value_to_object(child->FirstChildElement("value")));
if (name_element == nullptr)
throw rpc_error(XMLRPC_PARSE_ERROR, "struct member missing name element");
map[name_element->GetText()] = std::move(xml_value_to_object(child->FirstChildElement("value")));
} }
return map_raw; return map_raw;
} else if (std::strncmp(root_type, "base64", sizeof("base64")) == 0) { } else if (std::strncmp(root_type, "base64", sizeof("base64")) == 0) {
auto child_element = root_element->FirstChild(); auto child_element = root_element->FirstChild();
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(child_element->ToText()->Value())));
} 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));
} }
return torrent::Object(); return torrent::Object();
} }
@@ -123,15 +153,32 @@ void
print_object_xml(const torrent::Object& obj, tinyxml2::XMLPrinter* printer) { print_object_xml(const torrent::Object& obj, tinyxml2::XMLPrinter* printer) {
switch (obj.type()) { switch (obj.type()) {
case torrent::Object::TYPE_STRING: case torrent::Object::TYPE_STRING:
if (obj.flags() & torrent::Object::flag_as_binary) {
// It is optimal for tinyxml2 to pass base64 strings directly from torrent::string_utf8 sources.
if (obj.flags() & torrent::Object::flag_base64) {
printer->OpenElement("base64", true);
printer->PushText(obj.as_string().c_str());
printer->CloseElement(true);
break;
}
printer->OpenElement("base64", true);
printer->PushText(torrent::utils::transform_to_base64(obj.as_string()).c_str());
printer->CloseElement(true);
break;
}
printer->OpenElement("string", true); printer->OpenElement("string", true);
printer->PushText(obj.as_string().c_str()); printer->PushText(obj.as_string().c_str());
printer->CloseElement(true); printer->CloseElement(true);
break; break;
case torrent::Object::TYPE_VALUE: case torrent::Object::TYPE_VALUE:
printer->OpenElement("i8", true); printer->OpenElement("i8", true);
printer->PushText(std::to_string(obj.as_value()).c_str()); printer->PushText(std::to_string(obj.as_value()).c_str());
printer->CloseElement(true); printer->CloseElement(true);
break; break;
case torrent::Object::TYPE_LIST: case torrent::Object::TYPE_LIST:
printer->OpenElement("array", true); printer->OpenElement("array", true);
printer->OpenElement("data", true); printer->OpenElement("data", true);
+8 -15
View File
@@ -12,11 +12,6 @@
namespace scgi { namespace scgi {
class ThreadScgiInternal {
public:
static ThreadScgi* thread_scgi() { return ThreadScgi::internal_thread_scgi(); }
};
ThreadScgi* ThreadScgi::m_thread_scgi{}; ThreadScgi* ThreadScgi::m_thread_scgi{};
void void
@@ -60,7 +55,7 @@ ThreadScgi::set_scgi(rpc::SCgi* scgi) {
change_rpc_log(); change_rpc_log();
callback(nullptr, [this]() { callback([this]() {
if (m_scgi == nullptr) if (m_scgi == nullptr)
throw torrent::internal_error("Tried to start SCGI but object was not present."); throw torrent::internal_error("Tried to start SCGI but object was not present.");
@@ -72,7 +67,7 @@ ThreadScgi::set_scgi(rpc::SCgi* scgi) {
void void
ThreadScgi::set_rpc_log(const std::string& filename) { ThreadScgi::set_rpc_log(const std::string& filename) {
callback(nullptr, [this, filename]() { callback([this, filename]() {
m_rpc_log_filename = filename; m_rpc_log_filename = filename;
change_rpc_log(); change_rpc_log();
}); });
@@ -127,15 +122,13 @@ ThreadScgi::next_timeout() {
namespace scgi_thread { namespace scgi_thread {
torrent::system::Thread* thread() { return scgi::ThreadScgiInternal::thread_scgi(); } torrent::system::Thread* thread() { return scgi::ThreadScgi::thread_scgi(); }
std::thread::id thread_id() { return scgi::ThreadScgiInternal::thread_scgi()->thread_id(); } std::thread::id thread_id() { return scgi::ThreadScgi::thread_scgi()->thread_id(); }
void callback(void* target, std::function<void ()>&& fn) { scgi::ThreadScgiInternal::thread_scgi()->callback(target, std::move(fn)); } void callback_interrupt(torrent::system::callback_id& id, std::function<void ()>&& fn) { scgi::ThreadScgi::thread_scgi()->callback_interrupt(id, std::move(fn)); }
void cancel_callback(void* target) { scgi::ThreadScgiInternal::thread_scgi()->cancel_callback(target); }
void cancel_callback_and_wait(void* target) { scgi::ThreadScgiInternal::thread_scgi()->cancel_callback_and_wait(target); }
rpc::SCgi* scgi() { return scgi::ThreadScgiInternal::thread_scgi()->scgi(); } rpc::SCgi* scgi() { return scgi::ThreadScgi::thread_scgi()->scgi(); }
void set_scgi(rpc::SCgi* scgi) { scgi::ThreadScgiInternal::thread_scgi()->set_scgi(scgi); } void set_scgi(rpc::SCgi* scgi) { scgi::ThreadScgi::thread_scgi()->set_scgi(scgi); }
void set_rpc_log(const std::string& filename) { scgi::ThreadScgiInternal::thread_scgi()->set_rpc_log(filename); } void set_rpc_log(const std::string& filename) { scgi::ThreadScgi::thread_scgi()->set_rpc_log(filename); }
} // namespace scgi_thread } // namespace scgi_thread
+2 -6
View File
@@ -11,8 +11,6 @@ class SCgi;
namespace scgi { namespace scgi {
class ThreadScgiInternal;
class ThreadScgi : public torrent::system::Thread { class ThreadScgi : public torrent::system::Thread {
public: public:
@@ -28,8 +26,6 @@ public:
void set_rpc_log(const std::string& filename); void set_rpc_log(const std::string& filename);
protected: protected:
friend class ThreadScgiInternal;
ThreadScgi() = default; ThreadScgi() = default;
static auto internal_thread_scgi() { return m_thread_scgi; } static auto internal_thread_scgi() { return m_thread_scgi; }
@@ -43,9 +39,9 @@ private:
void task_touch_log(); void task_touch_log();
void change_rpc_log(); void change_rpc_log();
static ThreadScgi* m_thread_scgi; static ThreadScgi* m_thread_scgi;
std::atomic<rpc::SCgi*> m_scgi{nullptr}; std::atomic<rpc::SCgi*> m_scgi{};
std::string m_rpc_log_filename; std::string m_rpc_log_filename;
}; };
+12 -5
View File
@@ -5,6 +5,7 @@
#include <cassert> #include <cassert>
#include <cstdlib> #include <cstdlib>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/system/callbacks.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include "globals.h" #include "globals.h"
@@ -23,6 +24,7 @@ namespace session {
SessionManager::SessionManager(torrent::system::Thread* thread) SessionManager::SessionManager(torrent::system::Thread* thread)
: m_thread(thread), : m_thread(thread),
m_callback_id(torrent::system::make_callback_id()),
m_lockfile(std::make_unique<utils::Lockfile>()) { m_lockfile(std::make_unique<utils::Lockfile>()) {
} }
@@ -221,8 +223,7 @@ SessionManager::cleanup() {
LT_LOG("session manager cleaned up", 0); LT_LOG("session manager cleaned up", 0);
session_thread::cancel_callback(this); torrent::system::cancel_callback_and_wait(m_callback_id, session_thread::thread(), torrent::main_thread::thread());
torrent::main_thread::cancel_callback(this);
} }
void void
@@ -233,7 +234,9 @@ SessionManager::callback_pending_builds() {
if (m_callback_scheduled_process_pending_builds.exchange(true)) if (m_callback_scheduled_process_pending_builds.exchange(true))
return; return;
torrent::main_thread::callback(this, [this]() { process_pending_builds(false); }); torrent::main_thread::callback(m_callback_id, [this]() {
process_pending_builds(false);
});
} }
void void
@@ -247,7 +250,9 @@ SessionManager::callback_save_request() {
if (m_callback_scheduled_process_saves_request.exchange(true)) if (m_callback_scheduled_process_saves_request.exchange(true))
return; return;
session_thread::callback(this, [this]() { process_save_request(); }); session_thread::callback(m_callback_id, [this]() {
process_save_request();
});
} }
void void
@@ -255,7 +260,9 @@ SessionManager::callback_finished_saves() {
if (m_callback_scheduled_process_finished_saves.exchange(true)) if (m_callback_scheduled_process_finished_saves.exchange(true))
return; return;
session_thread::callback(this, [this]() { process_finished_saves(); }); session_thread::callback(m_callback_id, [this]() {
process_finished_saves();
});
} }
void void
+23 -21
View File
@@ -89,37 +89,39 @@ private:
bool replace_save_request_unsafe(SaveRequest& download); bool replace_save_request_unsafe(SaveRequest& download);
bool remove_completely_unsafe(core::Download* download, std::unique_lock<std::mutex>& lock); bool remove_completely_unsafe(core::Download* download, std::unique_lock<std::mutex>& lock);
torrent::system::Thread* m_thread; using ProcessingSave = std::pair<std::future<void>, SaveRequest>;
bool m_freeze_info{}; torrent::system::Thread* m_thread;
std::string m_path; torrent::system::callback_id m_callback_id;
bool m_use_fsyncdisk{true};
bool m_use_lock{true};
std::mutex m_mutex; bool m_freeze_info{};
bool m_active{}; std::string m_path;
bool m_use_fsyncdisk{true};
bool m_use_lock{true};
typedef std::pair<std::future<void>, SaveRequest> ProcessingSave; align_cacheline std::mutex m_mutex;
std::deque<SaveRequest> m_save_requests; bool m_active{};
std::atomic<size_t> m_save_request_counter{};
std::list<ProcessingSave> m_processing_saves;
std::atomic<size_t> m_processing_save_counter{};
std::condition_variable m_finished_condition;
std::vector<ProcessingSave> m_finished_saves;
std::atomic<bool> m_callback_scheduled_process_pending_builds{}; std::deque<SaveRequest> m_save_requests;
std::atomic<bool> m_callback_scheduled_process_saves_request{}; std::atomic<size_t> m_save_request_counter{};
std::atomic<bool> m_callback_scheduled_process_finished_saves{}; std::list<ProcessingSave> m_processing_saves;
std::atomic<size_t> m_processing_save_counter{};
std::condition_variable m_finished_condition;
std::vector<ProcessingSave> m_finished_saves;
std::atomic<bool> m_callback_scheduled_process_pending_builds{};
std::atomic<bool> m_callback_scheduled_process_saves_request{};
std::atomic<bool> m_callback_scheduled_process_finished_saves{};
std::unique_ptr<utils::Lockfile> m_lockfile; std::unique_ptr<utils::Lockfile> m_lockfile;
std::chrono::microseconds m_last_storage_error_message{}; std::chrono::microseconds m_last_storage_error_message{};
unsigned int m_ignored_storage_error_count{}; unsigned int m_ignored_storage_error_count{};
// Pending builds are only ever locked by main thread. // Pending builds are only ever locked by main thread.
std::mutex m_pending_builds_mutex; std::mutex m_pending_builds_mutex;
std::deque<core::Download*> m_pending_builds; std::deque<core::Download*> m_pending_builds;
}; };
inline bool SessionManager::is_used() const { return !m_path.empty(); } inline bool SessionManager::is_used() const { return !m_path.empty(); }
+8 -12
View File
@@ -4,15 +4,11 @@
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include "globals.h"
#include "session/session_manager.h" #include "session/session_manager.h"
namespace session { namespace session {
class ThreadSessionInternal {
public:
static ThreadSession* thread_session() { return ThreadSession::internal_thread_session(); }
};
ThreadSession* ThreadSession::m_thread_session{nullptr}; ThreadSession* ThreadSession::m_thread_session{nullptr};
void void
@@ -74,14 +70,14 @@ ThreadSession::next_timeout() {
namespace session_thread { namespace session_thread {
torrent::system::Thread* thread() { return session::ThreadSessionInternal::thread_session(); } torrent::system::Thread* thread() { return session::ThreadSession::thread_session(); }
std::thread::id thread_id() { return session::ThreadSessionInternal::thread_session()->thread_id(); } std::thread::id thread_id() { return thread()->thread_id(); }
void callback(void* target, std::function<void ()>&& fn) { session::ThreadSessionInternal::thread_session()->callback(target, std::move(fn)); } void callback(std::function<void()>&& fn) { thread()->callback(std::move(fn)); }
void cancel_callback(void* target) { session::ThreadSessionInternal::thread_session()->cancel_callback(target); } void callback(torrent::system::callback_id& id, std::function<void()>&& fn) { thread()->callback(id, std::move(fn)); }
void cancel_callback_and_wait(void* target) { session::ThreadSessionInternal::thread_session()->cancel_callback_and_wait(target); } void cancel_callback(torrent::system::callback_id& id) { thread()->cancel_callback(id); }
session::SessionManager* manager() { return session::ThreadSessionInternal::thread_session()->manager(); } session::SessionManager* manager() { return session::ThreadSession::thread_session()->manager(); }
std::string session_path() { return session::ThreadSessionInternal::thread_session()->manager()->path(); } std::string session_path() { return manager()->path(); }
} // namespace session_thread } // namespace session_thread
+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);
+5 -9
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);
@@ -250,7 +246,7 @@ Download::activate_display(Display displayType, bool focusDisplay) {
// Set title. // Set title.
switch (displayType) { switch (displayType) {
case DISPLAY_MAX_SIZE: break; case DISPLAY_MAX_SIZE: break;
default: control->ui()->window_title()->set_title(m_download->info()->name()); break; default: control->ui()->window_title()->set_title(m_download->info()->name().str()); break;
} }
control->display()->adjust_layout(); control->display()->adjust_layout();
+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;
}; };
} }
+2 -1
View File
@@ -6,6 +6,7 @@
#include <sstream> #include <sstream>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/runtime/runtime.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/utils/string_manip.h> #include <torrent/utils/string_manip.h>
@@ -176,7 +177,7 @@ DownloadList::activate_display(Display displayType) {
switch (displayType) { switch (displayType) {
case DISPLAY_DOWNLOAD_LIST: case DISPLAY_DOWNLOAD_LIST:
control->ui()->window_title()->set_title("rTorrent " PACKAGE_VERSION "/" + control->ui()->window_title()->set_title("rTorrent " PACKAGE_VERSION "/" +
std::string(torrent::version()) + " - " + std::string(torrent::runtime::version()) + " - " +
rpc::call_command_string("session.name")); rpc::call_command_string("session.name"));
break; break;
case DISPLAY_LOG: case DISPLAY_LOG:
+12 -4
View File
@@ -2,6 +2,7 @@
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/system/callbacks.h>
#include <torrent/system/thread.h> #include <torrent/system/thread.h>
#include "display/frame.h" #include "display/frame.h"
@@ -17,11 +18,16 @@ namespace ui {
ElementLogComplete::ElementLogComplete(torrent::log_buffer* l) : ElementLogComplete::ElementLogComplete(torrent::log_buffer* l) :
m_log(l) { m_log(l) {
unsigned int signal_index = torrent::main_thread::thread()->signal_bitfield()->add_signal(std::bind(&ElementLogComplete::received_update, this)); m_log->lock_and_set_update_slot([this]() {
if (m_log_updating.exchange(true) == true)
return;
m_log->lock_and_set_update_slot([signal_index]() { torrent::main_thread::callback([this]() { received_update(); });
torrent::main_thread::thread()->send_event_signal(signal_index, false); });
}); }
ElementLogComplete::~ElementLogComplete() {
m_log->lock_and_set_update_slot(nullptr);
} }
void void
@@ -59,6 +65,8 @@ ElementLogComplete::window() {
void void
ElementLogComplete::received_update() { ElementLogComplete::received_update() {
m_log_updating = false;
if (m_window != NULL) if (m_window != NULL)
m_window->mark_dirty(); m_window->mark_dirty();
} }
+8 -3
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();
@@ -30,6 +31,10 @@ private:
WLogComplete* m_window{}; WLogComplete* m_window{};
torrent::log_buffer* m_log; torrent::log_buffer* m_log;
align_cacheline
std::atomic<bool> m_log_updating{};
}; };
} }
+1
View File
@@ -70,4 +70,5 @@ decode_base64(const std::string& input) {
} }
return decodedBytes; return decodedBytes;
} }
} // namespace utils } // namespace utils
+3 -1
View File
@@ -1,12 +1,14 @@
#ifndef RTORRENT_UTILS_BASE64_H #ifndef RTORRENT_UTILS_BASE64_H
#define RTORRENT_UTILS_BASE64_H #define RTORRENT_UTILS_BASE64_H
#include <optional>
#include <string> #include <string>
#include <vector>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
namespace utils { namespace utils {
// TODO: Deprecate.
std::string remove_newlines(const std::string& str); std::string remove_newlines(const std::string& str);
std::string decode_base64(const std::string& input); std::string decode_base64(const std::string& input);
+10 -5
View File
@@ -21,13 +21,16 @@ FileStatusCache::insert(const std::string& path) {
std::pair<iterator, bool> result = base_type::insert(value_type(path, file_status())); std::pair<iterator, bool> result = base_type::insert(value_type(path, file_status()));
// Return false if the file hasn't been modified since last time. We // Return false if the file hasn't changed since last time. We use
// use 'equal to' instead of 'greater than' since the file might // 'equal to' instead of 'greater than' since the file might have
// have been replaced by another file, and thus should be re-tried. // been replaced by another file, and thus should be re-tried.
if (!result.second && result.first->second.m_mtime == (uint32_t)fs.modified_time()) if (!result.second &&
result.first->second.m_mtime == (uint32_t)fs.modified_time() &&
result.first->second.m_size == (int64_t)fs.size())
return false; return false;
result.first->second.m_flags = 0; result.first->second.m_flags = 0;
result.first->second.m_size = (int64_t)fs.size();
result.first->second.m_mtime = fs.modified_time(); result.first->second.m_mtime = fs.modified_time();
return true; return true;
@@ -41,7 +44,9 @@ FileStatusCache::prune() {
torrent::utils::FileStat fs; torrent::utils::FileStat fs;
iterator tmp = itr++; iterator tmp = itr++;
if (!fs.update(expand_path(tmp->first)) || tmp->second.m_mtime != (uint32_t)fs.modified_time()) if (!fs.update(expand_path(tmp->first)) ||
tmp->second.m_mtime != (uint32_t)fs.modified_time() ||
tmp->second.m_size != (int64_t)fs.size())
base_type::erase(tmp); base_type::erase(tmp);
} }
} }
+4 -3
View File
@@ -9,6 +9,7 @@ namespace utils {
struct file_status { struct file_status {
int m_flags; int m_flags;
int64_t m_size;
uint32_t m_mtime; uint32_t m_mtime;
}; };
@@ -32,14 +33,14 @@ public:
using base_type::erase; using base_type::erase;
// Insert and return true if the entry does not exist or the new // Insert and return true if the entry does not exist or the file's
// file's mtime is more recent. // status has changed.
bool insert(const std::string& path); bool insert(const std::string& path);
// Add a function for pruning a sorted list of paths. // Add a function for pruning a sorted list of paths.
// Function for pruning entries that no longer points to a file, or // Function for pruning entries that no longer points to a file, or
// has a different mtime. // has different status.
void prune(); void prune();
}; };
+115
View File
@@ -0,0 +1,115 @@
#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]() {
bool is_running = true;
auto wait_time = 50ms;
while (is_running) {
if (!m_queue.empty()) {
auto start_time = std::chrono::steady_clock::now();
while (!m_wakeup_worker.load(std::memory_order_acquire) && !m_should_shutdown) {
auto elapsed = std::chrono::steady_clock::now() - start_time;
if (elapsed >= wait_time)
break;
std::this_thread::sleep_for(50ms);
}
} else {
m_wakeup_worker.wait(false, std::memory_order_acquire);
std::this_thread::sleep_for(50ms);
}
std::set<pid_t> queue;
{
std::lock_guard<std::mutex> guard(m_mutex);
if (m_should_shutdown) {
if (m_queue.empty())
return;
is_running = false;
}
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();
}
}
});
}
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();
// m_worker.wait();
}
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);
// if (!m_queue.empty()) {
// m_queue.push_back(pid);
// return;
// }
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
+44
View File
@@ -0,0 +1,44 @@
#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);
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;
bool m_should_shutdown{};
align_cacheline
std::atomic<bool> m_wakeup_worker{};
std::atomic<uint32_t> m_remaining{};
};
inline uint32_t WaitpidQueue::size() const { return m_remaining.load(std::memory_order_acquire); }
} // namespace utils
#endif
+158
View File
@@ -0,0 +1,158 @@
#include "config.h"
#include "utils/watch_ready_queue.h"
#include <algorithm>
#include <torrent/utils/file_stat.h>
#include <torrent/utils/log.h>
#include "globals.h"
#include "rpc/rpc_manager.h"
namespace utils {
namespace {
auto compare_next_time = [](const auto* lhs, const auto* rhs) {
return lhs->next_time > rhs->next_time;
};
}
WatchReadyQueue::WatchReadyQueue() {
m_task_process.slot() = [this]() { process(); };
}
WatchReadyQueue::~WatchReadyQueue() {
torrent::this_thread::scheduler()->erase(&m_task_process);
}
void
WatchReadyQueue::push(const std::string& command, const std::string& path) {
if (!m_active)
return;
auto result = m_entries.emplace(path, Entry());
auto& entry = result.first->second;
if (result.second) {
entry.path = path;
entry.first_seen = torrent::this_thread::cached_time();
}
entry.command = command;
entry.last_changed = torrent::this_thread::cached_time();
update_status(&entry);
if (result.second) {
update_next_time(&entry);
push_entry(&entry);
} else {
update_entry(&entry);
}
schedule();
}
void
WatchReadyQueue::shutdown() {
m_active = false;
m_entries.clear();
m_entry_queue.clear();
torrent::this_thread::scheduler()->erase(&m_task_process);
}
void
WatchReadyQueue::push_entry(Entry* entry) {
m_entry_queue.push_back(entry);
std::push_heap(m_entry_queue.begin(), m_entry_queue.end(), compare_next_time);
}
void
WatchReadyQueue::update_entry(Entry* entry) {
update_next_time(entry);
std::make_heap(m_entry_queue.begin(), m_entry_queue.end(), compare_next_time);
}
void
WatchReadyQueue::update_next_time(Entry* entry) {
if (entry->regular && entry->size > 0) {
entry->next_time = entry->last_changed + quiet_time;
return;
}
entry->next_time = std::min(torrent::this_thread::cached_time() + retry_time,
entry->first_seen + stale_time);
}
void
WatchReadyQueue::update_status(Entry* entry) {
torrent::utils::FileStat fs;
bool regular = false;
int64_t size = -1;
time_t mtime = 0;
if (fs.update(expand_path(entry->path))) {
regular = fs.is_regular();
size = fs.size();
mtime = fs.modified_time();
}
if (entry->regular != regular || entry->size != size || entry->mtime != mtime) {
entry->regular = regular;
entry->size = size;
entry->mtime = mtime;
entry->last_changed = torrent::this_thread::cached_time();
}
}
void
WatchReadyQueue::process() {
ready_list ready;
while (!m_entry_queue.empty() && m_entry_queue.front()->next_time <= torrent::this_thread::cached_time()) {
std::pop_heap(m_entry_queue.begin(), m_entry_queue.end(), compare_next_time);
Entry* entry = m_entry_queue.back();
m_entry_queue.pop_back();
update_status(entry);
bool unchanged_entry = torrent::this_thread::cached_time() - entry->last_changed >= quiet_time;
bool stale_entry = torrent::this_thread::cached_time() - entry->first_seen >= stale_time;
bool nonempty_regular = entry->regular && entry->size > 0;
if (nonempty_regular && unchanged_entry) {
ready.emplace_back(entry->command, entry->path);
std::string path = entry->path;
m_entries.erase(path);
continue;
}
if (!nonempty_regular && stale_entry) {
std::string path = entry->path;
lt_log_print(torrent::LOG_SYSTEM, "system: Dropped unready watch file after timeout: \"%s\"", path.c_str());
m_entries.erase(path);
continue;
}
update_next_time(entry);
push_entry(entry);
}
schedule();
for (const auto& item : ready)
rpc::commands.call_catch(item.first.c_str(), rpc::make_target(), item.second);
}
void
WatchReadyQueue::schedule() {
if (m_entry_queue.empty()) {
torrent::this_thread::scheduler()->erase(&m_task_process);
return;
}
torrent::this_thread::scheduler()->update_wait_until(&m_task_process, m_entry_queue.front()->next_time);
}
}
+60
View File
@@ -0,0 +1,60 @@
#ifndef RTORRENT_UTILS_WATCH_READY_QUEUE_H
#define RTORRENT_UTILS_WATCH_READY_QUEUE_H
#include <chrono>
#include <cstdint>
#include <ctime>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include <torrent/utils/scheduler.h>
namespace utils {
class WatchReadyQueue {
public:
WatchReadyQueue();
~WatchReadyQueue();
void push(const std::string& command, const std::string& path);
void shutdown();
bool empty() const { return m_entries.empty(); }
private:
using time_type = std::chrono::microseconds;
static constexpr auto quiet_time = std::chrono::milliseconds(500);
static constexpr auto retry_time = std::chrono::milliseconds(250);
static constexpr auto stale_time = std::chrono::seconds(10);
using ready_list = std::vector<std::pair<std::string, std::string>>;
struct Entry {
std::string command;
std::string path;
bool regular{};
int64_t size{-1};
time_t mtime{};
time_type first_seen{};
time_type last_changed{};
time_type next_time{};
};
void process();
void push_entry(Entry* entry);
void update_entry(Entry* entry);
void update_next_time(Entry* entry);
void update_status(Entry* entry);
void schedule();
std::map<std::string, Entry> m_entries;
std::vector<Entry*> m_entry_queue;
torrent::utils::SchedulerEntry m_task_process;
bool m_active{true};
};
}
#endif
+5 -1
View File
@@ -48,7 +48,11 @@ 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_watch_ready_queue.cc \
src/test_watch_ready_queue.h
rtorrent_Test_Rpc_CXXFLAGS = $(CPPUNIT_CFLAGS) rtorrent_Test_Rpc_CXXFLAGS = $(CPPUNIT_CFLAGS)
rtorrent_Test_Rpc_LDFLAGS = $(CPPUNIT_LIBS) -ldl rtorrent_Test_Rpc_LDFLAGS = $(CPPUNIT_LIBS) -ldl
+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;
+12 -79
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"
@@ -17,6 +17,7 @@
#define MOCK_LOG(log_fmt, ...) \ #define MOCK_LOG(log_fmt, ...) \
lt_log_print(torrent::LOG_MOCK_CALLS, "%s: " log_fmt, __func__, __VA_ARGS__); lt_log_print(torrent::LOG_MOCK_CALLS, "%s: " log_fmt, __func__, __VA_ARGS__);
namespace {
void void
mock_clear(bool ignore_assert) { mock_clear(bool ignore_assert) {
MOCK_CLEANUP_MAP(torrent::fd__accept); MOCK_CLEANUP_MAP(torrent::fd__accept);
@@ -28,23 +29,12 @@ mock_clear(bool ignore_assert) {
MOCK_CLEANUP_MAP(torrent::fd__setsockopt_int); MOCK_CLEANUP_MAP(torrent::fd__setsockopt_int);
MOCK_CLEANUP_MAP(torrent::fd__socket); MOCK_CLEANUP_MAP(torrent::fd__socket);
MOCK_CLEANUP_MAP(torrent::this_thread::event_open);
MOCK_CLEANUP_MAP(torrent::this_thread::event_open_and_count);
MOCK_CLEANUP_MAP(torrent::this_thread::event_close_and_count);
MOCK_CLEANUP_MAP(torrent::this_thread::event_closed_and_count);
MOCK_CLEANUP_MAP(torrent::this_thread::event_insert_read);
MOCK_CLEANUP_MAP(torrent::this_thread::event_insert_write);
MOCK_CLEANUP_MAP(torrent::this_thread::event_insert_error);
MOCK_CLEANUP_MAP(torrent::this_thread::event_remove_read);
MOCK_CLEANUP_MAP(torrent::this_thread::event_remove_write);
MOCK_CLEANUP_MAP(torrent::this_thread::event_remove_error);
MOCK_CLEANUP_MAP(torrent::this_thread::event_remove_and_close);
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
void void
mock_init() { mock_init() {
@@ -59,9 +49,15 @@ mock_cleanup() {
void void
mock_redirect_defaults([[maybe_unused]] mock_redirect_flags flags) { mock_redirect_defaults([[maybe_unused]] mock_redirect_flags flags) {
mock_redirect(torrent::fd__bind, std::function<int(int socket, const sockaddr *address, socklen_t address_len)>([](int socket, const sockaddr *address, socklen_t address_len) {
return ::bind(socket, address, address_len);
}));
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); }));
} }
@@ -118,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 -1
View File
@@ -6,7 +6,7 @@
#include "test/helpers/mock_function.h" #include "test/helpers/mock_function.h"
#include "torrent/exceptions.h" #include "torrent/exceptions.h"
#include "torrent/net/poll.h" #include "torrent/system/poll.h"
const int test_thread::test_flag_pre_stop; const int test_thread::test_flag_pre_stop;
const int test_thread::test_flag_long_timeout; const int test_thread::test_flag_long_timeout;
+1 -18
View File
@@ -4,7 +4,7 @@
#include <array> #include <array>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/net/network_config.h> #include <torrent/runtime/network_config.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h> #include <torrent/utils/option_strings.h>
@@ -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::net::NetworkConfig::encryption_none);
FLAGS_LT_ENCRYPTION_ASSERT("none", torrent::net::NetworkConfig::encryption_none);
FLAGS_LT_ENCRYPTION_ASSERT("require_rc4", torrent::net::NetworkConfig::encryption_require_RC4);
FLAGS_LT_ENCRYPTION_ASSERT("require_RC4", torrent::net::NetworkConfig::encryption_require_RC4);
FLAGS_LT_ENCRYPTION_ASSERT("require_RC4 | enable_retry", torrent::net::NetworkConfig::encryption_require_RC4 | torrent::net::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();
}; };

Some files were not shown because too many files have changed in this diff Show More