Compare commits

..

17 Commits

Author SHA1 Message Date
rakshasa d3ecc83b32 Release 0.15.7. 2025-09-05 07:28:59 +00:00
Bryant Eadon 457d5170b8 cleanup of personal information
Given the change to the README I thought it might be a good idea to also update the copyright notices which had an address too.
2025-09-05 07:28:47 +00:00
Jari Sundell 1c598d51ef Fix formatting and capitalization in README.md 2025-09-05 07:28:34 +00:00
Jari Sundell 644ea6c23f Revise README structure and donation information
Updated sections in README for clarity and formatting.
2025-09-05 07:28:25 +00:00
Bryant Eadon de3615a72c README combined
Combined README files to reduce clutter.  However, the build instructions are incomplete ... I've started by modifying these so a build will complete using the instructions once I get a closer look at libtorrent ... additions welcome.
2025-09-05 07:28:18 +00:00
Bryant Eadon e9e4064bb3 Delete uused file
minor cleanup.
2025-09-05 07:28:10 +00:00
Bryant Eadon 04fc51e76b Update README.md
proposing a new message for the release download.  to draw attention to the github repo, instead of the archive.
2025-09-05 07:28:02 +00:00
rakshasa 24693fc19e Release 0.15.6. 2025-08-23 08:08:36 +00:00
rakshasa 9661a2aaa9 Added test for xmlrpc string reflection. 2025-08-11 10:18:20 +02:00
rakshasa aaf42c247e Strict checking of d.views push_back arguments. 2025-08-11 10:16:23 +02:00
rakshasa cba48f4a10 Invert color of focus ui element. 2025-08-11 10:14:46 +02:00
rakshasa 4463bf418e Release 0.15.5. 2025-06-22 16:44:05 +00:00
Phil Rosenthal 8bd7c79755 Fix file descriptor leak in session file saving
When system.files.session.fdatasync is set to "no", file descriptors
  were not being closed after writing session files, causing a severe
  resource leak. Each save operation would leak one file descriptor.

  With hundreds of torrents, this leads to tens of thousands of leaked
  file descriptors within hours, mostly pointing to deleted session files.
  This can exhaust the system's file descriptor limit and cause rtorrent
  to fail when opening new files.

  The fix moves the close() call outside the fdatasync conditional block,
  ensuring file descriptors are always properly closed regardless of the
  fdatasync setting.
