Compare commits

...

194 Commits

Author SHA1 Message Date
rakshasa e56bffff4d Fixed socket category unit test. 2026-08-05 11:18:31 +02:00
xirvik d20c3234da Add system.sockets allocation budget commands.
They report the budget the socket manager enforces, so a client can show the limit.
2026-08-05 11:18:31 +02:00
Maks Verver dda5e4fa04 Fix deprecated command redirects (rtorrent -D) 2026-08-05 10:14:35 +02:00
Jari Sundell 9ab73352e1 Minor fixes to WaitpidQueue. 2026-08-05 00:59:33 +09:00
Jari Sundell f90a96d57a Added WaitpidQueue to handle background process reaping. 2026-08-04 19:03:26 +09:00
rakshasa 247ae7621a Tagged release 0.16.19. 2026-08-02 10:44:30 +02:00
xirvik 05831942a7 commands: don't register min_alloc/max_alloc for the generic socket category
SocketManager throws internal_error for category_generic, aborting rtorrent over RPC.
2026-07-30 12:42:24 +02:00
rakshasa ee18a1f3fc Added commit message linter. 2026-07-28 10:04:51 +02:00
Jari Sundell f52f20f1b6 Cleaned up system headers. 2026-07-26 19:53:18 +09:00
xirvik 41eef969ea commands: fix group.<name>.ratio.disable calling a removed command
group_insert() builds the disable method as "schedule_remove=...", but that
command no longer exists: the current name is schedule.remove, and the only
old-style aliases registered are schedule2 and schedule_remove2 (and those only
when deprecated commands are enabled at registration time). Every
group.<name>.ratio.disable therefore faults with

  Command "schedule_remove" does not exist.

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

  Unrecognized CPU architecture (riscv64) on Linux fallback path.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Fixes the use-after-free; preserves the std::string-based storage
the original commit aimed for.
2026-06-05 10:55:51 +02:00
Jari Sundell 44d39713d5 Clean out old cruft in autoconf scripts. 2026-06-05 00:46:47 +09:00
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
rakshasa b5a606649a Tagged release 0.16.10. 2026-04-23 10:31:51 +02:00
Jari Sundell eb96876eb2 Moved Thread header to torrent/system directory. 2026-04-22 23:02:05 +09:00
Jari Sundell a964b0a350 Added special syntax to config files to enable log categories early. 2026-04-20 01:07:38 +09:00
Jari Sundell 1adcf230d2 Fix core subsystem logic and safety bugs @Sirus20x6 2026-04-18 01:54:46 +09:00
rakshasa 4235507292 Fixed path expand when doing a single '~' when expanding directory entries. 2026-04-17 12:13:27 +02:00
Jari Sundell b7a21b1e9d Added support for gzip accepted_encoding in scgi headers. 2026-04-12 22:19:49 +09:00
Jari Sundell bd53959b40 Fixed session save getting stuck. 2026-04-11 16:50:58 +09:00
Jari Sundell 5a4ba8bc32 Removed deprecated rak header files. 2026-04-10 22:04:02 +09:00
Jari Sundell e64ec358fa Removed deprecated rak headers. 2026-04-10 03:49:38 +09:00
rakshasa b78fd9aef5 Tagged release 0.16.9. 2026-04-06 15:19:27 +02:00
rakshasa 21e399d099 Remove unneeded MSG_NOSIGNAL. 2026-04-06 10:48:35 +02:00
Xirvik 08a907b547 Whitelist additional read-only getters for untrusted connections
ruTorrent queries these commands for its settings and status pages.
They are all read-only getters with no side effects, safe to expose
for untrusted SCGI connections.

Tested against ruTorrent with both httprpc and multirpc plugins on
servers with active torrents — all modes (list, settings, totals,
open connections) work with zero blocked commands.
2026-03-30 11:55:24 +02:00
zqmfb a0b8702895 Address additional review feedback 2026-03-27 17:48:13 +01:00
zqmfb ad3c31862e Further clarify systemd socket selection 2026-03-27 17:48:13 +01:00
zqmfb 2711e4c044 Use libtorrent's fd_set_nonblock helper 2026-03-27 17:48:13 +01:00
zqmfb 8d50aee96f Add network.scgi.open_systemd command unconditionally 2026-03-27 17:48:13 +01:00
zqmfb 71bb51c3fd Clarify systemd socket selection logic 2026-03-27 17:48:13 +01:00
zqmfb 63ea08bde6 Add support for SCGI systemd socket activation
Add a new command, `network.scgi.open_systemd`, that binds to a file
descriptor passed in via systemd socket activation.
2026-03-27 17:48:13 +01:00
Xirvik Support a82bdf22ac Review fix: remove method.use_deprecated.set from untrusted whitelist 2026-03-23 15:11:07 +01:00
Xirvik 9f6731b4e2 Address review: tighten untrusted safelist and remove set_trusted 2026-03-23 15:11:07 +01:00
Xirvik d935e0ffe9 Address review feedback: explicit mark_safe whitelist and rpc trust flow 2026-03-23 15:11:07 +01:00
Xirvik ba239bc8c5 Address code review: fix setter exposure and narrow catch blocks
1. network.rpc.use_xmlrpc and network.rpc.use_jsonrpc: change from
   CMD2_VAR_BOOL_U (getter+setter both safe) to CMD2_VAR_BOOL_U_GET
   (getter safe, setter trusted-only). Untrusted callers could
   previously disable RPC transports entirely.

2. Remove broad catch(std::exception&) and catch(...) from xmlrpc_c.cc
   that masked real defects and altered fault semantics.

3. Revert SCGI callback catch-all to re-throw instead of swallowing
   exceptions with a generic error response.
2026-03-23 15:11:07 +01:00
Xirvik ea16276773 Fix crash on untrusted XMLRPC connections
Root cause: network.rpc.use_xmlrpc and network.rpc.use_jsonrpc were not
marked as untrusted-safe, but RpcManager::process() calls them before
dispatching to the protocol handler. When an untrusted request arrived,
call_command() threw untrusted_error for these gatekeepers, which escaped
the callback_interrupt_pollling callback and crashed rtorrent.

Fix: Mark network.rpc.use_xmlrpc/jsonrpc as safe (CMD2_VAR_BOOL_U).

Also harden exception safety:
- SCGI callback catch-all now sends a generic error response instead of
  re-throwing, since the callback infrastructure may not support
  exception propagation.
- xmlrpc_c.cc now has catch(std::exception&) and catch(...) safety nets
  after the specific exception handlers.
2026-03-23 15:11:07 +01:00
Xirvik f767053297 Mark safe commands with flag_untrusted_safe for whitelist enforcement
Annotate all commands that web UIs (ruTorrent) need for normal torrent
management with _U macro variants, which set flag_untrusted_safe.
Commands not marked are blocked by default for untrusted connections.

Safe commands include:
- d.* download getters, state, priorities, custom fields, start/stop
- f.* file getters, priority control
- p.* peer getters, disconnect, ban/snub
- t.* tracker getters, enable/disable
- throttle.* rate getters/setters, peer limits
- network.* read-only queries (getters safe, setters blocked)
- view.list, view.size, view.filter_all, ui.current_view
- load.*, download_list, d.multicall2, d.multicall.filtered
- convert.*, branch/if/and/or/not/cat/value/print
- system.* version/time/status queries (read-only)
- choke_group.* read-only queries
- method.has_key, method.const, method.list_keys, method.get, strings.*
- group.*.view, group.*.ratio.min/max/upload (dynamic, via flag propagation)

Blocked by default (not marked):
- execute*, method.insert/set/redirect, schedule*, import
- log.*, file.append, network.scgi.open_*, view.filter/sort/event_*
- system.shutdown, system.env, group.insert, choke_group.insert
- All user-defined commands (via method.insert)
2026-03-23 15:11:07 +01:00
Xirvik 598914908f Add untrusted connection security infrastructure (v3)
Replace the v2 blacklist approach with a per-command flag system.
Commands must opt in to being available for untrusted connections
via flag_untrusted_safe (0x400), checked in call_command() which
catches all execution paths including nested commands.

Infrastructure changes:
- Add flag_untrusted_safe to CommandMap
- Add untrusted_error exception type for proper error codes
- Enforce trust check in both call_command() overloads
- Add catch blocks in xmlrpc_c, xmlrpc_tinyxml2, and jsonrpc handlers
- Port SCGI trust state management from v2 (thread_local, header parsing)
- Add _U macro variants in command_helpers.h for safe command registration
- Add CMD2_VAR_*_U and CMD2_VAR_*_U_GET variants for variables
2026-03-23 15:11:07 +01:00
rakshasa 38fc815d52 Fix display/UI crash and correctness bugs (@sirus20x6) 2026-03-16 16:08:52 +01:00
Jari Sundell 674ae767aa Validate parsed int pair arguments for positivity 2026-03-16 13:56:43 +01:00
sirus20x6 0aaa470053 Fix resource leaks and minor issues
- Close pipe fds on fork failure in ExecFile::execute
- Add exception-safe fclose in cmd_file_append via try/catch
- Add overflow guards before K/M/G bit shifts in parse_whole_value
- Fix %u format for int* in sscanf (change to %d)
- Fix typo "atter"→"after" in error message
2026-03-16 13:56:43 +01:00
PiloUnk 6f27159627 Refactor full save scheduling with early return 2026-03-16 13:29:33 +01:00
PiloUnk de163293ab Avoid missing save scheduling after full save update 2026-03-16 13:29:33 +01:00
PiloUnk fb4e775689 Fix coalescing resume saves with pending full save 2026-03-16 13:29:33 +01:00
rakshasa 39f186e523 Tagged release 0.16.8. 2026-03-15 15:43:32 +01:00
Jari Sundell 70e6964823 Fixed various SCGI issues. 2026-03-10 23:25:28 +09:00
rakshasa 7ead88448b Removed rak/error_number.h from Makefile.am. 2026-03-04 11:39:25 +01:00
rakshasa 650f0299b5 Tagged release 0.16.7. 2026-03-04 10:47:30 +01:00
rakshasa 5dfb2ae938 Allow dht bootstrap nodes to be added when dht is off. 2026-03-02 09:45:09 +01:00
Jorge Israel Peña f05a2ae520 Re-send smkx on SIGWINCH to fix arrow keys after terminal reattach 2026-02-19 16:06:42 +01:00
Miroslav Marchev ecefdba734 dht_add_peer_node is empty, use dht_add_bootstrap_node instead
After a refactor dht_add_peer_node became empty function. Replace with dht_add_bootstrap_node to make adding bootstrap nodes work.
2026-02-14 14:32:48 +01:00
Jari Sundell 87666199b3 Added SocketManager to handle reuse of uninterested fd's by the kernel. 2026-01-29 04:00:31 +09:00
Jari Sundell 9489793dcb Created torrent/runtime include directory. 2026-01-27 08:21:05 +09:00
Jari Sundell 2fa7568165 Remove obsolete SocketFd class. 2026-01-26 19:13:03 +09:00
fffe f4b718f685 add separate commands for unbuffered logs 2026-01-16 16:10:02 +01:00
Jari Sundell b233e24465 Deprecated rak::path_expand. 2026-01-08 23:27:53 +09:00
Jari Sundell 289ab046bf Expand '~/' to $HOME in session path. 2026-01-06 20:44:40 +09:00
Jari Sundell a8b6a47054 Removed deprecated rak errno and file headers. 2026-01-04 03:22:02 +09:00
Zoltan Celedes 110591d5c1 Fix key/value pairs in Lua
Previously, the keys and values were swapped in d.custom.items()
2026-01-03 12:20:08 +01:00
rakshasa ae14baa357 Tagged release 0.16.6. 2026-01-02 15:22:29 +01:00
rakshasa 0b0c824b4b Changed magnet metadata handling and added 'magnet.path.set'. 2025-12-31 21:57:52 +01:00
Jari Sundell def6551488 Properly propagate errors from download session save. 2025-12-30 22:00:34 +09:00
Jari Sundell 787738e36a Make sure pending builds of session resume get processed. 2025-12-29 09:55:32 +09:00
Jari Sundell 40c2d90c45 Fixed dereferencing of potentially nullptr in SessionManager. 2025-12-25 23:05:08 +09:00
Jari Sundell 4a37fbde0b Session saving of resume data is added to a pre-queue. 2025-12-23 03:04:47 +09:00
Jari Sundell 4bdeb58eb6 Run multiple session save requests in parallel. 2025-12-22 06:26:35 +09:00
Jari Sundell 5dbb0020dc Replace ThreadWorker with scgi::ThreadScgi. 2025-12-18 07:43:43 +09:00
Jari Sundell 8f644e65dd Use separate thread for saving session data. 2025-12-18 06:42:44 +09:00
Jari Sundell 16ff32b88c Added missing Event::type_name() functions. 2025-12-13 19:06:02 +09:00
Jari Sundell 8806c06f9f Added timestamp helper commands. 2025-12-10 22:29:42 +09:00
rakshasa 60cfcd37c4 Release 0.16.5. 2025-12-02 17:51:26 +01:00
rakshasa a526ba58e9 Release 0.16.4. 2025-11-25 10:48:11 +01:00
rakshasa efd9507149 Release 0.16.3. 2025-11-21 14:39:27 +01:00
Jari Sundell 3d91dfdb33 Valgrind suppressions file for macos. 2025-11-20 15:57:30 +09:00
Jari Sundell 30cec98799 Moved Poll to net namespace. 2025-11-20 03:05:16 +09:00
rakshasa b825906034 Store copies of command names added to xmlrpc-c. 2025-11-17 20:30:13 +01:00
rakshasa b05ecb5c5a Push back views explicitly takes a string. 2025-11-17 09:36:32 +01:00
Jari Sundell 900be334dc Cleaned up xmlrpc-c string sanitization. 2025-11-17 02:48:32 +09:00
rakshasa 3a1da2fe34 Check if AF_INET6 is supported, or block IPv6 traffic. 2025-11-15 09:17:56 +01:00
Jari Sundell 70750a2bd3 Cleanup of DHT controller. 2025-11-13 00:01:22 +09:00
Jari Sundell 1d2c424a70 Remove throttle from DHT. 2025-11-08 17:51:00 +09:00
rakshasa 8550facf43 Tagged release 0.16.2. 2025-11-04 15:20:20 +01:00
rakshasa 184ead294a Export 'group2.*' commands. 2025-11-01 14:35:32 +01:00
Jari Sundell 84bfc491ba Added dual listening ports when both IPv4 and IPv6 are bound. 2025-11-01 07:24:31 +09:00
Khem Raj a2e0eca6e3 scripts/common.m4: Insert spaces in shell lists
$1=$(echo "$result" | tr -d '\n')

removes all newlines without inserting spaces
That usually isn’t what we want for shell lists.
It should typically be space-separated output.

Fixes a bug seen with yocto where compiler is not a single word
but a string e.g.

ccache aarch64-yoe-linux-musl-clang++  -mcpu=cortex-a72+crc+nocrypto   --dyld-prefix=/usr -fstack-protector-strong  -O2 -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/mnt/b/yoe/master/build/tmp/work/cortexa72-yoe-linux-musl/libtorrent/0.16.1/recipe-sysroot

It changes it to

ccacheaarch64-yoe-linux-musl-clang++-mcpu=cortex-a72+crc+nocrypto--dyld-prefix=/usr-fstack-protector-strong-O2-D_FORTIFY_SOURCE=2-Wformat-Wformat-security-Werror=format-security--sysroot=/mnt/b/yoe/master/build/tmp/work/cortexa72-yoe-linux-musl/libtorrent/0.16.1/recipe-sysroot

When doing c++17 checks on compiler, resulting in failure

