Compare commits

...

89 Commits

Author SHA1 Message Date
Jari Sundell d8664e6b4c Changing listen/dht port changes the listening/dht ports. 2026-07-12 17:39:26 +02:00
Jari Sundell 7e907616a2 Moved ChunkManager out of public header directory. 2026-07-12 17:36:27 +02:00
Jari Sundell dcf13ab21a Moved all sync/diskspace related methods to MemoryManager. 2026-07-12 17:36:27 +02:00
Jari Sundell 03c4467a33 Added runtime::MemoryManager to split out unrelated features from ChunkManager. 2026-07-12 17:36:27 +02:00
Jari Sundell fc5ce92c46 Fixed command_base t_pod align static asserts. 2026-07-12 17:36:27 +02:00
Jari Sundell f1b22023a0 Reordered http queue slots to avoid race conditions. 2026-07-12 17:36:27 +02:00
Jari Sundell 928f3e58a8 Removed unused add/remove error-event code. 2026-07-12 17:36:27 +02:00
Jesse Miller 743363b405 Add handshake/stream encryption RPC commands
Replace protocol.encryption.set bitmask flags with
protocol.encryption.handshake.set and protocol.encryption.stream.set.
Add encryption_config to map commands to libtorrent EncryptionPolicy.
Startup default matches the old preset (allow_incoming, enable_retry,
prefer_plaintext → handshake=allow, stream=allow).