2025-06-18 19:04:45 +02:00
rakshasa f2b83d50e8 Use libtorrent stable-0.15 branch in workflow. 2025-06-08 13:01:36 +02:00
rakshasa ab40059891 Use libtorrent stable-0.15 branch in workflow. 2025-06-08 13:01:36 +02:00
rakshasa b9d880dea3 Properly handle -1 value passed to curl set_timeout. 2025-06-08 13:01:36 +02:00
rakshasa b4c59d2c7a Fixed corrupted stack in curl stack due to wrong argument type. 2025-06-07 13:17:34 +02:00
199 changed files with 8553 additions and 8091 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
permissions:
pull-requests: write
# OPTIONAL: auto-closing conversations requires the `contents` permission
contents: read
contents: write
steps:
- name: Sleep for 30 seconds
run: sleep 30s
+5 -7
View File
@@ -11,16 +11,11 @@ jobs:
- name: Update Packages
run: |
sudo apt-get update
- name: Install Dependencies
run: |
sudo apt-get install -y \
bear \
clang-tidy \
libcurl4-openssl-dev \
zlib1g-dev
- name: Fetch libtorrent
run: |
git clone https://github.com/rakshasa/libtorrent
cd libtorrent
git checkout stable-0.15
- name: Build libtorrent
run: |
cd libtorrent
@@ -42,6 +37,9 @@ jobs:
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 }}"
- name: Install Dependencies
run: |
sudo apt-get install -y bear clang-tidy libcurl4-openssl-dev
- name: Configure Project
run: |
libtoolize
+27 -76
View File
@@ -4,104 +4,55 @@ on: pull_request
jobs:
ubuntu-base:
unit-tests:
runs-on: ubuntu-22.04
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
- name: Fetch libtorrent
run: |
git clone https://github.com/rakshasa/libtorrent
cd libtorrent
git checkout stable-0.15
- name: Build libtorrent
run: |
cd libtorrent
autoreconf -fiv
libtoolize
aclocal -I scripts
autoconf -i
autoheader
automake --add-missing
./configure
make
sudo make install DESTDIR=$GITHUB_WORKSPACE/libtorrent-dist
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
sudo make install
cd ..
rm -rf libtorrent
- uses: actions/checkout@v4
with:
name: installed-libtorrent
path: libtorrent-dist/
unit-tests-default:
runs-on: ubuntu-22.04
needs: ubuntu-base
steps:
- name: Update Packages
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Fetch base branch
run: |
sudo apt-get update
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 }}"
- 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: |
autoreconf -fiv
libtoolize
aclocal -I scripts
autoconf -i
autoheader
automake --add-missing
./configure
- name: Build Project
run: |
make
ls /usr/local/lib/
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
- name: Run Unit Tests
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
make check
-2
View File
@@ -18,7 +18,6 @@
.libs
Makefile
aclocal.m4
actmp.*
ar-lib
autom4te.cache
compile
@@ -70,5 +69,4 @@ TAGS
###########################
src/rtorrent
test/rtorrent_Test*
test/test-suite.log.tmp
test-driver
+85
View File
@@ -0,0 +1,85 @@
language: cpp
env:
global:
- MAKEFLAGS="-j 12"
matrix:
include:
- compiler: clang
env: COMPILER=clang++ SKIP_CHECK=true
- compiler: clang
env: COMPILER=clang++
addons:
apt:
packages:
- libcppunit-dev
- compiler: clang
env: COMPILER=clang++-3.6
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.6
packages:
- clang-3.6
- libcppunit-dev
- compiler: clang
env: COMPILER=clang++-3.7
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.7
packages:
- clang-3.7
- libcppunit-dev
- compiler: clang
env: COMPILER=clang++-3.8
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.8
packages:
- clang-3.8
- libcppunit-dev
- compiler: gcc
env: COMPILER=g++-4.7 SKIP_CHECK=true
addons:
apt:
sources: ubuntu-toolchain-r-test
packages:
- g++-4.7
- compiler: gcc
env: COMPILER=g++-4.7
addons:
apt:
sources: ubuntu-toolchain-r-test
packages:
- g++-4.7
- libcppunit-dev
- compiler: gcc
env: COMPILER=g++-4.8
addons:
apt:
sources: ubuntu-toolchain-r-test
packages:
- g++-4.8
- libcppunit-dev
# TODO: Use the same branch name if libtorrent has it.
before_install:
- git clone https://github.com/rakshasa/libtorrent.git
- |
pushd libtorrent \
&& (git checkout ${TRAVIS_BRANCH} || true) \
&& ./autogen.sh \
&& CXX="$COMPILER" ./configure --prefix=/usr \
&& make \
&& sudo make install \
&& popd
script:
- ./autogen.sh && CXX="$COMPILER" ./configure && make
- if [ ! $SKIP_CHECK ]; then make check; fi
View File
+12 -3
View File
@@ -3,10 +3,19 @@ SUBDIRS = \
src \
test
nobase_dist_pkgdata_DATA = \
lua/rtorrent.lua
EXTRA_DIST= \
rtorrent.lua \
rak/address_info.h \
rak/algorithm.h \
rak/error_number.h \
rak/file_stat.h \
rak/fs_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://github.com/rakshasa/libtorrent) with the same version rTorrent.
Install [libtorrent](https://libtorrent.org/) 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://github.com/rakshasa/libtorrent) closely, and thus the versions must be in sync.
RTorrent follows the development of [libtorrent](https://libtorrent.org/) closely, and thus the versions must be in sync.
## USAGE
+30 -24
View File
@@ -1,6 +1,6 @@
m4_pattern_allow([PKG_CHECK_EXISTS])
AC_INIT([rtorrent],[0.16.18],[sundell.software@gmail.com])
AC_INIT([rtorrent],[0.15.7],[sundell.software@gmail.com])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIRS([scripts])
@@ -10,11 +10,14 @@ AM_PROG_AR
LT_INIT
AC_PROG_CXX
AX_CXX_COMPILE_STDCXX(20, noext, mandatory)
AC_DEFINE([API_VERSION], [15], [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)
AC_DEFINE([API_VERSION], [23], [api version])
AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory])
AC_SYS_LARGEFILE
RAK_CHECK_CFLAGS
RAK_CHECK_CXXFLAGS
@@ -22,6 +25,15 @@ RAK_ENABLE_DEBUG
RAK_ENABLE_EXTRA_DEBUG
RAK_ENABLE_WERROR
TORRENT_DISABLE_IPV6
TORRENT_ENABLE_ARCH
TORRENT_WITH_SYSROOT
TORRENT_WITHOUT_VARIABLE_FDSET
TORRENT_WITHOUT_STATVFS
TORRENT_WITHOUT_STATFS
AC_ARG_ENABLE(execinfo,
AS_HELP_STRING([--disable-execinfo],
[disable libexecinfo [[default=enable]]]),
@@ -34,46 +46,40 @@ AC_ARG_ENABLE(execinfo,
])
AX_PTHREAD([], AC_MSG_ERROR([requires pthread]))
AX_WITH_CURSES
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
if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
AC_MSG_ERROR([requires either NcursesW or Ncurses library])
fi
PKG_CHECK_MODULES([LIBCURL], [libcurl],, [LIBCURL_CHECK_CONFIG])
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
PKG_CHECK_MODULES([ZLIB], [zlib])
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.18])
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.15.7])
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)], Http user agent)
AC_DEFINE(HAVE_CONFIG_H, 1, true if config.h was included)
AC_DEFINE(USER_AGENT, [std::string(PACKAGE "/" VERSION "/") + torrent::version()], Http user agent)
AC_CHECK_FUNCS(posix_memalign)
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 $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"
LIBS="$PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $LIBCURL $LIBCURL_LIBS $DEPENDENCIES_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
TORRENT_CHECK_CACHELINE
TORRENT_CHECK_POSIX_SPAWN_ADDCLOSEFROM_NP
TORRENT_CHECK_CACHELINE()
TORRENT_CHECK_POPCOUNT()
AC_CONFIG_FILES([
Makefile
+4 -14
View File
@@ -4,30 +4,20 @@
# 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_all", "tracker.log"
log.add_output = "tracker_events", "tracker.log"
log.add_output = "tracker_requests", "tracker.log"
log.add_output = "dht_debug", "tracker.log"
log.add_output = "tracker_debug", "tracker.log"
Each log handle can be added to multiple different logging events.
@@ -41,7 +31,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 receiving events from the event
displayed below, and each event also reciving events from the event
above in importance.
Thus some high-volume sub-group events such as “tracker\_debug” are not
+3 -4
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,9 +20,8 @@ Some control over formatting will be provided at a later date.
log.add_output = "info", "rtorrent.log"
log.add_output = "dht_all", "tracker.log"
log.add_output = "tracker_events", "tracker.log"
log.add_output = "tracker_requests", "tracker.log"
log.add_output = "dht_debug", "tracker.log"
log.add_output = "tracker_debug", "tracker.log"
\end{verbatim}
Each log handle can be added to multiple different logging events.
+1 -9
View File
@@ -46,12 +46,8 @@
#session.path.set = ./session
# Watch a directory for new torrents, and stop those that have been
# 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.
# deleted.
#
#directory.watch.ready = ./watch, load.start
#schedule2 = watch_directory,5,5,load.start=./watch/*.torrent
# Close torrents when disk-space is low.
@@ -123,10 +119,6 @@
#
#ui.torrent_list.layout.set = "full"
# Set navigation keymap style ("vi", "emacs"). Default is "emacs".
#
#ui.keymap.style.set = "emacs"
# Run rTorrent as a daemon, controlled via XMLRPC.
#
#system.daemon.set = false
+1 -4
View File
@@ -101,11 +101,8 @@ 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_events", "log"
#log.add_output = "tracker_requests", "log"
##log.add_output = "tracker_debug", "log"
### END of rtorrent.rc ###
-142
View File
@@ -1,142 +0,0 @@
--[[
A minimal rTorrent configuration that provides the basic features
you want to have in addition to the built-in defaults.
See https://github.com/rakshasa/rtorrent/wiki/CONFIG-Template
for an up-to-date version.
How to use this file:
1. Copy to destination, remove '-example' suffix
2. Create rtorrent.rc, containing single command, which executes THIS file.
For example:
lua.execute = (cat, (system.env, HOME), "/.config/rtorrent/rtorrent.rc.lua")
]]--
--[[ Bootstrap START ]]--
local rtorrent = require('rtorrent')
rc = rtorrent.autocall
--[[ Helper functions ]]--
local function makeArgs(cmdline)
return 'sh', '-c', "'"..table.concat(cmdline, "' '").."'"
end
-- Instance layout (base paths)
local home = os.getenv('HOME')
local cfg = {}
cfg.basedir = home..'/rtorrent/'
cfg.download = cfg.basedir..'download/'
cfg.logs = cfg.basedir..'log/'
cfg.logfile = cfg.logs..'rtorrent-'..rc.system.time()..'.log'
cfg.session = cfg.basedir..'.session/'
cfg.watch = cfg.basedir..'watch/'
rc.method.insert('cfg.download', 'private|const|string', cfg.download)
rc.method.insert('cfg.logs', 'private|const|string', cfg.logs)
rc.method.insert('cfg.logfile', 'private|const|string', cfg.logfile)
rc.method.insert('cfg.session', 'private|const|string', cfg.session)
rc.method.insert('cfg.watch', 'private|const|string', cfg.watch)
-- Create instance directories
rc.execute.throw(
makeArgs({'mkdir', '-p',
cfg.download,
cfg.logs,
cfg.session,
cfg.watch..'/load',
cfg.watch..'/start'}))
-- Listening port for incoming peer traffic (fixed; you can also randomize it)
rc.network.port_range = '50000-50000'
rc.network.port_random = false
-- Tracker-less torrent and UDP tracker support
-- (conservative settings for 'private' trackers, change for 'public')
rc.dht.mode = 'disable'
rc.protocol.pex = false
rc.trackers.use_udp = false
-- Peer settings
rc.throttle.max_uploads = 100
rc.throttle.max_uploads.global = 250
rc.throttle.min_peers.normal = 20
rc.throttle.max_peers.normal = 60
rc.throttle.min_peers.seed = 30
rc.throttle.max_peers.seed = 80
rc.trackers.numwant = 80
rc.protocol.encryption.set('allow_incoming', 'try_outgoing', 'enable_retry')
-- Limits for file handle resources, this is optimized for
-- an `ulimit` of 1024 (a common default). You MUST leave
-- a ceiling of handles reserved for rTorrent's internal needs!
rc.network.http.max_open = 50
rc.network.max_open_files = 600
rc.network.max_open_sockets = 300
-- Memory resource usage (increase if you have a large number of items loaded,
-- and/or the available resources to spend)
rc.pieces.memory.max = '1800M'
rc.network.xmlrpc.size_limit = '4M'
-- Basic operational settings (no need to change these)
rc.session.path = cfg.session
rc.directory.default = cfg.download
rc.log.execute(cfg.logs.."execute.log")
--rc.log.xmlrpc(cfg.logs.."xmlrpc.log")
rc.execute.nothrow(
"sh", "-c", table.concat(
{"echo >", rc.session.path(), "rtorrent.pid", " ", rc.system.pid()
}))
-- Other operational settings (check & adapt)
rc.encoding.add('utf8')
rc.system.umask = 0027
rc.system.cwd = rc.directory.default()
rc.network.http.dns_cache_timeout = 25
rc.schedule2('monitor_diskspace', '15', '60', 'close_low_diskspace=1000M')
--rc.pieces.hash.on_completion = false
--rc.view.sort_current('seeding', 'greater=d.ratio=')
--rc.keys.layout = 'qwerty'
--rc.network.http.capath = '/etc/ssl/certs'
--rc.network.http.ssl_verify_peer = 0
--rc.network.http.ssl_verify_host = 0
--rc.network.rpc.use_xmlrpc = true
--rc.network.rpc.use_jsonrpc = true
-- Some additional values and commands
-- NOTE: just common names, not commands
rc.method.insert('system.startup_time', 'value|const', rc.system.time())
rc.method.insert('d.data_path', 'simple',
[[if=(d.is_multi_file),
(cat, (d.directory), /),
(cat, (d.directory), /, (d.name))]])
rc.method.insert('d.session_file', 'simple', 'cat=(session.path), (d.hash), .torrent')
-- Watch directories (add more as you like, but use unique schedule names)
rc.schedule2('watch_start', '10', '10', 'load.start_verbose=(cat, (cfg.watch), "start/*.torrent")')
rc.schedule2('watch_load', '11', '10', 'load.verbose=(cat, (cfg.watch), "load/*.torrent")')
-- Run the rTorrent process as a daemon in the background
-- (and control via XMLRPC sockets)
--rc.system.daemon = true
--rc.network.scgi.open_local(cfg.session..'rtorrent.sock')
--rc.execute.nothrow('chmod', '770', cfg.session..'rtorrent.sock')
-- Logging:
-- Levels = critical error warn notice info debug
-- Groups = connection_* dht_* peer_* rpc_* storage_* thread_* tracker_* torrent_*
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_events', 'log')
--rc.log.add_output('tracker_requests', 'log')
--[[ END of rtorrent.rc.lua ]]--
-1
View File
@@ -1 +0,0 @@
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
@@ -1,343 +0,0 @@
{
<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
}
-127
View File
@@ -1,127 +0,0 @@
-- The "rtorrent" table is passed in by the C++ code, modify and
-- return it for loading.
local args = {...}
local rtorrent = args[1]
local math = require('math')
-- Autocall
-- Passes an empty first argument implicitly, as "".
-- Allows syntax like:
-- - `rtorrent.autocall.system.hostname()` or
-- - `rtorrent.autocall.session.directory.set("/tmp/")`
-- Has assignment operator, syntax sugar for `.set()` with single argument
-- - `rtorrent.autocall.session.directory = "/tmp/"`
-- Autocall-chains can be reused as aliases:
-- ```lua
-- a = rtorrent.autocall.system
-- a.hostname() -- same as `rtorrent.autocall.system.hostname()`
-- a.pid() -- same as `rtorrent.autocall.system.pid()`
local mt = {}
function mt.__call (t, ...)
name = table.concat(rawget(t, "__namestack"), ".")
tg = rawget(t, "__target") or ""
success, ret = pcall(rtorrent.call, name, tg, ...)
if not success then error(name..": "..ret, 2) end
return ret
end
function mt.__index (t, key)
ns = {table.unpack(rawget(t, "__namestack") or {})}
tg = rawget(t, "__target") or nil
table.insert(ns, key)
return setmetatable({__namestack=ns, __target=tg}, mt)
end
function mt.__newindex (t, key, value)
t[key].set(value)
end
rtorrent["autocall"] = setmetatable({}, mt)
-- Target-object
-- Sets first argment for Autocall, for commands that require target.
-- Second argument allows to add prefix to command.
-- Allows syntax like:
-- - `rtorrent.Target("some_infohash").d.name()` or
-- - `rtorrent.Target:new("some_infohash").d.name()`
-- Also can be stored as variable
local Target = setmetatable({
new = function (self, target, prefix)
return self(target, prefix)
end;
}, {
__call = function (self, target, prefix)
return setmetatable(
{__namestack={prefix}, __target=target}, mt)
end
})
rtorrent["Target"] = Target
-- Some aliases for Target-commands
for i, p in ipairs({ "d", "f", "p", "t", "load" }) do
rtorrent[p] = function (target) return Target(target, p) end
end
-- Insert Lua method
-- Allow insert global lua-finction `func_name` at rtorrent slot `name`
-- @param name string rtorrent's slot
-- @param func_name string name of global lua-function
-- @param method_args number|string[]|number[]
-- rtorrent's arguments like `(d.name)` to be passed to lua-function when it be
-- called by rtorrent:
--
-- 1) non-negative integer - number of first arguments to be passed
--
-- example:
-- rtorrent.insert_lua_method('d.watch_handler', 'watch_handler', 1)
--
-- gives following rtorrent event handler:
-- d.watch_handler=$argument.0
--
-- 2) table (array) of strings - concrete rtorrent strings, passed in specified
-- order
--
-- example:
-- rtorrent.insert_lua_method('d.watch_handler', 'watch_handler', { '$argument.0=' })
--
-- gives following rtorrent event handler:
-- d.watch_handler=$argument.0
--
-- 3) table (array) of non-negative integer - indexes, converted to
-- $argument.0, $argument.1 ..., passed in specified order
--
-- example:
-- rtorrent.insert_lua_method('d.watch_handler', 'watch_handler', { 1, 0 })
-- or:
-- rtorrent.insert_lua_method('d.watch_handler', 'watch_handler', { [1]=1, [2]=0 })
--
-- gives following rtorrent event handler:
-- d.watch_handler=$argument.1,$argument.0
--
-- NOTE: first argement of func_name is always target, may be empty string
rtorrent["insert_lua_method"] = function (name, func_name, method_args)
local args = {}
if method_args == nil then method_args = 0 end
if math.type(method_args) == 'integer' and method_args >= 0 then
for i = 1, method_args do
args[i] = "$argument." .. i-1 .. "="
end
elseif type(method_args) == 'table' then
for i, v in ipairs(method_args) do
if type(v) == 'string' then
args[i] = v
elseif math.type(v) == 'integer' and v >= 0 then
args[i] = "$argument." .. v .. "="
else
error("Incorrect arguments")
end
end
else
error("Incorrect arguments")
end
local arg_str = table.concat(args, ",")
if #arg_str > 0 then arg_str = ","..arg_str end
rtorrent.autocall.method.insert(name, "simple", 'lua.execute.str="return '..func_name..'(...)"'..arg_str)
end
return rtorrent
+95
View File
@@ -0,0 +1,95 @@
// 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
+196
View File
@@ -0,0 +1,196 @@
// 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 <functional>
#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 : public std::function<bool(_Value, _Value)> {
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
@@ -0,0 +1,85 @@
// 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
@@ -0,0 +1,75 @@
// 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
+84
View File
@@ -0,0 +1,84 @@
// 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 <jaris@ifi.uio.no>
//
// Skomakerveien 33
// 3185 Skoppum, NORWAY
#ifndef RAK_FS_STAT_H
#define RAK_FS_STAT_H
#include <string>
#include <cinttypes>
#include <rak/error_number.h>
#if HAVE_SYS_VFS_H
#include <sys/vfs.h>
#endif
#if HAVE_SYS_STATVFS_H
#include <sys/statvfs.h>
#endif
#if HAVE_SYS_STATFS_H
#include <sys/statfs.h>
#endif
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#if HAVE_SYS_MOUNT_H
#include <sys/mount.h>
#endif
namespace rak {
class fs_stat {
public:
typedef FS_STAT_SIZE_TYPE blocksize_type;
typedef FS_STAT_COUNT_TYPE blockcount_type;
typedef FS_STAT_STRUCT fs_stat_type;
bool update(int fd) { return FS_STAT_FD; }
bool update(const char* fn) { return FS_STAT_FN; }
bool update(const std::string& filename) { return update(filename.c_str()); }
blocksize_type blocksize() { return FS_STAT_BLOCK_SIZE; }
blockcount_type blocks_avail() { return m_stat.f_bavail; }
int64_t bytes_avail() { return (int64_t) blocksize() * m_stat.f_bavail; }
private:
fs_stat_type m_stat;
};
}
#endif
+197
View File
@@ -0,0 +1,197 @@
// 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
@@ -0,0 +1,105 @@
// 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
@@ -0,0 +1,109 @@
// 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 std::function<bool (std::string)> {
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
@@ -0,0 +1,557 @@
// 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(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
@@ -0,0 +1,425 @@
// 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
@@ -0,0 +1,107 @@
// 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
+42
View File
@@ -0,0 +1,42 @@
-- the "rtorrent" table is passed in by the C++ code, modify and
-- return it for loading.
local args = {...}
local rtorrent = args[1]
-- Autocall
-- Allows syntax like `rtorrent.autocall.system.hostname()`
local mt = {}
function mt.__call (t, ...)
name = table.concat(rawget(t, "__namestack"), ".")
success, ret = pcall(rtorrent.call, name, ...)
if not success then error(name..": "..ret, 2) end
return ret
end
function mt.__index (t, key)
ns = rawget(t, "__namestack") or {}
table.insert(ns, key)
return setmetatable({__namestack=ns}, mt)
end
rtorrent["autocall"] = setmetatable({}, mt)
-- Autocall-config Same as autocall, but passes an empty first target
-- implicitly, for syntax like `rtorrent.autocall_config.session.directory.set("/tmp/")`
local mt = {}
function mt.__call (t, ...)
name = table.concat(rawget(t, "__namestack"), ".")
success, ret = pcall(rtorrent.call, name, "", ...)
if not success then error(name..": "..ret, 2) end
return ret
end
function mt.__index (t, key)
ns = rawget(t, "__namestack")
if ns == nil then
if _G[key] ~= nil then return _G[key] end
ns = {}
end
table.insert(ns, key)
return setmetatable({__namestack=ns}, mt)
end
rtorrent["autocall_config"] = setmetatable({}, mt)
return rtorrent
+16 -124
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', '14', '17', '20', or
# '23' for the respective C++ standard version.
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
# or '14' (for the C++14 standard).
#
# 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,15 +36,13 @@
# 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 25
#serial 12
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
dnl (serial version number 13).
@@ -53,8 +51,6 @@ 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], [],
@@ -106,18 +102,9 @@ 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}" 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
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])
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
@@ -161,44 +148,23 @@ 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], [[
@@ -210,21 +176,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
#error "This is not a C++ compiler"
// 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
#elif __cplusplus < 201103L
#error "This is not a C++11 compiler"
@@ -515,7 +467,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
#error "This is not a C++ compiler"
#elif __cplusplus < 201402L && !defined _MSC_VER
#elif __cplusplus < 201402L
#error "This is not a C++14 compiler"
@@ -639,7 +591,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
#error "This is not a C++ compiler"
#elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 201703L
#elif __cplusplus < 201703L
#error "This is not a C++17 compiler"
@@ -1005,66 +957,6 @@ namespace cxx17
} // namespace cxx17
#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
#endif // __cplusplus < 201703L
]])
+34 -83
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, 5.3, and 5.4 side-by-side).
# 5.2, and 5.3 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,14 +49,6 @@
# 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
@@ -90,7 +82,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 performed. If ACTION-IF-NOT-
# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT-
# FOUND is blank, then it will default to printing an error. To prevent
# the default behavior, give ':' as an action.
#
@@ -160,7 +152,6 @@
#
# 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>
#
@@ -190,7 +181,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 47
#serial 42
dnl =========================================================================
dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION],
@@ -206,14 +197,13 @@ AC_DEFUN([AX_PROG_LUA],
AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1])
dnl Find a Lua interpreter.
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_define_default([_AX_LUA_INTERPRETER_LIST],
[lua lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 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:'],
@@ -252,7 +242,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])
@@ -278,25 +268,13 @@ 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 or "")'`]
print(ver)'`]
])
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.
@@ -486,50 +464,33 @@ 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.
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 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 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$with_luajit" != 'xno' &&
test "x$ac_cv_header_luajit_h" != 'xyes'],
test "x$ac_cv_header_lua_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])
@@ -539,12 +500,10 @@ 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'],
@@ -662,26 +621,18 @@ AC_DEFUN([AX_LUA_LIBS],
dnl Try to find the Lua libs.
_ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB"
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])])
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])
LIBS=$_ax_lua_saved_libs
AS_IF([test "x$ac_cv_search_lua_load" != 'xno' &&
+254 -117
View File
@@ -1,3 +1,48 @@
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)
@@ -9,8 +54,6 @@ AC_DEFUN([TORRENT_CHECK_EPOLL], [
}
])],
[
use_epoll=yes
AC_DEFINE(USE_EPOLL, 1, Use epoll.)
AC_MSG_RESULT(yes)
], [
@@ -43,8 +86,6 @@ AC_DEFUN([TORRENT_CHECK_KQUEUE], [
}
])],
[
use_kqueue=yes
AC_DEFINE(USE_KQUEUE, 1, Use kqueue.)
AC_MSG_RESULT(yes)
], [
@@ -77,9 +118,176 @@ AC_DEFUN([TORRENT_WITHOUT_KQUEUE], [
])
AC_DEFUN([TORRENT_WITHOUT_VARIABLE_FDSET], [
AC_ARG_WITH(variable-fdset,
AS_HELP_STRING([--without-variable-fdset],[do not use non-portable variable sized fd_set's]),
[
if test "$withval" = "yes"; then
AC_DEFINE(USE_VARIABLE_FDSET, 1, defined when we allow the use of fd_set's of any size)
fi
], [
AC_DEFINE(USE_VARIABLE_FDSET, 1, defined when we allow the use of fd_set's of any size)
])
])
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_CHECK_STATVFS], [
AC_CHECK_HEADERS(sys/vfs.h sys/statvfs.h sys/statfs.h)
AC_MSG_CHECKING(for statvfs)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#if HAVE_SYS_VFS_H
#include <sys/vfs.h>
#endif
#if HAVE_SYS_STATVFS_H
#include <sys/statvfs.h>
#endif
#if HAVE_SYS_STATFS_H
#include <sys/statfs.h>
#endif
]], [[
struct statvfs s; fsblkcnt_t c;
statvfs("", &s);
fstatvfs(0, &s);
]])],[
AC_DEFINE(FS_STAT_FD, [fstatvfs(fd, &m_stat) == 0], Function to determine filesystem stats from fd)
AC_DEFINE(FS_STAT_FN, [statvfs(fn, &m_stat) == 0], Function to determine filesystem stats from filename)
AC_DEFINE(FS_STAT_STRUCT, [struct statvfs], Type of second argument to statfs function)
AC_DEFINE(FS_STAT_SIZE_TYPE, [unsigned long], Type of block size member in stat struct)
AC_DEFINE(FS_STAT_COUNT_TYPE, [fsblkcnt_t], Type of block count member in stat struct)
AC_DEFINE(FS_STAT_BLOCK_SIZE, [(m_stat.f_frsize)], Determine the block size)
AC_MSG_RESULT(ok)
have_stat_vfs=yes
],[
AC_MSG_RESULT(no)
have_stat_vfs=no
])
])
AC_DEFUN([TORRENT_CHECK_STATFS], [
AC_CHECK_HEADERS(sys/statfs.h sys/param.h sys/mount.h)
AC_MSG_CHECKING(for statfs)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#if HAVE_SYS_STATFS_H
#include <sys/statfs.h>
#endif
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#if HAVE_SYS_MOUNT_H
#include <sys/mount.h>
#endif
]], [[
struct statfs s;
statfs("", &s);
fstatfs(0, &s);
]])],[
AC_DEFINE(FS_STAT_FD, [fstatfs(fd, &m_stat) == 0], Function to determine filesystem stats from fd)
AC_DEFINE(FS_STAT_FN, [statfs(fn, &m_stat) == 0], Function to determine filesystem stats from filename)
AC_DEFINE(FS_STAT_STRUCT, [struct statfs], Type of second argument to statfs function)
AC_DEFINE(FS_STAT_SIZE_TYPE, [long], Type of block size member in stat struct)
AC_DEFINE(FS_STAT_COUNT_TYPE, [long], Type of block count member in stat struct)
AC_DEFINE(FS_STAT_BLOCK_SIZE, [(m_stat.f_bsize)], Determine the block size)
AC_MSG_RESULT(ok)
have_stat_vfs=yes
],[
AC_MSG_RESULT(no)
have_stat_vfs=no
])
])
AC_DEFUN([TORRENT_DISABLED_STATFS], [
AC_DEFINE(FS_STAT_FD, [(errno = ENOSYS) == 0], Function to determine filesystem stats from fd)
AC_DEFINE(FS_STAT_FN, [(errno = ENOSYS) == 0], Function to determine filesystem stats from filename)
AC_DEFINE(FS_STAT_STRUCT, [struct {blocksize_type f_bsize; blockcount_type f_bavail;}], Type of second argument to statfs function)
AC_DEFINE(FS_STAT_SIZE_TYPE, [int], Type of block size member in stat struct)
AC_DEFINE(FS_STAT_COUNT_TYPE, [int], Type of block count member in stat struct)
AC_DEFINE(FS_STAT_BLOCK_SIZE, [(4096)], Determine the block size)
AC_MSG_RESULT(No filesystem stats available)
])
AC_DEFUN([TORRENT_WITHOUT_STATVFS], [
AC_ARG_WITH(statvfs,
AS_HELP_STRING([--without-statvfs],[don't try to use statvfs to find free diskspace]),
[
if test "$withval" = "yes"; then
TORRENT_CHECK_STATVFS
else
have_stat_vfs=no
fi
],
[
TORRENT_CHECK_STATVFS
])
])
AC_DEFUN([TORRENT_WITHOUT_STATFS], [
AC_ARG_WITH(statfs,
AS_HELP_STRING([--without-statfs],[don't try to use statfs to find free diskspace]),
[
if test "$have_stat_vfs" = "no"; then
if test "$withval" = "yes"; then
TORRENT_CHECK_STATFS
else
TORRENT_DISABLED_STATFS
fi
fi
],
[
if test "$have_stat_vfs" = "no"; then
TORRENT_CHECK_STATFS
if test "$have_stat_vfs" = "no"; then
TORRENT_DISABLED_STATFS
fi
fi
])
])
AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [
AC_ARG_WITH(address-space,
AS_HELP_STRING([--with-address-space=MB],[change the default address space size [default=1024mb-or-32768mb]]),
AS_HELP_STRING([--with-address-space=MB],[change the default address space size [[default=1024mb]]]),
[
if test ! -z $withval -a "$withval" != "yes" -a "$withval" != "no"; then
AC_DEFINE_UNQUOTED(DEFAULT_ADDRESS_SPACE_SIZE, [$withval])
@@ -91,48 +299,51 @@ AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [
AC_CHECK_SIZEOF(long)
if test $ac_cv_sizeof_long = 8; then
AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 32768, Default address space size.)
AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 4096, Default address space size.)
else
AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 1024, Default address space size.)
fi
])
])
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_DEFUN([TORRENT_WITH_FASTCGI], [
AC_ARG_WITH(fastcgi,
AS_HELP_STRING([--with-fastcgi=PATH],[enable FastCGI RPC support (DO NOT USE)]),
[
AC_MSG_RESULT([no])
ATOMIC_LIBS=""
],
[
save_LIBS="$LIBS"
LIBS="$LIBS -latomic"
AC_MSG_CHECKING([for FastCGI (DO NOT USE)])
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.])
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.)
])
LIBS="$save_LIBS"
])
AC_DEFINE(HAVE_FASTCGI, 1, Support for FastCGI.)
AC_LANG_POP(C++)
AC_SUBST([ATOMIC_LIBS])
else
CXXFLAGS="$CXXFLAGS -I$withval/include"
LIBS="$LIBS -lfcgi -L$withval/lib"
AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <fcgiapp.h>
]], [[ FCGX_Init(); ]])],[
AC_MSG_RESULT(ok)
],[
AC_MSG_RESULT(not found)
AC_MSG_ERROR(Could not compile FastCGI test.)
])
AC_DEFINE(HAVE_FASTCGI, 1, Support for FastCGI.)
fi
])
])
@@ -177,7 +388,6 @@ AC_DEFUN([TORRENT_WITH_XMLRPC_C], [
])
])
AC_DEFUN([TORRENT_WITH_TINYXML2], [
AC_MSG_CHECKING(for tinyxml2)
@@ -191,7 +401,6 @@ AC_DEFUN([TORRENT_WITH_TINYXML2], [
])
])
AC_DEFUN([TORRENT_WITH_LUA], [
AC_ARG_WITH(lua,
AS_HELP_STRING([--with-lua],[enable LUA support]),
@@ -200,45 +409,28 @@ AC_DEFUN([TORRENT_WITH_LUA], [
AC_MSG_RESULT(no)
else
AX_PROG_LUA
# 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
AX_LUA_LIBS
AX_LUA_HEADERS
AC_DEFINE(HAVE_LUA, 1, Use LUA.)
LIBS="$LIBS $LUA_LIB"
CXXFLAGS="$CXXFLAGS $LUA_INCLUDE"
fi
],[
AC_MSG_RESULT(ignored)
])
])
AC_DEFUN([TORRENT_WITH_INOTIFY], [
AC_LANG_PUSH(C++)
AC_CHECK_HEADERS([sys/inotify.h])
AC_CHECK_HEADERS([sys/inotify.h mcheck.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(USE_INOTIFY, 1, [sys/inotify.h exists and works correctly])
AC_DEFINE(HAVE_INOTIFY, 1, [sys/inotify.h exists and works correctly])
AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(failed)]
)
@@ -246,14 +438,12 @@ 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>
]], [[
@@ -278,7 +468,6 @@ AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [
])
])
AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [
AC_MSG_CHECKING([for pthread_setname_no])
@@ -296,55 +485,3 @@ 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])
])
+142 -168
View File
@@ -1,3 +1,65 @@
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],
[
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)
@@ -26,7 +88,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 or OpenBSD.])
AC_MSG_ERROR([failed, do *not* attempt fix this with --disable-mincore unless you are running Win32.])
])
])
@@ -80,196 +142,108 @@ AC_DEFUN([TORRENT_CHECK_POSIX_FADVISE], [
])
])
AC_DEFUN([TORRENT_CHECK_CACHELINE], [
AC_REQUIRE([AC_CANONICAL_HOST])
AC_MSG_CHECKING([for target cacheline size])
AC_DEFUN([TORRENT_CHECK_POPCOUNT], [
AC_MSG_CHECKING(for __builtin_popcount)
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])
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
],[
# 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_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; }
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
int f() { return __builtin_popcount(0); }
])],
[has_native_be64=yes; endian_header_file='<endian.h>'],
[has_native_be64=no]
)
AC_MSG_RESULT([$has_native_be64])
[
AC_MSG_RESULT(yes)
AC_DEFINE(USE_BUILTIN_POPCOUNT, 1, Use __builtin_popcount.)
], [
AC_MSG_RESULT(no)
])
])
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; }
AC_DEFUN([TORRENT_CHECK_CACHELINE], [
AC_MSG_CHECKING(for cacheline)
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); }
])],
[has_native_be64=yes; endian_header_file='<sys/endian.h>'],
[has_native_be64=no]
)
AC_MSG_RESULT([$has_native_be64])
fi
[
AC_MSG_RESULT(found builtin)
dnl Need to fix this so that it uses the stuff defined by the system.
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_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_INSTRUMENTATION], [
AC_DEFUN([TORRENT_ENABLE_ALIGNED], [
AC_ARG_ENABLE(aligned,
AS_HELP_STRING([--enable-aligned],
[enable alignment safe code [[default=check]]]),
[
if test "$enableval" = "yes"; then
AC_DEFINE(USE_ALIGNED, 1, Require byte alignment)
fi
],[
TORRENT_CHECK_ALIGNED
])
])
AC_DEFUN([TORRENT_DISABLE_INSTRUMENTATION], [
AC_MSG_CHECKING([if instrumentation should be included])
AC_ARG_ENABLE(instrumentation,
AS_HELP_STRING([--enable-instrumentation],
[enable instrumentation [[default=disabled]]]),
AS_HELP_STRING([--disable-instrumentation],
[disable instrumentation [[default=enabled]]]),
[
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_MSG_RESULT(no)
AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation)
AC_MSG_RESULT(yes)
])
])
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_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_DISABLE_IPV6], [
AC_ARG_ENABLE(ipv6,
AS_HELP_STRING([--enable-ipv6],
+15 -22
View File
@@ -5,6 +5,12 @@ rtorrent_LDADD = libsub_root.a @PTHREAD_LIBS@
rtorrent_SOURCES = main.cc
libsub_root_a_SOURCES = \
core/curl_get.cc \
core/curl_get.h \
core/curl_socket.cc \
core/curl_socket.h \
core/curl_stack.cc \
core/curl_stack.h \
core/dht_manager.cc \
core/dht_manager.h \
core/download.cc \
@@ -13,6 +19,9 @@ libsub_root_a_SOURCES = \
core/download_factory.h \
core/download_list.cc \
core/download_list.h \
core/download_slot_map.h \
core/download_store.cc \
core/download_store.h \
core/http_queue.cc \
core/http_queue.h \
core/manager.cc \
@@ -123,16 +132,6 @@ 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 \
@@ -169,13 +168,11 @@ 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/watch_ready_queue.cc \
utils/watch_ready_queue.h \
utils/socket_fd.cc \
utils/socket_fd.h \
\
command_download.cc \
command_dynamic.cc \
@@ -199,14 +196,10 @@ libsub_root_a_SOURCES = \
globals.h \
option_parser.cc \
option_parser.h \
setup.cc \
setup.h \
signal_handler.cc \
signal_handler.h
signal_handler.h \
thread_worker.cc \
thread_worker.h
if NO_NCURSES
libsub_root_a_SOURCES += display/curses_stub.cc display/curses_stub.h
endif
AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -DPACKAGE_DATADIR=\"$(pkgdatadir)\"
AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir)
+124 -258
View File
@@ -5,10 +5,16 @@
#include <functional>
#include <netdb.h>
#include <unistd.h>
#include <fnmatch.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 <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>
@@ -17,49 +23,41 @@
#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"
torrent::string_utf8
std::string
retrieve_d_base_path(core::Download* download) {
if (download->file_list()->is_multi_file())
return download->file_list()->frozen_root_dir();
if (download->file_list()->empty())
return {};
return download->file_list()->at(0)->frozen_path();
else
return download->file_list()->empty() ? std::string() : download->file_list()->at(0)->frozen_path();
}
torrent::string_utf8
std::string
retrieve_d_base_filename(core::Download* download) {
torrent::string_utf8 base_path;
const std::string* base;
if (download->file_list()->is_multi_file())
base_path = download->file_list()->frozen_root_dir();
else if (!download->file_list()->empty())
base_path = download->file_list()->at(0)->frozen_path();
base = &download->file_list()->frozen_root_dir();
else
return {};
base = &download->file_list()->at(0)->frozen_path();
auto split = base_path.str().rfind('/');
std::string::size_type split = base->rfind('/');
if (split == std::string::npos)
return base_path;
return torrent::string_utf8::from_string(base_path.str().substr(split + 1));
return *base;
else
return base->substr(split + 1);
}
torrent::Object
@@ -81,23 +79,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 = expand_path(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
link = rak::path_expand(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 = expand_path(prefix + rpc::call_command_string("d.base_filename", rpc::make_target(download)) + postfix);
link = rak::path_expand(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 = path_expand(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
// link = rak::path_expand(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
} else if (type == "tied") {
link = expand_path(rpc::call_command_string("d.tied_to_file", rpc::make_target(download)));
link = rak::path_expand(rpc::call_command_string("d.tied_to_file", rpc::make_target(download)));
if (link.empty())
return torrent::Object();
link = expand_path(prefix + link + postfix);
link = rak::path_expand(prefix + link + postfix);
target = rpc::call_command_string("d.base_path", rpc::make_target(download));
} else {
@@ -106,19 +104,22 @@ 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", std::strerror(errno));
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());
}
break;
case 1:
{
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));
rak::file_stat fileStat;
rak::error_number::clear_global();
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:
@@ -135,8 +136,8 @@ apply_d_delete_tied(core::Download* download) {
if (tie.empty())
return torrent::Object();
if (::unlink(expand_path(tie).c_str()) == -1)
control->core()->push_log_std("Could not unlink tied file: " + std::string(std::strerror(errno)));
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()));
rpc::call_command("d.tied_to_file.set", std::string(), rpc::make_target(download));
return torrent::Object();
@@ -147,9 +148,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().str());
download->set_root_directory(name + download->info()->name());
else
download->set_root_directory(name + "/" + download->info()->name().str());
download->set_root_directory(name + "/" + download->info()->name());
}
torrent::Object
@@ -163,8 +164,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::heuristics_enum t =
static_cast<torrent::heuristics_enum>(torrent::option_find_string(torrent::OPTION_CHOKE_HEURISTICS, name.c_str()));
torrent::Download::HeuristicType t =
(torrent::Download::HeuristicType)torrent::option_find_string(torrent::OPTION_CHOKE_HEURISTICS, name.c_str());
if (is_down)
download->download()->set_download_choke_heuristic(t);
@@ -264,16 +265,26 @@ retrieve_d_custom_map(core::Download* download, bool keys_only, const torrent::O
throw torrent::bencode_error("d.custom.keys/items takes no arguments.");
torrent::Object result = keys_only ? torrent::Object::create_list() : torrent::Object::create_map();
torrent::Object::map_type& entries = download->bencode()->get_key("rtorrent").get_key("custom").as_map();
for (const auto& entry : download->bencode()->get_key("rtorrent").get_key("custom").as_map())
if (keys_only)
result.as_list().push_back(entry.first);
else
result.as_map()[entry.first] = entry.second;
for (torrent::Object::map_type::const_iterator itr = entries.begin(), last = entries.end(); itr != last; itr++) {
if (keys_only) result.as_list().push_back(itr->first);
else result.as_map()[itr->first] = itr->second;
}
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;
@@ -296,12 +307,10 @@ 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_id());
auto callback_id = torrent::system::make_callback_id();
assert(std::this_thread::get_id() == torrent::main_thread::thread()->thread_id());
// Currently discarding SOCK_STREAM.
torrent::this_thread::resolver()->resolve_preferred(callback_id, host, AF_UNSPEC, AF_INET, [download, port](torrent::c_sa_shared_ptr sa, int err) {
torrent::this_thread::resolver()->resolve_preferred(NULL, 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;
@@ -323,8 +332,7 @@ d_chunks_seen(core::Download* download) {
std::string result;
result.resize(size * 2);
torrent::utils::transform_to_hex(seen, seen + size, result);
rak::transform_hex((const char*)seen, (const char*)seen + size, result.begin());
return result;
}
@@ -340,28 +348,30 @@ 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<std::string> regex_list;
std::vector<rak::regex> regex_list;
bool use_regex = true;
if (args.front().is_list())
for (const auto& o : args.front().as_list())
regex_list.push_back(o.as_string_c());
std::transform(args.front().as_list().begin(), args.front().as_list().end(),
std::back_inserter(regex_list),
std::bind(&torrent::Object::as_string_c, std::placeholders::_1));
else if (args.front().is_string() && !args.front().as_string().empty())
regex_list.push_back(args.front().as_string());
else
use_regex = false;
for (const auto& file : *download->file_list()) {
for (torrent::FileList::const_iterator itr = download->file_list()->begin(), last = download->file_list()->end(); itr != last; itr++) {
if (use_regex &&
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; }))
std::find_if(regex_list.begin(), regex_list.end(),
std::bind(&rak::regex::operator(), std::placeholders::_1, (*itr)->path()->as_string())) == regex_list.end())
continue;
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++) {
const std::string& cmd = cItr->as_string();
row.push_back(rpc::parse_command(rpc::make_target(file.get()), cmd.c_str(), cmd.c_str() + cmd.size()).first);
row.push_back(rpc::parse_command(rpc::make_target(itr->get()), cmd.c_str(), cmd.c_str() + cmd.size()).first);
}
}
@@ -378,19 +388,18 @@ 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.
auto result_raw = torrent::Object::create_list();
auto& result = result_raw.as_list();
torrent::Object result_raw = torrent::Object::create_list();
torrent::Object::list_type& 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 (auto cItr = ++args.begin(); cItr != args.end(); cItr++) {
auto& cmd = cItr->as_string();
for (torrent::Object::list_const_iterator cItr = ++args.begin(); cItr != args.end(); cItr++) {
const std::string& cmd = cItr->as_string();
row.push_back(rpc::parse_command(rpc::make_target(&tracker), cmd.c_str(), cmd.c_str() + cmd.size()).first);
}
@@ -409,16 +418,17 @@ 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.
auto resultRaw = torrent::Object::create_list();
auto& result = resultRaw.as_list();
torrent::Object resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list();
for (const auto& connection : *download->connection_list()) {
for (torrent::ConnectionList::const_iterator itr = download->connection_list()->begin(), last = download->connection_list()->end();
itr != last; itr++) {
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
for (auto cItr = ++args.begin(); cItr != args.end(); cItr++) {
for (torrent::Object::list_const_iterator 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);
row.push_back(rpc::parse_command(rpc::make_target(*itr), cmd.c_str(), cmd.c_str() + cmd.size()).first);
}
}
@@ -443,11 +453,9 @@ p_call_target(const torrent::Object::list_type& args) {
torrent::HashString hash;
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");
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.");
torrent::ConnectionList::iterator peerItr = download->connection_list()->find(hash.c_str());
@@ -468,14 +476,12 @@ 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 = 0;
int64_t group;
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 {
if (args.front().is_string())
rpc::parse_whole_value_nothrow(args.front().as_string().c_str(), &group);
else
group = args.front().as_value();
}
if (group < 0 || group > 32)
throw torrent::input_error("Tracker group number invalid.");
@@ -497,19 +503,6 @@ 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)
@@ -554,13 +547,16 @@ download_set_variable_string(core::Download* download, const torrent::Object::st
//
torrent::Object
d_list_push_back_string(core::Download* download, const std::string& arg, const char* first_key, const char* second_key) {
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();
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 std::string& arg, const char* first_key, const char* second_key) {
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();
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); }))
@@ -615,16 +611,12 @@ 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)); \
@@ -644,48 +636,30 @@ void cg_d_group_set(core::Download* download, const torrent::Objec
void
initialize_command_download() {
CMD2_DL("d.hash", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->hash()); });
CMD2_DL("d.local_id", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->local_id()); });
CMD2_DL("d.local_id_html", [](auto* download, auto) { return torrent::utils::copy_escape_html_str(download->info()->local_id()); });
CMD2_DL("d.bitfield", [](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.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.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(); });
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));
//
// Network related:
//
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.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.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 ("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_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));
@@ -701,16 +675,16 @@ initialize_command_download() {
// Control functinos:
//
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_open", CMD2_ON_INFO(is_open));
CMD2_DL ("d.is_active", CMD2_ON_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", [](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_private", CMD2_ON_INFO(is_private));
CMD2_DL ("d.is_pex_active", CMD2_ON_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", [](auto* download, auto) { return download->info()->is_meta_download(); });
CMD2_DL ("d.is_meta", CMD2_ON_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));
@@ -720,8 +694,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_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 ("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.update_priorities", CMD2_ON_DL(update_priorities));
@@ -781,15 +755,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", [](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 ("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_VAR_STRING("d.connection_leech", "rtorrent", "connection_leech");
CMD2_DL_VAR_STRING("d.connection_seed", "rtorrent", "connection_seed");
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 ("d.up.choke_heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, CMD2_ON_DL(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", [](auto* d, auto) { return torrent::option_to_c_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, d->download()->download_choke_heuristic()); });
CMD2_DL ("d.down.choke_heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, CMD2_ON_DL(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");
@@ -803,8 +777,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_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"));
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"));
// This command really needs to be improved, so we have proper
// logging support.
@@ -844,7 +818,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", [](auto* download, auto) { return download->file_list()->free_diskspace_no_cache(); });
CMD2_DL ("d.free_diskspace", CMD2_ON_FL(free_diskspace));
CMD2_DL ("d.size_files", CMD2_ON_FL(size_files));
CMD2_DL ("d.size_bytes", CMD2_ON_FL(size_bytes));
@@ -905,112 +879,4 @@ 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");
}
+18 -45
View File
@@ -25,9 +25,9 @@ static std::vector<std::pair<const char*, int>> object_storage_flags = {
static int
object_storage_parse_flag(const std::string& flag) {
for (auto [n, f] : object_storage_flags)
if (n == flag)
return f;
for (auto f : object_storage_flags)
if (f.first == flag)
return f.second;
throw torrent::input_error("unknown flag");
}
@@ -380,11 +380,13 @@ system_method_set_key(const torrent::Object::list_type& args) {
torrent::Object
system_method_list_keys(const torrent::Object::string_type& args) {
const torrent::Object::map_type& multi_cmd = control->object_storage()->get_str(args).as_map();
torrent::Object rawResult = torrent::Object::create_list();
torrent::Object::list_type& result = rawResult.as_list();
for (const auto& itr : control->object_storage()->get_str(args).as_map())
result.push_back(itr.first);
for (torrent::Object::map_const_iterator itr = multi_cmd.begin(), last = multi_cmd.end(); itr != last; itr++)
result.push_back(itr->first);
return rawResult;
}
@@ -411,7 +413,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", 3);
CMD2_VAR_VALUE ("method.use_intermediate", 0);
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,44 +446,15 @@ initialize_command_dynamic() {
CMD2_ANY ("catch", std::bind(&cmd_catch, std::placeholders::_1, std::placeholders::_2));
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); });
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));
// 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");
}
+74 -103
View File
@@ -2,14 +2,14 @@
#include <functional>
#include <cstdio>
#include <string>
#include <vector>
#include <rak/error_number.h>
#include <rak/file_stat.h>
#include <rak/path.h>
#include <rak/string_manip.h>
#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,12 +21,11 @@
#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("group." + group_name + ".view", rpc::make_target()).as_string());
auto view_itr = control->view_manager()->find(rpc::commands.call("group2." + group_name + ".view", rpc::make_target()).as_string());
if (view_itr == control->view_manager()->end())
throw torrent::input_error("Could not find view.");
@@ -34,9 +33,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("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();
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();
std::vector<core::Download*> downloads;
@@ -56,23 +55,23 @@ apply_on_ratio(const torrent::Object& rawArgs) {
auto ratio_command = "group." + group_name + ".ratio.command";
for (const auto& download : downloads)
rpc::commands.call_catch(ratio_command, rpc::make_target(download), torrent::Object(), "Ratio reached, but command failed: ");
for (std::vector<core::Download*>::iterator itr = downloads.begin(), last = downloads.end(); itr != last; itr++)
rpc::commands.call_catch(ratio_command, rpc::make_target(*itr), torrent::Object(), "Ratio reached, but command failed: ");
return torrent::Object();
}
torrent::Object
apply_start_tied() {
for (const auto& download : *control->core()->download_list()) {
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 1)
for (auto itr = control->core()->download_list()->begin(); itr != control->core()->download_list()->end(); ++itr) {
if (rpc::call_command_value("d.state", rpc::make_target(*itr)) == 1)
continue;
torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
rak::file_stat 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(expand_path(tied_to_file)))
rpc::parse_command_single(rpc::make_target(download), "d.try_start=");
if (!tied_to_file.empty() && fs.update(rak::path_expand(tied_to_file)))
rpc::parse_command_single(rpc::make_target(*itr), "d.try_start=");
}
return torrent::Object();
@@ -80,15 +79,15 @@ apply_start_tied() {
torrent::Object
apply_stop_untied() {
for (const auto& download : *control->core()->download_list()) {
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 0)
for (auto itr = control->core()->download_list()->begin(); itr != control->core()->download_list()->end(); ++itr) {
if (rpc::call_command_value("d.state", rpc::make_target(*itr)) == 0)
continue;
torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
rak::file_stat 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(expand_path(tied_to_file)))
rpc::parse_command_single(rpc::make_target(download), "d.try_stop=");
if (!tied_to_file.empty() && !fs.update(rak::path_expand(tied_to_file)))
rpc::parse_command_single(rpc::make_target(*itr), "d.try_stop=");
}
return torrent::Object();
@@ -96,12 +95,12 @@ apply_stop_untied() {
torrent::Object
apply_close_untied() {
for (const auto& download : *control->core()->download_list()) {
torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
for (auto itr = control->core()->download_list()->begin(); itr != control->core()->download_list()->end(); ++itr) {
rak::file_stat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(*itr));
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=");
if (rpc::call_command_value("d.ignore_commands", rpc::make_target(*itr)) == 0 && !tied_to_file.empty() && !fs.update(rak::path_expand(tied_to_file)))
rpc::parse_command_single(rpc::make_target(*itr), "d.try_close=");
}
return torrent::Object();
@@ -110,10 +109,10 @@ apply_close_untied() {
torrent::Object
apply_remove_untied() {
for (auto itr = control->core()->download_list()->begin(); itr != control->core()->download_list()->end(); ) {
torrent::utils::FileStat fs;
rak::file_stat 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(expand_path(tied_to_file))) {
if (!tied_to_file.empty() && !fs.update(rak::path_expand(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));
@@ -168,14 +167,12 @@ 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(cache) >= (uint64_t)arg)
if (download->file_list()->free_diskspace() >= (uint64_t)arg)
continue;
control->core()->download_list()->close(download);
@@ -213,7 +210,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(torrent::utils::transform_to_hex_str(*hashString));
resultList.push_back(rak::transform_hex(hashString->begin(), hashString->end()));
}
return result;
@@ -237,7 +234,9 @@ d_multicall(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.
std::vector<core::Download*> dlist((*view_itr)->begin_visible(), (*view_itr)->end_visible());
std::vector<core::Download*> dlist((*view_itr)->size_visible());
std::copy((*view_itr)->begin_visible(), (*view_itr)->end_visible(), dlist.data());
torrent::Object resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list();
@@ -258,12 +257,11 @@ 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.");
auto arg = args.begin();
torrent::Object::list_const_iterator arg = args.begin();
// Find the given view
auto* viewManager = control->view_manager();
auto view_itr = viewManager->find(arg->as_string().empty() ? "default" : arg->as_string());
core::ViewManager* viewManager = control->view_manager();
core::ViewManager::iterator 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() + "'.");
@@ -273,19 +271,18 @@ d_multicall_filtered(const torrent::Object::list_type& args) {
(*view_itr)->filter_by(*++arg, dlist);
// Generate result by iterating over all items
auto resultRaw = torrent::Object::create_list();
auto& result = resultRaw.as_list();
torrent::Object resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list();
++arg; // skip to first command
for (const auto& item : dlist) {
for (core::View::iterator item = dlist.begin(); item != dlist.end(); ++item) {
// Add empty row to result
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
// Call the provided commands and assemble their results
for (torrent::Object::list_const_iterator command = arg; command != args.end(); command++) {
auto& cmdstr = command->as_string();
row.push_back(rpc::parse_command(rpc::make_target(item), cmdstr.c_str(), cmdstr.c_str() + cmdstr.size()).first);
row.push_back(rpc::parse_command(rpc::make_target(*item), cmdstr.c_str(), cmdstr.c_str() + cmdstr.size()).first);
}
}
@@ -298,83 +295,57 @@ call_watch_command(const std::string& command, const std::string& path) {
}
torrent::Object
directory_watch(const torrent::Object::list_type& args, int flags) {
directory_watch_added(const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Too few arguments.");
auto& path = args.front().as_string();
auto& command = args.back().as_string();
std::string expanded_path = expand_path(path);
if (!control->directory_events()->open())
throw torrent::input_error("Could not open inotify:" + std::string(std::strerror(errno)));
throw torrent::input_error("Could not open inotify:" + std::string(rak::error_number::current().c_str()));
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);
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));
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", [](auto, auto& args) { return apply_on_ratio(args); });
CMD2_ANY_STRING ("on_ratio", std::bind(&apply_on_ratio, std::placeholders::_2));
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(); });
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_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); });
// 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_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_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_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_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_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_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_LIST ("download_list", [](auto, auto& args) { return apply_download_list(args); });
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));
// 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");
CMD2_ANY_LIST ("directory.watch.added", std::bind(&directory_watch_added, std::placeholders::_2));
}
+59 -78
View File
@@ -1,5 +1,41 @@
// 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>
@@ -24,34 +60,24 @@ apply_f_path(torrent::File* file) {
if (file->path()->empty())
return std::string();
torrent::Object result_raw(file->path()->begin()->str());
auto& result = result_raw.as_string();
torrent::Object resultRaw(*file->path()->begin());
torrent::Object::string_type& result = resultRaw.as_string();
for (torrent::Path::const_iterator itr = ++file->path()->begin(), last = file->path()->end(); itr != last; itr++)
result += '/' + itr->str();
result += '/' + *itr;
return result_raw;
return resultRaw;
}
torrent::Object
apply_f_path_components(torrent::File* file, int output_type) {
auto result_raw = torrent::Object::create_list();
auto& result = result_raw.as_list();
apply_f_path_components(torrent::File* file) {
torrent::Object resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list();
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");
};
}
for (torrent::Path::const_iterator itr = file->path()->begin(), last = file->path()->end(); itr != last; itr++)
result.push_back(*itr);
return result_raw;
return resultRaw;
}
torrent::Object
@@ -67,7 +93,7 @@ apply_fi_filename_last(torrent::FileListIterator* itr) {
if (itr->depth() >= itr->file()->path()->size())
return "ERROR";
return itr->file()->path()->at(itr->depth()).str();
return itr->file()->path()->at(itr->depth());
}
void
@@ -90,32 +116,21 @@ 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", [](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.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.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.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.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.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.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.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.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));
@@ -124,38 +139,4 @@ 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");
}
+36 -52
View File
@@ -180,14 +180,15 @@ apply_cg_insert(const std::string& arg) {
if (rpc::parse_whole_value_nothrow(arg.c_str(), &dummy))
throw torrent::input_error("Cannot use a value string as choke group name.");
if (arg.empty() || std::any_of(cg_list_hack.begin(), cg_list_hack.end(), [&arg](auto cg) { return arg == cg->name(); }))
if (arg.empty() ||
std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](torrent::choke_group* cg) { return arg == cg->name(); }) != cg_list_hack.end())
throw torrent::input_error("Duplicate name for choke group.");
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::HEURISTICS_UPLOAD_LEECH);
cg_list_hack.back()->down_queue()->set_heuristics(torrent::HEURISTICS_DOWNLOAD_LEECH);
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);
return torrent::Object();
}
@@ -337,69 +338,52 @@ options.
void
initialize_command_groups() {
CMD_ANY ("choke_group.list", std::bind(&apply_cg_list));
CMD_ANY_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2));
CMD2_ANY ("choke_group.list", std::bind(&apply_cg_list));
CMD2_ANY_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2));
#if USE_CHOKE_GROUP
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));
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));
#else
apply_cg_insert("default");
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));
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));
#endif
// Commands specific for a group. Supports as the first argument the
// name, the index or a negative index.
CMD_ANY ("choke_group.general.size", std::bind(&torrent::choke_group::size, CG_GROUP_AT()));
CMD2_ANY ("choke_group.general.size", std::bind(&torrent::choke_group::size, CG_GROUP_AT()));
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.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.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.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.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.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.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.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.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.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.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.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.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");
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));
}
+34
View File
@@ -1,3 +1,37 @@
// 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>
+13 -28
View File
@@ -7,25 +7,6 @@
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:
//
@@ -119,21 +100,25 @@ 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_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_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_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:
//
+50 -19
View File
@@ -1,12 +1,41 @@
// 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <rak/path.h>
#include <torrent/peer/peer_list.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
@@ -14,6 +43,13 @@
#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>
bool ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address_end);
torrent::Object
@@ -29,7 +65,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();
@@ -53,7 +89,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))
@@ -203,14 +239,9 @@ ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address
uint32_t mask=0;
uint32_t end_mask=0;
if (mask_bits == 0) {
mask = 0;
end_mask = ~(uint32_t)0;
} else {
mask = (~mask) << (32-mask_bits);
end_mask = (~end_mask) >> mask_bits;
}
mask = (~mask) << (32-mask_bits);
*address_start = ip & mask;
end_mask = (~end_mask) >> mask_bits;
*address_end = (ip & mask) | end_mask;
valid=true;
@@ -267,7 +298,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))
@@ -295,8 +326,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(expand_path(filename).c_str(), std::ios::in);
std::fstream file(rak::path_expand(filename).c_str(), std::ios::in);
if (!file.is_open())
throw torrent::input_error("Could not open ip filter file: " + filename);
@@ -353,7 +384,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_to_c_str_or_throw(torrent::OPTION_IP_FILTER, value));
snprintf(buffer, 64, "%s-%s %s", start_str, end_str, torrent::option_as_string(torrent::OPTION_IP_FILTER, value));
result.push_back((std::string)buffer);
+141 -183
View File
@@ -1,27 +1,26 @@
#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"
@@ -35,10 +34,11 @@ typedef torrent::FileManager FM_t;
torrent::Object
apply_pieces_stats_total_size() {
uint64_t size = 0;
core::DownloadList* d_list = control->core()->download_list();
for (const auto& d : *control->core()->download_list())
if (d->is_active())
size += d->file_list()->size_bytes();
for (core::DownloadList::iterator itr = d_list->begin(), last = d_list->end(); itr != last; itr++)
if ((*itr)->is_active())
size += (*itr)->file_list()->size_bytes();
return size;
}
@@ -96,12 +96,8 @@ post_increment(torrent::Object::list_const_iterator& itr, const torrent::Object:
inline const std::string&
check_name(const std::string& str) {
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.");
if (!rak::is_all_name(str))
throw torrent::input_error("Non-alphanumeric characters found.");
return str;
}
@@ -115,24 +111,40 @@ 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",
"schedule=group." + name + ".ratio,5,60,on_ratio=" + name));
"schedule2=group." + name + ".ratio,5,60,on_ratio=" + name));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.disable", "simple",
"schedule.remove=group." + name + ".ratio"));
"schedule_remove2=group." + name + ".ratio"));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.command", "simple",
"d.try_close= ;d.ignore_commands.set=1"));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".view", "string", view));
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));
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::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");
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");
}
return name;
}
@@ -171,16 +183,12 @@ cmd_file_append(const torrent::Object::list_type& args) {
FILE* output = fopen(args.front().as_string().c_str(), "a");
if (output == nullptr)
throw torrent::input_error("Could not append to file '" + args.front().as_string() + "': " + std::strerror(errno));
if (output == NULL)
throw torrent::input_error("Could not append to file '" + args.front().as_string() + "': " + rak::error_number::current().c_str());
try {
file_print_list(++args.begin(), args.end(), output, file_print_delim_space);
fprintf(output, "\n");
} catch (...) {
fclose(output);
throw;
}
file_print_list(++args.begin(), args.end(), output, file_print_delim_space);
fprintf(output, "\n");
fclose(output);
return torrent::Object();
}
@@ -188,187 +196,137 @@ 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();
CMD_ANY ("system.hostname", std::bind(&system_hostname));
CMD_ANY ("system.pid", std::bind(&getpid));
CMD2_ANY ("system.hostname", std::bind(&system_hostname));
CMD2_ANY ("system.pid", std::bind(&getpid));
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_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_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_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_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size,
CMD2_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size,
(utils::FileStatusCache::base_type*)control->core()->file_status_cache()));
CMD_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache()));
CMD2_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache()));
CMD_VAR_BOOL ("file.prioritize_toc", 0);
CMD_VAR_LIST ("file.prioritize_toc.first");
CMD_VAR_LIST ("file.prioritize_toc.last");
CMD2_VAR_BOOL ("file.prioritize_toc", 0);
CMD2_VAR_LIST ("file.prioritize_toc.first");
CMD2_VAR_LIST ("file.prioritize_toc.last");
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.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.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 ("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_STRING ("system.env", [](auto, auto& str) { return system_env(str); });
CMD2_ANY_STRING ("system.env", std::bind(&system_env, std::placeholders::_2));
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 ("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_VALUE_V ("system.umask.set", [](auto, auto& value) { return ::umask(value); });
CMD2_ANY_VALUE_V ("system.umask.set", std::bind(&umask, std::placeholders::_2));
CMD_VAR_BOOL ("system.daemon", false);
CMD2_VAR_BOOL ("system.daemon", false);
CMD_ANY_V ("system.shutdown.normal", [](auto, auto) { control->receive_normal_shutdown(); });
CMD_ANY_V ("system.shutdown.quick", [](auto, auto) { control->receive_quick_shutdown(); });
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_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
CMD2_ANY ("system.cwd", std::bind(&system_get_cwd));
CMD2_ANY_STRING ("system.cwd.set", std::bind(&system_set_cwd, std::placeholders::_2));
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.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.sockets.size", [](auto, auto) { return torrent::runtime::socket_manager()->size(); });
CMD_ANY ("system.sockets.max_size", [](auto, auto) { return torrent::runtime::socket_manager()->max_size(); });
CMD_ANY_VALUE_V ("system.sockets.max_size.set", [](auto, auto& value) { return torrent::runtime::socket_manager()->set_max_size_and_adjust(value); });
CMD_ANY_V ("system.sockets.adjust_alloc", [](auto, auto) { torrent::runtime::socket_manager()->adjust_allocation(); });
CMD2_ANY ("pieces.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));
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.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));
CMD_ANY (category_name + ".size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_managed_size(category); });
CMD_ANY (category_name + ".max_size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_size(category); });
CMD_ANY (category_name + ".min_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_min_allocation(category); });
CMD_ANY (category_name + ".max_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_allocation(category); });
CMD2_ANY ("pieces.stats.total_size", std::bind(&apply_pieces_stats_total_size));
if (i == 0)
continue;
CMD2_ANY ("pieces.hash.queue_size", std::bind(&torrent::main_thread::hash_queue_size));
CMD2_VAR_BOOL ("pieces.hash.on_completion", true);
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 ("directory.default", "./");
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_VAR_STRING ("session.name", "");
CMD2_VAR_BOOL ("session.use_lock", true);
CMD2_VAR_BOOL ("session.on_completion", true);
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 ("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.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); });
CMD2_ANY_V ("session.save", std::bind(&core::DownloadList::session_save, dList));
#ifdef HAVE_LUA
rpc::LuaEngine* lua_engine = control->lua_engine();
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));
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));
#endif
#define CMD_EXECUTE(key, flags) \
CMD_ANY(key, std::bind(&rpc::ExecFile::execute_object, &rpc::execFile, std::placeholders::_2, flags));
#define CMD2_EXECUTE(key, flags) \
CMD2_ANY(key, std::bind(&rpc::ExecFile::execute_object, &rpc::execFile, std::placeholders::_2, flags));
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);
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));
// 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";
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_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_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");
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
rpc::rpc.mark_safe(category_name + ".size");
rpc::rpc.mark_safe(category_name + ".max_size");
rpc::rpc.mark_safe(category_name + ".min_alloc");
rpc::rpc.mark_safe(category_name + ".max_alloc");
}
rpc::rpc.mark_safe("directory.default");
rpc::rpc.mark_safe("session.path");
rpc::rpc.mark_safe("session.use_lock");
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");
CMD2_ANY_LIST ("group.insert", std::bind(&group_insert, std::placeholders::_2));
}
+61 -64
View File
@@ -1,7 +1,6 @@
#include "config.h"
#include <fcntl.h>
#include <iterator>
#include <stdio.h>
#include <unistd.h>
#include <torrent/data/chunk_utils.h>
@@ -9,24 +8,53 @@
#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 "rpc/parse.h"
#include "rak/path.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& raw_args) {
std::vector<std::string> args;
apply_log_open(int output_flags, const torrent::Object::list_type& args) {
if (args.size() < 2)
throw torrent::input_error("Invalid number of arguments.");
for (const auto& arg : raw_args)
args.push_back(arg.as_string());
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());
apply_log_open_str(output_flags, args);
return torrent::Object();
}
@@ -34,35 +62,10 @@ 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();
}
@@ -83,7 +86,7 @@ apply_log(const torrent::Object::string_type& arg, int logType) {
}
if (!arg.empty()) {
int logFd = open(expand_path(arg).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644);
int logFd = open(rak::path_expand(arg).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644);
if (logFd < 0)
throw torrent::input_error("Could not open execute log file.");
@@ -105,21 +108,19 @@ apply_log(const torrent::Object::string_type& arg, int logType) {
torrent::Object
log_vmmap_dump(const std::string& str) {
core::DownloadList* d_list = control->core()->download_list();
std::vector<torrent::vm_mapping> all_mappings;
for (const auto& d : *control->core()->download_list()) {
std::vector<torrent::vm_mapping> tmp_mappings = torrent::chunk_list_mapping(d->download());
for (core::DownloadList::iterator itr = d_list->begin(), last = d_list->end(); itr != last; itr++) {
std::vector<torrent::vm_mapping> tmp_mappings = torrent::chunk_list_mapping((*itr)->download());
all_mappings.insert(all_mappings.end(), tmp_mappings.begin(), tmp_mappings.end());
all_mappings.insert(all_mappings.end(), tmp_mappings.begin(), tmp_mappings.end());
}
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));
for (std::vector<torrent::vm_mapping>::iterator itr = all_mappings.begin(), last = all_mappings.end(); itr != last; itr++) {
fprintf(log_file, "%8p-%8p [%5llxk]\n", itr->ptr, (char*)itr->ptr + itr->length, (long long unsigned int)(itr->length / 1024));
}
fclose(log_file);
@@ -128,23 +129,19 @@ 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_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_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_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.print", std::bind(&apply_log_print, std::placeholders::_2));
CMD2_ANY_LIST ("log.add_output", std::bind(&apply_log_add_output, 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", [](const auto&, const auto& str) { scgi_thread::set_rpc_log(str); });
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", std::bind(&ThreadWorker::set_rpc_log, worker_thread, std::placeholders::_2));
CMD2_REDIRECT ("log.xmlrpc", "log.rpc"); // For backwards compatibility
}
+140 -320
View File
@@ -3,25 +3,18 @@
#include <functional>
#include <cstdio>
#include <unistd.h>
#include <rak/address_info.h>
#include <rak/path.h>
#include <torrent/connection_manager.h>
#include <torrent/tracker/tracker.h>
#include <torrent/torrent.h>
#include <torrent/rate.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/curl_stack.h"
#include "core/download.h"
#include "core/manager.h"
#include "rpc/scgi.h"
@@ -29,121 +22,26 @@
#include "rpc/parse.h"
#include "rpc/parse_commands.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 {};
}
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
torrent::Object
apply_encryption(const torrent::Object::list_type& args) {
if (args.empty())
throw torrent::input_error("No encryption options specified.");
uint32_t options_mask = torrent::ConnectionManager::encryption_none;
torrent::encryption_mode encryption_mode, handshake_mode, stream_mode;
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
uint32_t opt = torrent::option_find_string(torrent::OPTION_ENCRYPTION, itr->as_string().c_str());
if (args.size() == 1) {
try {
encryption_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_MODE, args.front().as_string()));
} catch (torrent::input_error& e) {
return apply_obsolete_encryption(args);
}
torrent::runtime::network_config()->set_encryption_modes(encryption_mode, encryption_mode);
return {};
if (opt == torrent::ConnectionManager::encryption_none)
options_mask = torrent::ConnectionManager::encryption_none;
else
options_mask |= opt;
}
if (args.size() != 2)
return apply_obsolete_encryption(args);
torrent::connection_manager()->set_encryption_options(options_mask);
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 {};
return torrent::Object();
}
torrent::Object
@@ -153,60 +51,88 @@ 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::runtime::network_config()->set_priority(value);
torrent::connection_manager()->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();
initialize_rpc() {
rpc::rpc.initialize();
rpc::rpc.slot_find_download() = [](const char* hash) {
return control->core()->download_list()->find_hex_ptr(hash);
};
rpc::rpc.slot_find_file() = [](core::Download* d, uint32_t index) -> torrent::File* {
if (index >= d->file_list()->size_files())
throw torrent::input_error("invalid parameters: index not found");
return (*d->file_list())[index].get();
};
rpc::rpc.slot_find_tracker() = [](core::Download* d, uint32_t index) -> torrent::tracker::Tracker {
if (index >= d->tracker_controller().size())
throw torrent::input_error("invalid parameters: index not found");
// TODO: This should be rewritten to check if the tracker is valid and use a different
// function.
return d->tracker_controller().at(index);
};
rpc::rpc.slot_find_peer() = [](core::Download* d, const torrent::HashString& hash) -> torrent::Peer* {
auto itr = d->connection_list()->find(hash.c_str());
if (itr == d->connection_list()->end())
throw torrent::input_error("invalid parameters: hash not found");
return *itr;
};
unsigned int count = 0;
for (const auto& [name, cmd] : rpc::commands) {
if (!(cmd.m_flags & rpc::CommandMap::flag_public_rpc))
for (rpc::CommandMap::const_iterator itr = rpc::commands.begin(), last = rpc::commands.end(); itr != last; itr++, count++) {
if (!(itr->second.m_flags & rpc::CommandMap::flag_public_rpc))
continue;
rpc::rpc.insert_command(name.c_str(), cmd.m_parm, cmd.m_doc);
++count;
rpc::rpc.insert_command(itr->first.c_str(), itr->second.m_parm, itr->second.m_doc);
}
lt_log_print(torrent::LOG_RPC_EVENTS, "RPC manager initialized with %u functions.", count);
lt_log_print(torrent::LOG_RPC_EVENTS, "RPC initialized with %u functions.", count);
}
torrent::Object
apply_scgi(const std::string& arg, int type) {
if (scgi_thread::scgi() != nullptr)
if (worker_thread->scgi() != NULL)
throw torrent::input_error("SCGI already enabled.");
initialize_rpc_handlers();
initialize_rpc();
torrent::sa_unique_ptr sa;
rpc::SCgi* scgi = new rpc::SCgi;
auto scgi = std::make_unique<rpc::SCgi>();
rak::address_info* ai = NULL;
rak::socket_address sa;
rak::socket_address* saPtr;
try {
int port{};
char dummy{};
int port, err;
char dummy;
char address[1024];
std::string path;
switch (type) {
case 1:
if (std::sscanf(arg.c_str(), ":%i%c", &port, &dummy) == 1) {
sa = torrent::sa_make_inet();
sa.sa_inet()->clear();
saPtr = &sa;
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)) + ".");
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()));
}
saPtr = ai->address();
lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is bound to an address and might be a security risk");
@@ -217,82 +143,33 @@ apply_scgi(const std::string& arg, int type) {
if (port <= 0 || port >= (1 << 16))
throw torrent::input_error("Invalid port number.");
torrent::sap_set_port(sa, port);
scgi->open_port(sa.get(), torrent::sap_length(sa), rpc::call_command_value("network.scgi.dont_route"));
saPtr->set_port(port);
scgi->open_port(saPtr, saPtr->length(), rpc::call_command_value("network.scgi.dont_route"));
break;
case 2:
default:
path = expand_path(arg);
path = rak::path_expand(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());
}
scgi_thread::set_scgi(scgi.release());
worker_thread->set_scgi(scgi);
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;
@@ -312,142 +189,85 @@ apply_xmlrpc_dialect(const std::string& arg) {
void
initialize_command_network() {
auto file_manager = torrent::file_manager();
auto http_stack = torrent::net_thread::http_stack();
auto nw_config = torrent::runtime::network_config();
torrent::ConnectionManager* cm = torrent::connection_manager();
torrent::FileManager* fileManager = torrent::file_manager();
core::CurlStack* httpStack = control->core()->http_stack();
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); });
CMD2_ANY_STRING ("encoding.add", std::bind(&apply_encoding_list, std::placeholders::_2));
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); });
// 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_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_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_VAR_STRING ("protocol.connection.leech", "leech");
CMD_VAR_STRING ("protocol.connection.seed", "seed");
CMD2_VAR_BOOL ("protocol.pex", true);
CMD2_ANY_LIST ("protocol.encryption.set", std::bind(&apply_encryption, std::placeholders::_2));
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.connection.leech", "leech");
CMD2_VAR_STRING ("protocol.connection.seed", "seed");
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_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.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.http.cacert", std::bind(&core::CurlStack::http_cacert, httpStack));
CMD2_ANY_STRING_V("network.http.cacert.set", std::bind(&core::CurlStack::set_http_cacert, httpStack, std::placeholders::_2));
CMD2_ANY ("network.http.capath", std::bind(&core::CurlStack::http_capath, httpStack));
CMD2_ANY_STRING_V("network.http.capath.set", std::bind(&core::CurlStack::set_http_capath, httpStack, std::placeholders::_2));
CMD2_ANY ("network.http.dns_cache_timeout", std::bind(&core::CurlStack::dns_timeout, httpStack));
CMD2_ANY_VALUE_V ("network.http.dns_cache_timeout.set", std::bind(&core::CurlStack::set_dns_timeout, httpStack, std::placeholders::_2));
CMD2_ANY ("network.http.current_open", std::bind(&core::CurlStack::active, httpStack));
CMD2_ANY ("network.http.max_open", std::bind(&core::CurlStack::max_active, httpStack));
CMD2_ANY_VALUE_V ("network.http.max_open.set", std::bind(&core::CurlStack::set_max_active, httpStack, std::placeholders::_2));
CMD2_ANY ("network.http.proxy_address", std::bind(&core::CurlStack::http_proxy, httpStack));
CMD2_ANY_STRING_V("network.http.proxy_address.set", std::bind(&core::CurlStack::set_http_proxy, httpStack, std::placeholders::_2));
CMD2_ANY ("network.http.ssl_verify_host", std::bind(&core::CurlStack::ssl_verify_host, httpStack));
CMD2_ANY_VALUE_V ("network.http.ssl_verify_host.set", std::bind(&core::CurlStack::set_ssl_verify_host, httpStack, std::placeholders::_2));
CMD2_ANY ("network.http.ssl_verify_peer", std::bind(&core::CurlStack::ssl_verify_peer, httpStack));
CMD2_ANY_VALUE_V ("network.http.ssl_verify_peer.set", std::bind(&core::CurlStack::set_ssl_verify_peer, httpStack, 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.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.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.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.local_address", [nw_config](auto, auto) { return nw_config->local_address_best_match_str(); });
CMD_ANY_STRING_V("network.local_address.set", [nw_config](auto, auto& str) { return nw_config->set_local_address_str(str); });
CMD_ANY ("network.local_address.ipv4", [nw_config](auto, auto) { return nw_config->local_inet_address_str(); });
CMD_ANY_STRING_V("network.local_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet_address_str(str); });
CMD_ANY ("network.local_address.ipv6", [nw_config](auto, auto) { return nw_config->local_inet6_address_str(); });
CMD_ANY_STRING_V("network.local_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet6_address_str(str); });
CMD2_ANY ("network.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.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.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.open_files", [file_manager](auto, auto) { return file_manager->open_files(); });
CMD_ANY ("network.max_open_files", [file_manager](auto, auto) { return file_manager->max_open_files(); });
CMD_ANY ("network.total_handshakes", [](auto, auto) { return torrent::runtime::total_handshakes(); });
CMD2_ANY_STRING ("network.xmlrpc.dialect.set", [](const auto&, const auto& arg) { return apply_xmlrpc_dialect(arg); })
CMD2_ANY ("network.xmlrpc.size_limit", [](const auto&, const auto&){ return rpc::rpc.size_limit(); });
CMD2_ANY_VALUE_V ("network.xmlrpc.size_limit.set", [](const auto&, const auto& arg){ return rpc::rpc.set_size_limit(arg); });
CMD_ANY_STRING ("network.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_VAR_BOOL ("network.rpc.use_xmlrpc", true);
CMD2_VAR_BOOL ("network.rpc.use_jsonrpc", true);
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.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));
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");
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));
}
+69 -42
View File
@@ -1,28 +1,83 @@
// 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) {
auto sa = peer->peer_info()->socket_address();
auto addr_str = torrent::sa_addr_str(sa);
const rak::socket_address *addr = rak::socket_address::cast_from(peer->peer_info()->socket_address());
if (sa->sa_family == AF_INET6)
return "[" + addr_str + "]";
if (addr->family() == rak::socket_address::af_inet6)
return "[" + addr->address_str() + "]";
else
return addr->address_str();
}
return addr_str;
torrent::Object
retrieve_p_port(torrent::Peer* peer) {
return rak::socket_address::cast_from(peer->peer_info()->socket_address())->port();
}
torrent::Object
@@ -35,20 +90,18 @@ retrieve_p_client_version(torrent::Peer* peer) {
torrent::Object
retrieve_p_options_str(torrent::Peer* peer) {
return torrent::utils::transform_to_hex_str(peer->peer_info()->options(), peer->peer_info()->options() + 8);
return rak::transform_hex(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", [](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.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.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));
@@ -62,7 +115,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", [](auto* peer, auto) { return torrent::sa_port(peer->peer_info()->socket_address()); });
CMD2_PEER("p.port", std::bind(&retrieve_p_port, std::placeholders::_1));
CMD2_PEER("p.completed_percent", std::bind(&retrieve_p_completed_percent, std::placeholders::_1));
@@ -80,30 +133,4 @@ 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");
}
+82 -103
View File
@@ -1,10 +1,44 @@
// 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"
@@ -20,47 +54,35 @@ parse_address_range(const torrent::Object::list_type& args, torrent::Object::lis
unsigned int prefixWidth, ret;
char dummy;
char host[1024];
torrent::sa_unique_ptr sa;
rak::address_info* ai;
ret = std::sscanf(itr->as_string().c_str(), "%1023[^/]/%d%c", host, &prefixWidth, &dummy);
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()));
}
if (ret < 1 || rak::address_info::get_address_info(host, PF_INET, SOCK_STREAM, &ai) != 0)
throw torrent::input_error("Could not resolve host.");
uint32_t begin, end;
auto sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr);
begin = end = sa_addr;
rak::socket_address sa;
sa.copy(*ai->address(), ai->length());
begin = end = sa.sa_inet()->address_h();
rak::address_info::free_address_info(ai);
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_addr & ~netmask)
if (prefixWidth >= 32 || sa.sa_inet()->address_h() & ~netmask)
throw torrent::input_error("Invalid address/prefix.");
end = sa_addr | ~netmask;
end = sa.sa_inet()->address_h() | ~netmask;
} else if (++itr != args.end()) {
try {
sa = torrent::sa_copy(torrent::sa_lookup_address(itr->as_string(), AF_INET).get());
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.");
} 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;
sa.copy(*ai->address(), ai->length());
rak::address_info::free_address_info(ai);
end = sa.sa_inet()->address_h();
}
// convert to [begin, end) making sure the end doesn't overflow
@@ -70,35 +92,33 @@ parse_address_range(const torrent::Object::list_type& args, torrent::Object::lis
torrent::Object
apply_throttle(const torrent::Object::list_type& args, bool up) {
auto arg_itr = args.begin();
torrent::Object::list_const_iterator argItr = args.begin();
if (arg_itr == args.end())
if (argItr == args.end())
throw torrent::input_error("Missing throttle name.");
const std::string& name = arg_itr->as_string();
const std::string& name = argItr->as_string();
if (name.empty() || name == "NULL")
throw torrent::input_error("Invalid throttle name '" + name + "'.");
if (++arg_itr == args.end() || arg_itr->as_string().empty())
if (++argItr == args.end() || argItr->as_string().empty())
throw torrent::input_error("Missing throttle rate for '" + name + "'.");
int64_t rate;
rpc::parse_whole_value_nothrow(arg_itr->as_string().c_str(), &rate);
rpc::parse_whole_value_nothrow(argItr->as_string().c_str(), &rate);
if (rate < 0)
throw torrent::input_error("Throttle rate must be non-negative.");
auto itr = control->core()->throttles().find(name);
core::ThrottleMap::iterator itr = control->core()->throttles().find(name);
if (itr == control->core()->throttles().end())
itr = control->core()->throttles().insert(std::make_pair(name, core::ThrottlePair(nullptr, nullptr))).first;
itr = control->core()->throttles().insert(std::make_pair(name, torrent::ThrottlePair(NULL, NULL))).first;
auto*& throttle = up ? itr->second.first : itr->second.second;
if (rate != 0 && throttle == nullptr)
torrent::Throttle*& throttle = up ? itr->second.first : itr->second.second;
if (rate != 0 && throttle == NULL)
throttle = (up ? torrent::up_throttle_global() : torrent::down_throttle_global())->create_slave();
if (throttle != nullptr)
if (throttle != NULL)
throttle->set_max_rate(rate * 1024);
return torrent::Object();
@@ -111,10 +131,10 @@ static const int throttle_info_rate = (1 << 3);
torrent::Object
retrieve_throttle_info(const torrent::Object::string_type& name, int flags) {
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();
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();
if (throttle == NULL && name.empty())
throttle = global;
@@ -129,6 +149,20 @@ 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);
@@ -160,10 +194,10 @@ initialize_command_throttle() {
CMD2_VAR_VALUE ("throttle.max_downloads.div._val", 1);
CMD2_VAR_VALUE ("throttle.max_downloads.global._val", 0);
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_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_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));
@@ -186,65 +220,10 @@ 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");
}
+67 -101
View File
@@ -3,10 +3,9 @@
#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>
@@ -29,11 +28,14 @@ tracker_set_enabled(torrent::tracker::Tracker* tracker, bool state) {
torrent::Object
apply_dht_add_node(const std::string& arg) {
int port;
if (!torrent::dht_controller()->is_valid())
throw torrent::input_error("DHT not enabled.");
int port, ret;
char dummy;
char host[1024];
int ret = std::sscanf(arg.c_str(), "%1023[^:]:%i%c", host, &port, &dummy);
ret = std::sscanf(arg.c_str(), "%1023[^:]:%i%c", host, &port, &dummy);
if (ret == 1)
port = 6881;
@@ -43,23 +45,16 @@ 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_id());
auto host_str = std::string(host);
// TODO: Move this lookup to DhtController.
auto callback_id = torrent::system::make_callback_id();
assert(std::this_thread::get_id() == torrent::main_thread::thread()->thread_id());
// Currently discarding SOCK_STREAM.
torrent::this_thread::resolver()->resolve_specific(callback_id, host_str, PF_INET, [host_str, port](torrent::c_sa_shared_ptr sa, int err) {
torrent::this_thread::resolver()->resolve_specific(nullptr, host, PF_INET, [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());
lt_log_print(torrent::LOG_DHT_WARN, "Could not resolve host: %s", gai_strerror(err));
return;
}
lt_log_print(torrent::LOG_DHT_CONTROLLER, "dht.add_node : %s", host_str.c_str());
torrent::runtime::network_manager()->dht_add_bootstrap_node(host_str.c_str(), port);
torrent::dht_controller()->add_node(sa.get(), port);
});
return torrent::Object();
@@ -68,12 +63,22 @@ apply_dht_add_node(const std::string& arg) {
torrent::Object
apply_enable_trackers(int64_t arg) {
if (arg == 0) {
for (auto download : *control->core()->download_list())
download->tracker_controller().for_each([](auto& tracker) { tracker.disable(); });
for (auto itr : *control->core()->download_list())
itr->tracker_controller().for_each([](auto& tracker) { tracker.disable(); });
} else if (rpc::call_command_value("trackers.use_udp") == 0) {
for (auto itr : *control->core()->download_list()) {
itr->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(); });
for (auto itr : *control->core()->download_list())
itr->tracker_controller().for_each([](auto& tracker) { tracker.enable(); });
}
return torrent::Object();
@@ -81,100 +86,61 @@ apply_enable_trackers(int64_t arg) {
void
initialize_command_tracker() {
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(); });
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));
// TODO: Deprecate.
CMD2_TRACKER ("t.can_scrape", [](auto* tracker, auto) { return tracker->is_scrapable(); });
CMD2_TRACKER ("t.can_scrape", std::bind(&torrent::tracker::Tracker::is_scrapable, 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_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_VALUE_V("t.is_enabled.set", [](auto* tracker, auto& value) { return tracker_set_enabled(tracker, value); });
CMD2_TRACKER_VALUE_V("t.is_enabled.set", std::bind(&tracker_set_enabled, std::placeholders::_1, std::placeholders::_2));
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.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.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.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.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.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.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.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.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.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.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.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.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_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_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_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_ANY_VALUE ("trackers.enable", [](auto, auto) { return apply_enable_trackers(1); });
CMD2_ANY_VALUE ("trackers.disable", [](auto, auto) { return apply_enable_trackers(0); });
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_VAR_BOOL ("trackers.delay_scrape", false);
CMD2_VAR_VALUE ("trackers.numwant", -1);
CMD2_VAR_VALUE ("trackers.numwant", -1);
CMD2_VAR_BOOL ("trackers.use_udp", true);
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");
CMD2_ANY_STRING_V ("dht.mode.set", std::bind(&core::DhtManager::set_mode, control->dht_manager(), std::placeholders::_2));
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));
}
+98 -150
View File
@@ -4,6 +4,7 @@
#include <ctime>
#include <regex>
#include <rak/algorithm.h>
#include <torrent/utils/log.h>
#include "core/manager.h"
@@ -74,8 +75,8 @@ apply_view_list() {
torrent::Object rawResult = torrent::Object::create_list();
torrent::Object::list_type& result = rawResult.as_list();
for (auto itr : *control->view_manager())
result.push_back(itr->name());
for (core::ViewManager::const_iterator itr = control->view_manager()->begin(), last = control->view_manager()->end(); itr != last; itr++)
result.push_back((*itr)->name());
return rawResult;
}
@@ -192,18 +193,18 @@ apply_and(rpc::target_type target, const torrent::Object& rawArgs) {
if (rawArgs.type() != torrent::Object::TYPE_LIST)
return as_boolean(rawArgs);
for (const auto& itr : rawArgs.as_list())
if (itr.is_dict_key()) {
if (!as_boolean(rpc::commands.call_command(itr.as_dict_key().c_str(), itr.as_dict_obj(), target)))
for (torrent::Object::list_const_iterator itr = rawArgs.as_list().begin(), last = rawArgs.as_list().end(); itr != last; itr++)
if (itr->is_dict_key()) {
if (!as_boolean(rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target)))
return (int64_t)false;
} else if (itr.is_value()) {
if (!itr.as_value())
} else if (itr->is_value()) {
if (!itr->as_value())
return (int64_t)false;
} else {
// TODO: Switch to new versions that only accept the new command syntax.
if (!as_boolean(rpc::parse_command_single(target, itr.as_string())))
if (!as_boolean(rpc::parse_command_single(target, itr->as_string())))
return (int64_t)false;
}
@@ -215,17 +216,17 @@ apply_or(rpc::target_type target, const torrent::Object& rawArgs) {
if (rawArgs.type() != torrent::Object::TYPE_LIST)
return as_boolean(rawArgs);
for (const auto& itr : rawArgs.as_list())
if (itr.is_dict_key()) {
if (as_boolean(rpc::commands.call_command(itr.as_dict_key().c_str(), itr.as_dict_obj(), target)))
for (torrent::Object::list_const_iterator itr = rawArgs.as_list().begin(), last = rawArgs.as_list().end(); itr != last; itr++)
if (itr->is_dict_key()) {
if (as_boolean(rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target)))
return (int64_t)true;
} else if (itr.is_value()) {
if (itr.as_value())
} else if (itr->is_value()) {
if (itr->as_value())
return (int64_t)true;
} else {
if (as_boolean(rpc::parse_command_single(target, itr.as_string())))
if (as_boolean(rpc::parse_command_single(target, itr->as_string())))
return (int64_t)true;
}
@@ -472,14 +473,11 @@ 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& raw_args, int flags) {
auto& args = raw_args.as_list();
auto itr = args.begin();
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();
if (args.empty())
throw torrent::input_error("Empty argument list to " + std::string((flags & 0x1) ? "branch" : "if") + ".");
{
while (itr != args.end() && itr != --args.end()) {
torrent::Object tmp;
const torrent::Object* conditional;
@@ -503,44 +501,40 @@ apply_if(rpc::target_type target, const torrent::Object& raw_args, int flags) {
result = false;
break;
default:
throw torrent::input_error("Type not supported by " + std::string((flags & 0x1) ? "branch" : "if") + ".");
throw torrent::input_error("Type not supported by 'if'.");
};
itr++;
if (!result && itr != args.end())
itr++;
if (result)
break;
itr++;
}
if (itr == args.end())
return torrent::Object();
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;
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 (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_dict_key()) {
return rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target);
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());
} 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...
else if (cmd_itr.is_dict_key())
rpc::commands.call_command(cmd_itr.as_dict_key().c_str(), cmd_itr.as_dict_obj(), target);
for (torrent::Object::list_type::const_iterator cmdItr = itr->as_list().begin(), last = itr->as_list().end(); cmdItr != last; cmdItr++)
if (cmdItr->is_string())
rpc::parse_command(target, cmdItr->as_string().c_str(), cmdItr->as_string().c_str() + cmdItr->as_string().size());
else
throw torrent::input_error("Invalid command type in branch list.");
}
return torrent::Object();
return torrent::Object();
}
throw torrent::input_error("Invalid command type in branch.");
} else {
return *itr;
}
return *itr;
}
torrent::Object
@@ -636,26 +630,26 @@ as_vector(const torrent::Object::list_type& args) {
std::vector<int64_t> result;
for (const auto& arg : args) {
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
if (arg.is_value()) {
result.push_back(arg.as_value());
} else if (arg.is_string()) {
result.push_back(rpc::convert_to_value(arg.as_string()));
} else if (arg.is_list()) {
std::vector<int64_t> subResult = as_vector(arg.as_list());
if (itr->is_value()) {
result.push_back(itr->as_value());
} else if (itr->is_string()) {
result.push_back(rpc::convert_to_value(itr->as_string()));
} else if (itr->is_list()) {
std::vector<int64_t> subResult = as_vector(itr->as_list());
result.insert(result.end(), subResult.begin(), subResult.end());
} else {
throw torrent::input_error("Wrong type supplied to as_vector.");
}
}
return result;
}
template <typename Comp>
int64_t
apply_math_basic(const char* name, Comp op, const torrent::Object::list_type& args) {
apply_math_basic(const char* name, const std::function<int64_t(int64_t,int64_t)> op, const torrent::Object::list_type& args) {
int64_t val = 0, rhs = 0;
bool divides = !strcmp(name, "math.div") || !strcmp(name, "math.mod");
@@ -684,9 +678,8 @@ apply_math_basic(const char* name, Comp op, const torrent::Object::list_type& ar
return val;
}
template <typename Comp>
int64_t
apply_arith_basic(Comp op, const torrent::Object::list_type& args) {
apply_arith_basic(const std::function<int64_t(int64_t,int64_t)> op, const torrent::Object::list_type& args) {
if (args.size() == 0)
throw torrent::input_error("Wrong argument count in apply_arith_basic.");
@@ -718,44 +711,25 @@ apply_arith_count(const torrent::Object::list_type& args) {
int64_t val = 0;
for (const auto& arg : args) {
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
switch (arg.type()) {
switch (itr->type()) {
case torrent::Object::TYPE_VALUE:
case torrent::Object::TYPE_STRING:
val++;
break;
case torrent::Object::TYPE_LIST:
val += apply_arith_count(arg.as_list());
val += apply_arith_count(itr->as_list());
break;
default:
throw torrent::input_error("Wrong type supplied to apply_arith_count.");
}
}
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)
@@ -765,8 +739,7 @@ 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)median(result.begin(), result.end());
return (int64_t)rak::median(result.begin(), result.end());
} else {
throw torrent::input_error("Wrong operation supplied to apply_arith_other.");
}
@@ -779,9 +752,9 @@ cmd_status_throttle_names(bool up, const torrent::Object::list_type& args) {
std::vector<std::string> throttle_name_list;
for (const auto& arg : args) {
if (arg.is_string())
throttle_name_list.push_back(arg.as_string());
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
if (itr->is_string())
throttle_name_list.push_back(itr->as_string());
}
if (up)
@@ -802,59 +775,58 @@ 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));
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));
// 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_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 ("ui.keymap.style", std::bind(&ui::Root::keymap_style, control->ui()));
CMD2_ANY_STRING_V("ui.keymap.style.set", std::bind(&ui::Root::set_keymap_style, control->ui(), std::placeholders::_2));
CMD2_ANY_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));
// 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);
@@ -887,13 +859,13 @@ initialize_command_ui() {
CMD2_ANY_VALUE("convert.xb", std::bind(&apply_to_xb, std::placeholders::_2));
CMD2_ANY_VALUE("convert.throttle", std::bind(&apply_to_throttle, std::placeholders::_2));
CMD2_ANY_LIST("math.add", [](auto, const auto& args) { return apply_math_basic("math.add", std::plus(), args); });
CMD2_ANY_LIST("math.sub", [](auto, const auto& args) { return apply_math_basic("math.sub", std::minus(), args); });
CMD2_ANY_LIST("math.mul", [](auto, const auto& args) { return apply_math_basic("math.mul", std::multiplies(), args); });
CMD2_ANY_LIST("math.div", [](auto, const auto& args) { return apply_math_basic("math.div", std::divides(), args); });
CMD2_ANY_LIST("math.mod", [](auto, const auto& args) { return apply_math_basic("math.mod", std::modulus(), args); });
CMD2_ANY_LIST("math.min", [](auto, const auto& args) { return apply_arith_basic(std::less(), args); });
CMD2_ANY_LIST("math.max", [](auto, const auto& args) { return apply_arith_basic(std::greater(), args); });
CMD2_ANY_LIST("math.add", std::bind(&apply_math_basic, "math.add", std::plus<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.sub", std::bind(&apply_math_basic, "math.sub", std::minus<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.mul", std::bind(&apply_math_basic, "math.mul", std::multiplies<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.div", std::bind(&apply_math_basic, "math.div", std::divides<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.mod", std::bind(&apply_math_basic, "math.mod", std::modulus<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.min", std::bind(&apply_arith_basic, std::less<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.max", std::bind(&apply_arith_basic, std::greater<int64_t>(), std::placeholders::_2));
CMD2_ANY_LIST("math.cnt", std::bind(&apply_arith_count, std::placeholders::_2));
CMD2_ANY_LIST("math.avg", std::bind(&apply_arith_other, "average", std::placeholders::_2));
CMD2_ANY_LIST("math.med", std::bind(&apply_arith_other, "median", std::placeholders::_2));
@@ -913,28 +885,4 @@ 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");
}
+52 -66
View File
@@ -1,15 +1,13 @@
#include "config.h"
#include "control.h"
#include <unistd.h>
#include <sys/stat.h>
#include <torrent/net/http_stack.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/connection_manager.h>
#include <torrent/utils/directory_events.h>
#include "core/curl_stack.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,56 +24,68 @@
#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()),
#include "control.h"
Control::Control() :
m_ui(new ui::Root()),
m_display(new display::Manager()),
m_input(new input::Manager()),
m_inputStdin(new input::InputEvent(STDIN_FILENO)),
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_watch_ready_queue(new utils::WatchReadyQueue()) {
m_directory_events(new torrent::directory_events()) {
m_core = std::make_unique<core::Manager>();
m_view_manager = std::make_unique<core::ViewManager>();
m_dht_manager = std::make_unique<core::DhtManager>();
m_core = new core::Manager();
m_viewManager = new core::ViewManager();
m_dhtManager = new core::DhtManager();
m_inputStdin->slot_pressed(std::bind(&input::Manager::pressed, m_input.get(), std::placeholders::_1));
m_inputStdin->slot_pressed(std::bind(&input::Manager::pressed, m_input, std::placeholders::_1));
m_task_shutdown.slot() = [this] { handle_shutdown(); };
m_task_shutdown_clear_requests.slot() = [this] { handle_shutdown_clear_requests(); };
m_task_shutdown.slot() = std::bind(&Control::handle_shutdown, this);
m_commandScheduler->set_slot_error_message([this](const std::string& msg) { m_core->push_log_std(msg); });
}
Control::~Control() {
m_view_manager.reset();
delete m_inputStdin;
delete m_input;
m_ui.reset();
m_display.reset();
delete m_viewManager;
delete m_ui;
delete m_display;
delete m_core;
delete m_dhtManager;
delete m_directory_events;
delete m_commandScheduler;
delete m_objectStorage;
delete m_lua_engine;
}
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(); });
m_core->set_hashing_view(*m_view_manager->find_throw("hashing"));
m_core->http_stack()->set_user_agent(USER_AGENT);
m_core->initialize_second();
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_ui->init(this);
if(!display::Canvas::daemon())
m_inputStdin->insert();
if(!display::Canvas::daemon()) {
m_inputStdin->insert(torrent::this_thread::poll());
}
}
void
@@ -83,19 +93,12 @@ Control::cleanup() {
rpc::rpc.cleanup();
torrent::this_thread::scheduler()->erase(&m_task_shutdown);
torrent::this_thread::scheduler()->erase(&m_task_shutdown_clear_requests);
if(!display::Canvas::daemon())
m_inputStdin->remove();
if(!display::Canvas::daemon()) {
m_inputStdin->remove(torrent::this_thread::poll());
}
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_core->download_store()->disable();
m_ui->cleanup();
m_core->cleanup();
@@ -113,33 +116,27 @@ Control::cleanup_exception() {
bool
Control::is_shutdown_completed() {
if (!m_shutdown_quick)
if (!m_shutdownQuick || worker_thread->is_active())
return false;
// Tracker requests can be disowned, so wait for these to
// finish. The edge case of torrent http downloads may delay
// shutdown.
// 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()->size() != 0 || !core()->http_queue()->empty())
if (!core()->http_stack()->empty() || !core()->http_queue()->empty())
return false;
return core()->is_download_shutdown_completed();
return torrent::is_inactive();
}
void
Control::handle_shutdown() {
m_watch_ready_queue->shutdown();
rpc::commands.call_catch("event.system.shutdown", rpc::make_target(), "shutdown", "System shutdown event action failed: ");
if (scgi_thread::thread()->is_active())
scgi_thread::thread()->stop_thread_wait();
if (!m_shutdownQuick) {
if (worker_thread->is_active())
worker_thread->stop_thread_wait();
if (!m_shutdown_quick) {
torrent::runtime::network_manager()->listen_close();
torrent::runtime::shutdown();
torrent::connection_manager()->listen_close();
m_directory_events->close();
m_core->shutdown(false);
@@ -148,24 +145,13 @@ Control::handle_shutdown() {
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown, 5s);
} else {
torrent::runtime::quick_shutdown();
if (worker_thread->is_active())
worker_thread->stop_thread_wait();
m_core->shutdown(true);
}
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;
m_shutdownQuick = true;
m_shutdownReceived = 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);
}
+28 -41
View File
@@ -3,7 +3,6 @@
#include <atomic>
#include <cinttypes>
#include <memory>
#include <sys/types.h>
#include <torrent/torrent.h>
#include <torrent/utils/scheduler.h>
@@ -38,44 +37,38 @@ namespace torrent {
class directory_events;
}
namespace utils {
class WatchReadyQueue;
}
class Control {
public:
Control();
~Control();
bool is_shutdown_completed();
bool is_shutdown_received() { return m_shutdown_received; }
bool is_shutdown_started() { return m_shutdown_quick; }
bool is_shutdown_received() { return m_shutdownReceived; }
bool is_shutdown_started() { return m_shutdownQuick; }
void initialize();
void cleanup();
void cleanup_exception();
void handle_shutdown();
void handle_shutdown_clear_requests();
void receive_normal_shutdown() { m_shutdown_received = true; }
void receive_quick_shutdown() { m_shutdown_received = true; m_shutdown_quick = true; }
void receive_normal_shutdown() { m_shutdownReceived = true; }
void receive_quick_shutdown() { m_shutdownReceived = true; m_shutdownQuick = true; }
core::Manager* core() { return m_core.get(); }
core::ViewManager* view_manager() { return m_view_manager.get(); }
core::DhtManager* dht_manager() { return m_dht_manager.get(); }
core::Manager* core() { return m_core; }
core::ViewManager* view_manager() { return m_viewManager; }
core::DhtManager* dht_manager() { return m_dhtManager; }
ui::Root* ui() { return m_ui.get(); }
display::Manager* display() { return m_display.get(); }
input::Manager* input() { return m_input.get(); }
input::InputEvent* input_stdin() { return m_inputStdin.get(); }
ui::Root* ui() { return m_ui; }
display::Manager* display() { return m_display; }
input::Manager* input() { return m_input; }
input::InputEvent* input_stdin() { return m_inputStdin; }
rpc::CommandScheduler* command_scheduler() { return m_commandScheduler.get(); }
rpc::object_storage* object_storage() { return m_objectStorage.get(); }
rpc::LuaEngine* lua_engine() { return m_lua_engine.get(); }
rpc::CommandScheduler* command_scheduler() { return m_commandScheduler; }
rpc::object_storage* object_storage() { return m_objectStorage; }
rpc::LuaEngine* lua_engine() { return m_lua_engine; }
torrent::directory_events* directory_events() { return m_directory_events.get(); }
utils::WatchReadyQueue* watch_ready_queue() { return m_watch_ready_queue.get(); }
torrent::directory_events* directory_events() { return m_directory_events; }
uint64_t tick() const { return m_tick; }
void inc_tick() { m_tick++; }
@@ -87,20 +80,19 @@ private:
Control(const Control&);
void operator = (const Control&);
std::unique_ptr<core::Manager> m_core;
std::unique_ptr<core::ViewManager> m_view_manager;
std::unique_ptr<core::DhtManager> m_dht_manager;
core::Manager* m_core;
core::ViewManager* m_viewManager;
core::DhtManager* m_dhtManager;
std::unique_ptr<ui::Root> m_ui;
std::unique_ptr<display::Manager> m_display;
std::unique_ptr<input::Manager> m_input;
std::unique_ptr<input::InputEvent> m_inputStdin;
ui::Root* m_ui;
display::Manager* m_display;
input::Manager* m_input;
input::InputEvent* m_inputStdin;
std::unique_ptr<rpc::CommandScheduler> m_commandScheduler;
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;
rpc::CommandScheduler* m_commandScheduler;
rpc::object_storage* m_objectStorage;
rpc::LuaEngine* m_lua_engine;
torrent::directory_events* m_directory_events;
uint64_t m_tick{};
@@ -108,14 +100,9 @@ private:
std::string m_workingDirectory;
torrent::utils::SchedulerEntry m_task_shutdown;
torrent::utils::SchedulerEntry m_task_shutdown_clear_requests;
int m_clear_requests_count{};
align_cacheline
std::atomic<bool> m_shutdown_received{};
std::atomic<bool> m_shutdown_quick{};
std::atomic<bool> m_shutdownReceived{};
std::atomic<bool> m_shutdownQuick{};
};
#endif
+122
View File
@@ -0,0 +1,122 @@
#include "config.h"
#include "core/curl_get.h"
#include <iostream>
#include <curl/curl.h>
#include <curl/easy.h>
#include <torrent/exceptions.h>
#include "globals.h"
#include "core/curl_stack.h"
namespace core {
size_t
curl_get_receive_write(void* data, size_t size, size_t nmemb, void* handle) {
if (!((CurlGet*)handle)->stream()->write((const char*)data, size * nmemb).fail())
return size * nmemb;
else
return 0;
}
CurlGet::CurlGet(CurlStack* s) :
m_stack(s) {
m_task_timeout.slot() = [this]() { receive_timeout(); };
}
CurlGet::~CurlGet() {
close();
}
void
CurlGet::start() {
if (is_busy())
throw torrent::internal_error("Tried to call CurlGet::start on a busy object.");
if (m_stream == NULL)
throw torrent::internal_error("Tried to call CurlGet::start without a valid output stream.");
if (!m_stack->is_running())
return;
m_handle = curl_easy_init();
if (m_handle == NULL)
throw torrent::internal_error("Call to curl_easy_init() failed.");
curl_easy_setopt(m_handle, CURLOPT_URL, m_url.c_str());
curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION, &curl_get_receive_write);
curl_easy_setopt(m_handle, CURLOPT_WRITEDATA, this);
if (m_timeout != 0) {
curl_easy_setopt(m_handle, CURLOPT_CONNECTTIMEOUT, (long)60);
curl_easy_setopt(m_handle, CURLOPT_TIMEOUT, (long)m_timeout);
// Normally libcurl should handle the timeout. But sometimes that doesn't
// work right so we do a fallback timeout that just aborts the transfer.
torrent::this_thread::scheduler()->update_wait_for_ceil_seconds(&m_task_timeout, 5s + 1s*m_timeout);
}
curl_easy_setopt(m_handle, CURLOPT_FORBID_REUSE, (long)1);
curl_easy_setopt(m_handle, CURLOPT_NOSIGNAL, (long)1);
curl_easy_setopt(m_handle, CURLOPT_FOLLOWLOCATION, (long)1);
curl_easy_setopt(m_handle, CURLOPT_MAXREDIRS, (long)5);
curl_easy_setopt(m_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER);
curl_easy_setopt(m_handle, CURLOPT_ENCODING, "");
m_ipv6 = false;
m_stack->add_get(this);
}
void
CurlGet::close() {
torrent::this_thread::scheduler()->erase(&m_task_timeout);
if (!is_busy())
return;
m_stack->remove_get(this);
curl_easy_cleanup(m_handle);
m_handle = NULL;
}
void
CurlGet::retry_ipv6() {
CURL* nhandle = curl_easy_duphandle(m_handle);
curl_easy_setopt(nhandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
curl_easy_cleanup(m_handle);
m_handle = nhandle;
m_ipv6 = true;
}
void
CurlGet::receive_timeout() {
return m_stack->transfer_done(m_handle, "Timed out");
}
curl_off_t
CurlGet::size_done() {
curl_off_t d = 0;
curl_easy_getinfo(m_handle, CURLINFO_SIZE_DOWNLOAD_T, &d);
return d;
}
curl_off_t
CurlGet::size_total() {
curl_off_t d = 0;
curl_easy_getinfo(m_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &d);
return d;
}
}
+54
View File
@@ -0,0 +1,54 @@
#ifndef RTORRENT_CORE_CURL_GET_H
#define RTORRENT_CORE_CURL_GET_H
#include <iosfwd>
#include <string>
#include <curl/curl.h>
#include <torrent/http.h>
#include <torrent/utils/scheduler.h>
namespace core {
class CurlStack;
class CurlGet : public torrent::Http {
public:
CurlGet(CurlStack* s);
virtual ~CurlGet();
void start();
void close();
bool is_using_ipv6() { return m_ipv6; }
void retry_ipv6();
bool is_busy() const { return m_handle; }
bool is_active() const { return m_active; }
void set_active(bool a) { m_active = a; }
curl_off_t size_done();
curl_off_t size_total();
CURL* handle() { return m_handle; }
private:
friend class CurlStack;
CurlGet(const CurlGet&) = delete;
void operator = (const CurlGet&) = delete;
void receive_timeout();
bool m_active{};
bool m_ipv6;
torrent::utils::SchedulerEntry m_task_timeout;
CURL* m_handle{};
CurlStack* m_stack;
};
}
#endif
+99
View File
@@ -0,0 +1,99 @@
#include "config.h"
#include "curl_socket.h"
#include <cassert>
#include <curl/multi.h>
#include <torrent/poll.h>
#include <torrent/exceptions.h>
#include <torrent/utils/thread.h>
#include "control.h"
#include "core/curl_stack.h"
namespace core {
int
CurlSocket::receive_socket([[maybe_unused]] void* easy_handle, curl_socket_t fd, int what, void* userp, void* socketp) {
CurlStack* stack = (CurlStack*)userp;
CurlSocket* socket = (CurlSocket*)socketp;
if (!stack->is_running())
return 0;
if (what == CURL_POLL_REMOVE) {
// We also probably need the special code here as we're not
// guaranteed that the fd will be closed, afaik.
if (socket != NULL)
socket->close();
// TODO: Consider the possibility that we'll need to set the
// fd-associated pointer curl holds to NULL.
delete socket;
return 0;
}
if (socket == NULL) {
socket = stack->new_socket(fd);
torrent::this_thread::poll()->open(socket);
// No interface for libcurl to signal when it's interested in error events.
// Assume that hence it must always be interested in them.
torrent::this_thread::poll()->insert_error(socket);
}
if (what == CURL_POLL_NONE || what == CURL_POLL_OUT)
torrent::this_thread::poll()->remove_read(socket);
else
torrent::this_thread::poll()->insert_read(socket);
if (what == CURL_POLL_NONE || what == CURL_POLL_IN)
torrent::this_thread::poll()->remove_write(socket);
else
torrent::this_thread::poll()->insert_write(socket);
return 0;
}
CurlSocket::~CurlSocket() {
assert(m_fileDesc == -1 && "CurlSocket::~CurlSocket() m_fileDesc != -1.");
}
void
CurlSocket::close() {
if (m_fileDesc == -1)
throw torrent::internal_error("CurlSocket::close() m_fileDesc == -1.");
torrent::this_thread::poll()->closed(this);
m_fileDesc = -1;
}
void
CurlSocket::event_read() {
#if (LIBCURL_VERSION_NUM >= 0x071003)
return m_stack->receive_action(this, CURL_CSELECT_IN);
#else
return m_stack->receive_action(this, 0);
#endif
}
void
CurlSocket::event_write() {
#if (LIBCURL_VERSION_NUM >= 0x071003)
return m_stack->receive_action(this, CURL_CSELECT_OUT);
#else
return m_stack->receive_action(this, 0);
#endif
}
void
CurlSocket::event_error() {
#if (LIBCURL_VERSION_NUM >= 0x071003)
return m_stack->receive_action(this, CURL_CSELECT_ERR);
#else
return m_stack->receive_action(this, 0);
#endif
}
}
+37
View File
@@ -0,0 +1,37 @@
#ifndef RTORRENT_CORE_CURL_SOCKET_H
#define RTORRENT_CORE_CURL_SOCKET_H
#include <curl/curl.h>
#include <torrent/event.h>
#include "globals.h"
namespace core {
class CurlStack;
class CurlSocket : public torrent::Event {
public:
CurlSocket(int fd, CurlStack* stack) : m_stack(stack) { m_fileDesc = fd; }
~CurlSocket();
const char* type_name() const { return "curl"; }
void close();
static int receive_socket(void* easy_handle, curl_socket_t fd, int what, void* userp, void* socketp);
private:
CurlSocket(const CurlSocket&);
void operator = (const CurlSocket&);
virtual void event_read();
virtual void event_write();
virtual void event_error();
CurlStack* m_stack;
};
}
#endif
+247
View File
@@ -0,0 +1,247 @@
#include "config.h"
#include <algorithm>
#include <curl/multi.h>
#include <torrent/exceptions.h>
#include "curl_get.h"
#include "curl_socket.h"
#include "curl_stack.h"
namespace core {
CurlStack::CurlStack() {
m_handle = (void*)curl_multi_init();
m_task_timeout.slot() = std::bind(&CurlStack::receive_timeout, this);
#if (LIBCURL_VERSION_NUM >= 0x071000)
curl_multi_setopt((CURLM*)m_handle, CURLMOPT_TIMERDATA, this);
curl_multi_setopt((CURLM*)m_handle, CURLMOPT_TIMERFUNCTION, &CurlStack::set_timeout);
#endif
curl_multi_setopt((CURLM*)m_handle, CURLMOPT_SOCKETDATA, this);
curl_multi_setopt((CURLM*)m_handle, CURLMOPT_SOCKETFUNCTION, &CurlSocket::receive_socket);
}
CurlStack::~CurlStack() {
shutdown();
}
void
CurlStack::shutdown() {
if (!m_running)
return;
m_running = false;
while (!empty())
front()->close();
curl_multi_cleanup((CURLM*)m_handle);
torrent::this_thread::scheduler()->erase(&m_task_timeout);
}
CurlGet*
CurlStack::new_object() {
return new CurlGet(this);
}
CurlSocket*
CurlStack::new_socket(int fd) {
if (!m_running)
throw torrent::internal_error("CurlStack::new_socket() called when not running.");
CurlSocket* socket = new CurlSocket(fd, this);
curl_multi_assign((CURLM*)m_handle, fd, socket);
return socket;
}
void
CurlStack::receive_action(CurlSocket* socket, int events) {
CURLMcode code;
do {
int count;
#if (LIBCURL_VERSION_NUM >= 0x071003)
code = curl_multi_socket_action((CURLM*)m_handle,
socket != NULL ? socket->file_descriptor() : CURL_SOCKET_TIMEOUT,
events,
&count);
#else
code = curl_multi_socket((CURLM*)m_handle,
socket != NULL ? socket->file_descriptor() : CURL_SOCKET_TIMEOUT,
&count);
#endif
if (code > 0)
throw torrent::internal_error("Error calling curl_multi_socket_action.");
// Socket might be removed when cleaning handles below, future
// calls should not use it.
socket = NULL;
events = 0;
if ((unsigned int)count != size()) {
while (process_done_handle())
; // Do nothing.
if (empty())
torrent::this_thread::scheduler()->erase(&m_task_timeout);
}
} while (code == CURLM_CALL_MULTI_PERFORM);
}
bool
CurlStack::process_done_handle() {
int remaining_msgs = 0;
CURLMsg* msg = curl_multi_info_read((CURLM*)m_handle, &remaining_msgs);
if (msg == NULL)
return false;
if (msg->msg != CURLMSG_DONE)
throw torrent::internal_error("CurlStack::receive_action() msg->msg != CURLMSG_DONE.");
if (msg->data.result == CURLE_COULDNT_RESOLVE_HOST) {
iterator itr = std::find_if(begin(), end(), [&msg](CurlGet* get) { return get->handle() == msg->easy_handle; });
if (itr == end())
throw torrent::internal_error("Could not find CurlGet when calling CurlStack::receive_action.");
if (!(*itr)->is_using_ipv6()) {
(*itr)->retry_ipv6();
if (curl_multi_add_handle((CURLM*)m_handle, (*itr)->handle()) > 0)
throw torrent::internal_error("Error calling curl_multi_add_handle.");
}
} else {
transfer_done(msg->easy_handle,
msg->data.result == CURLE_OK ? NULL : curl_easy_strerror(msg->data.result));
}
return remaining_msgs != 0;
}
void
CurlStack::transfer_done(void* handle, const char* msg) {
iterator itr = std::find_if(begin(), end(), [&handle](CurlGet* get) { return get->handle() == handle; });
if (itr == end())
throw torrent::internal_error("Could not find CurlGet with the right easy_handle.");
if (msg == NULL)
(*itr)->trigger_done();
else
(*itr)->trigger_failed(msg);
}
void
CurlStack::receive_timeout() {
receive_action(NULL, 0);
if (!empty() && !m_task_timeout.is_scheduled()) {
// Sometimes libcurl forgets to reset the timeout. Try to poll the value in that case, or use 10
// seconds max.
long timeout_ms;
curl_multi_timeout((CURLM*)m_handle, &timeout_ms);
auto timeout = std::max<std::chrono::microseconds>(std::chrono::milliseconds(timeout_ms), 10s);
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_timeout, timeout);
}
}
void
CurlStack::add_get(CurlGet* get) {
if (!m_user_agent.empty())
curl_easy_setopt(get->handle(), CURLOPT_USERAGENT, m_user_agent.c_str());
if (!m_http_proxy.empty())
curl_easy_setopt(get->handle(), CURLOPT_PROXY, m_http_proxy.c_str());
if (!m_bind_address.empty())
curl_easy_setopt(get->handle(), CURLOPT_INTERFACE, m_bind_address.c_str());
if (!m_http_ca_path.empty())
curl_easy_setopt(get->handle(), CURLOPT_CAPATH, m_http_ca_path.c_str());
if (!m_http_ca_cert.empty())
curl_easy_setopt(get->handle(), CURLOPT_CAINFO, m_http_ca_cert.c_str());
curl_easy_setopt(get->handle(), CURLOPT_SSL_VERIFYHOST, (long)(m_ssl_verify_host ? 2 : 0));
curl_easy_setopt(get->handle(), CURLOPT_SSL_VERIFYPEER, (long)(m_ssl_verify_peer ? 1 : 0));
curl_easy_setopt(get->handle(), CURLOPT_DNS_CACHE_TIMEOUT, m_dns_timeout);
base_type::push_back(get);
if (m_active >= m_max_active)
return;
m_active++;
get->set_active(true);
if (curl_multi_add_handle((CURLM*)m_handle, get->handle()) > 0)
throw torrent::internal_error("Error calling curl_multi_add_handle.");
#if (LIBCURL_VERSION_NUM < 0x071000)
receive_timeout();
#endif
}
void
CurlStack::remove_get(CurlGet* get) {
iterator itr = std::find(begin(), end(), get);
if (itr == end())
throw torrent::internal_error("Could not find CurlGet when calling CurlStack::remove.");
base_type::erase(itr);
// The CurlGet object was never activated, so we just skip this one.
if (!get->is_active())
return;
get->set_active(false);
if (curl_multi_remove_handle((CURLM*)m_handle, get->handle()) > 0)
throw torrent::internal_error("Error calling curl_multi_remove_handle.");
if (m_active == m_max_active &&
(itr = std::find_if(begin(), end(), [](CurlGet* get) { return !get->is_active(); })) != end()) {
(*itr)->set_active(true);
if (curl_multi_add_handle((CURLM*)m_handle, (*itr)->handle()) > 0)
throw torrent::internal_error("Error calling curl_multi_add_handle.");
} else {
m_active--;
}
}
void
CurlStack::global_init() {
curl_global_init(CURL_GLOBAL_ALL);
}
void
CurlStack::global_cleanup() {
curl_global_cleanup();
}
// TODO: Is this function supposed to set a per-handle timeout, or is
// it the shortest timeout amongst all handles?
int
CurlStack::set_timeout(void*, long timeout_ms, void* userp) {
CurlStack* stack = (CurlStack*)userp;
if (timeout_ms == -1)
torrent::this_thread::scheduler()->erase(&stack->m_task_timeout);
else
torrent::this_thread::scheduler()->update_wait_for_ceil_seconds(&stack->m_task_timeout, std::chrono::milliseconds(timeout_ms));
return 0;
}
}
+120
View File
@@ -0,0 +1,120 @@
#ifndef RTORRENT_CORE_CURL_STACK_H
#define RTORRENT_CORE_CURL_STACK_H
#include <deque>
#include <string>
#include <torrent/utils/scheduler.h>
namespace core {
class CurlGet;
class CurlSocket;
// By using a deque instead of vector we allow for cheaper removal of
// the oldest elements, those that will be first in the in the
// deque.
//
// This should fit well with the use-case of a http stack, thus
// we get most of the cache locality benefits of a vector with fast
// removal of elements.
class CurlStack : std::deque<CurlGet*> {
public:
friend class CurlGet;
typedef std::deque<CurlGet*> base_type;
using base_type::value_type;
using base_type::iterator;
using base_type::const_iterator;
using base_type::reverse_iterator;
using base_type::const_reverse_iterator;
using base_type::begin;
using base_type::end;
using base_type::rbegin;
using base_type::rend;
using base_type::back;
using base_type::front;
using base_type::size;
using base_type::empty;
CurlStack();
~CurlStack();
void shutdown();
bool is_running() const { return m_running; }
CurlGet* new_object();
CurlSocket* new_socket(int fd);
unsigned int active() const { return m_active; }
unsigned int max_active() const { return m_max_active; }
void set_max_active(unsigned int a) { m_max_active = a; }
const std::string& user_agent() const { return m_user_agent; }
const std::string& http_proxy() const { return m_http_proxy; }
const std::string& bind_address() const { return m_bind_address; }
const std::string& http_capath() const { return m_http_ca_path; }
const std::string& http_cacert() const { return m_http_ca_cert; }
void set_user_agent(const std::string& s) { m_user_agent = s; }
void set_http_proxy(const std::string& s) { m_http_proxy = s; }
void set_bind_address(const std::string& s) { m_bind_address = s; }
void set_http_capath(const std::string& s) { m_http_ca_path = s; }
void set_http_cacert(const std::string& s) { m_http_ca_cert = s; }
bool ssl_verify_host() const { return m_ssl_verify_host; }
bool ssl_verify_peer() const { return m_ssl_verify_peer; }
void set_ssl_verify_host(bool s) { m_ssl_verify_host = s; }
void set_ssl_verify_peer(bool s) { m_ssl_verify_peer = s; }
long dns_timeout() const { return m_dns_timeout; }
void set_dns_timeout(long timeout) { m_dns_timeout = timeout; }
static void global_init();
static void global_cleanup();
void receive_action(CurlSocket* socket, int type);
static int set_timeout(void*, long timeout_ms, void* userp);
void transfer_done(void* handle, const char* msg);
protected:
void add_get(CurlGet* get);
void remove_get(CurlGet* get);
private:
CurlStack(const CurlStack&) = delete;
void operator = (const CurlStack&) = delete;
void receive_timeout();
bool process_done_handle();
void* m_handle;
bool m_running{true};
unsigned int m_active{0};
unsigned int m_max_active{32};
torrent::utils::SchedulerEntry m_task_timeout;
std::string m_user_agent;
std::string m_http_proxy;
std::string m_bind_address;
std::string m_http_ca_path;
std::string m_http_ca_cert;
bool m_ssl_verify_host{true};
bool m_ssl_verify_peer{true};
long m_dns_timeout{60};
};
}
#endif
+75 -93
View File
@@ -1,29 +1,25 @@
#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 "control.h"
#include "download.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__);
#include "globals.h"
#define LT_LOG_ERROR(log_fmt, ...) \
lt_log_print_subsystem(torrent::LOG_DHT_ERROR, "dht_manager", log_fmt, __VA_ARGS__);
#include "control.h"
#include "dht_manager.h"
#include "download.h"
#include "download_store.h"
#include "manager.h"
#define LT_LOG_THIS(log_fmt, ...) \
lt_log_print_subsystem(torrent::LOG_DHT_MANAGER, "dht_manager", log_fmt, __VA_ARGS__);
namespace core {
@@ -36,13 +32,13 @@ DhtManager::~DhtManager() {
void
DhtManager::load_dht_cache() {
if (m_start == dht_disable || !session_thread::manager()->is_used()) {
LT_LOG("ignoring cache file", 0);
if (m_start == dht_disable || !control->core()->download_store()->is_enabled()) {
LT_LOG_THIS("ignoring cache file", 0);
return;
}
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);
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);
torrent::Object cache = torrent::Object::create_map();
@@ -52,17 +48,17 @@ DhtManager::load_dht_cache() {
// If the cache file is corrupted we will just discard it with an
// error message.
if (cache_stream.fail()) {
LT_LOG_ERROR("cache file corrupted, discarding (path:%s)", cache_filename.c_str());
LT_LOG_THIS("cache file corrupted, discarding (path:%s)", cache_filename.c_str());
cache = torrent::Object::create_map();
} else {
LT_LOG("cache file read (path:%s)", cache_filename.c_str());
LT_LOG_THIS("cache file read (path:%s)", cache_filename.c_str());
}
} else {
LT_LOG("could not open cache file (path:%s)", cache_filename.c_str());
LT_LOG_THIS("could not open cache file (path:%s)", cache_filename.c_str());
}
torrent::runtime::network_manager()->dht_controller()->initialize(cache);
torrent::dht_controller()->initialize(cache);
if (m_start == dht_on)
start_dht();
@@ -72,22 +68,31 @@ void
DhtManager::start_dht() {
torrent::this_thread::scheduler()->erase(&m_stop_timeout);
if (!torrent::runtime::network_manager()->is_dht_valid()) {
LT_LOG("server start skipped, manager is uninitialized", 0);
if (!torrent::dht_controller()->is_valid()) {
LT_LOG_THIS("server start skipped, manager is uninitialized", 0);
return;
}
if (torrent::runtime::network_manager()->is_dht_active()) {
LT_LOG("server start skipped, already active", 0);
if (torrent::dht_controller()->is_active()) {
LT_LOG_THIS("server start skipped, already active", 0);
return;
}
if (!torrent::runtime::network_manager()->dht_controller()->start()) {
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);
int port = rpc::call_command_value("dht.port");
if (port <= 0)
return;
if (!torrent::dht_controller()->start(port)) {
m_start = dht_off;
return;
}
torrent::runtime::network_manager()->dht_controller()->reset_statistics();
torrent::dht_controller()->reset_statistics();
m_update_timeout.slot() = std::bind(&DhtManager::update, this);
@@ -106,31 +111,28 @@ DhtManager::stop_dht() {
torrent::this_thread::scheduler()->erase(&m_update_timeout);
torrent::this_thread::scheduler()->erase(&m_stop_timeout);
if (torrent::runtime::network_manager()->is_dht_active()) {
LT_LOG("stopping server", 0);
if (torrent::dht_controller()->is_active()) {
LT_LOG_THIS("stopping server", 0);
log_statistics(true);
torrent::runtime::network_manager()->dht_controller()->stop();
torrent::dht_controller()->stop();
}
}
void
DhtManager::save_dht_cache() {
if (!session_thread::manager()->is_used())
if (!control->core()->download_store()->is_enabled() || !torrent::dht_controller()->is_valid())
return;
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);
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 (!cache_file.is_open())
return;
torrent::Object cache = torrent::Object::create_map();
cache_file << *torrent::runtime::network_manager()->dht_controller()->store_cache(&cache);
cache_file << *torrent::dht_controller()->store_cache(&cache);
if (!cache_file.good())
return;
@@ -141,32 +143,17 @@ DhtManager::save_dht_cache() {
}
void
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;
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;
}
}
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 (i == dht_settings_num)
throw torrent::input_error("Invalid argument.");
if (m_start == dht_off)
stop_dht();
@@ -174,26 +161,9 @@ DhtManager::set_mode_directly(unsigned int mode) {
start_dht();
}
void
DhtManager::set_auto_if_untouched_and_has_session() {
if (m_set_by_user) {
set_mode_directly(m_start);
return;
}
if (rpc::call_command_string("session.path").empty()) {
LT_LOG("DHT auto-start disabled, session path not set.", 0);
return;
}
set_mode_directly(dht_auto);
LT_LOG("DHT auto-start enabled.", 0);
}
void
DhtManager::update() {
if (!torrent::runtime::network_manager()->is_dht_active())
if (!torrent::dht_controller()->is_active())
throw torrent::internal_error("DhtManager::update called with DHT inactive.");
if (m_start == dht_auto && !m_stop_timeout.is_scheduled()) {
@@ -218,23 +188,23 @@ DhtManager::update() {
bool
DhtManager::log_statistics(bool force) {
auto stats = torrent::runtime::network_manager()->dht_controller()->get_statistics();
auto stats = torrent::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::runtime::network_manager()->is_dht_active_and_receiving_requests())
LT_LOG("listening port appears to be unreachable, no queries received", 0);
if (torrent::dht_controller()->is_receiving_requests())
LT_LOG_THIS("listening port appears to be unreachable, no queries received", 0);
torrent::runtime::network_manager()->dht_controller()->set_receive_requests(false);
torrent::dht_controller()->set_receive_requests(false);
}
if (stats.queries_sent - m_dhtPrevQueriesSent > stats.num_nodes * 2 + 20 && stats.replies_received == m_dhtPrevRepliesReceived) {
// No replies to over 20 queries plus two per node we have. Probably firewalled.
if (!m_warned)
LT_LOG("listening port appears to be firewalled, no replies received", 0);
LT_LOG_THIS("listening port appears to be firewalled, no replies received", 0);
m_warned = true;
return false;
@@ -243,7 +213,7 @@ DhtManager::log_statistics(bool force) {
m_warned = false;
if (stats.queries_received > m_dhtPrevQueriesReceived)
torrent::runtime::network_manager()->dht_controller()->set_receive_requests(true);
torrent::dht_controller()->set_receive_requests(true);
// Nothing to log while bootstrapping, but check again every minute.
if (stats.cycle <= 1) {
@@ -265,11 +235,13 @@ 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, "
"DHT statistics: %d queries in, %d queries out, %d replies received, %lld bytes read, %lld bytes sent, "
"%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,
@@ -282,6 +254,8 @@ 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;
@@ -292,11 +266,11 @@ DhtManager::dht_statistics() {
torrent::Object dhtStats = torrent::Object::create_map();
dhtStats.insert_key("dht", dht_settings[m_start]);
dhtStats.insert_key("active", torrent::runtime::network_manager()->is_dht_active());
dhtStats.insert_key("throttle", "");
dhtStats.insert_key("active", torrent::dht_controller()->is_active());
dhtStats.insert_key("throttle", m_throttleName);
if (torrent::runtime::network_manager()->is_dht_active()) {
auto stats = torrent::runtime::network_manager()->dht_controller()->get_statistics();
if (torrent::dht_controller()->is_active()) {
auto stats = torrent::dht_controller()->get_statistics();
dhtStats.insert_key("cycle", stats.cycle);
dhtStats.insert_key("queries_received", stats.queries_received);
@@ -304,8 +278,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", int64_t());
dhtStats.insert_key("bytes_written", int64_t());
dhtStats.insert_key("bytes_read", stats.down_rate.total());
dhtStats.insert_key("bytes_written", stats.up_rate.total());
dhtStats.insert_key("nodes", stats.num_nodes);
dhtStats.insert_key("buckets", stats.num_buckets);
dhtStats.insert_key("peers", stats.num_peers);
@@ -316,4 +290,12 @@ 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;
}
}
+12 -11
View File
@@ -8,11 +8,6 @@ namespace core {
class DhtManager {
public:
static constexpr int dht_disable = 0;
static constexpr int dht_off = 1;
static constexpr int dht_auto = 2;
static constexpr int dht_on = 3;
~DhtManager();
void load_dht_cache();
@@ -23,14 +18,19 @@ public:
void stop_dht();
void auto_start() { if (m_start == dht_auto) start_dht(); }
void set_mode_by_user(const std::string& arg);
void set_mode_directly(unsigned int mode);
void set_mode(const std::string& arg);
void set_auto_if_untouched_and_has_session();
void set_throttle_name(const std::string& throttleName);
const std::string& throttle_name() const { return m_throttleName; }
private:
static constexpr int dht_settings_num = 4;
static const char* dht_settings[dht_settings_num];
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];
void update();
bool log_statistics(bool force);
@@ -46,8 +46,9 @@ private:
torrent::utils::SchedulerEntry m_stop_timeout;
bool m_warned{};
bool m_set_by_user{};
int m_start{dht_off};
std::string m_throttleName;
};
}
+30 -18
View File
@@ -1,25 +1,24 @@
#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 "core/manager.h"
#include "download.h"
#include "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,6 +31,21 @@ 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");
@@ -88,46 +102,44 @@ Download::distributed_copies() const {
}
void
Download::set_throttle_name(const std::string& name) {
Download::set_throttle_name(const std::string& throttleName) {
if (m_download.info()->is_active())
throw torrent::input_error("Cannot set throttle on active download.");
auto throttles = control->core()->get_throttle(name);
torrent::ThrottlePair throttles = control->core()->get_throttle(throttleName);
m_download.set_upload_throttle(throttles.first);
m_download.set_download_throttle(throttles.second);
m_download.bencode()->get_key("rtorrent").insert_key("throttle_name", name);
m_download.bencode()->get_key("rtorrent").insert_key("throttle_name", throttleName);
}
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.
torrent::utils::FileStat file_stat;
torrent::FileList* file_list = m_download.file_list();
rak::file_stat 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().str()))) {
!file_stat.update(file_list->front()->frozen_path()))) {
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 after the files have been moved.");
throw torrent::input_error("Cannot change the directory of an open download atter the files have been moved.");
}
control->core()->download_list()->close_directly(this);
file_list->set_root_dir(expand_path(path));
file_list->set_root_dir(rak::path_expand(path));
bencode()->get_key("rtorrent").insert_key("directory", path);
}
+4 -4
View File
@@ -22,8 +22,6 @@ 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;
@@ -72,6 +70,8 @@ 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& name);
void set_throttle_name(const std::string& throttleName);
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{default_resume_flags};
uint32_t m_resumeFlags{~uint32_t{}};
unsigned int m_group{};
};
+56 -61
View File
@@ -1,12 +1,12 @@
#include "config.h"
#include "download_factory.h"
#include <cstdlib>
#include <fstream>
#include <functional>
#include <sstream>
#include <stdexcept>
#include <rak/path.h>
#include <torrent/utils/log.h>
#include <torrent/utils/resume.h>
#include <torrent/object.h>
@@ -14,17 +14,19 @@
#include <torrent/exceptions.h>
#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/http_queue.h"
#include "core/manager.h"
#include "rpc/parse_commands.h"
#include "curl_get.h"
#include "control.h"
#include "http_queue.h"
#include "globals.h"
#include "manager.h"
#include "download.h"
#include "download_factory.h"
#include "download_store.h"
namespace core {
bool
@@ -75,7 +77,9 @@ DownloadFactory::~DownloadFactory() {
torrent::this_thread::scheduler()->erase(&m_task_load);
torrent::this_thread::scheduler()->erase(&m_task_commit);
delete m_stream;
delete m_object;
m_stream = NULL;
}
void
@@ -90,7 +94,7 @@ DownloadFactory::load_raw_data(const std::string& input) {
if (m_stream)
throw torrent::internal_error("DownloadFactory::load*() called on an object with m_stream != NULL");
m_stream.reset(new std::stringstream(input));
m_stream = new std::stringstream(input);
m_loaded = true;
}
@@ -105,42 +109,39 @@ DownloadFactory::receive_load() {
throw torrent::internal_error("DownloadFactory::load*() called on an object with m_stream != NULL");
if (is_network_uri(m_uri)) {
m_stream.reset(new std::stringstream);
// Http handling here.
m_stream = 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); };
m_manager->http_queue()->insert(m_uri, m_stream, done_fn, failed_fn);
(*itr)->signal_done().push_front(std::bind(&DownloadFactory::receive_loaded, this));
(*itr)->signal_failed().push_front(std::bind(&DownloadFactory::receive_failed, this, std::placeholders::_1));
m_variables["tied_to_file"] = (int64_t)false;
return;
}
if (is_magnet_uri(m_uri)) {
} else if (is_magnet_uri(m_uri)) {
// DEBUG: Use m_object.
m_stream.reset(new std::stringstream());
m_stream = 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
@@ -161,8 +162,8 @@ DownloadFactory::receive_commit() {
void
DownloadFactory::receive_success() {
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());
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());
uint32_t tracker_key;
@@ -171,8 +172,8 @@ DownloadFactory::receive_success() {
else
tracker_key = random() % (std::numeric_limits<uint32_t>::max() - 1) + 1;
Download* download = m_stream != nullptr ?
m_manager->download_list()->create(m_stream.get(), tracker_key, m_printLog) :
Download* download = m_stream != NULL ?
m_manager->download_list()->create(m_stream, tracker_key, m_printLog) :
m_manager->download_list()->create(m_object, tracker_key, m_printLog);
m_object = NULL;
@@ -193,8 +194,8 @@ DownloadFactory::receive_success() {
torrent::Object::list_type& commands = meta.insert_key("commands", torrent::Object::create_list()).as_list();
for (auto& m_command : m_commands)
commands.push_back(m_command);
for (command_list_type::iterator itr = m_commands.begin(); itr != m_commands.end(); ++itr)
commands.push_back(*itr);
}
if (m_session) {
@@ -241,6 +242,9 @@ 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);
@@ -254,25 +258,15 @@ 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_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 {
if (!rtorrent->has_key_string("directory"))
rpc::call_command("d.directory.set", m_variables["directory"], rpc::make_target(download));
}
else
rpc::call_command("d.directory_base.set", rtorrent->get_key("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", torrent::runtime::client_config()->is_pex_enabled(), rpc::make_target(download));
rpc::call_command("d.peer_exchange.set", rpc::call_command_value("protocol.pex"), rpc::make_target(download));
torrent::resume_load_addresses(*download->download(), resumeObject);
torrent::resume_load_file_priorities(*download->download(), resumeObject);
@@ -295,8 +289,9 @@ DownloadFactory::receive_success() {
if (torrent::log_groups[torrent::LOG_TORRENT_DEBUG].valid())
log_created(download, rtorrent);
for (const auto& command : m_commands)
rpc::parse_command_multiple_std(command, rpc::make_target(download));
std::for_each(m_commands.begin(), m_commands.end(), [&download](const std::string& cmd) {
rpc::parse_command_multiple_std(cmd, rpc::make_target(download));
});
if (m_manager->download_list()->find(infohash) == m_manager->download_list()->end())
throw torrent::input_error("The newly created download was removed.");
@@ -328,7 +323,7 @@ void
DownloadFactory::log_created(Download* download, torrent::Object* rtorrent) {
std::stringstream dump;
dump << "info_hash = " << torrent::utils::transform_to_hex_str(download->info()->hash()) << std::endl;
dump << "info_hash = " << torrent::hash_string_to_hex_str(download->info()->hash()) << std::endl;
dump << "session = " << (m_session ? "true" : "false") << std::endl;
if (download->download()->info()->is_meta_download())
@@ -341,8 +336,8 @@ DownloadFactory::log_created(Download* download, torrent::Object* rtorrent) {
dump << "---COMMANDS---" << std::endl;
for (const auto& m_command : m_commands) {
dump << m_command << std::endl;
for (command_list_type::const_iterator itr = m_commands.begin(); itr != m_commands.end(); itr++) {
dump << *itr << std::endl;
}
std::string dump_str = dump.str();
+3 -3
View File
@@ -62,9 +62,9 @@ private:
void initialize_rtorrent(Download* download, torrent::Object* rtorrent);
Manager* m_manager;
std::shared_ptr<std::iostream> m_stream;
torrent::Object* m_object{};
Manager* m_manager;
std::iostream* m_stream{};
torrent::Object* m_object{};
bool m_commited{};
bool m_loaded{};
+29 -41
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,7 +13,6 @@
#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"
@@ -23,10 +22,10 @@
#include "view.h"
#include "view_manager.h"
#include "core/dht_manager.h"
#include "core/download.h"
#include "core/download_list.h"
#include "session/session_manager.h"
#include "dht_manager.h"
#include "download.h"
#include "download_list.h"
#include "download_store.h"
#include "ui/root.h"
#define DL_TRIGGER_EVENT(download, event_name) \
@@ -69,8 +68,10 @@ DownloadList::clear() {
void
DownloadList::session_save() {
for (auto& download : *this)
session_thread::manager()->save_resume_download(download);
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.");
control->dht_manager()->save_dht_cache();
control->ui()->save_input_history();
@@ -83,13 +84,10 @@ DownloadList::find(const torrent::HashString& hash) {
DownloadList::iterator
DownloadList::find_hex(const char* hash) {
if (strlen(hash) < 40)
return end();
torrent::HashString key;
if (torrent::utils::transform_from_hex(hash, hash + 40, key) != key.end())
return end();
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));
return std::find_if(begin(), end(), [key](Download* d) { return key == d->info()->hash(); });
}
@@ -169,10 +167,8 @@ DownloadList::insert(Download* download) {
// This needs to be separated into two different calls to ensure
// the download remains in the view.
for (auto v : *control->view_manager())
v->insert(download);
for (auto v : *control->view_manager())
v->filter_download(download);
std::for_each(control->view_manager()->begin(), control->view_manager()->end(), [&download](View* v) { v->insert(download); });
std::for_each(control->view_manager()->begin(), control->view_manager()->end(), [&download](View* v) { v->filter_download(download); });
DL_TRIGGER_EVENT(*itr, "event.download.inserted");
@@ -201,12 +197,11 @@ DownloadList::erase(iterator itr) {
(*itr)->set_hash_failed(true);
close(*itr);
session_thread::manager()->remove_download(*itr);
control->core()->download_store()->remove(*itr);
DL_TRIGGER_EVENT(*itr, "event.download.erased");
for (auto v : *control->view_manager())
v->erase(*itr);
std::for_each(control->view_manager()->begin(), control->view_manager()->end(), [itr](View* v) { v->erase(*itr); });
torrent::download_remove(*(*itr)->download());
delete *itr;
@@ -331,7 +326,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() == Download::default_resume_flags)
if (download->resume_flags() == ~uint32_t())
download->set_resume_flags(flags);
// Manual or end-of-download rehashing clears the resume data so
@@ -404,7 +399,7 @@ DownloadList::resume(Download* download, int flags) {
download->set_priority(download->priority());
download->download()->start(download->resume_flags());
download->set_resume_flags(Download::default_resume_flags);
download->set_resume_flags(~uint32_t());
DL_TRIGGER_EVENT(download, "event.download.resumed");
@@ -421,7 +416,7 @@ DownloadList::pause(Download* download, int flags) {
try {
download->set_resume_flags(Download::default_resume_flags);
download->set_resume_flags(~uint32_t());
rpc::parse_command_single(rpc::make_target(download), "view.set_not_visible=active");
@@ -492,11 +487,7 @@ 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;
}
@@ -645,8 +636,10 @@ DownloadList::confirm_finished(Download* download) {
// the download.
//
// Obsolete.
if (!download->is_active() && rpc::call_command_value("session.on_completion") != 0)
session_thread::manager()->save_resume_download(download);
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);
}
// Send the completed request before resuming so we don't reset the
// up/downloaded baseline.
@@ -659,9 +652,9 @@ DownloadList::confirm_finished(Download* download) {
if (find(infohash) == end())
return;
// if (download->resume_flags() != Download::default_resume_flags)
// throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != Download::default_resume_flags.");
// if (download->resume_flags() != ~uint32_t())
// throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != ~uint32_t().");
// See #1292.
//
@@ -671,7 +664,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(Download::default_resume_flags);
download->set_resume_flags(~uint32_t());
if (!download->is_active() && rpc::call_command_value("d.state", rpc::make_target(download)) == 1)
resume(download,
@@ -687,10 +680,8 @@ 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().str();
std::string metafile = (*download->file_list()->begin())->frozen_path();
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;
@@ -698,13 +689,11 @@ 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.
@@ -715,7 +704,6 @@ 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);
}
+67
View File
@@ -0,0 +1,67 @@
// 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 <jaris@ifi.uio.no>
//
// Skomakerveien 33
// 3185 Skoppum, NORWAY
#ifndef RTORRENT_CORE_DOWNLOAD_SLOT_MAP_H
#define RTORRENT_CORE_DOWNLOAD_SLOT_MAP_H
#include <functional>
#include <map>
#include <string>
#include "download.h"
namespace core {
class DownloadSlotMap : public std::map<std::string, std::function<void (Download*)> > {
public:
typedef std::function<void (Download*)> slot_download;
typedef std::map<std::string, slot_download> Base;
void insert(const std::string& key, slot_download s) { Base::operator[](key) = s; }
void erase(const std::string& key) { Base::erase(key); }
void for_each(Download* d);
};
inline void
DownloadSlotMap::for_each(Download* d) {
for (iterator itr = begin(), last = end(); itr != last; ++itr)
itr->second(d);
}
}
#endif
+205
View File
@@ -0,0 +1,205 @@
// 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
@@ -0,0 +1,49 @@
#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
+24 -20
View File
@@ -1,41 +1,43 @@
#include "config.h"
#include "http_queue.h"
#include <memory>
#include <sstream>
#include <torrent/http.h>
#include <torrent/common.h>
#include <torrent/net/http_get.h>
#include <torrent/net/http_stack.h>
#include "http_queue.h"
#include "curl_get.h"
namespace core {
HttpQueue::iterator
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));
HttpQueue::insert(const std::string& url, std::iostream* s) {
std::unique_ptr<CurlGet> h(m_slot_factory());
itr->set_max_file_size(15 << 20);
itr->set_redirect_only_http_https();
h->set_url(url);
h->set_stream(s);
h->set_timeout(5 * 60);
for (auto& slot : m_signal_insert)
slot(*itr);
iterator signal_itr = base_type::insert(end(), h.get());
itr->add_done_slot(torrent::this_thread::thread(), std::move(done_fn));
itr->add_done_slot(torrent::this_thread::thread(), [this, itr]() { erase(itr); });
h->signal_done().push_back(std::bind(&HttpQueue::erase, this, signal_itr));
h->signal_failed().push_back(std::bind(&HttpQueue::erase, this, signal_itr));
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); });
(*signal_itr)->start();
torrent::net_thread::http_stack()->start_get(*itr);
h.release();
return itr;
for (signal_curl_get::iterator itr = m_signal_insert.begin(), last = m_signal_insert.end(); itr != last; itr++)
(*itr)(*signal_itr);
return signal_itr;
}
void
HttpQueue::erase(iterator signal_itr) {
for (const auto& slot : m_signal_erase)
slot(*signal_itr);
for (signal_curl_get::iterator itr = m_signal_erase.begin(), last = m_signal_erase.end(); itr != last; itr++)
(*itr)(*signal_itr);
signal_itr->close_and_keep_callbacks();
delete *signal_itr;
base_type::erase(signal_itr);
}
@@ -43,6 +45,8 @@ void
HttpQueue::clear() {
while (!empty())
erase(begin());
base_type::clear();
}
}
+10 -10
View File
@@ -3,20 +3,18 @@
#include <functional>
#include <iosfwd>
#include <memory>
#include <list>
#include <string>
#include <torrent/net/http_get.h>
namespace core {
// TODO: Remove this.
class CurlGet;
class HttpQueue : private std::list<torrent::net::HttpGet> {
class HttpQueue : private std::list<CurlGet*> {
public:
using base_type = std::list<torrent::net::HttpGet>;
using slot_curl_get = std::function<void (torrent::net::HttpGet)>;
using signal_curl_get = std::list<slot_curl_get>;
typedef std::list<CurlGet*> base_type;
typedef std::function<CurlGet* ()> slot_factory;
typedef std::function<void (CurlGet*)> slot_curl_get;
typedef std::list<slot_curl_get> signal_curl_get;
using base_type::iterator;
using base_type::const_iterator;
@@ -39,16 +37,18 @@ 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,
std::function<void()> done_fn, std::function<void(const std::string&)> failed_fn);
iterator insert(const std::string& url, std::iostream* s);
void erase(iterator itr);
void clear();
void set_slot_factory(slot_factory s) { m_slot_factory = s; }
signal_curl_get& signal_insert() { return m_signal_insert; }
signal_curl_get& signal_erase() { return m_signal_erase; }
private:
slot_factory m_slot_factory;
signal_curl_get m_signal_insert;
signal_curl_get m_signal_erase;
};
+204 -124
View File
@@ -1,25 +1,24 @@
#include "config.h"
#include "core/manager.h"
#include <cstdio>
#include <cstring>
#include <fstream>
#include <sstream>
#include <unistd.h>
#include <sys/select.h>
#include <fnmatch.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 <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"
@@ -27,13 +26,15 @@
#include "utils/file_status_cache.h"
#include "globals.h"
#include "curl_get.h"
#include "curl_stack.h"
#include "control.h"
#include "core/download.h"
#include "core/download_factory.h"
#include "core/http_queue.h"
#include "core/view.h"
#include <torrent/runtime/client_config.h>
#include "download.h"
#include "download_factory.h"
#include "download_store.h"
#include "http_queue.h"
#include "manager.h"
#include "view.h"
namespace core {
@@ -48,13 +49,15 @@ 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>();
m_http_stack = std::make_unique<CurlStack>();
torrent::Throttle* unthrottled = torrent::Throttle::create_throttle();
unthrottled->set_max_rate(0);
@@ -65,20 +68,6 @@ 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)
@@ -88,10 +77,10 @@ Manager::set_hashing_view(View* v) {
m_hashingView->signal_changed().push_back(std::bind(&Manager::receive_hashing_changed, this));
}
ThrottlePair
torrent::ThrottlePair
Manager::get_throttle(const std::string& name) {
auto itr = m_throttles.find(name);
auto throttles = (itr == m_throttles.end() ? ThrottlePair(nullptr, nullptr) : itr->second);
ThrottleMap::const_iterator itr = m_throttles.find(name);
torrent::ThrottlePair throttles = (itr == m_throttles.end() ? torrent::ThrottlePair(nullptr, nullptr) : itr->second);
if (throttles.first == nullptr)
throttles.first = torrent::up_throttle_global();
@@ -102,6 +91,17 @@ 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);
@@ -131,34 +131,172 @@ Manager::retrieve_throttle_value(const torrent::Object::string_type& name, bool
}
}
// Most of this should be possible to move out.
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 + '/';
Manager::initialize_second() {
torrent::Http::slot_factory() = std::bind(&CurlStack::new_object, m_http_stack.get());
m_http_queue->set_slot_factory(std::bind(&CurlStack::new_object, m_http_stack.get()));
CurlStack::global_init();
}
void
Manager::cleanup() {
m_http_stack->shutdown();
// Need to disconnect log signals? Not really since we won't receive
// any more.
m_download_list->clear();
// When we implement asynchronous DNS lookups, we need to cancel them
// here before the torrent::* objects are deleted.
torrent::cleanup();
m_http_stack.reset();
CurlStack::global_cleanup();
}
void
Manager::shutdown(bool force) {
if (!force) {
for (auto d : *m_download_list)
m_download_list->pause_default(d);
if (!force)
std::for_each(m_download_list->begin(), m_download_list->end(), [this](Download* d) { m_download_list->pause_default(d); });
else
std::for_each(m_download_list->begin(), m_download_list->end(), [this](Download* d) { m_download_list->close_quick(d); });
}
void
Manager::listen_open() {
// This stuff really should be moved outside of manager, make it
// part of the init script.
if (!rpc::call_command_value("network.port_open"))
return;
int portFirst, portLast;
torrent::Object portRange = rpc::call_command("network.port_range");
if (portRange.is_string()) {
if (std::sscanf(portRange.as_string().c_str(), "%i-%i", &portFirst, &portLast) != 2)
throw torrent::input_error("Invalid port_range argument.");
// } else if (portRange.is_list()) {
} else {
for (auto d : *m_download_list)
m_download_list->close_quick(d);
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());
}
m_http_stack->set_bind_address(!ai->address()->is_address_any() ? ai->address()->address_str() : std::string());
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;
}
}
@@ -174,10 +312,9 @@ is_data_uri(const std::string& uri) {
std::string
decode_data_uri(const std::string& uri) {
const auto pos = uri.find("base64,", 5);
if (pos == std::string::npos)
const auto start = uri.find("base64,", 5) + 7;
if (start == 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));
@@ -191,7 +328,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))
!file_status_cache()->insert(uri, 0))
return;
// Adding download.
@@ -227,8 +364,8 @@ Manager::try_create_download_from_meta_download(torrent::Object* bencode, const
torrent::Object& meta = bencode->get_key("rtorrent_meta_download");
torrent::Object::list_type& commands = meta.get_key_list("commands");
for (const auto& command : commands)
f->commands().insert(f->commands().end(), command.as_string());
for (torrent::Object::list_type::const_iterator itr = commands.begin(); itr != commands.end(); ++itr)
f->commands().insert(f->commands().end(), itr->as_string());
f->set_start(meta.get_key_value("start"));
f->set_print_log(meta.get_key_value("print_log"));
@@ -248,70 +385,14 @@ 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;
auto first = split_iterator(pattern, '/');
auto last = split_iterator(pattern);
rak::split_iterator_t<std::string> first = rak::split_iterator(pattern, '/');
rak::split_iterator_t<std::string> last = rak::split_iterator(pattern);
if (first == last)
return;
@@ -320,7 +401,7 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
if ((*first).empty()) {
currentCache.push_back(utils::Directory("/"));
++first;
} else if (torrent::utils::trim_spaces_str(*first) == "~") {
} else if (rak::trim(*first) == "~") {
currentCache.push_back(utils::Directory("~"));
++first;
} else {
@@ -330,29 +411,29 @@ 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) {
const std::string& pattern = *first;
rak::regex r(*first);
if (pattern.empty())
if (r.pattern().empty())
continue;
// Special case for ".."?
for (auto& itr : currentCache) {
for (std::vector<utils::Directory>::iterator itr = currentCache.begin(); itr != currentCache.end(); ++itr) {
// Only include filenames starting with '.' if the pattern
// starts with the same.
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());
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());
for (const auto& cache : itr)
nextCache.push_back(path_expand_transform(itr.path() + (itr.path() == "/" ? "" : "/"), cache));
std::transform(itr->begin(), itr->end(), std::back_inserter(nextCache), [itr](const utils::directory_entry& entry) {
return path_expand_transform(itr->path() + (itr->path() == "/" ? "" : "/"), entry);
});
}
currentCache.clear();
currentCache.swap(nextCache);
}
for (const auto& cache : currentCache)
paths->push_back(cache.path());
std::transform(currentCache.begin(), currentCache.end(), std::back_inserter(*paths), std::mem_fn(&utils::Directory::path));
}
bool
@@ -373,8 +454,8 @@ Manager::try_create_download_expand(const std::string& uri, int flags, command_l
path_expand(&paths, uri);
if (!paths.empty())
for (auto& path : paths)
try_create_download(path, flags, commands);
for (std::vector<std::string>::iterator itr = paths.begin(); itr != paths.end(); ++itr)
try_create_download(*itr, flags, commands);
else
try_create_download(uri, flags, commands);
@@ -385,8 +466,8 @@ Manager::try_create_download_expand(const std::string& uri, int flags, command_l
// hashing view and starts hashing if nessesary.
void
Manager::receive_hashing_changed() {
bool foundHashing = std::any_of(m_hashingView->begin_visible(), m_hashingView->end_visible(),
std::mem_fn(&Download::is_hash_checking));
bool foundHashing = std::find_if(m_hashingView->begin_visible(), m_hashingView->end_visible(),
std::mem_fn(&Download::is_hash_checking)) != m_hashingView->end_visible();
// Try quick hashing all those with hashing == initial, set them to
// something else when failed.
@@ -436,7 +517,6 @@ 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());
}
}
+35 -18
View File
@@ -5,6 +5,7 @@
#include <memory>
#include <vector>
#include <torrent/utils/log_buffer.h>
#include <torrent/connection_manager.h>
#include <torrent/object.h>
#include "download_list.h"
@@ -20,10 +21,11 @@ class FileStatusCache;
namespace core {
class CurlStack;
class DownloadStore;
class HttpQueue;
using ThrottlePair = std::pair<torrent::Throttle*, torrent::Throttle*>;
using ThrottleMap = std::map<std::string, ThrottlePair>;
typedef std::map<std::string, torrent::ThrottlePair> ThrottleMap;
class View;
@@ -35,28 +37,42 @@ public:
Manager();
~Manager();
bool is_download_shutdown_completed();
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(); }
DownloadList* download_list() { return m_download_list.get(); }
FileStatusCache* file_status_cache() { return m_file_status_cache.get(); }
HttpQueue* http_queue() { return m_http_queue.get(); }
CurlStack* http_stack() { return m_http_stack.get(); }
HttpQueue* http_queue() { return m_http_queue.get(); }
View* hashing_view() { return m_hashingView; }
View* hashing_view() { return m_hashingView; }
void set_hashing_view(View* v);
auto* log_important() { return m_log_important.get(); }
auto* log_complete() { return m_log_complete.get(); }
torrent::log_buffer* log_important() { return m_log_important.get(); }
torrent::log_buffer* log_complete() { return m_log_complete.get(); }
ThrottleMap& throttles() { return m_throttles; }
ThrottlePair get_throttle(const std::string& name);
ThrottleMap& throttles() { return m_throttles; }
torrent::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);
// Really should find a more descriptive name.
void initialize_second();
void cleanup();
const std::string& magnet_path();
void set_magnet_path(const std::string& path);
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);
void shutdown(bool force);
@@ -79,6 +95,8 @@ 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);
@@ -88,24 +106,23 @@ 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;
std::unique_ptr<CurlStack> m_http_stack;
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
+8 -7
View File
@@ -16,7 +16,7 @@
namespace core {
// Also add focus thingie here?
struct view_downloads_compare {
struct view_downloads_compare : std::function<bool (Download*, Download*)> {
view_downloads_compare(const torrent::Object& cmd) :
m_command(cmd) {}
@@ -50,7 +50,7 @@ struct view_downloads_compare {
const torrent::Object& m_command;
};
struct view_downloads_filter {
struct view_downloads_filter : std::function<bool (Download*)> {
view_downloads_filter(const torrent::Object& cmd, const torrent::Object& cmd2) :
m_command(cmd), m_command2(cmd2) {}
@@ -118,8 +118,8 @@ View::emit_changed() {
void
View::emit_changed_now() {
for (auto& itr : m_signal_changed)
itr();
for (signal_void::iterator itr = m_signal_changed.begin(), last = m_signal_changed.end(); itr != last; itr++)
(*itr)();
}
View::~View() {
@@ -138,11 +138,12 @@ View::initialize(const std::string& name) {
if (name.empty())
throw torrent::internal_error("View::initialize(...) called with an empty name.");
core::DownloadList* dlist = control->core()->download_list();
m_name = name;
// Urgh, wrong. No filtering being done.
for (const auto& d : *control->core()->download_list())
push_back(d);
std::for_each(dlist->begin(), dlist->end(), [&](Download* d) { push_back(d); });
m_size = base_type::size();
m_focus = 0;
@@ -346,7 +347,7 @@ View::clear_filter_on() {
inline void
View::insert_visible(Download* d) {
auto itr = std::find_if(begin_visible(), end_visible(), [this, d](auto d2) { return view_downloads_compare(m_sortNew)(d, d2); });
iterator itr = std::find_if(begin_visible(), end_visible(), [&d, this](Download* d2) { return view_downloads_compare(m_sortNew)(d, d2); });
m_size++;
m_focus += (m_focus >= position(itr));
+4 -6
View File
@@ -18,8 +18,7 @@ namespace core {
void
ViewManager::clear() {
for (auto v : *this)
delete v;
std::for_each(begin(), end(), [](View* v) { delete v; });
base_type::clear();
}
@@ -35,8 +34,7 @@ ViewManager::insert(const std::string& name) {
View* view = new View();
view->initialize(name);
base_type::push_back(view);
return --end();
return base_type::insert(end(), view);
}
ViewManager::iterator
@@ -90,8 +88,8 @@ ViewManager::set_filter_on(const std::string& name, const filter_args& args) {
// TODO: Ensure the filter keys are rlookup.
for (const auto& arg : args)
(*viewItr)->set_filter_on_event(arg);
for (filter_args::const_iterator itr = args.begin(); itr != args.end(); ++itr)
(*viewItr)->set_filter_on_event(*itr);
}
}
+4 -4
View File
@@ -2,16 +2,16 @@
#define RTORRENT_CORE_VIEW_MANAGER_H
#include <string>
#include <torrent/utils/unordered_vector.h>
#include <rak/unordered_vector.h>
#include "view.h"
namespace core {
class ViewManager : public torrent::utils::unordered_vector<View*> {
class ViewManager : public rak::unordered_vector<View*> {
public:
typedef torrent::utils::unordered_vector<View*> base_type;
typedef std::list<std::string> filter_args;
typedef rak::unordered_vector<View*> base_type;
typedef std::list<std::string> filter_args;
using base_type::iterator;
using base_type::const_iterator;
+1 -3
View File
@@ -4,9 +4,7 @@
#include <string>
#include <vector>
#if defined(HAVE_NO_NCURSES)
#include "curses_stub.h"
#elif defined(HAVE_NCURSESW_CURSES_H)
#if defined(HAVE_NCURSESW_CURSES_H)
#include <ncursesw/curses.h>
#elif defined(HAVE_NCURSESW_H)
#include <ncursesw.h>
+30 -38
View File
@@ -11,31 +11,23 @@
namespace display {
bool Canvas::m_initialized{};
bool Canvas::m_daemon{};
bool Canvas::m_isInitialized = false;
bool Canvas::m_isDaemon = false;
// 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_daemon) {
if (!m_isDaemon) {
m_window = newwin(height, width, y, x);
if (m_window == nullptr)
if (m_window == NULL)
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_daemon) {
if (!m_isDaemon) {
wresize(m_window, h, w);
mvwin(m_window, y, x);
}
@@ -43,7 +35,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_daemon) {
if (!m_isDaemon) {
move(x, y);
attr_t org_attr;
@@ -76,13 +68,14 @@ Canvas::print_attributes(unsigned int x, unsigned int y, const char* first, cons
void
Canvas::initialize() {
if (m_initialized)
throw torrent::internal_error("Canvas::initialize() called more than once.");
if (m_isInitialized)
return;
m_daemon = rpc::call_command_value("system.daemon");
m_initialized = true;
m_isDaemon = rpc::call_command_value("system.daemon");
if (!m_daemon) {
m_isInitialized = true;
if (!m_isDaemon) {
initscr();
start_color();
use_default_colors();
@@ -95,19 +88,6 @@ 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() {
@@ -122,7 +102,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_initialized || m_daemon)
if (!m_isInitialized || m_isDaemon)
return;
// basic color names, index maps to ncurses COLOR_*
@@ -135,9 +115,7 @@ 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
@@ -148,7 +126,6 @@ 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);
@@ -192,8 +169,10 @@ 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 " + std::to_string(get_colors()) + " colors.");
throw torrent::input_error(color_def + ": your terminal only supports " + buf + " colors.");
}
m_attr_map[k] = attr; // overwrite or insert the value
@@ -217,11 +196,24 @@ 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_daemon) {
if (!m_isDaemon) {
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
return std::pair<int, int>(ws.ws_col, ws.ws_row);
}
+43 -36
View File
@@ -1,7 +1,6 @@
#ifndef RTORRENT_DISPLAY_CANVAS_H
#define RTORRENT_DISPLAY_CANVAS_H
#include <cstdarg>
#include <string>
#include <unordered_map>
#include <vector>
@@ -54,9 +53,8 @@ public:
// Initialize stdscr.
static void initialize();
static void cleanup();
static void build_colors();
static void cleanup();
static int get_screen_width();
static int get_screen_height();
@@ -65,15 +63,15 @@ public:
static void do_update();
static bool daemon() { return m_daemon; }
static bool daemon() { return m_isDaemon; }
static const attributes_map& attr_map() { return m_attr_map; }
private:
Canvas(const Canvas&);
void operator=(const Canvas&);
static bool m_initialized;
static bool m_daemon;
static bool m_isInitialized;
static bool m_isDaemon;
// Maps ncurses color IDs to a ncurses attribute int
static std::unordered_map<int, int> m_attr_map;
@@ -81,59 +79,66 @@ private:
WINDOW* m_window;
};
inline Canvas::~Canvas() {
if (!m_isDaemon) {
delwin(m_window);
}
}
inline void
Canvas::refresh() {
if (!m_daemon) {
if (!m_isDaemon) {
wnoutrefresh(m_window);
}
}
inline void
Canvas::refresh_std() {
if (!m_daemon) {
if (!m_isDaemon) {
wnoutrefresh(stdscr);
}
}
inline void
Canvas::redraw() {
if (!m_daemon) {
if (!m_isDaemon) {
redrawwin(m_window);
}
}
inline void
Canvas::redraw_std() {
if (!m_daemon) {
if (!m_isDaemon) {
redrawwin(stdscr);
}
}
inline void
Canvas::resize(int w, int h) {
if (!m_daemon) {
if (!m_isDaemon) {
wresize(m_window, h, w);
}
}
inline void
Canvas::resize_term(int x, int y) {
if (!m_daemon) {
if (!m_isDaemon) {
resizeterm(y, x);
}
}
inline void
Canvas::resize_term(std::pair<int, int> dim) {
if (!m_daemon) {
if (!m_isDaemon) {
resizeterm(dim.second, dim.first);
}
}
inline unsigned int
Canvas::get_x() {
[[maybe_unused]] int x, y;
if (!m_daemon) {
int x;
[[maybe_unused]] int y;
if (!m_isDaemon) {
getyx(m_window, y, x);
} else {
x = 1;
@@ -143,8 +148,8 @@ Canvas::get_x() {
inline unsigned int
Canvas::get_y() {
[[maybe_unused]] int x, y;
if (!m_daemon) {
int x, y;
if (!m_isDaemon) {
getyx(m_window, y, x);
} else {
y = 1;
@@ -154,8 +159,9 @@ Canvas::get_y() {
inline unsigned int
Canvas::width() {
[[maybe_unused]] int x, y;
if (!m_daemon) {
int x;
[[maybe_unused]] int y;
if (!m_isDaemon) {
getmaxyx(m_window, y, x);
} else {
x = 80;
@@ -165,8 +171,8 @@ Canvas::width() {
inline unsigned int
Canvas::height() {
[[maybe_unused]] int x, y;
if (!m_daemon) {
int x, y;
if (!m_isDaemon) {
getmaxyx(m_window, y, x);
} else {
y = 24;
@@ -176,21 +182,21 @@ Canvas::height() {
inline void
Canvas::move(unsigned int x, unsigned int y) {
if (!m_daemon) {
if (!m_isDaemon) {
wmove(m_window, y, x);
}
}
inline void
Canvas::erase() {
if (!m_daemon) {
if (!m_isDaemon) {
werase(m_window);
}
}
inline void
Canvas::erase_std() {
if (!m_daemon) {
if (!m_isDaemon) {
werase(stdscr);
}
}
@@ -199,7 +205,7 @@ inline void
Canvas::print(const char* str, ...) {
va_list arglist;
if (!m_daemon) {
if (!m_isDaemon) {
va_start(arglist, str);
vw_printw(m_window, const_cast<char*>(str), arglist);
va_end(arglist);
@@ -210,7 +216,7 @@ inline void
Canvas::print(unsigned int x, unsigned int y, const char* str, ...) {
va_list arglist;
if (!m_daemon) {
if (!m_isDaemon) {
va_start(arglist, str);
wmove(m_window, y, x);
vw_printw(m_window, const_cast<char*>(str), arglist);
@@ -220,43 +226,44 @@ Canvas::print(unsigned int x, unsigned int y, const char* str, ...) {
inline void
Canvas::print_char(const chtype ch) {
if (!m_daemon) {
if (!m_isDaemon) {
waddch(m_window, ch);
}
}
inline void
Canvas::print_char(unsigned int x, unsigned int y, const chtype ch) {
if (!m_daemon) {
if (!m_isDaemon) {
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_daemon) {
if (!m_isDaemon) {
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_daemon) {
if (!m_isDaemon) {
mvwchgat(m_window, y, x, n, m_attr_map[k], k, NULL);
}
}
inline void
Canvas::set_default_attributes(int attr) {
if (!m_daemon) {
if (!m_isDaemon) {
(void)wattrset(m_window, attr);
}
}
inline int
Canvas::get_screen_width() {
[[maybe_unused]] int x, y;
if (!m_daemon) {
int x;
[[maybe_unused]] int y;
if (!m_isDaemon) {
getmaxyx(stdscr, y, x);
} else {
x = 80;
@@ -266,8 +273,8 @@ Canvas::get_screen_width() {
inline int
Canvas::get_screen_height() {
[[maybe_unused]] int x, y;
if (!m_daemon) {
int x, y;
if (!m_isDaemon) {
getmaxyx(stdscr, y, x);
} else {
y = 24;
@@ -277,7 +284,7 @@ Canvas::get_screen_height() {
inline void
Canvas::do_update() {
if (!m_daemon) {
if (!m_isDaemon) {
doupdate();
}
}
-4
View File
@@ -4,11 +4,7 @@
#include <array>
#include <map>
#if defined(HAVE_NO_NCURSES)
#include "curses_stub.h"
#else
#include <curses.h>
#endif
namespace display {
-6
View File
@@ -1,6 +0,0 @@
// 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
@@ -1,108 +0,0 @@
// 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
+51 -16
View File
@@ -1,12 +1,47 @@
#include "config.h"
// 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 "display/frame.h"
#include "config.h"
#include <algorithm>
#include <functional>
#include <rak/algorithm.h>
#include <torrent/exceptions.h>
#include "display/window.h"
#include "frame.h"
#include "window.h"
namespace display {
@@ -102,7 +137,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;
@@ -186,7 +221,7 @@ Frame::clear() {
m_window->set_offscreen(true);
break;
case TYPE_ROW:
case TYPE_COLUMN:
for (size_type i = 0; i < m_containerSize; ++i) {
@@ -300,10 +335,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);
@@ -332,11 +367,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;
@@ -361,7 +396,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 -= std::min(height, (*itr)->m_height);
height -= (*itr)->m_height;
}
}
@@ -375,10 +410,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);
@@ -388,7 +423,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
@@ -407,11 +442,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;
@@ -436,7 +471,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 -= std::min(width, (*itr)->m_width);
width -= (*itr)->m_width;
}
}
+35 -2
View File
@@ -1,8 +1,41 @@
// 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 {
@@ -88,7 +121,7 @@ private:
union {
Window* m_window;
struct {
size_type m_containerSize;
Frame* m_container[max_size];
+2 -7
View File
@@ -55,7 +55,6 @@ Manager::receive_update() {
m_force_redraw = false;
display::Canvas::resize_term(display::Canvas::term_size());
keypad(stdscr, TRUE);
Canvas::redraw_std();
adjust_layout();
@@ -80,14 +79,10 @@ Manager::schedule_update(std::chrono::microseconds min_interval) {
return;
}
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)
if (m_task_update.is_scheduled() && m_task_update.time() <= m_scheduler.front()->time())
return;
torrent::this_thread::scheduler()->update_wait_until(&m_task_update, std::max(next, m_time_last_update + min_interval));
torrent::this_thread::scheduler()->update_wait_until(&m_task_update, std::max(m_scheduler.front()->time(), m_time_last_update + min_interval));
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
#include <torrent/utils/scheduler.h>
#include "display/frame.h"
#include "frame.h"
namespace display {
+7 -8
View File
@@ -43,8 +43,7 @@ namespace display {
void
TextElementList::clear() {
for (auto t : *this)
delete t;
std::for_each(begin(), end(), [](TextElement* t) { delete t; });
base_type::clear();
}
@@ -54,14 +53,14 @@ TextElementList::print(char* first, char* last, Canvas::attributes_list* attribu
// Call print for each element even if first == last so that any
// attributes gets added to the list.
for (auto& itr : *this)
for (iterator itr = begin(); itr != end(); ++itr)
if (column-- > 0) {
char* columnEnd = std::min(last, first + *m_columnWidth);
if (columnEnd < first || columnEnd > last)
throw torrent::internal_error("TextElementList::print(...) columnEnd < first || columnEnd > last.");
first = itr->print(first, columnEnd, attributes, target);
first = (*itr)->print(first, columnEnd, attributes, target);
if (first > columnEnd)
throw torrent::internal_error("TextElementList::print(...) first > columnEnd.");
@@ -70,7 +69,7 @@ TextElementList::print(char* first, char* last, Canvas::attributes_list* attribu
first = columnEnd;
} else {
first = itr->print(first, last, attributes, target);
first = (*itr)->print(first, last, attributes, target);
}
return first;
@@ -81,15 +80,15 @@ TextElementList::max_length() {
extent_type length = 0;
int column = m_columnWidth != NULL ? m_column : 0;
for (auto& itr : *this) {
extent_type l = column-- > 0 ? std::min(itr->max_length(), *m_columnWidth) : itr->max_length();
for (iterator itr = begin(); itr != end(); ++itr) {
extent_type l = column-- > 0 ? std::min((*itr)->max_length(), *m_columnWidth) : (*itr)->max_length();
if (l == extent_full)
return extent_full;
length += l;
}
return length;
}
+50 -17
View File
@@ -1,10 +1,43 @@
// 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/text_element_string.h"
#include <torrent/utils/string_manip.h>
#include <rak/string_manip.h>
#include "rpc/parse_commands.h"
#include "text_element_string.h"
namespace display {
@@ -17,20 +50,20 @@ TextElementStringBase::print(char* first, char* last, Canvas::attributes_list* a
return first;
if (m_flags & flag_escape_hex) {
auto buffer = std::make_unique<char[]>(last - first);
char* bufferLast = copy_string(buffer.get(), buffer.get() + (last - first), target);
char buffer[last - first];
char* bufferLast = copy_string(buffer, buffer + (last - first), target);
first = torrent::utils::transform_to_hex(buffer.get(), bufferLast, first, last);
first = rak::transform_hex(buffer, bufferLast, first, last);
} else if (m_flags & flag_escape_html) {
auto buffer = std::make_unique<char[]>(last - first);
char* bufferLast = copy_string(buffer.get(), buffer.get() + (last - first), target);
char buffer[last - first];
char* bufferLast = copy_string(buffer, buffer + (last - first), target);
first = torrent::utils::copy_escape_html(buffer.get(), bufferLast, first, last);
first = rak::copy_escape_html(buffer, bufferLast, first, last);
} else {
first = copy_string(first, last, target);
}
}
push_attribute(attributes, Attributes(first, baseAttribute));
@@ -38,16 +71,16 @@ TextElementStringBase::print(char* first, char* last, Canvas::attributes_list* a
}
char*
TextElementString::copy_string(char* first, char* last, [[maybe_unused]] rpc::target_type target) {
TextElementString::copy_string(char* first, char* last, 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, [[maybe_unused]] rpc::target_type target) {
TextElementCString::copy_string(char* first, char* last, rpc::target_type target) {
extent_type length = std::min<extent_type>(last - first, m_length);
std::memcpy(first, m_string, length);
@@ -71,22 +104,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 = torrent::utils::transform_to_hex(str.c_str(), str.c_str() + str.size(), first, last);
first = rak::transform_hex(str.c_str(), str.c_str() + str.size(), first, last);
} else if (m_flags & flag_escape_html) {
first = torrent::utils::copy_escape_html(str.c_str(), str.c_str() + str.size(), first, last);
first = rak::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,3 +1,37 @@
// 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
-2
View File
@@ -4,8 +4,6 @@
#include <ctime>
#include <torrent/utils/chrono.h>
#include "globals.h"
namespace display {
+42 -50
View File
@@ -1,12 +1,12 @@
#include "config.h"
#include "display/utils.h"
#include <cstring>
#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>
@@ -14,24 +14,22 @@
#include <torrent/data/file_list.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/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"
#include "core/curl_stack.h"
#include "core/download.h"
#include "core/manager.h"
#include "rpc/parse_commands.h"
#include "ui/root.h"
#include "control.h"
#include "globals.h"
#include "utils.h"
namespace display {
char*
print_string(char* first, char* last, const char* str) {
print_string(char* first, char* last, char* str) {
if (first == last)
return first;
@@ -80,9 +78,17 @@ 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_string(first, last, torrent::sa_addr_str(sa).c_str());
return print_address(first, last, rak::socket_address::cast_from(sa));
}
char*
@@ -105,7 +111,7 @@ print_download_info_full(char* first, char* last, core::Download* d) {
first = print_buffer(first, last, "%6.1f / %6.1f MB",
(double)d->download()->bytes_done() / (double)(1 << 20),
(double)d->download()->file_list()->size_bytes() / (double)(1 << 20));
first = print_buffer(first, last, " Rate: %5.1f / %5.1f KB Uploaded: %7.1f MB",
(double)d->info()->up_rate()->rate() / (1 << 10),
(double)d->info()->down_rate()->rate() / (1 << 10),
@@ -151,11 +157,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()->file_list()->size_chunks() != 0 ? (d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks() : 0);
(d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks());
} else if (d->tracker_controller().has_active_trackers_not_scrape()) {
auto tracker = d->tracker_controller().find_if([](const auto& t) {
return t.is_requesting_not_scrape();
return t.is_busy_not_scrape();
});
if (tracker.is_valid()) {
@@ -209,7 +215,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()->size_chunks() != 0 ? (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks() : 0);
first = print_buffer(first, last, " %2u%% ",(d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks());
else
first = print_buffer(first, last, " ");
@@ -261,7 +267,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()->size_chunks() != 0 ? (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks() : 0);
return print_buffer(first, last, "[%2u%%]", (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks());
}
char*
@@ -289,14 +295,15 @@ print_status_throttle_limit(char* first, char* last, bool up, const ui::Throttle
char* firstc = throttle_str;
char* lastc = throttle_str + 40 - 1;
for (const auto& throttle_name : throttle_names) {
for (ui::ThrottleNameList::const_iterator itr = throttle_names.begin(), laste = throttle_names.end(); itr != laste; itr++) {
if (!throttle_name.empty()) {
int64_t throttle_max = control->core()->retrieve_throttle_value(throttle_name, false, up);
if (!(*itr).empty()) {
int64_t throttle_max = control->core()->retrieve_throttle_value(*itr, false, up);
if (throttle_max > 0)
firstc = print_buffer(firstc, lastc, "|%1.0f", (double)throttle_max / 1024.0);
}
}
// Add temp buffer (chop first char first) into main buffer if temp buffer isn't empty
@@ -314,10 +321,10 @@ print_status_throttle_rate(char* first, char* last, bool up, const ui::ThrottleN
char* firstc = throttle_str;
char* lastc = throttle_str + 50 - 1;
for (const auto& throttle_name : throttle_names) {
for (ui::ThrottleNameList::const_iterator itr = throttle_names.begin(), laste = throttle_names.end(); itr != laste; itr++) {
if (!throttle_name.empty() && (up ? torrent::up_throttle_global()->is_throttled() : torrent::down_throttle_global()->is_throttled())) {
int64_t throttle_rate_value = control->core()->retrieve_throttle_value(throttle_name, true, up);
if (!(*itr).empty() && (up ? torrent::up_throttle_global()->is_throttled() : torrent::down_throttle_global()->is_throttled())) {
int64_t throttle_rate_value = control->core()->retrieve_throttle_value(*itr, true, up);
if (throttle_rate_value > -1) {
double throttle_rate = (double)throttle_rate_value / 1024.0;
@@ -326,6 +333,7 @@ print_status_throttle_rate(char* first, char* last, bool up, const ui::ThrottleN
firstc = print_buffer(firstc, lastc, "|%3.1f", throttle_rate);
}
}
}
// Add temp buffer into main buffer if temp buffer isn't empty
@@ -375,37 +383,21 @@ 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());
auto local_address = torrent::runtime::network_config()->local_address_best_match();
first = print_buffer(first, last, " [Port: %i]", (unsigned int)torrent::connection_manager()->listen_port());
if (!torrent::sa_is_any(local_address.get())) {
if (!rak::socket_address::cast_from(torrent::connection_manager()->local_address())->is_address_any()) {
first = print_buffer(first, last, " [Local ");
first = print_address(first, last, local_address.get());
first = print_address(first, last, torrent::connection_manager()->local_address());
first = print_buffer(first, last, "]");
}
if (first > last)
throw torrent::internal_error("print_status_info(...) wrote past end of the buffer.");
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) {
if (!rak::socket_address::cast_from(torrent::connection_manager()->bind_address())->is_address_any()) {
first = print_buffer(first, last, " [Bind ");
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_address(first, last, torrent::connection_manager()->bind_address());
first = print_buffer(first, last, "]");
}
@@ -423,14 +415,14 @@ 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()->size(),
torrent::net_thread::http_stack()->max_total_connections());
control->core()->http_stack()->active(),
control->core()->http_stack()->max_active());
first = print_buffer(first, last, " [S %i/%i/%i]",
torrent::runtime::total_handshakes(),
torrent::runtime::socket_manager()->size(),
torrent::runtime::socket_manager()->max_size());
torrent::total_handshakes(),
torrent::connection_manager()->size(),
torrent::connection_manager()->max_size());
first = print_buffer(first, last, " [F %i/%i]",
torrent::file_manager()->open_files(),
torrent::file_manager()->max_open_files());
+36 -1
View File
@@ -1,10 +1,45 @@
// 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 <jaris@ifi.uio.no>
//
// Skomakerveien 33
// 3185 Skoppum, NORWAY
#ifndef RTORRENT_DISPLAY_UTILS_H
#define RTORRENT_DISPLAY_UTILS_H
#include <ctime>
#include <cstdio>
#include <string>
#include <vector>
namespace core {
class Download;
+2 -2
View File
@@ -4,8 +4,6 @@
#include <stdexcept>
#include <torrent/utils/chrono.h>
namespace display {
Window::SlotTimer Window::m_slot_schedule;
@@ -31,6 +29,8 @@ 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
+2 -59
View File
@@ -3,7 +3,7 @@
#include <functional>
#include <torrent/utils/scheduler.h>
#include <torrent/system/thread.h>
#include <torrent/utils/thread.h>
#include "canvas.h"
#include "globals.h"
@@ -89,7 +89,7 @@ protected:
static SlotWindow m_slot_unschedule;
static Slot m_slot_adjust;
std::unique_ptr<Canvas> m_canvas;
Canvas* m_canvas;
int m_flags;
@@ -102,63 +102,6 @@ protected:
torrent::utils::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
+6 -5
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) :
@@ -55,8 +55,9 @@ WindowDownloadChunksSeen::redraw() {
const torrent::Bitfield* bitfield = m_download->download()->file_list()->bitfield();
const torrent::TransferList* transfers = m_download->download()->transfer_list();
std::vector<torrent::BlockList*> transferChunks(transfers->begin(), transfers->end());
std::vector<torrent::BlockList*> transferChunks(transfers->size(), 0);
std::copy(transfers->begin(), transfers->end(), transferChunks.begin());
std::sort(transferChunks.begin(), transferChunks.end());
std::vector<torrent::BlockList*>::const_iterator itrTransfer = transferChunks.begin();
@@ -82,7 +83,7 @@ WindowDownloadChunksSeen::redraw() {
attr = A_BOLD;
}
m_canvas->print_char(attr | torrent::utils::value_to_hex0(std::min<uint8_t>(*chunk, 0xF)));
m_canvas->print_char(attr | rak::value_to_hexchar<0>(std::min<uint8_t>(*chunk, 0xF)));
chunk++;
if ((chunk - seen) % 10 == 0) {
+10 -8
View File
@@ -1,15 +1,17 @@
#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() :
@@ -102,10 +104,10 @@ WindowDownloadList::redraw() {
typedef std::pair<core::View::iterator, core::View::iterator> Range;
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));
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));
// 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
+9 -8
View File
@@ -2,6 +2,7 @@
#include <cmath>
#include <stdexcept>
#include <rak/string_manip.h>
#include <torrent/data/block_list.h>
#include <torrent/data/transfer_list.h>
@@ -39,7 +40,7 @@ WindowDownloadTransferList::redraw() {
m_canvas->print(0, y, "%5u [P: %u F: %u]", (*itr)->index(), (*itr)->priority(), (*itr)->failed());
// Handle window size.
for (const auto& bItr : **itr) {
for (torrent::BlockList::const_iterator bItr = (*itr)->begin(), bLast = (*itr)->end(); bItr != bLast; ++bItr) {
if (m_canvas->get_x() >= m_canvas->width() - 1) {
if (++y >= m_canvas->height())
break;
@@ -50,22 +51,22 @@ WindowDownloadTransferList::redraw() {
char id;
chtype attr = A_NORMAL;
if (bItr.is_finished()) {
if (bItr->is_finished()) {
attr = A_REVERSE;
id = key_id(bItr.leader()->const_peer_info());
id = key_id(bItr->leader()->const_peer_info());
} else if (bItr.is_transfering()) {
} else if (bItr->is_transfering()) {
attr = A_BOLD;
id = key_id(bItr.leader()->const_peer_info());
id = key_id(bItr->leader()->const_peer_info());
} else if (bItr.queued()->size() >= 1) {
id = std::tolower(key_id(bItr.queued()->back()->const_peer_info()));
} else if (bItr->queued()->size() >= 1) {
id = std::tolower(key_id(bItr->queued()->back()->const_peer_info()));
} else {
id = '.';
}
if (bItr.size_all() > 1)
if (bItr->size_all() > 1)
attr |= A_UNDERLINE;
m_canvas->print_char(attr | id);
+4 -11
View File
@@ -135,11 +135,8 @@ WindowFileList::redraw() {
m_canvas->print(0, pos, "%*c%-*s", 16, ' ', filenameWidth, "EMPTY");
} else if (itr.is_entering()) {
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");
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");
} else if (itr.is_leaving()) {
m_canvas->print(0, pos, "%*c %-*s", 16 + (itr.depth() - 1), '/', filenameWidth - (itr.depth() - 1), "");
@@ -169,12 +166,8 @@ WindowFileList::redraw() {
else
m_canvas->print(8, pos, "%5.1f T", (double)val / (int64_t(1) << 40));
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");
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");
} else {
m_canvas->print(0, pos, "BORK BORK");
+15 -18
View File
@@ -3,8 +3,8 @@
#include "display/window_http_queue.h"
#include <stdexcept>
#include <torrent/net/http_get.h>
#include "core/curl_get.h"
#include "core/http_queue.h"
#include "display/canvas.h"
@@ -17,9 +17,8 @@ WindowHttpQueue::WindowHttpQueue(core::HttpQueue* q) :
m_queue(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_erase().end(), [this](auto h) { receive_erase(h); });
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_task_deactivate.slot() = [this] {
if (!m_container.empty())
@@ -56,14 +55,14 @@ WindowHttpQueue::redraw() {
Container::iterator itr = m_container.begin();
while (itr != m_container.end() && pos + 10 < m_canvas->width()) {
if (!itr->m_http.is_valid())
if (itr->m_http == NULL)
m_canvas->print(pos, 0, "[%s done]", itr->m_name.c_str());
else if (itr->m_http.size_total() == 0)
else if (itr->m_http->size_total() == 0)
m_canvas->print(pos, 0, "[%s ---%%]", itr->m_name.c_str());
else
m_canvas->print(pos, 0, "[%s %3i%%]", itr->m_name.c_str(), (int)(100.0 * itr->m_http.size_done() / itr->m_http.size_total()));
m_canvas->print(pos, 0, "[%s %3i%%]", itr->m_name.c_str(), (int)(100.0 * itr->m_http->size_done() / itr->m_http->size_total()));
pos += itr->m_name.size() + 6;
++itr;
@@ -73,7 +72,7 @@ WindowHttpQueue::redraw() {
void
WindowHttpQueue::cleanup_list() {
for (Container::iterator itr = m_container.begin(); itr != m_container.end(); ) {
if (!itr->m_http.is_valid() && itr->m_timer < torrent::this_thread::cached_time()) {
if (itr->m_http == nullptr && itr->m_timer < torrent::this_thread::cached_time()) {
itr = m_container.erase(itr);
continue;
}
@@ -83,10 +82,10 @@ WindowHttpQueue::cleanup_list() {
}
std::string
WindowHttpQueue::create_name(torrent::net::HttpGet http_get) {
size_t p = http_get.url().rfind('/', http_get.url().size() - std::min<int>(10, http_get.url().size()));
WindowHttpQueue::create_name(core::CurlGet* h) {
size_t p = h->url().rfind('/', h->url().size() - std::min<int>(10, h->url().size()));
std::string n = p != std::string::npos ? http_get.url().substr(p) : http_get.url();
std::string n = p != std::string::npos ? h->url().substr(p) : h->url();
if (n.empty())
throw std::logic_error("WindowHttpQueue::create_name(...) made a bad string");
@@ -106,8 +105,8 @@ WindowHttpQueue::create_name(torrent::net::HttpGet http_get) {
}
void
WindowHttpQueue::receive_insert(torrent::net::HttpGet http_get) {
m_container.push_back(Node(http_get, create_name(http_get)));
WindowHttpQueue::receive_insert(core::CurlGet* h) {
m_container.push_back(Node(h, create_name(h)));
if (!is_active()) {
set_active(true);
@@ -118,15 +117,13 @@ WindowHttpQueue::receive_insert(torrent::net::HttpGet http_get) {
}
void
WindowHttpQueue::receive_erase(torrent::net::HttpGet http_get) {
auto itr = std::find_if(m_container.begin(),
m_container.end(),
[http_get](auto& n) { return http_get == n.m_http; });
WindowHttpQueue::receive_erase(core::CurlGet* h) {
Container::iterator itr = std::find_if(m_container.begin(), m_container.end(), [h](Node& n) { return h == n.m_http; });
if (itr == m_container.end())
throw std::logic_error("WindowHttpQueue::receive_erase(...) tried to remove an object we don't have");
itr->m_http = torrent::net::HttpGet();
itr->m_http = nullptr;
itr->m_timer = torrent::this_thread::cached_time() + 4s;
mark_dirty();
+9 -9
View File
@@ -3,20 +3,20 @@
#include <functional>
#include <list>
#include <torrent/net/http_get.h>
#include "window.h"
namespace core {
class HttpQueue;
class CurlGet;
class HttpQueue;
}
namespace display {
class WindowHttpQueue : public Window {
public:
typedef std::function<void (torrent::net::HttpGet)> slot_curl_get;
typedef std::list<slot_curl_get> signal_curl_get;
typedef std::function<void (core::CurlGet*)> slot_curl_get;
typedef std::list<slot_curl_get> signal_curl_get;
WindowHttpQueue(core::HttpQueue* q);
~WindowHttpQueue() override;
@@ -25,9 +25,9 @@ public:
private:
struct Node {
Node(torrent::net::HttpGet h, const std::string& n) : m_http(h), m_name(n) {}
Node(core::CurlGet* h, const std::string& n) : m_http(h), m_name(n) {}
torrent::net::HttpGet m_http{};
core::CurlGet* m_http{};
std::string m_name;
std::chrono::microseconds m_timer{};
};
@@ -36,10 +36,10 @@ private:
void cleanup_list();
void receive_insert(torrent::net::HttpGet h);
void receive_erase(torrent::net::HttpGet h);
void receive_insert(core::CurlGet* h);
void receive_erase(core::CurlGet* h);
static std::string create_name(torrent::net::HttpGet h);
static std::string create_name(core::CurlGet* h);
core::HttpQueue* m_queue;
Container m_container;
+35 -1
View File
@@ -1,3 +1,37 @@
// 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"
@@ -12,7 +46,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 && m_input != NULL)
if (m_focus)
m_canvas->set_attr(m_input->get_pos() + 2 + m_title.size(), 0, 1, A_REVERSE, COLOR_PAIR(0));
}
+34
View File
@@ -1,3 +1,37 @@
// 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

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