Upstream-Status: Submitted [https://github.com/rakshasa/libtorrent/pull/583]
Signed-off-by: Khem Raj <raj.khem@gmail.com>
2025-10-25 11:30:56 +02:00
rakshasa 92eecdfd52 Updated log group documentation. 2025-10-24 15:38:01 +02:00
Jari Sundell 14219666c5 Fix syntax for appending to log files 2025-10-24 14:08:23 +02:00
Aden a79a844dac Fix typo in 'receiving' 2025-10-24 14:08:23 +02:00
Aden 59b6a013ce Document the log.append_file configuration key
This flag is extremely useful but I couldn't find any documentation
referencing it. I only found it once I started having a poke through the
source code, so hopefully this will save others a little bit of time :)
2025-10-24 14:08:23 +02:00
rakshasa b38f80e597 Release 0.16.1. 2025-10-16 10:54:05 +02:00
Auska 2d2a756a3c fix: replace VLA
Avoids -Wvla-cxx-extension warning by using standard C++ container.
VLA is not part of standard C++ and causes portability issues.
2025-10-14 11:16:35 +02:00
Jari Sundell 891a6ba69d Added local address commands for inet/inet6. 2025-10-13 19:09:20 +09:00
Jari Sundell 2be730e4b3 Reuse HTTP connections and added totale/cached connection limits. 2025-10-09 21:15:24 +09:00
Jari Sundell bf53700f97 Added 'network.bind_address.ipv{4,6}.set' commands. 2025-10-04 20:24:08 +09:00
Jari Sundell 946dc54f9b Fix libtorrent links in README.md
Updated links to point to the GitHub repository for libtorrent.
2025-10-02 16:31:53 +09:00
Jari Sundell ea2347734b Added network.block.outgoing. 2025-09-23 17:56:51 +09:00
Jari Sundell 5206e6cb88 Moved various socket options to NetworkConfig. 2025-09-22 06:01:18 +09:00
Jari Sundell 48f82c17c2 Remove deprecated no-target flags for commands. 2025-09-20 17:27:26 +09:00
rakshasa 675625255d Check if target is null in xmlrpc_to_object(). 2025-09-20 09:22:56 +02:00
Jari Sundell 658e9f7578 Use simplified fd_accept(). 2025-09-18 17:40:56 +09:00
Jari Sundell e7a80c7b18 Remove deprecated rak::socket_address. 2025-09-17 21:44:54 +09:00
Jari Sundell 677f8f45c8 Improved listen/dht port handling and added 'dht.override_port.set' command. 2025-09-13 17:51:04 +09:00
Jari Sundell ba1ba3096a Added NetworkConfig. 2025-09-12 06:09:24 +09:00
181 changed files with 7514 additions and 6957 deletions
+65
View File
@@ -0,0 +1,65 @@
name: "Lint Commit Message Size"
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
check-commit-bounds:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Validate Line Count, Width, and Spacing
run: |
# Fetch commit hashes unique to this PR branch
COMMITS=$(git log --no-merges --pretty=format:"%H" origin/${{ github.base_ref }}..HEAD)
MAX_LINES=3
MAX_CHARS=90
FAILED=0
for commit in $COMMITS; do
SUBJECT=$(git log --format="%s" -n 1 $commit)
# Extract clean commit message, trimming trailing blank lines
RAW_MSG=$(git log --format="%B" -n 1 $commit)
CLEAN_MSG=$(echo "$RAW_MSG" | awk '{msg[NR]=$0} END {while(NR>0 && msg[NR]=="") NR--; for(i=1;i<=NR;i++) print msg[i]}')
# 1. Check total line count
LINE_COUNT=$(echo "$CLEAN_MSG" | wc -l)
if [ "$LINE_COUNT" -gt "$MAX_LINES" ]; then
echo "❌ Error: Commit message has too many lines ($LINE_COUNT/$MAX_LINES)."
echo " Commit: '$SUBJECT'"
FAILED=1
fi
# 2. Check for multiple consecutive blank lines
# This regex looks for 2 or more empty lines anywhere in the message
if echo "$CLEAN_MSG" | grep -pz '(\r?\n){3,}'; then
echo "❌ Error: Commit message contains multiple consecutive line breaks."
echo " Commit: '$SUBJECT'"
FAILED=1
fi
# 3. Check maximum width of any individual line
while IFS= read -r line; do
LINE_LENGTH=${#line}
if [ "$LINE_LENGTH" -gt "$MAX_CHARS" ]; then
echo "❌ Error: Line length exceeds limit ($LINE_LENGTH / $MAX_CHARS chars)."
echo " Offending line: '$line'"
FAILED=1
fi
done <<< "$CLEAN_MSG"
done
# Fail the job if any check failed
if [ "$FAILED" -ne 0 ]; then
exit 1
fi
echo "✅ All commit messages passed style rules!"
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
permissions:
pull-requests: write
# OPTIONAL: auto-closing conversations requires the `contents` permission
contents: write
contents: read
steps:
- name: Sleep for 30 seconds
run: sleep 30s
+2 -1
View File
@@ -16,7 +16,8 @@ jobs:
sudo apt-get install -y \
bear \
clang-tidy \
libcurl4-openssl-dev
libcurl4-openssl-dev \
zlib1g-dev
- name: Fetch libtorrent
run: |
git clone https://github.com/rakshasa/libtorrent
+70 -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: |
@@ -15,6 +14,7 @@ jobs:
run: |
sudo apt-get install -y \
libcppunit-dev \
zlib1g-dev \
libcurl4-openssl-dev
- name: Fetch libtorrent
run: |
@@ -22,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
-10
View File
@@ -7,16 +7,6 @@ nobase_dist_pkgdata_DATA = \
lua/rtorrent.lua
EXTRA_DIST= \
rak/address_info.h \
rak/algorithm.h \
rak/error_number.h \
rak/file_stat.h \
rak/path.h \
rak/partial_queue.h \
rak/regex.h \
rak/socket_address.h \
rak/string_manip.h \
rak/unordered_vector.h \
scripts/checks.m4 \
scripts/common.m4 \
scripts/attributes.m4
+2 -2
View File
@@ -41,7 +41,7 @@ cd rtorrent
## Install build dependencies
Install [libtorrent](https://libtorrent.org/) with the same version rTorrent.
Install [libtorrent](https://github.com/rakshasa/libtorrent) with the same version rTorrent.
Generate configure scripts:
@@ -57,7 +57,7 @@ docbook2man rtorrent.1.xml
Man pages output to "doc/rtorrent.1".
RTorrent follows the development of [libtorrent](https://libtorrent.org/) closely, and thus the versions must be in sync.
RTorrent follows the development of [libtorrent](https://github.com/rakshasa/libtorrent) closely, and thus the versions must be in sync.
## USAGE
+24 -20
View File
@@ -1,6 +1,6 @@
m4_pattern_allow([PKG_CHECK_EXISTS])
AC_INIT([rtorrent],[0.16.0],[sundell.software@gmail.com])
AC_INIT([rtorrent],[0.16.19],[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], [16], [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], [24], [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,35 +34,46 @@ 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([DEPENDENCIES], [libtorrent >= 0.16.0])
PKG_CHECK_MODULES([ZLIB], [zlib])
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.19])
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
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.
LIBS="$PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $DEPENDENCIES_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
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
+14 -4
View File
@@ -4,20 +4,30 @@
# log.open_file = "log name", "file path"
log.open_file = "rtorrent.log", (cat,/tmp/rtorrent.log.,(system.pid))
log.open_file = "rtorrent.log", (cat,/tmp/rtorrent.log.,(system.pid))
A newly opened log file is not connected to any logging events.
Some control over formatting will be provided at a later date.
## Appending to log files
log.append_file = "rtorrent.log", "/tmp/rtorrent.log"
The `log.open_file` clears any existing contents of the file. If you'd
prefer to have a single log file that persists across application
restarts, you can use the `log.append_file` in place of the
`log.open_file` configuration key.
## Adding outputs to events
# log.add_output = "logging event", "log name"
log.add_output = "info", "rtorrent.log"
log.add_output = "dht_debug", "tracker.log"
log.add_output = "tracker_debug", "tracker.log"
log.add_output = "dht_all", "tracker.log"
log.add_output = "tracker_events", "tracker.log"
log.add_output = "tracker_requests", "tracker.log"
Each log handle can be added to multiple different logging events.
@@ -31,7 +41,7 @@ Each log handle can be added to multiple different logging events.
"debug"
The above events receive logging events from all the sub-groups
displayed below, and each event also reciving events from the event
displayed below, and each event also receiving events from the event
above in importance.
Thus some high-volume sub-group events such as “tracker\_debug” are not
+4 -3
View File
@@ -5,7 +5,7 @@
\begin{verbatim}
# log.open_file = "log name", "file path"
log.open_file = "rtorrent.log", (cat,/tmp/rtorrent.log.,(system.pid))
log.open_file = "rtorrent.log", (cat,/tmp/rtorrent.log.,(system.pid))
\end{verbatim}
A newly opened log file is not connected to any logging events.
@@ -20,8 +20,9 @@ Some control over formatting will be provided at a later date.
log.add_output = "info", "rtorrent.log"
log.add_output = "dht_debug", "tracker.log"
log.add_output = "tracker_debug", "tracker.log"
log.add_output = "dht_all", "tracker.log"
log.add_output = "tracker_events", "tracker.log"
log.add_output = "tracker_requests", "tracker.log"
\end{verbatim}
Each log handle can be added to multiple different logging events.
+5 -1
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.
+4 -1
View File
@@ -101,8 +101,11 @@ schedule2 = watch_load, 11, 10, ((load.verbose, (cat, (cfg.watch), "load/*.torre
# Levels = critical error warn notice info debug
# Groups = connection_* dht_* peer_* rpc_* storage_* thread_* tracker_* torrent_*
print = (cat, "Logging to ", (cfg.logfile))
log.open_file = "log", (cfg.logfile)
log.add_output = "info", "log"
##log.add_output = "tracker_debug", "log"
#log.add_output = "tracker_events", "log"
#log.add_output = "tracker_requests", "log"
### END of rtorrent.rc ###
+2 -1
View File
@@ -136,6 +136,7 @@ rc.schedule2('watch_load', '11', '10', 'load.verbose=(cat, (cfg.watch), "load/*.
rc.print('Logging to '..rc.cfg.logfile())
rc.log.open_file('log', rc.cfg.logfile())
rc.log.add_output('info', 'log')
--rc.log.add_output('tracker_debug', 'log')
--rc.log.add_output('tracker_events', 'log')
--rc.log.add_output('tracker_requests', 'log')
--[[ END of rtorrent.rc.lua ]]--
+1
View File
@@ -0,0 +1 @@
valgrind --leak-check=full --track-origins=yes --show-reachable=yes --suppressions=/Users/rakshasa/projects/rtorrent/doc/valgrind.suppression --gen-suppressions=all --log-file=valgrind.log /usr/local/bin/rtorrent
+343
View File
@@ -0,0 +1,343 @@
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:__76-*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:__76-*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:__80-*
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:__93-*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:__93-*
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:__108-*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:__108-*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:__108-*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsPlistSource setDomainIdentifier:]
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:-[CFPrefsSearchListSource alreadylocked_copyValueForKey:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsSearchListSource alreadylocked_copyValueForKey:]
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:-[CFPrefsSearchListSource alreadylocked_copyValueForKey:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsSearchListSource addNamedVolatileSourceForIdentifier:]
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:-[CFPrefsSearchListSource handleReply:toRequestNewDataMessage:onConnection:retryCount:error:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsSearchListSource addSourceForIdentifier:user:byHost:container:]
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:-[CFPrefsSearchListSource handleReply:toRequestNewDataMessage:onConnection:retryCount:error:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsSearchListSource handleReply:toRequestNewDataMessage:onConnection:retryCount:error:]
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:-[CFPrefsSearchListSource handleReply:toRequestNewDataMessage:onConnection:retryCount:error:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[_CFXPreferences copyAppValueForKey:identifier:container:configurationURL:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[_CFXPreferences withSearchListForIdentifier:container:cloudConfigurationURL:perform:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[_CFXPreferences withSearchLists:]
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:-[CFPrefsSource copyValueForKey:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[OS_xpc_object dealloc]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_CF*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:__CF*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_NS*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:getSuperclassMetadata
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_ZN5dyld*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_ZNK5dyld*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:___ZNK5dyld*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_dyld_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:*_dyld_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:___SC_getApplicationBundleID_block_invoke
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:xpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:xpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_xpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:_xpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_libxpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:si_module_with_name
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:_dispatch_*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:_dispatch_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_dispatch_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:*_swift_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:libdispatch_init
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_os_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_od_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_objc_init
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_notify_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:bootstrap_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:$ss*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:loadlocale
fun:setlocale
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:initscr
fun:_ZN7display6Canvas10initializeEv
fun:_ZN7Control10initializeEv
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:assume_default_colors_sp
fun:_ZN7display6Canvas10initializeEv
fun:_ZN7Control10initializeEv
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:start_color_sp
fun:_ZN7display6Canvas10initializeEv
fun:_ZN7Control10initializeEv
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_ZN7display6Canvas10initializeEv
}
-95
View File
@@ -1,95 +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
// Wrapper for addrinfo with focus on zero-copy conversion to and from
// the c-type and wrapper.
//
// Do use the wrapper on a pre-existing struct addrinfo, cast the
// pointer rather than the base type.
#ifndef RAK_ADDRESS_INFO_H
#define RAK_ADDRESS_INFO_H
#include <netdb.h>
#include <rak/socket_address.h>
namespace rak {
class address_info {
public:
void clear() { std::memset(this, 0, sizeof(address_info)); }
int flags() const { return m_addrinfo.ai_flags; }
void set_flags(int f) { m_addrinfo.ai_flags = f; }
int family() const { return m_addrinfo.ai_family; }
void set_family(int f) { m_addrinfo.ai_family = f; }
int socket_type() const { return m_addrinfo.ai_socktype; }
void set_socket_type(int t) { m_addrinfo.ai_socktype = t; }
int protocol() const { return m_addrinfo.ai_protocol; }
void set_protocol(int p) { m_addrinfo.ai_protocol = p; }
size_t length() const { return m_addrinfo.ai_addrlen; }
socket_address* address() { return reinterpret_cast<socket_address*>(m_addrinfo.ai_addr); }
addrinfo* c_addrinfo() { return &m_addrinfo; }
const addrinfo* c_addrinfo() const { return &m_addrinfo; }
address_info* next() { return reinterpret_cast<address_info*>(m_addrinfo.ai_next); }
static int get_address_info(const char* node, int domain, int type, address_info** ai);
static void free_address_info(address_info* ai) { ::freeaddrinfo(ai->c_addrinfo()); }
static const char* strerror(int err) { return gai_strerror(err); }
private:
addrinfo m_addrinfo;
};
inline int
address_info::get_address_info(const char* node, int pfamily, int stype, address_info** ai) {
address_info hints;
hints.clear();
hints.set_family(pfamily);
hints.set_socket_type(stype);
return ::getaddrinfo(node, NULL, hints.c_addrinfo(), reinterpret_cast<addrinfo**>(ai));
}
}
#endif
-195
View File
@@ -1,195 +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>
#ifndef RAK_ALGORITHM_H
#define RAK_ALGORITHM_H
#include <algorithm>
#include <limits>
namespace rak {
template <typename _InputIter, typename _Function>
_Function
for_each_pre(_InputIter __first, _InputIter __last, _Function __f) {
_InputIter __tmp;
while (__first != __last) {
__tmp = __first++;
__f(*__tmp);
}
return __f;
}
// Return a range with a distance of no more than __distance and
// between __first and __last, centered on __middle1.
template <typename _InputIter, typename _Distance>
std::pair<_InputIter, _InputIter>
advance_bidirectional(_InputIter __first, _InputIter __middle1, _InputIter __last, _Distance __distance) {
_InputIter __middle2 = __middle1;
do {
if (!__distance)
break;
if (__middle2 != __last) {
++__middle2;
--__distance;
} else if (__middle1 == __first) {
break;
}
if (!__distance)
break;
if (__middle1 != __first) {
--__middle1;
--__distance;
} else if (__middle2 == __last) {
break;
}
} while (true);
return std::make_pair(__middle1, __middle2);
}
template <typename _InputIter, typename _Distance>
_InputIter
advance_forward(_InputIter __first, _InputIter __last, _Distance __distance) {
while (__first != __last && __distance != 0) {
__first++;
__distance--;
}
return __first;
}
template <typename _InputIter, typename _Distance>
_InputIter
advance_backward(_InputIter __first, _InputIter __last, _Distance __distance) {
while (__first != __last && __distance != 0) {
__first--;
__distance--;
}
return __first;
}
template <typename _Value>
struct compare_base {
bool operator () (const _Value& complete, const _Value& base) const {
return !complete.compare(0, base.size(), base);
}
};
// Count the number of elements from the start of the containers to
// the first inequal element.
template <typename _InputIter1, typename _InputIter2>
typename std::iterator_traits<_InputIter1>::difference_type
count_base(_InputIter1 __first1, _InputIter1 __last1,
_InputIter2 __first2, _InputIter2 __last2) {
typename std::iterator_traits<_InputIter1>::difference_type __n = 0;
for ( ;__first1 != __last1 && __first2 != __last2; ++__first1, ++__first2, ++__n)
if (*__first1 != *__first2)
return __n;
return __n;
}
template <typename _Return, typename _InputIter, typename _Ftor>
_Return
make_base(_InputIter __first, _InputIter __last, _Ftor __ftor) {
if (__first == __last)
return "";
_Return __base = __ftor(*__first++);
for ( ;__first != __last; ++__first) {
typename std::iterator_traits<_InputIter>::difference_type __pos = count_base(__base.begin(), __base.end(),
__ftor(*__first).begin(), __ftor(*__first).end());
if (__pos < (typename std::iterator_traits<_InputIter>::difference_type)__base.size())
__base.resize(__pos);
}
return __base;
}
template<typename T>
inline int popcount_wrapper(T t) {
#if USE_BUILTIN_POPCOUNT
return __builtin_popcountll(t);
#else
#error __builtin_popcount not found.
unsigned int count = 0;
while (t) {
count += t & 0x1;
t >> 1;
}
return count;
#endif
}
// Get the median of an unordered set of numbers of arbitrary
// type by modifing the underlying dataset
template <typename T = double, typename _InputIter>
T median(_InputIter __first, _InputIter __last) {
T __med;
unsigned int __size = __last - __first;
unsigned int __middle = __size / 2;
_InputIter __target1 = __first + __middle;
std::nth_element(__first, __target1, __last);
__med = *__target1;
if (__size % 2 == 0) {
_InputIter __target2 = std::max_element(__first, __target1);
__med = (__med + *__target2) / 2.0;
}
return __med;
}
}
#endif
-85
View File
@@ -1,85 +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>
#ifndef RAK_ERROR_NUMBER_H
#define RAK_ERROR_NUMBER_H
#include <cerrno>
#include <cstring>
namespace rak {
class error_number {
public:
static const int e_access = EACCES;
static const int e_again = EAGAIN;
static const int e_connreset = ECONNRESET;
static const int e_connaborted = ECONNABORTED;
static const int e_deadlk = EDEADLK;
static const int e_noent = ENOENT;
static const int e_nodev = ENODEV;
static const int e_nomem = ENOMEM;
static const int e_notdir = ENOTDIR;
static const int e_isdir = EISDIR;
static const int e_intr = EINTR;
error_number() : m_errno(0) {}
error_number(int e) : m_errno(e) {}
bool is_valid() const { return m_errno != 0; }
int value() const { return m_errno; }
const char* c_str() const { return std::strerror(m_errno); }
bool is_blocked_momentary() const { return m_errno == e_again || m_errno == e_intr; }
bool is_blocked_prolonged() const { return m_errno == e_deadlk; }
bool is_closed() const { return m_errno == e_connreset || m_errno == e_connaborted; }
bool is_bad_path() const { return m_errno == e_noent || m_errno == e_notdir || m_errno == e_access; }
static error_number current() { return errno; }
static void clear_global() { errno = 0; }
static void set_global(error_number err) { errno = err.m_errno; }
bool operator == (const error_number& e) const { return m_errno == e.m_errno; }
private:
int m_errno;
};
}
#endif
-75
View File
@@ -1,75 +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>
#ifndef RAK_FILE_STAT_H
#define RAK_FILE_STAT_H
#include <string>
#include <cinttypes>
#include <sys/stat.h>
namespace rak {
class file_stat {
public:
// Consider storing rak::error_number.
bool update(int fd) { return fstat(fd, &m_stat) == 0; }
bool update(const char* filename) { return stat(filename, &m_stat) == 0; }
bool update(const std::string& filename) { return update(filename.c_str()); }
bool update_link(const char* filename) { return lstat(filename, &m_stat) == 0; }
bool update_link(const std::string& filename) { return update_link(filename.c_str()); }
bool is_regular() const { return S_ISREG(m_stat.st_mode); }
bool is_directory() const { return S_ISDIR(m_stat.st_mode); }
bool is_character() const { return S_ISCHR(m_stat.st_mode); }
bool is_block() const { return S_ISBLK(m_stat.st_mode); }
bool is_fifo() const { return S_ISFIFO(m_stat.st_mode); }
bool is_link() const { return S_ISLNK(m_stat.st_mode); }
bool is_socket() const { return S_ISSOCK(m_stat.st_mode); }
off_t size() const { return m_stat.st_size; }
time_t access_time() const { return m_stat.st_atime; }
time_t change_time() const { return m_stat.st_ctime; }
time_t modified_time() const { return m_stat.st_mtime; }
private:
struct stat m_stat;
};
}
#endif
-197
View File
@@ -1,197 +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>
#ifndef RAK_PARTIAL_QUEUE_H
#define RAK_PARTIAL_QUEUE_H
#include <array>
#include <cstring>
#include <stdexcept>
#include <cinttypes>
namespace rak {
// First step, don't allow overflowing to the next layer. Only disable
// the above layers for now.
// We also include 0 in a single layer as some chunk may be available
// only through seeders.
class partial_queue {
public:
typedef uint8_t key_type;
typedef uint32_t mapped_type;
typedef uint16_t size_type;
typedef std::pair<size_type, size_type> size_pair_type;
static const size_type num_layers = 8;
partial_queue() : m_data(NULL), m_maxLayerSize(0) {}
~partial_queue() { disable(); }
bool is_full() const { return m_ceiling == 0; }
bool is_layer_full(size_type l) const { return m_layers[l].second >= m_maxLayerSize; }
bool is_enabled() const { return m_data != NULL; }
// Add check to see if we can add more. Also make it possible to
// check how full we are in the lower parts so the caller knows when
// he can stop searching.
//
// Though propably not needed, as we must continue til the first
// layer is full.
size_type max_size() const { return m_maxLayerSize * num_layers; }
size_type max_layer_size() const { return m_maxLayerSize; }
// Must be less that or equal to (max size_type) / num_layers.
void enable(size_type ls);
void disable();
void clear();
// Safe to call while pop'ing and it will not reuse pop'ed indices
// so it is guaranteed to reach max_size at some point. This will
// ensure that the user needs to refill with new data at regular
// intervals.
bool insert(key_type key, mapped_type value);
// Only call this when pop'ing as it moves the index.
bool prepare_pop();
mapped_type pop();
private:
partial_queue(const partial_queue&);
void operator = (const partial_queue&);
static size_type ceiling(size_type layer) { return (2 << layer) - 1; }
void find_non_empty();
mapped_type* m_data;
size_type m_maxLayerSize;
size_type m_index;
size_type m_ceiling;
std::array<size_pair_type, num_layers> m_layers;
};
inline void
partial_queue::enable(size_type ls) {
if (ls == 0)
throw std::logic_error("partial_queue::enable(...) ls == 0.");
delete [] m_data;
m_data = new mapped_type[ls * num_layers];
m_maxLayerSize = ls;
}
inline void
partial_queue::disable() {
delete [] m_data;
m_data = NULL;
m_maxLayerSize = 0;
}
inline void
partial_queue::clear() {
if (m_data == NULL)
return;
m_index = 0;
m_ceiling = ceiling(num_layers - 1);
m_layers = {};
}
inline bool
partial_queue::insert(key_type key, mapped_type value) {
if (key >= m_ceiling)
return false;
size_type idx = 0;
// Hmm... since we already check the 'm_ceiling' above, we only need
// to find the target layer. Could this be calculated directly?
while (key >= ceiling(idx))
++idx;
m_index = std::min(m_index, idx);
// Currently don't allow overflow.
if (is_layer_full(idx))
throw std::logic_error("partial_queue::insert(...) layer already full.");
//return false;
m_data[m_maxLayerSize * idx + m_layers[idx].second] = value;
m_layers[idx].second++;
if (is_layer_full(idx))
// Set the ceiling to 0 when layer 0 is full so no more values can
// be inserted.
m_ceiling = idx > 0 ? ceiling(idx - 1) : 0;
return true;
}
// is_empty() will iterate to the first layer with un-popped elements
// and return true, else return false when it reaches a overflowed or
// the last layer.
inline bool
partial_queue::prepare_pop() {
while (m_layers[m_index].first == m_layers[m_index].second) {
if (is_layer_full(m_index) || m_index + 1 == num_layers)
return false;
m_index++;
}
return true;
}
inline partial_queue::mapped_type
partial_queue::pop() {
if (m_index >= num_layers || m_layers[m_index].first >= m_layers[m_index].second)
throw std::logic_error("partial_queue::pop() bad state.");
return m_data[m_index * m_maxLayerSize + m_layers[m_index].first++];
}
}
#endif
-105
View File
@@ -1,105 +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>
// Various functions for manipulating file paths. Also consider making
// a directory iterator.
#ifndef RAK_PATH_H
#define RAK_PATH_H
#include <cstdlib>
#include <string>
namespace rak {
inline std::string
path_expand(const std::string& path) {
if (path.empty() || path[0] != '~')
return path;
char* home = std::getenv("HOME");
if (home == NULL)
return path;
return home + path.substr(1);
}
// Don't inline this...
//
// Same strlcpy as found in *bsd.
inline size_t
strlcpy(char *dest, const char *src, size_t size) {
size_t n = size;
const char* first = src;
if (n != 0) {
while (--n != 0)
if ((*dest++ = *src++) == '\0')
break;
}
if (n == 0) {
if (size != 0)
*dest = '\0';
while (*src++)
;
}
return src - first - 1;
}
inline char*
path_expand(const char* src, char* first, char* last) {
if (*src == '~') {
char* home = std::getenv("HOME");
if (home == NULL)
return first;
first += strlcpy(first, home, std::distance(first, last));
if (first > last)
return last;
src++;
}
return std::min(first + strlcpy(first, src, std::distance(first, last)), last);
}
}
#endif
-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
-557
View File
@@ -1,557 +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>
// Wrappers for the various sockaddr types with focus on zero-copy
// casting between the original type and the wrapper class.
//
// The default ctor does not initialize any data.
//
// _n suffixes indicate that the argument or return value is in
// network byte order, _h that they are in hardware byte order.
// Add define for inet6 scope id?
#ifndef RAK_SOCKET_ADDRESS_H
#define RAK_SOCKET_ADDRESS_H
#include <cinttypes>
#include <cstdint>
#include <cstring>
#include <stdexcept>
#include <string>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
namespace rak {
class socket_address_inet;
class socket_address_inet6;
class socket_address {
public:
static const sa_family_t af_inet = AF_INET;
static const int pf_inet = PF_INET;
static const sa_family_t af_inet6 = AF_INET6;
static const int pf_inet6 = PF_INET6;
static const sa_family_t af_unspec = AF_UNSPEC;
static const int pf_unspec = PF_UNSPEC;
#ifdef AF_LOCAL
static const sa_family_t af_local = AF_LOCAL;
static const int pf_local = PF_LOCAL;
#else
static const sa_family_t af_local = AF_UNIX;
static const int pf_local = PF_UNIX;
#endif
bool is_any() const;
bool is_valid() const;
bool is_bindable() const;
bool is_address_any() const;
bool is_valid_inet_class() const { return family() == af_inet || family() == af_inet6; }
void clear() { std::memset(this, 0, sizeof(socket_address)); set_family(); }
sa_family_t family() const { return m_sockaddr.sa_family; }
void set_family() { m_sockaddr.sa_family = af_unspec; }
uint16_t port() const;
void set_port(uint16_t p);
std::string address_str() const;
bool address_c_str(char* buf, socklen_t size) const;
std::string pretty_address_str() const;
// Attemts to set it as an inet, then an inet6 address. It will
// never set anything but net addresses, no local/unix.
bool set_address_str(const std::string& a) { return set_address_c_str(a.c_str()); }
bool set_address_c_str(const char* a);
uint32_t length() const;
socket_address_inet* sa_inet() { return reinterpret_cast<socket_address_inet*>(this); }
const socket_address_inet* sa_inet() const { return reinterpret_cast<const socket_address_inet*>(this); }
sockaddr* c_sockaddr() { return &m_sockaddr; }
sockaddr_in* c_sockaddr_inet() { return &m_sockaddrInet; }
const sockaddr* c_sockaddr() const { return &m_sockaddr; }
const sockaddr_in* c_sockaddr_inet() const { return &m_sockaddrInet; }
socket_address_inet6* sa_inet6() { return reinterpret_cast<socket_address_inet6*>(this); }
const socket_address_inet6* sa_inet6() const { return reinterpret_cast<const socket_address_inet6*>(this); }
sockaddr_in6* c_sockaddr_inet6() { return &m_sockaddrInet6; }
const sockaddr_in6* c_sockaddr_inet6() const { return &m_sockaddrInet6; }
// Copy a socket address which has the length 'length. Zero out any
// extranous bytes and ensure it does not go beyond the size of this
// struct.
void copy(const socket_address& src, size_t length);
void copy_sockaddr(const sockaddr* src);
static socket_address* cast_from(sockaddr* sa) { return reinterpret_cast<socket_address*>(sa); }
static const socket_address* cast_from(const sockaddr* sa) { return reinterpret_cast<const socket_address*>(sa); }
// The different families will be sorted according to the
// sa_family_t's numeric value.
bool operator == (const socket_address& rhs) const;
bool operator < (const socket_address& rhs) const;
bool operator == (const sockaddr& rhs) const { return *this == *cast_from(&rhs); }
bool operator == (const sockaddr* rhs) const { return *this == *cast_from(rhs); }
bool operator < (const sockaddr& rhs) const { return *this == *cast_from(&rhs); }
bool operator < (const sockaddr* rhs) const { return *this == *cast_from(rhs); }
private:
union {
sockaddr m_sockaddr;
sockaddr_in m_sockaddrInet;
sockaddr_in6 m_sockaddrInet6;
};
};
// Remember to set the AF_INET.
class socket_address_inet {
public:
bool is_any() const { return is_port_any() && is_address_any(); }
bool is_valid() const { return !is_port_any() && !is_address_any(); }
bool is_port_any() const { return port() == 0; }
bool is_address_any() const { return m_sockaddr.sin_addr.s_addr == htonl(INADDR_ANY); }
void clear() { std::memset(this, 0, sizeof(socket_address_inet)); set_family(); }
uint16_t port() const { return ntohs(m_sockaddr.sin_port); }
uint16_t port_n() const { return m_sockaddr.sin_port; }
void set_port(uint16_t p) { m_sockaddr.sin_port = htons(p); }
void set_port_n(uint16_t p) { m_sockaddr.sin_port = p; }
// Should address() return the uint32_t?
in_addr address() const { return m_sockaddr.sin_addr; }
uint32_t address_h() const { return ntohl(m_sockaddr.sin_addr.s_addr); }
uint32_t address_n() const { return m_sockaddr.sin_addr.s_addr; }
std::string address_str() const;
bool address_c_str(char* buf, socklen_t size) const;
void set_address(in_addr a) { m_sockaddr.sin_addr = a; }
void set_address_h(uint32_t a) { m_sockaddr.sin_addr.s_addr = htonl(a); }
void set_address_n(uint32_t a) { m_sockaddr.sin_addr.s_addr = a; }
bool set_address_str(const std::string& a) { return set_address_c_str(a.c_str()); }
bool set_address_c_str(const char* a);
void set_address_any() { set_port(0); set_address_h(INADDR_ANY); }
sa_family_t family() const { return m_sockaddr.sin_family; }
void set_family() { m_sockaddr.sin_family = AF_INET; }
sockaddr* c_sockaddr() { return reinterpret_cast<sockaddr*>(&m_sockaddr); }
sockaddr_in* c_sockaddr_inet() { return &m_sockaddr; }
const sockaddr* c_sockaddr() const { return reinterpret_cast<const sockaddr*>(&m_sockaddr); }
const sockaddr_in* c_sockaddr_inet() const { return &m_sockaddr; }
socket_address_inet6 to_mapped_address() const;
bool operator == (const socket_address_inet& rhs) const;
bool operator < (const socket_address_inet& rhs) const;
private:
struct sockaddr_in m_sockaddr;
};
class socket_address_inet6 {
public:
bool is_any() const { return is_port_any() && is_address_any(); }
bool is_valid() const { return !is_port_any() && !is_address_any(); }
bool is_port_any() const { return port() == 0; }
bool is_address_any() const { return std::memcmp(&m_sockaddr.sin6_addr, &in6addr_any, sizeof(in6_addr)) == 0; }
void clear() { std::memset(this, 0, sizeof(socket_address_inet6)); set_family(); }
uint16_t port() const { return ntohs(m_sockaddr.sin6_port); }
uint16_t port_n() const { return m_sockaddr.sin6_port; }
void set_port(uint16_t p) { m_sockaddr.sin6_port = htons(p); }
void set_port_n(uint16_t p) { m_sockaddr.sin6_port = p; }
in6_addr address() const { return m_sockaddr.sin6_addr; }
const in6_addr* address_ptr() const { return &m_sockaddr.sin6_addr; }
std::string address_str() const;
bool address_c_str(char* buf, socklen_t size) const;
void set_address(in6_addr a) { m_sockaddr.sin6_addr = a; }
bool set_address_str(const std::string& a) { return set_address_c_str(a.c_str()); }
bool set_address_c_str(const char* a);
void set_address_any() { set_port(0); set_address(in6addr_any); }
std::string pretty_address_str() const;
sa_family_t family() const { return m_sockaddr.sin6_family; }
void set_family() { m_sockaddr.sin6_family = AF_INET6; }
sockaddr* c_sockaddr() { return reinterpret_cast<sockaddr*>(&m_sockaddr); }
sockaddr_in6* c_sockaddr_inet6() { return &m_sockaddr; }
const sockaddr* c_sockaddr() const { return reinterpret_cast<const sockaddr*>(&m_sockaddr); }
const sockaddr_in6* c_sockaddr_inet6() const { return &m_sockaddr; }
socket_address normalize_address() const;
bool operator == (const socket_address_inet6& rhs) const;
bool operator < (const socket_address_inet6& rhs) const;
private:
struct sockaddr_in6 m_sockaddr;
};
inline bool
socket_address::is_any() const {
switch (family()) {
case af_inet:
return sa_inet()->is_any();
case af_inet6:
return sa_inet6()->is_any();
default:
return false;
}
}
inline bool
socket_address::is_valid() const {
switch (family()) {
case af_inet:
return sa_inet()->is_valid();
case af_inet6:
return sa_inet6()->is_valid();
default:
return false;
}
}
inline bool
socket_address::is_bindable() const {
switch (family()) {
case af_inet:
return !sa_inet()->is_address_any();
case af_inet6:
return !sa_inet6()->is_address_any();
default:
return false;
}
}
inline bool
socket_address::is_address_any() const {
switch (family()) {
case af_inet:
return sa_inet()->is_address_any();
case af_inet6:
return sa_inet6()->is_address_any();
default:
return true;
}
}
inline uint16_t
socket_address::port() const {
switch (family()) {
case af_inet:
return sa_inet()->port();
case af_inet6:
return sa_inet6()->port();
default:
return 0;
}
}
inline void
socket_address::set_port(uint16_t p) {
switch (family()) {
case af_inet:
return sa_inet()->set_port(p);
case af_inet6:
return sa_inet6()->set_port(p);
default:
break;
}
}
inline std::string
socket_address::address_str() const {
switch (family()) {
case af_inet:
return sa_inet()->address_str();
case af_inet6:
return sa_inet6()->address_str();
default:
return std::string();
}
}
inline bool
socket_address::address_c_str(char* buf, socklen_t size) const {
switch (family()) {
case af_inet:
return sa_inet()->address_c_str(buf, size);
case af_inet6:
return sa_inet6()->address_c_str(buf, size);
default:
return false;
}
}
inline std::string
socket_address::pretty_address_str() const {
switch (family()) {
case af_inet:
return sa_inet()->address_str();
case af_inet6:
return sa_inet6()->pretty_address_str();
case af_unspec:
return std::string("unspec");
default:
return std::string("invalid");
}
}
inline bool
socket_address::set_address_c_str(const char* a) {
if (sa_inet()->set_address_c_str(a)) {
sa_inet()->set_family();
return true;
} else if (sa_inet6()->set_address_c_str(a)) {
sa_inet6()->set_family();
return true;
} else {
return false;
}
}
// Is the zero length really needed, should we require some length?
inline uint32_t
socket_address::length() const {
switch(family()) {
case af_inet:
return sizeof(sockaddr_in);
case af_inet6:
return sizeof(sockaddr_in6);
default:
return 0;
}
}
inline void
socket_address::copy(const socket_address& src, size_t length) {
length = std::min(length, sizeof(socket_address));
std::memset(this, 0, sizeof(socket_address));
std::memcpy(this, &src, length);
}
inline void
socket_address::copy_sockaddr(const sockaddr* src) {
std::memset(this, 0, sizeof(socket_address));
std::memcpy(static_cast<void*>(this), src, socket_address::cast_from(src)->length());
}
inline bool
socket_address::operator == (const socket_address& rhs) const {
if (family() != rhs.family())
return false;
switch (family()) {
case af_inet:
return *sa_inet() == *rhs.sa_inet();
case af_inet6:
return *sa_inet6() == *rhs.sa_inet6();
default:
throw std::logic_error("socket_address::operator == (rhs) invalid type comparison.");
}
}
inline bool
socket_address::operator < (const socket_address& rhs) const {
if (family() != rhs.family())
return family() < rhs.family();
switch (family()) {
case af_inet:
return *sa_inet() < *rhs.sa_inet();
case af_inet6:
return *sa_inet6() < *rhs.sa_inet6();
default:
throw std::logic_error("socket_address::operator < (rhs) invalid type comparison.");
}
}
inline std::string
socket_address_inet::address_str() const {
char buf[INET_ADDRSTRLEN];
if (!address_c_str(buf, INET_ADDRSTRLEN))
return std::string();
return std::string(buf);
}
inline bool
socket_address_inet::address_c_str(char* buf, socklen_t size) const {
return inet_ntop(family(), &m_sockaddr.sin_addr, buf, size);
}
inline bool
socket_address_inet::set_address_c_str(const char* a) {
return inet_pton(AF_INET, a, &m_sockaddr.sin_addr);
}
inline socket_address_inet6
socket_address_inet::to_mapped_address() const {
uint32_t addr32[4];
addr32[0] = 0;
addr32[1] = 0;
addr32[2] = htonl(0xffff);
addr32[3] = m_sockaddr.sin_addr.s_addr;
socket_address_inet6 sa;
sa.clear();
sa.set_address(*reinterpret_cast<in6_addr *>(addr32));
sa.set_port_n(m_sockaddr.sin_port);
return sa;
}
inline bool
socket_address_inet::operator == (const socket_address_inet& rhs) const {
return
m_sockaddr.sin_addr.s_addr == rhs.m_sockaddr.sin_addr.s_addr &&
m_sockaddr.sin_port == rhs.m_sockaddr.sin_port;
}
inline bool
socket_address_inet::operator < (const socket_address_inet& rhs) const {
return
m_sockaddr.sin_addr.s_addr < rhs.m_sockaddr.sin_addr.s_addr ||
(m_sockaddr.sin_addr.s_addr == rhs.m_sockaddr.sin_addr.s_addr &&
m_sockaddr.sin_port < rhs.m_sockaddr.sin_port);
}
inline std::string
socket_address_inet6::address_str() const {
char buf[INET6_ADDRSTRLEN];
if (!address_c_str(buf, INET6_ADDRSTRLEN))
return std::string();
return std::string(buf);
}
inline bool
socket_address_inet6::address_c_str(char* buf, socklen_t size) const {
return inet_ntop(family(), &m_sockaddr.sin6_addr, buf, size);
}
inline bool
socket_address_inet6::set_address_c_str(const char* a) {
return inet_pton(AF_INET6, a, &m_sockaddr.sin6_addr);
}
inline std::string
socket_address_inet6::pretty_address_str() const {
char buf[INET6_ADDRSTRLEN + 2 + 6];
if (inet_ntop(family(), &m_sockaddr.sin6_addr, buf + 1, INET6_ADDRSTRLEN) == NULL)
return std::string();
buf[0] = '[';
char* last_char = (char*)std::memchr(buf + 1, 0, INET6_ADDRSTRLEN);
// TODO: Throw exception here.
if (last_char == NULL || last_char >= buf + 1 + INET6_ADDRSTRLEN)
throw std::logic_error("inet_ntop for inet6 returned bad buffer");
*(last_char++) = ']';
if (!is_port_any()) {
if (snprintf(last_char, 7, ":%" PRIu16, port()) == -1)
return std::string("error"); // TODO: Throw here.
} else {
*last_char = '\0';
}
return std::string(buf);
}
inline socket_address
socket_address_inet6::normalize_address() const {
const uint32_t *addr32 = reinterpret_cast<const uint32_t *>(m_sockaddr.sin6_addr.s6_addr);
if (addr32[0] == 0 && addr32[1] == 0 && addr32[2] == htonl(0xffff)) {
socket_address addr4;
addr4.sa_inet()->set_family();
addr4.sa_inet()->set_address_n(addr32[3]);
addr4.sa_inet()->set_port_n(m_sockaddr.sin6_port);
return addr4;
}
return *reinterpret_cast<const socket_address*>(this);
}
inline bool
socket_address_inet6::operator == (const socket_address_inet6& rhs) const {
return
memcmp(&m_sockaddr.sin6_addr, &rhs.m_sockaddr.sin6_addr, sizeof(in6_addr)) == 0 &&
m_sockaddr.sin6_port == rhs.m_sockaddr.sin6_port;
}
inline bool
socket_address_inet6::operator < (const socket_address_inet6& rhs) const {
int addr_comp = memcmp(&m_sockaddr.sin6_addr, &rhs.m_sockaddr.sin6_addr, sizeof(in6_addr));
return
addr_comp < 0 ||
(addr_comp == 0 ||
m_sockaddr.sin6_port < rhs.m_sockaddr.sin6_port);
}
}
#endif
-425
View File
@@ -1,425 +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>
#ifndef RAK_STRING_MANIP_H
#define RAK_STRING_MANIP_H
#include <algorithm>
#include <cctype>
#include <climits>
#include <cstdlib>
#include <functional>
#include <iterator>
#include <locale>
#include <random>
namespace rak {
// Use these trim functions until n1872 is widely supported.
template <typename Sequence>
Sequence trim_begin(const Sequence& seq) {
if (seq.empty() || !std::isspace(*seq.begin()))
return seq;
typename Sequence::size_type pos = 0;
while (pos != seq.length() && std::isspace(seq[pos]))
pos++;
return seq.substr(pos, seq.length() - pos);
}
template <typename Sequence>
Sequence trim_end(const Sequence& seq) {
if (seq.empty() || !std::isspace(*(--seq.end())))
return seq;
typename Sequence::size_type pos = seq.size();
while (pos != 0 && std::isspace(seq[pos - 1]))
pos--;
return seq.substr(0, pos);
}
template <typename Sequence>
Sequence trim(const Sequence& seq) {
return trim_begin(trim_end(seq));
}
template <typename Sequence>
Sequence trim_begin_classic(const Sequence& seq) {
if (seq.empty() || !std::isspace(*seq.begin(), std::locale::classic()))
return seq;
typename Sequence::size_type pos = 0;
while (pos != seq.length() && std::isspace(seq[pos], std::locale::classic()))
pos++;
return seq.substr(pos, seq.length() - pos);
}
template <typename Sequence>
Sequence trim_end_classic(const Sequence& seq) {
if (seq.empty() || !std::isspace(*(--seq.end()), std::locale::classic()))
return seq;
typename Sequence::size_type pos = seq.size();
while (pos != 0 && std::isspace(seq[pos - 1], std::locale::classic()))
pos--;
return seq.substr(0, pos);
}
template <typename Sequence>
Sequence trim_classic(const Sequence& seq) {
return trim_begin_classic(trim_end_classic(seq));
}
// Consider rewritting such that m_seq is replaced by first/last.
template <typename Sequence>
class split_iterator_t {
public:
typedef typename Sequence::const_iterator const_iterator;
typedef typename Sequence::value_type value_type;
split_iterator_t() {}
split_iterator_t(const Sequence& seq, value_type delim) :
m_seq(&seq),
m_delim(delim),
m_pos(seq.begin()),
m_next(std::find(seq.begin(), seq.end(), delim)) {
}
Sequence operator * () { return Sequence(m_pos, m_next); }
split_iterator_t& operator ++ () {
m_pos = m_next;
if (m_pos == m_seq->end())
return *this;
m_pos++;
m_next = std::find(m_pos, m_seq->end(), m_delim);
return *this;
}
bool operator == (const split_iterator_t&) const { return m_pos == m_seq->end(); }
bool operator != (const split_iterator_t&) const { return m_pos != m_seq->end(); }
private:
const Sequence* m_seq;
value_type m_delim;
const_iterator m_pos;
const_iterator m_next;
};
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence& seq, typename Sequence::value_type delim) {
return split_iterator_t<Sequence>(seq, delim);
}
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence&) {
return split_iterator_t<Sequence>();
}
// Could optimize this abit.
inline char
hexchar_to_value(char c) {
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return 10 + c - 'A';
else
return 10 + c - 'a';
}
template <int pos, typename Value>
inline char
value_to_hexchar(Value v) {
v >>= pos * 4;
v &= 0xf;
if (v < 0xA)
return '0' + v;
else
return 'A' + v - 0xA;
}
template <typename InputIterator, typename OutputIterator>
OutputIterator
copy_escape_html(InputIterator first, InputIterator last, OutputIterator dest) {
while (first != last) {
if (std::isalpha(*first, std::locale::classic()) ||
std::isdigit(*first, std::locale::classic()) ||
*first == '-') {
*(dest++) = *first;
} else {
*(dest++) = '%';
*(dest++) = value_to_hexchar<1>(*first);
*(dest++) = value_to_hexchar<0>(*first);
}
++first;
}
return dest;
}
template <typename InputIterator, typename OutputIterator>
OutputIterator
copy_escape_html(InputIterator first1, InputIterator last1, OutputIterator first2, OutputIterator last2) {
while (first1 != last1) {
if (std::isalpha(*first1, std::locale::classic()) ||
std::isdigit(*first1, std::locale::classic()) ||
*first1 == '-') {
if (first2 == last2) break; else *(first2++) = *first1;
} else {
if (first2 == last2) break; else *(first2++) = '%';
if (first2 == last2) break; else *(first2++) = value_to_hexchar<1>(*first1);
if (first2 == last2) break; else *(first2++) = value_to_hexchar<0>(*first1);
}
++first1;
}
return first2;
}
template <typename Iterator>
inline std::string
copy_escape_html(Iterator first, Iterator last) {
std::string dest;
copy_escape_html(first, last, std::back_inserter(dest));
return dest;
}
template <typename Sequence>
inline Sequence
copy_escape_html(const Sequence& src) {
Sequence dest;
copy_escape_html(src.begin(), src.end(), std::back_inserter(dest));
return dest;
}
template <typename Sequence>
inline std::string
copy_escape_html_str(const Sequence& src) {
std::string dest;
copy_escape_html(src.begin(), src.end(), std::back_inserter(dest));
return dest;
}
// Consider support for larger than char type.
template <typename InputIterator, typename OutputIterator>
OutputIterator
transform_hex(InputIterator first, InputIterator last, OutputIterator dest) {
while (first != last) {
*(dest++) = value_to_hexchar<1>(*first);
*(dest++) = value_to_hexchar<0>(*first);
++first;
}
return dest;
}
template <typename InputIterator, typename OutputIterator>
OutputIterator
transform_hex(InputIterator first1, InputIterator last1, OutputIterator first2, OutputIterator last2) {
while (first1 != last1) {
if (first2 == last2) break; else *(first2++) = value_to_hexchar<1>(*first1);
if (first2 == last2) break; else *(first2++) = value_to_hexchar<0>(*first1);
++first1;
}
return first2;
}
template <typename Sequence>
inline Sequence
transform_hex(const Sequence& src) {
Sequence dest;
transform_hex(src.begin(), src.end(), std::back_inserter(dest));
return dest;
}
template <typename Iterator>
inline std::string
transform_hex(Iterator first, Iterator last) {
std::string dest;
transform_hex(first, last, std::back_inserter(dest));
return dest;
}
template <typename Sequence>
inline std::string
transform_hex_str(const Sequence& seq) {
std::string dest;
transform_hex(seq.begin(), seq.end(), std::back_inserter(dest));
return dest;
}
template <typename Sequence>
Sequence
generate_random(size_t length) {
std::random_device rd;
std::mt19937 mt(rd());
using bytes_randomizer = std::independent_bits_engine<std::mt19937, CHAR_BIT, uint8_t>;
bytes_randomizer bytes(mt);
Sequence s;
s.reserve(length);
std::generate_n(std::back_inserter(s), length, std::ref(bytes));
return s;
}
template <typename Iterator>
inline bool
is_all_alpha(Iterator first, Iterator last) {
while (first != last)
if (!std::isalpha(*first++, std::locale::classic()))
return false;
return true;
}
template <typename Sequence>
inline bool
is_all_alpha(const Sequence& src) {
return is_all_alpha(src.begin(), src.end());
}
template <typename Iterator>
inline bool
is_all_alnum(Iterator first, Iterator last) {
while (first != last)
if (!std::isalnum(*first++, std::locale::classic()))
return false;
return true;
}
template <typename Sequence>
inline bool
is_all_alnum(const Sequence& src) {
return is_all_alnum(src.begin(), src.end());
}
template <typename Iterator>
inline bool
is_all_name(Iterator first, Iterator last) {
while (first != last) {
if (!std::isalnum(*first, std::locale::classic()) && *first != '_')
return false;
first++;
}
return true;
}
template <typename Sequence>
inline bool
is_all_name(const Sequence& src) {
return is_all_name(src.begin(), src.end());
}
template <typename Iterator>
std::string
sanitize(Iterator first, Iterator last) {
std::string dest;
for (; first != last; ++first) {
if (std::isprint(*first) && *first != '\r' && *first != '\n' && *first != '\t')
dest += *first;
else
dest += " ";
}
return dest;
}
template <typename Sequence>
std::string
sanitize(const Sequence& src) {
return trim(sanitize(src.begin(), src.end()));
}
template <typename Iterator>
std::string striptags(Iterator first, Iterator last) {
bool copychar = true;
std::string dest;
for (; first != last; ++first) {
if (std::isprint(*first) && *first == '<') {
copychar = false;
} else if (std::isprint(*first) && *first == '>') {
copychar = true;
continue;
}
if (copychar)
dest += *first;
}
return dest;
}
template <typename Sequence>
std::string striptags(const Sequence& src) {
return striptags(src.begin(), src.end());
}
}
#endif
-107
View File
@@ -1,107 +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>
#ifndef RAK_UNORDERED_VECTOR_H
#define RAK_UNORDERED_VECTOR_H
#include <vector>
namespace rak {
template <typename _Tp>
class unordered_vector : private std::vector<_Tp> {
public:
typedef std::vector<_Tp> Base;
typedef typename Base::value_type value_type;
typedef typename Base::pointer pointer;
typedef typename Base::const_pointer const_pointer;
typedef typename Base::reference reference;
typedef typename Base::const_reference const_reference;
typedef typename Base::size_type size_type;
typedef typename Base::difference_type difference_type;
typedef typename Base::allocator_type allocator_type;
typedef typename Base::iterator iterator;
typedef typename Base::reverse_iterator reverse_iterator;
typedef typename Base::const_iterator const_iterator;
typedef typename Base::const_reverse_iterator const_reverse_iterator;
using Base::clear;
using Base::empty;
using Base::size;
using Base::reserve;
using Base::front;
using Base::back;
using Base::begin;
using Base::end;
using Base::rbegin;
using Base::rend;
using Base::push_back;
using Base::pop_back;
// Use the range erase function, the single element erase gets
// overloaded.
using Base::erase;
iterator insert(iterator position, const value_type& x);
iterator erase(iterator position);
private:
};
template <typename _Tp>
typename unordered_vector<_Tp>::iterator
unordered_vector<_Tp>::insert(iterator position, const value_type& x) {
Base::push_back(x);
return --end();
}
template <typename _Tp>
typename unordered_vector<_Tp>::iterator
unordered_vector<_Tp>::erase(iterator position) {
// We don't need to check if position == end - 1 since we then copy
// to the position we pop later.
*position = Base::back();
Base::pop_back();
return position;
}
}
#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' &&
+117 -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])
@@ -358,3 +296,55 @@ 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]]]),
[
if test "$withval" = "yes"; then
PKG_CHECK_MODULES([SYSTEMD], [libsystemd],
[
CXXFLAGS="$CXXFLAGS $SYSTEMD_CFLAGS"
LIBS="$LIBS $SYSTEMD_LIBS"
AC_DEFINE(HAVE_SYSTEMD, 1, [Support for systemd socket activation.])
],
[AC_MSG_ERROR([libsystemd not found. Install libsystemd-dev (or the equivalent for your distribution).])])
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 -144
View File
@@ -1,66 +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 '^$'`
$1=$(echo "$result" | tr -d '\n')
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)
@@ -89,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.])
])
])
@@ -143,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],
+23 -7
View File
@@ -13,8 +13,6 @@ libsub_root_a_SOURCES = \
core/download_factory.h \
core/download_list.cc \
core/download_list.h \
core/download_store.cc \
core/download_store.h \
core/http_queue.cc \
core/http_queue.h \
core/manager.cc \
@@ -125,6 +123,16 @@ libsub_root_a_SOURCES = \
rpc/tinyxml2/tinyxml2.cc \
rpc/nlohmann/json.h \
\
scgi/thread_scgi.cc \
scgi/thread_scgi.h \
\
session/download_storer.cc \
session/download_storer.h \
session/session_manager.cc \
session/session_manager.h \
session/thread_session.cc \
session/thread_session.h \
\
ui/download.cc \
ui/download.h \
ui/download_list.cc \
@@ -161,11 +169,15 @@ libsub_root_a_SOURCES = \
utils/file_status_cache.cc \
utils/file_status_cache.h \
utils/functional.h \
utils/gzip.cc \
utils/gzip.h \
utils/list_focus.h \
utils/lockfile.cc \
utils/lockfile.h \
utils/socket_fd.cc \
utils/socket_fd.h \
utils/waitpid_queue.cc \
utils/waitpid_queue.h \
utils/watch_ready_queue.cc \
utils/watch_ready_queue.h \
\
command_download.cc \
command_dynamic.cc \
@@ -189,10 +201,14 @@ libsub_root_a_SOURCES = \
globals.h \
option_parser.cc \
option_parser.h \
setup.cc \
setup.h \
signal_handler.cc \
signal_handler.h \
thread_worker.cc \
thread_worker.h
signal_handler.h
if NO_NCURSES
libsub_root_a_SOURCES += display/curses_stub.cc display/curses_stub.h
endif
AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -DPACKAGE_DATADIR=\"$(pkgdatadir)\"
+247 -110
View File
@@ -5,16 +5,10 @@
#include <functional>
#include <netdb.h>
#include <unistd.h>
#include <rak/file_stat.h>
#include <rak/error_number.h>
#include <rak/path.h>
#include <rak/socket_address.h>
#include <rak/string_manip.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>
@@ -23,41 +17,49 @@
#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>
#include <torrent/utils/string_manip.h>
#include "core/download.h"
#include "core/download_store.h"
#include "core/manager.h"
#include "rpc/parse.h"
#include "session/session_manager.h"
#include "globals.h"
#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();
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
@@ -79,23 +81,23 @@ apply_d_change_link(core::Download* download, const torrent::Object::list_type&
if (type == "base_path") {
target = rpc::call_command_string("d.base_path", rpc::make_target(download));
link = rak::path_expand(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
link = expand_path(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
} else if (type == "base_filename") {
target = rpc::call_command_string("d.base_path", rpc::make_target(download));
link = rak::path_expand(prefix + rpc::call_command_string("d.base_filename", rpc::make_target(download)) + postfix);
link = expand_path(prefix + rpc::call_command_string("d.base_filename", rpc::make_target(download)) + postfix);
// } else if (type == "directory_path") {
// target = rpc::call_command_string("d.directory", rpc::make_target(download));
// link = rak::path_expand(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
// link = path_expand(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
} else if (type == "tied") {
link = rak::path_expand(rpc::call_command_string("d.tied_to_file", rpc::make_target(download)));
link = expand_path(rpc::call_command_string("d.tied_to_file", rpc::make_target(download)));
if (link.empty())
return torrent::Object();
link = rak::path_expand(prefix + link + postfix);
link = expand_path(prefix + link + postfix);
target = rpc::call_command_string("d.base_path", rpc::make_target(download));
} else {
@@ -104,22 +106,19 @@ apply_d_change_link(core::Download* download, const torrent::Object::list_type&
switch (changeType) {
case 0:
if (symlink(target.c_str(), link.c_str()) == -1){
lt_log_print(torrent::LOG_TORRENT_WARN, "create_link failed: %s",
rak::error_number::current().c_str());
}
if (symlink(target.c_str(), link.c_str()) == -1)
lt_log_print(torrent::LOG_TORRENT_WARN, "create_link failed: %s", std::strerror(errno));
break;
case 1:
{
rak::file_stat fileStat;
rak::error_number::clear_global();
torrent::utils::FileStat fileStat;
errno = 0;
if (!fileStat.update_link(link) || !fileStat.is_link() || unlink(link.c_str()) == -1)
lt_log_print(torrent::LOG_TORRENT_WARN, "delete_link failed: %s", std::strerror(errno));
if (!fileStat.update_link(link) || !fileStat.is_link() ||
unlink(link.c_str()) == -1){
lt_log_print(torrent::LOG_TORRENT_WARN, "delete_link failed: %s",
rak::error_number::current().c_str());
}
break;
}
default:
@@ -136,8 +135,8 @@ apply_d_delete_tied(core::Download* download) {
if (tie.empty())
return torrent::Object();
if (::unlink(rak::path_expand(tie).c_str()) == -1)
control->core()->push_log_std("Could not unlink tied file: " + std::string(rak::error_number::current().c_str()));
if (::unlink(expand_path(tie).c_str()) == -1)
control->core()->push_log_std("Could not unlink tied file: " + std::string(std::strerror(errno)));
rpc::call_command("d.tied_to_file.set", std::string(), rpc::make_target(download));
return torrent::Object();
@@ -148,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
@@ -164,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);
@@ -275,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(rak::transform_hex(bitField->begin(), bitField->end()));
}
void
apply_d_add_peer(core::Download* download, const std::string& arg) {
int port, ret;
@@ -307,10 +296,12 @@ apply_d_add_peer(core::Download* download, const std::string& arg) {
if (port < 1 || port > 65535)
throw torrent::input_error("Invalid port number.");
assert(std::this_thread::get_id() == torrent::main_thread::thread()->thread_id());
assert(std::this_thread::get_id() == torrent::main_thread::thread_id());
auto callback_id = torrent::system::make_callback_id();
// Currently discarding SOCK_STREAM.
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;
@@ -332,7 +323,8 @@ d_chunks_seen(core::Download* download) {
std::string result;
result.resize(size * 2);
rak::transform_hex((const char*)seen, (const char*)seen + size, result.begin());
torrent::utils::transform_to_hex(seen, seen + size, result);
return result;
}
@@ -348,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;
@@ -362,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();
@@ -386,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);
}
@@ -416,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);
@@ -450,9 +443,11 @@ p_call_target(const torrent::Object::list_type& args) {
torrent::HashString hash;
if (peer_id.size() != 40 ||
torrent::hash_string_from_hex_c_str(peer_id.c_str(), hash) == peer_id.c_str())
throw torrent::input_error("Not a hash string.");
if (peer_id.size() != 40)
throw torrent::input_error("invalid argument: peer id target is not 40 bytes long");
if (torrent::utils::transform_from_hex(peer_id.c_str(), peer_id.c_str() + 40, hash) != hash.end())
throw torrent::input_error("invalid argument: peer id target is not a hex string");
torrent::ConnectionList::iterator peerItr = download->connection_list()->find(hash.c_str());
@@ -473,12 +468,14 @@ download_tracker_insert(core::Download* download, const torrent::Object::list_ty
if (args.size() != 2)
throw torrent::input_error("Wrong argument count.");
int64_t group;
int64_t group = 0;
if (args.front().is_string())
rpc::parse_whole_value_nothrow(args.front().as_string().c_str(), &group);
else
if (args.front().is_string()) {
if (!rpc::parse_whole_value_nothrow(args.front().as_string().c_str(), &group))
throw torrent::input_error("Invalid tracker group number.");
} else {
group = args.front().as_value();
}
if (group < 0 || group > 32)
throw torrent::input_error("Tracker group number invalid.");
@@ -500,6 +497,19 @@ download_get_variable(core::Download* download, const char* first_key, const cha
return download->bencode()->get_key(first_key).get_key(second_key);
}
torrent::Object
download_get_value_or_zero(core::Download* download, const char* first_key, const char* second_key = NULL) {
auto object = download_get_variable(download, first_key, second_key);
if (object.is_empty())
return int64_t(0);
if (!object.is_value())
throw torrent::bencode_error("Download variable is not a value.");
return object;
}
torrent::Object
download_set_variable(core::Download* download, const torrent::Object& rawArgs, const char* first_key, const char* second_key = NULL) {
if (second_key == NULL)
@@ -544,16 +554,13 @@ download_set_variable_string(core::Download* download, const torrent::Object::st
//
torrent::Object
d_list_push_back_string(core::Download* download, const torrent::Object& rawArgs, const char* first_key, const char* second_key) {
auto& arg = rawArgs.as_string();
d_list_push_back_string(core::Download* download, const std::string& arg, const char* first_key, const char* second_key) {
download_get_variable(download, first_key, second_key).as_list().push_back(arg);
return torrent::Object();
}
torrent::Object
d_list_push_back_unique_string(core::Download* download, const torrent::Object& rawArgs, const char* first_key, const char* second_key) {
auto& arg = rawArgs.as_string();
d_list_push_back_unique_string(core::Download* download, const std::string& arg, const char* first_key, const char* second_key) {
torrent::Object::list_type& list = download_get_variable(download, first_key, second_key).as_list();
if (std::none_of(list.begin(), list.end(), [arg](const torrent::Object& obj) { return torrent::object_equal(obj, arg); }))
@@ -608,12 +615,16 @@ d_list_remove(core::Download* download, const torrent::Object& rawArgs, const ch
#define CMD2_DL_TIMESTAMP(key, first_key, second_key) \
CMD2_DL(key, std::bind(&download_get_variable, std::placeholders::_1, first_key, second_key)); \
CMD2_DL_VALUE_P(key ".set", std::bind(&download_set_variable_value, \
std::placeholders::_1, std::placeholders::_2, \
first_key, second_key)); \
CMD2_DL_VALUE_P(key ".set", std::bind(&download_set_variable_value, \
std::placeholders::_1, std::placeholders::_2, \
first_key, second_key)); \
CMD2_DL_VALUE_P(key ".set_if_z", std::bind(&download_set_variable_value_ifz, \
std::placeholders::_1, std::placeholders::_2, \
first_key, second_key)); \
CMD2_DL(key ".or_zero", [](core::Download* download, auto) { \
return download_get_value_or_zero(download, first_key, second_key); }); \
CMD2_DL_VALUE(key ".elapsed", [](core::Download* download, auto value) { \
return download_get_value_or_zero(download, first_key, second_key).as_value() > value; });
#define CMD2_DL_VAR_STRING(key, first_key, second_key) \
CMD2_DL(key, std::bind(&download_get_variable, std::placeholders::_1, first_key, second_key)); \
@@ -633,30 +644,48 @@ void cg_d_group_set(core::Download* download, const torrent::Objec
void
initialize_command_download() {
CMD2_DL("d.hash", std::bind(&rak::transform_hex_str<torrent::HashString>, CMD2_ON_INFO(hash)));
CMD2_DL("d.local_id", std::bind(&rak::transform_hex_str<torrent::HashString>, CMD2_ON_INFO(local_id)));
CMD2_DL("d.local_id_html", std::bind(&rak::copy_escape_html_str<torrent::HashString>, CMD2_ON_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));
@@ -672,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));
@@ -691,8 +720,8 @@ initialize_command_download() {
CMD2_DL_V ("d.erase", std::bind(&core::DownloadList::erase_ptr, control->core()->download_list(), std::placeholders::_1));
CMD2_DL_V ("d.check_hash", std::bind(&core::DownloadList::check_hash, control->core()->download_list(), std::placeholders::_1));
CMD2_DL ("d.save_resume", std::bind(&core::DownloadStore::save_resume, control->core()->download_store(), std::placeholders::_1));
CMD2_DL ("d.save_full_session", std::bind(&core::DownloadStore::save_full, control->core()->download_store(), std::placeholders::_1));
CMD2_DL_V ("d.save_resume", [](core::Download* download, auto) { session_thread::manager()->save_resume_download(download); });
CMD2_DL_V ("d.save_full_session", [](core::Download* download, auto) { session_thread::manager()->save_full_download(download); });
CMD2_DL_V ("d.update_priorities", CMD2_ON_DL(update_priorities));
@@ -752,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");
@@ -774,8 +803,8 @@ initialize_command_download() {
CMD2_DL ("d.views", std::bind(&download_get_variable, std::placeholders::_1, "rtorrent", "views"));
CMD2_DL ("d.views.has", std::bind(&d_list_has, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views"));
CMD2_DL ("d.views.remove", std::bind(&d_list_remove, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views"));
CMD2_DL ("d.views.push_back", std::bind(&d_list_push_back_string, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views"));
CMD2_DL ("d.views.push_back_unique", std::bind(&d_list_push_back_unique_string, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views"));
CMD2_DL_STRING ("d.views.push_back", std::bind(&d_list_push_back_string, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views"));
CMD2_DL_STRING ("d.views.push_back_unique", std::bind(&d_list_push_back_unique_string, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views"));
// This command really needs to be improved, so we have proper
// logging support.
@@ -815,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));
@@ -876,4 +905,112 @@ initialize_command_download() {
CMD2_DL_LIST ("t.multicall", std::bind(&t_multicall, std::placeholders::_1, std::placeholders::_2));
CMD2_ANY_LIST ("p.call_target", std::bind(&p_call_target, std::placeholders::_2));
rpc::rpc.mark_safe("add_peer");
rpc::rpc.mark_safe("d.hash");
rpc::rpc.mark_safe("d.local_id");
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");
rpc::rpc.mark_safe("d.load_date");
rpc::rpc.mark_safe("d.up.rate");
rpc::rpc.mark_safe("d.up.total");
rpc::rpc.mark_safe("d.down.rate");
rpc::rpc.mark_safe("d.down.total");
rpc::rpc.mark_safe("d.skip.rate");
rpc::rpc.mark_safe("d.skip.total");
rpc::rpc.mark_safe("d.is_open");
rpc::rpc.mark_safe("d.is_active");
rpc::rpc.mark_safe("d.is_hash_checked");
rpc::rpc.mark_safe("d.is_hash_checking");
rpc::rpc.mark_safe("d.is_multi_file");
rpc::rpc.mark_safe("d.is_private");
rpc::rpc.mark_safe("d.is_pex_active");
rpc::rpc.mark_safe("d.is_partially_done");
rpc::rpc.mark_safe("d.is_not_partially_done");
rpc::rpc.mark_safe("d.is_meta");
rpc::rpc.mark_safe("d.peer_exchange");
rpc::rpc.mark_safe("d.resume");
rpc::rpc.mark_safe("d.pause");
rpc::rpc.mark_safe("d.open");
rpc::rpc.mark_safe("d.close");
rpc::rpc.mark_safe("d.close.directly");
rpc::rpc.mark_safe("d.erase");
rpc::rpc.mark_safe("d.check_hash");
rpc::rpc.mark_safe("d.save_resume");
rpc::rpc.mark_safe("d.save_full_session");
rpc::rpc.mark_safe("d.update_priorities");
rpc::rpc.mark_safe("d.custom");
rpc::rpc.mark_safe("d.custom1");
rpc::rpc.mark_safe("d.custom2");
rpc::rpc.mark_safe("d.custom3");
rpc::rpc.mark_safe("d.custom4");
rpc::rpc.mark_safe("d.custom5");
rpc::rpc.mark_safe("d.size_bytes");
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.started");
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");
rpc::rpc.mark_safe("d.tracker_size");
rpc::rpc.mark_safe("d.completed_chunks");
rpc::rpc.mark_safe("d.left_bytes");
rpc::rpc.mark_safe("d.chunk_size");
rpc::rpc.mark_safe("d.priority");
rpc::rpc.mark_safe("d.priority_str");
rpc::rpc.mark_safe("d.state");
rpc::rpc.mark_safe("d.state_changed");
rpc::rpc.mark_safe("d.state_counter");
rpc::rpc.mark_safe("d.connection_current");
rpc::rpc.mark_safe("d.connection_leech");
rpc::rpc.mark_safe("d.connection_seed");
rpc::rpc.mark_safe("d.throttle_name");
rpc::rpc.mark_safe("d.uploads_max");
rpc::rpc.mark_safe("d.downloads_max");
rpc::rpc.mark_safe("d.peers_min");
rpc::rpc.mark_safe("d.peers_max");
rpc::rpc.mark_safe("d.peers_connected");
rpc::rpc.mark_safe("d.peers_not_connected");
rpc::rpc.mark_safe("d.peers_complete");
rpc::rpc.mark_safe("d.tracker_numwant");
rpc::rpc.mark_safe("d.tracker_focus");
rpc::rpc.mark_safe("d.message");
rpc::rpc.mark_safe("d.hashing");
rpc::rpc.mark_safe("d.hashing_failed");
rpc::rpc.mark_safe("d.free_diskspace");
rpc::rpc.mark_safe("d.views");
rpc::rpc.mark_safe("d.views.remove");
rpc::rpc.mark_safe("d.views.push_back_unique");
rpc::rpc.mark_safe("d.ratio");
rpc::rpc.mark_safe("f.multicall");
rpc::rpc.mark_safe("p.multicall");
rpc::rpc.mark_safe("p.call_target");
rpc::rpc.mark_safe("t.multicall");
}
+40 -11
View File
@@ -411,7 +411,7 @@ initialize_command_dynamic() {
// Keep these for future use when we deprecate more commands.
CMD2_VAR_BOOL ("method.use_deprecated", false);
CMD2_VAR_VALUE ("method.use_intermediate", 0);
CMD2_VAR_VALUE ("method.use_intermediate", 3);
CMD2_ANY_LIST ("method.insert", std::bind(&system_method_insert, std::placeholders::_2));
CMD2_ANY_LIST ("method.insert.value", std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_value_type));
@@ -444,15 +444,44 @@ initialize_command_dynamic() {
CMD2_ANY ("catch", std::bind(&cmd_catch, std::placeholders::_1, std::placeholders::_2));
CMD2_ANY ("strings.choke_heuristics", std::bind(&torrent::option_list_strings, torrent::OPTION_CHOKE_HEURISTICS));
CMD2_ANY ("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.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));
CMD2_ANY ("strings.tracker_event", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_EVENT));
CMD2_ANY ("strings.tracker_mode", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_MODE));
CMD2_ANY_STRING ("enum.log_group", [](auto, const auto& str) { return torrent::option_find_string_str(torrent::OPTION_LOG_GROUP, str); });
CMD2_ANY ("strings.choke_heuristics", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS); });
CMD2_ANY ("strings.choke_heuristics.upload", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_UPLOAD); });
CMD2_ANY ("strings.choke_heuristics.download", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_DOWNLOAD); });
CMD2_ANY ("strings.connection_type", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CONNECTION_TYPE); });
CMD2_ANY ("strings.encryption", [](auto, auto) { return torrent::Object::create_list(); });
CMD2_ANY ("strings.encryption.handshake", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_HANDSHAKE); });
CMD2_ANY ("strings.encryption.stream", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_STREAM); });
CMD2_ANY ("strings.ip_filter", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_FILTER); });
CMD2_ANY ("strings.ip_tos", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_TOS); });
CMD2_ANY ("strings.log_group", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_LOG_GROUP); });
CMD2_ANY ("strings.tracker_event", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_TRACKER_EVENT); });
CMD2_ANY ("strings.tracker_mode", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_TRACKER_MODE); });
// clang-format on
#ifdef HAVE_XMLRPC_TINYXML2
rpc::rpc.mark_safe("system.listMethods");
#endif
rpc::rpc.mark_safe("method.use_deprecated");
rpc::rpc.mark_safe("method.const");
rpc::rpc.mark_safe("method.has_key");
rpc::rpc.mark_safe("method.list_keys");
rpc::rpc.mark_safe("method.get");
rpc::rpc.mark_safe("method.rlookup");
rpc::rpc.mark_safe("catch");
rpc::rpc.mark_safe("enum.log_group");
rpc::rpc.mark_safe("strings.choke_heuristics");
rpc::rpc.mark_safe("strings.choke_heuristics.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.ip_filter");
rpc::rpc.mark_safe("strings.ip_tos");
rpc::rpc.mark_safe("strings.log_group");
rpc::rpc.mark_safe("strings.tracker_event");
rpc::rpc.mark_safe("strings.tracker_mode");
}
+87 -56
View File
@@ -2,14 +2,14 @@
#include <functional>
#include <cstdio>
#include <rak/error_number.h>
#include <rak/file_stat.h>
#include <rak/path.h>
#include <rak/string_manip.h>
#include <string>
#include <vector>
#include <torrent/rate.h>
#include <torrent/hash_string.h>
#include <torrent/utils/log.h>
#include <torrent/utils/directory_events.h>
#include <torrent/utils/file_stat.h>
#include <torrent/utils/string_manip.h>
#include "globals.h"
#include "control.h"
@@ -21,11 +21,12 @@
#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) {
auto& group_name = rawArgs.as_string();
auto view_itr = control->view_manager()->find(rpc::commands.call("group2." + group_name + ".view", rpc::make_target()).as_string());
auto view_itr = control->view_manager()->find(rpc::commands.call("group." + group_name + ".view", rpc::make_target()).as_string());
if (view_itr == control->view_manager()->end())
throw torrent::input_error("Could not find view.");
@@ -33,9 +34,9 @@ apply_on_ratio(const torrent::Object& rawArgs) {
// first argument: minimum ratio to reach
// second argument: minimum upload amount to reach [optional]
// third argument: maximum ratio to reach [optional]
int64_t min_ratio = rpc::commands.call("group2." + group_name + ".ratio.min", rpc::make_target()).as_value();
int64_t max_ratio = rpc::commands.call("group2." + group_name + ".ratio.max", rpc::make_target()).as_value();
int64_t min_upload = rpc::commands.call("group2." + group_name + ".ratio.upload", rpc::make_target()).as_value();
int64_t min_ratio = rpc::commands.call("group." + group_name + ".ratio.min", rpc::make_target()).as_value();
int64_t max_ratio = rpc::commands.call("group." + group_name + ".ratio.max", rpc::make_target()).as_value();
int64_t min_upload = rpc::commands.call("group." + group_name + ".ratio.upload", rpc::make_target()).as_value();
std::vector<core::Download*> downloads;
@@ -67,10 +68,10 @@ apply_start_tied() {
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 1)
continue;
rak::file_stat fs;
torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
if (!tied_to_file.empty() && fs.update(rak::path_expand(tied_to_file)))
if (!tied_to_file.empty() && fs.update(expand_path(tied_to_file)))
rpc::parse_command_single(rpc::make_target(download), "d.try_start=");
}
@@ -83,10 +84,10 @@ apply_stop_untied() {
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 0)
continue;
rak::file_stat fs;
torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
if (!tied_to_file.empty() && !fs.update(rak::path_expand(tied_to_file)))
if (!tied_to_file.empty() && !fs.update(expand_path(tied_to_file)))
rpc::parse_command_single(rpc::make_target(download), "d.try_stop=");
}
@@ -96,10 +97,10 @@ apply_stop_untied() {
torrent::Object
apply_close_untied() {
for (const auto& download : *control->core()->download_list()) {
rak::file_stat fs;
torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
if (rpc::call_command_value("d.ignore_commands", rpc::make_target(download)) == 0 && !tied_to_file.empty() && !fs.update(rak::path_expand(tied_to_file)))
if (rpc::call_command_value("d.ignore_commands", rpc::make_target(download)) == 0 && !tied_to_file.empty() && !fs.update(expand_path(tied_to_file)))
rpc::parse_command_single(rpc::make_target(download), "d.try_close=");
}
@@ -109,10 +110,10 @@ apply_close_untied() {
torrent::Object
apply_remove_untied() {
for (auto itr = control->core()->download_list()->begin(); itr != control->core()->download_list()->end(); ) {
rak::file_stat fs;
torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(*itr));
if (!tied_to_file.empty() && !fs.update(rak::path_expand(tied_to_file))) {
if (!tied_to_file.empty() && !fs.update(expand_path(tied_to_file))) {
// Need to clear tied_to_file so it doesn't try to delete it.
rpc::call_command("d.tied_to_file.set", std::string(), rpc::make_target(*itr));
@@ -167,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);
@@ -210,7 +213,7 @@ apply_download_list(const torrent::Object::list_type& args) {
for (core::View::const_iterator itr = (*view_itr)->begin_visible(), last = (*view_itr)->end_visible(); itr != last; itr++) {
const torrent::HashString* hashString = &(*itr)->info()->hash();
resultList.push_back(rak::transform_hex(hashString->begin(), hashString->end()));
resultList.push_back(torrent::utils::transform_to_hex_str(*hashString));
}
return result;
@@ -255,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() + "'.");
@@ -269,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) {
@@ -293,57 +298,83 @@ 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(rak::error_number::current().c_str()));
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");
rpc::rpc.mark_safe("close_untied");
rpc::rpc.mark_safe("remove_untied");
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.multicall");
rpc::rpc.mark_safe("d.multicall.filtered");
}
+78 -59
View File
@@ -1,41 +1,5 @@
// 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>
#include "config.h"
#include <rak/error_number.h>
#include <rak/path.h>
#include <torrent/data/file.h>
#include <torrent/data/file_list.h>
#include <torrent/data/file_list_iterator.h>
@@ -60,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
@@ -93,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
@@ -116,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));
@@ -139,4 +124,38 @@ initialize_command_file() {
CMD2_FILEITR("fi.filename_last", std::bind(&apply_fi_filename_last, std::placeholders::_1));
CMD2_FILEITR("fi.is_file", std::bind(&torrent::FileListIterator::is_file, std::placeholders::_1));
rpc::rpc.mark_safe("f.path");
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");
rpc::rpc.mark_safe("f.completed_chunks");
rpc::rpc.mark_safe("f.range_first");
rpc::rpc.mark_safe("f.range_second");
rpc::rpc.mark_safe("f.priority");
rpc::rpc.mark_safe("f.priority.set");
rpc::rpc.mark_safe("f.is_created");
rpc::rpc.mark_safe("f.is_open");
rpc::rpc.mark_safe("f.is_create_queued");
rpc::rpc.mark_safe("f.is_resize_queued");
rpc::rpc.mark_safe("f.prioritize_first");
rpc::rpc.mark_safe("f.prioritize_first.enable");
rpc::rpc.mark_safe("f.prioritize_first.disable");
rpc::rpc.mark_safe("f.prioritize_last");
rpc::rpc.mark_safe("f.prioritize_last.enable");
rpc::rpc.mark_safe("f.prioritize_last.disable");
rpc::rpc.mark_safe("f.last_touched");
rpc::rpc.mark_safe("f.match_depth_prev");
rpc::rpc.mark_safe("f.match_depth_next");
rpc::rpc.mark_safe("fi.filename_last");
rpc::rpc.mark_safe("fi.is_file");
}
+51 -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,52 +337,69 @@ 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");
rpc::rpc.mark_safe("choke_group.index_of");
rpc::rpc.mark_safe("choke_group.general.size");
rpc::rpc.mark_safe("choke_group.tracker.mode");
rpc::rpc.mark_safe("choke_group.up.rate");
rpc::rpc.mark_safe("choke_group.down.rate");
rpc::rpc.mark_safe("choke_group.up.max");
rpc::rpc.mark_safe("choke_group.up.max.unlimited");
rpc::rpc.mark_safe("choke_group.up.total");
rpc::rpc.mark_safe("choke_group.up.queued");
rpc::rpc.mark_safe("choke_group.up.unchoked");
rpc::rpc.mark_safe("choke_group.up.heuristics");
rpc::rpc.mark_safe("choke_group.down.max");
rpc::rpc.mark_safe("choke_group.down.max.unlimited");
rpc::rpc.mark_safe("choke_group.down.total");
rpc::rpc.mark_safe("choke_group.down.queued");
rpc::rpc.mark_safe("choke_group.down.unchoked");
rpc::rpc.mark_safe("choke_group.down.heuristics");
}
-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>
#include "config.h"
#include <torrent/exceptions.h>
+28 -13
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:
//
@@ -100,25 +119,21 @@ void initialize_commands();
CMD2_ANY(key, std::bind(&rpc::command_function_call_object, torrent::Object(torrent::raw_string::from_c_str(cmds)), \
std::placeholders::_1, std::placeholders::_2));
#define CMD2_REDIRECT(from_key, to_key) \
#define CMD2_REDIRECT(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_dont_delete);
#define CMD2_REDIRECT_GENERIC(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_no_target | rpc::CommandMap::flag_dont_delete);
#define CMD2_REDIRECT_GENERIC_NO_EXPORT(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_no_target | rpc::CommandMap::flag_dont_delete);
#define CMD2_REDIRECT_NO_EXPORT(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_dont_delete);
#define CMD2_REDIRECT_MUTABLE(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc);
#define CMD2_REDIRECT_STR(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc);
#define CMD2_REDIRECT_STR_NO_EXPORT(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, 0);
#define CMD2_REDIRECT_FILE(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_file_target | rpc::CommandMap::flag_dont_delete);
#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);
#define CMD2_REDIRECT_GENERIC_STR(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, \
rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_no_target);
#define CMD2_REDIRECT_GENERIC_STR_NO_EXPORT(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, \
rpc::CommandMap::flag_no_target);
//
// Conversion of return types:
//
+19 -50
View File
@@ -1,54 +1,18 @@
// 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>
#include "config.h"
#include <fstream>
#include <rak/path.h>
#include <torrent/peer/peer_list.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "globals.h"
#include "command_helpers.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <torrent/peer/peer_list.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "globals.h"
#include "command_helpers.h"
bool ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address_end);
@@ -65,7 +29,7 @@ torrent::Object
apply_ip_tables_size_data(const std::string& args) {
rpc::ip_table_list::const_iterator itr = ip_tables.find(args);
if (itr != ip_tables.end())
if (itr == ip_tables.end())
throw torrent::input_error("IP table does not exist.");
uint32_t size = itr->table.sizeof_data();
@@ -89,7 +53,7 @@ apply_ip_tables_get(const torrent::Object::list_type& args) {
if (table_itr == ip_tables.end())
throw torrent::input_error("Could not find ip table.");
if (!ipv4_range_parse(address.c_str(), &address_start, &address_end))
if (!ipv4_range_parse(address.c_str(), &address_start, &address_end))
throw torrent::input_error("Invalid address format.");
if(!table_itr->table.defined(address_start, address_end))
@@ -239,9 +203,14 @@ ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address
uint32_t mask=0;
uint32_t end_mask=0;
mask = (~mask) << (32-mask_bits);
if (mask_bits == 0) {
mask = 0;
end_mask = ~(uint32_t)0;
} else {
mask = (~mask) << (32-mask_bits);
end_mask = (~end_mask) >> mask_bits;
}
*address_start = ip & mask;
end_mask = (~end_mask) >> mask_bits;
*address_end = (ip & mask) | end_mask;
valid=true;
@@ -298,7 +267,7 @@ apply_ipv4_filter_get(const std::string& args) {
uint32_t address_start;
uint32_t address_end;
if (!ipv4_range_parse(args.c_str(), &address_start, &address_end))
if (!ipv4_range_parse(args.c_str(), &address_start, &address_end))
throw torrent::input_error("Invalid address format.");
if(!torrent::PeerList::ipv4_filter()->defined(address_start, address_end))
@@ -326,8 +295,8 @@ apply_ipv4_filter_load(const torrent::Object::list_type& args) {
std::string value_name = args.back().as_string();
int value = torrent::option_find_string(torrent::OPTION_IP_FILTER, value_name.c_str());
std::fstream file(rak::path_expand(filename).c_str(), std::ios::in);
std::fstream file(expand_path(filename).c_str(), std::ios::in);
if (!file.is_open())
throw torrent::input_error("Could not open ip filter file: " + filename);
@@ -384,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);
+191 -137
View File
@@ -1,27 +1,27 @@
#include "config.h"
#include <cerrno>
#include <fcntl.h>
#include <functional>
#include <stdio.h>
#include <unistd.h>
#include <rak/path.h>
#include <rak/error_number.h>
#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>
#include "core/download.h"
#include "core/download_list.h"
#include "core/download_store.h"
#include "core/manager.h"
#include "rak/string_manip.h"
#include "rpc/parse_commands.h"
#include "rpc/scgi.h"
#include "session/session_manager.h"
#include "utils/file_status_cache.h"
#include "globals.h"
@@ -96,8 +96,12 @@ post_increment(torrent::Object::list_const_iterator& itr, const torrent::Object:
inline const std::string&
check_name(const std::string& str) {
if (!rak::is_all_name(str))
throw torrent::input_error("Non-alphanumeric characters found.");
auto itr = std::find_if(str.begin(), str.end(), [](char c) {
return !std::isalnum(c, std::locale::classic()) && c != '_';
});
if (itr != str.end())
throw torrent::input_error("Invalid characters found in name.");
return str;
}
@@ -111,40 +115,24 @@ 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("group2." + name + ".view", "string", view));
rpc::commands.call("method.insert", rpc::create_object_list("group2." + name + ".ratio.min", "value", (int64_t)200));
rpc::commands.call("method.insert", rpc::create_object_list("group2." + name + ".ratio.max", "value", (int64_t)300));
rpc::commands.call("method.insert", rpc::create_object_list("group2." + name + ".ratio.upload", "value", (int64_t)20 << 20));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".view", "string", view));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.min", "value", (int64_t)200));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.max", "value", (int64_t)300));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.upload", "value", (int64_t)20 << 20));
if (rpc::call_command_value("method.use_intermediate") == 1) {
// Deprecated in 0.7.0:
CMD2_REDIRECT_GENERIC_STR("group." + name + ".view", "group2." + name + ".view");
CMD2_REDIRECT_GENERIC_STR("group." + name + ".view.set", "group2." + name + ".view.set");
CMD2_REDIRECT_GENERIC_STR("group." + name + ".ratio.min", "group2." + name + ".ratio.min");
CMD2_REDIRECT_GENERIC_STR("group." + name + ".ratio.min.set", "group2." + name + ".ratio.min.set");
CMD2_REDIRECT_GENERIC_STR("group." + name + ".ratio.max", "group2." + name + ".ratio.max");
CMD2_REDIRECT_GENERIC_STR("group." + name + ".ratio.max.set", "group2." + name + ".ratio.max.set");
CMD2_REDIRECT_GENERIC_STR("group." + name + ".ratio.upload", "group2." + name + ".ratio.upload");
CMD2_REDIRECT_GENERIC_STR("group." + name + ".ratio.upload.set", "group2." + name + ".ratio.upload.set");
} if (rpc::call_command_value("method.use_intermediate") == 2) {
// Deprecated in 0.7.0:
CMD2_REDIRECT_GENERIC_STR_NO_EXPORT("group." + name + ".view", "group2." + name + ".view");
CMD2_REDIRECT_GENERIC_STR_NO_EXPORT("group." + name + ".view.set", "group2." + name + ".view.set");
CMD2_REDIRECT_GENERIC_STR_NO_EXPORT("group." + name + ".ratio.min", "group2." + name + ".ratio.min");
CMD2_REDIRECT_GENERIC_STR_NO_EXPORT("group." + name + ".ratio.min.set", "group2." + name + ".ratio.min.set");
CMD2_REDIRECT_GENERIC_STR_NO_EXPORT("group." + name + ".ratio.max", "group2." + name + ".ratio.max");
CMD2_REDIRECT_GENERIC_STR_NO_EXPORT("group." + name + ".ratio.max.set", "group2." + name + ".ratio.max.set");
CMD2_REDIRECT_GENERIC_STR_NO_EXPORT("group." + name + ".ratio.upload", "group2." + name + ".ratio.upload");
CMD2_REDIRECT_GENERIC_STR_NO_EXPORT("group." + name + ".ratio.upload.set", "group2." + name + ".ratio.upload.set");
}
rpc::rpc.mark_safe("group." + name + ".view");
rpc::rpc.mark_safe("group." + name + ".view.set");
rpc::rpc.mark_safe("group." + name + ".ratio.min");
rpc::rpc.mark_safe("group." + name + ".ratio.min.set");
rpc::rpc.mark_safe("group." + name + ".ratio.max");
rpc::rpc.mark_safe("group." + name + ".ratio.max.set");
rpc::rpc.mark_safe("group." + name + ".ratio.upload");
rpc::rpc.mark_safe("group." + name + ".ratio.upload.set");
return name;
}
@@ -183,12 +171,16 @@ cmd_file_append(const torrent::Object::list_type& args) {
FILE* output = fopen(args.front().as_string().c_str(), "a");
if (output == NULL)
throw torrent::input_error("Could not append to file '" + args.front().as_string() + "': " + rak::error_number::current().c_str());
if (output == nullptr)
throw torrent::input_error("Could not append to file '" + args.front().as_string() + "': " + std::strerror(errno));
file_print_list(++args.begin(), args.end(), output, file_print_delim_space);
fprintf(output, "\n");
try {
file_print_list(++args.begin(), args.end(), output, file_print_delim_space);
fprintf(output, "\n");
} catch (...) {
fclose(output);
throw;
}
fclose(output);
return torrent::Object();
}
@@ -196,137 +188,199 @@ cmd_file_append(const torrent::Object::list_type& args) {
void
initialize_command_local() {
core::DownloadList* dList = control->core()->download_list();
core::DownloadStore* dStore = control->core()->download_store();
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));
CMD_ANY ("system.hostname", std::bind(&system_hostname));
CMD_ANY ("system.pid", std::bind(&getpid));
CMD2_VAR_C_STRING("system.api_version", (int64_t)API_VERSION);
CMD2_VAR_C_STRING("system.client_version", PACKAGE_VERSION);
CMD2_VAR_C_STRING("system.library_version", torrent::version());
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_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_VALUE ("system.file.allocate", 0);
CMD_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
CMD_VAR_VALUE ("system.file.split_size", -1);
CMD_VAR_STRING ("system.file.split_suffix", ".part");
CMD2_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size,
CMD_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size,
(utils::FileStatusCache::base_type*)control->core()->file_status_cache()));
CMD2_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache()));
CMD_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache()));
CMD2_VAR_BOOL ("file.prioritize_toc", 0);
CMD2_VAR_LIST ("file.prioritize_toc.first");
CMD2_VAR_LIST ("file.prioritize_toc.last");
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_VAR_BOOL ("system.files.session.fdatasync", true);
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_GENERIC_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(); });
CMD_ANY ("system.sockets.reserved_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->reserved_allocation(); });
CMD_ANY ("system.sockets.available_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->available_allocation(); });
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); });
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) {
CMD_ANY (category_name + ".min_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->generic_min_allocation(); });
continue;
}
CMD2_VAR_STRING ("directory.default", "./");
CMD_ANY (category_name + ".min_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_min_allocation(category); });
CMD_ANY (category_name + ".max_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_allocation(category); });
CMD_ANY_VALUE_V(category_name + ".min_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_min_allocation(category, value); });
CMD_ANY_VALUE_V(category_name + ".max_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_max_allocation(category, value); });
}
CMD2_VAR_STRING ("session.name", "");
CMD2_VAR_BOOL ("session.use_lock", true);
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 ("session.path", std::bind(&core::DownloadStore::path, dStore));
CMD2_ANY_STRING_V("session.path.set", std::bind(&core::DownloadStore::set_path, dStore, std::placeholders::_2));
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_V ("session.save", std::bind(&core::DownloadList::session_save, dList));
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");
rpc::rpc.mark_safe("system.library_version");
rpc::rpc.mark_safe("system.file.max_size");
rpc::rpc.mark_safe("system.file.split_size");
rpc::rpc.mark_safe("system.file.split_suffix");
rpc::rpc.mark_safe("system.sockets.size");
rpc::rpc.mark_safe("system.sockets.max_size");
rpc::rpc.mark_safe("system.sockets.reserved_alloc");
rpc::rpc.mark_safe("system.sockets.available_alloc");
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
rpc::rpc.mark_safe(category_name + ".size");
rpc::rpc.mark_safe(category_name + ".max_size");
if (i == 0) {
rpc::rpc.mark_safe(category_name + ".min_alloc");
continue;
}
rpc::rpc.mark_safe(category_name + ".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");
rpc::rpc.mark_safe("session.on_completion");
rpc::rpc.mark_safe("pieces.sync.always_safe");
rpc::rpc.mark_safe("pieces.sync.timeout");
rpc::rpc.mark_safe("pieces.sync.timeout_safe");
rpc::rpc.mark_safe("pieces.preload.type");
rpc::rpc.mark_safe("pieces.preload.min_size");
rpc::rpc.mark_safe("pieces.preload.min_rate");
rpc::rpc.mark_safe("pieces.memory.max");
rpc::rpc.mark_safe("pieces.hash.on_completion");
}
+59 -55
View File
@@ -1,6 +1,7 @@
#include "config.h"
#include <fcntl.h>
#include <iterator>
#include <stdio.h>
#include <unistd.h>
#include <torrent/data/chunk_utils.h>
@@ -8,53 +9,24 @@
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "control.h"
#include "command_helpers.h"
#include "globals.h"
#include "setup.h"
#include "core/download.h"
#include "core/download_list.h"
#include "core/manager.h"
#include "rak/path.h"
#include "rpc/parse.h"
#include "rpc/parse_commands.h"
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
static const int log_flag_use_gz = 0x1;
static const int log_flag_append_pid = 0x2;
static const int log_flag_append_file = 0x4;
void
log_add_group_output_str(const char* group_name, const char* output_id) {
int log_group = torrent::option_find_string(torrent::OPTION_LOG_GROUP, group_name);
torrent::log_add_group_output(log_group, output_id);
}
torrent::Object
apply_log_open(int output_flags, const torrent::Object::list_type& args) {
if (args.size() < 2)
throw torrent::input_error("Invalid number of arguments.");
apply_log_open(int output_flags, const torrent::Object::list_type& raw_args) {
std::vector<std::string> args;
torrent::Object::list_const_iterator itr = args.begin();
std::string output_id = (itr++)->as_string();
std::string file_name = rak::path_expand((itr++)->as_string());
if ((output_flags & log_flag_append_pid)) {
char buffer[32];
snprintf(buffer, 32, ".%li", (long)getpid());
file_name += buffer;
}
bool append = (output_flags & log_flag_append_file);
if ((output_flags & log_flag_use_gz))
torrent::log_open_gz_file_output(output_id.c_str(), file_name.c_str(), append);
else
torrent::log_open_file_output(output_id.c_str(), file_name.c_str(), append);
while (itr != args.end())
log_add_group_output_str((itr++)->as_string().c_str(), output_id.c_str());
for (const auto& arg : raw_args)
args.push_back(arg.as_string());
apply_log_open_str(output_flags, args);
return torrent::Object();
}
@@ -62,10 +34,35 @@ torrent::Object
apply_log_add_output(const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Invalid number of arguments.");
log_add_group_output_str(args.front().as_string().c_str(),
args.back().as_string().c_str());
log_add_group_output_str(args.front().as_string(), args.back().as_string());
return torrent::Object();
}
torrent::Object
apply_log_print(const torrent::Object::list_type& args) {
if (args.size() < 2)
throw torrent::input_error("Invalid number of arguments.");
torrent::Object::value_type group;
if (args.front().is_value())
group = args.front().as_value();
else if (args.front().is_string())
group = torrent::option_find_string_str(torrent::OPTION_LOG_GROUP, args.front().as_string());
else
throw torrent::input_error("Invalid log group.");
if (group < 0 || group >= torrent::LOG_GROUP_MAX_SIZE)
throw torrent::input_error("Invalid log group.");
std::string message;
for (auto itr = std::next(args.begin()); itr != args.end(); ++itr)
rpc::print_object_std(&message, &*itr, 0);
torrent::log_groups[group].internal_print(message);
return torrent::Object();
}
@@ -86,7 +83,7 @@ apply_log(const torrent::Object::string_type& arg, int logType) {
}
if (!arg.empty()) {
int logFd = open(rak::path_expand(arg).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644);
int logFd = open(expand_path(arg).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644);
if (logFd < 0)
throw torrent::input_error("Could not open execute log file.");
@@ -118,6 +115,9 @@ log_vmmap_dump(const std::string& str) {
FILE* log_file = fopen(str.c_str(), "w");
if (log_file == NULL)
throw torrent::input_error("Could not open log file: " + str);
for (auto& all_mapping : all_mappings) {
fprintf(log_file, "%8p-%8p [%5llxk]\n", all_mapping.ptr, (char*)all_mapping.ptr + all_mapping.length, (long long unsigned int)(all_mapping.length / 1024));
}
@@ -128,19 +128,23 @@ log_vmmap_dump(const std::string& str) {
void
initialize_command_logging() {
CMD2_ANY_LIST ("log.open_file", std::bind(&apply_log_open, 0, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_gz_file", std::bind(&apply_log_open, log_flag_use_gz, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_file_pid", std::bind(&apply_log_open, log_flag_append_pid, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_gz_file_pid", std::bind(&apply_log_open, log_flag_append_pid | log_flag_use_gz, std::placeholders::_2));
CMD2_ANY_LIST ("log.append_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
CMD2_ANY_LIST ("log.append_gz_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_file", std::bind(&apply_log_open, 0, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_file.flush", std::bind(&apply_log_open, log_flag_flush, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_gz_file", std::bind(&apply_log_open, log_flag_use_gz, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_file_pid", std::bind(&apply_log_open, log_flag_append_pid, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_gz_file_pid", std::bind(&apply_log_open, log_flag_append_pid | log_flag_use_gz, std::placeholders::_2));
CMD2_ANY_LIST ("log.append_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
CMD2_ANY_LIST ("log.append_file.flush", std::bind(&apply_log_open, log_flag_append_file | log_flag_flush, std::placeholders::_2));
CMD2_ANY_LIST ("log.append_gz_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
CMD2_ANY_STRING_V("log.close", std::bind(&torrent::log_close_output_str, std::placeholders::_2));
CMD2_ANY_STRING_V("log.close", std::bind(&torrent::log_close_output_str, std::placeholders::_2));
CMD2_ANY_LIST ("log.add_output", std::bind(&apply_log_add_output, std::placeholders::_2));
CMD2_ANY_LIST ("log.add_output", std::bind(&apply_log_add_output, std::placeholders::_2));
CMD2_ANY_LIST ("log.print", std::bind(&apply_log_print, std::placeholders::_2));
CMD2_ANY_STRING ("log.execute", std::bind(&apply_log, std::placeholders::_2, 0));
CMD2_ANY_STRING ("log.vmmap.dump", std::bind(&log_vmmap_dump, std::placeholders::_2));
CMD2_ANY_STRING_V("log.rpc", std::bind(&ThreadWorker::set_rpc_log, worker_thread, std::placeholders::_2));
CMD2_REDIRECT ("log.xmlrpc", "log.rpc"); // For backwards compatibility
CMD2_ANY_STRING ("log.execute", std::bind(&apply_log, std::placeholders::_2, 0));
CMD2_ANY_STRING ("log.vmmap.dump", std::bind(&log_vmmap_dump, std::placeholders::_2));
CMD2_ANY_STRING_V("log.rpc", [](const auto&, const auto& str) { scgi_thread::set_rpc_log(str); });
CMD2_REDIRECT ("log.xmlrpc", "log.rpc"); // For backwards compatibility
}
+310 -107
View File
@@ -3,18 +3,25 @@
#include <functional>
#include <cstdio>
#include <unistd.h>
#include <rak/address_info.h>
#include <rak/path.h>
#include <torrent/torrent.h>
#include <torrent/rate.h>
#include <torrent/connection_manager.h>
#include <torrent/data/file_manager.h>
#include <torrent/download/resource_manager.h>
#include <torrent/net/http_stack.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 "globals.h"
#include "control.h"
#include "command_helpers.h"
#include "core/download.h"
#include "core/manager.h"
#include "rpc/scgi.h"
@@ -22,26 +29,121 @@
#include "rpc/parse.h"
#include "rpc/parse_commands.h"
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
#ifdef HAVE_SYSTEMD
#include <sys/socket.h>
#include <systemd/sd-daemon.h>
#endif
torrent::Object
listen_port_range() {
auto port_range = torrent::runtime::client_config()->listen_port_range();
return std::to_string(port_range.first) + "-" + std::to_string(port_range.second);
}
void
set_listen_port_range(const std::string& arg) {
unsigned int port_first{}, port_last{};
if (std::sscanf(arg.c_str(), "%i-%i", &port_first, &port_last) != 2)
throw torrent::input_error("Invalid port_range argument.");
if (port_first >= (1 << 16) || port_last >= (1 << 16))
throw torrent::input_error("Port range out-of-bounds.");
torrent::runtime::client_config()->set_listen_port_range(port_first, port_last);
}
torrent::Object
get_encryption() {
auto encryption_modes = torrent::runtime::network_config()->encryption_modes();
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_HANDSHAKE, encryption_modes.first) + "," +
torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_STREAM, encryption_modes.second);
}
torrent::Object
get_handshake_encryption() {
auto encryption_modes = torrent::runtime::network_config()->encryption_modes();
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_MODE, encryption_modes.first);
}
torrent::Object
get_stream_encryption() {
auto encryption_modes = torrent::runtime::network_config()->encryption_modes();
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_MODE, encryption_modes.second);
}
torrent::Object
apply_obsolete_encryption(const torrent::Object::list_type& args) {
torrent::encryption_mode handshake_mode{torrent::ENCRYPTION_MODE_ALLOW};
torrent::encryption_mode stream_mode{torrent::ENCRYPTION_MODE_ALLOW};
for (auto& itr : args) {
auto arg = itr.as_string();
if (arg == "none") {
handshake_mode = torrent::ENCRYPTION_MODE_DENY;
stream_mode = torrent::ENCRYPTION_MODE_DENY;
break;
} else if (arg == "allow_incoming") {
} else if (arg == "try_outgoing") {
} else if (arg == "require") {
handshake_mode = torrent::ENCRYPTION_MODE_REQUIRE;
} else if (arg == "require_RC4" || arg == "require_rc4") {
handshake_mode = torrent::ENCRYPTION_MODE_REQUIRE;
stream_mode = torrent::ENCRYPTION_MODE_REQUIRE;
break;
} else if (arg == "enable_retry") {
} else if (arg == "prefer_plaintext") {
} else {
throw torrent::input_error("Invalid encryption option: '" + arg + "'");
}
}
lt_log_print(torrent::LOG_WARN, "Obsolete encryption options used, use 'handshake_{deny,allow,prefer,require}, stream_{deny,allow,prefer,require}' instead.");
torrent::runtime::network_config()->set_encryption_modes(handshake_mode, stream_mode);
return {};
}
torrent::Object
apply_encryption(const torrent::Object::list_type& args) {
uint32_t options_mask = torrent::ConnectionManager::encryption_none;
if (args.empty())
throw torrent::input_error("No encryption options specified.");
for (const auto& arg : args) {
uint32_t opt = torrent::option_find_string(torrent::OPTION_ENCRYPTION, arg.as_string().c_str());
torrent::encryption_mode encryption_mode, handshake_mode, stream_mode;
if (opt == torrent::ConnectionManager::encryption_none)
options_mask = torrent::ConnectionManager::encryption_none;
else
options_mask |= opt;
if (args.size() == 1) {
try {
encryption_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_MODE, args.front().as_string()));
} catch (torrent::input_error& e) {
return apply_obsolete_encryption(args);
}
torrent::runtime::network_config()->set_encryption_modes(encryption_mode, encryption_mode);
return {};
}
torrent::connection_manager()->set_encryption_options(options_mask);
if (args.size() != 2)
return apply_obsolete_encryption(args);
return torrent::Object();
try {
handshake_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_HANDSHAKE, args.front().as_string()));
stream_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_STREAM, args.back().as_string()));
} catch (torrent::input_error& e) {
return apply_obsolete_encryption(args);
}
torrent::runtime::network_config()->set_encryption_modes(handshake_mode, stream_mode);
return {};
}
torrent::Object
@@ -51,13 +153,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::connection_manager()->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();
@@ -77,37 +177,36 @@ initialize_rpc_handlers() {
torrent::Object
apply_scgi(const std::string& arg, int type) {
if (worker_thread->scgi() != NULL)
if (scgi_thread::scgi() != nullptr)
throw torrent::input_error("SCGI already enabled.");
initialize_rpc_handlers();
rpc::SCgi* scgi = new rpc::SCgi;
torrent::sa_unique_ptr sa;
rak::address_info* ai = NULL;
rak::socket_address sa;
rak::socket_address* saPtr;
auto scgi = std::make_unique<rpc::SCgi>();
try {
int port, err;
char dummy;
int port{};
char dummy{};
char address[1024];
std::string path;
switch (type) {
case 1:
if (std::sscanf(arg.c_str(), ":%i%c", &port, &dummy) == 1) {
sa.sa_inet()->clear();
saPtr = &sa;
sa = torrent::sa_make_inet();
lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is open to any address and is a security risk");
} else if (std::sscanf(arg.c_str(), "%1023[^:]:%i%c", address, &port, &dummy) == 2 ||
std::sscanf(arg.c_str(), "[%64[^]]]:%i%c", address, &port, &dummy) == 2) { // [xx::xx]:port format
if ((err = rak::address_info::get_address_info(address,PF_UNSPEC, SOCK_STREAM, &ai)) != 0)
throw torrent::input_error("Could not bind address: " + std::string(rak::address_info::strerror(err)) + ".");
saPtr = ai->address();
try {
sa = torrent::sa_copy(torrent::sa_lookup_address(address, AF_UNSPEC).get());
} catch (torrent::input_error& e) {
throw torrent::input_error("Could not bind address: " + std::string(e.what()));
}
lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is bound to an address and might be a security risk");
@@ -118,33 +217,82 @@ apply_scgi(const std::string& arg, int type) {
if (port <= 0 || port >= (1 << 16))
throw torrent::input_error("Invalid port number.");
saPtr->set_port(port);
scgi->open_port(saPtr, saPtr->length(), rpc::call_command_value("network.scgi.dont_route"));
torrent::sap_set_port(sa, port);
scgi->open_port(sa.get(), torrent::sap_length(sa), rpc::call_command_value("network.scgi.dont_route"));
break;
case 2:
default:
path = rak::path_expand(arg);
path = expand_path(arg);
unlink(path.c_str());
scgi->open_named(path);
break;
}
if (ai != NULL) rak::address_info::free_address_info(ai);
} catch (torrent::local_error& e) {
if (ai != NULL) rak::address_info::free_address_info(ai);
delete scgi;
throw torrent::input_error(e.what());
}
worker_thread->set_scgi(scgi);
scgi_thread::set_scgi(scgi.release());
return torrent::Object();
}
torrent::Object
apply_scgi_systemd() {
#ifdef HAVE_SYSTEMD
if (scgi_thread::scgi() != nullptr)
throw torrent::input_error("SCGI already enabled.");
int n = sd_listen_fds(0);
if (n < 1)
throw torrent::input_error("No systemd socket(s) provided (sd_listen_fds returned " +
std::to_string(n) + ").");
// Iterate over all provided fds. Use the first listening stream socket;
// close the rest. The systemd docs say unused fds should be closed.
int selected_fd = -1;
for (int i = 0; i < n; i++) {
int fd = SD_LISTEN_FDS_START + i;
if (selected_fd != -1) {
::close(fd);
continue;
}
auto err = sd_is_socket(fd, AF_UNSPEC, SOCK_STREAM, 1);
if (err < 0) {
// Safe to ignore errors here - we just skip it and move on.
::close(fd);
continue;
}
if (err == 0) {
// Not the socket we're looking for.
::close(fd);
continue;
}
selected_fd = fd;
}
if (selected_fd == -1)
throw torrent::input_error("No listening stream socket found among systemd-provided fds.");
initialize_rpc_handlers();
rpc::SCgi* scgi = new rpc::SCgi;
scgi->open_fd(selected_fd);
scgi_thread::set_scgi(scgi);
return torrent::Object();
#else
throw torrent::input_error("Systemd SCGI endpoint is not supported.");
#endif
}
torrent::Object
apply_xmlrpc_dialect(const std::string& arg) {
int value;
@@ -164,87 +312,142 @@ apply_xmlrpc_dialect(const std::string& arg) {
void
initialize_command_network() {
auto cm = torrent::connection_manager();
auto fileManager = torrent::file_manager();
auto http_stack = torrent::net_thread::http_stack();
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_ANY ("protocol.pex", [](auto, auto) { return torrent::runtime::client_config()->is_pex_enabled(); });
CMD_ANY_VALUE_V ("protocol.pex.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_pex_enabled(value); });
CMD2_ANY ("network.listen.port", std::bind(&torrent::ConnectionManager::listen_port, cm));
CMD2_ANY ("network.listen.backlog", std::bind(&torrent::ConnectionManager::listen_backlog, cm));
CMD2_ANY_VALUE_V ("network.listen.backlog.set", std::bind(&torrent::ConnectionManager::set_listen_backlog, cm, std::placeholders::_2));
CMD_ANY_LIST ("protocol.encryption", [](auto, auto) { return get_encryption(); });
CMD_ANY_LIST ("protocol.encryption.set", [](auto, auto& args) { return apply_encryption(args); });
CMD_ANY_LIST ("protocol.encryption.handshake", [](auto, auto) { return get_handshake_encryption(); });
CMD_ANY_LIST ("protocol.encryption.stream", [](auto, auto) { return get_stream_encryption(); });
CMD2_VAR_BOOL ("protocol.pex", true);
CMD2_ANY_LIST ("protocol.encryption.set", std::bind(&apply_encryption, std::placeholders::_2));
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->active(); });
CMD2_ANY ("network.http.max_open", [http_stack](auto, auto) { return http_stack->max_active(); });
CMD2_ANY_VALUE_V ("network.http.max_open.set", [http_stack](auto, auto& value) { return http_stack->set_max_active(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", std::bind(&torrent::ConnectionManager::send_buffer_size, cm));
CMD2_ANY_VALUE_V ("network.send_buffer.size.set", std::bind(&torrent::ConnectionManager::set_send_buffer_size, cm, std::placeholders::_2));
CMD2_ANY ("network.receive_buffer.size", std::bind(&torrent::ConnectionManager::receive_buffer_size, cm));
CMD2_ANY_VALUE_V ("network.receive_buffer.size.set", std::bind(&torrent::ConnectionManager::set_receive_buffer_size, cm, std::placeholders::_2));
CMD2_ANY_STRING ("network.tos.set", std::bind(&apply_tos, std::placeholders::_2));
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", std::bind(&core::Manager::bind_address, control->core()));
CMD2_ANY_STRING_V("network.bind_address.set", std::bind(&core::Manager::set_bind_address, control->core(), std::placeholders::_2));
CMD2_ANY ("network.local_address", std::bind(&core::Manager::local_address, control->core()));
CMD2_ANY_STRING_V("network.local_address.set", std::bind(&core::Manager::set_local_address, control->core(), std::placeholders::_2));
CMD2_ANY ("network.proxy_address", std::bind(&core::Manager::proxy_address, control->core()));
CMD2_ANY_STRING_V("network.proxy_address.set", std::bind(&core::Manager::set_proxy_address, control->core(), std::placeholders::_2));
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.open_files", std::bind(&torrent::FileManager::open_files, fileManager));
CMD2_ANY ("network.max_open_files", std::bind(&torrent::FileManager::max_open_files, fileManager));
CMD2_ANY_VALUE_V ("network.max_open_files.set", std::bind(&torrent::FileManager::set_max_open_files, fileManager, std::placeholders::_2));
CMD2_ANY ("network.total_handshakes", std::bind(&torrent::total_handshakes));
CMD2_ANY ("network.open_sockets", std::bind(&torrent::ConnectionManager::size, cm));
CMD2_ANY ("network.max_open_sockets", std::bind(&torrent::ConnectionManager::max_size, cm));
CMD2_ANY_VALUE_V ("network.max_open_sockets.set", std::bind(&torrent::ConnectionManager::set_max_size, cm, std::placeholders::_2));
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_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);
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_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 ("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_VAR_BOOL ("network.rpc.use_xmlrpc", true);
CMD2_VAR_BOOL ("network.rpc.use_jsonrpc", true);
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.block.ipv4", std::bind(&torrent::ConnectionManager::is_block_ipv4, cm));
CMD2_ANY_VALUE_V ("network.block.ipv4.set", std::bind(&torrent::ConnectionManager::set_block_ipv4, cm, std::placeholders::_2));
CMD2_ANY ("network.block.ipv6", std::bind(&torrent::ConnectionManager::is_block_ipv6, cm));
CMD2_ANY_VALUE_V ("network.block.ipv6.set", std::bind(&torrent::ConnectionManager::set_block_ipv6, cm, std::placeholders::_2));
CMD2_ANY ("network.block.ipv4in6", std::bind(&torrent::ConnectionManager::is_block_ipv4in6, cm));
CMD2_ANY_VALUE_V ("network.block.ipv4in6.set", std::bind(&torrent::ConnectionManager::set_block_ipv4in6, cm, std::placeholders::_2));
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 ("network.prefer.ipv6", std::bind(&torrent::ConnectionManager::is_prefer_ipv6, cm));
CMD2_ANY_VALUE_V ("network.prefer.ipv6.set", std::bind(&torrent::ConnectionManager::set_prefer_ipv6, cm, std::placeholders::_2));
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); });
CMD_ANY ("network.rpc.use_xmlrpc", [](auto, auto) { return rpc::rpc.use_xmlrpc(); });
CMD_ANY_VALUE_V ("network.rpc.use_xmlrpc.set", [](auto, auto& arg) { return rpc::rpc.set_use_xmlrpc(arg); });
CMD_ANY ("network.rpc.use_jsonrpc", [](auto, auto) { return rpc::rpc.use_jsonrpc(); });
CMD_ANY_VALUE_V ("network.rpc.use_jsonrpc.set", [](auto, auto& arg) { return rpc::rpc.set_use_jsonrpc(arg); });
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");
rpc::rpc.mark_safe("network.port_range");
rpc::rpc.mark_safe("network.listen.port");
rpc::rpc.mark_safe("network.listen.backlog");
rpc::rpc.mark_safe("network.http.current_open");
rpc::rpc.mark_safe("network.http.max_cache_connections");
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.send_buffer.size");
rpc::rpc.mark_safe("network.receive_buffer.size");
rpc::rpc.mark_safe("network.bind_address");
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.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");
rpc::rpc.mark_safe("network.rpc.use_jsonrpc");
}
+42 -69
View File
@@ -1,83 +1,28 @@
// 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>
#include "config.h"
#include <rak/error_number.h>
#include <rak/path.h>
#include <rak/socket_address.h>
#include <rak/string_manip.h>
#include <torrent/bitfield.h>
#include <torrent/rate.h>
#include <torrent/net/socket_address.h>
#include <torrent/peer/connection_list.h>
#include <torrent/peer/peer.h>
#include <torrent/peer/peer_info.h>
#include <torrent/utils/string_manip.h>
#include "globals.h"
#include "command_helpers.h"
#include "control.h"
#include "core/manager.h"
#include "display/utils.h"
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
torrent::Object
retrieve_p_id(torrent::Peer* peer) {
const torrent::HashString* hashString = &peer->id();
return rak::transform_hex(hashString->begin(), hashString->end());
}
torrent::Object
retrieve_p_id_html(torrent::Peer* peer) {
const torrent::HashString* hashString = &peer->id();
return rak::copy_escape_html(hashString->begin(), hashString->end());
}
torrent::Object
retrieve_p_address(torrent::Peer* peer) {
const rak::socket_address *addr = rak::socket_address::cast_from(peer->peer_info()->socket_address());
auto sa = peer->peer_info()->socket_address();
auto addr_str = torrent::sa_addr_str(sa);
if (addr->family() == rak::socket_address::af_inet6)
return "[" + addr->address_str() + "]";
else
return addr->address_str();
}
if (sa->sa_family == AF_INET6)
return "[" + addr_str + "]";
torrent::Object
retrieve_p_port(torrent::Peer* peer) {
return rak::socket_address::cast_from(peer->peer_info()->socket_address())->port();
return addr_str;
}
torrent::Object
@@ -90,18 +35,20 @@ retrieve_p_client_version(torrent::Peer* peer) {
torrent::Object
retrieve_p_options_str(torrent::Peer* peer) {
return rak::transform_hex(peer->peer_info()->options(), peer->peer_info()->options() + 8);
return torrent::utils::transform_to_hex_str(peer->peer_info()->options(), peer->peer_info()->options() + 8);
}
torrent::Object
retrieve_p_completed_percent(torrent::Peer* peer) {
if (peer->bitfield()->size_bits() == 0)
return int64_t(0);
return (100 * peer->bitfield()->size_set()) / peer->bitfield()->size_bits();
}
void
initialize_command_peer() {
CMD2_PEER("p.id", std::bind(&retrieve_p_id, std::placeholders::_1));
CMD2_PEER("p.id_html", std::bind(&retrieve_p_id_html, std::placeholders::_1));
CMD2_PEER("p.id", [](auto* peer, auto) { return torrent::utils::transform_to_hex_str(peer->id()); });
CMD2_PEER("p.id_html", [](auto* peer, auto) { return torrent::utils::copy_escape_html_str(peer->id()); });
CMD2_PEER("p.client_version", std::bind(&retrieve_p_client_version, std::placeholders::_1));
CMD2_PEER("p.options_str", std::bind(&retrieve_p_options_str, std::placeholders::_1));
@@ -115,7 +62,7 @@ initialize_command_peer() {
CMD2_PEER("p.is_preferred", std::bind(&torrent::PeerInfo::is_preferred, std::bind(&torrent::Peer::peer_info, std::placeholders::_1)));
CMD2_PEER("p.address", std::bind(&retrieve_p_address, std::placeholders::_1));
CMD2_PEER("p.port", std::bind(&retrieve_p_port, std::placeholders::_1));
CMD2_PEER("p.port", [](auto* peer, auto) { return torrent::sa_port(peer->peer_info()->socket_address()); });
CMD2_PEER("p.completed_percent", std::bind(&retrieve_p_completed_percent, std::placeholders::_1));
@@ -133,4 +80,30 @@ initialize_command_peer() {
CMD2_PEER_V("p.disconnect", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, 0));
CMD2_PEER_V("p.disconnect_delayed", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, torrent::ConnectionList::disconnect_delayed));
rpc::rpc.mark_safe("p.address");
rpc::rpc.mark_safe("p.port");
rpc::rpc.mark_safe("p.client_version");
rpc::rpc.mark_safe("p.options_str");
rpc::rpc.mark_safe("p.id");
rpc::rpc.mark_safe("p.id_html");
rpc::rpc.mark_safe("p.up_rate");
rpc::rpc.mark_safe("p.up_total");
rpc::rpc.mark_safe("p.down_rate");
rpc::rpc.mark_safe("p.down_total");
rpc::rpc.mark_safe("p.peer_rate");
rpc::rpc.mark_safe("p.peer_total");
rpc::rpc.mark_safe("p.is_encrypted");
rpc::rpc.mark_safe("p.is_incoming");
rpc::rpc.mark_safe("p.is_obfuscated");
rpc::rpc.mark_safe("p.is_snubbed");
rpc::rpc.mark_safe("p.is_unwanted");
rpc::rpc.mark_safe("p.is_preferred");
rpc::rpc.mark_safe("p.snubbed");
rpc::rpc.mark_safe("p.snubbed.set");
rpc::rpc.mark_safe("p.banned");
rpc::rpc.mark_safe("p.banned.set");
rpc::rpc.mark_safe("p.completed_percent");
rpc::rpc.mark_safe("p.disconnect");
rpc::rpc.mark_safe("p.disconnect_delayed");
}
+104 -83
View File
@@ -1,44 +1,10 @@
// 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>
#include "config.h"
#include <cstdio>
#include <rak/address_info.h>
#include <torrent/throttle.h>
#include <torrent/rate.h>
#include <torrent/download/resource_manager.h>
#include <torrent/net/socket_address.h>
#include "core/manager.h"
#include "ui/root.h"
@@ -54,35 +20,47 @@ parse_address_range(const torrent::Object::list_type& args, torrent::Object::lis
unsigned int prefixWidth, ret;
char dummy;
char host[1024];
rak::address_info* ai;
torrent::sa_unique_ptr sa;
ret = std::sscanf(itr->as_string().c_str(), "%1023[^/]/%d%c", host, &prefixWidth, &dummy);
if (ret < 1 || rak::address_info::get_address_info(host, PF_INET, SOCK_STREAM, &ai) != 0)
throw torrent::input_error("Could not resolve host.");
if (ret < 1)
throw torrent::input_error("Invalid address/prefix.");
try {
sa = torrent::sa_copy(torrent::sa_lookup_address(host, AF_INET).get());
} catch (torrent::input_error& e) {
throw torrent::input_error("Could not resolve host: " + std::string(e.what()));
}
uint32_t begin, end;
rak::socket_address sa;
sa.copy(*ai->address(), ai->length());
begin = end = sa.sa_inet()->address_h();
rak::address_info::free_address_info(ai);
auto sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr);
begin = end = sa_addr;
if (ret == 2) {
if (++itr != args.end())
throw torrent::input_error("Cannot specify both network and range end.");
uint32_t netmask = std::numeric_limits<uint32_t>::max() << (32 - prefixWidth);
if (prefixWidth >= 32 || sa.sa_inet()->address_h() & ~netmask)
if (prefixWidth >= 32 || sa_addr & ~netmask)
throw torrent::input_error("Invalid address/prefix.");
end = sa.sa_inet()->address_h() | ~netmask;
end = sa_addr | ~netmask;
} else if (++itr != args.end()) {
if (rak::address_info::get_address_info(itr->as_string().c_str(), PF_INET, SOCK_STREAM, &ai) != 0)
throw torrent::input_error("Could not resolve host.");
try {
sa = torrent::sa_copy(torrent::sa_lookup_address(itr->as_string(), AF_INET).get());
sa.copy(*ai->address(), ai->length());
rak::address_info::free_address_info(ai);
end = sa.sa_inet()->address_h();
} catch (torrent::input_error& e) {
throw torrent::input_error("Could not resolve host: " + std::string(e.what()));
}
sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr);
end = sa_addr;
}
// convert to [begin, end) making sure the end doesn't overflow
@@ -92,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();
@@ -131,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;
@@ -149,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);
@@ -194,10 +160,10 @@ initialize_command_throttle() {
CMD2_VAR_VALUE ("throttle.max_downloads.div._val", 1);
CMD2_VAR_VALUE ("throttle.max_downloads.global._val", 0);
CMD2_REDIRECT_GENERIC("throttle.max_uploads.div", "throttle.max_uploads.div._val");
CMD2_REDIRECT_GENERIC("throttle.max_uploads.global", "throttle.max_uploads.global._val");
CMD2_REDIRECT_GENERIC("throttle.max_downloads.div", "throttle.max_downloads.div._val");
CMD2_REDIRECT_GENERIC("throttle.max_downloads.global", "throttle.max_downloads.global._val");
CMD2_REDIRECT ("throttle.max_uploads.div", "throttle.max_uploads.div._val");
CMD2_REDIRECT ("throttle.max_uploads.global", "throttle.max_uploads.global._val");
CMD2_REDIRECT ("throttle.max_downloads.div", "throttle.max_downloads.div._val");
CMD2_REDIRECT ("throttle.max_downloads.global", "throttle.max_downloads.global._val");
CMD2_ANY_VALUE ("throttle.max_uploads.div.set", std::bind(&throttle_update, "throttle.max_uploads.div._val.set", std::placeholders::_2));
CMD2_ANY_VALUE ("throttle.max_uploads.global.set", std::bind(&throttle_update, "throttle.max_uploads.global._val.set", std::placeholders::_2));
@@ -220,10 +186,65 @@ 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));
CMD2_ANY_STRING ("throttle.down.max", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_max));
CMD2_ANY_STRING ("throttle.down.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_rate));
rpc::rpc.mark_safe("throttle.unchoked_uploads");
rpc::rpc.mark_safe("throttle.max_unchoked_uploads");
rpc::rpc.mark_safe("throttle.unchoked_downloads");
rpc::rpc.mark_safe("throttle.max_unchoked_downloads");
rpc::rpc.mark_safe("throttle.min_peers.normal");
rpc::rpc.mark_safe("throttle.min_peers.normal.set");
rpc::rpc.mark_safe("throttle.max_peers.normal");
rpc::rpc.mark_safe("throttle.max_peers.normal.set");
rpc::rpc.mark_safe("throttle.min_peers.seed");
rpc::rpc.mark_safe("throttle.min_peers.seed.set");
rpc::rpc.mark_safe("throttle.max_peers.seed");
rpc::rpc.mark_safe("throttle.max_peers.seed.set");
rpc::rpc.mark_safe("throttle.min_uploads");
rpc::rpc.mark_safe("throttle.min_uploads.set");
rpc::rpc.mark_safe("throttle.max_uploads");
rpc::rpc.mark_safe("throttle.max_uploads.set");
rpc::rpc.mark_safe("throttle.min_downloads");
rpc::rpc.mark_safe("throttle.min_downloads.set");
rpc::rpc.mark_safe("throttle.max_downloads");
rpc::rpc.mark_safe("throttle.max_downloads.set");
rpc::rpc.mark_safe("throttle.max_uploads.div");
rpc::rpc.mark_safe("throttle.max_uploads.div.set");
rpc::rpc.mark_safe("throttle.max_uploads.div._val");
rpc::rpc.mark_safe("throttle.max_uploads.div._val.set");
rpc::rpc.mark_safe("throttle.max_uploads.global");
rpc::rpc.mark_safe("throttle.max_uploads.global.set");
rpc::rpc.mark_safe("throttle.max_uploads.global._val");
rpc::rpc.mark_safe("throttle.max_uploads.global._val.set");
rpc::rpc.mark_safe("throttle.max_downloads.div");
rpc::rpc.mark_safe("throttle.max_downloads.div.set");
rpc::rpc.mark_safe("throttle.max_downloads.div._val");
rpc::rpc.mark_safe("throttle.max_downloads.div._val.set");
rpc::rpc.mark_safe("throttle.max_downloads.global");
rpc::rpc.mark_safe("throttle.max_downloads.global.set");
rpc::rpc.mark_safe("throttle.max_downloads.global._val");
rpc::rpc.mark_safe("throttle.max_downloads.global._val.set");
rpc::rpc.mark_safe("throttle.global_up.rate");
rpc::rpc.mark_safe("throttle.global_up.total");
rpc::rpc.mark_safe("throttle.global_up.max_rate");
rpc::rpc.mark_safe("throttle.global_up.max_rate.set");
rpc::rpc.mark_safe("throttle.global_up.max_rate.set_kb");
rpc::rpc.mark_safe("throttle.global_down.rate");
rpc::rpc.mark_safe("throttle.global_down.total");
rpc::rpc.mark_safe("throttle.global_down.max_rate");
rpc::rpc.mark_safe("throttle.global_down.max_rate.set");
rpc::rpc.mark_safe("throttle.global_down.max_rate.set_kb");
rpc::rpc.mark_safe("throttle.up.max");
rpc::rpc.mark_safe("throttle.up.rate");
rpc::rpc.mark_safe("throttle.down.max");
rpc::rpc.mark_safe("throttle.down.rate");
}
+89 -61
View File
@@ -3,9 +3,10 @@
#include <cassert>
#include <cstdio>
#include <netdb.h>
#include <rak/address_info.h>
#include <rak/error_number.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>
@@ -28,9 +29,6 @@ tracker_set_enabled(torrent::tracker::Tracker* tracker, bool state) {
torrent::Object
apply_dht_add_node(const std::string& arg) {
if (!torrent::dht_controller()->is_valid())
throw torrent::input_error("DHT not enabled.");
int port;
char dummy;
char host[1024];
@@ -45,21 +43,23 @@ apply_dht_add_node(const std::string& arg) {
if (port < 1 || port > 65535)
throw torrent::input_error("Invalid port number.");
assert(std::this_thread::get_id() == torrent::main_thread::thread()->thread_id());
assert(std::this_thread::get_id() == torrent::main_thread::thread_id());
auto host_str = std::string(host);
// 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;
}
lt_log_print(torrent::LOG_DHT_CONTROLLER, "dht.add_node : %s", host_str.c_str());
torrent::dht_controller()->add_node(sa.get(), port);
torrent::runtime::network_manager()->dht_add_bootstrap_node(host_str.c_str(), port);
});
return torrent::Object();
@@ -71,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(); });
@@ -91,62 +81,100 @@ 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);
CMD2_ANY_STRING_V ("dht.mode.set", std::bind(&core::DhtManager::set_mode, control->dht_manager(), std::placeholders::_2));
// TODO: This should query DhtController.
CMD2_VAR_VALUE ("dht.port", int64_t(6881));
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, control->dht_manager()));
CMD2_ANY ("dht.throttle.name", std::bind(&core::DhtManager::throttle_name, control->dht_manager()));
CMD2_ANY_STRING_V ("dht.throttle.name.set", std::bind(&core::DhtManager::set_throttle_name, control->dht_manager(), std::placeholders::_2));
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", [](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", [](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", [](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");
rpc::rpc.mark_safe("t.id");
rpc::rpc.mark_safe("t.type");
rpc::rpc.mark_safe("t.is_usable");
rpc::rpc.mark_safe("t.is_busy");
rpc::rpc.mark_safe("t.is_enabled");
rpc::rpc.mark_safe("t.is_enabled.set");
rpc::rpc.mark_safe("t.is_extra_tracker");
rpc::rpc.mark_safe("t.is_open");
rpc::rpc.mark_safe("t.normal_interval");
rpc::rpc.mark_safe("t.scrape_time_last");
rpc::rpc.mark_safe("t.scrape_counter");
rpc::rpc.mark_safe("t.success_time_last");
rpc::rpc.mark_safe("t.success_counter");
rpc::rpc.mark_safe("t.failed_time_last");
rpc::rpc.mark_safe("t.failed_counter");
rpc::rpc.mark_safe("t.activity_time_last");
rpc::rpc.mark_safe("t.activity_time_next");
rpc::rpc.mark_safe("t.scrape_complete");
rpc::rpc.mark_safe("t.scrape_incomplete");
rpc::rpc.mark_safe("t.scrape_downloaded");
rpc::rpc.mark_safe("dht.mode.set");
rpc::rpc.mark_safe("dht.port");
rpc::rpc.mark_safe("dht.override_port");
rpc::rpc.mark_safe("dht.add_node");
rpc::rpc.mark_safe("dht.statistics");
rpc::rpc.mark_safe("trackers.numwant");
rpc::rpc.mark_safe("trackers.use_udp");
}
+109 -60
View File
@@ -4,7 +4,6 @@
#include <ctime>
#include <regex>
#include <rak/algorithm.h>
#include <torrent/utils/log.h>
#include "core/manager.h"
@@ -473,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;
@@ -501,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
@@ -730,6 +736,26 @@ apply_arith_count(const torrent::Object::list_type& args) {
return val;
}
// Get the median of an unordered set of numbers of arbitrary
// type by modifing the underlying dataset
template <typename T = double, typename _InputIter>
T median(_InputIter __first, _InputIter __last) {
T __med;
unsigned int __size = __last - __first;
unsigned int __middle = __size / 2;
_InputIter __target1 = __first + __middle;
std::nth_element(__first, __target1, __last);
__med = *__target1;
if (__size % 2 == 0) {
_InputIter __target2 = std::max_element(__first, __target1);
__med = (__med + *__target2) / 2.0;
}
return __med;
}
int64_t
apply_arith_other(const char* op, const torrent::Object::list_type& args) {
if (args.size() == 0)
@@ -739,7 +765,8 @@ apply_arith_other(const char* op, const torrent::Object::list_type& args) {
return (int64_t)(apply_math_basic(op, std::plus<int64_t>(), args) / apply_arith_count(args));
} else if (strcmp(op, "median") == 0) {
std::vector<int64_t> result = as_vector(args);
return (int64_t)rak::median(result.begin(), result.end());
return (int64_t)median(result.begin(), result.end());
} else {
throw torrent::input_error("Wrong operation supplied to apply_arith_other.");
}
@@ -775,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);
@@ -888,4 +913,28 @@ initialize_command_ui() {
return control->object_storage()->get_str(display::color_vars[color_id]);
});
}
rpc::rpc.mark_safe("view.set_visible");
rpc::rpc.mark_safe("view.set_not_visible");
rpc::rpc.mark_safe("cat");
rpc::rpc.mark_safe("if");
rpc::rpc.mark_safe("branch");
rpc::rpc.mark_safe("and");
rpc::rpc.mark_safe("or");
rpc::rpc.mark_safe("not");
rpc::rpc.mark_safe("value");
rpc::rpc.mark_safe("compare");
rpc::rpc.mark_safe("elapsed.less");
rpc::rpc.mark_safe("elapsed.greater");
rpc::rpc.mark_safe("convert.gm_time");
rpc::rpc.mark_safe("convert.gm_date");
rpc::rpc.mark_safe("convert.time");
rpc::rpc.mark_safe("convert.date");
rpc::rpc.mark_safe("convert.elapsed_time");
rpc::rpc.mark_safe("convert.kb");
rpc::rpc.mark_safe("convert.mb");
rpc::rpc.mark_safe("convert.xb");
rpc::rpc.mark_safe("convert.throttle");
}
+54 -32
View File
@@ -4,12 +4,12 @@
#include <unistd.h>
#include <sys/stat.h>
#include <torrent/connection_manager.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"
#include "core/download_store.h"
#include "core/http_queue.h"
#include "core/manager.h"
#include "core/view_manager.h"
@@ -26,7 +26,9 @@
#include "rpc/lua.h"
#include "rpc/parse_commands.h"
#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,21 +38,23 @@ 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_viewManager = std::make_unique<core::ViewManager>();
m_dhtManager = std::make_unique<core::DhtManager>();
m_core = std::make_unique<core::Manager>();
m_view_manager = std::make_unique<core::ViewManager>();
m_dht_manager = std::make_unique<core::DhtManager>();
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); });
}
Control::~Control() {
m_viewManager.reset();
m_view_manager.reset();
m_ui.reset();
m_display.reset();
@@ -58,23 +62,20 @@ Control::~Control() {
void
Control::initialize() {
session_thread::thread()->start_thread();
scgi_thread::thread()->start_thread();
display::Canvas::initialize();
display::Window::slot_schedule([this](display::Window* w, std::chrono::microseconds t) { m_display->schedule(w, t); });
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->download_store()->enable(rpc::call_command_value("session.use_lock"));
m_core->set_hashing_view(*m_viewManager->find_throw("hashing"));
m_dhtManager->set_port(torrent::connection_manager()->listen_port());
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,12 +83,19 @@ 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());
}
if(!display::Canvas::daemon())
m_inputStdin->remove();
m_core->download_store()->disable();
m_directory_events->close();
if (scgi_thread::thread()->is_active())
scgi_thread::thread()->stop_thread_wait();
// Wait for all session files to be written.
session_thread::manager()->flush_all_pending_builds();
session_thread::thread()->stop_thread_wait();
m_ui->cleanup();
m_core->cleanup();
@@ -105,7 +113,7 @@ Control::cleanup_exception() {
bool
Control::is_shutdown_completed() {
if (!m_shutdownQuick || worker_thread->is_active())
if (!m_shutdown_quick)
return false;
// Tracker requests can be disowned, so wait for these to
@@ -114,21 +122,24 @@ Control::is_shutdown_completed() {
// TODO: We keep http requests in the queue for a while after, so improve this check to ignore
// those.
if (torrent::net_thread::http_stack()->active() != 0 || !core()->http_queue()->empty())
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 (!m_shutdownQuick) {
if (worker_thread->is_active())
worker_thread->stop_thread_wait();
if (scgi_thread::thread()->is_active())
scgi_thread::thread()->stop_thread_wait();
torrent::connection_manager()->listen_close();
if (!m_shutdown_quick) {
torrent::runtime::network_manager()->listen_close();
torrent::runtime::shutdown();
m_directory_events->close();
m_core->shutdown(false);
@@ -137,13 +148,24 @@ Control::handle_shutdown() {
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown, 5s);
} else {
if (worker_thread->is_active())
worker_thread->stop_thread_wait();
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);
}
+24 -12
View File
@@ -6,7 +6,7 @@
#include <memory>
#include <sys/types.h>
#include <torrent/torrent.h>
#include <torrent/utils/scheduler.h>
#include <torrent/system/scheduler.h>
namespace ui {
class Root;
@@ -38,27 +38,32 @@ 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_viewManager.get(); }
core::DhtManager* dht_manager() { return m_dhtManager.get(); }
core::ViewManager* view_manager() { return m_view_manager.get(); }
core::DhtManager* dht_manager() { return m_dht_manager.get(); }
ui::Root* ui() { return m_ui.get(); }
display::Manager* display() { return m_display.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++; }
@@ -82,8 +88,8 @@ private:
void operator = (const Control&);
std::unique_ptr<core::Manager> m_core;
std::unique_ptr<core::ViewManager> m_viewManager;
std::unique_ptr<core::DhtManager> m_dhtManager;
std::unique_ptr<core::ViewManager> m_view_manager;
std::unique_ptr<core::DhtManager> m_dht_manager;
std::unique_ptr<ui::Root> m_ui;
std::unique_ptr<display::Manager> m_display;
@@ -94,16 +100,22 @@ 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{};
mode_t m_umask;
std::string m_workingDirectory;
torrent::utils::SchedulerEntry m_task_shutdown;
torrent::system::SchedulerEntry m_task_shutdown;
torrent::system::SchedulerEntry m_task_shutdown_clear_requests;
std::atomic<bool> m_shutdownReceived{};
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
+78 -75
View File
@@ -1,22 +1,23 @@
#include "config.h"
#include "dht_manager.h"
#include <fstream>
#include <sstream>
#include <torrent/object.h>
#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>
#include "rpc/parse_commands.h"
#include "globals.h"
#include "control.h"
#include "dht_manager.h"
#include "download.h"
#include "download_store.h"
#include "globals.h"
#include "manager.h"
#include "rpc/parse_commands.h"
#include "session/session_manager.h"
#define LT_LOG(log_fmt, ...) \
lt_log_print_subsystem(torrent::LOG_DHT_CONTROLLER, "dht_manager", log_fmt, __VA_ARGS__);
@@ -33,25 +34,15 @@ DhtManager::~DhtManager() {
torrent::this_thread::scheduler()->erase(&m_stop_timeout);
}
void
DhtManager::set_port(uint16_t port) {
if (torrent::dht_controller()->is_active()) {
LT_LOG_ERROR("cannot change port while DHT is active", 0);
throw torrent::input_error("cannot change port while DHT is active");
}
m_port = port;
}
void
DhtManager::load_dht_cache() {
if (m_start == dht_disable || !control->core()->download_store()->is_enabled()) {
if (m_start == dht_disable || !session_thread::manager()->is_used()) {
LT_LOG("ignoring cache file", 0);
return;
}
std::string cache_filename = control->core()->download_store()->path() + "rtorrent.dht_cache";
std::fstream cache_stream(cache_filename.c_str(), std::ios::in | std::ios::binary);
auto cache_filename = session_thread::manager()->path() + "rtorrent.dht_cache";
auto cache_stream = std::fstream(cache_filename.c_str(), std::ios::in | std::ios::binary);
torrent::Object cache = torrent::Object::create_map();
@@ -71,7 +62,7 @@ DhtManager::load_dht_cache() {
LT_LOG("could not open cache file (path:%s)", cache_filename.c_str());
}
torrent::dht_controller()->initialize(cache);
torrent::runtime::network_manager()->dht_controller()->initialize(cache);
if (m_start == dht_on)
start_dht();
@@ -81,33 +72,22 @@ void
DhtManager::start_dht() {
torrent::this_thread::scheduler()->erase(&m_stop_timeout);
if (!torrent::dht_controller()->is_valid()) {
if (!torrent::runtime::network_manager()->is_dht_valid()) {
LT_LOG("server start skipped, manager is uninitialized", 0);
return;
}
if (torrent::dht_controller()->is_active()) {
if (torrent::runtime::network_manager()->is_dht_active()) {
LT_LOG("server start skipped, already active", 0);
return;
}
torrent::ThrottlePair throttles = control->core()->get_throttle(m_throttleName);
torrent::dht_controller()->set_upload_throttle(throttles.first);
torrent::dht_controller()->set_download_throttle(throttles.second);
if (m_port <= 0) {
LT_LOG("server start skipped, port not set", 0);
return;
}
LT_LOG("starting server : port=%" PRIu16, m_port);
if (!torrent::dht_controller()->start(m_port)) {
if (!torrent::runtime::network_manager()->dht_controller()->start()) {
m_start = dht_off;
return;
}
torrent::dht_controller()->reset_statistics();
torrent::runtime::network_manager()->dht_controller()->reset_statistics();
m_update_timeout.slot() = std::bind(&DhtManager::update, this);
@@ -126,28 +106,31 @@ DhtManager::stop_dht() {
torrent::this_thread::scheduler()->erase(&m_update_timeout);
torrent::this_thread::scheduler()->erase(&m_stop_timeout);
if (torrent::dht_controller()->is_active()) {
if (torrent::runtime::network_manager()->is_dht_active()) {
LT_LOG("stopping server", 0);
log_statistics(true);
torrent::dht_controller()->stop();
torrent::runtime::network_manager()->dht_controller()->stop();
}
}
void
DhtManager::save_dht_cache() {
if (!control->core()->download_store()->is_enabled() || !torrent::dht_controller()->is_valid())
if (!session_thread::manager()->is_used())
return;
std::string filename = control->core()->download_store()->path() + "rtorrent.dht_cache";
std::string filename_tmp = filename + ".new";
std::fstream cache_file(filename_tmp.c_str(), std::ios::out | std::ios::trunc);
if (!torrent::runtime::network_manager()->is_dht_valid())
return;
auto filename = session_thread::manager()->path() + "rtorrent.dht_cache";
auto filename_tmp = filename + ".new";
auto cache_file = std::fstream(filename_tmp.c_str(), std::ios::out | std::ios::trunc);
if (!cache_file.is_open())
return;
torrent::Object cache = torrent::Object::create_map();
cache_file << *torrent::dht_controller()->store_cache(&cache);
cache_file << *torrent::runtime::network_manager()->dht_controller()->store_cache(&cache);
if (!cache_file.good())
return;
@@ -158,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();
@@ -176,9 +174,26 @@ 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::dht_controller()->is_active())
if (!torrent::runtime::network_manager()->is_dht_active())
throw torrent::internal_error("DhtManager::update called with DHT inactive.");
if (m_start == dht_auto && !m_stop_timeout.is_scheduled()) {
@@ -203,17 +218,17 @@ DhtManager::update() {
bool
DhtManager::log_statistics(bool force) {
auto stats = torrent::dht_controller()->get_statistics();
auto stats = torrent::runtime::network_manager()->dht_controller()->get_statistics();
// Check for firewall problems.
if (stats.cycle > 2 && stats.queries_sent - m_dhtPrevQueriesSent > 100 && stats.queries_received == m_dhtPrevQueriesReceived) {
// We should have had clients ping us at least but have received
// nothing, that means the UDP port is probably unreachable.
if (torrent::dht_controller()->is_receiving_requests())
if (torrent::runtime::network_manager()->is_dht_active_and_receiving_requests())
LT_LOG("listening port appears to be unreachable, no queries received", 0);
torrent::dht_controller()->set_receive_requests(false);
torrent::runtime::network_manager()->dht_controller()->set_receive_requests(false);
}
if (stats.queries_sent - m_dhtPrevQueriesSent > stats.num_nodes * 2 + 20 && stats.replies_received == m_dhtPrevRepliesReceived) {
@@ -228,7 +243,7 @@ DhtManager::log_statistics(bool force) {
m_warned = false;
if (stats.queries_received > m_dhtPrevQueriesReceived)
torrent::dht_controller()->set_receive_requests(true);
torrent::runtime::network_manager()->dht_controller()->set_receive_requests(true);
// Nothing to log while bootstrapping, but check again every minute.
if (stats.cycle <= 1) {
@@ -250,13 +265,11 @@ DhtManager::log_statistics(bool force) {
if ((force && stats.cycle != m_dhtPrevCycle) || stats.cycle == 3 || stats.cycle > m_dhtPrevCycle + 7) {
char buffer[256];
snprintf(buffer, sizeof(buffer),
"DHT statistics: %d queries in, %d queries out, %d replies received, %lld bytes read, %lld bytes sent, "
"DHT statistics: %d queries in, %d queries out, %d replies received, "
"%d known nodes in %d buckets, %d peers (highest: %d) tracked in %d torrents.",
stats.queries_received - m_dhtPrevQueriesReceived,
stats.queries_sent - m_dhtPrevQueriesSent,
stats.replies_received - m_dhtPrevRepliesReceived,
(long long unsigned int)(stats.down_rate.total() - m_dhtPrevBytesDown),
(long long unsigned int)(stats.up_rate.total() - m_dhtPrevBytesUp),
stats.num_nodes,
stats.num_buckets,
stats.num_peers,
@@ -269,8 +282,6 @@ DhtManager::log_statistics(bool force) {
m_dhtPrevQueriesSent = stats.queries_sent;
m_dhtPrevRepliesReceived = stats.replies_received;
m_dhtPrevQueriesReceived = stats.queries_received;
m_dhtPrevBytesUp = stats.up_rate.total();
m_dhtPrevBytesDown = stats.down_rate.total();
}
return false;
@@ -281,11 +292,11 @@ DhtManager::dht_statistics() {
torrent::Object dhtStats = torrent::Object::create_map();
dhtStats.insert_key("dht", dht_settings[m_start]);
dhtStats.insert_key("active", torrent::dht_controller()->is_active());
dhtStats.insert_key("throttle", m_throttleName);
dhtStats.insert_key("active", torrent::runtime::network_manager()->is_dht_active());
dhtStats.insert_key("throttle", "");
if (torrent::dht_controller()->is_active()) {
auto stats = torrent::dht_controller()->get_statistics();
if (torrent::runtime::network_manager()->is_dht_active()) {
auto stats = torrent::runtime::network_manager()->dht_controller()->get_statistics();
dhtStats.insert_key("cycle", stats.cycle);
dhtStats.insert_key("queries_received", stats.queries_received);
@@ -293,8 +304,8 @@ DhtManager::dht_statistics() {
dhtStats.insert_key("replies_received", stats.replies_received);
dhtStats.insert_key("errors_received", stats.errors_received);
dhtStats.insert_key("errors_caught", stats.errors_caught);
dhtStats.insert_key("bytes_read", stats.down_rate.total());
dhtStats.insert_key("bytes_written", stats.up_rate.total());
dhtStats.insert_key("bytes_read", int64_t());
dhtStats.insert_key("bytes_written", int64_t());
dhtStats.insert_key("nodes", stats.num_nodes);
dhtStats.insert_key("buckets", stats.num_buckets);
dhtStats.insert_key("peers", stats.num_peers);
@@ -305,12 +316,4 @@ DhtManager::dht_statistics() {
return dhtStats;
}
void
DhtManager::set_throttle_name(const std::string& throttleName) {
if (torrent::dht_controller()->is_active())
throw torrent::input_error("Cannot set DHT throttle while active.");
m_throttleName = throttleName;
}
}
+14 -20
View File
@@ -2,16 +2,18 @@
#define RTORRENT_CORE_DHT_MANAGER_H
#include <torrent/object.h>
#include <torrent/utils/scheduler.h>
#include <torrent/system/scheduler.h>
namespace core {
class DhtManager {
public:
~DhtManager();
static constexpr int dht_disable = 0;
static constexpr int dht_off = 1;
static constexpr int dht_auto = 2;
static constexpr int dht_on = 3;
uint16_t port() const { return m_port; }
void set_port(uint16_t port);
~DhtManager();
void load_dht_cache();
void save_dht_cache();
@@ -21,19 +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_throttle_name(const std::string& throttleName);
const std::string& throttle_name() const { return m_throttleName; }
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);
@@ -45,15 +42,12 @@ private:
uint64_t m_dhtPrevBytesUp;
uint64_t m_dhtPrevBytesDown;
torrent::utils::SchedulerEntry m_update_timeout;
torrent::utils::SchedulerEntry m_stop_timeout;
torrent::system::SchedulerEntry m_update_timeout;
torrent::system::SchedulerEntry m_stop_timeout;
bool m_warned{};
bool m_set_by_user{};
int m_start{dht_off};
uint16_t m_port{0};
std::string m_throttleName;
};
}
+17 -30
View File
@@ -1,25 +1,25 @@
#include "config.h"
#include "core/download.h"
#include <list>
#include <rak/file_stat.h>
#include <rak/path.h>
#include <torrent/exceptions.h>
#include <torrent/rate.h>
#include <torrent/torrent.h>
#include <torrent/tracker/tracker.h>
#include <torrent/data/file.h>
#include <torrent/data/file_list.h>
#include <torrent/utils/file_stat.h>
#include "rpc/parse_commands.h"
#include "control.h"
#include "download.h"
#include "manager.h"
#include "core/manager.h"
namespace core {
Download::Download(download_type d) :
m_download(d) {
Download::Download(download_type d)
: m_download(d) {
m_download.info()->signal_tracker_success().push_back(std::bind(&Download::receive_tracker_msg, this, ""));
m_download.info()->signal_tracker_failed().push_back(std::bind(&Download::receive_tracker_msg, this, std::placeholders::_1));
@@ -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,44 +88,46 @@ 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
Download::set_root_directory(const std::string& path) {
// If the download is open, hashed and has completed chunks make
// sure to verify that the download files are still present.
//
//
// This should ensure that no one tries to set the destination
// directory 'after' moving files. In cases where the user wants to
// override this behavior the download must first be closed or
// 'd.directory_base.set' may be used.
rak::file_stat file_stat;
torrent::FileList* file_list = m_download.file_list();
torrent::utils::FileStat file_stat;
torrent::FileList* file_list = m_download.file_list();
if (is_hash_checked() && file_list->completed_chunks() != 0 &&
(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));
control->core()->download_list()->close_directly(this);
throw torrent::input_error("Cannot change the directory of an open download atter the files have been moved.");
throw torrent::input_error("Cannot change the directory of an open download after the files have been moved.");
}
control->core()->download_list()->close_directly(this);
file_list->set_root_dir(rak::path_expand(path));
file_list->set_root_dir(expand_path(path));
bencode()->get_key("rtorrent").insert_key("directory", path);
}
+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{};
};
+41 -32
View File
@@ -7,7 +7,6 @@
#include <functional>
#include <sstream>
#include <stdexcept>
#include <rak/path.h>
#include <torrent/utils/log.h>
#include <torrent/utils/resume.h>
#include <torrent/object.h>
@@ -16,11 +15,12 @@
#include <torrent/rate.h>
#include <torrent/data/file_utils.h>
#include <torrent/net/http_stack.h>
#include <torrent/runtime/client_config.h>
#include <torrent/utils/string_manip.h>
#include "control.h"
#include "globals.h"
#include "core/download.h"
#include "core/download_store.h"
#include "core/http_queue.h"
#include "core/manager.h"
#include "rpc/parse_commands.h"
@@ -105,40 +105,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(rak::path_expand(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
@@ -159,8 +161,8 @@ DownloadFactory::receive_commit() {
void
DownloadFactory::receive_success() {
auto rtorrent_object = download_factory_load_stream((rak::path_expand(m_uri) + ".rtorrent").c_str());
auto libtorrent_resume_object = download_factory_load_stream((rak::path_expand(m_uri) + ".libtorrent_resume").c_str());
auto rtorrent_object = download_factory_load_stream((expand_path(m_uri) + ".rtorrent").c_str());
auto libtorrent_resume_object = download_factory_load_stream((expand_path(m_uri) + ".libtorrent_resume").c_str());
uint32_t tracker_key;
@@ -239,9 +241,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);
@@ -255,15 +254,25 @@ DownloadFactory::receive_success() {
rpc::call_command_value("system.file.split_size"),
rpc::call_command_string("system.file.split_suffix"));
if (!rtorrent->has_key_string("directory"))
rpc::call_command("d.directory.set", m_variables["directory"], rpc::make_target(download));
else
if (rtorrent->has_key_string("directory")) {
rpc::call_command("d.directory_base.set", rtorrent->get_key("directory"), rpc::make_target(download));
} else if (download->download()->info()->is_meta_download()) {
auto& metadata_path = control->core()->magnet_path();
if (!metadata_path.empty())
rpc::call_command("d.directory.set", metadata_path, rpc::make_target(download));
else
rpc::call_command("d.directory.set", session_thread::session_path(), rpc::make_target(download));
} else {
rpc::call_command("d.directory.set", m_variables["directory"], rpc::make_target(download));
}
if (!m_session && m_variables["tied_to_file"].as_value())
rpc::call_command("d.tied_to_file.set", m_uri.empty() ? m_variables["tied_file"] : m_uri, rpc::make_target(download));
rpc::call_command("d.peer_exchange.set", rpc::call_command_value("protocol.pex"), rpc::make_target(download));
rpc::call_command("d.peer_exchange.set", torrent::runtime::client_config()->is_pex_enabled(), rpc::make_target(download));
torrent::resume_load_addresses(*download->download(), resumeObject);
torrent::resume_load_file_priorities(*download->download(), resumeObject);
@@ -319,7 +328,7 @@ void
DownloadFactory::log_created(Download* download, torrent::Object* rtorrent) {
std::stringstream dump;
dump << "info_hash = " << torrent::hash_string_to_hex_str(download->info()->hash()) << std::endl;
dump << "info_hash = " << torrent::utils::transform_to_hex_str(download->info()->hash()) << std::endl;
dump << "session = " << (m_session ? "true" : "false") << std::endl;
if (download->download()->info()->is_meta_download())
+4 -4
View File
@@ -9,7 +9,7 @@
#include <iosfwd>
#include <torrent/object.h>
#include <torrent/utils/scheduler.h>
#include <torrent/system/scheduler.h>
#include "http_queue.h"
@@ -79,9 +79,9 @@ private:
command_list_type m_commands;
torrent::Object::map_type m_variables;
slot_void m_slot_finished;
torrent::utils::SchedulerEntry m_task_load;
torrent::utils::SchedulerEntry m_task_commit;
slot_void m_slot_finished;
torrent::system::SchedulerEntry m_task_load;
torrent::system::SchedulerEntry m_task_commit;
};
bool is_network_uri(const std::string& uri);
+35 -26
View File
@@ -1,9 +1,9 @@
#include "config.h"
#include <algorithm>
#include <cstring>
#include <fstream>
#include <iostream>
#include <rak/string_manip.h>
#include <torrent/data/file.h>
#include <torrent/utils/resume.h>
#include <torrent/exceptions.h>
@@ -13,6 +13,7 @@
#include <torrent/object_stream.h>
#include <torrent/torrent.h>
#include <torrent/utils/log.h>
#include <torrent/utils/string_manip.h>
#include "rpc/parse_commands.h"
@@ -22,10 +23,10 @@
#include "view.h"
#include "view_manager.h"
#include "dht_manager.h"
#include "download.h"
#include "download_list.h"
#include "download_store.h"
#include "core/dht_manager.h"
#include "core/download.h"
#include "core/download_list.h"
#include "session/session_manager.h"
#include "ui/root.h"
#define DL_TRIGGER_EVENT(download, event_name) \
@@ -68,10 +69,8 @@ DownloadList::clear() {
void
DownloadList::session_save() {
unsigned int c = std::count_if(begin(), end(), [&](Download* d) { return control->core()->download_store()->save_resume(d); });
if (c != size())
lt_log_print(torrent::LOG_ERROR, "Failed to save session torrents.");
for (auto& download : *this)
session_thread::manager()->save_resume_download(download);
control->dht_manager()->save_dht_cache();
control->ui()->save_input_history();
@@ -84,10 +83,13 @@ DownloadList::find(const torrent::HashString& hash) {
DownloadList::iterator
DownloadList::find_hex(const char* hash) {
if (strlen(hash) < 40)
return end();
torrent::HashString key;
for (torrent::HashString::iterator itr = key.begin(), last = key.end(); itr != last; itr++, hash += 2)
*itr = (rak::hexchar_to_value(*hash) << 4) + rak::hexchar_to_value(*(hash + 1));
if (torrent::utils::transform_from_hex(hash, hash + 40, key) != key.end())
return end();
return std::find_if(begin(), end(), [key](Download* d) { return key == d->info()->hash(); });
}
@@ -199,10 +201,10 @@ DownloadList::erase(iterator itr) {
(*itr)->set_hash_failed(true);
close(*itr);
control->core()->download_store()->remove(*itr);
session_thread::manager()->remove_download(*itr);
DL_TRIGGER_EVENT(*itr, "event.download.erased");
for (auto v : *control->view_manager())
v->erase(*itr);
@@ -329,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
@@ -402,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");
@@ -419,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");
@@ -490,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;
}
@@ -639,10 +645,8 @@ DownloadList::confirm_finished(Download* download) {
// the download.
//
// Obsolete.
if (!download->is_active() && rpc::call_command_value("session.on_completion") != 0) {
// torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
control->core()->download_store()->save_resume(download);
}
if (!download->is_active() && rpc::call_command_value("session.on_completion") != 0)
session_thread::manager()->save_resume_download(download);
// Send the completed request before resuming so we don't reset the
// up/downloaded baseline.
@@ -655,9 +659,9 @@ 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);
}
-205
View File
@@ -1,205 +0,0 @@
// DownloadStore handles the saving and listing of session torrents.
#include "config.h"
#include <fstream>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <rak/error_number.h>
#include <rak/path.h>
#include <rak/string_manip.h>
#include <torrent/utils/resume.h>
#include <torrent/object.h>
#include <torrent/exceptions.h>
#include <torrent/torrent.h>
#include <torrent/rate.h>
#include <torrent/object_stream.h>
#include "utils/directory.h"
#include "download.h"
#include "download_store.h"
#include "rpc/parse_commands.h"
namespace core {
void
DownloadStore::enable(bool lock) {
if (is_enabled())
throw torrent::input_error("Session directory already enabled.");
if (m_path.empty())
return;
if (lock)
m_lockfile.set_path(m_path + "rtorrent.lock");
else
m_lockfile.set_path(std::string());
if (!m_lockfile.try_lock()) {
if (rak::error_number::current().is_bad_path())
throw torrent::input_error("Could not lock session directory: \"" + m_path + "\", " + rak::error_number::current().c_str());
else
throw torrent::input_error("Could not lock session directory: \"" + m_path + "\", held by \"" + m_lockfile.locked_by_as_string() + "\".");
}
}
void
DownloadStore::disable() {
if (!is_enabled())
return;
m_lockfile.unlock();
}
void
DownloadStore::set_path(const std::string& path) {
if (is_enabled())
throw torrent::input_error("Tried to change session directory while it is enabled.");
if (!path.empty() && *path.rbegin() != '/')
m_path = rak::path_expand(path + '/');
else
m_path = rak::path_expand(path);
}
bool
DownloadStore::write_bencode(const std::string& filename, const torrent::Object& obj, uint32_t skip_mask) {
int fd;
torrent::Object tmp;
std::fstream output(filename.c_str(), std::ios::out | std::ios::trunc);
if (!output.is_open())
goto download_store_save_error;
torrent::object_write_bencode(&output, &obj, skip_mask);
if (!output.good())
goto download_store_save_error;
output.close();
// Test the new file, to ensure it is a valid bencode string.
output.open(filename.c_str(), std::ios::in);
output >> tmp;
if (!output.good())
goto download_store_save_error;
output.close();
// Ensure that the new file is actually written to the disk
fd = ::open(filename.c_str(), O_WRONLY);
if (fd < 0)
goto download_store_save_error;
if (rpc::call_command_value("system.files.session.fdatasync")) {
#ifdef __APPLE__
fsync(fd);
#else
fdatasync(fd);
#endif
}
::close(fd);
return true;
download_store_save_error:
output.close();
return false;
}
bool
DownloadStore::save(Download* d, int flags) {
if (!is_enabled())
return true;
torrent::Object* resume_base = &d->download()->bencode()->get_key("libtorrent_resume");
torrent::Object* rtorrent_base = &d->download()->bencode()->get_key("rtorrent");
// Move this somewhere else?
rtorrent_base->insert_key("chunks_done", d->download()->file_list()->completed_chunks());
rtorrent_base->insert_key("chunks_wanted", d->download()->data()->wanted_chunks());
rtorrent_base->insert_key("total_uploaded", d->info()->up_rate()->total());
rtorrent_base->insert_key("total_downloaded", d->info()->down_rate()->total());
// Don't save for completed torrents when we've cleared the uncertain_pieces.
torrent::resume_save_progress(*d->download(), *resume_base);
torrent::resume_save_uncertain_pieces(*d->download(), *resume_base);
torrent::resume_save_addresses(*d->download(), *resume_base);
torrent::resume_save_file_priorities(*d->download(), *resume_base);
torrent::resume_save_tracker_settings(*d->download(), *resume_base);
// Temp fixing of all flags, move to a better place:
resume_base->set_flags(torrent::Object::flag_session_data);
rtorrent_base->set_flags(torrent::Object::flag_session_data);
std::string base_filename = create_filename(d);
if (!write_bencode(base_filename + ".libtorrent_resume.new", *resume_base, 0) ||
!write_bencode(base_filename + ".rtorrent.new", *rtorrent_base, 0))
return false;
::rename((base_filename + ".libtorrent_resume.new").c_str(), (base_filename + ".libtorrent_resume").c_str());
::rename((base_filename + ".rtorrent.new").c_str(), (base_filename + ".rtorrent").c_str());
if (!(flags & flag_skip_static) &&
write_bencode(base_filename + ".new", *d->bencode(), torrent::Object::flag_session_data))
::rename((base_filename + ".new").c_str(), base_filename.c_str());
return true;
}
void
DownloadStore::remove(Download* d) {
if (!is_enabled())
return;
::unlink((create_filename(d) + ".libtorrent_resume").c_str());
::unlink((create_filename(d) + ".rtorrent").c_str());
::unlink(create_filename(d).c_str());
}
// This also needs to check that it isn't a directory.
bool
not_correct_format(const utils::directory_entry& entry) {
return !DownloadStore::is_correct_format(entry.s_name);
}
utils::Directory
DownloadStore::get_formated_entries() {
if (!is_enabled())
return utils::Directory();
utils::Directory d(m_path);
if (!d.update(utils::Directory::update_hide_dot))
throw torrent::storage_error("core::DownloadStore::update() could not open directory \"" + m_path + "\"");
d.erase(std::remove_if(d.begin(), d.end(), [&](const utils::directory_entry& entry) { return not_correct_format(entry); }), d.end());
return d;
}
bool
DownloadStore::is_correct_format(const std::string& f) {
if (f.size() != 48 || f.substr(40) != ".torrent")
return false;
for (std::string::const_iterator itr = f.begin(); itr != f.end() - 8; ++itr)
if (!(*itr >= '0' && *itr <= '9') &&
!(*itr >= 'A' && *itr <= 'F'))
return false;
return true;
}
std::string
DownloadStore::create_filename(Download* d) {
return m_path + rak::transform_hex(d->info()->hash().begin(), d->info()->hash().end()) + ".torrent";
}
}
-49
View File
@@ -1,49 +0,0 @@
#ifndef RTORRENT_CORE_DOWNLOAD_STORE_H
#define RTORRENT_CORE_DOWNLOAD_STORE_H
#include <string>
#include "utils/lockfile.h"
namespace utils {
class Directory;
}
namespace core {
class Download;
class DownloadStore {
public:
static const int flag_skip_static = 0x1;
bool is_enabled() { return m_lockfile.is_locked(); }
void enable(bool lock);
void disable();
const std::string& path() const { return m_path; }
void set_path(const std::string& path);
bool save(Download* d, int flags);
bool save_full(Download* d) { return save(d, 0); }
bool save_resume(Download* d) { return save(d, flag_skip_static); }
void remove(Download* d);
// Currently shows all entries in the correct format.
utils::Directory get_formated_entries();
static bool is_correct_format(const std::string& f);
private:
std::string create_filename(Download* d);
bool write_bencode(const std::string& filename, const torrent::Object& obj, uint32_t skip_mask);
std::string m_path;
utils::Lockfile m_lockfile;
};
}
#endif
+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();
+108 -170
View File
@@ -1,25 +1,25 @@
#include "config.h"
#include "core/manager.h"
#include <cstdio>
#include <cstring>
#include <fstream>
#include <sstream>
#include <unistd.h>
#include <sys/select.h>
#include <rak/address_info.h>
#include <rak/error_number.h>
#include <rak/regex.h>
#include <rak/path.h>
#include <rak/string_manip.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/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>
#include "rpc/parse_commands.h"
#include "utils/directory.h"
@@ -30,11 +30,11 @@
#include "control.h"
#include "core/download.h"
#include "core/download_factory.h"
#include "core/download_store.h"
#include "core/http_queue.h"
#include "core/manager.h"
#include "core/view.h"
#include <torrent/runtime/client_config.h>
namespace core {
const int Manager::create_start;
@@ -48,11 +48,10 @@ Manager::push_log(const char* msg) {
m_log_complete->lock_and_push_log(msg, strlen(msg), 0);
}
Manager::Manager() :
m_log_important(torrent::log_open_log_buffer("important")),
Manager::Manager()
: m_log_important(torrent::log_open_log_buffer("important")),
m_log_complete(torrent::log_open_log_buffer("complete")) {
m_download_store = std::make_unique<DownloadStore>();
m_download_list = std::make_unique<DownloadList>();
m_file_status_cache = std::make_unique<FileStatusCache>();
m_http_queue = std::make_unique<HttpQueue>();
@@ -66,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)
@@ -75,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();
@@ -89,17 +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) {
return m_addressThrottles.get(rak::socket_address::cast_from(addr)->sa_inet()->address_h(), 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);
@@ -129,6 +131,16 @@ Manager::retrieve_throttle_value(const torrent::Object::string_type& name, bool
}
}
void
Manager::set_magnet_path(const std::string& path) {
if (path.empty())
m_magnet_path.clear();
else if (path.back() == '/')
m_magnet_path = path;
else
m_magnet_path = path + '/';
}
void
Manager::cleanup() {
// Need to disconnect log signals? Not really since we won't receive
@@ -141,144 +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::connection_manager()->listen_open(boundary, portLast) ||
torrent::connection_manager()->listen_open(portFirst, boundary))
return;
} else {
if (torrent::connection_manager()->listen_open(portFirst, portLast))
return;
}
throw torrent::input_error("Could not open/bind port for listening: " + std::string(rak::error_number::current().c_str()));
}
std::string
Manager::bind_address() const {
return rak::socket_address::cast_from(torrent::connection_manager()->bind_address())->address_str();
}
void
Manager::set_bind_address(const std::string& addr) {
int err;
rak::address_info* ai;
if ((err = rak::address_info::get_address_info(addr.c_str(), PF_INET, SOCK_STREAM, &ai)) != 0 &&
(err = rak::address_info::get_address_info(addr.c_str(), PF_INET6, SOCK_STREAM, &ai)) != 0)
throw torrent::input_error("Could not set bind address: " + std::string(rak::address_info::strerror(err)) + ".");
try {
if (torrent::connection_manager()->listen_port() != 0) {
torrent::connection_manager()->listen_close();
torrent::connection_manager()->set_bind_address(ai->address()->c_sockaddr());
listen_open();
} else {
torrent::connection_manager()->set_bind_address(ai->address()->c_sockaddr());
}
if (ai->address()->is_address_any())
torrent::net_thread::http_stack()->set_bind_address(std::string());
else
torrent::net_thread::http_stack()->set_bind_address(ai->address()->address_str());
rak::address_info::free_address_info(ai);
} catch (torrent::input_error& e) {
rak::address_info::free_address_info(ai);
throw e;
}
}
std::string
Manager::local_address() const {
return rak::socket_address::cast_from(torrent::connection_manager()->local_address())->address_str();
}
void
Manager::set_local_address(const std::string& addr) {
int err;
rak::address_info* ai;
if ((err = rak::address_info::get_address_info(addr.c_str(), PF_INET, SOCK_STREAM, &ai)) != 0 &&
(err = rak::address_info::get_address_info(addr.c_str(), PF_INET6, SOCK_STREAM, &ai)) != 0)
throw torrent::input_error("Could not set local address: " + std::string(rak::address_info::strerror(err)) + ".");
try {
torrent::connection_manager()->set_local_address(ai->address()->c_sockaddr());
rak::address_info::free_address_info(ai);
} catch (torrent::input_error& e) {
rak::address_info::free_address_info(ai);
throw e;
}
}
std::string
Manager::proxy_address() const {
return rak::socket_address::cast_from(torrent::connection_manager()->proxy_address())->address_str();
}
void
Manager::set_proxy_address(const std::string& addr) {
int port;
rak::address_info* ai;
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;
if ((err = rak::address_info::get_address_info(buf.data(), PF_INET, SOCK_STREAM, &ai)) != 0)
throw torrent::input_error("Could not set proxy address: " + std::string(rak::address_info::strerror(err)) + ".");
try {
ai->address()->set_port(port);
torrent::connection_manager()->set_proxy_address(ai->address()->c_sockaddr());
rak::address_info::free_address_info(ai);
} catch (torrent::input_error& e) {
rak::address_info::free_address_info(ai);
throw e;
}
}
@@ -294,9 +174,10 @@ is_data_uri(const std::string& uri) {
std::string
decode_data_uri(const std::string& uri) {
const auto start = uri.find("base64,", 5) + 7;
if (start == std::string::npos)
const auto pos = uri.find("base64,", 5);
if (pos == std::string::npos)
throw torrent::input_error("Invalid data uri: not base64 encoded.");
const auto start = pos + 7;
if (start >= uri.size())
throw torrent::input_error("Empty base64.");
return utils::decode_base64(uri.substr(start));
@@ -310,7 +191,7 @@ Manager::try_create_download(const std::string& uri, int flags, const command_li
!is_network_uri(uri) &&
!is_magnet_uri(uri) &&
!is_data_uri(uri) &&
!file_status_cache()->insert(uri, 0))
!file_status_cache()->insert(uri))
return;
// Adding download.
@@ -367,14 +248,70 @@ path_expand_transform(std::string path, const utils::directory_entry& entry) {
return path + entry.s_name;
}
namespace {
// Consider rewritting such that m_seq is replaced by first/last.
template <typename Sequence>
class split_iterator_t {
public:
typedef typename Sequence::const_iterator const_iterator;
typedef typename Sequence::value_type value_type;
split_iterator_t() {}
split_iterator_t(const Sequence& seq, value_type delim) :
m_seq(&seq),
m_delim(delim),
m_pos(seq.begin()),
m_next(std::find(seq.begin(), seq.end(), delim)) {
}
Sequence operator * () { return Sequence(m_pos, m_next); }
split_iterator_t& operator ++ () {
m_pos = m_next;
if (m_pos == m_seq->end())
return *this;
m_pos++;
m_next = std::find(m_pos, m_seq->end(), m_delim);
return *this;
}
bool operator == (const split_iterator_t&) const { return m_pos == m_seq->end(); }
bool operator != (const split_iterator_t&) const { return m_pos != m_seq->end(); }
private:
const Sequence* m_seq;
value_type m_delim;
const_iterator m_pos;
const_iterator m_next;
};
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence& seq, typename Sequence::value_type delim) {
return split_iterator_t<Sequence>(seq, delim);
}
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence&) {
return split_iterator_t<Sequence>();
}
}
// Move this somewhere better.
void
path_expand(std::vector<std::string>* paths, const std::string& pattern) {
std::vector<utils::Directory> currentCache;
std::vector<utils::Directory> nextCache;
rak::split_iterator_t<std::string> first = rak::split_iterator(pattern, '/');
rak::split_iterator_t<std::string> last = rak::split_iterator(pattern);
auto first = split_iterator(pattern, '/');
auto last = split_iterator(pattern);
if (first == last)
return;
@@ -383,7 +320,7 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
if ((*first).empty()) {
currentCache.push_back(utils::Directory("/"));
++first;
} else if (rak::trim(*first) == "~") {
} else if (torrent::utils::trim_spaces_str(*first) == "~") {
currentCache.push_back(utils::Directory("~"));
++first;
} else {
@@ -393,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 ".."?
@@ -403,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));
@@ -499,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());
}
}
+18 -30
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"
@@ -21,10 +20,10 @@ class FileStatusCache;
namespace core {
class DownloadStore;
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;
@@ -36,39 +35,28 @@ public:
Manager();
~Manager();
DownloadList* download_list() { return m_download_list.get(); }
DownloadStore* download_store() { return m_download_store.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();
std::string bind_address() const;
void set_bind_address(const std::string& addr);
std::string local_address() const;
void set_local_address(const std::string& addr);
std::string proxy_address() const;
void set_proxy_address(const std::string& addr);
const std::string& magnet_path();
void set_magnet_path(const std::string& path);
void shutdown(bool force);
@@ -91,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,22 +88,24 @@ private:
void receive_hashing_changed();
std::unique_ptr<DownloadList> m_download_list;
std::unique_ptr<DownloadStore> m_download_store;
std::unique_ptr<FileStatusCache> m_file_status_cache;
std::unique_ptr<HttpQueue> m_http_queue;
View* m_hashingView{};
ThrottleMap m_throttles;
AddressThrottleMap m_addressThrottles;
torrent::log_buffer_ptr m_log_important;
torrent::log_buffer_ptr m_log_complete;
std::string m_magnet_path;
};
// Meh, cleanup.
extern void receive_tracker_dump(const std::string& url, const char* data, size_t size);
inline const std::string& Manager::magnet_path() { return m_magnet_path; }
}
#endif
+3 -3
View File
@@ -18,7 +18,7 @@
#include <string>
#include <vector>
#include <torrent/object.h>
#include <torrent/utils/scheduler.h>
#include <torrent/system/scheduler.h>
#include "globals.h"
@@ -153,8 +153,8 @@ private:
std::chrono::microseconds m_last_changed{};
signal_void m_signal_changed;
torrent::utils::SchedulerEntry m_delay_changed;
signal_void m_signal_changed;
torrent::system::SchedulerEntry m_delay_changed;
};
} // namespace core
+2 -1
View File
@@ -35,7 +35,8 @@ ViewManager::insert(const std::string& name) {
View* view = new View();
view->initialize(name);
return base_type::insert(end(), view);
base_type::push_back(view);
return --end();
}
ViewManager::iterator
+4 -4
View File
@@ -2,16 +2,16 @@
#define RTORRENT_CORE_VIEW_MANAGER_H
#include <string>
#include <rak/unordered_vector.h>
#include <torrent/utils/unordered_vector.h>
#include "view.h"
namespace core {
class ViewManager : public rak::unordered_vector<View*> {
class ViewManager : public torrent::utils::unordered_vector<View*> {
public:
typedef rak::unordered_vector<View*> base_type;
typedef std::list<std::string> filter_args;
typedef torrent::utils::unordered_vector<View*> base_type;
typedef std::list<std::string> filter_args;
using base_type::iterator;
using base_type::const_iterator;
+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>
+38 -30
View File
@@ -11,23 +11,31 @@
namespace display {
bool Canvas::m_isInitialized = false;
bool Canvas::m_isDaemon = false;
bool Canvas::m_initialized{};
bool Canvas::m_daemon{};
// Maps ncurses color IDs to a ncurses attribute int
std::unordered_map<int, int> Canvas::m_attr_map = {};
Canvas::Canvas(int x, int y, int width, int height) {
if (!m_isDaemon) {
if (!m_daemon) {
m_window = newwin(height, width, y, x);
if (m_window == NULL)
if (m_window == nullptr)
throw torrent::internal_error("Could not allocate ncurses canvas.");
}
}
Canvas::~Canvas() {
if (!m_daemon && m_window != nullptr) {
delwin(m_window);
m_window = nullptr;
}
}
void
Canvas::resize(int x, int y, int w, int h) {
if (!m_isDaemon) {
if (!m_daemon) {
wresize(m_window, h, w);
mvwin(m_window, y, x);
}
@@ -35,7 +43,7 @@ Canvas::resize(int x, int y, int w, int h) {
void
Canvas::print_attributes(unsigned int x, unsigned int y, const char* first, const char* last, const attributes_list* attributes) {
if (!m_isDaemon) {
if (!m_daemon) {
move(x, y);
attr_t org_attr;
@@ -68,14 +76,13 @@ Canvas::print_attributes(unsigned int x, unsigned int y, const char* first, cons
void
Canvas::initialize() {
if (m_isInitialized)
return;
if (m_initialized)
throw torrent::internal_error("Canvas::initialize() called more than once.");
m_isDaemon = rpc::call_command_value("system.daemon");
m_daemon = rpc::call_command_value("system.daemon");
m_initialized = true;
m_isInitialized = true;
if (!m_isDaemon) {
if (!m_daemon) {
initscr();
start_color();
use_default_colors();
@@ -88,6 +95,19 @@ Canvas::initialize() {
}
}
void
Canvas::cleanup() {
if (!m_initialized)
return;
m_initialized = false;
if (!m_daemon) {
noraw();
endwin();
}
}
// Function wrapper for what possibly is a macro
int
get_colors() {
@@ -102,7 +122,7 @@ Canvas::build_colors() {
// This may get called early in the start process by the config
// file, so we need to delay building until initscr() has a chance
// to run
if (!m_isInitialized || m_isDaemon)
if (!m_initialized || m_daemon)
return;
// basic color names, index maps to ncurses COLOR_*
@@ -115,7 +135,9 @@ Canvas::build_colors() {
for (int k = 1; k < RCOLOR_MAX; k++) {
init_pair(k, -1, -1);
std::string color_def = rpc::call_command_string(color_vars[k]);
if (color_def.empty())
continue; // Use terminal default if definition is empty
@@ -126,6 +148,7 @@ Canvas::build_colors() {
// Process string as space-separated words
size_t start = 0, end = 0;
while (true) {
end = color_def.find(' ', start);
std::string word = color_def.substr(start, end - start);
@@ -169,10 +192,8 @@ Canvas::build_colors() {
// Check that fg & bg color index is valid
if ((color[0] != -1 && color[0] >= get_colors()) || (color[1] != -1 && color[1] >= get_colors())) {
char buf[33];
snprintf(buf, 33, "%d", get_colors());
Canvas::cleanup();
throw torrent::input_error(color_def + ": your terminal only supports " + buf + " colors.");
throw torrent::input_error(color_def + ": your terminal only supports " + std::to_string(get_colors()) + " colors.");
}
m_attr_map[k] = attr; // overwrite or insert the value
@@ -196,24 +217,11 @@ Canvas::build_colors() {
}
}
void
Canvas::cleanup() {
if (!m_isInitialized)
return;
m_isInitialized = false;
if (!m_isDaemon) {
noraw();
endwin();
}
}
std::pair<int, int>
Canvas::term_size() {
struct winsize ws;
if (!m_isDaemon) {
if (!m_daemon) {
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
return std::pair<int, int>(ws.ws_col, ws.ws_row);
}
+35 -43
View File
@@ -54,9 +54,10 @@ public:
// Initialize stdscr.
static void initialize();
static void build_colors();
static void cleanup();
static void build_colors();
static int get_screen_width();
static int get_screen_height();
@@ -64,15 +65,15 @@ public:
static void do_update();
static bool daemon() { return m_isDaemon; }
static bool daemon() { return m_daemon; }
static const attributes_map& attr_map() { return m_attr_map; }
private:
Canvas(const Canvas&);
void operator=(const Canvas&);
static bool m_isInitialized;
static bool m_isDaemon;
static bool m_initialized;
static bool m_daemon;
// Maps ncurses color IDs to a ncurses attribute int
static std::unordered_map<int, int> m_attr_map;
@@ -80,66 +81,59 @@ private:
WINDOW* m_window;
};
inline Canvas::~Canvas() {
if (!m_isDaemon) {
delwin(m_window);
}
}
inline void
Canvas::refresh() {
if (!m_isDaemon) {
if (!m_daemon) {
wnoutrefresh(m_window);
}
}
inline void
Canvas::refresh_std() {
if (!m_isDaemon) {
if (!m_daemon) {
wnoutrefresh(stdscr);
}
}
inline void
Canvas::redraw() {
if (!m_isDaemon) {
if (!m_daemon) {
redrawwin(m_window);
}
}
inline void
Canvas::redraw_std() {
if (!m_isDaemon) {
if (!m_daemon) {
redrawwin(stdscr);
}
}
inline void
Canvas::resize(int w, int h) {
if (!m_isDaemon) {
if (!m_daemon) {
wresize(m_window, h, w);
}
}
inline void
Canvas::resize_term(int x, int y) {
if (!m_isDaemon) {
if (!m_daemon) {
resizeterm(y, x);
}
}
inline void
Canvas::resize_term(std::pair<int, int> dim) {
if (!m_isDaemon) {
if (!m_daemon) {
resizeterm(dim.second, dim.first);
}
}
inline unsigned int
Canvas::get_x() {
int x;
[[maybe_unused]] int y;
if (!m_isDaemon) {
[[maybe_unused]] int x, y;
if (!m_daemon) {
getyx(m_window, y, x);
} else {
x = 1;
@@ -149,8 +143,8 @@ Canvas::get_x() {
inline unsigned int
Canvas::get_y() {
int x, y;
if (!m_isDaemon) {
[[maybe_unused]] int x, y;
if (!m_daemon) {
getyx(m_window, y, x);
} else {
y = 1;
@@ -160,9 +154,8 @@ Canvas::get_y() {
inline unsigned int
Canvas::width() {
int x;
[[maybe_unused]] int y;
if (!m_isDaemon) {
[[maybe_unused]] int x, y;
if (!m_daemon) {
getmaxyx(m_window, y, x);
} else {
x = 80;
@@ -172,8 +165,8 @@ Canvas::width() {
inline unsigned int
Canvas::height() {
int x, y;
if (!m_isDaemon) {
[[maybe_unused]] int x, y;
if (!m_daemon) {
getmaxyx(m_window, y, x);
} else {
y = 24;
@@ -183,21 +176,21 @@ Canvas::height() {
inline void
Canvas::move(unsigned int x, unsigned int y) {
if (!m_isDaemon) {
if (!m_daemon) {
wmove(m_window, y, x);
}
}
inline void
Canvas::erase() {
if (!m_isDaemon) {
if (!m_daemon) {
werase(m_window);
}
}
inline void
Canvas::erase_std() {
if (!m_isDaemon) {
if (!m_daemon) {
werase(stdscr);
}
}
@@ -206,7 +199,7 @@ inline void
Canvas::print(const char* str, ...) {
va_list arglist;
if (!m_isDaemon) {
if (!m_daemon) {
va_start(arglist, str);
vw_printw(m_window, const_cast<char*>(str), arglist);
va_end(arglist);
@@ -217,7 +210,7 @@ inline void
Canvas::print(unsigned int x, unsigned int y, const char* str, ...) {
va_list arglist;
if (!m_isDaemon) {
if (!m_daemon) {
va_start(arglist, str);
wmove(m_window, y, x);
vw_printw(m_window, const_cast<char*>(str), arglist);
@@ -227,44 +220,43 @@ Canvas::print(unsigned int x, unsigned int y, const char* str, ...) {
inline void
Canvas::print_char(const chtype ch) {
if (!m_isDaemon) {
if (!m_daemon) {
waddch(m_window, ch);
}
}
inline void
Canvas::print_char(unsigned int x, unsigned int y, const chtype ch) {
if (!m_isDaemon) {
if (!m_daemon) {
mvwaddch(m_window, y, x, ch);
}
}
inline void
Canvas::set_attr(unsigned int x, unsigned int y, unsigned int n, int attr, int color) {
if (!m_isDaemon) {
if (!m_daemon) {
mvwchgat(m_window, y, x, n, attr, color, NULL);
}
}
inline void
Canvas::set_attr(unsigned int x, unsigned int y, unsigned int n, ColorKind k) {
if (!m_isDaemon) {
if (!m_daemon) {
mvwchgat(m_window, y, x, n, m_attr_map[k], k, NULL);
}
}
inline void
Canvas::set_default_attributes(int attr) {
if (!m_isDaemon) {
if (!m_daemon) {
(void)wattrset(m_window, attr);
}
}
inline int
Canvas::get_screen_width() {
int x;
[[maybe_unused]] int y;
if (!m_isDaemon) {
[[maybe_unused]] int x, y;
if (!m_daemon) {
getmaxyx(stdscr, y, x);
} else {
x = 80;
@@ -274,8 +266,8 @@ Canvas::get_screen_width() {
inline int
Canvas::get_screen_height() {
int x, y;
if (!m_isDaemon) {
[[maybe_unused]] int x, y;
if (!m_daemon) {
getmaxyx(stdscr, y, x);
} else {
y = 24;
@@ -285,7 +277,7 @@ Canvas::get_screen_height() {
inline void
Canvas::do_update() {
if (!m_isDaemon) {
if (!m_daemon) {
doupdate();
}
}
+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
+16 -51
View File
@@ -1,47 +1,12 @@
// 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>
#include "config.h"
#include "display/frame.h"
#include <algorithm>
#include <functional>
#include <rak/algorithm.h>
#include <torrent/exceptions.h>
#include "frame.h"
#include "window.h"
#include "display/window.h"
namespace display {
@@ -137,7 +102,7 @@ Frame::preferred_size() const {
for (size_type i = 0; i < m_containerSize; ++i) {
bounds_type p = m_container[i]->preferred_size();
accum.minWidth += p.minWidth;
accum.minHeight += p.minHeight;
@@ -221,7 +186,7 @@ Frame::clear() {
m_window->set_offscreen(true);
break;
case TYPE_ROW:
case TYPE_COLUMN:
for (size_type i = 0; i < m_containerSize; ++i) {
@@ -335,10 +300,10 @@ Frame::balance_row(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
dynamic_type dynamicFrames[max_size];
int remaining = height;
for (Frame **itr = m_container, **last = m_container + m_containerSize; itr != last; ++itr) {
bounds_type bounds = (*itr)->preferred_size();
if ((*itr)->is_height_dynamic()) {
(*itr)->m_height = 0;
dynamicFrames[dynamicSize++] = std::make_pair(*itr, bounds);
@@ -367,11 +332,11 @@ Frame::balance_row(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
for (dynamic_type *itr = dynamicFrames, *last = dynamicFrames + dynamicSize; itr != last; ++itr) {
uint32_t adjust = (std::max(remaining, 0) + std::distance(itr, last) - 1) / std::distance(itr, last);
adjust += itr->first->m_height;
adjust = std::max(adjust, itr->second.minHeight);
adjust = std::min(adjust, itr->second.maxHeight);
remaining -= adjust - itr->first->m_height;
retry = retry || itr->first->m_height != adjust;
@@ -396,7 +361,7 @@ Frame::balance_row(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
(*itr)->balance(x, y, m_width, std::min((*itr)->m_height, height));
y += (*itr)->m_height;
height -= (*itr)->m_height;
height -= std::min(height, (*itr)->m_height);
}
}
@@ -410,10 +375,10 @@ Frame::balance_column(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
dynamic_type dynamicFrames[max_size];
int remaining = width;
for (Frame **itr = m_container, **last = m_container + m_containerSize; itr != last; ++itr) {
bounds_type bounds = (*itr)->preferred_size();
if ((*itr)->is_width_dynamic()) {
(*itr)->m_width = 0;
dynamicFrames[dynamicSize++] = std::make_pair(*itr, bounds);
@@ -423,7 +388,7 @@ Frame::balance_column(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
remaining -= bounds.minWidth;
}
}
// Sort the dynamic frames by the min size in the direction we are
// interested in. Then try to satisfy the largest first, and if we
// have any remaining space we can use that to extend it and any
@@ -442,11 +407,11 @@ Frame::balance_column(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
for (dynamic_type *itr = dynamicFrames, *last = dynamicFrames + dynamicSize; itr != last; ++itr) {
uint32_t adjust = (std::max(remaining, 0) + std::distance(itr, last) - 1) / std::distance(itr, last);
adjust += itr->first->m_width;
adjust = std::max(adjust, itr->second.minWidth);
adjust = std::min(adjust, itr->second.maxWidth);
remaining -= adjust - itr->first->m_width;
retry = retry || itr->first->m_width != adjust;
@@ -471,7 +436,7 @@ Frame::balance_column(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
(*itr)->balance(x, y, std::min((*itr)->m_width, width), m_height);
x += (*itr)->m_width;
width -= (*itr)->m_width;
width -= std::min(width, (*itr)->m_width);
}
}
+2 -35
View File
@@ -1,41 +1,8 @@
// 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_DISPLAY_FRAME_H
#define RTORRENT_DISPLAY_FRAME_H
#include <cinttypes>
#include <utility>
namespace display {
@@ -121,7 +88,7 @@ private:
union {
Window* m_window;
struct {
size_type m_containerSize;
Frame* m_container[max_size];
+7 -2
View File
@@ -55,6 +55,7 @@ Manager::receive_update() {
m_force_redraw = false;
display::Canvas::resize_term(display::Canvas::term_size());
keypad(stdscr, TRUE);
Canvas::redraw_std();
adjust_layout();
@@ -79,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));
}
}
+5 -5
View File
@@ -1,9 +1,9 @@
#ifndef RTORRENT_DISPLAY_MANAGER_H
#define RTORRENT_DISPLAY_MANAGER_H
#include <torrent/utils/scheduler.h>
#include <torrent/system/scheduler.h>
#include "frame.h"
#include "display/frame.h"
namespace display {
@@ -31,9 +31,9 @@ private:
bool m_force_redraw{false};
Frame m_root_frame;
std::chrono::microseconds m_time_last_update{};
torrent::utils::ExternalScheduler m_scheduler;
torrent::utils::SchedulerEntry m_task_update;
std::chrono::microseconds m_time_last_update{};
torrent::system::ExternalScheduler m_scheduler;
torrent::system::SchedulerEntry m_task_update;
};
}
+17 -50
View File
@@ -1,43 +1,10 @@
// 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>
#include "config.h"
#include <rak/string_manip.h>
#include "display/text_element_string.h"
#include <torrent/utils/string_manip.h>
#include "rpc/parse_commands.h"
#include "text_element_string.h"
namespace display {
@@ -50,20 +17,20 @@ TextElementStringBase::print(char* first, char* last, Canvas::attributes_list* a
return first;
if (m_flags & flag_escape_hex) {
char buffer[last - first];
char* bufferLast = copy_string(buffer, buffer + (last - first), target);
auto buffer = std::make_unique<char[]>(last - first);
char* bufferLast = copy_string(buffer.get(), buffer.get() + (last - first), target);
first = rak::transform_hex(buffer, bufferLast, first, last);
first = torrent::utils::transform_to_hex(buffer.get(), bufferLast, first, last);
} else if (m_flags & flag_escape_html) {
char buffer[last - first];
char* bufferLast = copy_string(buffer, buffer + (last - first), target);
auto buffer = std::make_unique<char[]>(last - first);
char* bufferLast = copy_string(buffer.get(), buffer.get() + (last - first), target);
first = rak::copy_escape_html(buffer, bufferLast, first, last);
first = torrent::utils::copy_escape_html(buffer.get(), bufferLast, first, last);
} else {
first = copy_string(first, last, target);
}
}
push_attribute(attributes, Attributes(first, baseAttribute));
@@ -71,16 +38,16 @@ TextElementStringBase::print(char* first, char* last, Canvas::attributes_list* a
}
char*
TextElementString::copy_string(char* first, char* last, rpc::target_type target) {
TextElementString::copy_string(char* first, char* last, [[maybe_unused]] rpc::target_type target) {
extent_type length = std::min<extent_type>(last - first, m_string.size());
std::memcpy(first, m_string.c_str(), length);
return first + length;
}
char*
TextElementCString::copy_string(char* first, char* last, rpc::target_type target) {
TextElementCString::copy_string(char* first, char* last, [[maybe_unused]] rpc::target_type target) {
extent_type length = std::min<extent_type>(last - first, m_length);
std::memcpy(first, m_string, length);
@@ -104,22 +71,22 @@ TextElementCommand::print(char* first, char* last, Canvas::attributes_list* attr
const std::string& str = result.as_string();
if (m_flags & flag_escape_hex) {
first = rak::transform_hex(str.c_str(), str.c_str() + str.size(), first, last);
first = torrent::utils::transform_to_hex(str.c_str(), str.c_str() + str.size(), first, last);
} else if (m_flags & flag_escape_html) {
first = rak::copy_escape_html(str.c_str(), str.c_str() + str.size(), first, last);
first = torrent::utils::copy_escape_html(str.c_str(), str.c_str() + str.size(), first, last);
} else {
size_t length = std::min<size_t>(str.size(), std::distance(first, last));
std::memcpy(first, str.c_str(), length);
first += std::min<size_t>(str.size(), length);
}
}
break;
}
case torrent::Object::TYPE_VALUE:
{
{
first += std::min<ptrdiff_t>(std::max(snprintf(first, last - first + 1, "%lld", (long long int)result.as_value()), 0), last - first + 1);
break;
}
-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_DISPLAY_TEXT_ELEMENT_STRING_H
#define RTORRENT_DISPLAY_TEXT_ELEMENT_STRING_H
+36 -26
View File
@@ -6,9 +6,7 @@
#include <cstdio>
#include <sstream>
#include <iomanip>
#include <rak/socket_address.h>
#include <torrent/exceptions.h>
#include <torrent/connection_manager.h>
#include <torrent/rate.h>
#include <torrent/throttle.h>
#include <torrent/torrent.h>
@@ -17,7 +15,11 @@
#include <torrent/data/file_manager.h>
#include <torrent/download/resource_manager.h>
#include <torrent/net/http_stack.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"
@@ -29,7 +31,7 @@
namespace display {
char*
print_string(char* first, char* last, char* str) {
print_string(char* first, char* last, const char* str) {
if (first == last)
return first;
@@ -78,17 +80,9 @@ print_ddmmyyyy(char* first, char* last, time_t t) {
return print_buffer(first, last, "%02u/%02u/%04u", u->tm_mday, (u->tm_mon + 1), (1900 + u->tm_year));
}
char*
print_address(char* first, char* last, const rak::socket_address* sa) {
if (!sa->address_c_str(first, last - first))
return first;
return std::find(first, last, '\0');
}
inline char*
print_address(char* first, char* last, const sockaddr* sa) {
return print_address(first, last, rak::socket_address::cast_from(sa));
return print_string(first, last, torrent::sa_addr_str(sa).c_str());
}
char*
@@ -157,11 +151,11 @@ print_download_status(char* first, char* last, core::Download* d) {
if (d->is_hash_checking()) {
first = print_buffer(first, last, "Checking hash [%2i%%]",
(d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks());
d->download()->file_list()->size_chunks() != 0 ? (d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks() : 0);
} 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()) {
@@ -215,7 +209,7 @@ print_download_info_compact(char* first, char* last, core::Download* d) {
if (d->is_done())
first = print_buffer(first, last, " 100%% ");
else if (d->is_open())
first = print_buffer(first, last, " %2u%% ",(d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks());
first = print_buffer(first, last, " %2u%% ", d->download()->file_list()->size_chunks() != 0 ? (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks() : 0);
else
first = print_buffer(first, last, " ");
@@ -267,7 +261,7 @@ print_download_percentage_done(char* first, char* last, core::Download* d) {
//return print_buffer(first, last, "[--%%]");
return print_buffer(first, last, " ");
else
return print_buffer(first, last, "[%2u%%]", (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks());
return print_buffer(first, last, "[%2u%%]", d->download()->file_list()->size_chunks() != 0 ? (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks() : 0);
}
char*
@@ -381,21 +375,37 @@ print_status_info(char* first, char* last) {
first = print_status_throttle_rate(first, last, false, throttle_down_names, global_downrate);
first = print_buffer(first, last, " KB]");
first = print_buffer(first, last, " [Port: %i]", (unsigned int)torrent::runtime::listen_port());
first = print_buffer(first, last, " [Port: %i]", (unsigned int)torrent::connection_manager()->listen_port());
auto local_address = torrent::runtime::network_config()->local_address_best_match();
if (!rak::socket_address::cast_from(torrent::connection_manager()->local_address())->is_address_any()) {
if (!torrent::sa_is_any(local_address.get())) {
first = print_buffer(first, last, " [Local ");
first = print_address(first, last, torrent::connection_manager()->local_address());
first = print_address(first, last, local_address.get());
first = print_buffer(first, last, "]");
}
if (first > last)
throw torrent::internal_error("print_status_info(...) wrote past end of the buffer.");
if (!rak::socket_address::cast_from(torrent::connection_manager()->bind_address())->is_address_any()) {
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());
if (show_bind_inet || show_bind_inet6) {
first = print_buffer(first, last, " [Bind ");
first = print_address(first, last, torrent::connection_manager()->bind_address());
if (show_bind_inet)
first = print_address(first, last, bind_inet_address.get());
if (show_bind_inet6) {
if (show_bind_inet)
first = print_buffer(first, last, " / ");
first = print_address(first, last, bind_inet6_address.get());
}
first = print_buffer(first, last, "]");
}
@@ -413,13 +423,13 @@ print_status_extra(char* first, char* last) {
torrent::resource_manager()->max_download_unchoked());
first = print_buffer(first, last, " [H %u/%u]",
torrent::net_thread::http_stack()->active(),
torrent::net_thread::http_stack()->max_active());
torrent::net_thread::http_stack()->size(),
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(),
-2
View File
@@ -31,8 +31,6 @@ Window::Window(Canvas* canvas, int flags, extent_type min_width, extent_type min
Window::~Window() {
if (is_active())
m_slot_unschedule(this);
delete m_canvas;
}
void
+61 -4
View File
@@ -2,8 +2,8 @@
#define RTORRENT_WINDOW_BASE_H
#include <functional>
#include <torrent/utils/scheduler.h>
#include <torrent/utils/thread.h>
#include <torrent/system/scheduler.h>
#include <torrent/system/thread.h>
#include "canvas.h"
#include "globals.h"
@@ -89,7 +89,7 @@ protected:
static SlotWindow m_slot_unschedule;
static Slot m_slot_adjust;
Canvas* m_canvas;
std::unique_ptr<Canvas> m_canvas;
int m_flags;
@@ -99,9 +99,66 @@ protected:
extent_type m_max_width;
extent_type m_max_height;
torrent::utils::SchedulerEntry m_task_update;
torrent::system::SchedulerEntry m_task_update;
};
// Return a range with a distance of no more than __distance and
// between __first and __last, centered on __middle1.
template <typename _InputIter, typename _Distance>
std::pair<_InputIter, _InputIter>
advance_bidirectional(_InputIter __first, _InputIter __middle1, _InputIter __last, _Distance __distance) {
_InputIter __middle2 = __middle1;
do {
if (!__distance)
break;
if (__middle2 != __last) {
++__middle2;
--__distance;
} else if (__middle1 == __first) {
break;
}
if (!__distance)
break;
if (__middle1 != __first) {
--__middle1;
--__distance;
} else if (__middle2 == __last) {
break;
}
} while (true);
return std::make_pair(__middle1, __middle2);
}
template <typename _InputIter, typename _Distance>
_InputIter
advance_forward(_InputIter __first, _InputIter __last, _Distance __distance) {
while (__first != __last && __distance != 0) {
__first++;
__distance--;
}
return __first;
}
template <typename _InputIter, typename _Distance>
_InputIter
advance_backward(_InputIter __first, _InputIter __last, _Distance __distance) {
while (__first != __last && __distance != 0) {
__first--;
__distance--;
}
return __first;
}
}
#endif
+4 -4
View File
@@ -1,17 +1,17 @@
#include "config.h"
#include "display/window_download_chunks_seen.h"
#include <cmath>
#include <stdexcept>
#include <rak/string_manip.h>
#include <torrent/bitfield.h>
#include <torrent/data/block.h>
#include <torrent/data/block_list.h>
#include <torrent/data/transfer_list.h>
#include <torrent/utils/string_manip.h>
#include "core/download.h"
#include "window_download_chunks_seen.h"
namespace display {
WindowDownloadChunksSeen::WindowDownloadChunksSeen(core::Download* d, unsigned int *focus) :
@@ -82,7 +82,7 @@ WindowDownloadChunksSeen::redraw() {
attr = A_BOLD;
}
m_canvas->print_char(attr | rak::value_to_hexchar<0>(std::min<uint8_t>(*chunk, 0xF)));
m_canvas->print_char(attr | torrent::utils::value_to_hex0(std::min<uint8_t>(*chunk, 0xF)));
chunk++;
if ((chunk - seen) % 10 == 0) {
+8 -10
View File
@@ -1,17 +1,15 @@
#include "config.h"
#include "display/color_map.h"
#include <rak/algorithm.h>
#include "globals.h"
#include "core/download.h"
#include "core/view.h"
#include "display/canvas.h"
#include "display/utils.h"
#include "display/window_download_list.h"
#include "rpc/parse_commands.h"
#include "canvas.h"
#include "globals.h"
#include "utils.h"
#include "window_download_list.h"
namespace display {
WindowDownloadList::WindowDownloadList() :
@@ -104,10 +102,10 @@ WindowDownloadList::redraw() {
typedef std::pair<core::View::iterator, core::View::iterator> Range;
Range range = rak::advance_bidirectional(m_view->begin_visible(),
m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(),
m_view->end_visible(),
page_size(layout_name));
Range range = advance_bidirectional(m_view->begin_visible(),
m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(),
m_view->end_visible(),
page_size(layout_name));
// Make sure we properly fill out the last lines so it looks like
// there are more torrents, yet don't hide it if we got the last one
@@ -2,7 +2,6 @@
#include <cmath>
#include <stdexcept>
#include <rak/string_manip.h>
#include <torrent/data/block_list.h>
#include <torrent/data/transfer_list.h>
+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");
+1 -1
View File
@@ -19,7 +19,7 @@ WindowHttpQueue::WindowHttpQueue(core::HttpQueue* q) :
set_active(false);
m_conn_insert = m_queue->signal_insert().insert(m_queue->signal_insert().end(), [this](auto h) { receive_insert(h); });
m_conn_erase = m_queue->signal_erase().insert(m_queue->signal_insert().end(), [this](auto h) { receive_erase(h); });
m_conn_erase = m_queue->signal_erase().insert(m_queue->signal_erase().end(), [this](auto h) { receive_erase(h); });
m_task_deactivate.slot() = [this] {
if (!m_container.empty())
+1 -1
View File
@@ -47,7 +47,7 @@ private:
signal_curl_get::iterator m_conn_insert;
signal_curl_get::iterator m_conn_erase;
torrent::utils::SchedulerEntry m_task_deactivate;
torrent::system::SchedulerEntry m_task_deactivate;
};
}
+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>
#include "config.h"
#include "canvas.h"
@@ -46,7 +12,7 @@ WindowInput::redraw() {
m_canvas->erase();
m_canvas->print(0, 0, "%s> %s", m_title.c_str(), m_input != NULL ? m_input->c_str() : "<NULL>");
if (m_focus)
if (m_focus && m_input != NULL)
m_canvas->set_attr(m_input->get_pos() + 2 + m_title.size(), 0, 1, A_REVERSE, COLOR_PAIR(0));
}
-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_DISPLAY_WINDOW_INPUT_H
#define RTORRENT_DISPLAY_WINDOW_INPUT_H
+13 -8
View File
@@ -2,7 +2,8 @@
#include <ctime>
#include <torrent/torrent.h>
#include <torrent/utils/thread.h>
#include <torrent/system/callbacks.h>
#include <torrent/system/thread.h>
#include "canvas.h"
#include "utils.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;
+7 -3
View File
@@ -1,8 +1,8 @@
#ifndef RTORRENT_DISPLAY_WINDOW_LOG_H
#define RTORRENT_DISPLAY_WINDOW_LOG_H
#include <torrent/system/scheduler.h>
#include <torrent/utils/log_buffer.h>
#include <torrent/utils/scheduler.h>
#include "window.h"
@@ -22,8 +22,12 @@ public:
private:
inline iterator find_older();
torrent::log_buffer* m_log;
torrent::utils::SchedulerEntry m_task_update;
torrent::log_buffer* m_log;
torrent::system::SchedulerEntry m_task_update;
align_cacheline
std::atomic<bool> m_log_updating{};
};
}
+11 -12
View File
@@ -1,19 +1,18 @@
#include "config.h"
#include "display/window_peer_list.h"
#include <stdexcept>
#include <rak/socket_address.h>
#include <torrent/rate.h>
#include <torrent/data/block_transfer.h>
#include <torrent/data/piece.h>
#include <torrent/net/socket_address.h>
#include <torrent/peer/client_list.h>
#include <torrent/peer/peer_info.h>
#include "core/download.h"
#include "rak/algorithm.h"
#include "canvas.h"
#include "utils.h"
#include "window_peer_list.h"
#include "display/canvas.h"
#include "display/utils.h"
namespace display {
@@ -50,10 +49,10 @@ WindowPeerList::redraw() {
typedef std::pair<PList::iterator, PList::iterator> Range;
Range range = rak::advance_bidirectional(m_list->begin(),
*m_focus != m_list->end() ? *m_focus : m_list->begin(),
m_list->end(),
m_canvas->height() - y);
Range range = advance_bidirectional(m_list->begin(),
*m_focus != m_list->end() ? *m_focus : m_list->begin(),
m_list->end(),
m_canvas->height() - y);
if (m_download->download()->file_list()->size_chunks() <= 0)
throw std::logic_error("WindowPeerList::redraw() m_slotChunksTotal() returned invalid value");
@@ -63,7 +62,7 @@ WindowPeerList::redraw() {
x = 0;
std::string ip_address = rak::socket_address::cast_from(p->address())->address_str();
auto ip_address = torrent::sa_addr_str(p->address());
if (ip_address.size() >= 24) {
ip_address.replace(ip_address.begin() + 21, ip_address.end(), "...");
@@ -92,7 +91,7 @@ WindowPeerList::redraw() {
peerType = 'u';
else if (p->peer_info()->is_preferred())
peerType = 'p';
else
else
peerType = ' ';
m_canvas->print(x, y, "%c%c/%c%c/%c%c",
+6 -7
View File
@@ -2,10 +2,9 @@
#include "window_tracker_list.h"
#include <rak/algorithm.h>
#include <rak/string_manip.h>
#include <torrent/exceptions.h>
#include <torrent/tracker/tracker.h>
#include <torrent/utils/string_manip.h>
#include "core/download.h"
@@ -37,7 +36,7 @@ WindowTrackerList::redraw() {
if (tc_size == 0 || *m_focus >= tc_size)
return;
auto range = rak::advance_bidirectional<unsigned int>(0, *m_focus, tc_size, (m_canvas->height() - 1) / 2);
auto range = advance_bidirectional<unsigned int>(0, *m_focus, tc_size, (m_canvas->height() - 1) / 2);
auto group = tc.at(range.first).group();
while (range.first != range.second) {
@@ -52,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 = " ";
@@ -63,7 +62,7 @@ WindowTrackerList::redraw() {
m_canvas->print(0, pos++, "%s Id: %s Counters: %uf / %us (%u) %s S/L/D: %u/%u/%u (%u/%u)",
state,
rak::copy_escape_html(tracker.tracker_id()).c_str(),
torrent::utils::copy_escape_html_str(tracker.tracker_id()).c_str(),
tracker_state.failed_counter(),
tracker_state.success_counter(),
tracker_state.scrape_counter(),
@@ -80,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));
}
+23 -1
View File
@@ -2,7 +2,29 @@
#include "globals.h"
#include <cstdlib>
#include <torrent/exceptions.h>
rpc::ip_table_list ip_tables;
Control* control{};
ThreadWorker* worker_thread{};
std::string
expand_path(const std::string& path) {
if (path.empty())
return std::string();
if (path[0] == '~') {
if (path.size() >= 2 && path[1] != '/')
throw torrent::input_error("Could not expand ~ in path, only '~' or '~/...' is supported.");
const char* home = std::getenv("HOME");
if (home == nullptr || *home == '\0')
throw torrent::input_error("Could not expand ~ in path, HOME environment variable not set.");
return home + path.substr(1);
}
return path;
}
+41 -4
View File
@@ -1,14 +1,51 @@
#ifndef TORRENT_GLOBALS_H
#define TORRENT_GLOBALS_H
#include "thread_worker.h"
#include <torrent/common.h>
#include "rpc/ip_table_list.h"
class Control;
extern rpc::ip_table_list ip_tables;
extern rpc::ip_table_list ip_tables;
extern Control* control;
extern Control* control;
extern ThreadWorker* worker_thread;
std::string expand_path(const std::string& path);
namespace rpc {
class SCgi;
}
namespace session {
class SessionManager;
}
namespace scgi_thread {
torrent::system::Thread* thread();
std::thread::id thread_id();
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);
} // namespace torrent::scgi_thread
namespace session_thread {
torrent::system::Thread* thread();
std::thread::id thread_id();
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();
} // namespace torrent::session_thread
#endif
+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::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::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);
}
}
+10 -10
View File
@@ -2,24 +2,24 @@
#define RTORRENT_INPUT_INPUT_EVENT_H
#include <functional>
#include <torrent/event.h>
#include <torrent/poll.h>
#include <torrent/system/event.h>
namespace input {
class InputEvent : public torrent::Event {
class InputEvent : public torrent::system::Event {
public:
typedef std::function<void (int)> slot_int;
InputEvent(int fd) { m_fileDesc = fd; }
InputEvent(int fd) { set_file_descriptor(fd); }
void insert(torrent::Poll* p);
void remove(torrent::Poll* p);
const char* type_name() const override { return "input"; }
void event_read();
void event_write();
void event_error();
void insert();
void remove();
void event_read() override;
void event_write() override;
void event_error() override;
void slot_pressed(slot_int s) { m_slotPressed = s; }
+49 -38
View File
@@ -1,42 +1,7 @@
// 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>
#include "config.h"
#include <algorithm>
#include <functional>
#include <rak/algorithm.h>
#include <rak/path.h>
#include <dirent.h>
#include <sys/stat.h>
@@ -45,6 +10,52 @@
#include "path_input.h"
namespace {
template <typename _Value>
struct compare_base {
bool operator () (const _Value& complete, const _Value& base) const {
return !complete.compare(0, base.size(), base);
}
};
// Count the number of elements from the start of the containers to
// the first inequal element.
template <typename _InputIter1, typename _InputIter2>
typename std::iterator_traits<_InputIter1>::difference_type
count_base(_InputIter1 __first1, _InputIter1 __last1,
_InputIter2 __first2, _InputIter2 __last2) {
typename std::iterator_traits<_InputIter1>::difference_type __n = 0;
for ( ;__first1 != __last1 && __first2 != __last2; ++__first1, ++__first2, ++__n)
if (*__first1 != *__first2)
return __n;
return __n;
}
template <typename _Return, typename _InputIter, typename _Ftor>
_Return
make_base(_InputIter __first, _InputIter __last, _Ftor __ftor) {
if (__first == __last)
return "";
_Return __base = __ftor(*__first++);
for ( ;__first != __last; ++__first) {
auto __pos = count_base(__base.begin(), __base.end(),
__ftor(*__first).begin(), __ftor(*__first).end());
if (__pos < (typename std::iterator_traits<_InputIter>::difference_type)__base.size())
__base.resize(__pos);
}
return __base;
}
}
namespace input {
bool
@@ -73,7 +84,7 @@ PathInput::receive_do_complete() {
size_type dirEnd = find_last_delim();
utils::Directory dir(dirEnd != 0 ? str().substr(0, dirEnd) : "./");
if (!dir.update(utils::Directory::update_sort | utils::Directory::update_hide_dot) || dir.empty()) {
mark_dirty();
@@ -94,7 +105,7 @@ PathInput::receive_do_complete() {
if (r.first == r.second)
return; // Show some nice colors here.
std::string base = rak::make_base<std::string>(r.first, r.second, std::mem_fn(&utils::directory_entry::s_name));
std::string base = make_base<std::string>(r.first, r.second, [](auto& de) { return de.s_name; });
// Clear the path after the cursor to make this code cleaner. It's
// not really nessesary to add the complexity just because someone
+208 -203
View File
@@ -1,7 +1,5 @@
#include "config.h"
#define __STDC_FORMAT_MACROS
#include <cstdlib>
#include <iostream>
#include <sstream>
@@ -12,9 +10,12 @@
#include <torrent/torrent.h>
#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>
#include <rak/error_number.h>
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
@@ -22,28 +23,27 @@
#include "core/dht_manager.h"
#include "core/download.h"
#include "core/download_factory.h"
#include "core/download_store.h"
#include "core/manager.h"
#include "display/canvas.h"
#include "display/window.h"
#include "display/manager.h"
#include "input/bindings.h"
#include "ui/root.h"
#include "rpc/command_scheduler.h"
#include "rpc/command_scheduler_item.h"
#include "rpc/parse_commands.h"
#include "scgi/thread_scgi.h"
#include "session/session_manager.h"
#include "session/thread_session.h"
#include "ui/root.h"
#include "utils/directory.h"
#include "control.h"
#include "command_helpers.h"
#include "globals.h"
#include "setup.h"
#include "signal_handler.h"
#include "option_parser.h"
#include "thread_worker.h"
#define LT_LOG(log_fmt, ...) \
lt_log_print(torrent::LOG_SYSTEM, "system: " log_fmt, __VA_ARGS__);
@@ -52,37 +52,6 @@ void do_panic(int signum);
void print_help();
void initialize_commands();
void do_nothing() {}
void do_nothing_str(const std::string&) {}
int
parse_options(int argc, char** argv) {
try {
OptionParser optionParser;
// Converted.
optionParser.insert_flag('h', std::bind(&print_help));
optionParser.insert_flag('n', std::bind(&do_nothing_str, std::placeholders::_1));
optionParser.insert_flag('D', std::bind(&do_nothing_str, std::placeholders::_1));
optionParser.insert_flag('I', std::bind(&do_nothing_str, std::placeholders::_1));
optionParser.insert_flag('K', std::bind(&do_nothing_str, std::placeholders::_1));
optionParser.insert_option('b', std::bind(&rpc::call_command_set_string, "network.bind_address.set", std::placeholders::_1));
optionParser.insert_option('d', std::bind(&rpc::call_command_set_string, "directory.default.set", std::placeholders::_1));
optionParser.insert_option('i', std::bind(&rpc::call_command_set_string, "ip", std::placeholders::_1));
optionParser.insert_option('p', std::bind(&rpc::call_command_set_string, "network.port_range.set", std::placeholders::_1));
optionParser.insert_option('s', std::bind(&rpc::call_command_set_string, "session", std::placeholders::_1));
optionParser.insert_option('O', std::bind(&rpc::parse_command_single_std, std::placeholders::_1));
optionParser.insert_option_list('o', std::bind(&rpc::call_command_set_std_string, std::placeholders::_1, std::placeholders::_2));
return optionParser.process(argc, argv);
} catch (torrent::input_error& e) {
throw torrent::input_error("Failed to parse command line option: " + std::string(e.what()));
}
}
void
initialize_rpc_slots() {
rpc::rpc.slot_find_download() = [](const char* hash) {
@@ -112,43 +81,6 @@ initialize_rpc_slots() {
};
}
void
load_session_torrents() {
utils::Directory entries = control->core()->download_store()->get_formated_entries();
for (const auto& entry : entries) {
// We don't really support session torrents that are links. These
// would be overwritten anyway on exit, and thus not really be
// useful.
if (!entry.is_file())
continue;
core::DownloadFactory* f = new core::DownloadFactory(control->core());
// Replace with session torrent flag.
f->set_session(true);
f->set_init_load(true);
f->slot_finished([f](){ delete f; });
f->load(entries.path() + entry.s_name);
f->commit();
}
}
void
load_arg_torrents(char** first, char** last) {
//std::for_each(begin, end, std::bind1st(std::mem_fun(&core::Manager::insert), &control->get_core()));
for (; first != last; ++first) {
core::DownloadFactory* f = new core::DownloadFactory(control->core());
// Replace with session torrent flag.
f->set_start(true);
f->set_init_load(true);
f->slot_finished([f](){ delete f; });
f->load(*first);
f->commit();
}
}
static void
client_perform() {
// Use throw exclusively.
@@ -178,34 +110,50 @@ main(int argc, char** argv) {
srandom(random_seed);
srand48(random_seed);
torrent::initialize_main_thread();
torrent::log_initialize();
control = new Control;
// TODO: Create a fake thread object for initializing other processes and enabling logging.
torrent::initialize_main_thread();
SignalHandler::set_ignore(SIGPIPE);
SignalHandler::set_handler(SIGINT, std::bind(&Control::receive_normal_shutdown, control));
SignalHandler::set_handler(SIGHUP, std::bind(&Control::receive_normal_shutdown, control));
SignalHandler::set_handler(SIGTERM, std::bind(&Control::receive_quick_shutdown, control));
SignalHandler::set_handler(SIGWINCH, std::bind(&display::Manager::force_redraw, control->display()));
// 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));
// Limited list of commands with the following format:
//
// cat ~/.rtorrent.rc:
//
// # do:log.open_file=system,/usr/rakshasa/system.log
// # do:log.add_output=system,system
//
parse_config_file(argc, argv, [](auto& path) {
if (path.empty())
return;
parse_config_file_comments(path);
});
control = new Control;
SignalHandler::set_handler(SIGINT, std::bind(&Control::receive_normal_shutdown, control));
SignalHandler::set_handler(SIGHUP, std::bind(&Control::receive_normal_shutdown, control));
SignalHandler::set_handler(SIGTERM, std::bind(&Control::receive_quick_shutdown, control));
SignalHandler::set_handler(SIGWINCH, std::bind(&display::Manager::force_redraw, control->display()));
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::utils::Thread::should_handle_sigusr1())
SignalHandler::set_handler(SIGUSR1, std::bind(&do_nothing));
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");
@@ -214,10 +162,12 @@ 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);
worker_thread = new ThreadWorker();
worker_thread->init_thread();
scgi::ThreadScgi::create_thread();
session::ThreadSession::create_thread();
SignalHandler::set_unblock(SIGCHLD);
// Initialize option handlers after libtorrent to ensure
// torrent::ConnectionManager* are valid etc.
@@ -263,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"
@@ -281,7 +238,7 @@ main(int argc, char** argv) {
"file.prioritize_toc.first.set = {*.avi,*.mp4,*.mkv,*.gz}\n"
"file.prioritize_toc.last.set = {*.zip}\n"
// Allow setting 'group2.view' as constant, so that we can't
// Allow setting 'group.view' as constant, so that we can't
// modify the value. And look into the possibility of making
// 'const' use non-heap memory, as we know they can't be
// erased.
@@ -337,14 +294,12 @@ 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"
"protocol.encryption.set=allow_incoming,prefer_plaintext,enable_retry\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"
"ui.color.focus.set=reverse\n"
);
@@ -352,139 +307,180 @@ 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_GENERIC("upload_rate", "throttle.global_up.max_rate.set_kb");
CMD2_REDIRECT_GENERIC("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_GENERIC("ratio.enable", "group.seeding.ratio.enable");
CMD2_REDIRECT_GENERIC("ratio.disable", "group.seeding.ratio.disable");
CMD2_REDIRECT_GENERIC("ratio.min", "group2.seeding.ratio.min");
CMD2_REDIRECT_GENERIC("ratio.max", "group2.seeding.ratio.max");
CMD2_REDIRECT_GENERIC("ratio.upload", "group2.seeding.ratio.upload");
CMD2_REDIRECT_GENERIC("ratio.min.set", "group2.seeding.ratio.min.set");
CMD2_REDIRECT_GENERIC("ratio.max.set", "group2.seeding.ratio.max.set");
CMD2_REDIRECT_GENERIC("ratio.upload.set", "group2.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_GENERIC("encryption", "protocol.encryption.set");
CMD2_REDIRECT_GENERIC("encoding_list", "encoding.add");
CMD_REDIRECT("encryption", "protocol.encryption.set");
CMD2_REDIRECT_GENERIC("connection_leech", "protocol.connection.leech.set");
CMD2_REDIRECT_GENERIC("connection_seed", "protocol.connection.seed.set");
CMD_REDIRECT("check_hash", "pieces.hash.on_completion.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("connection_leech", "protocol.connection.leech.set");
CMD_REDIRECT("connection_seed", "protocol.connection.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_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 ("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("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_GENERIC("max_memory_usage", "pieces.memory.max.set");
CMD_REDIRECT("max_uploads_div", "throttle.max_uploads.div.set");
CMD_REDIRECT("max_uploads_global", "throttle.max_uploads.global.set");
CMD_REDIRECT("max_downloads_div", "throttle.max_downloads.div.set");
CMD_REDIRECT("max_downloads_global", "throttle.max_downloads.global.set");
CMD2_REDIRECT ("bind", "network.bind_address.set");
CMD2_REDIRECT ("ip", "network.local_address.set");
CMD2_REDIRECT ("port_range", "network.port_range.set");
CMD_REDIRECT("directory", "directory.default.set");
CMD_REDIRECT("session", "session.path.set");
CMD2_REDIRECT_GENERIC("dht", "dht.mode.set");
CMD2_REDIRECT_GENERIC("dht_port", "dht.port.set");
CMD_REDIRECT_NO_EXPORT("port_range", "network.listen.port.range.set");
CMD_REDIRECT_NO_EXPORT("scgi_port", "network.scgi.open_port");
CMD_REDIRECT_NO_EXPORT("scgi_local", "network.scgi.open_local");
CMD2_REDIRECT ("port_random", "network.port_random.set");
CMD2_REDIRECT ("proxy_address", "network.proxy_address.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 ("scgi_port", "network.scgi.open_port");
CMD2_REDIRECT ("scgi_local", "network.scgi.open_local");
// TODO: Deprecate these at some point a while after 1.1 release.
CMD2_REDIRECT_GENERIC("directory", "directory.default.set");
CMD2_REDIRECT_GENERIC("session", "session.path.set");
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 ("check_hash", "pieces.hash.on_completion.set");
rpc::rpc.mark_safe("d.multicall2");
rpc::rpc.mark_safe("network.max_open_sockets");
rpc::rpc.mark_safe("network.http.proxy_address");
CMD2_REDIRECT ("key_layout", "keys.layout.set");
CMD2_ANY_VALUE_V("network.http.max_total_connections.set", [](auto, auto) {
lt_log_print(torrent::LOG_WARN, "network.http.max_total_connections.set is deprecated, use system.sockets.http.min_alloc.set instead.");
});
CMD2_REDIRECT_GENERIC("to_gm_time", "convert.gm_time");
CMD2_REDIRECT_GENERIC("to_gm_date", "convert.gm_date");
CMD2_REDIRECT_GENERIC("to_time", "convert.time");
CMD2_REDIRECT_GENERIC("to_date", "convert.date");
CMD2_REDIRECT_GENERIC("to_elapsed_time", "convert.elapsed_time");
CMD2_REDIRECT_GENERIC("to_kb", "convert.kb");
CMD2_REDIRECT_GENERIC("to_mb", "convert.mb");
CMD2_REDIRECT_GENERIC("to_xb", "convert.xb");
CMD2_REDIRECT_GENERIC("to_throttle", "convert.throttle");
CMD2_REDIRECT ("torrent_list_layout", "ui.torrent_list.layout.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_GENERIC("execute", "execute2");
// CMD2_REDIRECT_GENERIC("schedule", "schedule2");
// CMD2_REDIRECT_GENERIC("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_GENERIC_NO_EXPORT("execute", "execute2");
// CMD2_REDIRECT_GENERIC_NO_EXPORT("schedule", "schedule2");
// CMD2_REDIRECT_GENERIC_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");
int firstArg = parse_options(argc, argv);
CMD_REDIRECT("bind", "network.bind_address.set");
CMD_REDIRECT("ip", "network.local_address.set");
if (OptionParser::has_flag('n', argc, argv)) {
lt_log_print(torrent::LOG_WARN, "Ignoring rtorrent.rc.");
} else {
char* config_dir = std::getenv("XDG_CONFIG_HOME");
char* home_dir = std::getenv("HOME");
// TODO: Check if dht is on by default.
CMD_REDIRECT("dht", "dht.mode.set");
if (config_dir != NULL && config_dir[0] == '/' &&
access((std::string(config_dir) + "/rtorrent/rtorrent.rc").c_str(), F_OK) != -1) {
rpc::parse_command_single(rpc::make_target(), "try_import = " + std::string(config_dir) + "/rtorrent/rtorrent.rc");
} else if (home_dir != NULL && access((std::string(home_dir) + "/.config/rtorrent/rtorrent.rc").c_str(), F_OK) != -1) {
rpc::parse_command_single(rpc::make_target(), "try_import = ~/.config/rtorrent/rtorrent.rc");
CMD_REDIRECT("port_random", "network.listen.port.random.set");
CMD_REDIRECT("proxy_address", "network.proxy.http.set");
CMD_REDIRECT("key_layout", "keys.layout.set");
CMD_REDIRECT("torrent_list_layout", "ui.torrent_list.layout.set");
CMD2_VAR_STRING("dht.throttle.name", "deprecated");
rpc::rpc.mark_safe("dht.throttle.name");
CMD_REDIRECT("network.http.max_open", "network.http.max_total_connections");
CMD_REDIRECT("network.http.max_open.set", "network.http.max_total_connections.set");
// Users should check their setups to see if they need to modify their use of these options.
CMD_REDIRECT("max_memory_usage", "pieces.memory.max.set");
CMD_ANY_LIST("throttle.ip", []( auto, auto) {
lt_log_print(torrent::LOG_WARN, "The 'throttle.ip' command is deprecated and does nothing.");
return torrent::Object();
});
CMD_ANY("network.port_open", [](auto, auto) {
lt_log_print(torrent::LOG_WARN, "The 'network.port_open' command is deprecated and does nothing.");
return torrent::Object();
});
CMD_ANY("network.port_open.set", [](auto, auto) {
lt_log_print(torrent::LOG_WARN, "The 'network.port_open.set' command is deprecated and does nothing.");
return torrent::Object();
});
CMD_REDIRECT("network.port_random", "network.listen.port.random");
CMD_REDIRECT("network.port_random.set", "network.listen.port.random.set");
CMD_REDIRECT("network.port_range", "network.listen.port.range");
CMD_REDIRECT("network.port_range.set", "network.listen.port.range.set");
}
{
auto fd = torrent::fd_open_family(torrent::fd_flag_stream, AF_INET6);
if (fd == -1) {
if (errno == EAFNOSUPPORT) {
lt_log_print(torrent::LOG_WARN, "disabling ipv6 support, not available on system");
rpc::call_command_set_value("network.block.ipv6.set", true);
}
} else {
rpc::parse_command_single(rpc::make_target(), "try_import = ~/.rtorrent.rc");
torrent::fd_close(fd);
}
}
int firstArg = parse_main_options(argc, argv);
parse_config_file(argc, argv, [](auto& path) {
if (path.empty()) {
lt_log_print(torrent::LOG_WARN, "Ignoring rtorrent.rc.");
return;
}
rpc::parse_command_single(rpc::make_target(), "try_import=" + path);
});
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();
// TODO: Check if this is required.
// rak::priority_queue_perform(&taskScheduler, cachedTime);
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();
worker_thread->start_thread();
rpc::commands.call_catch("event.system.startup_done", rpc::make_target(), "startup_done", "System startup_done event action failed: ");
torrent::utils::Thread::self()->event_loop();
torrent::system::Thread::self()->event_loop();
control->core()->download_list()->session_save();
control->cleanup();
@@ -498,6 +494,8 @@ main(int argc, char** argv) {
lt_log_print_dump(torrent::LOG_CRITICAL, e.backtrace().c_str(), e.backtrace().size(),
"Caught internal_error: '%s'.", e.what());
torrent::log_cleanup();
return -1;
} catch (std::exception& e) {
@@ -506,13 +504,18 @@ main(int argc, char** argv) {
std::cout << "rtorrent: caught" << typeid(e).name() << " : " << e.what() << std::endl;
lt_log_print(torrent::LOG_CRITICAL, "Caught exception: '%s'.", e.what());
torrent::log_cleanup();
return -1;
}
torrent::log_cleanup();
delete control;
delete worker_thread;
control = nullptr;
scgi::ThreadScgi::destroy_thread();
session::ThreadSession::destroy_thread();
torrent::log_cleanup();
return 0;
}
@@ -541,7 +544,7 @@ handle_sigbus(int signum, siginfo_t* sa, [[maybe_unused]] void* ptr) {
#else
output << "Stack dump not enabled." << std::endl;
#endif
output << std::endl << "Error: " << rak::error_number(sa->si_errno).c_str() << std::endl;
output << std::endl << "Error: " << std::strerror(sa->si_errno) << std::endl;
const char* signal_reason;
@@ -593,7 +596,9 @@ do_panic(int signum) {
// Use the default signal handler in the future to avoid infinit
// loops.
SignalHandler::set_default(signum);
display::Canvas::cleanup();
if (control != nullptr)
display::Canvas::cleanup();
std::stringstream output;
+6 -37
View File
@@ -1,39 +1,7 @@
// 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>
#include "config.h"
#include "option_parser.h"
#include <algorithm>
#include <cstring>
#include <cstdio>
@@ -42,8 +10,6 @@
#include <stdexcept>
#include <unistd.h>
#include "option_parser.h"
void
OptionParser::insert_flag(char c, slot_string s) {
m_container[c].m_slot = s;
@@ -137,8 +103,11 @@ void
OptionParser::call_int_pair(slot_int_pair slot, const std::string& arg) {
int a, b;
if (std::sscanf(arg.c_str(), "%u-%u", &a, &b) != 2)
if (std::sscanf(arg.c_str(), "%d-%d", &a, &b) != 2)
throw std::runtime_error("Invalid argument, \"" + arg + "\" should be \"a-b\"");
if (a < 0 || b < 0)
throw std::runtime_error("Invalid argument, \"" + arg + "\" should be positive numbers");
slot(a, b);
}
-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_OPTION_PARSER_H
#define RTORRENT_OPTION_PARSER_H
+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; }; \

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