Update docs and examples. Peer list distinguishes RC4 (R/L),
handshake-only (H/h), and plain (r/l). Fix ui encrypted column
conditional. Add encryption config tests; remove OPTION_ENCRYPTION
parse tests. Drop the encryption command redirect.
2026-07-08 10:23:16 -06: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
rakshasa 2700b3141f Tagged release 0.16.11. 2026-05-01 11:29:09 +02:00
fffe 4fec56a243 assert valid read_length 2026-04-24 12:22:34 +02:00
fffe dcf24711ef refactor 2026-04-24 12:22:34 +02:00
fffe 4e3ad1ab62 fix off-by-one in SCgiTask::event_read 2026-04-24 12:22:34 +02:00
103 changed files with 3316 additions and 2065 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
permissions:
pull-requests: write
# OPTIONAL: auto-closing conversations requires the `contents` permission
contents: write
contents: read
steps:
- name: Sleep for 30 seconds
run: sleep 30s
+69 -27
View File
@@ -4,9 +4,8 @@ on: pull_request
jobs:
unit-tests:
ubuntu-base:
runs-on: ubuntu-22.04
steps:
- name: Update Packages
run: |
@@ -23,43 +22,86 @@ jobs:
- name: Build libtorrent
run: |
cd libtorrent
libtoolize
aclocal -I scripts
autoconf -i
autoheader
automake --add-missing
autoreconf -fiv
./configure
make
sudo make install
cd ..
rm -rf libtorrent
- uses: actions/checkout@v4
sudo make install DESTDIR=$GITHUB_WORKSPACE/libtorrent-dist
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Fetch base branch
name: installed-libtorrent
path: libtorrent-dist/
unit-tests-default:
runs-on: ubuntu-22.04
needs: ubuntu-base
steps:
- name: Update Packages
run: |
git remote add upstream "https://github.com/${{ github.event.pull_request.base.repo.full_name }}"
git fetch --no-tags --no-recurse-submodules upstream "${{ github.event.pull_request.base.ref }}"
sudo apt-get update
- 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
run: |
libtoolize
aclocal -I scripts
autoconf -i
autoheader
automake --add-missing
autoreconf -fiv
./configure
- name: Build Project
run: |
make
- name: Run Unit Tests
run: |
ls /usr/local/lib/
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib"
make check
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" make check
- name: Archive test/test-suite.log
if: success() || failure()
uses: actions/upload-artifact@v4
with:
name: 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
Makefile
aclocal.m4
actmp.*
ar-lib
autom4te.cache
compile
@@ -69,4 +70,5 @@ TAGS
###########################
src/rtorrent
test/rtorrent_Test*
test/test-suite.log.tmp
test-driver
-1
View File
@@ -7,7 +7,6 @@ nobase_dist_pkgdata_DATA = \
lua/rtorrent.lua
EXTRA_DIST= \
rak/regex.h \
scripts/checks.m4 \
scripts/common.m4 \
scripts/attributes.m4
+19 -17
View File
@@ -1,6 +1,6 @@
m4_pattern_allow([PKG_CHECK_EXISTS])
AC_INIT([rtorrent],[0.16.10],[sundell.software@gmail.com])
AC_INIT([rtorrent],[0.16.17],[sundell.software@gmail.com])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIRS([scripts])
@@ -10,15 +10,11 @@ AM_PROG_AR
LT_INIT
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.
TORRENT_REMOVE_UNWANTED(CXX, $CXX, -std=c++11 -std=gnu++11)
AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory])
AC_SYS_LARGEFILE
AC_DEFINE([API_VERSION], [23], [api version])
RAK_CHECK_CFLAGS
RAK_CHECK_CXXFLAGS
@@ -26,9 +22,6 @@ RAK_ENABLE_DEBUG
RAK_ENABLE_EXTRA_DEBUG
RAK_ENABLE_WERROR
TORRENT_ENABLE_ARCH
TORRENT_WITH_SYSROOT
AC_ARG_ENABLE(execinfo,
AS_HELP_STRING([--disable-execinfo],
[disable libexecinfo [[default=enable]]]),
@@ -41,20 +34,28 @@ AC_ARG_ENABLE(execinfo,
])
AX_PTHREAD([], AC_MSG_ERROR([requires pthread]))
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])
TORRENT_WITHOUT_NCURSES
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
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
PKG_CHECK_MODULES([ZLIB], [zlib])
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.10])
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.17])
AC_LANG_PUSH(C++)
TORRENT_WITH_XMLRPC_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_TINYXML2
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])
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 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"
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([
Makefile
+9 -15
View File
@@ -214,21 +214,15 @@ Add a preferred filename encoding to the list. The encodings are
attempted in the order they are inserted, if none match the torrent
default is used.
.TP
\fBencryption = \fIoption\fB,\fI\&...\fB\fR
Set how rtorrent should deal with encrypted Bittorrent connections. By
default, encryption is disabled, equivalent to specifying the option
\fBnone\fR\&. Alternatively, any number of the following
options may be specified:
\fBallow_incoming\fR (allow incoming encrypted connections),
\fBtry_outgoing\fR (use encryption for outgoing connections),
\fBrequire\fR (disable unencrypted handshakes),
\fBrequire_RC4\fR (also disable plaintext transmission after the
initial encrypted handshake),
\fBenable_retry\fR (if the initial outgoing connection fails, retry
with encryption turned on if it was off or off if it was on),
\fBprefer_plaintext\fR (choose plaintext when peer offers a choice
between plaintext transmission and RC4 encryption, otherwise RC4 will be used).
\fBprotocol.encryption.handshake.set\fR (deny, allow, prefer, require)
and \fBprotocol.encryption.stream.set\fR (deny, allow, prefer,
require) control how rtorrent deals with encrypted Bittorrent
connections. Handshake \fBallow\fR accepts plain or PE inbound and
tries plain first outbound with one PE retry on failure.
\fBprefer\fR tries PE first with one plain retry. Stream \fBallow\fR
and \fBprefer\fR offer both handshake-only and RC4 on outgoing PE
connections. The default is handshake=allow, stream=allow. Use
\fBprotocol.encryption\fR to inspect the effective policy.
.TP
\fBpeer_exchange = \fIyes | no\fB\fR
Enable/disable peer exchange for torrents that aren't marked private. Disabled by default.
+11 -17
View File
@@ -484,25 +484,19 @@ default is used.
</varlistentry>
<varlistentry>
<term>encryption = <replaceable>option</replaceable>,<replaceable>...</replaceable></term>
<term>protocol.encryption.handshake.set = <replaceable>policy</replaceable></term>
<listitem><para>
Set how rtorrent should deal with encrypted Bittorrent connections. By
default, encryption is disabled, equivalent to specifying the option
<emphasis>none</emphasis>. Alternatively, any number of the following
options may be specified:
</para><para>
<emphasis>allow_incoming</emphasis> (allow incoming encrypted connections),
<emphasis>try_outgoing</emphasis> (use encryption for outgoing connections),
<emphasis>require</emphasis> (disable unencrypted handshakes),
<emphasis>require_RC4</emphasis> (also disable plaintext transmission after the
initial encrypted handshake),
<emphasis>enable_retry</emphasis> (if the initial outgoing connection fails, retry
with encryption turned on if it was off or off if it was on),
<emphasis>prefer_plaintext</emphasis> (choose plaintext when peer offers a choice
between plaintext transmission and RC4 encryption, otherwise RC4 will be used).
Control how rtorrent deals with encrypted Bittorrent connections.
<emphasis>protocol.encryption.handshake.set</emphasis> (deny, allow, prefer,
require) and <emphasis>protocol.encryption.stream.set</emphasis> (deny, allow,
prefer, require). Handshake <emphasis>allow</emphasis> accepts plain or PE
inbound and tries plain first outbound with one PE retry on failure.
<emphasis>prefer</emphasis> tries PE first with one plain retry. Stream
<emphasis>allow</emphasis> and <emphasis>prefer</emphasis> offer both
handshake-only and RC4 on outgoing PE connections. The default is
handshake=allow, stream=allow. Use <emphasis>protocol.encryption</emphasis> to
inspect the effective policy.
</para></listitem>
</varlistentry>
+37 -7
View File
@@ -46,8 +46,12 @@
#session.path.set = ./session
# 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
# Close torrents when disk-space is low.
@@ -91,14 +95,40 @@
#schedule2 = ip_tick,0,1800,ip=rakshasa
#schedule2 = bind_tick,0,1800,bind=rakshasa
# Encryption options, set to none (default) or any combination of the following:
# allow_incoming, try_outgoing, require, require_RC4, enable_retry, prefer_plaintext
# --- Protocol encryption (PE handshake / optional RC4 stream) ---
# Default at startup: handshake=allow stream=allow
#
# The example value allows incoming encrypted connections, starts unencrypted
# outgoing connections but retries with encryption if they fail, preferring
# plain-text to RC4 encryption after the encrypted handshake.
# Handshake (PE handshake; incoming accepts plain or PE for allow/prefer):
# deny - plain BT handshake only both ways; no retry
# allow - accept plain or PE inbound; plain first outbound with one PE retry
# prefer - accept plain or PE inbound; PE first outbound with one plain retry
# require - PE handshake required both ways; no retry
#
# protocol.encryption.set = allow_incoming,enable_retry,prefer_plaintext
# protocol.encryption.handshake.set = allow
#
# Stream (cipher negotiation after PE handshake succeeds):
# deny - offer handshake-only; require handshake-only (no RC4)
# allow - incoming: prefer handshake-only if offered, else RC4
# outgoing: offer both handshake-only and RC4
# prefer - incoming: pick RC4 if offered, else handshake-only
# outgoing: offer both handshake-only and RC4
# require - offer RC4 only; require RC4
#
# Outgoing retry summary with handshake=allow (plain first):
# stream=allow/prefer -> plain BT -> PE (both)
# stream=deny -> plain BT -> PE (handshake-only)
# stream=require -> plain BT -> PE (RC4-only)
#
# Outgoing retry summary with handshake=prefer (PE first):
# stream=allow/prefer -> PE (both) -> plain BT
# stream=deny -> PE (handshake-only) -> plain BT
# stream=require -> PE (RC4-only) -> plain BT
#
# Negotiated stream cipher is logged at connection_handshake level.
#
# protocol.encryption.stream.set = allow
#
# Use protocol.encryption to inspect the effective policy.
# Enable DHT support for trackerless torrents or when all trackers are down.
# May be set to "disable" (completely disable DHT), "off" (do not start DHT),
+2 -1
View File
@@ -42,7 +42,8 @@ throttle.min_peers.seed.set = 30
throttle.max_peers.seed.set = 80
trackers.numwant.set = 80
protocol.encryption.set = allow_incoming,try_outgoing,enable_retry
protocol.encryption.handshake.set = allow
protocol.encryption.stream.set = prefer
# Limits for file handle resources, this is optimized for
# an `ulimit` of 1024 (a common default). You MUST leave
+2 -1
View File
@@ -72,7 +72,8 @@ rc.throttle.min_peers.seed = 30
rc.throttle.max_peers.seed = 80
rc.trackers.numwant = 80
rc.protocol.encryption.set('allow_incoming', 'try_outgoing', 'enable_retry')
rc.protocol.encryption.handshake.set('allow')
rc.protocol.encryption.stream.set('prefer')
-- Limits for file handle resources, this is optimized for
-- an `ulimit` of 1024 (a common default). You MUST leave
+1 -1
View File
@@ -12,7 +12,7 @@ echo "Client version: " `xmlrpc2scgi.py -p scgi://127.0.0.1:${PORT_NUMBER} syste
echo
echo "Generated by 'rtorrent/doc/scripts/print_option_string.sh' on `date -u`."
for i in strings.choke_heuristics strings.choke_heuristics.upload strings.choke_heuristics.download strings.connection_type strings.encryption strings.ip_filter strings.ip_tos strings.log_group strings.tracker_event strings.tracker_mode; do
for i in strings.choke_heuristics strings.choke_heuristics.upload strings.choke_heuristics.download strings.connection_type strings.encryption.handshake strings.encryption.stream strings.ip_filter strings.ip_tos strings.log_group strings.tracker_event strings.tracker_mode; do
echo
echo $i
echo `echo $i | tr 'a-z_.' '-'`
-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
# 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)
# or '14' (for the C++14 standard).
# CXXCPP to enable support. VERSION may be '11', '14', '17', '20', or
# '23' for the respective C++ standard version.
#
# 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.
@@ -36,13 +36,15 @@
# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
# Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.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
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 12
#serial 25
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
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"],
[$1], [14], [ax_cxx_compile_alternatives="14 1y"],
[$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_if([$2], [], [],
[$2], [ext], [],
@@ -102,9 +106,18 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
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 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 switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}" MSVC; do
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,
$cachevar,
[ac_save_CXX="$CXX"
@@ -148,23 +161,44 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
dnl Test body for checking C++11 support
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
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14]
)
dnl Test body for checking C++17 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
_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_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_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
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"
#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"
@@ -467,7 +515,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
#error "This is not a C++ compiler"
#elif __cplusplus < 201402L
#elif __cplusplus < 201402L && !defined _MSC_VER
#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"
#elif __cplusplus < 201703L
#elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 201703L
#error "This is not a C++17 compiler"
@@ -957,6 +1005,66 @@ 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
# 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,
# 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
# 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
# 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
# version number greater or equal to MINIMUM-VERSION will be accepted. If
# 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.
#
# 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
# the default behavior, give ':' as an action.
#
@@ -152,6 +160,7 @@
#
# LICENSE
#
# Copyright (c) 2023 Caleb Maclennan <caleb@alerque.com>
# Copyright (c) 2015 Reuben Thomas <rrt@sc3d.org>
# Copyright (c) 2014 Tim Perkins <tprk77@gmail.com>
#
@@ -181,7 +190,7 @@
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 42
#serial 47
dnl =========================================================================
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])
dnl Find a Lua interpreter.
m4_define_default([_AX_LUA_INTERPRETER_LIST],
[lua lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua50])
AM_COND_IF([LUAJIT],
[_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], [],
[ dnl No version check is needed. Find any Lua interpreter.
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'
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"])
AC_CACHE_CHECK([$_ax_check_text],
[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
_AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue])
_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 '
-- return a version number in X.Y format
local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)")
print(ver)'`]
print(ver or "")'`]
])
AS_IF([test "x$ax_cv_lua_version" = 'x'],
[AC_MSG_ERROR([invalid Lua version number])])
AC_SUBST([LUA_VERSION], [$ax_cv_lua_version])
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 At times (like when building shared libraries) you may want to know
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])
])
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.
AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1])
dnl Some default directories to search.
LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'`
m4_define_default([_AX_LUA_INCLUDE_LIST],
[ /usr/include/lua$LUA_VERSION \
/usr/include/lua-$LUA_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 Some default directories to search.
AM_COND_IF([LUAJIT],
[_ax_lua_include_list="
/usr/include/luajit-$LUAJIT_VERSION
/usr/include/luajit-$LUAJIT_SHORT_VERSION
/usr/local/include/luajit-$LUAJIT_VERSION
/usr/local/include/luajit-$LUAJIT_SHORT_VERSION"],
[_ax_lua_include_list="
/usr/include/lua$LUA_VERSION
/usr/include/lua-$LUA_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.
_ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
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
dnl Try some other directories if LUA_INCLUDE was not set.
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.
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
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_lauxlib_h])
AS_UNSET([ac_cv_header_luaconf_h])
AS_UNSET([ac_cv_header_luajit_h])
_ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$_ax_include_path"
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
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.
_ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB"
AC_SEARCH_LIBS([lua_load],
[ lua$LUA_VERSION \
lua$LUA_SHORT_VERSION \
lua-$LUA_VERSION \
lua-$LUA_SHORT_VERSION \
:liblua-$LUA_VERSION.so.0 \
:liblua-$LUA_SHORT_VERSION.so.0 \
lua \
],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'],
[$_ax_lua_extra_libs])
AM_COND_IF([LUAJIT],
[AC_SEARCH_LIBS([lua_load],
[ luajit$LUA_VERSION \
luajit$LUA_SHORT_VERSION \
luajit-$LUA_VERSION \
luajit-$LUA_SHORT_VERSION \
luajit],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'],
[$_ax_lua_extra_libs])],
[AC_SEARCH_LIBS([lua_load],
[ 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
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_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_MSG_RESULT(yes)
], [
@@ -86,6 +43,8 @@ AC_DEFUN([TORRENT_CHECK_KQUEUE], [
}
])],
[
use_kqueue=yes
AC_DEFINE(USE_KQUEUE, 1, Use kqueue.)
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_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
AC_DEFINE_UNQUOTED(DEFAULT_ADDRESS_SPACE_SIZE, [$withval])
@@ -171,51 +91,48 @@ AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [
AC_CHECK_SIZEOF(long)
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
AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 1024, Default address space size.)
fi
])
])
AC_DEFUN([TORRENT_WITH_FASTCGI], [
AC_ARG_WITH(fastcgi,
AS_HELP_STRING([--with-fastcgi=PATH],[enable FastCGI RPC support (DO NOT USE)]),
AC_DEFUN([TORRENT_CHECK_ATOMIC], [
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_MSG_RESULT(no)
elif test "$withval" = "yes"; then
CXXFLAGS="$CXXFLAGS"
LIBS="$LIBS -lfcgi"
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_LINK_IFELSE([AC_LANG_PROGRAM([[#include <cstdint>
#include <atomic>]],
[[std::atomic<uint64_t> x(0);
return x.load();]])],
[
AC_MSG_RESULT([yes])
ATOMIC_LIBS="-latomic"
],
[
AC_MSG_RESULT([unsupported])
AC_MSG_ERROR([Compiler target lacks proper 64-bit atomic support.])
])
AC_DEFINE(HAVE_FASTCGI, 1, Support for FastCGI.)
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
LIBS="$save_LIBS"
])
AC_LANG_POP(C++)
AC_SUBST([ATOMIC_LIBS])
])
@@ -260,6 +177,7 @@ AC_DEFUN([TORRENT_WITH_XMLRPC_C], [
])
])
AC_DEFUN([TORRENT_WITH_TINYXML2], [
AC_MSG_CHECKING(for tinyxml2)
@@ -273,6 +191,7 @@ AC_DEFUN([TORRENT_WITH_TINYXML2], [
])
])
AC_DEFUN([TORRENT_WITH_LUA], [
AC_ARG_WITH(lua,
AS_HELP_STRING([--with-lua],[enable LUA support]),
@@ -281,29 +200,45 @@ AC_DEFUN([TORRENT_WITH_LUA], [
AC_MSG_RESULT(no)
else
AX_PROG_LUA
AX_LUA_LIBS
AX_LUA_HEADERS
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"
# 1. Override AX_LUA_LIBS default crash behavior
AX_LUA_LIBS([have_lua_libs=yes], [have_lua_libs=no])
# 2. Override AX_LUA_HEADERS default crash behavior
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
],[
AC_MSG_RESULT(ignored)
])
])
AC_DEFUN([TORRENT_WITH_INOTIFY], [
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_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <sys/inotify.h>
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(failed)]
)
@@ -311,12 +246,14 @@ AC_DEFUN([TORRENT_WITH_INOTIFY], [
AC_LANG_POP(C++)
])
AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [
AC_CHECK_HEADERS(pthread.h)
AC_MSG_CHECKING(for pthread_setname_np type)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#define _GNU_SOURCE
#include <pthread.h>
#include <sys/types.h>
]], [[
@@ -341,6 +278,7 @@ AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [
])
])
AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [
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_ARG_WITH(systemd,
AS_HELP_STRING([--with-systemd],[enable systemd socket activation support [[default=no]]]),
@@ -375,3 +331,20 @@ AC_DEFUN([TORRENT_WITH_SYSTEMD], [
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_LANG_PUSH(C++)
AC_MSG_CHECKING(signedness of mincore parameter)
@@ -90,7 +26,7 @@ AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [
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_MSG_CHECKING(for cacheline)
AC_REQUIRE([AC_CANONICAL_HOST])
AC_MSG_CHECKING([for target cacheline size])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <stdlib.h>
#include <linux/cache.h>
void* vptr __cacheline_aligned;
void f() { posix_memalign(&vptr, SMP_CACHE_BYTES, 42); }
])],
[
AC_MSG_RESULT(found builtin)
dnl Need to fix this so that it uses the stuff defined by the system.
case "$host_os" in
linux*)
# REGION: Linux Kernel Extraction Loop
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
#include <stdlib.h>
#include <linux/cache.h>
void* vptr;
void f() {
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.)
], [
AC_MSG_RESULT(using default 128 bytes)
AC_DEFINE(LT_SMP_CACHE_BYTES, 128, Largest L1 cache size we know of should work on all archs.)
])
])
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)
if test "$torrent_cv_cacheline_size" -gt 0; then
AC_MSG_RESULT([linux builtin ($torrent_cv_cacheline_size bytes)])
AC_DEFINE_UNQUOTED([LT_SMP_CACHE_BYTES], [$torrent_cv_cacheline_size], [System-defined Linux L1 SMP cacheline size.])
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.])
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_ARG_ENABLE(instrumentation,
AS_HELP_STRING([--disable-instrumentation],
[disable instrumentation [[default=enabled]]]),
AS_HELP_STRING([--enable-instrumentation],
[enable instrumentation [[default=disabled]]]),
[
if test "$enableval" = "yes"; then
AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation)
AC_MSG_RESULT(yes)
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(no)
AC_MSG_RESULT(no)
fi
],[
AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation)
AC_MSG_RESULT(yes)
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_ENABLE_INTERRUPT_SOCKET], [
AC_ARG_ENABLE(interrupt-socket,
AS_HELP_STRING([--enable-interrupt-socket],
[enable interrupt socket [[default=no]]]),
[
if test "$enableval" = "yes"; then
AC_DEFINE(USE_INTERRUPT_SOCKET, 1, Use interrupt socket instead of pthread_kill)
fi
]
)
AC_DEFUN([TORRENT_ENABLE_CUSTOM_STACK_SIZE], [
AC_ARG_ENABLE([pthread-setstacksize],
[AS_HELP_STRING([--enable-pthread-setstacksize], [explicitly set pthread stack size (auto-enabled for musl)])],
[enable_pthread_setstacksize=$enableval],
[enable_pthread_setstacksize=auto])
AC_CANONICAL_HOST
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_ARG_ENABLE(ipv6,
AS_HELP_STRING([--enable-ipv6],
+8
View File
@@ -174,6 +174,8 @@ libsub_root_a_SOURCES = \
utils/list_focus.h \
utils/lockfile.cc \
utils/lockfile.h \
utils/watch_ready_queue.cc \
utils/watch_ready_queue.h \
\
command_download.cc \
command_dynamic.cc \
@@ -186,6 +188,8 @@ libsub_root_a_SOURCES = \
command_local.cc \
command_logging.cc \
command_network.cc \
encryption_config.cc \
encryption_config.h \
command_peer.cc \
command_throttle.cc \
command_tracker.cc \
@@ -203,4 +207,8 @@ libsub_root_a_SOURCES = \
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)\"
+101 -67
View File
@@ -5,11 +5,10 @@
#include <functional>
#include <netdb.h>
#include <unistd.h>
#include <rak/regex.h>
#include <fnmatch.h>
#include <torrent/rate.h>
#include <torrent/throttle.h>
#include <torrent/tracker/tracker.h>
#include <torrent/connection_manager.h>
#include <torrent/data/download_data.h>
#include <torrent/data/file.h>
#include <torrent/data/file_list.h>
@@ -18,6 +17,7 @@
#include <torrent/net/types.h>
#include <torrent/peer/connection_list.h>
#include <torrent/peer/peer_list.h>
#include <torrent/system/callbacks.h>
#include <torrent/utils/file_stat.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
@@ -32,31 +32,34 @@
#include "control.h"
#include "command_helpers.h"
std::string
torrent::string_utf8
retrieve_d_base_path(core::Download* download) {
if (download->file_list()->is_multi_file())
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) {
const std::string* base;
torrent::string_utf8 base_path;
if (download->file_list()->is_multi_file())
base = &download->file_list()->frozen_root_dir();
else if (download->file_list()->empty())
return std::string();
base_path = download->file_list()->frozen_root_dir();
else if (!download->file_list()->empty())
base_path = download->file_list()->at(0)->frozen_path();
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)
return *base;
else
return base->substr(split + 1);
return base_path;
return torrent::string_utf8::from_string(base_path.str().substr(split + 1));
}
torrent::Object
@@ -144,9 +147,9 @@ apply_d_directory(core::Download* download, const std::string& name) {
if (!download->file_list()->is_multi_file())
download->set_root_directory(name);
else if (name.empty() || *name.rbegin() == '/')
download->set_root_directory(name + download->info()->name());
download->set_root_directory(name + download->info()->name().str());
else
download->set_root_directory(name + "/" + download->info()->name());
download->set_root_directory(name + "/" + download->info()->name().str());
}
torrent::Object
@@ -160,8 +163,8 @@ apply_d_connection_type(core::Download* download, const std::string& name) {
torrent::Object
apply_d_choke_heuristics(core::Download* download, const std::string& name, bool is_down) {
torrent::Download::HeuristicType t =
(torrent::Download::HeuristicType)torrent::option_find_string(torrent::OPTION_CHOKE_HEURISTICS, name.c_str());
torrent::heuristics_enum t =
static_cast<torrent::heuristics_enum>(torrent::option_find_string(torrent::OPTION_CHOKE_HEURISTICS, name.c_str()));
if (is_down)
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;
}
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
apply_d_add_peer(core::Download* download, const std::string& arg) {
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());
auto callback_id = torrent::system::make_callback_id();
// 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) {
lt_log_print(torrent::LOG_TORRENT_WARN, "could not resolve hostname for added peer: %s", gai_strerror(err));
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.
torrent::Object resultRaw = torrent::Object::create_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;
@@ -359,7 +354,7 @@ f_multicall(core::Download* download, const torrent::Object::list_type& args) {
for (const auto& file : *download->file_list()) {
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;
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
// 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++) {
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);
if (!tracker.is_valid())
continue;
for (torrent::Object::list_const_iterator cItr = ++args.begin(); cItr != args.end(); cItr++) {
const std::string& cmd = cItr->as_string();
for (auto cItr = ++args.begin(); cItr != args.end(); cItr++) {
auto& cmd = cItr->as_string();
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
// parsing and searching command map for every single call.
torrent::Object resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list();
auto resultRaw = torrent::Object::create_list();
auto& result = resultRaw.as_list();
for (const auto& connection : *download->connection_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();
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
initialize_command_download() {
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_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.base_path", std::bind(&retrieve_d_base_path, std::placeholders::_1));
CMD2_DL("d.base_filename", std::bind(&retrieve_d_base_filename, std::placeholders::_1));
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_html", [](auto* download, auto) { return torrent::utils::copy_escape_html_str(download->info()->local_id()); });
CMD2_DL("d.bitfield", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(*download->download()->file_list()->bitfield()); });
CMD2_DL("d.base_path", [](auto* download, auto) { return retrieve_d_base_path(download).str(); });
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.creation_date", CMD2_ON_INFO(creation_date));
CMD2_DL("d.load_date", CMD2_ON_INFO(load_date));
CMD2_DL("d.name", [](auto* download, auto) { return download->info()->name().str(); });
CMD2_DL("d.name.hex", [](auto* download, auto) { return download->info()->name().object_hex(); });
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:
//
CMD2_DL ("d.up.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(up_rate)));
CMD2_DL ("d.up.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(up_rate)));
CMD2_DL ("d.down.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(down_rate)));
CMD2_DL ("d.down.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(down_rate)));
CMD2_DL ("d.skip.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(skip_rate)));
CMD2_DL ("d.skip.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(skip_rate)));
CMD2_DL ("d.up.rate", [](auto* download, auto) { return download->info()->up_rate()->rate(); });
CMD2_DL ("d.up.total", [](auto* download, auto) { return download->info()->up_rate()->total(); });
CMD2_DL ("d.down.rate", [](auto* download, auto) { return download->info()->down_rate()->rate(); });
CMD2_DL ("d.down.total", [](auto* download, auto) { return download->info()->down_rate()->total(); });
CMD2_DL ("d.skip.rate", [](auto* download, auto) { return download->info()->skip_rate()->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_VALUE_V ("d.peer_exchange.set", std::bind(&torrent::Download::set_pex_enabled, CMD2_BIND_DL, std::placeholders::_2));
CMD2_DL ("d.peer_exchange", [](auto* download, auto) { return download->info()->is_pex_enabled(); });
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.delete_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 1));
@@ -687,16 +701,16 @@ initialize_command_download() {
// Control functinos:
//
CMD2_DL ("d.is_open", CMD2_ON_INFO(is_open));
CMD2_DL ("d.is_active", CMD2_ON_INFO(is_active));
CMD2_DL ("d.is_open", [](auto* download, auto) { return download->info()->is_open(); });
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_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_private", CMD2_ON_INFO(is_private));
CMD2_DL ("d.is_pex_active", CMD2_ON_INFO(is_pex_active));
CMD2_DL ("d.is_private", [](auto* download, auto) { return download->info()->is_private(); });
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_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.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.finished", "rtorrent", "timestamp.finished");
CMD2_DL ("d.connection_current", std::bind(&torrent::option_as_string, torrent::OPTION_CONNECTION_TYPE, CMD2_ON_DL(connection_type)));
CMD2_DL_STRING("d.connection_current.set", std::bind(&apply_d_connection_type, std::placeholders::_1, std::placeholders::_2));
CMD2_DL ("d.connection_current", [](auto* d, auto) { return torrent::option_to_c_str_or_throw(torrent::OPTION_CONNECTION_TYPE, d->download()->connection_type()); });
CMD2_DL_STRING_V("d.connection_current.set", [](auto* d, auto arg) { apply_d_connection_type(d, arg); });
CMD2_DL_VAR_STRING("d.connection_leech", "rtorrent", "connection_leech");
CMD2_DL_VAR_STRING("d.connection_seed", "rtorrent", "connection_seed");
CMD2_DL ("d.up.choke_heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, CMD2_ON_DL(upload_choke_heuristic)));
CMD2_DL ("d.up.choke_heuristics", [](auto* d, auto) { return torrent::option_to_c_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, d->download()->upload_choke_heuristic()); });
CMD2_DL_STRING("d.up.choke_heuristics.set", std::bind(&apply_d_choke_heuristics, std::placeholders::_1, std::placeholders::_2, false));
CMD2_DL ("d.down.choke_heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, CMD2_ON_DL(download_choke_heuristic)));
CMD2_DL ("d.down.choke_heuristics", [](auto* d, auto) { return torrent::option_to_c_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, d->download()->download_choke_heuristic()); });
CMD2_DL_STRING("d.down.choke_heuristics.set", std::bind(&apply_d_choke_heuristics, std::placeholders::_1, std::placeholders::_2, true));
CMD2_DL_VAR_STRING("d.up.choke_heuristics.leech", "rtorrent", "choke_heuristics.up.leech");
@@ -830,7 +844,7 @@ initialize_command_download() {
CMD2_DL ("d.bytes_done", CMD2_ON_DL(bytes_done));
CMD2_DL ("d.ratio", std::bind(&retrieve_d_ratio, std::placeholders::_1));
CMD2_DL ("d.chunks_hashed", CMD2_ON_DL(chunks_hashed));
CMD2_DL ("d.free_diskspace", CMD2_ON_FL(free_diskspace));
CMD2_DL ("d.free_diskspace", [](auto* download, auto) { return download->file_list()->free_diskspace_no_cache(); });
CMD2_DL ("d.size_files", CMD2_ON_FL(size_files));
CMD2_DL ("d.size_bytes", CMD2_ON_FL(size_bytes));
@@ -898,8 +912,26 @@ initialize_command_download() {
rpc::rpc.mark_safe("d.local_id_html");
rpc::rpc.mark_safe("d.bitfield");
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.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.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_base");
rpc::rpc.mark_safe("d.creation_date");
@@ -941,6 +973,8 @@ initialize_command_download() {
rpc::rpc.mark_safe("d.size_chunks");
rpc::rpc.mark_safe("d.size_pex");
rpc::rpc.mark_safe("d.completed_bytes");
rpc::rpc.mark_safe("d.complete");
rpc::rpc.mark_safe("d.timestamp.finished");
rpc::rpc.mark_safe("d.bytes_done");
rpc::rpc.mark_safe("d.peers_accounted");
rpc::rpc.mark_safe("d.chunks_hashed");
+4 -2
View File
@@ -448,7 +448,8 @@ initialize_command_dynamic() {
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.connection_type", std::bind(&torrent::option_list_strings, torrent::OPTION_CONNECTION_TYPE));
CMD2_ANY ("strings.encryption", std::bind(&torrent::option_list_strings, torrent::OPTION_ENCRYPTION));
CMD2_ANY ("strings.encryption.handshake", std::bind(&torrent::option_list_strings, torrent::OPTION_ENCRYPTION_MODE));
CMD2_ANY ("strings.encryption.stream", std::bind(&torrent::option_list_strings, torrent::OPTION_ENCRYPTION_MODE));
CMD2_ANY ("strings.ip_filter", std::bind(&torrent::option_list_strings, torrent::OPTION_IP_FILTER));
CMD2_ANY ("strings.ip_tos", std::bind(&torrent::option_list_strings, torrent::OPTION_IP_TOS));
CMD2_ANY ("strings.log_group", std::bind(&torrent::option_list_strings, torrent::OPTION_LOG_GROUP));
@@ -472,7 +473,8 @@ initialize_command_dynamic() {
rpc::rpc.mark_safe("strings.choke_heuristics.upload");
rpc::rpc.mark_safe("strings.choke_heuristics.download");
rpc::rpc.mark_safe("strings.connection_type");
rpc::rpc.mark_safe("strings.encryption");
rpc::rpc.mark_safe("strings.encryption.handshake");
rpc::rpc.mark_safe("strings.encryption.stream");
rpc::rpc.mark_safe("strings.ip_filter");
rpc::rpc.mark_safe("strings.ip_tos");
rpc::rpc.mark_safe("strings.log_group");
+61 -39
View File
@@ -2,6 +2,8 @@
#include <functional>
#include <cstdio>
#include <string>
#include <vector>
#include <torrent/rate.h>
#include <torrent/hash_string.h>
#include <torrent/utils/log.h>
@@ -19,6 +21,7 @@
#include "rpc/command_scheduler.h"
#include "rpc/parse.h"
#include "rpc/parse_commands.h"
#include "utils/watch_ready_queue.h"
torrent::Object
apply_on_ratio(const torrent::Object& rawArgs) {
@@ -165,12 +168,14 @@ torrent::Object
apply_close_low_diskspace(int64_t arg, uint32_t skip_priority) {
bool closed = false;
torrent::FileList::cache_list cache;
for (auto download : *control->core()->download_list()) {
if (!download->is_downloading())
continue;
if (download->priority() >= skip_priority)
continue;
if (download->file_list()->free_diskspace() >= (uint64_t)arg)
if (download->file_list()->free_diskspace(cache) >= (uint64_t)arg)
continue;
control->core()->download_list()->close(download);
@@ -253,11 +258,12 @@ torrent::Object
d_multicall_filtered(const torrent::Object::list_type& args) {
if (args.size() < 2)
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
core::ViewManager* viewManager = control->view_manager();
core::ViewManager::iterator view_itr = viewManager->find(arg->as_string().empty() ? "default" : arg->as_string());
auto* viewManager = control->view_manager();
auto view_itr = viewManager->find(arg->as_string().empty() ? "default" : arg->as_string());
if (view_itr == viewManager->end())
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);
// Generate result by iterating over all items
torrent::Object resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list();
auto resultRaw = torrent::Object::create_list();
auto& result = resultRaw.as_list();
++arg; // skip to first command
for (const auto& item : dlist) {
@@ -291,59 +298,74 @@ call_watch_command(const std::string& command, const std::string& path) {
}
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)
throw torrent::input_error("Too few arguments.");
auto& path = args.front().as_string();
auto& command = args.back().as_string();
std::string expanded_path = expand_path(path);
if (!control->directory_events()->open())
throw torrent::input_error("Could not open inotify:" + std::string(std::strerror(errno)));
control->directory_events()->notify_on(path.c_str(),
torrent::directory_events::flag_on_added | torrent::directory_events::flag_on_updated,
std::bind(&call_watch_command, command, std::placeholders::_1));
torrent::watch_descriptor::slot_string slot =
flags == torrent::directory_events::flag_on_ready ?
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();
}
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
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 ("stop_untied", std::bind(&apply_stop_untied));
CMD2_ANY ("close_untied", std::bind(&apply_close_untied));
CMD2_ANY ("remove_untied", std::bind(&apply_remove_untied));
CMD2_ANY ("start_tied", [](auto, auto) { return apply_start_tied(); });
CMD2_ANY ("stop_untied", [](auto, auto) { return apply_stop_untied(); });
CMD2_ANY ("close_untied", [](auto, auto) { return apply_close_untied(); });
CMD2_ANY ("remove_untied", [](auto, auto) { return apply_remove_untied(); });
// TODO: Deprecate schedule2 in the future.
CMD2_ANY_LIST ("schedule", std::bind(&apply_schedule, std::placeholders::_2));
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_LIST ("schedule", [](auto, auto& args) { return apply_schedule(args); });
CMD2_ANY_STRING_V("schedule.remove", [](auto, auto& str) { return control->command_scheduler()->erase_str(str); });
CMD2_ANY_STRING_V("import", std::bind(&apply_import, std::placeholders::_2));
CMD2_ANY_STRING_V("try_import", std::bind(&apply_try_import, std::placeholders::_2));
CMD2_ANY_STRING_V("import", [](auto, auto& str) { return apply_import(str); });
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.verbose", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_tied));
CMD2_ANY_LIST ("load.start", std::bind(&apply_load, std::placeholders::_2,
core::Manager::create_quiet | 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", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_quiet | 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", std::bind(&apply_load, std::placeholders::_2,
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_LIST ("load.normal", [](auto, auto& args) { return apply_load(args, core::Manager::create_quiet | 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", [](auto, auto& args) { return apply_load(args, 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.raw", [](auto, auto& args) { return apply_load(args, 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_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_verbose", [](auto, auto& args) { return apply_load(args, 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.normal", std::bind(&apply_close_low_diskspace, std::placeholders::_2, 3));
CMD2_ANY_VALUE ("close_low_diskspace", [](auto, auto& arg) { return apply_close_low_diskspace(arg, 99); });
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 ("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 ("download_list", [](auto, auto& args) { return apply_download_list(args); });
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("stop_untied");
@@ -353,6 +375,6 @@ initialize_command_events() {
rpc::rpc.mark_safe("close_low_diskspace");
rpc::rpc.mark_safe("close_low_diskspace.normal");
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");
}
+50 -23
View File
@@ -24,24 +24,34 @@ apply_f_path(torrent::File* file) {
if (file->path()->empty())
return std::string();
torrent::Object resultRaw(*file->path()->begin());
torrent::Object::string_type& result = resultRaw.as_string();
torrent::Object result_raw(file->path()->begin()->str());
auto& result = result_raw.as_string();
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
apply_f_path_components(torrent::File* file) {
torrent::Object resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list();
apply_f_path_components(torrent::File* file, int output_type) {
auto result_raw = torrent::Object::create_list();
auto& result = result_raw.as_list();
for (const auto& itr : *file->path())
result.push_back(itr);
for (const auto& itr : *file->path()) {
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
@@ -57,7 +67,7 @@ apply_fi_filename_last(torrent::FileListIterator* itr) {
if (itr->depth() >= itr->file()->path()->size())
return "ERROR";
return itr->file()->path()->at(itr->depth());
return itr->file()->path()->at(itr->depth()).str();
}
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.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_chunks", std::bind(&torrent::File::size_chunks, std::placeholders::_1));
CMD2_FILE("f.completed_chunks", std::bind(&torrent::File::completed_chunks, std::placeholders::_1));
CMD2_FILE("f.size_bytes", [](auto* file, auto) { return file->size_bytes(); });
CMD2_FILE("f.size_chunks", [](auto* file, auto) { return file->size_chunks(); });
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.range_first", std::bind(&torrent::File::range_first, std::placeholders::_1));
CMD2_FILE("f.range_second", std::bind(&torrent::File::range_second, std::placeholders::_1));
CMD2_FILE("f.offset", [](auto* file, auto) { return file->offset(); });
CMD2_FILE("f.range_first", [](auto* file, auto) { return file->range_first(); });
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_VALUE_V("f.priority.set", std::bind(&apply_f_set_priority, std::placeholders::_1, std::placeholders::_2));
CMD2_FILE("f.priority", [](auto* file, auto) { return file->priority(); });
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_components", std::bind(&apply_f_path_components, std::placeholders::_1));
CMD2_FILE("f.path_depth", std::bind(&apply_f_path_depth, std::placeholders::_1));
CMD2_FILE("f.frozen_path", std::bind(&torrent::File::frozen_path, std::placeholders::_1));
CMD2_FILE("f.path", [](auto* file, auto) { return apply_f_path(file); });
CMD2_FILE("f.path_components", [](auto* file, auto) { return apply_f_path_components(file, 0); });
CMD2_FILE("f.path_components.hex", [](auto* file, auto) { return apply_f_path_components(file, 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_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_depth");
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.size_bytes");
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.back()->set_name(arg);
cg_list_hack.back()->up_queue()->set_heuristics(torrent::choke_queue::HEURISTICS_UPLOAD_LEECH);
cg_list_hack.back()->down_queue()->set_heuristics(torrent::choke_queue::HEURISTICS_DOWNLOAD_LEECH);
cg_list_hack.back()->up_queue()->set_heuristics(torrent::HEURISTICS_UPLOAD_LEECH);
cg_list_hack.back()->down_queue()->set_heuristics(torrent::HEURISTICS_DOWNLOAD_LEECH);
return torrent::Object();
}
@@ -337,54 +337,51 @@ options.
void
initialize_command_groups() {
CMD2_ANY ("choke_group.list", std::bind(&apply_cg_list));
CMD2_ANY_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2));
CMD_ANY ("choke_group.list", std::bind(&apply_cg_list));
CMD_ANY_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2));
#if USE_CHOKE_GROUP
CMD2_ANY ("choke_group.size", std::bind(&torrent::ResourceManager::group_size, torrent::resource_manager()));
CMD2_ANY_STRING ("choke_group.index_of", std::bind(&torrent::ResourceManager::group_index_of, torrent::resource_manager(), std::placeholders::_2));
CMD_ANY ("choke_group.size", std::bind(&torrent::ResourceManager::group_size, torrent::resource_manager()));
CMD_ANY_STRING ("choke_group.index_of", std::bind(&torrent::ResourceManager::group_index_of, torrent::resource_manager(), std::placeholders::_2));
#else
apply_cg_insert("default");
CMD2_ANY ("choke_group.size", std::bind(&std::vector<torrent::choke_group*>::size, cg_list_hack));
CMD2_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2));
CMD_ANY ("choke_group.size", std::bind(&std::vector<torrent::choke_group*>::size, cg_list_hack));
CMD_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2));
#endif
// Commands specific for a group. Supports as the first argument the
// name, the index or a negative index.
CMD2_ANY ("choke_group.general.size", std::bind(&torrent::choke_group::size, CG_GROUP_AT()));
CMD_ANY ("choke_group.general.size", std::bind(&torrent::choke_group::size, CG_GROUP_AT()));
CMD2_ANY ("choke_group.tracker.mode", std::bind(&torrent::option_as_string, torrent::OPTION_TRACKER_MODE,
std::bind(&torrent::choke_group::tracker_mode, CG_GROUP_AT())));
CMD2_ANY_LIST ("choke_group.tracker.mode.set", std::bind(&apply_cg_tracker_mode_set, std::placeholders::_2));
CMD_ANY ("choke_group.tracker.mode", [](auto, auto arg) { return torrent::option_to_str_or_throw(torrent::OPTION_TRACKER_MODE, cg_get_group(arg)->tracker_mode()); });
CMD_ANY_LIST ("choke_group.tracker.mode.set", [](auto, auto arg) { return apply_cg_tracker_mode_set(arg); });
CMD2_ANY ("choke_group.all.up.update_balance", std::bind(&apply_cg_all_update_balance, true));
CMD2_ANY ("choke_group.all.down.update_balance", std::bind(&apply_cg_all_update_balance, false));
CMD_ANY ("choke_group.all.up.update_balance", std::bind(&apply_cg_all_update_balance, true));
CMD_ANY ("choke_group.all.down.update_balance", std::bind(&apply_cg_all_update_balance, false));
CMD2_ANY ("choke_group.up.rate", std::bind(&torrent::choke_group::up_rate, CG_GROUP_AT()));
CMD2_ANY ("choke_group.down.rate", std::bind(&torrent::choke_group::down_rate, CG_GROUP_AT()));
CMD_ANY ("choke_group.up.rate", std::bind(&torrent::choke_group::up_rate, CG_GROUP_AT()));
CMD_ANY ("choke_group.down.rate", std::bind(&torrent::choke_group::down_rate, CG_GROUP_AT()));
CMD2_ANY ("choke_group.up.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD2_ANY ("choke_group.up.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD2_ANY_LIST ("choke_group.up.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, true));
CMD_ANY ("choke_group.up.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD_ANY ("choke_group.up.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD_ANY_LIST ("choke_group.up.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, true));
CMD2_ANY ("choke_group.up.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD2_ANY ("choke_group.up.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD2_ANY ("choke_group.up.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD2_ANY ("choke_group.up.heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS,
std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::up_queue))));
CMD2_ANY_LIST ("choke_group.up.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, true));
CMD_ANY ("choke_group.up.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD_ANY ("choke_group.up.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD_ANY ("choke_group.up.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD_ANY ("choke_group.up.heuristics", [](auto, auto arg) { return torrent::option_to_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, cg_get_group(arg)->up_queue()->heuristics()); });
CMD_ANY_LIST ("choke_group.up.heuristics.set", [](auto, auto arg) { return apply_cg_heuristics_set(arg, true); });
CMD2_ANY ("choke_group.down.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD2_ANY ("choke_group.down.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD2_ANY_LIST ("choke_group.down.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, false));
CMD_ANY ("choke_group.down.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD_ANY ("choke_group.down.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD_ANY_LIST ("choke_group.down.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, false));
CMD2_ANY ("choke_group.down.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD2_ANY ("choke_group.down.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD2_ANY ("choke_group.down.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD2_ANY ("choke_group.down.heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS,
std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::down_queue))));
CMD2_ANY_LIST ("choke_group.down.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, false));
CMD_ANY ("choke_group.down.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD_ANY ("choke_group.down.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD_ANY ("choke_group.down.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD_ANY ("choke_group.down.heuristics", [](auto, auto arg) { return torrent::option_to_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, cg_get_group(arg)->down_queue()->heuristics()); });
CMD_ANY_LIST ("choke_group.down.heuristics.set", [](auto, auto arg) { return apply_cg_heuristics_set(arg, false); });
rpc::rpc.mark_safe("choke_group.list");
rpc::rpc.mark_safe("choke_group.size");
+19 -1
View File
@@ -7,6 +7,25 @@
void initialize_commands();
//
// Aliases with CMD_* for the below
//
#define CMD_ANY(key, slot) CMD2_ANY(key, slot)
#define CMD_ANY_P(key, slot) CMD2_ANY_P(key, slot)
#define CMD_REDIRECT(key, slot) CMD2_REDIRECT(key, slot)
#define CMD_REDIRECT_NO_EXPORT(key, slot) CMD2_REDIRECT_NO_EXPORT(key, slot)
#define CMD_ANY_STRING(key, slot) CMD2_ANY_STRING(key, slot)
#define CMD_ANY_STRING_V(key, slot) CMD2_ANY_STRING_V(key, slot)
#define CMD_ANY_LIST(key, slot) CMD2_ANY_LIST(key, slot)
#define CMD_VAR_VALUE(key, value) CMD2_VAR_VALUE(key, value)
#define CMD_VAR_BOOL(key, value) CMD2_VAR_BOOL(key, value)
#define CMD_VAR_STRING(key, value) CMD2_VAR_STRING(key, value)
#define CMD_VAR_C_STRING(key, value) CMD2_VAR_C_STRING(key, value)
#define CMD_VAR_LIST(key) CMD2_VAR_LIST(key)
#define CMD_ANY_V(key, slot) CMD2_ANY_V(key, slot)
#define CMD_ANY_VALUE_V(key, slot) CMD2_ANY_VALUE_V(key, slot)
//
// New std::function based command_base helper functions:
//
@@ -115,7 +134,6 @@ void initialize_commands();
#define CMD2_REDIRECT_TRACKER(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_tracker_target | rpc::CommandMap::flag_dont_delete);
//
// Conversion of return types:
//
+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_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);
+133 -114
View File
@@ -8,9 +8,11 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <torrent/torrent.h>
#include <torrent/chunk_manager.h>
#include <torrent/data/file_manager.h>
#include <torrent/data/chunk_utils.h>
#include <torrent/runtime/runtime.h>
#include <torrent/runtime/memory_manager.h>
#include <torrent/runtime/socket_manager.h>
#include <torrent/utils/chrono.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());
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",
"schedule_remove2=group." + name + ".ratio"));
"schedule_remove=group." + name + ".ratio"));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.command", "simple",
"d.try_close= ;d.ignore_commands.set=1"));
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.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;
}
@@ -199,142 +188,160 @@ cmd_file_append(const torrent::Object::list_type& args) {
void
initialize_command_local() {
core::DownloadList* dList = control->core()->download_list();
torrent::ChunkManager* chunkManager = torrent::chunk_manager();
torrent::FileManager* fileManager = torrent::file_manager();
CMD2_ANY ("system.hostname", std::bind(&system_hostname));
CMD2_ANY ("system.pid", std::bind(&getpid));
if (rpc::call_command_value("method.use_deprecated") == 1) {
CMD_ANY_LIST ("file.append", std::bind(&cmd_file_append, std::placeholders::_2));
}
CMD2_VAR_C_STRING("system.api_version", (int64_t)API_VERSION);
CMD2_VAR_C_STRING("system.client_version", PACKAGE_VERSION);
CMD2_VAR_C_STRING("system.library_version", torrent::version());
CMD_ANY ("system.hostname", std::bind(&system_hostname));
CMD_ANY ("system.pid", std::bind(&getpid));
CMD2_VAR_VALUE ("system.file.allocate", 0);
CMD2_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
CMD2_VAR_VALUE ("system.file.split_size", -1);
CMD2_VAR_STRING ("system.file.split_suffix", ".part");
CMD_VAR_C_STRING("system.api_version", (int64_t)API_VERSION);
CMD_VAR_C_STRING("system.client_version", PACKAGE_VERSION);
CMD_VAR_C_STRING("system.library_version", torrent::runtime::version());
CMD2_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size,
CMD_VAR_VALUE ("system.file.allocate", 0);
CMD_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
CMD_VAR_VALUE ("system.file.split_size", -1);
CMD_VAR_STRING ("system.file.split_suffix", ".part");
CMD_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size,
(utils::FileStatusCache::base_type*)control->core()->file_status_cache()));
CMD2_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache()));
CMD_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache()));
CMD2_VAR_BOOL ("file.prioritize_toc", 0);
CMD2_VAR_LIST ("file.prioritize_toc.first");
CMD2_VAR_LIST ("file.prioritize_toc.last");
CMD_VAR_BOOL ("file.prioritize_toc", 0);
CMD_VAR_LIST ("file.prioritize_toc.first");
CMD_VAR_LIST ("file.prioritize_toc.last");
CMD2_ANY ("system.files.advise_random", std::bind(&FM_t::advise_random, fileManager));
CMD2_ANY_VALUE_V ("system.files.advise_random.set", std::bind(&FM_t::set_advise_random, fileManager, std::placeholders::_2));
CMD2_ANY ("system.files.advise_random.hashing", std::bind(&FM_t::advise_random_hashing, fileManager));
CMD2_ANY_VALUE_V ("system.files.advise_random.hashing.set", std::bind(&FM_t::set_advise_random_hashing, fileManager, std::placeholders::_2));
CMD2_ANY ("system.files.session.fdatasync", [](auto, auto) { return session_thread::manager()->use_fsyncdisk(); });
CMD2_ANY_VALUE_V ("system.files.session.fdatasync.set", [](auto, auto& value) { return session_thread::manager()->set_use_fsyncdisk(value); });
CMD_ANY ("system.files.advise_random", std::bind(&FM_t::advise_random, fileManager));
CMD_ANY_VALUE_V ("system.files.advise_random.set", std::bind(&FM_t::set_advise_random, fileManager, std::placeholders::_2));
CMD_ANY ("system.files.advise_random.hashing", std::bind(&FM_t::advise_random_hashing, fileManager));
CMD_ANY_VALUE_V ("system.files.advise_random.hashing.set", std::bind(&FM_t::set_advise_random_hashing, fileManager, std::placeholders::_2));
CMD_ANY ("system.files.session.fdatasync", [](auto, auto) { return session_thread::manager()->use_fsyncdisk(); });
CMD_ANY_VALUE_V ("system.files.session.fdatasync.set", [](auto, auto& value) { return session_thread::manager()->set_use_fsyncdisk(value); });
CMD2_ANY ("system.files.opened_counter", std::bind(&FM_t::files_opened_counter, fileManager));
CMD2_ANY ("system.files.closed_counter", std::bind(&FM_t::files_closed_counter, fileManager));
CMD2_ANY ("system.files.failed_counter", std::bind(&FM_t::files_failed_counter, fileManager));
CMD_ANY ("system.files.opened_counter", std::bind(&FM_t::files_opened_counter, fileManager));
CMD_ANY ("system.files.closed_counter", std::bind(&FM_t::files_closed_counter, fileManager));
CMD_ANY ("system.files.failed_counter", std::bind(&FM_t::files_failed_counter, fileManager));
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 {
return torrent::this_thread::cached_seconds().count();
});
CMD2_ANY ("system.time_seconds", []([[maybe_unused]] auto t, [[maybe_unused]] auto o) -> torrent::Object {
return torrent::utils::cast_seconds(torrent::utils::time_since_epoch()).count();
});
CMD2_ANY ("system.time_usec", []([[maybe_unused]] auto t, [[maybe_unused]] auto o) -> torrent::Object {
return torrent::utils::time_since_epoch().count();
});
CMD_ANY ("system.time", [](auto, auto) { return torrent::this_thread::cached_seconds().count(); });
CMD_ANY ("system.time_seconds", [](auto, auto) { return torrent::utils::cast_seconds(torrent::utils::time_since_epoch()).count(); });
CMD_ANY ("system.time_usec", [](auto, auto) { return torrent::utils::time_since_epoch().count(); });
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));
CMD2_ANY_V ("system.shutdown.quick", std::bind(&Control::receive_quick_shutdown, control));
CMD2_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
CMD_ANY_V ("system.shutdown.normal", [](auto, auto) { control->receive_normal_shutdown(); });
CMD_ANY_V ("system.shutdown.quick", [](auto, auto) { control->receive_quick_shutdown(); });
CMD2_ANY ("system.cwd", std::bind(&system_get_cwd));
CMD2_ANY_STRING ("system.cwd.set", std::bind(&system_set_cwd, std::placeholders::_2));
CMD_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
CMD2_ANY ("pieces.sync.always_safe", std::bind(&CM_t::safe_sync, chunkManager));
CMD2_ANY_VALUE_V ("pieces.sync.always_safe.set", std::bind(&CM_t::set_safe_sync, chunkManager, std::placeholders::_2));
CMD2_ANY ("pieces.sync.safe_free_diskspace", std::bind(&CM_t::safe_free_diskspace, chunkManager));
CMD2_ANY ("pieces.sync.timeout", std::bind(&CM_t::timeout_sync, chunkManager));
CMD2_ANY_VALUE_V ("pieces.sync.timeout.set", std::bind(&CM_t::set_timeout_sync, chunkManager, std::placeholders::_2));
CMD2_ANY ("pieces.sync.timeout_safe", std::bind(&CM_t::timeout_safe_sync, chunkManager));
CMD2_ANY_VALUE_V ("pieces.sync.timeout_safe.set", std::bind(&CM_t::set_timeout_safe_sync, chunkManager, std::placeholders::_2));
CMD2_ANY ("pieces.sync.queue_size", std::bind(&CM_t::sync_queue_size, chunkManager));
CMD_ANY ("system.cwd", [](auto, auto) { return system_get_cwd(); });
CMD_ANY_STRING ("system.cwd.set", [](auto, auto& str) { return system_set_cwd(str); });
CMD2_ANY ("pieces.preload.type", std::bind(&CM_t::preload_type, chunkManager));
CMD2_ANY_VALUE_V ("pieces.preload.type.set", std::bind(&CM_t::set_preload_type, chunkManager, std::placeholders::_2));
CMD2_ANY ("pieces.preload.min_size", std::bind(&CM_t::preload_min_size, chunkManager));
CMD2_ANY_VALUE_V ("pieces.preload.min_size.set", std::bind(&CM_t::set_preload_min_size, chunkManager, std::placeholders::_2));
CMD2_ANY ("pieces.preload.min_rate", std::bind(&CM_t::preload_required_rate, chunkManager));
CMD2_ANY_VALUE_V ("pieces.preload.min_rate.set", std::bind(&CM_t::set_preload_required_rate, chunkManager, std::placeholders::_2));
CMD_ANY ("system.sockets.size", [](auto, auto) { return torrent::runtime::socket_manager()->size(); });
CMD_ANY ("system.sockets.max_size", [](auto, auto) { return torrent::runtime::socket_manager()->max_size(); });
CMD_ANY_VALUE_V ("system.sockets.max_size.set", [](auto, auto& value) { return torrent::runtime::socket_manager()->set_max_size_and_adjust(value); });
CMD_ANY_V ("system.sockets.adjust_alloc", [](auto, auto) { torrent::runtime::socket_manager()->adjust_allocation(); });
CMD2_ANY ("pieces.memory.current", std::bind(&CM_t::memory_usage, chunkManager));
CMD2_ANY ("pieces.memory.sync_queue", std::bind(&CM_t::sync_queue_memory_usage, chunkManager));
CMD2_ANY ("pieces.memory.block_count", std::bind(&CM_t::memory_block_count, chunkManager));
CMD2_ANY ("pieces.memory.max", std::bind(&CM_t::max_memory_usage, chunkManager));
CMD2_ANY_VALUE_V ("pieces.memory.max.set", std::bind(&CM_t::set_max_memory_usage, chunkManager, std::placeholders::_2));
CMD2_ANY ("pieces.stats_preloaded", std::bind(&CM_t::stats_preloaded, chunkManager));
CMD2_ANY ("pieces.stats_not_preloaded", std::bind(&CM_t::stats_not_preloaded, chunkManager));
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
auto category = static_cast<torrent::runtime::socket_manager_category_t>(i);
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
CMD2_ANY ("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); });
CMD_ANY (category_name + ".min_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_min_allocation(category); });
CMD_ANY (category_name + ".max_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_allocation(category); });
CMD2_ANY ("pieces.hash.queue_size", std::bind(&torrent::main_thread::hash_queue_size));
CMD2_VAR_BOOL ("pieces.hash.on_completion", true);
if (i == 0)
continue;
CMD2_VAR_STRING ("directory.default", "./");
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", "");
CMD2_ANY ("session.path", [](auto, auto) { return session_thread::manager()->path(); });
CMD2_ANY_STRING_V("session.path.set", [](auto, auto& str) { return session_thread::manager()->set_path(str); });
CMD2_ANY ("session.use_lock", [](auto, auto) { return session_thread::manager()->use_lock(); });
CMD2_ANY_VALUE_V ("session.use_lock.set", [](auto, auto& value) { return session_thread::manager()->set_use_lock(value); });
CMD2_VAR_BOOL ("session.on_completion", true);
CMD_ANY ("pieces.sync.always_safe", [](auto, auto) { return torrent::runtime::memory_manager()->safe_sync(); });
CMD_ANY_VALUE_V ("pieces.sync.always_safe.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_safe_sync(value); });
CMD_ANY ("pieces.sync.safe_free_diskspace", [](auto, auto) { return torrent::runtime::memory_manager()->sync_safe_free_diskspace(); });
CMD_ANY ("pieces.sync.timeout", [](auto, auto) { return torrent::runtime::memory_manager()->timeout_sync().count(); });
CMD_ANY_VALUE_V ("pieces.sync.timeout.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_timeout_sync(value); });
// 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(); });
CMD2_ANY_STRING_V("magnet.path.set", [](auto, auto& str) { return control->core()->set_magnet_path(str); });
CMD_ANY ("pieces.stats_preloaded", [](auto, auto) { return torrent::runtime::memory_manager()->stats_preloaded(); });
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
rpc::LuaEngine* lua_engine = control->lua_engine();
CMD2_ANY ("lua.execute", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, 0));
CMD2_ANY ("lua.execute.str", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, rpc::LuaEngine::flag_string));
CMD_ANY ("lua.execute", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, 0));
CMD_ANY ("lua.execute.str", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, rpc::LuaEngine::flag_string));
#endif
#define CMD2_EXECUTE(key, flags) \
CMD2_ANY(key, std::bind(&rpc::ExecFile::execute_object, &rpc::execFile, std::placeholders::_2, flags));
#define CMD_EXECUTE(key, flags) \
CMD_ANY(key, std::bind(&rpc::ExecFile::execute_object, &rpc::execFile, std::placeholders::_2, flags));
CMD2_EXECUTE ("execute", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
CMD2_EXECUTE ("execute2", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
CMD2_EXECUTE ("execute.throw", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
CMD2_EXECUTE ("execute.throw.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background);
CMD2_EXECUTE ("execute.nothrow", rpc::ExecFile::flag_expand_tilde);
CMD2_EXECUTE ("execute.nothrow.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_background);
CMD2_EXECUTE ("execute.raw", rpc::ExecFile::flag_throw);
CMD2_EXECUTE ("execute.raw.bg", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background);
CMD2_EXECUTE ("execute.raw_nothrow", 0);
CMD2_EXECUTE ("execute.raw_nothrow.bg", rpc::ExecFile::flag_background);
CMD2_EXECUTE ("execute.capture", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
CMD2_EXECUTE ("execute.capture_nothrow", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
CMD2_ANY_LIST ("file.append", std::bind(&cmd_file_append, std::placeholders::_2));
CMD_EXECUTE ("execute", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
CMD_EXECUTE ("execute.throw", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
CMD_EXECUTE ("execute.throw.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background);
CMD_EXECUTE ("execute.nothrow", rpc::ExecFile::flag_expand_tilde);
CMD_EXECUTE ("execute.nothrow.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_background);
CMD_EXECUTE ("execute.raw", rpc::ExecFile::flag_throw);
CMD_EXECUTE ("execute.raw.bg", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background);
CMD_EXECUTE ("execute.raw_nothrow", 0);
CMD_EXECUTE ("execute.raw_nothrow.bg", rpc::ExecFile::flag_background);
CMD_EXECUTE ("execute.capture", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
CMD_EXECUTE ("execute.capture_nothrow", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
// TODO: Convert to new command types:
*rpc::command_base::argument(0) = "placeholder.0";
*rpc::command_base::argument(1) = "placeholder.1";
*rpc::command_base::argument(2) = "placeholder.2";
*rpc::command_base::argument(3) = "placeholder.3";
CMD2_ANY_P("argument.0", std::bind(&rpc::command_base::argument_ref, 0));
CMD2_ANY_P("argument.1", std::bind(&rpc::command_base::argument_ref, 1));
CMD2_ANY_P("argument.2", std::bind(&rpc::command_base::argument_ref, 2));
CMD2_ANY_P("argument.3", std::bind(&rpc::command_base::argument_ref, 3));
CMD_ANY_P("argument.0", std::bind(&rpc::command_base::argument_ref, 0));
CMD_ANY_P("argument.1", std::bind(&rpc::command_base::argument_ref, 1));
CMD_ANY_P("argument.2", std::bind(&rpc::command_base::argument_ref, 2));
CMD_ANY_P("argument.3", std::bind(&rpc::command_base::argument_ref, 3));
CMD2_ANY_LIST ("group.insert", std::bind(&group_insert, std::placeholders::_2));
CMD_ANY_LIST ("group.insert", std::bind(&group_insert, std::placeholders::_2));
rpc::rpc.mark_safe("system.api_version");
rpc::rpc.mark_safe("system.client_version");
@@ -343,6 +350,18 @@ initialize_command_local() {
rpc::rpc.mark_safe("system.file.split_size");
rpc::rpc.mark_safe("system.file.split_suffix");
rpc::rpc.mark_safe("system.sockets.size");
rpc::rpc.mark_safe("system.sockets.max_size");
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
rpc::rpc.mark_safe(category_name + ".size");
rpc::rpc.mark_safe(category_name + ".max_size");
rpc::rpc.mark_safe(category_name + ".min_alloc");
rpc::rpc.mark_safe(category_name + ".max_alloc");
}
rpc::rpc.mark_safe("directory.default");
rpc::rpc.mark_safe("session.path");
rpc::rpc.mark_safe("session.use_lock");
+95 -114
View File
@@ -8,12 +8,18 @@
#include <torrent/data/file_manager.h>
#include <torrent/download/resource_manager.h>
#include <torrent/net/http_stack.h>
#include <torrent/net/network_config.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/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "encryption_config.h"
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
@@ -29,24 +35,6 @@
#include <systemd/sd-daemon.h>
#endif
torrent::Object
apply_encryption(const torrent::Object::list_type& args) {
uint32_t options_mask = torrent::net::NetworkConfig::encryption_none;
for (const auto& arg : args) {
uint32_t opt = torrent::option_find_string(torrent::OPTION_ENCRYPTION, arg.as_string().c_str());
if (opt == torrent::net::NetworkConfig::encryption_none)
options_mask = torrent::net::NetworkConfig::encryption_none;
else
options_mask |= opt;
}
torrent::config::network_config()->set_encryption_options(options_mask);
return torrent::Object();
}
torrent::Object
apply_tos(const torrent::Object::string_type& arg) {
rpc::command_base::value_type value;
@@ -54,13 +42,11 @@ apply_tos(const torrent::Object::string_type& arg) {
if (!rpc::parse_whole_value_nothrow(arg.c_str(), &value, 16, 1))
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();
}
torrent::Object apply_encoding_list(const std::string& arg) { torrent::encoding_list()->push_back(arg); return torrent::Object(); }
void
initialize_rpc_handlers() {
rpc::rpc.initialize_handlers();
@@ -215,111 +201,105 @@ apply_xmlrpc_dialect(const std::string& arg) {
void
initialize_command_network() {
auto cm = torrent::connection_manager();
auto file_manager = torrent::file_manager();
auto http_stack = torrent::net_thread::http_stack();
auto nw_config = torrent::config::network_config();
auto file_manager = torrent::file_manager();
auto http_stack = torrent::net_thread::http_stack();
auto nw_config = torrent::runtime::network_config();
CMD2_ANY_STRING ("encoding.add", std::bind(&apply_encoding_list, std::placeholders::_2));
CMD_ANY ("network.listen.port", [](auto, auto) { return torrent::runtime::network_manager()->listen_port(); });
CMD_ANY_VALUE_V ("network.listen.port.set", [](auto, auto& value) { return torrent::runtime::network_manager()->set_listen_port(value); });
CMD_ANY ("network.listen.port.random", [](auto, auto) { return torrent::runtime::client_config()->listen_port_random(); });
CMD_ANY_VALUE_V ("network.listen.port.random.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_listen_port_random(value); });
CMD_ANY ("network.listen.port.range", [](auto, auto) { return listen_port_range(); });
CMD_ANY_STRING_V("network.listen.port.range.set", [](auto, auto& value) { return set_listen_port_range(value); });
CMD_ANY ("network.listen.backlog", [](auto, auto) { return torrent::runtime::network_config()->listen_backlog(); });
CMD_ANY_VALUE_V ("network.listen.backlog.set", [](auto, auto& value) { return torrent::runtime::network_config()->set_listen_backlog(value); });
// Isn't port_open used?
CMD2_VAR_BOOL ("network.port_open", true);
CMD2_VAR_BOOL ("network.port_random", true);
CMD2_VAR_STRING ("network.port_range", "6881-6999");
CMD_VAR_BOOL ("protocol.pex", true);
CMD2_ANY ("network.listen.port", [](auto, auto) { return torrent::runtime::listen_port(); });
CMD2_ANY ("network.listen.backlog", [nw_config](auto, auto) { return nw_config->listen_backlog(); });
CMD2_ANY_VALUE_V ("network.listen.backlog.set", [nw_config](auto, auto& value) { return nw_config->set_listen_backlog(value); });
encryption_config::initialize_commands();
CMD2_VAR_BOOL ("protocol.pex", true);
CMD2_ANY_LIST ("protocol.encryption.set", [](auto, auto& args) { return apply_encryption(args); });
CMD_VAR_STRING ("protocol.connection.leech", "leech");
CMD_VAR_STRING ("protocol.connection.seed", "seed");
CMD2_VAR_STRING ("protocol.connection.leech", "leech");
CMD2_VAR_STRING ("protocol.connection.seed", "seed");
CMD_VAR_STRING ("protocol.choke_heuristics.up.leech", "upload_leech");
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");
CMD2_VAR_STRING ("protocol.choke_heuristics.up.seed", "upload_leech");
CMD2_VAR_STRING ("protocol.choke_heuristics.down.leech", "download_leech");
CMD2_VAR_STRING ("protocol.choke_heuristics.down.seed", "download_leech");
CMD_ANY ("network.http.cacert", [http_stack](auto, auto) { return http_stack->http_cacert(); });
CMD_ANY_STRING_V("network.http.cacert.set", [http_stack](auto, auto& str) { return http_stack->set_http_cacert(str); });
CMD_ANY ("network.http.capath", [http_stack](auto, auto) { return http_stack->http_capath(); });
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(); });
CMD2_ANY_STRING_V("network.http.cacert.set", [http_stack](auto, auto& str) { return http_stack->set_http_cacert(str); });
CMD2_ANY ("network.http.capath", [http_stack](auto, auto) { return http_stack->http_capath(); });
CMD2_ANY_STRING_V("network.http.capath.set", [http_stack](auto, auto& str) { return http_stack->set_http_capath(str); });
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); });
CMD_ANY ("network.http.ssl_verify_host", [http_stack](auto, auto) { return http_stack->ssl_verify_host(); });
CMD_ANY_VALUE_V ("network.http.ssl_verify_host.set", [http_stack](auto, auto& value) { return http_stack->set_ssl_verify_host(value); });
CMD_ANY ("network.http.ssl_verify_peer", [http_stack](auto, auto) { return http_stack->ssl_verify_peer(); });
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.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); });
CMD2_ANY ("network.receive_buffer.size", [nw_config](auto, auto) { return nw_config->receive_buffer_size(); });
CMD2_ANY_VALUE_V ("network.receive_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_receive_buffer_size(value); });
CMD2_ANY_STRING ("network.tos.set", [](auto, auto& str) { return apply_tos(str); });
CMD_ANY ("network.send_buffer.size", [nw_config](auto, auto) { return nw_config->send_buffer_size(); });
CMD_ANY_VALUE_V ("network.send_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_send_buffer_size(value); });
CMD_ANY ("network.receive_buffer.size", [nw_config](auto, auto) { return nw_config->receive_buffer_size(); });
CMD_ANY_VALUE_V ("network.receive_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_receive_buffer_size(value); });
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(); });
CMD2_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(); });
CMD2_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(); });
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 ("network.bind_address", [nw_config](auto, auto) { return nw_config->bind_address_best_match_str(); });
CMD_ANY_STRING_V("network.bind_address.set", [nw_config](auto, auto& str) { return nw_config->set_bind_address_str(str); });
CMD_ANY ("network.bind_address.ipv4", [nw_config](auto, auto) { return nw_config->bind_inet_address_str(); });
CMD_ANY_STRING_V("network.bind_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet_address_str(str); });
CMD_ANY ("network.bind_address.ipv6", [nw_config](auto, auto) { return nw_config->bind_inet6_address_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(); });
CMD2_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(); });
CMD2_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(); });
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 ("network.local_address", [nw_config](auto, auto) { return nw_config->local_address_best_match_str(); });
CMD_ANY_STRING_V("network.local_address.set", [nw_config](auto, auto& str) { return nw_config->set_local_address_str(str); });
CMD_ANY ("network.local_address.ipv4", [nw_config](auto, auto) { return nw_config->local_inet_address_str(); });
CMD_ANY_STRING_V("network.local_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet_address_str(str); });
CMD_ANY ("network.local_address.ipv6", [nw_config](auto, auto) { return nw_config->local_inet6_address_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(); });
CMD2_ANY_STRING_V("network.proxy_address.set", [](auto, auto& str) { return control->core()->set_proxy_address(str); });
CMD_ANY ("network.proxy.global", [](auto, auto) { return torrent::runtime::proxy_manager()->proxy_url(); });
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(); });
CMD2_ANY ("network.max_open_files", [file_manager](auto, auto) { return file_manager->max_open_files(); });
CMD2_ANY_VALUE_V ("network.max_open_files.set", [file_manager](auto, auto& value) { return file_manager->set_max_open_files(value); });
CMD2_ANY ("network.total_handshakes", [](auto, auto) { return torrent::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); });
CMD_ANY ("network.open_files", [file_manager](auto, auto) { return file_manager->open_files(); });
CMD_ANY ("network.max_open_files", [file_manager](auto, auto) { return file_manager->max_open_files(); });
CMD_ANY ("network.total_handshakes", [](auto, auto) { return torrent::runtime::total_handshakes(); });
CMD2_ANY_STRING ("network.scgi.open_port", std::bind(&apply_scgi, std::placeholders::_2, 1));
CMD2_ANY_STRING ("network.scgi.open_local", std::bind(&apply_scgi, std::placeholders::_2, 2));
CMD2_VAR_BOOL ("network.scgi.dont_route", false);
CMD2_ANY ("network.scgi.open_systemd", [](auto, auto) { return apply_scgi_systemd(); });
CMD_ANY_STRING ("network.scgi.open_port", [](auto, auto& arg) { return apply_scgi(arg, 1); });
CMD_ANY_STRING ("network.scgi.open_local", [](auto, auto& arg) { return apply_scgi(arg, 2); });
CMD_VAR_BOOL ("network.scgi.dont_route", false);
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(); });
CMD2_ANY_VALUE_V ("network.scgi.use_gzip.set", [](const auto&, const 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(); });
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 ("network.scgi.use_gzip", [](auto, auto) { return rpc::rpc.scgi_allow_compression(); });
CMD_ANY_VALUE_V ("network.scgi.use_gzip.set", [](auto, auto& arg) { return rpc::rpc.set_scgi_allow_compression(arg); });
CMD_ANY ("network.scgi.gzip.min_size", [](auto, auto) { return rpc::rpc.scgi_min_compress_size(); });
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); })
CMD2_ANY ("network.xmlrpc.size_limit", [](const auto&, const 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_STRING ("network.xmlrpc.dialect.set", [](auto, auto& arg) { return apply_xmlrpc_dialect(arg); })
CMD_ANY ("network.xmlrpc.size_limit", [](auto, auto) { return rpc::rpc.size_limit(); });
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);
CMD2_VAR_BOOL ("network.rpc.use_jsonrpc", true);
CMD_VAR_BOOL ("network.rpc.use_xmlrpc", true);
CMD_VAR_BOOL ("network.rpc.use_jsonrpc", true);
CMD2_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); });
CMD2_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); });
CMD2_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); });
CMD2_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); });
CMD2_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 ("network.block.ipv4", [nw_config](auto, auto) { return nw_config->is_block_ipv4(); });
CMD_ANY_VALUE_V ("network.block.ipv4.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4(value); });
CMD_ANY ("network.block.ipv6", [nw_config](auto, auto) { return nw_config->is_block_ipv6(); });
CMD_ANY_VALUE_V ("network.block.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv6(value); });
CMD_ANY ("network.block.ipv4in6", [nw_config](auto, auto) { return nw_config->is_block_ipv4in6(); });
CMD_ANY_VALUE_V ("network.block.ipv4in6.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4in6(value); });
CMD_ANY ("network.block.outgoing", [nw_config](auto, auto) { return nw_config->is_block_outgoing(); });
CMD_ANY_VALUE_V ("network.block.outgoing.set", [nw_config](auto, auto& value) { return nw_config->set_block_outgoing(value); });
CMD_ANY ("network.prefer.ipv6", [nw_config](auto, auto) { return nw_config->is_prefer_ipv6(); });
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_random");
@@ -332,10 +312,9 @@ initialize_command_network() {
rpc::rpc.mark_safe("network.http.max_host_connections");
rpc::rpc.mark_safe("network.http.max_total_connections");
rpc::rpc.mark_safe("network.total_handshakes");
rpc::rpc.mark_safe("network.open_files");
rpc::rpc.mark_safe("network.max_open_files");
rpc::rpc.mark_safe("network.max_open_sockets");
rpc::rpc.mark_safe("network.total_handshakes");
rpc::rpc.mark_safe("network.send_buffer.size");
rpc::rpc.mark_safe("network.receive_buffer.size");
@@ -343,11 +322,13 @@ initialize_command_network() {
rpc::rpc.mark_safe("network.local_address");
rpc::rpc.mark_safe("network.xmlrpc.size_limit");
rpc::rpc.mark_safe("network.open_sockets");
rpc::rpc.mark_safe("network.http.cacert");
rpc::rpc.mark_safe("network.http.capath");
rpc::rpc.mark_safe("network.http.proxy_address");
rpc::rpc.mark_safe("network.proxy_address");
rpc::rpc.mark_safe("network.proxy.global");
rpc::rpc.mark_safe("network.proxy.http");
rpc::rpc.mark_safe("network.scgi.dont_route");
rpc::rpc.mark_safe("protocol.pex");
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
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.");
const std::string& name = argItr->as_string();
const std::string& name = arg_itr->as_string();
if (name.empty() || name == "NULL")
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 + "'.");
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)
throw torrent::input_error("Throttle rate must be non-negative.");
core::ThrottleMap::iterator 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;
auto itr = control->core()->throttles().find(name);
torrent::Throttle*& throttle = up ? itr->second.first : itr->second.second;
if (rate != 0 && throttle == NULL)
if (itr == control->core()->throttles().end())
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();
if (throttle != NULL)
if (throttle != nullptr)
throttle->set_max_rate(rate * 1024);
return torrent::Object();
@@ -109,10 +111,10 @@ static const int throttle_info_rate = (1 << 3);
torrent::Object
retrieve_throttle_info(const torrent::Object::string_type& name, int flags) {
core::ThrottleMap::iterator itr = control->core()->throttles().find(name);
torrent::ThrottlePair throttles = itr == control->core()->throttles().end() ? torrent::ThrottlePair(NULL, NULL) : itr->second;
torrent::Throttle* 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 itr = control->core()->throttles().find(name);
auto throttles = (itr == control->core()->throttles().end()) ? core::ThrottlePair(nullptr, nullptr) : itr->second;
auto* throttle = flags & throttle_info_down ? throttles.second : throttles.first;
auto* global = flags & throttle_info_down ? torrent::down_throttle_global() : torrent::up_throttle_global();
if (throttle == NULL && name.empty())
throttle = global;
@@ -127,20 +129,6 @@ retrieve_throttle_info(const torrent::Object::string_type& name, int flags) {
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
throttle_update(const char* variable, int64_t value) {
rpc::commands.call_command(variable, value);
@@ -198,7 +186,6 @@ initialize_command_throttle() {
// than kB.
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.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.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 <cstdio>
#include <netdb.h>
#include <torrent/net/network_config.h>
#include <torrent/net/resolver.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/system/callbacks.h>
#include <torrent/tracker/dht_controller.h>
#include <torrent/tracker/tracker.h>
#include <torrent/utils/log.h>
@@ -48,8 +49,10 @@ apply_dht_add_node(const std::string& arg) {
// TODO: Move this lookup to DhtController.
auto callback_id = torrent::system::make_callback_id();
// 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) {
lt_log_print(torrent::LOG_DHT_ERROR, "dht.add_node : could not resolve host : %s (%s)", gai_strerror(err), host_str.c_str());
return;
@@ -68,16 +71,6 @@ apply_enable_trackers(int64_t arg) {
for (auto download : *control->core()->download_list())
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 {
for (auto download : *control->core()->download_list())
download->tracker_controller().for_each([](auto& tracker) { tracker.enable(); });
@@ -88,69 +81,71 @@ apply_enable_trackers(int64_t arg) {
void
initialize_command_tracker() {
CMD2_TRACKER ("t.is_busy", std::bind(&torrent::tracker::Tracker::is_busy, std::placeholders::_1));
CMD2_TRACKER ("t.is_enabled", std::bind(&torrent::tracker::Tracker::is_enabled, std::placeholders::_1));
CMD2_TRACKER ("t.is_extra_tracker", std::bind(&torrent::tracker::Tracker::is_extra_tracker, std::placeholders::_1));
CMD2_TRACKER ("t.is_open", std::bind(&torrent::tracker::Tracker::is_busy, std::placeholders::_1));
CMD2_TRACKER ("t.is_scrapable", std::bind(&torrent::tracker::Tracker::is_scrapable, std::placeholders::_1));
CMD2_TRACKER ("t.is_usable", std::bind(&torrent::tracker::Tracker::is_usable, std::placeholders::_1));
CMD2_TRACKER ("t.is_busy", [](auto* tracker, auto) { return tracker->is_requesting(); });
CMD2_TRACKER ("t.is_enabled", [](auto* tracker, auto) { return tracker->is_enabled(); });
CMD2_TRACKER ("t.is_extra_tracker", [](auto* tracker, auto) { return tracker->is_extra_tracker(); });
CMD2_TRACKER ("t.is_open", [](auto* tracker, auto) { return tracker->is_requesting(); });
CMD2_TRACKER ("t.is_scrapable", [](auto* tracker, auto) { return tracker->is_scrapable(); });
CMD2_TRACKER ("t.is_usable", [](auto* tracker, auto) { return tracker->is_usable(); });
// 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.disable", std::bind(&torrent::tracker::Tracker::disable, std::placeholders::_1));
CMD2_TRACKER_V ("t.enable", [](auto* tracker, auto) { tracker->enable(); });
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.group", std::bind(&torrent::tracker::Tracker::group, std::placeholders::_1));
CMD2_TRACKER ("t.type", std::bind(&torrent::tracker::Tracker::type, std::placeholders::_1));
CMD2_TRACKER ("t.id", std::bind(&torrent::tracker::Tracker::tracker_id, std::placeholders::_1));
CMD2_TRACKER ("t.url", [](auto* tracker, auto) { return tracker->url(); });
CMD2_TRACKER ("t.group", [](auto* tracker, auto) { return tracker->group(); });
CMD2_TRACKER ("t.type", [](auto* tracker, auto) { return tracker->type(); });
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_new_peers", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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_event", [](auto* tracker, auto) { return tracker->state().latest_event(); });
CMD2_TRACKER ("t.latest_new_peers", [](auto* tracker, auto) { return tracker->state().latest_new_peers(); });
CMD2_TRACKER ("t.latest_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.min_interval", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().min_interval(); });
CMD2_TRACKER ("t.normal_interval", [](auto* tracker, auto) { return tracker->state().normal_interval().count(); });
CMD2_TRACKER ("t.min_interval", [](auto* tracker, auto) { return tracker->state().min_interval().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_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().activity_time_last(); });
CMD2_TRACKER ("t.activity_time_next", [](auto* tracker, auto) { return tracker->state().activity_time_next().count(); });
CMD2_TRACKER ("t.activity_time_last", [](auto* tracker, auto) { return tracker->state().activity_time_last().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_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().success_time_last(); });
CMD2_TRACKER ("t.success_counter", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().success_counter(); });
CMD2_TRACKER ("t.success_time_next", [](auto* tracker, auto) { return tracker->state().success_time_next().count(); });
CMD2_TRACKER ("t.success_time_last", [](auto* tracker, auto) { return tracker->state().success_time_last().count(); });
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_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().failed_time_last(); });
CMD2_TRACKER ("t.failed_counter", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().failed_counter(); });
CMD2_TRACKER ("t.failed_time_next", [](auto* tracker, auto) { return tracker->state().failed_time_next().count(); });
CMD2_TRACKER ("t.failed_time_last", [](auto* tracker, auto) { return tracker->state().failed_time_last().count(); });
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_counter", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_counter(); });
CMD2_TRACKER ("t.scrape_time_last", [](auto* tracker, auto) { return tracker->state().scrape_time_last().count(); });
CMD2_TRACKER ("t.scrape_counter", [](auto* tracker, auto) { return tracker->state().scrape_counter(); });
CMD2_TRACKER ("t.scrape_complete", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> 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_downloaded", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_downloaded(); });
CMD2_TRACKER ("t.scrape_complete", [](auto* tracker, auto) { return tracker->state().scrape_complete(); });
CMD2_TRACKER ("t.scrape_incomplete", [](auto* tracker, auto) { return tracker->state().scrape_incomplete(); });
CMD2_TRACKER ("t.scrape_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.disable", std::bind(&apply_enable_trackers, int64_t(0)));
CMD2_ANY_VALUE ("trackers.enable", [](auto, auto) { return apply_enable_trackers(1); });
CMD2_ANY_VALUE ("trackers.disable", [](auto, auto) { return apply_enable_trackers(0); });
CMD2_VAR_BOOL ("trackers.delay_scrape", false);
CMD2_VAR_VALUE ("trackers.numwant", -1);
CMD2_VAR_BOOL ("trackers.use_udp", true);
CMD2_VAR_VALUE ("trackers.numwant", -1);
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 ("dht.port", [](auto, auto) { return torrent::runtime::network_manager()->dht_controller()->port(); });
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_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);
});
CMD2_ANY ("dht.override_port", std::bind(&torrent::net::NetworkConfig::override_dht_port, torrent::config::network_config()));
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 ("dht.override_port", [](auto, auto) { return torrent::runtime::network_config()->override_dht_port(); });
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 ("dht.statistics", std::bind(&core::DhtManager::dht_statistics, dht_manager));
CMD2_ANY_STRING ("dht.add_node", [](auto, auto& str) { return apply_dht_add_node(str); });
CMD2_ANY ("dht.statistics", [](auto, auto) { return control->dht_manager()->dht_statistics(); });
rpc::rpc.mark_safe("t.url");
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 }
// <cond1>,<branch1>,<cond2>,<branch2>,<branch3>
torrent::Object
apply_if(rpc::target_type target, const torrent::Object& rawArgs, int flags) {
const torrent::Object::list_type& args = rawArgs.as_list();
torrent::Object::list_const_iterator itr = args.begin();
apply_if(rpc::target_type target, const torrent::Object& raw_args, int flags) {
auto& args = raw_args.as_list();
auto itr = args.begin();
while (itr != args.end() && itr != --args.end()) {
if (args.empty())
throw torrent::input_error("Empty argument list to " + std::string((flags & 0x1) ? "branch" : "if") + ".");
{
torrent::Object tmp;
const torrent::Object* conditional;
@@ -500,40 +503,44 @@ apply_if(rpc::target_type target, const torrent::Object& rawArgs, int flags) {
result = false;
break;
default:
throw torrent::input_error("Type not supported by 'if'.");
throw torrent::input_error("Type not supported by " + std::string((flags & 0x1) ? "branch" : "if") + ".");
};
itr++;
if (result)
break;
itr++;
if (!result && itr != args.end())
itr++;
}
if (itr == args.end())
return torrent::Object();
if (flags & 0x1 && itr->is_string()) {
return rpc::parse_command(target, itr->as_string().c_str(), itr->as_string().c_str() + itr->as_string().size()).first;
if (flags & 0x1) {
if (itr->is_string())
return rpc::parse_command(target, itr->as_string().c_str(), itr->as_string().c_str() + itr->as_string().size()).first;
} else if (flags & 0x1 && itr->is_dict_key()) {
return rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target);
if (itr->is_dict_key())
return rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target);
} else if (flags & 0x1 && itr->is_list()) {
// Move this into a special function or something. Also, might be
// nice to have a parse_command function that takes list
// iterator...
if (itr->is_list()) {
for (const auto& cmd_itr : itr->as_list()) {
if (cmd_itr.is_string())
rpc::parse_command(target, cmd_itr.as_string().c_str(), cmd_itr.as_string().c_str() + cmd_itr.as_string().size());
for (const auto& cmdItr : itr->as_list())
if (cmdItr.is_string())
rpc::parse_command(target, cmdItr.as_string().c_str(), cmdItr.as_string().c_str() + cmdItr.as_string().size());
else if (cmd_itr.is_dict_key())
rpc::commands.call_command(cmd_itr.as_dict_key().c_str(), cmd_itr.as_dict_obj(), target);
return torrent::Object();
else
throw torrent::input_error("Invalid command type in branch list.");
}
} else {
return *itr;
return torrent::Object();
}
throw torrent::input_error("Invalid command type in branch.");
}
return *itr;
}
torrent::Object
@@ -795,61 +802,59 @@ apply_set_color(int color_id, const torrent::Object::string_type& color_str) {
void
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_LIST("view.set", std::bind(&apply_view_set, std::placeholders::_2));
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.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.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_BOOL ("view.filter.temp.log", 0);
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.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_BOOL ("view.filter.temp.log", 0);
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_current", std::bind(&apply_view_event, &core::ViewManager::set_sort_current, 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_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_removed", std::bind(&apply_view_event, &core::ViewManager::set_event_removed, 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));
// 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.persistent", std::bind(&cmd_view_persistent, std::placeholders::_2));
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.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_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_not_visible", std::bind(&cmd_view_set_not_visible, 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_not_visible", std::bind(&cmd_view_set_not_visible, std::placeholders::_1, std::placeholders::_2));
// Commands that affect the default rtorrent UI.
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_STRING("ui.current_view.set", std::bind(&cmd_ui_set_view, std::placeholders::_2));
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_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_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 ("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_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.medium", 50);
CMD2_VAR_VALUE ("ui.throttle.global.step.large", 500);
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.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.down.set", std::bind(&cmd_status_throttle_names, false, 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 ("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));
// 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.
CMD2_ANY("print", &apply_print);
+33 -12
View File
@@ -6,6 +6,7 @@
#include <sys/stat.h>
#include <torrent/net/http_stack.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/utils/directory_events.h>
#include "core/dht_manager.h"
@@ -27,6 +28,7 @@
#include "rpc/object_storage.h"
#include "session/session_manager.h"
#include "ui/root.h"
#include "utils/watch_ready_queue.h"
Control::Control()
: m_ui(new ui::Root()),
@@ -36,7 +38,8 @@ Control::Control()
m_commandScheduler(new rpc::CommandScheduler()),
m_objectStorage(new rpc::object_storage()),
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_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_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); });
}
@@ -66,15 +70,12 @@ Control::initialize() {
display::Window::slot_unschedule([this](display::Window* w) { m_display->unschedule(w); });
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_ui->init(this);
if(!display::Canvas::daemon())
m_inputStdin->insert(torrent::this_thread::poll());
m_inputStdin->insert();
}
void
@@ -82,9 +83,12 @@ Control::cleanup() {
rpc::rpc.cleanup();
torrent::this_thread::scheduler()->erase(&m_task_shutdown);
torrent::this_thread::scheduler()->erase(&m_task_shutdown_clear_requests);
if(!display::Canvas::daemon())
m_inputStdin->remove(torrent::this_thread::poll());
m_inputStdin->remove();
m_directory_events->close();
if (scgi_thread::thread()->is_active())
scgi_thread::thread()->stop_thread_wait();
@@ -109,7 +113,7 @@ Control::cleanup_exception() {
bool
Control::is_shutdown_completed() {
if (!m_shutdownQuick)
if (!m_shutdown_quick)
return false;
// 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())
return false;
return torrent::is_inactive();
return core()->is_download_shutdown_completed();
}
void
Control::handle_shutdown() {
m_watch_ready_queue->shutdown();
rpc::commands.call_catch("event.system.shutdown", rpc::make_target(), "shutdown", "System shutdown event action failed: ");
if (scgi_thread::thread()->is_active())
scgi_thread::thread()->stop_thread_wait();
if (!m_shutdownQuick) {
if (!m_shutdown_quick) {
torrent::runtime::network_manager()->listen_close();
torrent::runtime::shutdown();
m_directory_events->close();
m_core->shutdown(false);
@@ -141,10 +148,24 @@ Control::handle_shutdown() {
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown, 5s);
} else {
torrent::runtime::quick_shutdown();
m_core->shutdown(true);
}
m_shutdownQuick = true;
m_shutdownReceived = false;
if (!m_task_shutdown_clear_requests.is_scheduled())
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;
}
namespace utils {
class WatchReadyQueue;
}
class Control {
public:
Control();
~Control();
bool is_shutdown_completed();
bool is_shutdown_received() { return m_shutdownReceived; }
bool is_shutdown_started() { return m_shutdownQuick; }
bool is_shutdown_received() { return m_shutdown_received; }
bool is_shutdown_started() { return m_shutdown_quick; }
void initialize();
void cleanup();
void cleanup_exception();
void handle_shutdown();
void handle_shutdown_clear_requests();
void receive_normal_shutdown() { m_shutdownReceived = true; }
void receive_quick_shutdown() { m_shutdownReceived = true; m_shutdownQuick = true; }
void receive_normal_shutdown() { m_shutdown_received = true; }
void receive_quick_shutdown() { m_shutdown_received = true; m_shutdown_quick = true; }
core::Manager* core() { return m_core.get(); }
core::ViewManager* view_manager() { return m_view_manager.get(); }
@@ -70,6 +75,7 @@ public:
rpc::LuaEngine* lua_engine() { return m_lua_engine.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; }
void inc_tick() { m_tick++; }
@@ -94,6 +100,7 @@ private:
std::unique_ptr<rpc::object_storage> m_objectStorage;
std::unique_ptr<rpc::LuaEngine> m_lua_engine;
std::unique_ptr<torrent::directory_events> m_directory_events;
std::unique_ptr<utils::WatchReadyQueue> m_watch_ready_queue;
uint64_t m_tick{};
@@ -101,9 +108,14 @@ private:
std::string m_workingDirectory;
torrent::utils::SchedulerEntry m_task_shutdown;
torrent::utils::SchedulerEntry m_task_shutdown_clear_requests;
std::atomic<bool> m_shutdownReceived{};
std::atomic<bool> m_shutdownQuick{};
int m_clear_requests_count{};
align_cacheline
std::atomic<bool> m_shutdown_received{};
std::atomic<bool> m_shutdown_quick{};
};
#endif
+42 -9
View File
@@ -8,6 +8,7 @@
#include <torrent/object_stream.h>
#include <torrent/rate.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/tracker/dht_controller.h>
#include <torrent/utils/log.h>
@@ -140,17 +141,32 @@ DhtManager::save_dht_cache() {
}
void
DhtManager::set_mode(const std::string& arg) {
int i;
for (i = 0; i < dht_settings_num; i++) {
if (arg == dht_settings[i]) {
m_start = i;
break;
}
DhtManager::set_mode_by_user(const std::string& arg) {
unsigned int mode = [arg]() {
for (int i = 0; i < dht_settings_num; i++) {
if (arg == dht_settings[i])
return i;
}
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)
throw torrent::input_error("Invalid argument.");
set_mode_directly(mode);
}
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)
stop_dht();
@@ -158,6 +174,23 @@ DhtManager::set_mode(const std::string& arg) {
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
DhtManager::update() {
if (!torrent::runtime::network_manager()->is_dht_active())
+12 -8
View File
@@ -8,6 +8,11 @@ namespace core {
class DhtManager {
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();
void load_dht_cache();
@@ -18,16 +23,14 @@ public:
void stop_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:
static const int dht_disable = 0;
static const int dht_off = 1;
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];
static constexpr int dht_settings_num = 4;
static const char* dht_settings[dht_settings_num];
void update();
bool log_statistics(bool force);
@@ -43,6 +46,7 @@ private:
torrent::utils::SchedulerEntry m_stop_timeout;
bool m_warned{};
bool m_set_by_user{};
int m_start{dht_off};
};
+5 -19
View File
@@ -32,21 +32,6 @@ Download::~Download() {
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
Download::priority() {
return bencode()->get_key("rtorrent").get_key_value("priority");
@@ -103,15 +88,16 @@ Download::distributed_copies() const {
}
void
Download::set_throttle_name(const std::string& throttleName) {
Download::set_throttle_name(const std::string& name) {
if (m_download.info()->is_active())
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_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
@@ -131,7 +117,7 @@ Download::set_root_directory(const std::string& path) {
(file_list->is_multi_file() ?
!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.");
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 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_initial = 1;
static const int variable_hashing_last = 2;
@@ -70,8 +72,6 @@ public:
const std::string& message() const { return m_message; }
void set_message(const std::string& msg) { m_message = msg; }
void enable_udp_trackers(bool state);
uint32_t priority();
void set_priority(uint32_t p);
@@ -80,7 +80,7 @@ public:
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;
@@ -102,7 +102,7 @@ private:
download_type m_download;
bool m_hashFailed{};
std::string m_message;
uint32_t m_resumeFlags{~uint32_t{}};
uint32_t m_resumeFlags{default_resume_flags};
unsigned int m_group{};
};
+22 -23
View File
@@ -104,40 +104,42 @@ DownloadFactory::receive_load() {
throw torrent::internal_error("DownloadFactory::load*() called on an object with m_stream != NULL");
if (is_network_uri(m_uri)) {
// Http handling here.
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(); });
itr->add_failed_slot([this](const std::string& error) { receive_failed(error); });
m_manager->http_queue()->insert(m_uri, m_stream, done_fn, failed_fn);
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.
m_stream.reset(new std::stringstream());
*m_stream << "d10:magnet-uri" << m_uri.length() << ":" << m_uri << "e";
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();
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
@@ -238,9 +240,6 @@ DownloadFactory::receive_success() {
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
if (m_initLoad && rpc::call_command_value("trackers.delay_scrape"))
download->set_resume_flags(torrent::Download::start_skip_tracker);
+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
// 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);
// 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->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");
@@ -421,7 +421,7 @@ DownloadList::pause(Download* download, int flags) {
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");
@@ -492,7 +492,11 @@ DownloadList::hash_done(Download* download) {
if (!download->is_hash_checked()) {
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");
return;
}
@@ -656,8 +660,8 @@ DownloadList::confirm_finished(Download* download) {
if (find(infohash) == end())
return;
// if (download->resume_flags() != ~uint32_t())
// throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != ~uint32_t().");
// if (download->resume_flags() != Download::default_resume_flags)
// throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != Download::default_resume_flags.");
// 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
// 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)
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.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);
if (!file.is_open()) {
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not read download metadata.");
return;
@@ -692,11 +698,13 @@ DownloadList::process_meta_download(Download* download) {
torrent::Object* bencode = new torrent::Object(torrent::Object::create_map());
file >> bencode->insert_key("info", torrent::Object());
if (file.fail()) {
delete bencode;
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download, the input is not a valid torrent.");
return;
}
file.close();
// 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"));
erase_ptr(download);
control->core()->try_create_download_from_meta_download(bencode, metafile);
}
+9 -5
View File
@@ -9,17 +9,21 @@
namespace core {
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));
itr->set_max_file_size(15 << 20);
itr->set_redirect_only_http_https();
for (auto& slot : m_signal_insert)
slot(*itr);
itr->add_done_slot([this, itr]() { erase(itr); });
itr->add_failed_slot([this, itr](auto) { erase(itr); });
itr->add_done_slot(torrent::this_thread::thread(), std::move(done_fn));
itr->add_done_slot(torrent::this_thread::thread(), [this, itr]() { erase(itr); });
// TODO: Downloading http torrents doesn't seem to work.
// TODO: Quitting no longer works.
itr->add_failed_slot(torrent::this_thread::thread(), std::move(failed_fn));
itr->add_failed_slot(torrent::this_thread::thread(), [this, itr](auto) { erase(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
// 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 clear();
+28 -91
View File
@@ -8,17 +8,15 @@
#include <sstream>
#include <unistd.h>
#include <sys/select.h>
#include <rak/regex.h>
#include <fnmatch.h>
#include <torrent/utils/resume.h>
#include <torrent/object.h>
#include <torrent/connection_manager.h>
#include <torrent/error.h>
#include <torrent/exceptions.h>
#include <torrent/object_stream.h>
#include <torrent/throttle.h>
#include <torrent/net/http_stack.h>
#include <torrent/net/network_config.h>
#include <torrent/net/socket_address.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/utils/log.h>
#include <torrent/utils/string_manip.h>
@@ -35,6 +33,8 @@
#include "core/http_queue.h"
#include "core/view.h"
#include <torrent/runtime/client_config.h>
namespace core {
const int Manager::create_start;
@@ -65,6 +65,20 @@ Manager::~Manager() {
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
Manager::set_hashing_view(View* v) {
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));
}
torrent::ThrottlePair
ThrottlePair
Manager::get_throttle(const std::string& name) {
ThrottleMap::const_iterator itr = m_throttles.find(name);
torrent::ThrottlePair throttles = (itr == m_throttles.end() ? torrent::ThrottlePair(nullptr, nullptr) : itr->second);
auto itr = m_throttles.find(name);
auto throttles = (itr == m_throttles.end() ? ThrottlePair(nullptr, nullptr) : itr->second);
if (throttles.first == nullptr)
throttles.first = torrent::up_throttle_global();
@@ -88,20 +102,6 @@ Manager::get_throttle(const std::string& name) {
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
Manager::retrieve_throttle_value(const torrent::Object::string_type& name, bool rate, bool up) {
ThrottleMap::iterator itr = throttles().find(name);
@@ -153,76 +153,12 @@ Manager::cleanup() {
void
Manager::shutdown(bool force) {
if (!force)
if (!force) {
for (auto d : *m_download_list)
m_download_list->pause_default(d);
else
} else {
for (auto d : *m_download_list)
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.
for (; first != last; ++first) {
rak::regex r(*first);
const std::string& pattern = *first;
if (r.pattern().empty())
if (pattern.empty())
continue;
// Special case for ".."?
@@ -404,8 +340,8 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
for (auto& itr : currentCache) {
// Only include filenames starting with '.' if the pattern
// starts with the same.
itr.update((r.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.update((pattern[0] != '.') ? utils::Directory::update_hide_dot : 0);
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)
nextCache.push_back(path_expand_transform(itr.path() + (itr.path() == "/" ? "" : "/"), cache));
@@ -500,6 +436,7 @@ Manager::receive_hashing_changed() {
} else {
(*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());
}
}
+12 -21
View File
@@ -5,7 +5,6 @@
#include <memory>
#include <vector>
#include <torrent/utils/log_buffer.h>
#include <torrent/connection_manager.h>
#include <torrent/object.h>
#include "download_list.h"
@@ -23,7 +22,8 @@ namespace core {
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;
@@ -35,32 +35,26 @@ public:
Manager();
~Manager();
DownloadList* download_list() { return m_download_list.get(); }
FileStatusCache* file_status_cache() { return m_file_status_cache.get(); }
bool is_download_shutdown_completed();
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);
torrent::log_buffer* log_important() { return m_log_important.get(); }
torrent::log_buffer* log_complete() { return m_log_complete.get(); }
auto* log_important() { return m_log_important.get(); }
auto* log_complete() { return m_log_complete.get(); }
ThrottleMap& throttles() { return m_throttles; }
torrent::ThrottlePair get_throttle(const std::string& name);
ThrottleMap& throttles() { return m_throttles; }
ThrottlePair get_throttle(const std::string& name);
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 listen_open();
void set_proxy_address(const std::string& addr);
const std::string& magnet_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);
private:
typedef RangeMap<uint32_t, torrent::ThrottlePair> AddressThrottleMap;
void create_http(const std::string& uri);
void create_final(std::istream* s);
@@ -102,7 +94,6 @@ private:
View* m_hashingView{};
ThrottleMap m_throttles;
AddressThrottleMap m_addressThrottles;
torrent::log_buffer_ptr m_log_important;
torrent::log_buffer_ptr m_log_complete;
+3 -1
View File
@@ -4,7 +4,9 @@
#include <string>
#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>
#elif defined(HAVE_NCURSESW_H)
#include <ncursesw.h>
+6 -9
View File
@@ -132,8 +132,7 @@ Canvas::resize_term(std::pair<int, int> dim) {
inline unsigned int
Canvas::get_x() {
int x;
[[maybe_unused]] int y;
[[maybe_unused]] int x, y;
if (!m_daemon) {
getyx(m_window, y, x);
} else {
@@ -144,7 +143,7 @@ Canvas::get_x() {
inline unsigned int
Canvas::get_y() {
int x, y;
[[maybe_unused]] int x, y;
if (!m_daemon) {
getyx(m_window, y, x);
} else {
@@ -155,8 +154,7 @@ Canvas::get_y() {
inline unsigned int
Canvas::width() {
int x;
[[maybe_unused]] int y;
[[maybe_unused]] int x, y;
if (!m_daemon) {
getmaxyx(m_window, y, x);
} else {
@@ -167,7 +165,7 @@ Canvas::width() {
inline unsigned int
Canvas::height() {
int x, y;
[[maybe_unused]] int x, y;
if (!m_daemon) {
getmaxyx(m_window, y, x);
} else {
@@ -257,8 +255,7 @@ Canvas::set_default_attributes(int attr) {
inline int
Canvas::get_screen_width() {
int x;
[[maybe_unused]] int y;
[[maybe_unused]] int x, y;
if (!m_daemon) {
getmaxyx(stdscr, y, x);
} else {
@@ -269,7 +266,7 @@ Canvas::get_screen_width() {
inline int
Canvas::get_screen_height() {
int x, y;
[[maybe_unused]] int x, y;
if (!m_daemon) {
getmaxyx(stdscr, y, x);
} else {
+4
View File
@@ -4,7 +4,11 @@
#include <array>
#include <map>
#if defined(HAVE_NO_NCURSES)
#include "curses_stub.h"
#else
#include <curses.h>
#endif
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;
}
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;
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 <iomanip>
#include <torrent/exceptions.h>
#include <torrent/connection_manager.h>
#include <torrent/rate.h>
#include <torrent/throttle.h>
#include <torrent/torrent.h>
@@ -16,9 +15,11 @@
#include <torrent/data/file_manager.h>
#include <torrent/download/resource_manager.h>
#include <torrent/net/http_stack.h>
#include <torrent/net/network_config.h>
#include <torrent/net/socket_address.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 "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()) {
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()) {
@@ -376,7 +377,7 @@ print_status_info(char* first, char* last) {
first = print_buffer(first, last, " KB]");
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())) {
first = print_buffer(first, last, " [Local ");
@@ -387,8 +388,8 @@ print_status_info(char* first, char* last) {
if (first > last)
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_inet6_address = torrent::config::network_config()->bind_inet6_address();
auto bind_inet_address = torrent::runtime::network_config()->bind_inet_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_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());
first = print_buffer(first, last, " [S %i/%i/%i]",
torrent::total_handshakes(),
torrent::connection_manager()->size(),
torrent::connection_manager()->max_size());
torrent::runtime::total_handshakes(),
torrent::runtime::socket_manager()->size(),
torrent::runtime::socket_manager()->max_size());
first = print_buffer(first, last, " [F %i/%i]",
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");
} else if (itr.is_entering()) {
m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\',
itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN");
if (itr.depth() < itr.file()->path()->size())
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()) {
m_canvas->print(0, pos, "%*c %-*s", 16 + (itr.depth() - 1), '/', filenameWidth - (itr.depth() - 1), "");
@@ -166,8 +169,12 @@ WindowFileList::redraw() {
else
m_canvas->print(8, pos, "%5.1f T", (double)val / (int64_t(1) << 40));
m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|',
itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN");
if (itr.depth() < itr.file()->path()->size())
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 {
m_canvas->print(0, pos, "BORK BORK");
+12 -7
View File
@@ -2,6 +2,7 @@
#include <ctime>
#include <torrent/torrent.h>
#include <torrent/system/callbacks.h>
#include <torrent/system/thread.h>
#include "canvas.h"
@@ -14,16 +15,18 @@ WindowLog::WindowLog(torrent::log_buffer* l) :
Window(new Canvas, 0, 0, 0, extent_full, extent_static),
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::thread()->send_event_signal(signal_index, false);
});
torrent::main_thread::callback([this]() { receive_update(); });
});
}
WindowLog::~WindowLog() {
m_log->lock_and_set_update_slot(nullptr);
torrent::this_thread::scheduler()->erase(&m_task_update);
}
@@ -52,11 +55,13 @@ WindowLog::redraw() {
// gets updated.
void
WindowLog::receive_update() {
m_log_updating = false;
if (!is_active())
return;
iterator itr = find_older();
extent_type height = std::min(std::distance(itr, (iterator)m_log->end()), (std::iterator_traits<iterator>::difference_type)10);
auto itr = find_older();
auto height = std::min(std::distance(itr, (iterator)m_log->end()), (std::iterator_traits<iterator>::difference_type)10);
if (height != m_max_height) {
m_min_height = height != 0 ? 1 : 0;
+2
View File
@@ -24,6 +24,8 @@ private:
torrent::log_buffer* m_log;
torrent::utils::SchedulerEntry m_task_update;
align_cacheline std::atomic<bool> m_log_updating{};
};
}
+17 -1
View File
@@ -16,6 +16,21 @@
namespace display {
namespace {
char
connection_type_char(const torrent::Peer* p) {
if (p->is_encrypted())
return p->is_incoming() ? 'R' : 'L';
if (p->is_obfuscated())
return p->is_incoming() ? 'H' : 'h';
return p->is_incoming() ? 'r' : 'l';
}
} // namespace
WindowPeerList::WindowPeerList(core::Download* d, PList* l, PList::iterator* f) :
Window(new Canvas, 0, 0, 0, extent_full, extent_full),
m_download(d),
@@ -35,6 +50,7 @@ WindowPeerList::redraw() {
m_canvas->print(x, y, "UP"); x += 7;
m_canvas->print(x, y, "DOWN"); x += 7;
m_canvas->print(x, y, "PEER"); x += 7;
// CT: R/H/r or L/h/l (RC4 / handshake-only / plain) + peer type (u/p/ )
m_canvas->print(x, y, "CT/RE/LO"); x += 10;
m_canvas->print(x, y, "QS"); x += 6;
m_canvas->print(x, y, "DONE"); x += 6;
@@ -95,7 +111,7 @@ WindowPeerList::redraw() {
peerType = ' ';
m_canvas->print(x, y, "%c%c/%c%c/%c%c",
p->is_encrypted() ? (p->is_incoming() ? 'R' : 'L') : (p->is_incoming() ? 'r' : 'l'),
connection_type_char(p),
peerType,
p->is_down_choked() ? std::tolower(remoteChoked) : remoteChoked,
+3 -3
View File
@@ -51,9 +51,9 @@ WindowTrackerList::redraw() {
if (pos < m_canvas->height()) {
const char* state;
if (tracker.is_busy_not_scrape())
if (tracker.is_requesting_not_scrape())
state = "req ";
else if (tracker.is_busy())
else if (tracker.is_requesting())
state = "scr ";
else
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));
}
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 - 1, 4, A_REVERSE, COLOR_PAIR(0));
}
+88
View File
@@ -0,0 +1,88 @@
#include "config.h"
#include "encryption_config.h"
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/runtime.h>
#include <torrent/utils/option_strings.h>
#include "command_helpers.h"
#include "globals.h"
namespace encryption_config {
namespace {
Policy
current_policy() {
return torrent::runtime::network_config()->encryption_policy();
}
} // namespace
Policy
default_policy() {
Policy policy;
policy.handshake = Policy::Mode::allow;
policy.stream = Policy::Mode::allow;
return policy;
}
std::string
mode_to_string(Policy::Mode value) {
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_MODE,
static_cast<unsigned int>(value));
}
void
apply_mode_value(Policy& policy, Policy::Mode Policy::*field, const std::string& value) {
policy.*field = static_cast<Policy::Mode>(
torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_MODE, value));
}
std::string
summary_string(const Policy& policy) {
return "handshake=" + mode_to_string(policy.handshake)
+ " stream=" + mode_to_string(policy.stream);
}
void
apply_policy(const Policy& policy) {
torrent::runtime::network_config()->set_encryption_policy(policy);
}
torrent::Object
apply_handshake_set(const std::string& value) {
Policy policy = current_policy();
apply_mode_value(policy, &Policy::handshake, value);
apply_policy(policy);
return torrent::Object();
}
torrent::Object
apply_stream_set(const std::string& value) {
Policy policy = current_policy();
apply_mode_value(policy, &Policy::stream, value);
apply_policy(policy);
return torrent::Object();
}
void
initialize_commands() {
apply_policy(default_policy());
CMD2_ANY("protocol.encryption", [](auto, auto) { return summary_string(current_policy()); });
CMD2_ANY("protocol.encryption.handshake", [](auto, auto) { return mode_to_string(current_policy().handshake); });
CMD2_ANY_STRING_V("protocol.encryption.handshake.set", [](auto, auto& str) { return apply_handshake_set(str); });
CMD2_ANY("protocol.encryption.stream", [](auto, auto) { return mode_to_string(current_policy().stream); });
CMD2_ANY_STRING_V("protocol.encryption.stream.set", [](auto, auto& str) { return apply_stream_set(str); });
rpc::rpc.mark_safe("protocol.encryption");
rpc::rpc.mark_safe("protocol.encryption.handshake");
rpc::rpc.mark_safe("protocol.encryption.handshake.set");
rpc::rpc.mark_safe("protocol.encryption.stream");
rpc::rpc.mark_safe("protocol.encryption.stream.set");
}
} // namespace encryption_config
+29
View File
@@ -0,0 +1,29 @@
#ifndef RTORRENT_ENCRYPTION_CONFIG_H
#define RTORRENT_ENCRYPTION_CONFIG_H
#include <cstdint>
#include <string>
#include <torrent/runtime/encryption_policy.h>
#include <torrent/object.h>
namespace encryption_config {
using Policy = torrent::EncryptionPolicy;
Policy default_policy();
std::string mode_to_string(Policy::Mode value);
void apply_mode_value(Policy& policy, Policy::Mode Policy::*field, const std::string& value);
std::string summary_string(const Policy& policy);
void apply_policy(const Policy& policy);
torrent::Object apply_handshake_set(const std::string& value);
torrent::Object apply_stream_set(const std::string& value);
void initialize_commands();
} // namespace encryption_config
#endif
+7 -9
View File
@@ -25,13 +25,11 @@ namespace scgi_thread {
torrent::system::Thread* thread();
std::thread::id thread_id();
void callback(void* target, std::function<void ()>&& fn);
void cancel_callback(void* target);
void cancel_callback_and_wait(void* target);
void callback_interrupt(torrent::system::callback_id& id, std::function<void ()>&& fn);
rpc::SCgi* scgi();
void set_scgi(rpc::SCgi* scgi);
void set_rpc_log(const std::string& filename);
rpc::SCgi* scgi();
void set_scgi(rpc::SCgi* scgi);
void set_rpc_log(const std::string& filename);
} // namespace torrent::scgi_thread
@@ -41,9 +39,9 @@ namespace session_thread {
torrent::system::Thread* thread();
std::thread::id thread_id();
void callback(void* target, std::function<void ()>&& fn);
void cancel_callback(void* target);
void cancel_callback_and_wait(void* target);
void callback(std::function<void ()>&& fn);
void callback(torrent::system::callback_id& id, std::function<void ()>&& fn);
void cancel_callback(torrent::system::callback_id& id);
session::SessionManager* manager();
std::string session_path();
+14 -6
View File
@@ -2,20 +2,26 @@
#include "input_event.h"
#include <torrent/common.h>
#include <torrent/system/poll.h>
#include "display/attributes.h"
namespace input {
void
InputEvent::insert(torrent::net::Poll* p) {
p->open(this);
p->insert_read(this);
InputEvent::insert() {
torrent::this_thread::poll()->open(this);
torrent::this_thread::poll()->insert_read(this);
}
void
InputEvent::remove(torrent::net::Poll* p) {
p->remove_read(this);
p->close(this);
InputEvent::remove() {
if (!is_open())
return;
torrent::this_thread::poll()->remove_and_close(this);
set_file_descriptor(-1);
}
void
@@ -32,6 +38,8 @@ InputEvent::event_write() {
void
InputEvent::event_error() {
torrent::this_thread::poll()->remove_and_close(this);
set_file_descriptor(-1);
}
}
+3 -4
View File
@@ -4,7 +4,6 @@
#include <functional>
#include <torrent/event.h>
#include <torrent/net/poll.h>
namespace input {
@@ -12,12 +11,12 @@ class InputEvent : public torrent::Event {
public:
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"; }
void insert(torrent::net::Poll* p);
void remove(torrent::net::Poll* p);
void insert();
void remove();
void event_read() override;
void event_write() override;
+145 -100
View File
@@ -11,6 +11,9 @@
#include <torrent/exceptions.h>
#include <torrent/data/chunk_utils.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/log.h>
@@ -112,7 +115,16 @@ main(int argc, char** argv) {
// TODO: Create a fake thread object for initializing other processes and enabling logging.
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(SIGILL, std::bind(&do_panic, SIGILL));
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);
// SIGUSR1 is used for interrupting polling, forcing the target
// thread to process new non-socket events.
//
// 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_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_DHT_ERROR, "complete");
@@ -160,11 +162,13 @@ main(int argc, char** argv) {
initialize_rpc_slots();
torrent::initialize();
torrent::set_main_thread_slots(std::bind(&client_perform));
torrent::main_thread::set_client_callback(&client_perform);
scgi::ThreadScgi::create_thread();
session::ThreadSession::create_thread();
SignalHandler::set_unblock(SIGCHLD);
// Initialize option handlers after libtorrent to ensure
// torrent::ConnectionManager* are valid etc.
initialize_commands();
@@ -209,7 +213,14 @@ main(int argc, char** argv) {
"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_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, 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,15 @@ main(int argc, char** argv) {
"view.filter = leeching,((and,((d.state)),((not,((d.complete))))))\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"
"schedule2 = view.name,10,10,((view.sort,name,20))\n"
"schedule = view.main,10,10,((view.sort,main,20))\n"
"schedule = view.name,10,10,((view.sort,name,20))\n"
"schedule2 = session_save,1200,1200,((session.save))\n"
"schedule2 = low_diskspace,5,60,((close_low_diskspace,500M))\n"
"schedule2 = prune_file_status,3600,86400,((system.file_status_cache.prune))\n"
"schedule = session_save,1200,1200,((session.save))\n"
"schedule = low_diskspace,5,60,((close_low_diskspace,500M))\n"
"schedule = prune_file_status,3600,86400,((system.file_status_cache.prune))\n"
"protocol.encryption.set=allow_incoming,prefer_plaintext,enable_retry\n"
"protocol.encryption.handshake.set=allow\n"
"protocol.encryption.stream.set=allow\n"
"ui.color.focus.set=reverse\n"
);
@@ -298,101 +310,130 @@ main(int argc, char** argv) {
// Functions that might not get depracted as they are nice for
// configuration files, and thus might do with just some
// cleanup.
CMD2_REDIRECT("upload_rate", "throttle.global_up.max_rate.set_kb");
CMD2_REDIRECT("download_rate", "throttle.global_down.max_rate.set_kb");
CMD_REDIRECT("upload_rate", "throttle.global_up.max_rate.set_kb");
CMD_REDIRECT("download_rate", "throttle.global_down.max_rate.set_kb");
CMD2_REDIRECT("ratio.enable", "group.seeding.ratio.enable");
CMD2_REDIRECT("ratio.disable", "group.seeding.ratio.disable");
CMD2_REDIRECT("ratio.min", "group.seeding.ratio.min");
CMD2_REDIRECT("ratio.max", "group.seeding.ratio.max");
CMD2_REDIRECT("ratio.upload", "group.seeding.ratio.upload");
CMD2_REDIRECT("ratio.min.set", "group.seeding.ratio.min.set");
CMD2_REDIRECT("ratio.max.set", "group.seeding.ratio.max.set");
CMD2_REDIRECT("ratio.upload.set", "group.seeding.ratio.upload.set");
CMD_REDIRECT("ratio.enable", "group.seeding.ratio.enable");
CMD_REDIRECT("ratio.disable", "group.seeding.ratio.disable");
CMD_REDIRECT("ratio.min", "group.seeding.ratio.min");
CMD_REDIRECT("ratio.max", "group.seeding.ratio.max");
CMD_REDIRECT("ratio.upload", "group.seeding.ratio.upload");
CMD_REDIRECT("ratio.min.set", "group.seeding.ratio.min.set");
CMD_REDIRECT("ratio.max.set", "group.seeding.ratio.max.set");
CMD_REDIRECT("ratio.upload.set", "group.seeding.ratio.upload.set");
CMD2_REDIRECT("encryption", "protocol.encryption.set");
CMD2_REDIRECT("encoding_list", "encoding.add");
CMD_REDIRECT("check_hash", "pieces.hash.on_completion.set");
CMD2_REDIRECT("connection_leech", "protocol.connection.leech.set");
CMD2_REDIRECT("connection_seed", "protocol.connection.seed.set");
CMD_REDIRECT("connection_leech", "protocol.connection.leech.set");
CMD_REDIRECT("connection_seed", "protocol.connection.seed.set");
CMD2_REDIRECT("min_peers", "throttle.min_peers.normal.set");
CMD2_REDIRECT("max_peers", "throttle.max_peers.normal.set");
CMD2_REDIRECT("min_peers_seed", "throttle.min_peers.seed.set");
CMD2_REDIRECT("max_peers_seed", "throttle.max_peers.seed.set");
CMD_REDIRECT("min_peers", "throttle.min_peers.normal.set");
CMD_REDIRECT("max_peers", "throttle.max_peers.normal.set");
CMD_REDIRECT("min_peers_seed", "throttle.min_peers.seed.set");
CMD_REDIRECT("max_peers_seed", "throttle.max_peers.seed.set");
CMD2_REDIRECT("min_uploads", "throttle.min_uploads.set");
CMD2_REDIRECT("max_uploads", "throttle.max_uploads.set");
CMD2_REDIRECT("min_downloads", "throttle.min_downloads.set");
CMD2_REDIRECT("max_downloads", "throttle.max_downloads.set");
CMD_REDIRECT("min_uploads", "throttle.min_uploads.set");
CMD_REDIRECT("max_uploads", "throttle.max_uploads.set");
CMD_REDIRECT("min_downloads", "throttle.min_downloads.set");
CMD_REDIRECT("max_downloads", "throttle.max_downloads.set");
CMD2_REDIRECT("max_uploads_div", "throttle.max_uploads.div.set");
CMD2_REDIRECT("max_uploads_global", "throttle.max_uploads.global.set");
CMD2_REDIRECT("max_downloads_div", "throttle.max_downloads.div.set");
CMD2_REDIRECT("max_downloads_global", "throttle.max_downloads.global.set");
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("max_memory_usage", "pieces.memory.max.set");
CMD_REDIRECT("directory", "directory.default.set");
CMD_REDIRECT("session", "session.path.set");
CMD2_REDIRECT("bind", "network.bind_address.set");
CMD2_REDIRECT("ip", "network.local_address.set");
CMD2_REDIRECT("port_range", "network.port_range.set");
CMD_REDIRECT("scgi_port", "network.scgi.open_port");
CMD_REDIRECT("scgi_local", "network.scgi.open_local");
CMD2_REDIRECT("dht", "dht.mode.set");
CMD_REDIRECT("to_gm_time", "convert.gm_time");
CMD_REDIRECT("to_gm_date", "convert.gm_date");
CMD_REDIRECT("to_time", "convert.time");
CMD_REDIRECT("to_date", "convert.date");
CMD_REDIRECT("to_elapsed_time", "convert.elapsed_time");
CMD_REDIRECT("to_kb", "convert.kb");
CMD_REDIRECT("to_mb", "convert.mb");
CMD_REDIRECT("to_xb", "convert.xb");
CMD_REDIRECT("to_throttle", "convert.throttle");
CMD2_REDIRECT("port_random", "network.port_random.set");
CMD2_REDIRECT("proxy_address", "network.proxy_address.set");
// TODO: Deprecate these at some point a while after 1.1 release.
CMD2_REDIRECT("scgi_port", "network.scgi.open_port");
CMD2_REDIRECT("scgi_local", "network.scgi.open_local");
CMD_REDIRECT("d.multicall2", "d.multicall");
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("directory", "directory.default.set");
CMD2_REDIRECT("session", "session.path.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("check_hash", "pieces.hash.on_completion.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("key_layout", "keys.layout.set");
CMD2_REDIRECT("to_gm_time", "convert.gm_time");
CMD2_REDIRECT("to_gm_date", "convert.gm_date");
CMD2_REDIRECT("to_time", "convert.time");
CMD2_REDIRECT("to_date", "convert.date");
CMD2_REDIRECT("to_elapsed_time", "convert.elapsed_time");
CMD2_REDIRECT("to_kb", "convert.kb");
CMD2_REDIRECT("to_mb", "convert.mb");
CMD2_REDIRECT("to_xb", "convert.xb");
CMD2_REDIRECT("to_throttle", "convert.throttle");
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.
CMD2_ANY_VALUE_V("network.max_open_files.set", [](auto, auto) {
lt_log_print(torrent::LOG_WARN, "network.max_open_files.set is deprecated, use system.sockets.files.min_alloc.set instead.");
});
// if (rpc::call_command_value("method.use_intermediate") == 1) {
// CMD2_REDIRECT("execute", "execute2");
// CMD2_REDIRECT("schedule", "schedule2");
// CMD2_REDIRECT("schedule_remove", "schedule_remove2");
// } else if (rpc::call_command_value("method.use_intermediate") == 2) {
// 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");
// TODO: Remove file.append when cleaning these up.
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);
@@ -419,20 +460,24 @@ main(int argc, char** argv) {
});
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->ui()->load_input_history();
// Load session torrents and perform scheduled tasks to ensure
// session torrents are loaded before arg torrents.
torrent::net_thread::http_stack()->set_user_agent(USER_AGENT);
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();
load_session_torrents(session_thread::manager()->path());
load_arg_torrents(argv + firstArg, argv + argc);
// Make sure we update the display before any scheduled tasks can
// run, so that loading of torrents doesn't look like it hangs on
// startup.
// Make sure we update the display before any scheduled tasks can run, so that loading of
// torrents doesn't look like it hangs on startup.
control->display()->adjust_layout();
control->display()->receive_update();
+31 -31
View File
@@ -6,31 +6,31 @@
#include "command.h"
#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<core::Download*>(command_base* rawCommand, 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::tracker::Tracker*>(command_base* rawCommand, 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::FileListIterator*>(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* command_raw, 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* command_raw, 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* command_raw, target_type target, const torrent::Object& args);
namespace rpc {
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))
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);
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))
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) {
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))
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
command_base_call_value(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) {
return command_base_call_value_base<T>(rawCommand, target, rawArgs, 0, 1);
command_base_call_value(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
return command_base_call_value_base<T>(command_raw, target, args_raw, 0, 1);
}
template <typename T> const torrent::Object
command_base_call_value_kb(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) {
return command_base_call_value_base<T>(rawCommand, target, rawArgs, 0, 1024);
command_base_call_value_kb(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
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_kb);
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))
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)
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);
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))
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;
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);
+80 -51
View File
@@ -3,6 +3,7 @@
#include <functional>
#include <limits>
#include <new>
#include <torrent/common.h>
#include <torrent/object.h>
#include <torrent/data/file_list_iterator.h>
@@ -28,7 +29,6 @@ struct target_wrapper<void> {
typedef no_type* cleaned_type;
};
// Since c++0x isn't out yet...
template <typename T1, typename T2, typename T3>
struct rt_triple : private std::pair<T1, T2> {
typedef std::pair<T1, T2> base_type;
@@ -55,15 +55,11 @@ struct rt_triple : private std::pair<T1, T2> {
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;
typedef const torrent::Object (*command_base_call_type)(command_base*, target_type, const torrent::Object&);
typedef std::function<torrent::Object (target_type, const torrent::Object&)> base_function;
using target_type = rt_triple<int, void*, void*>;
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 <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&);
static const int target_generic = 0;
static const int target_any = 1;
static const int target_download = 2;
static const int target_peer = 3;
static const int target_tracker = 4;
static const int target_file = 5;
static const int target_file_itr = 6;
static constexpr int target_generic = 0;
static constexpr int target_any = 1;
static constexpr int target_download = 2;
static constexpr int target_peer = 3;
static constexpr int target_tracker = 4;
static constexpr int target_file = 5;
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 {
torrent::Object* begin() { return reinterpret_cast<torrent::Object*>(buffer); }
@@ -114,10 +111,48 @@ public:
char buffer[sizeof(torrent::Object) * max_arguments];
};
command_base() { new (&_pod<base_function>()) base_function(); }
command_base(const command_base& src) { new (&_pod<base_function>()) base_function(src._pod<base_function>()); }
command_base() : m_copy_helper(nullptr), m_dest_helper(nullptr) {}
~command_base() { _pod<base_function>().~base_function(); }
command_base(const command_base& src) {
m_copy_helper = src.m_copy_helper;
m_dest_helper = src.m_dest_helper;
if (src.m_copy_helper)
src.m_copy_helper(t_pod, src.t_pod);
}
command_base& operator=(const command_base& src) {
if (this != &src) {
if (m_dest_helper) m_dest_helper(t_pod);
m_copy_helper = src.m_copy_helper;
m_dest_helper = src.m_dest_helper;
if (src.m_copy_helper)
src.m_copy_helper(t_pod, src.t_pod);
}
return *this;
}
command_base(command_base&& src) noexcept {
m_copy_helper = src.m_copy_helper;
m_dest_helper = src.m_dest_helper;
if (src.m_copy_helper)
src.m_copy_helper(t_pod, src.t_pod);
}
command_base& operator=(command_base&& src) noexcept {
if (this != &src) {
if (m_dest_helper) m_dest_helper(t_pod);
m_copy_helper = src.m_copy_helper;
m_dest_helper = src.m_dest_helper;
if (src.m_copy_helper)
src.m_copy_helper(t_pod, src.t_pod);
}
return *this;
}
~command_base() {
if (m_dest_helper)
m_dest_helper(t_pod);
}
static torrent::Object* argument(unsigned int index) { return current_stack.begin() + index; }
static torrent::Object& argument_ref(unsigned int index) { return *(current_stack.begin() + index); }
@@ -132,55 +167,51 @@ public:
static void pop_stack(stack_type* stack, torrent::Object* last_stack);
template <typename T>
void set_function(T s, [[maybe_unused]] int value = command_base_is_valid<T>::value) { _pod<T>() = s; }
void set_function(T s, [[maybe_unused]] int value = command_base_is_valid<T>::value) {
static_assert(sizeof(T) <= sizeof(t_pod), "t_pod storage overflow");
static_assert(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>
void set_function_2(typename command_base_is_type<T>::type s, [[maybe_unused]] int value = command_base_is_valid<typename command_base_is_type<T>::type>::value) {
_pod<typename command_base_is_type<T>::type>() = s;
set_function<typename command_base_is_type<T>::type>(std::move(s));
}
// The std::function object in GCC is castable between types with a
// pointer to a struct of ctor/dtor/calls for non-POD slots. As such
// it should be safe to cast between different std::function
// template types, yet what the C++0x standard will say about this I
// have no idea atm.
template <typename tmpl> tmpl& _pod() { return reinterpret_cast<tmpl&>(t_pod); }
template <typename tmpl> const tmpl& _pod() const { return reinterpret_cast<const tmpl&>(t_pod); }
template <typename Func, typename T, typename Args>
static const torrent::Object _call(command_base* cmd, target_type target, Args args);
command_base& operator = (const command_base& src) {
_pod<base_function>() = src._pod<base_function>();
return *this;
}
protected:
// For use by functions that need to use placeholders to arguments
// within commands. E.d. callable command strings where one of the
// arguments within the command needs to be supplied by the caller.
using copy_fn_t = void (*)(void* dest, const void* src);
using dest_fn_t = void (*)(void* ptr);
#ifdef HAVE_CXX11
union {
base_function t_pod;
// char t_pod[sizeof(base_function)];
};
#else
union {
char t_pod[sizeof(base_function)];
};
#endif
alignas(optimal_alignment) char t_pod[sizeof(base_function)];
copy_fn_t m_copy_helper;
dest_fn_t m_dest_helper;
};
template <typename T1 = void, typename T2 = void>
struct target_type_id {
// Nothing here, so we cause an error.
};
template <typename T> inline bool
is_target_compatible(const target_type& target) { return target.first == target_type_id<T>::value; }
// Splitting pairs into separate targets.
inline bool is_target_pair(const target_type& target) { return target.first >= command_base::target_download_pair; }
template <typename T> inline T
@@ -203,7 +234,7 @@ command_base::_call(command_base* cmd, target_type target, Args args) {
#define COMMAND_BASE_TEMPLATE_TYPE(func_type, func_parm) \
template <typename T, int proper = target_type_id<T>::proper_type> struct func_type { typedef std::function<func_parm> type; }; \
\
\
template <> struct command_base_is_valid<func_type<target_type>::type> { static const int value = 1; }; \
template <> struct command_base_is_valid<func_type<core::Download*>::type> { static const int value = 1; }; \
template <> struct command_base_is_valid<func_type<torrent::Peer*>::type> { static const int value = 1; }; \
@@ -211,8 +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::FileListIterator*>::type> { static const int value = 1; };
// template <typename Q> struct command_base_is_valid<typename func_type<Q>::type > { static const int value = 1; };
COMMAND_BASE_TEMPLATE_TYPE(command_function, torrent::Object (T, const torrent::Object&));
COMMAND_BASE_TEMPLATE_TYPE(command_value_function, torrent::Object (T, const torrent::Object::value_type&));
COMMAND_BASE_TEMPLATE_TYPE(command_string_function, torrent::Object (T, const std::string&));
@@ -220,7 +249,7 @@ COMMAND_BASE_TEMPLATE_TYPE(command_list_function, torrent::Object (T, const to
#define COMMAND_BASE_TEMPLATE_CALL(func_name, func_type) \
template <typename T> const torrent::Object func_name(command_base* rawCommand, target_type target, const torrent::Object& args); \
\
\
template <> struct command_base_is_type<func_name<target_type> > { static const int value = 1; typedef func_type<target_type>::type type; }; \
template <> struct command_base_is_type<func_name<core::Download*> > { static const int value = 1; typedef func_type<core::Download*>::type type; }; \
template <> struct command_base_is_type<func_name<torrent::Peer*> > { static const int value = 1; typedef func_type<torrent::Peer*>::type type; }; \
+84 -51
View File
@@ -1,23 +1,32 @@
#include "config.h"
#include <cassert>
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <spawn.h>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <torrent/net/fd.h>
#include <torrent/system/thread.h>
#include <torrent/system/types.h>
#include "exec_file.h"
#include "parse.h"
// Standard POSIX environment pointer
extern char** environ;
namespace rpc {
// TODO: Access fd through torrent logging?
int
ExecFile::execute(const char* file, char* const* argv, int flags) {
assert(!((flags & flag_capture) && (flags & flag_background)));
// Write the executed command and its parameters to the log fd.
[[maybe_unused]] int result;
@@ -34,86 +43,103 @@ ExecFile::execute(const char* file, char* const* argv, int flags) {
result = write(m_log_fd, "\n---\n", sizeof("\n---\n"));
}
int pipeFd[2];
posix_spawn_file_actions_t actions{};
if ((flags & flag_capture) && pipe(pipeFd))
throw torrent::input_error("ExecFile::execute(...) Pipe creation failed.");
if (posix_spawn_file_actions_init(&actions) != 0)
throw torrent::internal_error("ExecFile::execute(...) posix_spawn_file_actions_init failed.");
pid_t childPid = fork();
posix_spawnattr_t attr;
posix_spawnattr_init(&attr);
if (childPid == -1) {
if (flags & flag_capture) {
::close(pipeFd[0]);
::close(pipeFd[1]);
}
throw torrent::input_error("ExecFile::execute(...) Fork failed.");
// Try to avoid leaking open fds to the spawned process. Prefer POSIX_SPAWN_CLOEXEC_DEFAULT
// (macOS-only) or posix_spawn_file_actions_addclosefrom_np (glibc >= 2.34, FreeBSD >= 13.1).
//
// Other platforms like musl libc, OpenBSD and NetBSD must rely on explicit O_CLOEXEC.
// Handle standard input redirection (/dev/null), posix_spawn_file_actions_addopen handles opening
// and dup2 natively
if (posix_spawn_file_actions_addopen(&actions, 0, "/dev/null", O_RDWR, 0) != 0) {
// Fallback if open fails inside action setup
posix_spawn_file_actions_addclose(&actions, 0);
}
if (childPid == 0) {
if (flags & flag_background) {
pid_t detached_pid = fork();
int pipe_0 = -1;
int pipe_1 = -1;
if (detached_pid == -1)
_exit(-1);
// Handle standard output redirection
if (flags & flag_capture) {
torrent::fd_open_pipe(pipe_0, pipe_1);
if (detached_pid != 0) {
if (m_log_fd != -1)
result = write(m_log_fd, "\n--- Background task ---\n", sizeof("\n--- Background task ---\n"));
posix_spawn_file_actions_adddup2(&actions, pipe_1, 1);
_exit(0);
}
// Ensure the write end of the pipe is closed in the child after duplicating.
posix_spawn_file_actions_addclose(&actions, pipe_0);
posix_spawn_file_actions_addclose(&actions, pipe_1);
m_log_fd = -1;
flags &= ~flag_capture;
}
} else if (m_log_fd != -1) {
posix_spawn_file_actions_adddup2(&actions, m_log_fd, 1);
int devNull = open("/dev/null", O_RDWR);
} else {
posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_RDWR, 0);
}
if (devNull != -1)
dup2(devNull, 0);
else
::close(0);
if (m_log_fd != -1) {
posix_spawn_file_actions_adddup2(&actions, m_log_fd, 2);
} else {
posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_RDWR, 0);
}
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);
short spawn_flags = 0;
if (m_log_fd != -1)
dup2(m_log_fd, 2);
else if (devNull != -1)
dup2(devNull, 2);
else
::close(2);
#if defined(POSIX_SPAWN_CLOEXEC_DEFAULT)
spawn_flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
#elif defined(HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP)
posix_spawn_file_actions_addclosefrom_np(&actions, 3);
#endif
// Close all fd's.
for (int i = 3, last = sysconf(_SC_OPEN_MAX); i != last; i++)
::close(i);
if (flags & flag_background) {
#ifdef POSIX_SPAWN_SETSID
spawn_flags |= POSIX_SPAWN_SETSID;
#else
spawn_flags |= POSIX_SPAWN_SETPGROUP;
posix_spawnattr_setpgroup(&attr, 0);
#endif
}
result = execvp(file, argv);
posix_spawnattr_setflags(&attr, spawn_flags);
_exit(result);
pid_t child_pid{};
int spawn_status = posix_spawnp(&child_pid, file, &actions, &attr, argv, environ);
posix_spawn_file_actions_destroy(&actions);
posix_spawnattr_destroy(&attr);
if (spawn_status != 0) {
if (pipe_0 != -1)
torrent::fd_close(pipe_0);
if (pipe_1 != -1)
torrent::fd_close(pipe_1);
throw torrent::input_error("ExecFile::execute() posix_spawn failed: " + torrent::system::errno_enum_str(spawn_status));
}
if (flags & flag_capture) {
m_capture = std::string();
::close(pipeFd[1]);
torrent::fd_close(pipe_1);
char buffer[4096];
ssize_t length;
do {
length = read(pipeFd[0], buffer, sizeof(buffer));
length = read(pipe_0, buffer, sizeof(buffer));
if (length > 0)
m_capture += std::string(buffer, length);
} while (length > 0);
::close(pipeFd[0]);
torrent::fd_close(pipe_0);
if (m_log_fd != -1) {
result = write(m_log_fd, "Captured output:\n", sizeof("Captured output:\n"));
@@ -121,9 +147,16 @@ ExecFile::execute(const char* file, char* const* argv, int flags) {
}
}
if (flags & flag_background) {
if (m_log_fd != -1)
result = write(m_log_fd, "\n--- Running in Background ---\n", sizeof("\n--- Running in Background ---\n"));
return 0;
}
int status;
while (waitpid(childPid, &status, 0) == -1) {
while (::waitpid(child_pid, &status, 0) == -1) {
switch (errno) {
case EINTR:
continue;
+34 -5
View File
@@ -6,6 +6,7 @@
#include <string>
#include <torrent/common.h>
#include <torrent/torrent.h>
#include <torrent/utils/string_manip.h>
#include "rpc/rpc_manager.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_integer:
return torrent::Object(value.get<int64_t>());
case json::value_t::boolean:
return value.get<bool>() ? torrent::Object(int64_t(1)) : torrent::Object(int64_t(0));
case json::value_t::string:
return torrent::Object(value.get<std::string>());
case json::value_t::array: {
auto array_raw = torrent::Object::create_list();
auto& array = array_raw.as_list();
for (const auto& entry : value) {
for (const auto& entry : value)
array.push_back(json_to_object(entry));
}
return array_raw;
}
case json::value_t::object: {
auto map_raw = torrent::Object::create_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());
}
return map_raw;
}
case json::value_t::number_float:
@@ -61,17 +67,38 @@ json_to_object(const json& value) {
}
}
// Note: Only throw rpc_errors here.
json
object_to_json(const torrent::Object& object) noexcept {
object_to_json(const torrent::Object& object) {
switch (object.type()) {
case torrent::Object::TYPE_VALUE:
return object.as_value();
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();
case torrent::Object::TYPE_LIST: {
json result = json::array();
for (const auto& obj : object.as_list())
result.push_back(object_to_json(obj));
return result;
}
case torrent::Object::TYPE_MAP: {
@@ -135,7 +162,9 @@ jsonrpc_call_command(const std::string& method, const json& params) {
try {
const auto& result = rpc::commands.call_command(itr, params_object, target);
return object_to_json(result);
} catch (untrusted_error& e) {
throw rpc_error(JSONRPC_METHOD_NOT_FOUND_ERROR, e.what());
}
+5 -1
View File
@@ -10,6 +10,10 @@
#ifdef HAVE_LUA
#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
#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))));
*deleter = [tracker]() { delete tracker; };
*target = rpc::make_target(command_base::target_tracker, target);
*target = rpc::make_target(command_base::target_tracker, tracker);
}
break;
+234 -135
View File
@@ -1,9 +1,9 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
/****************************************************************************\
@@ -34,10 +34,10 @@
// #include <nlohmann/adl_serializer.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -47,10 +47,10 @@
// #include <nlohmann/detail/abi_macros.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -59,15 +59,15 @@
#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
#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!"
#endif
#endif
#endif
#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_PATCH 3 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum)
#ifndef JSON_DIAGNOSTICS
#define JSON_DIAGNOSTICS 0
@@ -160,10 +160,10 @@
// #include <nlohmann/detail/conversions/from_json.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -186,10 +186,10 @@
// #include <nlohmann/detail/exceptions.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -206,10 +206,10 @@
// #include <nlohmann/detail/value_t.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -222,10 +222,10 @@
// #include <nlohmann/detail/macro_scope.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -234,10 +234,10 @@
// #include <nlohmann/detail/meta/detected.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -247,10 +247,10 @@
// #include <nlohmann/detail/meta/void_t.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -334,10 +334,10 @@ NLOHMANN_JSON_NAMESPACE_END
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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-License-Identifier: MIT
@@ -2398,15 +2398,20 @@ JSON_HEDLEY_DIAGNOSTIC_POP
// C++ language standard detection
// 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(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
#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)
#define JSON_HAS_CPP_23
#define JSON_HAS_CPP_20
#define JSON_HAS_CPP_17
#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_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
#endif
// the cpp 11 flag is always specified because it is the minimal required version
@@ -2767,88 +2772,140 @@ JSON_HEDLEY_DIAGNOSTIC_POP
@brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
@since version 3.9.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#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__)) } \
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__)) }
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__)) } \
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
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
@since version 3.11.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#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__)) } \
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__)) }
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__)) } \
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
@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, ...) \
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
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
@since version 3.9.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/
#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__)) } \
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__)) }
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__)) } \
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
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT
@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, ...) \
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__)) } \
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__)) }
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__)) } \
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
@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, ...) \
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
@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, ...) \
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__)) } \
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__)) }
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__)) } \
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, ...) \
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__)) } \
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__)) }
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__)) } \
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, ...) \
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
@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, ...) \
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__)) } \
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__)) }
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__)) } \
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, ...) \
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__)) } \
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__)) }
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__)) } \
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, ...) \
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
// 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);
//
// 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>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -3084,10 +3141,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/position_t.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -3126,10 +3183,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/cpp_future.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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-License-Identifier: MIT
@@ -3300,10 +3357,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/type_traits.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -3317,10 +3374,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/iterator_traits.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -3385,10 +3442,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/call_std/begin.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -3405,10 +3462,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/call_std/end.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -3429,10 +3486,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/json_fwd.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
@@ -4280,10 +4337,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/string_concat.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -4534,16 +4591,34 @@ class exception : public std::exception
{
return concat(a, '/', detail::escape(b));
});
return concat('(', str, ") ");
return concat('(', str, ") ", get_byte_positions(leaf_element));
#else
static_cast<void>(leaf_element);
return "";
return get_byte_positions(leaf_element);
#endif
}
private:
/// an exception object as storage for error messages
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
@@ -4682,10 +4757,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/identity_tag.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -4706,10 +4781,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/std_fs.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -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>()...);
}
template<typename BasicJsonType>
std::tuple<> from_json_tuple_impl_base(BasicJsonType& /*unused*/, index_sequence<> /*unused*/)
{
return {};
}
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*/)
{
@@ -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));
}
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
@@ -5278,10 +5364,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/conversions/to_json.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -5304,10 +5390,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -5328,10 +5414,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/string_utils.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -5987,6 +6073,13 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<
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>
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>
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
@@ -6072,10 +6170,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/byte_container_with_subtype.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -6184,10 +6282,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/hash.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -6317,10 +6415,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/binary_reader.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -6346,10 +6444,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/input_adapters.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -6901,10 +6999,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/json_sax.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -6920,10 +7018,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/lexer.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -8171,7 +8269,7 @@ scan_number_done:
// we are done scanning a number)
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;
// 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>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -12752,10 +12850,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/parser.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -13281,10 +13379,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/internal_iterator.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -13294,10 +13392,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -13453,10 +13551,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/iter_impl.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -14224,10 +14322,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -14359,10 +14457,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/json_custom_base_class.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -14401,10 +14499,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/json_pointer.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -14634,7 +14732,7 @@ class json_pointer
}
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
const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
if (p == p_end // invalid input or empty string
@@ -15156,7 +15254,7 @@ class json_pointer
// iterate array and use index as reference string
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);
}
}
@@ -15175,7 +15273,7 @@ class json_pointer
// iterate object and use keys as reference string
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;
@@ -15396,10 +15494,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/json_ref.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -15490,10 +15588,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/output/binary_writer.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -15516,10 +15614,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/output/output_adapters.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -17496,11 +17594,11 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/output/serializer.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// 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
@@ -17521,11 +17619,11 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/conversions/to_chars.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// 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
@@ -19617,10 +19715,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/ordered_map.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -20168,7 +20266,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
basic_json result;
result["copyright"] = "(C) 2013-2023 Niels Lohmann";
result["copyright"] = "(C) 2013-2025 Niels Lohmann";
result["name"] = "JSON for Modern C++";
result["url"] = "https://github.com/nlohmann/json";
result["version"]["string"] =
@@ -20433,7 +20531,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
object = nullptr; // silence warning, see #821
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;
}
@@ -25220,10 +25318,10 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC
// #include <nlohmann/detail/macro_unscope.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
@@ -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_17
#undef JSON_HAS_CPP_20
#undef JSON_HAS_CPP_23
#undef JSON_HAS_FILESYSTEM
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
#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>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| 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
+1 -1
View File
@@ -17,7 +17,7 @@ namespace rpc {
struct object_storage_node {
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;
+2 -2
View File
@@ -105,8 +105,8 @@ private:
bool m_is_jsonrpc_enabled{true};
bool m_is_xmlrpc_enabled{true};
std::atomic<bool> m_scgi_allow_compression{false};
std::atomic<unsigned int> m_scgi_min_compress_size{0};
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_file m_slot_find_file;
+50 -58
View File
@@ -5,12 +5,11 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/un.h>
#include <torrent/connection_manager.h>
#include <torrent/torrent.h>
#include <torrent/exceptions.h>
#include <torrent/net/fd.h>
#include <torrent/net/poll.h>
#include <torrent/net/socket_address.h>
#include <torrent/system/poll.h>
#include <torrent/runtime/socket_manager.h>
#include "control.h"
@@ -34,64 +33,60 @@ SCgi::~SCgi() {
void
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,
sa->sa_family);
int fd = torrent::fd_open_family(torrent::fd_flag_stream | torrent::fd_flag_nonblock | torrent::fd_flag_reuse_address,
sa->sa_family);
if (fd == -1)
throw torrent::resource_error("Could not open socket for listening: " + std::string(std::strerror(errno)));
if (fd == -1)
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)) {
torrent::fd_close(fd);
throw torrent::resource_error("Could not set socket option IP_DONTROUTE: " + std::string(std::strerror(errno)));
}
if (dont_route && !torrent::fd_set_dont_route(fd, true)) {
torrent::fd_close(fd);
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
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.");
auto buffer = std::make_unique<char[]>(sizeof(sockaddr_un) + filename.size() + 1);
sockaddr_un* sa = reinterpret_cast<sockaddr_un*>(buffer.get());
sa->sun_family = AF_LOCAL;
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)
throw torrent::resource_error("Could not open socket for listening: " + std::string(std::strerror(errno)));
if (fd == -1)
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;
}
void
SCgi::open_fd(int fd) {
torrent::runtime::socket_manager()->open_event_or_throw(this, [&]() {
if (!torrent::fd_set_nonblock(fd))
throw torrent::resource_error("Could not set non-blocking on systemd fd: " +
std::string(std::strerror(errno)));
set_file_descriptor(fd);
// fd is already bound and listening; no bind()/listen() needed.
});
if (!torrent::fd_set_nonblock(fd))
throw torrent::resource_error("Could not set non-blocking on systemd fd: " +
std::string(std::strerror(errno)));
set_file_descriptor(fd);
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
@@ -116,7 +111,6 @@ SCgi::activate() {
torrent::this_thread::poll()->open(this);
torrent::this_thread::poll()->insert_read(this);
torrent::this_thread::poll()->insert_error(this);
}
// TODO: This should close the fd to avoid reuse.
@@ -132,15 +126,13 @@ SCgi::stop() {
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::fd_close(file_descriptor());
set_file_descriptor(-1);
});
torrent::connection_manager()->dec_socket_count();
if (!m_path.empty())
::unlink(m_path.c_str());
}
@@ -159,38 +151,38 @@ SCgi::event_read() {
if (m_current == m_tasks.end())
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) {
// TODO: Currently just close, although we should remove ourselves from read.
int fd = torrent::fd_accept(file_descriptor());
if (fd != -1)
torrent::fd_close(fd);
torrent::fd_close(fd);
continue;
}
auto open_func = [this, 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)));
}
auto open_func = [this, fd, task = m_current->get()]() {
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();
};
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)
break;
+48 -24
View File
@@ -11,10 +11,10 @@
#include <sys/socket.h>
#include <torrent/exceptions.h>
#include <torrent/net/fd.h>
#include <torrent/net/poll.h>
#include <torrent/runtime/socket_manager.h>
#include <torrent/system/callbacks.h>
#include <torrent/system/poll.h>
#include <torrent/utils/log.h>
#include <torrent/system/thread.h>
#include "control.h"
#include "globals.h"
@@ -24,6 +24,12 @@
namespace rpc {
SCgiTask::SCgiTask()
: m_callback_id(torrent::system::make_callback_id()) {
reset_file_descriptor();
}
void
SCgiTask::open(SCgi* parent, int fd) {
set_file_descriptor(fd);
@@ -34,11 +40,15 @@ SCgiTask::open(SCgi* parent, int fd) {
m_content_length = 0;
m_content_type = XML;
m_content_type_set = 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()->insert_read(this);
torrent::this_thread::poll()->insert_error(this);
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::fd_close(file_descriptor());
set_file_descriptor(-1);
reset_file_descriptor();
};
void
@@ -62,14 +72,13 @@ SCgiTask::close() {
if (!is_open())
return;
torrent::main_thread::thread()->cancel_callback_and_wait(this);
torrent::system::Thread::self()->cancel_callback(this);
torrent::system::cancel_callback_and_wait(m_callback_id, scgi_thread::thread(), torrent::main_thread::thread());
torrent::runtime::socket_manager()->close_event_or_throw(this, [this]() {
torrent::this_thread::poll()->remove_and_close(this);
torrent::fd_close(file_descriptor());
set_file_descriptor(-1);
reset_file_descriptor();
});
// The callbacks are guaranteed to be finished/canceled at this point.
@@ -80,7 +89,15 @@ SCgiTask::close() {
void
SCgiTask::event_read() {
int bytes = ::recv(m_fileDesc, m_buffer.data() + m_position, m_buffer.size() - m_position, 0);
int read_length = m_buffer.size() - m_position;
if (m_content_length == 0)
read_length--;
if (read_length <= 0)
throw torrent::internal_error("SCgiTask::event_read() no space in buffer for event_read.");
int bytes = ::recv(file_descriptor(), m_buffer.data() + m_position, read_length, 0);
if (bytes <= 0) {
if (bytes == 0 || !(errno == EAGAIN || errno == EINTR))
@@ -108,6 +125,9 @@ SCgiTask::event_read() {
if (current == m_buffer.data())
return;
if (current == m_buffer.data() + m_position)
return;
if (*current != ':' || header_size < 17 || header_size > max_header_size)
goto event_read_failed;
@@ -145,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);
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);
return;
@@ -155,10 +180,10 @@ event_read_failed:
void
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 (!(errno == EAGAIN || errno == EINTR || errno == EPIPE))
if (!(errno == EAGAIN || errno == EINTR))
close();
return;
@@ -223,7 +248,8 @@ SCgiTask::parse_headers(const char* current, unsigned int header_length) {
if (std::strncmp(value, "1", 1+1) == 0)
m_trusted = false;
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
return false;
}
@@ -277,19 +303,17 @@ scgi_match_content_type(const std::string& content_type, const char* type) {
bool
SCgiTask::detect_content_type(const std::string& content_type) {
if (content_type.empty()) {
// If no CONTENT_TYPE was supplied, peek at the body to check if it's JSON
// { is a single request object, while [ is a batch array
if (m_buffer[m_body] == '{' || m_buffer[m_body] == '[')
m_content_type = ContentType::JSON;
else
m_content_type = ContentType::XML;
// Defer body-peek detection until the full body is received.
// event_read() will auto-detect from the first body byte after
// confirming m_position >= m_body + m_content_length.
} 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")) {
m_content_type = ContentType::XML;
m_content_type = ContentType::XML;
m_content_type_set = true;
} else {
// If the content type is not JSON or XML, we don't know how to handle it.
@@ -301,7 +325,7 @@ SCgiTask::detect_content_type(const std::string& content_type) {
void
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;
@@ -328,7 +352,7 @@ SCgiTask::receive_call(const char* buffer, uint32_t length) {
m_result_mutex.lock();
m_result_mutex.unlock();
scgi_thread::thread()->callback_interrupt_polling(this, [this]() {
scgi_thread::callback_interrupt(m_callback_id, [this]() {
if (!is_open())
return;
@@ -348,7 +372,7 @@ SCgiTask::receive_call(const char* buffer, uint32_t length) {
m_result_mutex.lock();
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.
// std::atomic_thread_fence(std::memory_order_acquire);
m_result_mutex.lock();
@@ -363,7 +387,7 @@ SCgiTask::receive_call(const char* buffer, uint32_t length) {
void
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))
throw torrent::internal_error("SCgiTask::receive_write(...) received bad input.");
+6 -4
View File
@@ -18,12 +18,12 @@ public:
enum ContentType { XML, JSON };
SCgiTask() { m_fileDesc = -1; }
SCgiTask();
const char* type_name() const override { return "scgi-task"; }
bool is_open() const { return m_fileDesc != -1; }
bool is_available() const { return m_fileDesc == -1; }
bool is_open() const { return file_descriptor() != -1; }
bool is_available() const { return file_descriptor() == -1; }
void open(SCgi* parent, int fd);
void cancel_open();
@@ -54,7 +54,8 @@ private:
void plaintext_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;
@@ -67,6 +68,7 @@ private:
bool m_accepts_compression{};
bool m_trusted{true};
bool m_content_type_set{false};
};
}
+71 -79
View File
@@ -27,7 +27,7 @@ distribution.
#include "tinyxml2.h"
#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 <stdarg.h>
#else
@@ -239,13 +239,13 @@ char* StrPair::ParseName( char* p )
if ( !p || !(*p) ) {
return 0;
}
if ( !XMLUtil::IsNameStartChar( (unsigned char) *p ) ) {
if ( !XMLUtil::IsNameStartChar( static_cast<unsigned char>(*p) ) ) {
return 0;
}
char* const start = p;
++p;
while ( *p && XMLUtil::IsNameChar( (unsigned char) *p ) ) {
while ( *p && XMLUtil::IsNameChar( static_cast<unsigned char>(*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;
if ( *(p+1) == '#' && *(p+2) ) {
unsigned long ucs = 0;
TIXMLASSERT( sizeof( ucs ) >= 4 );
static const uint32_t MAX_CODE_POINT = 0x10FFFF;
if (*(p + 1) == '#' && *(p + 2)) {
uint32_t ucs = 0;
ptrdiff_t delta = 0;
unsigned mult = 1;
uint32_t mult = 1;
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.
const char* q = p+3;
if ( !(*q) ) {
return 0;
}
hex = true;
radix = 16;
terminator = 'x';
q = strchr( q, SEMICOLON );
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;
}
q = p + 3;
}
else {
// Decimal.
const char* q = p+2;
if ( !(*q) ) {
q = p + 2;
}
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;
}
TIXMLASSERT(digit < radix);
q = strchr( q, SEMICOLON );
if ( !q ) {
return 0;
const unsigned int digitScaled = mult * digit;
ucs += digitScaled;
mult *= radix;
// 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;
while ( *q != '#' ) {
if ( *q >= '0' && *q <= '9' ) {
const unsigned int digit = *q - '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;
}
}
// Out of range:
if (ucs > MAX_CODE_POINT) {
return 0;
}
// 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+1;
return p + 1;
}
void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
{
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) {
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) {
*value = (uint64_t)v;
*value = static_cast<uint64_t>(v);
return true;
}
return false;
@@ -1982,7 +1974,7 @@ char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
}
// attribute.
if (XMLUtil::IsNameStartChar( (unsigned char) *p ) ) {
if (XMLUtil::IsNameStartChar( static_cast<unsigned char>(*p) ) ) {
XMLAttribute* attrib = CreateAttribute();
TIXMLASSERT( attrib );
attrib->_parseLineNum = _document->_parseCurLineNum;
+2 -2
View File
@@ -98,11 +98,11 @@ distribution.
/* Versioning, past 1.0.14:
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_PATCH_VERSION = 0;
#define TINYXML2_MAJOR_VERSION 10
#define TINYXML2_MAJOR_VERSION 11
#define TINYXML2_MINOR_VERSION 0
#define TINYXML2_PATCH_VERSION 0
+8 -6
View File
@@ -1,3 +1,4 @@
#include <deque>
#include "config.h"
#include "xmlrpc.h"
@@ -9,20 +10,21 @@
namespace rpc {
std::vector<std::unique_ptr<const char>> XmlRpc::m_command_names;
std::deque<std::string> XmlRpc::m_command_names;
const char*
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.");
for (const auto& itr : m_command_names) {
if (::strcmp(itr.get(), name) == 0)
return itr.get();
if (itr == name)
return itr.c_str();
}
m_command_names.push_back(std::unique_ptr<const char>(::strdup(name)));
return m_command_names.back().get();
m_command_names.push_back(name);
return m_command_names.back().c_str();
}
#ifndef HAVE_XMLRPC_C
+2 -1
View File
@@ -1,6 +1,7 @@
#ifndef RTORRENT_RPC_XMLRPC_H
#define RTORRENT_RPC_XMLRPC_H
#include <deque>
#include <functional>
#include <torrent/common.h>
#include <torrent/hash_string.h>
@@ -61,7 +62,7 @@ public:
private:
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_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
if (rpc.dialect() != XmlRpc::dialect_generic)
return xmlrpc_i8_new(env, object.as_value());
#else
return xmlrpc_int_new(env, object.as_value());
#endif
return xmlrpc_int_new(env, object.as_value());
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
// The versions that support I8 do implicit utf-8 validation.
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/object.h>
#include <torrent/utils/string_manip.h>
#include "parse_commands.h"
#include "rpc/tinyxml2/tinyxml2.h"
@@ -66,56 +67,85 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
if (elem == nullptr) {
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received null element to convert");
}
if (std::strncmp(elem->Value(), "value", sizeof("value")) != 0) {
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received non-value element to convert");
}
auto root_element = elem->FirstChild();
if (root_element == nullptr)
throw rpc_error(XMLRPC_TYPE_ERROR, "empty value element");
auto root_type = root_element->Value();
if (std::strncmp(root_type, "string", sizeof("string")) == 0) {
auto child_element = root_element->FirstChild();
if (child_element == nullptr) {
if (child_element == nullptr)
return torrent::Object("");
}
return torrent::Object(child_element->ToText()->Value());
} else if (std::strncmp(root_type, "int", sizeof("int")) == 0 ||
std::strncmp(root_type, "i4", sizeof("i4")) == 0 ||
std::strncmp(root_type, "i8", sizeof("i8")) == 0) {
return torrent::Object(element_to_int(root_element));
} else if (std::strncmp(root_type, "boolean", sizeof("boolean")) == 0) {
auto boolean_text = std::string(root_element->FirstChild()->ToText()->Value());
if (boolean_text == "1") {
auto child_element = root_element->FirstChild();
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);
} else if (boolean_text == "0") {
else if (boolean_text == "0")
return torrent::Object((int64_t)0);
}
throw rpc_error(XMLRPC_TYPE_ERROR, "unknown boolean value: " + boolean_text);
} else if (std::strncmp(root_type, "array", sizeof("array")) == 0) {
auto array_raw = torrent::Object::create_list();
auto& array = array_raw.as_list();
auto data_element = root_element->ToElement()->FirstChildElement("data");
if (data_element == nullptr)
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));
}
return array_raw;
} else if (std::strncmp(root_type, "struct", sizeof("struct")) == 0) {
auto map_raw = torrent::Object::create_map();
auto& map = map_raw.as_map();
for (auto child = root_element->FirstChildElement("member"); child; child = child->NextSiblingElement("member")) {
auto key = child->FirstChildElement("name")->GetText();
map[key] = std::move(xml_value_to_object(child->FirstChildElement("value")));
auto name_element = child->FirstChildElement("name");
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;
} else if (std::strncmp(root_type, "base64", sizeof("base64")) == 0) {
auto child_element = root_element->FirstChild();
if (child_element == nullptr) {
if (child_element == nullptr)
return torrent::Object("");
}
return torrent::Object(utils::decode_base64(utils::remove_newlines(child_element->ToText()->Value())));
} else {
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received unsupported value type: " + std::string(root_type));
}
return torrent::Object();
}
@@ -123,15 +153,32 @@ void
print_object_xml(const torrent::Object& obj, tinyxml2::XMLPrinter* printer) {
switch (obj.type()) {
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->PushText(obj.as_string().c_str());
printer->CloseElement(true);
break;
case torrent::Object::TYPE_VALUE:
printer->OpenElement("i8", true);
printer->PushText(std::to_string(obj.as_value()).c_str());
printer->CloseElement(true);
break;
case torrent::Object::TYPE_LIST:
printer->OpenElement("array", true);
printer->OpenElement("data", true);
+8 -15
View File
@@ -12,11 +12,6 @@
namespace scgi {
class ThreadScgiInternal {
public:
static ThreadScgi* thread_scgi() { return ThreadScgi::internal_thread_scgi(); }
};
ThreadScgi* ThreadScgi::m_thread_scgi{};
void
@@ -60,7 +55,7 @@ ThreadScgi::set_scgi(rpc::SCgi* scgi) {
change_rpc_log();
callback(nullptr, [this]() {
callback([this]() {
if (m_scgi == nullptr)
throw torrent::internal_error("Tried to start SCGI but object was not present.");
@@ -72,7 +67,7 @@ ThreadScgi::set_scgi(rpc::SCgi* scgi) {
void
ThreadScgi::set_rpc_log(const std::string& filename) {
callback(nullptr, [this, filename]() {
callback([this, filename]() {
m_rpc_log_filename = filename;
change_rpc_log();
});
@@ -127,15 +122,13 @@ ThreadScgi::next_timeout() {
namespace scgi_thread {
torrent::system::Thread* thread() { return scgi::ThreadScgiInternal::thread_scgi(); }
std::thread::id thread_id() { return scgi::ThreadScgiInternal::thread_scgi()->thread_id(); }
torrent::system::Thread* thread() { return scgi::ThreadScgi::thread_scgi(); }
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 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); }
void callback_interrupt(torrent::system::callback_id& id, std::function<void ()>&& fn) { scgi::ThreadScgi::thread_scgi()->callback_interrupt(id, std::move(fn)); }
rpc::SCgi* scgi() { return scgi::ThreadScgiInternal::thread_scgi()->scgi(); }
void set_scgi(rpc::SCgi* scgi) { scgi::ThreadScgiInternal::thread_scgi()->set_scgi(scgi); }
void set_rpc_log(const std::string& filename) { scgi::ThreadScgiInternal::thread_scgi()->set_rpc_log(filename); }
rpc::SCgi* scgi() { return scgi::ThreadScgi::thread_scgi()->scgi(); }
void set_scgi(rpc::SCgi* scgi) { scgi::ThreadScgi::thread_scgi()->set_scgi(scgi); }
void set_rpc_log(const std::string& filename) { scgi::ThreadScgi::thread_scgi()->set_rpc_log(filename); }
} // namespace scgi_thread
+2 -6
View File
@@ -11,8 +11,6 @@ class SCgi;
namespace scgi {
class ThreadScgiInternal;
class ThreadScgi : public torrent::system::Thread {
public:
@@ -28,8 +26,6 @@ public:
void set_rpc_log(const std::string& filename);
protected:
friend class ThreadScgiInternal;
ThreadScgi() = default;
static auto internal_thread_scgi() { return m_thread_scgi; }
@@ -43,9 +39,9 @@ private:
void task_touch_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;
};
+12 -5
View File
@@ -5,6 +5,7 @@
#include <cassert>
#include <cstdlib>
#include <torrent/exceptions.h>
#include <torrent/system/callbacks.h>
#include <torrent/utils/log.h>
#include "globals.h"
@@ -23,6 +24,7 @@ namespace session {
SessionManager::SessionManager(torrent::system::Thread* thread)
: m_thread(thread),
m_callback_id(torrent::system::make_callback_id()),
m_lockfile(std::make_unique<utils::Lockfile>()) {
}
@@ -221,8 +223,7 @@ SessionManager::cleanup() {
LT_LOG("session manager cleaned up", 0);
session_thread::cancel_callback(this);
torrent::main_thread::cancel_callback(this);
torrent::system::cancel_callback_and_wait(m_callback_id, session_thread::thread(), torrent::main_thread::thread());
}
void
@@ -233,7 +234,9 @@ SessionManager::callback_pending_builds() {
if (m_callback_scheduled_process_pending_builds.exchange(true))
return;
torrent::main_thread::callback(this, [this]() { process_pending_builds(false); });
torrent::main_thread::callback(m_callback_id, [this]() {
process_pending_builds(false);
});
}
void
@@ -247,7 +250,9 @@ SessionManager::callback_save_request() {
if (m_callback_scheduled_process_saves_request.exchange(true))
return;
session_thread::callback(this, [this]() { process_save_request(); });
session_thread::callback(m_callback_id, [this]() {
process_save_request();
});
}
void
@@ -255,7 +260,9 @@ SessionManager::callback_finished_saves() {
if (m_callback_scheduled_process_finished_saves.exchange(true))
return;
session_thread::callback(this, [this]() { process_finished_saves(); });
session_thread::callback(m_callback_id, [this]() {
process_finished_saves();
});
}
void
+23 -21
View File
@@ -89,37 +89,39 @@ private:
bool replace_save_request_unsafe(SaveRequest& download);
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{};
std::string m_path;
bool m_use_fsyncdisk{true};
bool m_use_lock{true};
torrent::system::Thread* m_thread;
torrent::system::callback_id m_callback_id;
std::mutex m_mutex;
bool m_active{};
bool m_freeze_info{};
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;
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;
bool m_active{};
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::deque<SaveRequest> m_save_requests;
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::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::chrono::microseconds m_last_storage_error_message{};
unsigned int m_ignored_storage_error_count{};
std::chrono::microseconds m_last_storage_error_message{};
unsigned int m_ignored_storage_error_count{};
// Pending builds are only ever locked by main thread.
std::mutex m_pending_builds_mutex;
std::deque<core::Download*> m_pending_builds;
std::mutex m_pending_builds_mutex;
std::deque<core::Download*> m_pending_builds;
};
inline bool SessionManager::is_used() const { return !m_path.empty(); }
+8 -12
View File
@@ -4,15 +4,11 @@
#include <torrent/exceptions.h>
#include "globals.h"
#include "session/session_manager.h"
namespace session {
class ThreadSessionInternal {
public:
static ThreadSession* thread_session() { return ThreadSession::internal_thread_session(); }
};
ThreadSession* ThreadSession::m_thread_session{nullptr};
void
@@ -74,14 +70,14 @@ ThreadSession::next_timeout() {
namespace session_thread {
torrent::system::Thread* thread() { return session::ThreadSessionInternal::thread_session(); }
std::thread::id thread_id() { return session::ThreadSessionInternal::thread_session()->thread_id(); }
torrent::system::Thread* thread() { return session::ThreadSession::thread_session(); }
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 cancel_callback(void* target) { session::ThreadSessionInternal::thread_session()->cancel_callback(target); }
void cancel_callback_and_wait(void* target) { session::ThreadSessionInternal::thread_session()->cancel_callback_and_wait(target); }
void callback(std::function<void()>&& fn) { thread()->callback(std::move(fn)); }
void callback(torrent::system::callback_id& id, std::function<void()>&& fn) { thread()->callback(id, std::move(fn)); }
void cancel_callback(torrent::system::callback_id& id) { thread()->cancel_callback(id); }
session::SessionManager* manager() { return session::ThreadSessionInternal::thread_session()->manager(); }
std::string session_path() { return session::ThreadSessionInternal::thread_session()->manager()->path(); }
session::SessionManager* manager() { return session::ThreadSession::thread_session()->manager(); }
std::string session_path() { return manager()->path(); }
} // namespace session_thread
+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.");
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = &SignalHandler::caught;
@@ -52,6 +53,46 @@ SignalHandler::set_handler(unsigned int signum, slot_void 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
SignalHandler::set_sigaction_handler(unsigned int signum, handler_slot slot) {
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
#define RTORRENT_SIGNAL_HANDLER_H
@@ -56,6 +22,11 @@ public:
static void set_ignore(unsigned int signum);
static void set_handler(unsigned int signum, slot_void slot);
static void set_block(unsigned int signum);
static void set_unblock(unsigned int signum);
static void set_sigchild_ignore();
static void set_sigaction_handler(unsigned int signum, handler_slot slot);
static const char* as_string(unsigned int signum);
+5 -9
View File
@@ -3,8 +3,6 @@
#include <cassert>
#include <torrent/exceptions.h>
#include <torrent/chunk_manager.h>
#include <torrent/connection_manager.h>
#include <torrent/throttle.h>
#include <torrent/torrent.h>
#include <torrent/data/file_list.h>
@@ -32,10 +30,10 @@
namespace ui {
Download::Download(core::Download* d) :
m_download(d) {
Download::Download(core::Download* d)
: m_download(d) {
m_windowDownloadStatus = new WDownloadStatus(d);
m_windowDownloadStatus = std::make_unique<WDownloadStatus>(d);
m_windowDownloadStatus->set_bottom(true);
m_uiArray[DISPLAY_MENU] = create_menu();
@@ -61,8 +59,6 @@ Download::~Download() {
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; });
delete m_windowDownloadStatus;
}
inline ElementBase*
@@ -172,7 +168,7 @@ Download::activate(display::Frame* frame, [[maybe_unused]] bool focus) {
m_frame = frame;
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);
activate_display_menu(DISPLAY_PEER_LIST);
@@ -250,7 +246,7 @@ Download::activate_display(Display displayType, bool focusDisplay) {
// Set title.
switch (displayType) {
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();
+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
#define RTORRENT_UI_DOWNLOAD_H
@@ -113,7 +79,7 @@ private:
bool m_focusDisplay{};
WDownloadStatus* m_windowDownloadStatus;
std::unique_ptr<WDownloadStatus> m_windowDownloadStatus;
};
}
+2 -1
View File
@@ -6,6 +6,7 @@
#include <sstream>
#include <torrent/exceptions.h>
#include <torrent/torrent.h>
#include <torrent/runtime/runtime.h>
#include <torrent/utils/log.h>
#include <torrent/utils/string_manip.h>
@@ -176,7 +177,7 @@ DownloadList::activate_display(Display displayType) {
switch (displayType) {
case DISPLAY_DOWNLOAD_LIST:
control->ui()->window_title()->set_title("rTorrent " PACKAGE_VERSION "/" +
std::string(torrent::version()) + " - " +
std::string(torrent::runtime::version()) + " - " +
rpc::call_command_string("session.name"));
break;
case DISPLAY_LOG:
+12 -4
View File
@@ -2,6 +2,7 @@
#include <torrent/exceptions.h>
#include <torrent/torrent.h>
#include <torrent/system/callbacks.h>
#include <torrent/system/thread.h>
#include "display/frame.h"
@@ -17,11 +18,16 @@ namespace ui {
ElementLogComplete::ElementLogComplete(torrent::log_buffer* 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::thread()->send_event_signal(signal_index, false);
});
torrent::main_thread::callback([this]() { received_update(); });
});
}
ElementLogComplete::~ElementLogComplete() {
m_log->lock_and_set_update_slot(nullptr);
}
void
@@ -59,6 +65,8 @@ ElementLogComplete::window() {
void
ElementLogComplete::received_update() {
m_log_updating = false;
if (m_window != NULL)
m_window->mark_dirty();
}
+8 -3
View File
@@ -15,12 +15,13 @@ namespace ui {
class ElementLogComplete : public ElementBase {
public:
typedef display::WindowLogComplete WLogComplete;
using WLogComplete = display::WindowLogComplete;
ElementLogComplete(torrent::log_buffer* l);
~ElementLogComplete() override;
void activate(display::Frame* frame, bool focus = true);
void disable();
void activate(display::Frame* frame, bool focus = true) override;
void disable() override;
display::Window* window();
@@ -30,6 +31,10 @@ private:
WLogComplete* m_window{};
torrent::log_buffer* m_log;
align_cacheline
std::atomic<bool> m_log_updating{};
};
}
+1 -1
View File
@@ -111,7 +111,7 @@ ElementPeerList::create_info() {
element->push_column("Client:", te_command("p.client_version="));
element->push_column("Options:", te_command("p.options_str="));
element->push_column("Connected:", te_command("if=$p.is_incoming=,incoming,outgoing"));
element->push_column("Encrypted:", te_command("if=$p.is_encrypted=,yes,$p.is_obfuscated=,handshake,no"));
element->push_column("Encrypted:", te_command("if=$p.is_encrypted=,yes,$if=$p.is_obfuscated=\\,handshake\\,no"));
element->push_back("");
element->push_column("Snubbed:", te_command("if=$p.is_snubbed=,yes,no"));
+1
View File
@@ -70,4 +70,5 @@ decode_base64(const std::string& input) {
}
return decodedBytes;
}
} // namespace utils
+3 -1
View File
@@ -1,12 +1,14 @@
#ifndef RTORRENT_UTILS_BASE64_H
#define RTORRENT_UTILS_BASE64_H
#include <optional>
#include <string>
#include <vector>
#include <torrent/exceptions.h>
namespace utils {
// TODO: Deprecate.
std::string remove_newlines(const std::string& str);
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()));
// Return false if the file hasn't been modified since last time. We
// use 'equal to' instead of 'greater than' since the file might
// have been replaced by another file, and thus should be re-tried.
if (!result.second && result.first->second.m_mtime == (uint32_t)fs.modified_time())
// Return false if the file hasn't changed since last time. We use
// 'equal to' instead of 'greater than' since the file might have
// been replaced by another file, and thus should be re-tried.
if (!result.second &&
result.first->second.m_mtime == (uint32_t)fs.modified_time() &&
result.first->second.m_size == (int64_t)fs.size())
return false;
result.first->second.m_flags = 0;
result.first->second.m_size = (int64_t)fs.size();
result.first->second.m_mtime = fs.modified_time();
return true;
@@ -41,7 +44,9 @@ FileStatusCache::prune() {
torrent::utils::FileStat fs;
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);
}
}
+4 -3
View File
@@ -9,6 +9,7 @@ namespace utils {
struct file_status {
int m_flags;
int64_t m_size;
uint32_t m_mtime;
};
@@ -32,14 +33,14 @@ public:
using base_type::erase;
// Insert and return true if the entry does not exist or the new
// file's mtime is more recent.
// Insert and return true if the entry does not exist or the file's
// status has changed.
bool insert(const std::string& path);
// Add a function for pruning a sorted list of paths.
// Function for pruning entries that no longer points to a file, or
// has a different mtime.
// has different status.
void prune();
};
+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) \
src/test_command_dynamic.cc \
src/test_command_dynamic.h
src/test_command_dynamic.h \
src/test_encryption_config.cc \
src/test_encryption_config.h \
src/test_watch_ready_queue.cc \
src/test_watch_ready_queue.h
rtorrent_Test_Rpc_CXXFLAGS = $(CPPUNIT_CFLAGS)
rtorrent_Test_Rpc_LDFLAGS = $(CPPUNIT_LIBS) -ldl
+7 -13
View File
@@ -17,6 +17,7 @@
#define MOCK_LOG(log_fmt, ...) \
lt_log_print(torrent::LOG_MOCK_CALLS, "%s: " log_fmt, __func__, __VA_ARGS__);
namespace {
void
mock_clear(bool ignore_assert) {
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__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_uint32);
mock_compare_map<torrent::Event>::values.clear();
};
}
} // namespace
void
mock_init() {
@@ -59,6 +49,10 @@ mock_cleanup() {
void
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__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)); }));
+1 -1
View File
@@ -6,7 +6,7 @@
#include "test/helpers/mock_function.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_long_timeout;
+10 -11
View File
@@ -4,7 +4,7 @@
#include <array>
#include <torrent/exceptions.h>
#include <torrent/net/network_config.h>
#include <torrent/runtime/network_config.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
@@ -170,19 +170,18 @@ TestParseOptions::test_flag_libtorrent() {
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_IP_TOS_ASSERT(flags, result) \
CPPUNIT_ASSERT(rpc::parse_option_flags(flags, std::bind(&torrent::option_find_string_str, torrent::OPTION_IP_TOS, 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)))
#define FLAGS_LT_IP_TOS_ASSERT_ERROR(flags) \
ASSERT_CATCH_INPUT_ERROR(rpc::parse_option_flags(flags, std::bind(&torrent::option_find_string_str, torrent::OPTION_IP_TOS, 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_IP_TOS_ASSERT("throughput", torrent::option_find_string(torrent::OPTION_IP_TOS, "throughput"));
FLAGS_LT_IP_TOS_ASSERT("lowdelay | throughput",
torrent::option_find_string(torrent::OPTION_IP_TOS, "lowdelay")
| torrent::option_find_string(torrent::OPTION_IP_TOS, "throughput"));
FLAGS_LT_ENCRYPTION_ASSERT_ERROR("require_");
FLAGS_LT_IP_TOS_ASSERT_ERROR("throughput_");
}
+45
View File
@@ -0,0 +1,45 @@
#include "config.h"
#include "test/src/test_encryption_config.h"
#include <torrent/runtime/encryption_policy.h>
#include "encryption_config.h"
CPPUNIT_TEST_SUITE_REGISTRATION(TestEncryptionConfig);
void
TestEncryptionConfig::test_default_policy() {
const auto policy = encryption_config::default_policy();
CPPUNIT_ASSERT(policy.handshake == encryption_config::Policy::Mode::allow);
CPPUNIT_ASSERT(policy.stream == encryption_config::Policy::Mode::allow);
CPPUNIT_ASSERT_EQUAL(std::string("handshake=allow stream=allow"),
encryption_config::summary_string(policy));
}
void
TestEncryptionConfig::test_granular_round_trip() {
auto round_trip_handshake = [](const std::string& value) {
encryption_config::Policy policy;
encryption_config::apply_mode_value(policy, &encryption_config::Policy::handshake, value);
return encryption_config::mode_to_string(policy.handshake);
};
CPPUNIT_ASSERT_EQUAL(std::string("deny"), round_trip_handshake("deny"));
CPPUNIT_ASSERT_EQUAL(std::string("allow"), round_trip_handshake("allow"));
CPPUNIT_ASSERT_EQUAL(std::string("prefer"), round_trip_handshake("prefer"));
CPPUNIT_ASSERT_EQUAL(std::string("require"), round_trip_handshake("require"));
auto round_trip_stream = [](const std::string& value) {
encryption_config::Policy policy;
encryption_config::apply_mode_value(policy, &encryption_config::Policy::stream, value);
return encryption_config::mode_to_string(policy.stream);
};
CPPUNIT_ASSERT_EQUAL(std::string("deny"), round_trip_stream("deny"));
CPPUNIT_ASSERT_EQUAL(std::string("allow"), round_trip_stream("allow"));
CPPUNIT_ASSERT_EQUAL(std::string("prefer"), round_trip_stream("prefer"));
CPPUNIT_ASSERT_EQUAL(std::string("require"), round_trip_stream("require"));
}

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