mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-10-05 13:49:21 +00:00
Compare commits
234 Commits
v0.16.9
...
4c0535b1fa
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c0535b1fa | |||
| d59765fda4 | |||
| 66c49d24e5 | |||
| 3af0410a6e | |||
| 3c94b03323 | |||
| c5d54fbb97 | |||
| 44d51171e1 | |||
| dc916278c2 | |||
| 366b936ded | |||
| 8bcc4c6e92 | |||
| 5421a35349 | |||
| d5ce0984fc | |||
| a89ce3cd7b | |||
| d98a441dc4 | |||
| fc6e57c3fa | |||
| 8e23ba8b17 | |||
| e656801cfa | |||
| 9f6fd7994e | |||
| bb77aa644c | |||
| 83a5e7687b | |||
| 910b9b35fc | |||
| e3051df78c | |||
| 21287a1355 | |||
| 6471dc181e | |||
| a5c39566c8 | |||
| 019c16a077 | |||
| 439d23ce62 | |||
| 83b03c2c1e | |||
| 05cdd03cd2 | |||
| d1a6942ea7 | |||
| 3652329e2d | |||
| 7916e6022f | |||
| c59da0848e | |||
| e2f66ff08c | |||
| f0b6f2cb47 | |||
| d334b8d339 | |||
| 19b71a98b7 | |||
| 8ee3b984dd | |||
| 4e7129c71c | |||
| 80ae6a4590 | |||
| 2086e8e780 | |||
| d9439d7a95 | |||
| 0d3404576a | |||
| 84a0d86f2c | |||
| 1b15b7c518 | |||
| b1be898ba1 | |||
| bd00b5f6fe | |||
| b34eb1e895 | |||
| b857acd081 | |||
| 988aca1281 | |||
| 378c7ee854 | |||
| da9606bfae | |||
| 8423803b6e | |||
| c1ce2febf0 | |||
| 5ebb7bd151 | |||
| 50f0cad0c6 | |||
| 3717649bb3 | |||
| c32b56e9d8 | |||
| de54a4c2ad | |||
| 26fdca8f4d | |||
| 00bcdaee23 | |||
| 3787dbe7f3 | |||
| 282c86597a | |||
| c41e63248a | |||
| 641626029a | |||
| 0c507c581f | |||
| af0b600b54 | |||
| c984a69fd1 | |||
| 20002ce937 | |||
| 3caa31113d | |||
| 0373d227c1 | |||
| a8be7e443a | |||
| f327523a07 | |||
| 2804be2c9c | |||
| 21019fde41 | |||
| e4e858cc40 | |||
| 60eb8f0383 | |||
| 072cbae0c6 | |||
| 16bfaf2694 | |||
| c8da93bd30 | |||
| 2cd3ca052d | |||
| 8be1fcb3a4 | |||
| 109a20c09c | |||
| ac1b2685d5 | |||
| d7b2cfe4f2 | |||
| dcfbf82a38 | |||
| 3ae91b6536 | |||
| af6d8a10ad | |||
| 9066afc063 | |||
| a2fabb10de | |||
| 13e073d9ff | |||
| d73f245692 | |||
| 7db356cae4 | |||
| 8378c0a83a | |||
| 4c6f7afdc7 | |||
| 385e149ccc | |||
| 58e627f2be | |||
| 9ff0cd28d5 | |||
| 5e7dd98c67 | |||
| b24db0eaa5 | |||
| 6fa139f7bf | |||
| 68904ba005 | |||
| b401068246 | |||
| 946a4b5c83 | |||
| a075001a73 | |||
| 781520fef8 | |||
| 224f06fdc2 | |||
| 324811d6f0 | |||
| e51fad5bd2 | |||
| 44bb2d691f | |||
| 161a36dedd | |||
| a69e44a096 | |||
| 4dd8b1fd7e | |||
| b5f6bb5652 | |||
| 3ae6bbc394 | |||
| fdfca98f1c | |||
| 31196ff5f0 | |||
| cfb9e083d2 | |||
| 5b4b607c98 | |||
| 87db1cf0f0 | |||
| dd3ddf7c39 | |||
| e56bffff4d | |||
| d20c3234da | |||
| dda5e4fa04 | |||
| 9ab73352e1 | |||
| f90a96d57a | |||
| 247ae7621a | |||
| 05831942a7 | |||
| ee18a1f3fc | |||
| f52f20f1b6 | |||
| 41eef969ea | |||
| 1def5e8b36 | |||
| 8355870586 | |||
| ba2bc7e64d | |||
| 0c11deac50 | |||
| f6b3ad0efd | |||
| 5d350f0bcc | |||
| 86fa0d195f | |||
| 2354c9cdb5 | |||
| 1b25bc7f56 | |||
| c6bde213b4 | |||
| 959448acda | |||
| 3b6da6feac | |||
| ea2d22cb87 | |||
| fa351c017d | |||
| 1e1bdd551a | |||
| a07f57a703 | |||
| 0494ce70c8 | |||
| cfad36bf12 | |||
| 123c327a8f | |||
| 9884c9dd9b | |||
| d99cd5eb73 | |||
| 786f1234d2 | |||
| 23921cc97c | |||
| 6558f0ad42 | |||
| 9431dd5acc | |||
| 8598873627 | |||
| ce4cd27697 | |||
| 59fe93515e | |||
| 8249070ff1 | |||
| 517454b413 | |||
| a79aaad9ab | |||
| c6de871a77 | |||
| acb02379b8 | |||
| bf74686c29 | |||
| dbe7997131 | |||
| d595ebf7d8 | |||
| 08828045f5 | |||
| 662d67e861 | |||
| d08d7de20d | |||
| 83bdc271a4 | |||
| 29edda2f15 | |||
| fd69baa2c1 | |||
| 1603ba0573 | |||
| 19305ab662 | |||
| 5b2e202452 | |||
| c63db9cf97 | |||
| 72f2197add | |||
| e96f594afc | |||
| bc8d0da70f | |||
| 75377817b4 | |||
| 595617c408 | |||
| 578fe03ad6 | |||
| efe258a137 | |||
| 44d39713d5 | |||
| 49625ab5e5 | |||
| cc15e9308a | |||
| a5a96236df | |||
| f1cfe8ad72 | |||
| d0fc48cf7b | |||
| 5725eb3773 | |||
| ca706c21c0 | |||
| 14edd68653 | |||
| 50a609ade9 | |||
| 2cf8f2351b | |||
| add305f90c | |||
| 880510073a | |||
| 0989b2218d | |||
| 3d7e6cb077 | |||
| 64881317c6 | |||
| a5ecd89a54 | |||
| 84229a85be | |||
| e03ab27cf0 | |||
| e41b066555 | |||
| 8318133c79 | |||
| 088bf40bdf | |||
| 2f7a984992 | |||
| 840a57915e | |||
| 5e211c3a28 | |||
| 797a194abc | |||
| 501853cfbc | |||
| b0ef95592b | |||
| 26a7e9f545 | |||
| 0ed10984b3 | |||
| 5f09bb74de | |||
| c368b2de1e | |||
| 71ac256cb5 | |||
| b05b65bd65 | |||
| f05b48e228 | |||
| 1279bd9f7a | |||
| 981184574d | |||
| 2700b3141f | |||
| 4fec56a243 | |||
| dcf24711ef | |||
| 4e3ad1ab62 | |||
| b5a606649a | |||
| eb96876eb2 | |||
| a964b0a350 | |||
| 1adcf230d2 | |||
| 4235507292 | |||
| b7a21b1e9d | |||
| bd53959b40 | |||
| 5a4ba8bc32 | |||
| e64ec358fa |
@@ -0,0 +1,65 @@
|
||||
name: "Lint Commit Message Size"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
check-commit-bounds:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Validate Line Count, Width, and Spacing
|
||||
run: |
|
||||
# Fetch commit hashes unique to this PR branch
|
||||
COMMITS=$(git log --no-merges --pretty=format:"%H" origin/${{ github.base_ref }}..HEAD)
|
||||
|
||||
MAX_LINES=3
|
||||
MAX_CHARS=90
|
||||
FAILED=0
|
||||
|
||||
for commit in $COMMITS; do
|
||||
SUBJECT=$(git log --format="%s" -n 1 $commit)
|
||||
|
||||
# Extract clean commit message, trimming trailing blank lines
|
||||
RAW_MSG=$(git log --format="%B" -n 1 $commit)
|
||||
CLEAN_MSG=$(echo "$RAW_MSG" | awk '{msg[NR]=$0} END {while(NR>0 && msg[NR]=="") NR--; for(i=1;i<=NR;i++) print msg[i]}')
|
||||
|
||||
# 1. Check total line count
|
||||
LINE_COUNT=$(echo "$CLEAN_MSG" | wc -l)
|
||||
if [ "$LINE_COUNT" -gt "$MAX_LINES" ]; then
|
||||
echo "❌ Error: Commit message has too many lines ($LINE_COUNT/$MAX_LINES)."
|
||||
echo " Commit: '$SUBJECT'"
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
# 2. Check for multiple consecutive blank lines
|
||||
# This regex looks for 2 or more empty lines anywhere in the message
|
||||
if echo "$CLEAN_MSG" | grep -Pz '(\r?\n){3,}'; then
|
||||
echo "❌ Error: Commit message contains multiple consecutive line breaks."
|
||||
echo " Commit: '$SUBJECT'"
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
# 3. Check maximum width of any individual line
|
||||
while IFS= read -r line; do
|
||||
LINE_LENGTH=${#line}
|
||||
if [ "$LINE_LENGTH" -gt "$MAX_CHARS" ]; then
|
||||
echo "❌ Error: Line length exceeds limit ($LINE_LENGTH / $MAX_CHARS chars)."
|
||||
echo " Offending line: '$line'"
|
||||
FAILED=1
|
||||
fi
|
||||
done <<< "$CLEAN_MSG"
|
||||
|
||||
done
|
||||
|
||||
# Fail the job if any check failed
|
||||
if [ "$FAILED" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ All commit messages passed style rules!"
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
permissions:
|
||||
pull-requests: write
|
||||
# OPTIONAL: auto-closing conversations requires the `contents` permission
|
||||
contents: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Sleep for 30 seconds
|
||||
run: sleep 30s
|
||||
|
||||
@@ -16,7 +16,8 @@ jobs:
|
||||
sudo apt-get install -y \
|
||||
bear \
|
||||
clang-tidy \
|
||||
libcurl4-openssl-dev
|
||||
libcurl4-openssl-dev \
|
||||
zlib1g-dev
|
||||
- name: Fetch libtorrent
|
||||
run: |
|
||||
git clone https://github.com/rakshasa/libtorrent
|
||||
|
||||
@@ -4,9 +4,8 @@ on: pull_request
|
||||
|
||||
jobs:
|
||||
|
||||
unit-tests:
|
||||
ubuntu-base:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Update Packages
|
||||
run: |
|
||||
@@ -15,6 +14,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get install -y \
|
||||
libcppunit-dev \
|
||||
zlib1g-dev \
|
||||
libcurl4-openssl-dev
|
||||
- name: Fetch libtorrent
|
||||
run: |
|
||||
@@ -22,43 +22,86 @@ jobs:
|
||||
- name: Build libtorrent
|
||||
run: |
|
||||
cd libtorrent
|
||||
libtoolize
|
||||
aclocal -I scripts
|
||||
autoconf -i
|
||||
autoheader
|
||||
automake --add-missing
|
||||
autoreconf -fiv
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
cd ..
|
||||
rm -rf libtorrent
|
||||
- uses: actions/checkout@v4
|
||||
sudo make install DESTDIR=$GITHUB_WORKSPACE/libtorrent-dist
|
||||
- name: Upload Build Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
- name: Fetch base branch
|
||||
name: installed-libtorrent
|
||||
path: libtorrent-dist/
|
||||
|
||||
|
||||
unit-tests-default:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: ubuntu-base
|
||||
steps:
|
||||
- name: Update Packages
|
||||
run: |
|
||||
git remote add upstream "https://github.com/${{ github.event.pull_request.base.repo.full_name }}"
|
||||
git fetch --no-tags --no-recurse-submodules upstream "${{ github.event.pull_request.base.ref }}"
|
||||
sudo apt-get update
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get install -y \
|
||||
libcppunit-dev \
|
||||
zlib1g-dev \
|
||||
libcurl4-openssl-dev
|
||||
- name: Download Libtorrent Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: installed-libtorrent
|
||||
path: ./libtorrent-dist
|
||||
- name: Move Artifacts to System Path
|
||||
run: |
|
||||
# Elevate permissions with sudo to safely place the files
|
||||
sudo cp -r ./libtorrent-dist/usr/local/* /usr/local/
|
||||
rm -rf ./libtorrent-dist
|
||||
- uses: actions/checkout@v4
|
||||
- name: Configure Project
|
||||
run: |
|
||||
libtoolize
|
||||
aclocal -I scripts
|
||||
autoconf -i
|
||||
autoheader
|
||||
automake --add-missing
|
||||
autoreconf -fiv
|
||||
./configure
|
||||
- name: Build Project
|
||||
run: |
|
||||
make
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
ls /usr/local/lib/
|
||||
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib"
|
||||
make check
|
||||
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" make check
|
||||
- name: Archive test/test-suite.log
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-suite.log
|
||||
path: test/test-suite.log
|
||||
|
||||
unit-tests-variants:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: unit-tests-default
|
||||
strategy:
|
||||
matrix:
|
||||
config_flag: ["--with-xmlrpc-c", "--with-xmlrpc-tinyxml2"]
|
||||
steps:
|
||||
- name: Update Packages
|
||||
run: |
|
||||
sudo apt-get update
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get install -y \
|
||||
libcppunit-dev \
|
||||
zlib1g-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libxmlrpc-c++8-dev
|
||||
- name: Download Libtorrent Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: installed-libtorrent
|
||||
path: ./libtorrent-dist
|
||||
- name: Move Artifacts to System Path
|
||||
run: |
|
||||
sudo cp -r ./libtorrent-dist/usr/local/* /usr/local/
|
||||
rm -rf ./libtorrent-dist
|
||||
- uses: actions/checkout@v4
|
||||
- name: Configure Project
|
||||
run: |
|
||||
autoreconf -fiv
|
||||
./configure ${{ matrix.config_flag }}
|
||||
make
|
||||
ls /usr/local/lib/
|
||||
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" make check
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
.libs
|
||||
Makefile
|
||||
aclocal.m4
|
||||
actmp.*
|
||||
ar-lib
|
||||
autom4te.cache
|
||||
compile
|
||||
@@ -69,4 +70,5 @@ TAGS
|
||||
###########################
|
||||
src/rtorrent
|
||||
test/rtorrent_Test*
|
||||
test/test-suite.log.tmp
|
||||
test-driver
|
||||
|
||||
@@ -7,12 +7,6 @@ nobase_dist_pkgdata_DATA = \
|
||||
lua/rtorrent.lua
|
||||
|
||||
EXTRA_DIST= \
|
||||
rak/address_info.h \
|
||||
rak/algorithm.h \
|
||||
rak/partial_queue.h \
|
||||
rak/regex.h \
|
||||
rak/string_manip.h \
|
||||
rak/unordered_vector.h \
|
||||
scripts/checks.m4 \
|
||||
scripts/common.m4 \
|
||||
scripts/attributes.m4
|
||||
|
||||
+24
-19
@@ -1,6 +1,6 @@
|
||||
m4_pattern_allow([PKG_CHECK_EXISTS])
|
||||
|
||||
AC_INIT([rtorrent],[0.16.9],[sundell.software@gmail.com])
|
||||
AC_INIT([rtorrent],[0.16.24],[sundell.software@gmail.com])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIRS([scripts])
|
||||
@@ -10,15 +10,11 @@ AM_PROG_AR
|
||||
LT_INIT
|
||||
|
||||
AC_PROG_CXX
|
||||
AX_CXX_COMPILE_STDCXX(20, noext, mandatory)
|
||||
|
||||
AC_DEFINE([API_VERSION], [19], [api version])
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
# Filter out unwanted flags added by autoconf on some systems, e.g. MacOS.
|
||||
TORRENT_REMOVE_UNWANTED(CXX, $CXX, -std=c++11 -std=gnu++11)
|
||||
|
||||
AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory])
|
||||
|
||||
AC_SYS_LARGEFILE
|
||||
AC_DEFINE([API_VERSION], [28], [api version])
|
||||
|
||||
RAK_CHECK_CFLAGS
|
||||
RAK_CHECK_CXXFLAGS
|
||||
@@ -26,8 +22,7 @@ RAK_ENABLE_DEBUG
|
||||
RAK_ENABLE_EXTRA_DEBUG
|
||||
RAK_ENABLE_WERROR
|
||||
|
||||
TORRENT_ENABLE_ARCH
|
||||
TORRENT_WITH_SYSROOT
|
||||
TORRENT_CHECK_ATOMIC
|
||||
|
||||
AC_ARG_ENABLE(execinfo,
|
||||
AS_HELP_STRING([--disable-execinfo],
|
||||
@@ -41,19 +36,28 @@ AC_ARG_ENABLE(execinfo,
|
||||
])
|
||||
|
||||
AX_PTHREAD([], AC_MSG_ERROR([requires pthread]))
|
||||
AX_WITH_CURSES
|
||||
|
||||
if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
|
||||
AC_MSG_ERROR([requires either NcursesW or Ncurses library])
|
||||
TORRENT_WITHOUT_NCURSES
|
||||
|
||||
if test "x$with_ncurses" != xno; then
|
||||
AX_WITH_CURSES
|
||||
|
||||
if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
|
||||
AC_MSG_ERROR([requires either NcursesW or Ncurses library])
|
||||
fi
|
||||
fi
|
||||
|
||||
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
|
||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.9])
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib])
|
||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.24])
|
||||
|
||||
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
|
||||
@@ -62,15 +66,16 @@ if test ${with_xmlrpc_c+y} && test ${with_xmlrpc_tinyxml2+y}; then
|
||||
AC_MSG_ERROR([--with-xmlrpc-c and --with-xmlrpc-tinyxml2 cannot be used together. Please choose only one])
|
||||
fi
|
||||
|
||||
AC_DEFINE(USER_AGENT, [std::string(PACKAGE "/" VERSION "/") + torrent::version()], Http user agent)
|
||||
AC_DEFINE(USER_AGENT, [std::string(PACKAGE "/" VERSION)], Http user agent)
|
||||
|
||||
dnl Only update global build variables immediately before generating the output,
|
||||
dnl to avoid affecting the global build environment for other autoconf checks.
|
||||
LIBS="$PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $DEPENDENCIES_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
|
||||
LIBS="$ATOMIC_LIBS $PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $ZLIB_LIBS $DEPENDENCIES_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS"
|
||||
|
||||
TORRENT_CHECK_POPCOUNT()
|
||||
TORRENT_CHECK_CACHELINE
|
||||
TORRENT_CHECK_POSIX_SPAWN_ADDCLOSEFROM_NP
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Resolving paths
|
||||
|
||||
Commands that return a path have `.realpath` variants that resolve it to a
|
||||
canonical one, with symlinks followed and any `.` or `..` components removed.
|
||||
|
||||
The intent is to make paths safer to hand to an external script.
|
||||
A script called through `execute` receives whatever path rtorrent gives it, and
|
||||
many scripts do no sanity checking of their own, so resolving the path before it
|
||||
leaves rtorrent removes a class of surprises: a download directory that is a
|
||||
symlink into somewhere unexpected, or a torrent whose name walks upwards out of
|
||||
the directory it is supposed to live in.
|
||||
|
||||
# Instead of this
|
||||
execute = ~/bin/on-finished, (d.base_path)
|
||||
|
||||
# Pass the resolved path
|
||||
execute = ~/bin/on-finished, (d.base_path.realpath.or_throw)
|
||||
|
||||
## Available variants
|
||||
|
||||
Each command below comes in an `.or_empty` and an `.or_throw` form.
|
||||
|
||||
| Command | Resolves |
|
||||
| --- | --- |
|
||||
| `d.base_path.realpath.*` | `d.base_path` |
|
||||
| `d.directory.realpath.*` | `d.directory` |
|
||||
| `d.tied_to_file.realpath.*` | `d.tied_to_file` |
|
||||
| `d.loaded_file.realpath.*` | `d.loaded_file` |
|
||||
| `f.frozen_path.realpath.*` | `f.frozen_path` |
|
||||
| `session.path.realpath.*` | `session.path` |
|
||||
| `directory.default.realpath.*` | `directory.default` |
|
||||
|
||||
A leading `~` is expanded first, exactly as it is for `execute`, so
|
||||
`~/downloads` resolves the same way it would on the command line.
|
||||
|
||||
## Paths that do not exist
|
||||
|
||||
Resolving requires the path to name an existing file or directory, which is
|
||||
often not the case for a download whose data has not been written yet.
|
||||
The two forms differ only in what they do about it.
|
||||
|
||||
`.or_empty` returns an empty string:
|
||||
|
||||
print = (d.base_path.realpath.or_empty) # ""
|
||||
|
||||
This keeps `d.multicall` usable over a view that mixes started and unstarted
|
||||
downloads, since a single unresolvable path does not abort the whole call.
|
||||
A script receiving one of these paths should still check that it is not empty
|
||||
before acting on it.
|
||||
|
||||
`.or_throw` raises an error instead:
|
||||
|
||||
print = (d.base_path.realpath.or_throw) # Could not resolve path: '...'
|
||||
|
||||
Prefer this one wherever an unresolvable path means the command should not run
|
||||
at all, such as a single `execute` on `event.download.finished`.
|
||||
|
||||
Note that a download only has a file list once it has been opened, so
|
||||
`d.base_path` and `f.frozen_path` are empty at `event.download.inserted` time
|
||||
and their `.realpath` variants resolve nothing.
|
||||
@@ -0,0 +1,139 @@
|
||||
# String functions
|
||||
|
||||
The `string.*` commands manipulate and inspect text from within the
|
||||
configuration file.
|
||||
They are pure functions with no side effects, so they can be nested freely and
|
||||
are safe to call over RPC.
|
||||
|
||||
Every argument is converted to its string representation before use, which means
|
||||
numbers can be passed where text is expected.
|
||||
Commands that count characters, such as `string.length` and `string.substr`, count
|
||||
utf-8 characters rather than bytes.
|
||||
|
||||
## string.length
|
||||
|
||||
# string.length = «text»
|
||||
|
||||
string.length = "héllo" # 5
|
||||
|
||||
Returns the number of utf-8 characters in the text.
|
||||
|
||||
## string.equals
|
||||
|
||||
# string.equals = «text», «other»[, ...]
|
||||
|
||||
string.equals = (d.name), "first.iso", "second.iso"
|
||||
|
||||
Returns `1` if the first argument equals any of the following arguments,
|
||||
otherwise `0`.
|
||||
|
||||
## string.starts_with, string.ends_with
|
||||
|
||||
# string.starts_with = «text», «prefix»[, ...]
|
||||
# string.ends_with = «text», «tail»[, ...]
|
||||
|
||||
string.starts_with = (t.url), "http://", "https://"
|
||||
string.ends_with = (d.name), ".iso"
|
||||
|
||||
Returns `1` if the text begins, or ends, with any of the given prefixes or
|
||||
tails.
|
||||
|
||||
## string.contains, string.contains_i
|
||||
|
||||
# string.contains = «haystack», «needle»[, ...]
|
||||
# string.contains_i = «haystack», «needle»[, ...]
|
||||
|
||||
string.contains = (t.url), "retracker.local"
|
||||
string.contains_i = (t.url), "RETRACKER.local"
|
||||
|
||||
Returns `1` if the haystack contains any of the needles.
|
||||
The `_i` variant compares case-insensitively, and only handles ascii.
|
||||
|
||||
## string.substr
|
||||
|
||||
# string.substr = «text»[, «position»[, «count»[, «default»]]]
|
||||
|
||||
string.substr = "abcdef", 2, 3 # "cde"
|
||||
string.substr = "abcdef", -2 # "ef"
|
||||
string.substr = "abcdef", 10, 1, "?" # "?"
|
||||
|
||||
Extracts a part of the text, starting at `position` and spanning `count`
|
||||
characters.
|
||||
The position defaults to the start of the text, and the count to the rest of it.
|
||||
A negative position is relative to the end of the text.
|
||||
If the position falls outside the text, the default value is returned instead,
|
||||
which is the empty string unless given.
|
||||
|
||||
## string.split
|
||||
|
||||
# string.split = «text», «delimiter»
|
||||
|
||||
string.split = "a.b.c", "." # {"a", "b", "c"}
|
||||
string.split = "abc", "" # {"a", "b", "c"}
|
||||
|
||||
Splits the text into a list, keeping empty fields.
|
||||
An empty delimiter splits the text into its utf-8 characters.
|
||||
|
||||
## string.join
|
||||
|
||||
# string.join = «delimiter»[, «object»[, ...]]
|
||||
|
||||
string.join = "-", (string.split, "a.b.c", ".") # "a-b-c"
|
||||
|
||||
Concatenates the objects, inserting the delimiter between them.
|
||||
Lists are flattened, so the result of `string.split` can be joined back
|
||||
together.
|
||||
|
||||
## string.lpad, string.rpad
|
||||
|
||||
# string.lpad = «text», «length»[, «padding»]
|
||||
# string.rpad = «text», «length»[, «padding»]
|
||||
|
||||
string.lpad = 7, 3, 0 # "007"
|
||||
string.rpad = "a", 3 # "a "
|
||||
|
||||
Pads the text at the start, or the end, until it is `length` characters long.
|
||||
The padding defaults to a single space and is repeated as needed.
|
||||
Text that is already long enough is returned unchanged.
|
||||
|
||||
## string.strip, string.lstrip, string.rstrip
|
||||
|
||||
# string.strip = «text»[, «strippable»[, ...]]
|
||||
# string.lstrip = «text»[, «head»[, ...]]
|
||||
# string.rstrip = «text»[, «tail»[, ...]]
|
||||
|
||||
string.strip = " padded " # "padded"
|
||||
string.strip = "//path//", "/" # "path"
|
||||
|
||||
Removes characters from both ends of the text, or from only the start or the
|
||||
end.
|
||||
The arguments after the text form a set of utf-8 characters to remove.
|
||||
Without them, whitespace is removed.
|
||||
|
||||
## string.map
|
||||
|
||||
# string.map = «text», {«old», «new»}[, ...]
|
||||
|
||||
string.map = (d.state), {0, "stopped"}, {1, "started"}
|
||||
|
||||
Returns the replacement of the first pair whose `old` value equals the whole
|
||||
text.
|
||||
If no pair matches, the text is returned unchanged.
|
||||
|
||||
## string.replace
|
||||
|
||||
# string.replace = «text», {«old», «new»}[, ...]
|
||||
|
||||
string.replace = "a-b-c", {"-", "+"} # "a+b+c"
|
||||
|
||||
Replaces every occurrence of `old` with `new`.
|
||||
The pairs are applied in order, so a later pair operates on the result of the
|
||||
earlier ones.
|
||||
|
||||
## Example: dropping unwanted trackers
|
||||
|
||||
The following disables every tracker that points at `retracker.local` as soon as
|
||||
a download is inserted.
|
||||
|
||||
method.set_key = event.download.inserted, drop_retracker, \
|
||||
((t.multicall, default, "branch=(string.contains,(t.url),retracker.local),((t.disable))"))
|
||||
+15
-3
@@ -46,8 +46,12 @@
|
||||
#session.path.set = ./session
|
||||
|
||||
# Watch a directory for new torrents, and stop those that have been
|
||||
# deleted.
|
||||
# deleted. Use directory.watch.ready for network shares or other watch
|
||||
# directories where files may be copied or written directly into place.
|
||||
# Do not mix directory.watch.ready and directory.watch.added on the same
|
||||
# watch directory.
|
||||
#
|
||||
#directory.watch.ready = ./watch, load.start
|
||||
#schedule2 = watch_directory,5,5,load.start=./watch/*.torrent
|
||||
|
||||
# Close torrents when disk-space is low.
|
||||
@@ -59,6 +63,14 @@
|
||||
#network.local_address.set = 127.0.0.1
|
||||
#network.local_address.set = rakshasa.no
|
||||
|
||||
# The port reported to the tracker, separate for ipv4/ipv6. A value of 0
|
||||
# (the default) falls back to reporting the listening port. network.local_port.set
|
||||
# sets both the ipv4 and ipv6 ports at once.
|
||||
#
|
||||
#network.local_port.set = 6881
|
||||
#network.local_port.ipv4.set = 6881
|
||||
#network.local_port.ipv6.set = 6882
|
||||
|
||||
# The IP address the listening socket and outgoing connections is
|
||||
# bound to.
|
||||
#
|
||||
@@ -67,11 +79,11 @@
|
||||
|
||||
# Port range to use for listening.
|
||||
#
|
||||
#network.port_range.set = 6890-6999
|
||||
#network.listen.port.range.set = 6890-6999
|
||||
|
||||
# Start opening ports at a random position within the port range.
|
||||
#
|
||||
#network.port_random.set = no
|
||||
#network.listen.port.random.set = no
|
||||
|
||||
# Set RPC type
|
||||
#network.rpc.use_xmlrpc.set = true
|
||||
|
||||
@@ -23,8 +23,8 @@ execute.throw = sh, -c, (cat,\
|
||||
"\"",(cfg.watch),"/start\" ")
|
||||
|
||||
# Listening port for incoming peer traffic (fixed; you can also randomize it)
|
||||
network.port_range.set = 50000-50000
|
||||
network.port_random.set = no
|
||||
network.listen.port.range.set = 50000-50000
|
||||
network.listen.port.random.set = no
|
||||
|
||||
# Tracker-less torrent and UDP tracker support
|
||||
# (conservative settings for 'private' trackers, change for 'public')
|
||||
|
||||
@@ -53,8 +53,8 @@ rc.execute.throw(
|
||||
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
|
||||
rc.network.listen.port.range = '50000-50000'
|
||||
rc.network.listen.port.random = false
|
||||
|
||||
-- Tracker-less torrent and UDP tracker support
|
||||
-- (conservative settings for 'private' trackers, change for 'public')
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
// 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 <cstring>
|
||||
|
||||
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; }
|
||||
|
||||
addrinfo* c_addrinfo() { return &m_addrinfo; }
|
||||
const addrinfo* c_addrinfo() const { return &m_addrinfo; }
|
||||
|
||||
address_info* next() { return reinterpret_cast<address_info*>(m_addrinfo.ai_next); }
|
||||
|
||||
static int get_address_info(const char* node, int domain, int type, address_info** ai);
|
||||
|
||||
static void free_address_info(address_info* ai) { ::freeaddrinfo(ai->c_addrinfo()); }
|
||||
|
||||
static const char* strerror(int err) { return gai_strerror(err); }
|
||||
|
||||
private:
|
||||
addrinfo m_addrinfo;
|
||||
};
|
||||
|
||||
inline int
|
||||
address_info::get_address_info(const char* node, int pfamily, int stype, address_info** ai) {
|
||||
address_info hints;
|
||||
hints.clear();
|
||||
hints.set_family(pfamily);
|
||||
hints.set_socket_type(stype);
|
||||
|
||||
return ::getaddrinfo(node, NULL, hints.c_addrinfo(), reinterpret_cast<addrinfo**>(ai));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
-195
@@ -1,195 +0,0 @@
|
||||
// rak - Rakshasa's toolbox
|
||||
// Copyright (C) 2005-2007, Jari Sundell
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for
|
||||
// all of the code used other than OpenSSL. If you modify file(s)
|
||||
// with this exception, you may extend this exception to your version
|
||||
// of the file(s), but you are not obligated to do so. If you do not
|
||||
// wish to do so, delete this exception statement from your version.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <sundell.software@gmail.com>
|
||||
|
||||
|
||||
#ifndef RAK_ALGORITHM_H
|
||||
#define RAK_ALGORITHM_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace rak {
|
||||
|
||||
template <typename _InputIter, typename _Function>
|
||||
_Function
|
||||
for_each_pre(_InputIter __first, _InputIter __last, _Function __f) {
|
||||
_InputIter __tmp;
|
||||
|
||||
while (__first != __last) {
|
||||
__tmp = __first++;
|
||||
|
||||
__f(*__tmp);
|
||||
}
|
||||
|
||||
return __f;
|
||||
}
|
||||
|
||||
// Return a range with a distance of no more than __distance and
|
||||
// between __first and __last, centered on __middle1.
|
||||
template <typename _InputIter, typename _Distance>
|
||||
std::pair<_InputIter, _InputIter>
|
||||
advance_bidirectional(_InputIter __first, _InputIter __middle1, _InputIter __last, _Distance __distance) {
|
||||
_InputIter __middle2 = __middle1;
|
||||
|
||||
do {
|
||||
if (!__distance)
|
||||
break;
|
||||
|
||||
if (__middle2 != __last) {
|
||||
++__middle2;
|
||||
--__distance;
|
||||
|
||||
} else if (__middle1 == __first) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!__distance)
|
||||
break;
|
||||
|
||||
if (__middle1 != __first) {
|
||||
--__middle1;
|
||||
--__distance;
|
||||
|
||||
} else if (__middle2 == __last) {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
return std::make_pair(__middle1, __middle2);
|
||||
}
|
||||
|
||||
template <typename _InputIter, typename _Distance>
|
||||
_InputIter
|
||||
advance_forward(_InputIter __first, _InputIter __last, _Distance __distance) {
|
||||
while (__first != __last && __distance != 0) {
|
||||
__first++;
|
||||
__distance--;
|
||||
}
|
||||
|
||||
return __first;
|
||||
}
|
||||
|
||||
template <typename _InputIter, typename _Distance>
|
||||
_InputIter
|
||||
advance_backward(_InputIter __first, _InputIter __last, _Distance __distance) {
|
||||
while (__first != __last && __distance != 0) {
|
||||
__first--;
|
||||
__distance--;
|
||||
}
|
||||
|
||||
return __first;
|
||||
}
|
||||
|
||||
template <typename _Value>
|
||||
struct compare_base {
|
||||
bool operator () (const _Value& complete, const _Value& base) const {
|
||||
return !complete.compare(0, base.size(), base);
|
||||
}
|
||||
};
|
||||
|
||||
// Count the number of elements from the start of the containers to
|
||||
// the first inequal element.
|
||||
template <typename _InputIter1, typename _InputIter2>
|
||||
typename std::iterator_traits<_InputIter1>::difference_type
|
||||
count_base(_InputIter1 __first1, _InputIter1 __last1,
|
||||
_InputIter2 __first2, _InputIter2 __last2) {
|
||||
|
||||
typename std::iterator_traits<_InputIter1>::difference_type __n = 0;
|
||||
|
||||
for ( ;__first1 != __last1 && __first2 != __last2; ++__first1, ++__first2, ++__n)
|
||||
if (*__first1 != *__first2)
|
||||
return __n;
|
||||
|
||||
return __n;
|
||||
}
|
||||
|
||||
template <typename _Return, typename _InputIter, typename _Ftor>
|
||||
_Return
|
||||
make_base(_InputIter __first, _InputIter __last, _Ftor __ftor) {
|
||||
if (__first == __last)
|
||||
return "";
|
||||
|
||||
_Return __base = __ftor(*__first++);
|
||||
|
||||
for ( ;__first != __last; ++__first) {
|
||||
typename std::iterator_traits<_InputIter>::difference_type __pos = count_base(__base.begin(), __base.end(),
|
||||
__ftor(*__first).begin(), __ftor(*__first).end());
|
||||
|
||||
if (__pos < (typename std::iterator_traits<_InputIter>::difference_type)__base.size())
|
||||
__base.resize(__pos);
|
||||
}
|
||||
|
||||
return __base;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline int popcount_wrapper(T t) {
|
||||
#if USE_BUILTIN_POPCOUNT
|
||||
return __builtin_popcountll(t);
|
||||
#else
|
||||
#error __builtin_popcount not found.
|
||||
unsigned int count = 0;
|
||||
|
||||
while (t) {
|
||||
count += t & 0x1;
|
||||
t >> 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get the median of an unordered set of numbers of arbitrary
|
||||
// type by modifing the underlying dataset
|
||||
template <typename T = double, typename _InputIter>
|
||||
T median(_InputIter __first, _InputIter __last) {
|
||||
T __med;
|
||||
|
||||
unsigned int __size = __last - __first;
|
||||
unsigned int __middle = __size / 2;
|
||||
_InputIter __target1 = __first + __middle;
|
||||
std::nth_element(__first, __target1, __last);
|
||||
__med = *__target1;
|
||||
|
||||
if (__size % 2 == 0) {
|
||||
_InputIter __target2 = std::max_element(__first, __target1);
|
||||
__med = (__med + *__target2) / 2.0;
|
||||
}
|
||||
|
||||
return __med;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,197 +0,0 @@
|
||||
// rak - Rakshasa's toolbox
|
||||
// Copyright (C) 2005-2007, Jari Sundell
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for
|
||||
// all of the code used other than OpenSSL. If you modify file(s)
|
||||
// with this exception, you may extend this exception to your version
|
||||
// of the file(s), but you are not obligated to do so. If you do not
|
||||
// wish to do so, delete this exception statement from your version.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <sundell.software@gmail.com>
|
||||
|
||||
|
||||
#ifndef RAK_PARTIAL_QUEUE_H
|
||||
#define RAK_PARTIAL_QUEUE_H
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <cinttypes>
|
||||
|
||||
namespace rak {
|
||||
|
||||
// First step, don't allow overflowing to the next layer. Only disable
|
||||
// the above layers for now.
|
||||
|
||||
// We also include 0 in a single layer as some chunk may be available
|
||||
// only through seeders.
|
||||
|
||||
class partial_queue {
|
||||
public:
|
||||
typedef uint8_t key_type;
|
||||
typedef uint32_t mapped_type;
|
||||
typedef uint16_t size_type;
|
||||
typedef std::pair<size_type, size_type> size_pair_type;
|
||||
|
||||
static const size_type num_layers = 8;
|
||||
|
||||
partial_queue() : m_data(NULL), m_maxLayerSize(0) {}
|
||||
~partial_queue() { disable(); }
|
||||
|
||||
bool is_full() const { return m_ceiling == 0; }
|
||||
bool is_layer_full(size_type l) const { return m_layers[l].second >= m_maxLayerSize; }
|
||||
|
||||
bool is_enabled() const { return m_data != NULL; }
|
||||
|
||||
// Add check to see if we can add more. Also make it possible to
|
||||
// check how full we are in the lower parts so the caller knows when
|
||||
// he can stop searching.
|
||||
//
|
||||
// Though propably not needed, as we must continue til the first
|
||||
// layer is full.
|
||||
|
||||
size_type max_size() const { return m_maxLayerSize * num_layers; }
|
||||
size_type max_layer_size() const { return m_maxLayerSize; }
|
||||
|
||||
// Must be less that or equal to (max size_type) / num_layers.
|
||||
void enable(size_type ls);
|
||||
void disable();
|
||||
|
||||
void clear();
|
||||
|
||||
// Safe to call while pop'ing and it will not reuse pop'ed indices
|
||||
// so it is guaranteed to reach max_size at some point. This will
|
||||
// ensure that the user needs to refill with new data at regular
|
||||
// intervals.
|
||||
bool insert(key_type key, mapped_type value);
|
||||
|
||||
// Only call this when pop'ing as it moves the index.
|
||||
bool prepare_pop();
|
||||
|
||||
mapped_type pop();
|
||||
|
||||
private:
|
||||
partial_queue(const partial_queue&);
|
||||
void operator = (const partial_queue&);
|
||||
|
||||
static size_type ceiling(size_type layer) { return (2 << layer) - 1; }
|
||||
|
||||
void find_non_empty();
|
||||
|
||||
mapped_type* m_data;
|
||||
size_type m_maxLayerSize;
|
||||
|
||||
size_type m_index;
|
||||
size_type m_ceiling;
|
||||
|
||||
std::array<size_pair_type, num_layers> m_layers;
|
||||
};
|
||||
|
||||
inline void
|
||||
partial_queue::enable(size_type ls) {
|
||||
if (ls == 0)
|
||||
throw std::logic_error("partial_queue::enable(...) ls == 0.");
|
||||
|
||||
delete [] m_data;
|
||||
m_data = new mapped_type[ls * num_layers];
|
||||
|
||||
m_maxLayerSize = ls;
|
||||
}
|
||||
|
||||
inline void
|
||||
partial_queue::disable() {
|
||||
delete [] m_data;
|
||||
m_data = NULL;
|
||||
|
||||
m_maxLayerSize = 0;
|
||||
}
|
||||
|
||||
inline void
|
||||
partial_queue::clear() {
|
||||
if (m_data == NULL)
|
||||
return;
|
||||
|
||||
m_index = 0;
|
||||
m_ceiling = ceiling(num_layers - 1);
|
||||
|
||||
m_layers = {};
|
||||
}
|
||||
|
||||
inline bool
|
||||
partial_queue::insert(key_type key, mapped_type value) {
|
||||
if (key >= m_ceiling)
|
||||
return false;
|
||||
|
||||
size_type idx = 0;
|
||||
|
||||
// Hmm... since we already check the 'm_ceiling' above, we only need
|
||||
// to find the target layer. Could this be calculated directly?
|
||||
while (key >= ceiling(idx))
|
||||
++idx;
|
||||
|
||||
m_index = std::min(m_index, idx);
|
||||
|
||||
// Currently don't allow overflow.
|
||||
if (is_layer_full(idx))
|
||||
throw std::logic_error("partial_queue::insert(...) layer already full.");
|
||||
//return false;
|
||||
|
||||
m_data[m_maxLayerSize * idx + m_layers[idx].second] = value;
|
||||
m_layers[idx].second++;
|
||||
|
||||
if (is_layer_full(idx))
|
||||
// Set the ceiling to 0 when layer 0 is full so no more values can
|
||||
// be inserted.
|
||||
m_ceiling = idx > 0 ? ceiling(idx - 1) : 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// is_empty() will iterate to the first layer with un-popped elements
|
||||
// and return true, else return false when it reaches a overflowed or
|
||||
// the last layer.
|
||||
inline bool
|
||||
partial_queue::prepare_pop() {
|
||||
while (m_layers[m_index].first == m_layers[m_index].second) {
|
||||
if (is_layer_full(m_index) || m_index + 1 == num_layers)
|
||||
return false;
|
||||
|
||||
m_index++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline partial_queue::mapped_type
|
||||
partial_queue::pop() {
|
||||
if (m_index >= num_layers || m_layers[m_index].first >= m_layers[m_index].second)
|
||||
throw std::logic_error("partial_queue::pop() bad state.");
|
||||
|
||||
return m_data[m_index * m_maxLayerSize + m_layers[m_index].first++];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
// rak - Rakshasa's toolbox
|
||||
// Copyright (C) 2005-2007, Jari Sundell
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for
|
||||
// all of the code used other than OpenSSL. If you modify file(s)
|
||||
// with this exception, you may extend this exception to your version
|
||||
// of the file(s), but you are not obligated to do so. If you do not
|
||||
// wish to do so, delete this exception statement from your version.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <sundell.software@gmail.com>
|
||||
|
||||
|
||||
// This is a hacked up whole string pattern matching. Replace with
|
||||
// TR1's regex when that becomes widely available. It is intended for
|
||||
// small strings.
|
||||
|
||||
#ifndef RAK_REGEX_H
|
||||
#define RAK_REGEX_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
namespace rak {
|
||||
|
||||
class regex {
|
||||
public:
|
||||
regex() {}
|
||||
regex(const std::string& p) : m_pattern(p) {}
|
||||
|
||||
const std::string& pattern() const { return m_pattern; }
|
||||
|
||||
bool operator () (const std::string& p) const;
|
||||
|
||||
private:
|
||||
std::string m_pattern;
|
||||
};
|
||||
|
||||
// This isn't optimized, or very clean. A simple hack that should work.
|
||||
inline bool
|
||||
regex::operator () (const std::string& text) const {
|
||||
if (m_pattern.empty() ||
|
||||
text.empty() ||
|
||||
(m_pattern[0] != '*' && m_pattern[0] != text[0]))
|
||||
return false;
|
||||
|
||||
// Replace with unordered_vector?
|
||||
std::list<unsigned int> paths;
|
||||
paths.push_front(0);
|
||||
|
||||
for (std::string::const_iterator itrText = ++text.begin(), lastText = text.end(); itrText != lastText; ++itrText) {
|
||||
|
||||
for (std::list<unsigned int>::iterator itrPaths = paths.begin(), lastPaths = paths.end(); itrPaths != lastPaths; ) {
|
||||
|
||||
unsigned int next = *itrPaths + 1;
|
||||
|
||||
if (m_pattern[*itrPaths] != '*')
|
||||
itrPaths = paths.erase(itrPaths);
|
||||
else
|
||||
itrPaths++;
|
||||
|
||||
// When we reach the end of 'm_pattern', we don't have a whole
|
||||
// match of 'text'.
|
||||
if (next == m_pattern.size())
|
||||
continue;
|
||||
|
||||
// Push to the back so that '*' will match zero length strings.
|
||||
if (m_pattern[next] == '*')
|
||||
paths.push_back(next);
|
||||
|
||||
if (m_pattern[next] == *itrText)
|
||||
paths.push_front(next);
|
||||
}
|
||||
|
||||
if (paths.empty())
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::find(paths.begin(), paths.end(), m_pattern.size() - 1) != paths.end();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,425 +0,0 @@
|
||||
// rak - Rakshasa's toolbox
|
||||
// Copyright (C) 2005-2007, Jari Sundell
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for
|
||||
// all of the code used other than OpenSSL. If you modify file(s)
|
||||
// with this exception, you may extend this exception to your version
|
||||
// of the file(s), but you are not obligated to do so. If you do not
|
||||
// wish to do so, delete this exception statement from your version.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <sundell.software@gmail.com>
|
||||
|
||||
|
||||
#ifndef RAK_STRING_MANIP_H
|
||||
#define RAK_STRING_MANIP_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <locale>
|
||||
#include <random>
|
||||
|
||||
|
||||
namespace rak {
|
||||
|
||||
// Use these trim functions until n1872 is widely supported.
|
||||
|
||||
template <typename Sequence>
|
||||
Sequence trim_begin(const Sequence& seq) {
|
||||
if (seq.empty() || !std::isspace(*seq.begin()))
|
||||
return seq;
|
||||
|
||||
typename Sequence::size_type pos = 0;
|
||||
|
||||
while (pos != seq.length() && std::isspace(seq[pos]))
|
||||
pos++;
|
||||
|
||||
return seq.substr(pos, seq.length() - pos);
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
Sequence trim_end(const Sequence& seq) {
|
||||
if (seq.empty() || !std::isspace(*(--seq.end())))
|
||||
return seq;
|
||||
|
||||
typename Sequence::size_type pos = seq.size();
|
||||
|
||||
while (pos != 0 && std::isspace(seq[pos - 1]))
|
||||
pos--;
|
||||
|
||||
return seq.substr(0, pos);
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
Sequence trim(const Sequence& seq) {
|
||||
return trim_begin(trim_end(seq));
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
Sequence trim_begin_classic(const Sequence& seq) {
|
||||
if (seq.empty() || !std::isspace(*seq.begin(), std::locale::classic()))
|
||||
return seq;
|
||||
|
||||
typename Sequence::size_type pos = 0;
|
||||
|
||||
while (pos != seq.length() && std::isspace(seq[pos], std::locale::classic()))
|
||||
pos++;
|
||||
|
||||
return seq.substr(pos, seq.length() - pos);
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
Sequence trim_end_classic(const Sequence& seq) {
|
||||
if (seq.empty() || !std::isspace(*(--seq.end()), std::locale::classic()))
|
||||
return seq;
|
||||
|
||||
typename Sequence::size_type pos = seq.size();
|
||||
|
||||
while (pos != 0 && std::isspace(seq[pos - 1], std::locale::classic()))
|
||||
pos--;
|
||||
|
||||
return seq.substr(0, pos);
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
Sequence trim_classic(const Sequence& seq) {
|
||||
return trim_begin_classic(trim_end_classic(seq));
|
||||
}
|
||||
|
||||
// Consider rewritting such that m_seq is replaced by first/last.
|
||||
template <typename Sequence>
|
||||
class split_iterator_t {
|
||||
public:
|
||||
typedef typename Sequence::const_iterator const_iterator;
|
||||
typedef typename Sequence::value_type value_type;
|
||||
|
||||
split_iterator_t() {}
|
||||
|
||||
split_iterator_t(const Sequence& seq, value_type delim) :
|
||||
m_seq(&seq),
|
||||
m_delim(delim),
|
||||
m_pos(seq.begin()),
|
||||
m_next(std::find(seq.begin(), seq.end(), delim)) {
|
||||
}
|
||||
|
||||
Sequence operator * () { return Sequence(m_pos, m_next); }
|
||||
|
||||
split_iterator_t& operator ++ () {
|
||||
m_pos = m_next;
|
||||
|
||||
if (m_pos == m_seq->end())
|
||||
return *this;
|
||||
|
||||
m_pos++;
|
||||
m_next = std::find(m_pos, m_seq->end(), m_delim);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator == (const split_iterator_t&) const { return m_pos == m_seq->end(); }
|
||||
bool operator != (const split_iterator_t&) const { return m_pos != m_seq->end(); }
|
||||
|
||||
private:
|
||||
const Sequence* m_seq;
|
||||
value_type m_delim;
|
||||
const_iterator m_pos;
|
||||
const_iterator m_next;
|
||||
};
|
||||
|
||||
template <typename Sequence>
|
||||
inline split_iterator_t<Sequence>
|
||||
split_iterator(const Sequence& seq, typename Sequence::value_type delim) {
|
||||
return split_iterator_t<Sequence>(seq, delim);
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
inline split_iterator_t<Sequence>
|
||||
split_iterator(const Sequence&) {
|
||||
return split_iterator_t<Sequence>();
|
||||
}
|
||||
|
||||
// Could optimize this abit.
|
||||
inline char
|
||||
hexchar_to_value(char c) {
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
return 10 + c - 'A';
|
||||
|
||||
else
|
||||
return 10 + c - 'a';
|
||||
}
|
||||
|
||||
template <int pos, typename Value>
|
||||
inline char
|
||||
value_to_hexchar(Value v) {
|
||||
v >>= pos * 4;
|
||||
v &= 0xf;
|
||||
|
||||
if (v < 0xA)
|
||||
return '0' + v;
|
||||
else
|
||||
return 'A' + v - 0xA;
|
||||
}
|
||||
|
||||
template <typename InputIterator, typename OutputIterator>
|
||||
OutputIterator
|
||||
copy_escape_html(InputIterator first, InputIterator last, OutputIterator dest) {
|
||||
while (first != last) {
|
||||
if (std::isalpha(*first, std::locale::classic()) ||
|
||||
std::isdigit(*first, std::locale::classic()) ||
|
||||
*first == '-') {
|
||||
*(dest++) = *first;
|
||||
|
||||
} else {
|
||||
*(dest++) = '%';
|
||||
*(dest++) = value_to_hexchar<1>(*first);
|
||||
*(dest++) = value_to_hexchar<0>(*first);
|
||||
}
|
||||
|
||||
++first;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <typename InputIterator, typename OutputIterator>
|
||||
OutputIterator
|
||||
copy_escape_html(InputIterator first1, InputIterator last1, OutputIterator first2, OutputIterator last2) {
|
||||
while (first1 != last1) {
|
||||
if (std::isalpha(*first1, std::locale::classic()) ||
|
||||
std::isdigit(*first1, std::locale::classic()) ||
|
||||
*first1 == '-') {
|
||||
if (first2 == last2) break; else *(first2++) = *first1;
|
||||
|
||||
} else {
|
||||
if (first2 == last2) break; else *(first2++) = '%';
|
||||
if (first2 == last2) break; else *(first2++) = value_to_hexchar<1>(*first1);
|
||||
if (first2 == last2) break; else *(first2++) = value_to_hexchar<0>(*first1);
|
||||
}
|
||||
|
||||
++first1;
|
||||
}
|
||||
|
||||
return first2;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
inline std::string
|
||||
copy_escape_html(Iterator first, Iterator last) {
|
||||
std::string dest;
|
||||
copy_escape_html(first, last, std::back_inserter(dest));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
inline Sequence
|
||||
copy_escape_html(const Sequence& src) {
|
||||
Sequence dest;
|
||||
copy_escape_html(src.begin(), src.end(), std::back_inserter(dest));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
inline std::string
|
||||
copy_escape_html_str(const Sequence& src) {
|
||||
std::string dest;
|
||||
copy_escape_html(src.begin(), src.end(), std::back_inserter(dest));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Consider support for larger than char type.
|
||||
template <typename InputIterator, typename OutputIterator>
|
||||
OutputIterator
|
||||
transform_hex(InputIterator first, InputIterator last, OutputIterator dest) {
|
||||
while (first != last) {
|
||||
*(dest++) = value_to_hexchar<1>(*first);
|
||||
*(dest++) = value_to_hexchar<0>(*first);
|
||||
|
||||
++first;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <typename InputIterator, typename OutputIterator>
|
||||
OutputIterator
|
||||
transform_hex(InputIterator first1, InputIterator last1, OutputIterator first2, OutputIterator last2) {
|
||||
while (first1 != last1) {
|
||||
if (first2 == last2) break; else *(first2++) = value_to_hexchar<1>(*first1);
|
||||
if (first2 == last2) break; else *(first2++) = value_to_hexchar<0>(*first1);
|
||||
|
||||
++first1;
|
||||
}
|
||||
|
||||
return first2;
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
inline Sequence
|
||||
transform_hex(const Sequence& src) {
|
||||
Sequence dest;
|
||||
transform_hex(src.begin(), src.end(), std::back_inserter(dest));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
inline std::string
|
||||
transform_hex(Iterator first, Iterator last) {
|
||||
std::string dest;
|
||||
transform_hex(first, last, std::back_inserter(dest));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
inline std::string
|
||||
transform_hex_str(const Sequence& seq) {
|
||||
std::string dest;
|
||||
transform_hex(seq.begin(), seq.end(), std::back_inserter(dest));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
Sequence
|
||||
generate_random(size_t length) {
|
||||
std::random_device rd;
|
||||
std::mt19937 mt(rd());
|
||||
using bytes_randomizer = std::independent_bits_engine<std::mt19937, CHAR_BIT, uint8_t>;
|
||||
bytes_randomizer bytes(mt);
|
||||
Sequence s;
|
||||
s.reserve(length);
|
||||
std::generate_n(std::back_inserter(s), length, std::ref(bytes));
|
||||
return s;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
inline bool
|
||||
is_all_alpha(Iterator first, Iterator last) {
|
||||
while (first != last)
|
||||
if (!std::isalpha(*first++, std::locale::classic()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
inline bool
|
||||
is_all_alpha(const Sequence& src) {
|
||||
return is_all_alpha(src.begin(), src.end());
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
inline bool
|
||||
is_all_alnum(Iterator first, Iterator last) {
|
||||
while (first != last)
|
||||
if (!std::isalnum(*first++, std::locale::classic()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
inline bool
|
||||
is_all_alnum(const Sequence& src) {
|
||||
return is_all_alnum(src.begin(), src.end());
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
inline bool
|
||||
is_all_name(Iterator first, Iterator last) {
|
||||
while (first != last) {
|
||||
if (!std::isalnum(*first, std::locale::classic()) && *first != '_')
|
||||
return false;
|
||||
|
||||
first++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
inline bool
|
||||
is_all_name(const Sequence& src) {
|
||||
return is_all_name(src.begin(), src.end());
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
std::string
|
||||
sanitize(Iterator first, Iterator last) {
|
||||
std::string dest;
|
||||
for (; first != last; ++first) {
|
||||
if (std::isprint(*first) && *first != '\r' && *first != '\n' && *first != '\t')
|
||||
dest += *first;
|
||||
else
|
||||
dest += " ";
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
std::string
|
||||
sanitize(const Sequence& src) {
|
||||
return trim(sanitize(src.begin(), src.end()));
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
std::string striptags(Iterator first, Iterator last) {
|
||||
bool copychar = true;
|
||||
std::string dest;
|
||||
|
||||
for (; first != last; ++first) {
|
||||
if (std::isprint(*first) && *first == '<') {
|
||||
copychar = false;
|
||||
} else if (std::isprint(*first) && *first == '>') {
|
||||
copychar = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (copychar)
|
||||
dest += *first;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
std::string striptags(const Sequence& src) {
|
||||
return striptags(src.begin(), src.end());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,107 +0,0 @@
|
||||
// rak - Rakshasa's toolbox
|
||||
// Copyright (C) 2005-2007, Jari Sundell
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for
|
||||
// all of the code used other than OpenSSL. If you modify file(s)
|
||||
// with this exception, you may extend this exception to your version
|
||||
// of the file(s), but you are not obligated to do so. If you do not
|
||||
// wish to do so, delete this exception statement from your version.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <sundell.software@gmail.com>
|
||||
|
||||
|
||||
#ifndef RAK_UNORDERED_VECTOR_H
|
||||
#define RAK_UNORDERED_VECTOR_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace rak {
|
||||
|
||||
template <typename _Tp>
|
||||
class unordered_vector : private std::vector<_Tp> {
|
||||
public:
|
||||
typedef std::vector<_Tp> Base;
|
||||
|
||||
typedef typename Base::value_type value_type;
|
||||
typedef typename Base::pointer pointer;
|
||||
typedef typename Base::const_pointer const_pointer;
|
||||
typedef typename Base::reference reference;
|
||||
typedef typename Base::const_reference const_reference;
|
||||
typedef typename Base::size_type size_type;
|
||||
typedef typename Base::difference_type difference_type;
|
||||
typedef typename Base::allocator_type allocator_type;
|
||||
|
||||
typedef typename Base::iterator iterator;
|
||||
typedef typename Base::reverse_iterator reverse_iterator;
|
||||
typedef typename Base::const_iterator const_iterator;
|
||||
typedef typename Base::const_reverse_iterator const_reverse_iterator;
|
||||
|
||||
using Base::clear;
|
||||
using Base::empty;
|
||||
using Base::size;
|
||||
using Base::reserve;
|
||||
|
||||
using Base::front;
|
||||
using Base::back;
|
||||
using Base::begin;
|
||||
using Base::end;
|
||||
using Base::rbegin;
|
||||
using Base::rend;
|
||||
|
||||
using Base::push_back;
|
||||
using Base::pop_back;
|
||||
|
||||
// Use the range erase function, the single element erase gets
|
||||
// overloaded.
|
||||
using Base::erase;
|
||||
|
||||
iterator insert(iterator position, const value_type& x);
|
||||
iterator erase(iterator position);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
template <typename _Tp>
|
||||
typename unordered_vector<_Tp>::iterator
|
||||
unordered_vector<_Tp>::insert(iterator position, const value_type& x) {
|
||||
Base::push_back(x);
|
||||
|
||||
return --end();
|
||||
}
|
||||
|
||||
template <typename _Tp>
|
||||
typename unordered_vector<_Tp>::iterator
|
||||
unordered_vector<_Tp>::erase(iterator position) {
|
||||
// We don't need to check if position == end - 1 since we then copy
|
||||
// to the position we pop later.
|
||||
*position = Base::back();
|
||||
Base::pop_back();
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -10,8 +10,8 @@
|
||||
#
|
||||
# Check for baseline language coverage in the compiler for the specified
|
||||
# version of the C++ standard. If necessary, add switches to CXX and
|
||||
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
|
||||
# or '14' (for the C++14 standard).
|
||||
# CXXCPP to enable support. VERSION may be '11', '14', '17', '20', or
|
||||
# '23' for the respective C++ standard version.
|
||||
#
|
||||
# The second argument, if specified, indicates whether you insist on an
|
||||
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
|
||||
@@ -36,13 +36,15 @@
|
||||
# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
|
||||
# Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
|
||||
# Copyright (c) 2020 Jason Merrill <jason@redhat.com>
|
||||
# Copyright (c) 2021, 2024 Jörn Heusipp <osmanx@problemloesungsmaschine.de>
|
||||
# Copyright (c) 2015, 2022, 2023, 2024 Olly Betts
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 12
|
||||
#serial 25
|
||||
|
||||
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
|
||||
dnl (serial version number 13).
|
||||
@@ -51,6 +53,8 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
|
||||
[$1], [14], [ax_cxx_compile_alternatives="14 1y"],
|
||||
[$1], [17], [ax_cxx_compile_alternatives="17 1z"],
|
||||
[$1], [20], [ax_cxx_compile_alternatives="20"],
|
||||
[$1], [23], [ax_cxx_compile_alternatives="23"],
|
||||
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$2], [], [],
|
||||
[$2], [ext], [],
|
||||
@@ -102,9 +106,18 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
dnl HP's aCC needs +std=c++11 according to:
|
||||
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
|
||||
dnl Cray's crayCC needs "-h std=c++11"
|
||||
dnl MSVC needs -std:c++NN for C++17 and later (default is C++14)
|
||||
for alternative in ${ax_cxx_compile_alternatives}; do
|
||||
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}" MSVC; do
|
||||
if test x"$switch" = xMSVC; then
|
||||
dnl AS_TR_SH maps both `:` and `=` to `_` so -std:c++17 would collide
|
||||
dnl with -std=c++17. We suffix the cache variable name with _MSVC to
|
||||
dnl avoid this.
|
||||
switch=-std:c++${alternative}
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_${switch}_MSVC])
|
||||
else
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
fi
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXX="$CXX"
|
||||
@@ -148,23 +161,44 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
dnl Test body for checking C++11 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11]
|
||||
)
|
||||
|
||||
|
||||
dnl Test body for checking C++14 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14]
|
||||
)
|
||||
|
||||
dnl Test body for checking C++17 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
|
||||
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17]
|
||||
)
|
||||
|
||||
dnl Test body for checking C++20 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_20],
|
||||
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_20]
|
||||
)
|
||||
|
||||
dnl Test body for checking C++23 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_23],
|
||||
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_20
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_23]
|
||||
)
|
||||
|
||||
|
||||
dnl Tests for new features in C++11
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
|
||||
@@ -176,7 +210,21 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201103L
|
||||
// MSVC always sets __cplusplus to 199711L in older versions; newer versions
|
||||
// only set it correctly if /Zc:__cplusplus is specified as well as a
|
||||
// /std:c++NN switch:
|
||||
//
|
||||
// https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
|
||||
//
|
||||
// The value __cplusplus ought to have is available in _MSVC_LANG since
|
||||
// Visual Studio 2015 Update 3:
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros
|
||||
//
|
||||
// This was also the first MSVC version to support C++14 so we can't use the
|
||||
// value of either __cplusplus or _MSVC_LANG to quickly rule out MSVC having
|
||||
// C++11 or C++14 support, but we can check _MSVC_LANG for C++17 and later.
|
||||
#elif __cplusplus < 201103L && !defined _MSC_VER
|
||||
|
||||
#error "This is not a C++11 compiler"
|
||||
|
||||
@@ -467,7 +515,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201402L
|
||||
#elif __cplusplus < 201402L && !defined _MSC_VER
|
||||
|
||||
#error "This is not a C++14 compiler"
|
||||
|
||||
@@ -591,7 +639,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201703L
|
||||
#elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 201703L
|
||||
|
||||
#error "This is not a C++17 compiler"
|
||||
|
||||
@@ -957,6 +1005,66 @@ namespace cxx17
|
||||
|
||||
} // namespace cxx17
|
||||
|
||||
#endif // __cplusplus < 201703L
|
||||
#endif // (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 201703L
|
||||
|
||||
]])
|
||||
|
||||
|
||||
dnl Tests for new features in C++20
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_20], [[
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202002L
|
||||
|
||||
#error "This is not a C++20 compiler"
|
||||
|
||||
#else
|
||||
|
||||
#include <version>
|
||||
|
||||
namespace cxx20
|
||||
{
|
||||
|
||||
// As C++20 supports feature test macros in the standard, there is no
|
||||
// immediate need to actually test for feature availability on the
|
||||
// Autoconf side.
|
||||
|
||||
} // namespace cxx20
|
||||
|
||||
#endif // (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202002L
|
||||
|
||||
]])
|
||||
|
||||
|
||||
dnl Tests for new features in C++23
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_23], [[
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202302L
|
||||
|
||||
#error "This is not a C++23 compiler"
|
||||
|
||||
#else
|
||||
|
||||
#include <version>
|
||||
|
||||
namespace cxx23
|
||||
{
|
||||
|
||||
// As C++23 supports feature test macros in the standard, there is no
|
||||
// immediate need to actually test for feature availability on the
|
||||
// Autoconf side.
|
||||
|
||||
} // namespace cxx23
|
||||
|
||||
#endif // (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202302L
|
||||
|
||||
]])
|
||||
|
||||
+83
-34
@@ -19,7 +19,7 @@
|
||||
# header is checked to match the Lua interpreter version exactly. When
|
||||
# searching for Lua libraries, the version number is used as a suffix.
|
||||
# This is done with the goal of supporting multiple Lua installs (5.1,
|
||||
# 5.2, and 5.3 side-by-side).
|
||||
# 5.2, 5.3, and 5.4 side-by-side).
|
||||
#
|
||||
# A note on compatibility with previous versions: This file has been
|
||||
# mostly rewritten for serial 18. Most developers should be able to use
|
||||
@@ -49,6 +49,14 @@
|
||||
# interpreter. If LUA is blank, the user's path is searched for an
|
||||
# suitable interpreter.
|
||||
#
|
||||
# Optionally a LUAJIT option may be set ahead of time to look for and
|
||||
# validate a LuaJIT install instead of PUC Lua. Usage might look like:
|
||||
#
|
||||
# AC_ARG_WITH(luajit, [AS_HELP_STRING([--with-luajit],
|
||||
# [Prefer LuaJIT over PUC Lua, even if the latter is newer. Default: no])
|
||||
# ])
|
||||
# AM_CONDITIONAL([LUAJIT], [test "x$with_luajit" != 'xno'])
|
||||
#
|
||||
# If MINIMUM-VERSION is supplied, then only Lua interpreters with a
|
||||
# version number greater or equal to MINIMUM-VERSION will be accepted. If
|
||||
# TOO-BIG-VERSION is also supplied, then only Lua interpreters with a
|
||||
@@ -82,7 +90,7 @@
|
||||
# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES.
|
||||
#
|
||||
# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is
|
||||
# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT-
|
||||
# performed, otherwise ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-
|
||||
# FOUND is blank, then it will default to printing an error. To prevent
|
||||
# the default behavior, give ':' as an action.
|
||||
#
|
||||
@@ -152,6 +160,7 @@
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2023 Caleb Maclennan <caleb@alerque.com>
|
||||
# Copyright (c) 2015 Reuben Thomas <rrt@sc3d.org>
|
||||
# Copyright (c) 2014 Tim Perkins <tprk77@gmail.com>
|
||||
#
|
||||
@@ -181,7 +190,7 @@
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 42
|
||||
#serial 47
|
||||
|
||||
dnl =========================================================================
|
||||
dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION],
|
||||
@@ -197,13 +206,14 @@ AC_DEFUN([AX_PROG_LUA],
|
||||
AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1])
|
||||
|
||||
dnl Find a Lua interpreter.
|
||||
m4_define_default([_AX_LUA_INTERPRETER_LIST],
|
||||
[lua lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua50])
|
||||
AM_COND_IF([LUAJIT],
|
||||
[_ax_lua_interpreter_list='luajit luajit-2.1.0-beta3 luajit-2.0.5 luajit-2.0.4 luajit-2.0.3'],
|
||||
[_ax_lua_interpreter_list='lua lua5.4 lua54 lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua5.0 lua50'])
|
||||
|
||||
m4_if([$1], [],
|
||||
[ dnl No version check is needed. Find any Lua interpreter.
|
||||
AS_IF([test "x$LUA" = 'x'],
|
||||
[AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])])
|
||||
[AC_PATH_PROGS([LUA], [$_ax_lua_interpreter_list], [:])])
|
||||
ax_display_LUA='lua'
|
||||
|
||||
AS_IF([test "x$LUA" != 'x:'],
|
||||
@@ -242,7 +252,7 @@ AC_DEFUN([AX_PROG_LUA],
|
||||
[_ax_check_text="for a Lua interpreter with version >= $1, < $2"])
|
||||
AC_CACHE_CHECK([$_ax_check_text],
|
||||
[ax_cv_pathless_LUA],
|
||||
[ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do
|
||||
[ for ax_cv_pathless_LUA in $_ax_lua_interpreter_list none; do
|
||||
test "x$ax_cv_pathless_LUA" = 'xnone' && break
|
||||
_AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue])
|
||||
_AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break])
|
||||
@@ -268,13 +278,25 @@ AC_DEFUN([AX_PROG_LUA],
|
||||
ax_cv_lua_version=[`$LUA -e '
|
||||
-- return a version number in X.Y format
|
||||
local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)")
|
||||
print(ver)'`]
|
||||
print(ver or "")'`]
|
||||
])
|
||||
AS_IF([test "x$ax_cv_lua_version" = 'x'],
|
||||
[AC_MSG_ERROR([invalid Lua version number])])
|
||||
AC_SUBST([LUA_VERSION], [$ax_cv_lua_version])
|
||||
AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`])
|
||||
|
||||
AM_COND_IF([LUAJIT], [
|
||||
AC_CACHE_CHECK([for $ax_display_LUA jit version], [ax_cv_luajit_version],
|
||||
[ ax_cv_luajit_version=[`$LUA -e '
|
||||
local _, _, ver = string.find(jit and jit.version, "(%d+%..+)")
|
||||
print(ver or "")'`]
|
||||
])
|
||||
AS_IF([test "x$ax_cv_luajit_version" = 'x'],
|
||||
[AC_MSG_ERROR([invalid Lua jit version number])])
|
||||
AC_SUBST([LUAJIT_VERSION], [$ax_cv_luajit_version])
|
||||
AC_SUBST([LUAJIT_SHORT_VERSION], [$(echo "$LUAJIT_VERSION" | $SED 's|\.|§|;s|\..*||;s|§|.|')])
|
||||
])
|
||||
|
||||
dnl The following check is not supported:
|
||||
dnl At times (like when building shared libraries) you may want to know
|
||||
dnl which OS platform Lua thinks this is.
|
||||
@@ -464,33 +486,50 @@ AC_DEFUN([AX_LUA_HEADERS],
|
||||
AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION])
|
||||
])
|
||||
|
||||
AM_COND_IF([LUAJIT],[
|
||||
dnl Check for LUAJIT_VERSION.
|
||||
AC_MSG_CHECKING([if LUAJIT_VERSION is defined])
|
||||
AS_IF([test "x$LUAJIT_VERSION" != 'x'],
|
||||
[AC_MSG_RESULT([yes])],
|
||||
[ AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR([cannot check Lua jit headers without knowing LUAJIT_VERSION])
|
||||
])
|
||||
])
|
||||
|
||||
dnl Make LUA_INCLUDE a precious variable.
|
||||
AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1])
|
||||
|
||||
dnl Some default directories to search.
|
||||
LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'`
|
||||
m4_define_default([_AX_LUA_INCLUDE_LIST],
|
||||
[ /usr/include/lua$LUA_VERSION \
|
||||
/usr/include/lua-$LUA_VERSION \
|
||||
/usr/include/lua/$LUA_VERSION \
|
||||
/usr/include/lua$LUA_SHORT_VERSION \
|
||||
/usr/local/include/lua$LUA_VERSION \
|
||||
/usr/local/include/lua-$LUA_VERSION \
|
||||
/usr/local/include/lua/$LUA_VERSION \
|
||||
/usr/local/include/lua$LUA_SHORT_VERSION \
|
||||
])
|
||||
dnl Some default directories to search.
|
||||
AM_COND_IF([LUAJIT],
|
||||
[_ax_lua_include_list="
|
||||
/usr/include/luajit-$LUAJIT_VERSION
|
||||
/usr/include/luajit-$LUAJIT_SHORT_VERSION
|
||||
/usr/local/include/luajit-$LUAJIT_VERSION
|
||||
/usr/local/include/luajit-$LUAJIT_SHORT_VERSION"],
|
||||
[_ax_lua_include_list="
|
||||
/usr/include/lua$LUA_VERSION
|
||||
/usr/include/lua-$LUA_VERSION
|
||||
/usr/include/lua/$LUA_VERSION
|
||||
/usr/include/lua$LUA_SHORT_VERSION
|
||||
/usr/local/include/lua$LUA_VERSION
|
||||
/usr/local/include/lua-$LUA_VERSION
|
||||
/usr/local/include/lua/$LUA_VERSION
|
||||
/usr/local/include/lua$LUA_SHORT_VERSION"])
|
||||
|
||||
dnl Try to find the headers.
|
||||
_ax_lua_saved_cppflags=$CPPFLAGS
|
||||
CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
|
||||
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
|
||||
AM_COND_IF([LUAJIT], [AC_CHECK_HEADERS([luajit.h])])
|
||||
CPPFLAGS=$_ax_lua_saved_cppflags
|
||||
|
||||
dnl Try some other directories if LUA_INCLUDE was not set.
|
||||
AS_IF([test "x$LUA_INCLUDE" = 'x' &&
|
||||
test "x$ac_cv_header_lua_h" != 'xyes'],
|
||||
test "x$ac_cv_header_lua_h" != 'xyes' ||
|
||||
test "x$with_luajit" != 'xno' &&
|
||||
test "x$ac_cv_header_luajit_h" != 'xyes'],
|
||||
[ dnl Try some common include paths.
|
||||
for _ax_include_path in _AX_LUA_INCLUDE_LIST; do
|
||||
for _ax_include_path in $_ax_lua_include_list; do
|
||||
test ! -d "$_ax_include_path" && continue
|
||||
|
||||
AC_MSG_CHECKING([for Lua headers in])
|
||||
@@ -500,10 +539,12 @@ AC_DEFUN([AX_LUA_HEADERS],
|
||||
AS_UNSET([ac_cv_header_lualib_h])
|
||||
AS_UNSET([ac_cv_header_lauxlib_h])
|
||||
AS_UNSET([ac_cv_header_luaconf_h])
|
||||
AS_UNSET([ac_cv_header_luajit_h])
|
||||
|
||||
_ax_lua_saved_cppflags=$CPPFLAGS
|
||||
CPPFLAGS="$CPPFLAGS -I$_ax_include_path"
|
||||
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
|
||||
AM_COND_IF([LUAJIT], [AC_CHECK_HEADERS([luajit.h])])
|
||||
CPPFLAGS=$_ax_lua_saved_cppflags
|
||||
|
||||
AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
|
||||
@@ -621,18 +662,26 @@ AC_DEFUN([AX_LUA_LIBS],
|
||||
dnl Try to find the Lua libs.
|
||||
_ax_lua_saved_libs=$LIBS
|
||||
LIBS="$LIBS $LUA_LIB"
|
||||
AC_SEARCH_LIBS([lua_load],
|
||||
[ lua$LUA_VERSION \
|
||||
lua$LUA_SHORT_VERSION \
|
||||
lua-$LUA_VERSION \
|
||||
lua-$LUA_SHORT_VERSION \
|
||||
:liblua-$LUA_VERSION.so.0 \
|
||||
:liblua-$LUA_SHORT_VERSION.so.0 \
|
||||
lua \
|
||||
],
|
||||
[_ax_found_lua_libs='yes'],
|
||||
[_ax_found_lua_libs='no'],
|
||||
[$_ax_lua_extra_libs])
|
||||
AM_COND_IF([LUAJIT],
|
||||
[AC_SEARCH_LIBS([lua_load],
|
||||
[ luajit$LUA_VERSION \
|
||||
luajit$LUA_SHORT_VERSION \
|
||||
luajit-$LUA_VERSION \
|
||||
luajit-$LUA_SHORT_VERSION \
|
||||
luajit],
|
||||
[_ax_found_lua_libs='yes'],
|
||||
[_ax_found_lua_libs='no'],
|
||||
[$_ax_lua_extra_libs])],
|
||||
[AC_SEARCH_LIBS([lua_load],
|
||||
[ lua$LUA_VERSION \
|
||||
lua$LUA_SHORT_VERSION \
|
||||
lua-$LUA_VERSION \
|
||||
lua-$LUA_SHORT_VERSION \
|
||||
lua \
|
||||
],
|
||||
[_ax_found_lua_libs='yes'],
|
||||
[_ax_found_lua_libs='no'],
|
||||
[$_ax_lua_extra_libs])])
|
||||
LIBS=$_ax_lua_saved_libs
|
||||
|
||||
AS_IF([test "x$ac_cv_search_lua_load" != 'xno' &&
|
||||
|
||||
+100
-127
@@ -1,48 +1,3 @@
|
||||
AC_DEFUN([TORRENT_CHECK_XFS], [
|
||||
AC_MSG_CHECKING(for XFS support)
|
||||
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||
#include <xfs/libxfs.h>
|
||||
#include <sys/ioctl.h>
|
||||
int main() {
|
||||
struct xfs_flock64 l;
|
||||
ioctl(0, XFS_IOC_RESVSP64, &l);
|
||||
return 0;
|
||||
}
|
||||
])],
|
||||
[
|
||||
AC_DEFINE(USE_XFS, 1, Use XFS filesystem stuff.)
|
||||
AC_MSG_RESULT(yes)
|
||||
], [
|
||||
AC_MSG_RESULT(no)
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_WITHOUT_XFS], [
|
||||
AC_ARG_WITH(xfs,
|
||||
AS_HELP_STRING([--without-xfs],[do not check for XFS filesystem support]),
|
||||
[
|
||||
if test "$withval" = "yes"; then
|
||||
TORRENT_CHECK_XFS
|
||||
fi
|
||||
], [
|
||||
TORRENT_CHECK_XFS
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_WITH_XFS], [
|
||||
AC_ARG_WITH(xfs,
|
||||
AS_HELP_STRING([--with-xfs],[check for XFS filesystem support]),
|
||||
[
|
||||
if test "$withval" = "yes"; then
|
||||
TORRENT_CHECK_XFS
|
||||
fi
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_CHECK_EPOLL], [
|
||||
AC_MSG_CHECKING(for epoll support)
|
||||
|
||||
@@ -54,6 +9,8 @@ AC_DEFUN([TORRENT_CHECK_EPOLL], [
|
||||
}
|
||||
])],
|
||||
[
|
||||
use_epoll=yes
|
||||
|
||||
AC_DEFINE(USE_EPOLL, 1, Use epoll.)
|
||||
AC_MSG_RESULT(yes)
|
||||
], [
|
||||
@@ -86,6 +43,8 @@ AC_DEFUN([TORRENT_CHECK_KQUEUE], [
|
||||
}
|
||||
])],
|
||||
[
|
||||
use_kqueue=yes
|
||||
|
||||
AC_DEFINE(USE_KQUEUE, 1, Use kqueue.)
|
||||
AC_MSG_RESULT(yes)
|
||||
], [
|
||||
@@ -118,48 +77,9 @@ AC_DEFUN([TORRENT_WITHOUT_KQUEUE], [
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_CHECK_FALLOCATE], [
|
||||
AC_MSG_CHECKING(for fallocate)
|
||||
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
]], [[ fallocate(0, FALLOC_FL_KEEP_SIZE, 0, 0); return 0;
|
||||
]])],[
|
||||
AC_DEFINE(HAVE_FALLOCATE, 1, Linux's fallocate supported.)
|
||||
AC_MSG_RESULT(yes)
|
||||
],[
|
||||
AC_MSG_RESULT(no)
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_CHECK_POSIX_FALLOCATE], [
|
||||
AC_MSG_CHECKING(for posix_fallocate)
|
||||
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <fcntl.h>
|
||||
]], [[ posix_fallocate(0, 0, 0);
|
||||
]])],[
|
||||
AC_DEFINE(USE_POSIX_FALLOCATE, 1, posix_fallocate supported.)
|
||||
AC_MSG_RESULT(yes)
|
||||
],[
|
||||
AC_MSG_RESULT(no)
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_WITH_POSIX_FALLOCATE], [
|
||||
AC_ARG_WITH(posix-fallocate,
|
||||
AS_HELP_STRING([--with-posix-fallocate],[check for and use posix_fallocate to allocate files]),
|
||||
[
|
||||
if test "$withval" = "yes"; then
|
||||
TORRENT_CHECK_POSIX_FALLOCATE
|
||||
fi
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [
|
||||
AC_ARG_WITH(address-space,
|
||||
AS_HELP_STRING([--with-address-space=MB],[change the default address space size [[default=1024mb]]]),
|
||||
AS_HELP_STRING([--with-address-space=MB],[change the default address space size [default=1024mb-or-32768mb]]),
|
||||
[
|
||||
if test ! -z $withval -a "$withval" != "yes" -a "$withval" != "no"; then
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_ADDRESS_SPACE_SIZE, [$withval])
|
||||
@@ -171,51 +91,48 @@ AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [
|
||||
AC_CHECK_SIZEOF(long)
|
||||
|
||||
if test $ac_cv_sizeof_long = 8; then
|
||||
AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 4096, Default address space size.)
|
||||
AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 32768, Default address space size.)
|
||||
else
|
||||
AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 1024, Default address space size.)
|
||||
fi
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([TORRENT_WITH_FASTCGI], [
|
||||
AC_ARG_WITH(fastcgi,
|
||||
AS_HELP_STRING([--with-fastcgi=PATH],[enable FastCGI RPC support (DO NOT USE)]),
|
||||
|
||||
AC_DEFUN([TORRENT_CHECK_ATOMIC], [
|
||||
AC_MSG_CHECKING([whether 64-bit atomic operations require -latomic])
|
||||
AC_LANG_PUSH(C++)
|
||||
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <cstdint>
|
||||
#include <atomic>]],
|
||||
[[std::atomic<uint64_t> x(0);
|
||||
return x.load();]])],
|
||||
[
|
||||
AC_MSG_CHECKING([for FastCGI (DO NOT USE)])
|
||||
AC_MSG_RESULT([no])
|
||||
ATOMIC_LIBS=""
|
||||
],
|
||||
[
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$LIBS -latomic"
|
||||
|
||||
if test "$withval" = "no"; then
|
||||
AC_MSG_RESULT(no)
|
||||
|
||||
elif test "$withval" = "yes"; then
|
||||
CXXFLAGS="$CXXFLAGS"
|
||||
LIBS="$LIBS -lfcgi"
|
||||
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <fcgiapp.h>
|
||||
]], [[ FCGX_Init(); ]])],[
|
||||
AC_MSG_RESULT(ok)
|
||||
],[
|
||||
AC_MSG_RESULT(not found)
|
||||
AC_MSG_ERROR(Could not compile FastCGI test.)
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <cstdint>
|
||||
#include <atomic>]],
|
||||
[[std::atomic<uint64_t> x(0);
|
||||
return x.load();]])],
|
||||
[
|
||||
AC_MSG_RESULT([yes])
|
||||
ATOMIC_LIBS="-latomic"
|
||||
],
|
||||
[
|
||||
AC_MSG_RESULT([unsupported])
|
||||
AC_MSG_ERROR([Compiler target lacks proper 64-bit atomic support.])
|
||||
])
|
||||
|
||||
AC_DEFINE(HAVE_FASTCGI, 1, Support for FastCGI.)
|
||||
|
||||
else
|
||||
CXXFLAGS="$CXXFLAGS -I$withval/include"
|
||||
LIBS="$LIBS -lfcgi -L$withval/lib"
|
||||
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <fcgiapp.h>
|
||||
]], [[ FCGX_Init(); ]])],[
|
||||
AC_MSG_RESULT(ok)
|
||||
],[
|
||||
AC_MSG_RESULT(not found)
|
||||
AC_MSG_ERROR(Could not compile FastCGI test.)
|
||||
])
|
||||
|
||||
AC_DEFINE(HAVE_FASTCGI, 1, Support for FastCGI.)
|
||||
fi
|
||||
LIBS="$save_LIBS"
|
||||
])
|
||||
|
||||
AC_LANG_POP(C++)
|
||||
AC_SUBST([ATOMIC_LIBS])
|
||||
])
|
||||
|
||||
|
||||
@@ -260,6 +177,7 @@ AC_DEFUN([TORRENT_WITH_XMLRPC_C], [
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_WITH_TINYXML2], [
|
||||
AC_MSG_CHECKING(for tinyxml2)
|
||||
|
||||
@@ -273,6 +191,7 @@ AC_DEFUN([TORRENT_WITH_TINYXML2], [
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_WITH_LUA], [
|
||||
AC_ARG_WITH(lua,
|
||||
AS_HELP_STRING([--with-lua],[enable LUA support]),
|
||||
@@ -281,29 +200,45 @@ AC_DEFUN([TORRENT_WITH_LUA], [
|
||||
AC_MSG_RESULT(no)
|
||||
else
|
||||
AX_PROG_LUA
|
||||
AX_LUA_LIBS
|
||||
AX_LUA_HEADERS
|
||||
AC_DEFINE(HAVE_LUA, 1, Use LUA.)
|
||||
AC_DEFINE(LUA_DATADIR, [PACKAGE_DATADIR "/lua"], [LUA data directory])
|
||||
LIBS="$LIBS $LUA_LIB"
|
||||
CXXFLAGS="$CXXFLAGS $LUA_INCLUDE"
|
||||
|
||||
# 1. Override AX_LUA_LIBS default crash behavior
|
||||
AX_LUA_LIBS([have_lua_libs=yes], [have_lua_libs=no])
|
||||
|
||||
# 2. Override AX_LUA_HEADERS default crash behavior
|
||||
AX_LUA_HEADERS([have_lua_headers=yes], [have_lua_headers=no])
|
||||
|
||||
# 3. Only inject if both checks completely pass
|
||||
if test "x$have_lua_libs" = "xyes" && test "x$have_lua_headers" = "xyes"; then
|
||||
AC_DEFINE(HAVE_LUA, 1, Use LUA.)
|
||||
AC_DEFINE(LUA_DATADIR, [PACKAGE_DATADIR "/lua"], [LUA data directory])
|
||||
LIBS="$LIBS $LUA_LIB"
|
||||
CXXFLAGS="$CXXFLAGS $LUA_INCLUDE"
|
||||
else
|
||||
# Throw fatal error ONLY if user strictly ran --with-lua=yes
|
||||
if test "$withval" = "yes"; then
|
||||
AC_MSG_ERROR([Lua support explicitly requested, but compatible Lua 5.3 libs/headers were not found.])
|
||||
else
|
||||
AC_MSG_WARN([Lua 5.3 libs or headers missing. Proceeding without Lua support.])
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
],[
|
||||
AC_MSG_RESULT(ignored)
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_WITH_INOTIFY], [
|
||||
AC_LANG_PUSH(C++)
|
||||
|
||||
AC_CHECK_HEADERS([sys/inotify.h mcheck.h])
|
||||
AC_CHECK_HEADERS([sys/inotify.h])
|
||||
AC_MSG_CHECKING([whether sys/inotify.h actually works])
|
||||
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||
#include <sys/inotify.h>
|
||||
int main(int,const char**) { return (-1 == inotify_init()); }])
|
||||
],[
|
||||
AC_DEFINE(HAVE_INOTIFY, 1, [sys/inotify.h exists and works correctly])
|
||||
AC_DEFINE(USE_INOTIFY, 1, [sys/inotify.h exists and works correctly])
|
||||
AC_MSG_RESULT(yes)],
|
||||
[AC_MSG_RESULT(failed)]
|
||||
)
|
||||
@@ -311,12 +246,14 @@ AC_DEFUN([TORRENT_WITH_INOTIFY], [
|
||||
AC_LANG_POP(C++)
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [
|
||||
AC_CHECK_HEADERS(pthread.h)
|
||||
|
||||
AC_MSG_CHECKING(for pthread_setname_np type)
|
||||
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#define _GNU_SOURCE
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
]], [[
|
||||
@@ -341,6 +278,7 @@ AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [
|
||||
AC_MSG_CHECKING([for pthread_setname_no])
|
||||
|
||||
@@ -360,6 +298,24 @@ AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_CHECK_POSIX_SPAWN_ADDCLOSEFROM_NP], [
|
||||
AC_MSG_CHECKING(for posix_spawn_file_actions_addclosefrom_np)
|
||||
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#define _GNU_SOURCE
|
||||
#include <spawn.h>
|
||||
]], [[
|
||||
posix_spawn_file_actions_t actions;
|
||||
posix_spawn_file_actions_addclosefrom_np(&actions, 3);
|
||||
]])],[
|
||||
AC_DEFINE(HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP, 1, [Define if posix_spawn_file_actions_addclosefrom_np is available.])
|
||||
AC_MSG_RESULT(yes)
|
||||
],[
|
||||
AC_MSG_RESULT(no)
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_WITH_SYSTEMD], [
|
||||
AC_ARG_WITH(systemd,
|
||||
AS_HELP_STRING([--with-systemd],[enable systemd socket activation support [[default=no]]]),
|
||||
@@ -375,3 +331,20 @@ AC_DEFUN([TORRENT_WITH_SYSTEMD], [
|
||||
fi
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_WITHOUT_NCURSES], [
|
||||
AC_ARG_WITH([ncurses],
|
||||
[AS_HELP_STRING([--without-ncurses], [build without ncurses (daemon-only mode)])],
|
||||
[with_ncurses=$withval],
|
||||
[with_ncurses=yes])
|
||||
|
||||
if test "x$with_ncurses" = xno; then
|
||||
AC_DEFINE([HAVE_NO_NCURSES], [1], [Define to 1 if building without ncurses])
|
||||
CURSES_LIBS=""
|
||||
CURSES_CFLAGS=""
|
||||
CURSES_LIB=""
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([NO_NCURSES], [test "x$with_ncurses" = xno])
|
||||
])
|
||||
|
||||
+169
-145
@@ -1,67 +1,3 @@
|
||||
AC_DEFUN([TORRENT_WITH_SYSROOT], [
|
||||
AC_ARG_WITH(sysroot,
|
||||
AS_HELP_STRING([--with-sysroot=PATH],
|
||||
[compile and link with a specific sysroot]),
|
||||
[
|
||||
AC_MSG_CHECKING(for sysroot)
|
||||
|
||||
if test "$withval" = "no"; then
|
||||
AC_MSG_RESULT(no)
|
||||
|
||||
elif test "$withval" = "yes"; then
|
||||
AC_MSG_RESULT(not a path)
|
||||
AC_MSG_ERROR(The sysroot option must point to a directory, like f.ex "/Developer/SDKs/MacOSX10.4u.sdk".)
|
||||
else
|
||||
AC_MSG_RESULT($withval)
|
||||
|
||||
CXXFLAGS="$CXXFLAGS -isysroot $withval"
|
||||
LDFLAGS="$LDFLAGS -Wl,-syslibroot,$withval"
|
||||
fi
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_REMOVE_UNWANTED],
|
||||
[
|
||||
AC_REQUIRE([AC_PROG_GREP])
|
||||
values_to_check=`for i in $2; do echo $i; done`
|
||||
unwanted_values=`for i in $3; do echo $i; done`
|
||||
if test -z "${unwanted_values}"; then
|
||||
$1="$2"
|
||||
else
|
||||
result=`echo "${values_to_check}" | $GREP -Fvx -- "${unwanted_values}" | $GREP -v '^$'`
|
||||
# join with spaces, squeeze repeats, and trim trailing space
|
||||
$1=$(printf '%s\n' "$result" | tr '\n' ' ' | sed 's/ */ /g; s/ *$//')
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_ENABLE_ARCH], [
|
||||
AC_ARG_ENABLE(arch,
|
||||
AS_HELP_STRING([--enable-arch=ARCH],
|
||||
[comma seprated list of architectures to compile for]),
|
||||
[
|
||||
AC_MSG_CHECKING(for target architectures)
|
||||
|
||||
if test "$enableval" = "yes"; then
|
||||
AC_MSG_ERROR(no arch supplied)
|
||||
|
||||
elif test "$enableval" = "no"; then
|
||||
AC_MSG_RESULT(using default)
|
||||
|
||||
else
|
||||
AC_MSG_RESULT($enableval)
|
||||
|
||||
for i in `IFS=,; echo $enableval`; do
|
||||
CFLAGS="$CFLAGS -march=$i"
|
||||
CXXFLAGS="$CXXFLAGS -march=$i"
|
||||
LDFLAGS="$LDFLAGS -march=$i"
|
||||
done
|
||||
fi
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_MSG_CHECKING(signedness of mincore parameter)
|
||||
@@ -90,7 +26,7 @@ AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [
|
||||
AC_MSG_RESULT(signed)
|
||||
],
|
||||
[
|
||||
AC_MSG_ERROR([failed, do *not* attempt fix this with --disable-mincore unless you are running Win32.])
|
||||
AC_MSG_ERROR([failed, do *not* attempt fix this with --disable-mincore unless you are running Win32 or OpenBSD.])
|
||||
])
|
||||
])
|
||||
|
||||
@@ -144,108 +80,196 @@ AC_DEFUN([TORRENT_CHECK_POSIX_FADVISE], [
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([TORRENT_CHECK_POPCOUNT], [
|
||||
AC_MSG_CHECKING(for __builtin_popcount)
|
||||
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||
int f() { return __builtin_popcount(0); }
|
||||
])],
|
||||
[
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(USE_BUILTIN_POPCOUNT, 1, Use __builtin_popcount.)
|
||||
], [
|
||||
AC_MSG_RESULT(no)
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([TORRENT_CHECK_CACHELINE], [
|
||||
AC_MSG_CHECKING(for cacheline)
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
AC_MSG_CHECKING([for target cacheline size])
|
||||
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||
#include <stdlib.h>
|
||||
#include <linux/cache.h>
|
||||
void* vptr __cacheline_aligned;
|
||||
void f() { posix_memalign(&vptr, SMP_CACHE_BYTES, 42); }
|
||||
])],
|
||||
[
|
||||
AC_MSG_RESULT(found builtin)
|
||||
dnl Need to fix this so that it uses the stuff defined by the system.
|
||||
case "$host_os" in
|
||||
linux*)
|
||||
# REGION: Linux Kernel Extraction Loop
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
|
||||
#include <stdlib.h>
|
||||
#include <linux/cache.h>
|
||||
void* vptr;
|
||||
void f() {
|
||||
int res = posix_memalign(&vptr, SMP_CACHE_BYTES, 42);
|
||||
(void)res;
|
||||
}
|
||||
]])],[
|
||||
# We need an explicit variable fallback condition inside AC_COMPUTE_INT
|
||||
AC_COMPUTE_INT([torrent_cv_cacheline_size], [SMP_CACHE_BYTES], [#include <linux/cache.h>], [torrent_cv_cacheline_size=0])
|
||||
|
||||
AC_DEFINE(LT_SMP_CACHE_BYTES, 128, Largest L1 cache size we know of should work on all archs.)
|
||||
], [
|
||||
AC_MSG_RESULT(using default 128 bytes)
|
||||
AC_DEFINE(LT_SMP_CACHE_BYTES, 128, Largest L1 cache size we know of should work on all archs.)
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([TORRENT_CHECK_ALIGNED], [
|
||||
AC_MSG_CHECKING(the byte alignment)
|
||||
|
||||
AC_RUN_IFELSE([AC_LANG_SOURCE([
|
||||
#include <inttypes.h>
|
||||
int main() {
|
||||
char buf@<:@8@:>@ = { 0, 0, 0, 0, 1, 0, 0, 0 };
|
||||
int i;
|
||||
for (i = 1; i < 4; ++i)
|
||||
if (*(uint32_t*)(buf + i) == 0) return -1;
|
||||
return 0;
|
||||
}
|
||||
])],
|
||||
[
|
||||
AC_MSG_RESULT(none needed)
|
||||
], [
|
||||
AC_DEFINE(USE_ALIGNED, 1, Require byte alignment)
|
||||
AC_MSG_RESULT(required)
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_ENABLE_ALIGNED], [
|
||||
AC_ARG_ENABLE(aligned,
|
||||
AS_HELP_STRING([--enable-aligned],
|
||||
[enable alignment safe code [[default=check]]]),
|
||||
[
|
||||
if test "$enableval" = "yes"; then
|
||||
AC_DEFINE(USE_ALIGNED, 1, Require byte alignment)
|
||||
if test "$torrent_cv_cacheline_size" -gt 0; then
|
||||
AC_MSG_RESULT([linux builtin ($torrent_cv_cacheline_size bytes)])
|
||||
AC_DEFINE_UNQUOTED([LT_SMP_CACHE_BYTES], [$torrent_cv_cacheline_size], [System-defined Linux L1 SMP cacheline size.])
|
||||
else
|
||||
# Handle scenarios where macro maps to a complex runtime expression or fails
|
||||
AC_MSG_RESULT([failed to parse SMP_CACHE_BYTES value])
|
||||
AC_MSG_FAILURE([Linux kernel headers found, but cacheline constant could not be computed at compile-time.])
|
||||
fi
|
||||
],[
|
||||
TORRENT_CHECK_ALIGNED
|
||||
])
|
||||
],[
|
||||
# Explicitly validate the CPU type even on Linux if the header check fails
|
||||
case "$host_cpu" in
|
||||
x86_64*|amd64*|i386*|i486*|i586*|i686*)
|
||||
AC_MSG_RESULT([linux fallback x86 64 bytes])
|
||||
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux x86 hardware.])
|
||||
;;
|
||||
arm*|aarch64*|powerpc*|ppc*|s390x*)
|
||||
AC_MSG_RESULT([linux fallback enterprise 128 bytes])
|
||||
AC_DEFINE([LT_SMP_CACHE_BYTES], 128, [Fallback 128-byte alignment for Linux enterprise hardware.])
|
||||
;;
|
||||
riscv32*|riscv64*)
|
||||
AC_MSG_RESULT([linux fallback RISC-V 64 bytes])
|
||||
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux RISC-V hardware.])
|
||||
;;
|
||||
loongarch32*|loongarch64*)
|
||||
AC_MSG_RESULT([linux fallback LoongArch 64 bytes])
|
||||
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux LoongArch hardware.])
|
||||
;;
|
||||
*)
|
||||
AC_MSG_RESULT([unrecognized CPU arch on Linux header fallback])
|
||||
AC_MSG_FAILURE([Unrecognized CPU architecture ($host_cpu) on Linux fallback path. Aborting build.])
|
||||
;;
|
||||
esac
|
||||
])
|
||||
;;
|
||||
|
||||
*)
|
||||
# REGION: Cross-Platform Strict Hardware Mapping (macOS, FreeBSD, OpenBSD, NetBSD)
|
||||
case "$host_cpu" in
|
||||
x86_64*|amd64*|i386*|i486*|i586*|i686*)
|
||||
# Explicit x86 desktop hardware baseline block
|
||||
AC_MSG_RESULT([$host_os ($host_cpu) standard x86 64 bytes])
|
||||
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Standard 64-byte alignment for stable x86 hardware layout.])
|
||||
;;
|
||||
arm*|aarch64*|powerpc*|ppc*|s390x*)
|
||||
# Explicit modern enterprise and Apple Silicon hardware baseline block
|
||||
AC_MSG_RESULT([$host_os ($host_cpu) stable enterprise 128 bytes])
|
||||
AC_DEFINE([LT_SMP_CACHE_BYTES], 128, [Optimized 128-byte alignment for newer high-performance chipsets.])
|
||||
;;
|
||||
*)
|
||||
# STRICT ENFORCEMENT: Fail the build immediately if the CPU isn't explicitly known
|
||||
AC_MSG_RESULT([unrecognized architecture])
|
||||
AC_MSG_FAILURE([The target CPU architecture ($host_cpu) is unrecognized. Aborting configuration to prevent fatal runtime false-sharing or memory misalignment errors.])
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_DISABLE_INSTRUMENTATION], [
|
||||
AC_DEFUN([TORRENT_CHECK_CUSTOM_ENDIAN64], [
|
||||
AC_CHECK_HEADERS([endian.h sys/endian.h libkern/OSByteOrder.h])
|
||||
AC_C_BIGENDIAN
|
||||
|
||||
AH_TEMPLATE([CUSTOM_ENDIAN_HEADER], [The system header to include for endian conversions.])
|
||||
AH_TEMPLATE([custom_ntohll], [Convert 64-bit integer from network to host byte order.])
|
||||
AH_TEMPLATE([custom_htonll], [Convert 64-bit integer from host to network byte order.])
|
||||
|
||||
dnl 1. Initialize our header variable tracking
|
||||
endian_header_file='<stdint.h>'
|
||||
|
||||
dnl 2. Test if native be64toh works natively via <endian.h> (Standard Linux/glibc)
|
||||
AC_MSG_CHECKING([for native be64toh via endian.h])
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([
|
||||
#include <stdint.h>
|
||||
#ifdef HAVE_ENDIAN_H
|
||||
# include <endian.h>
|
||||
#endif
|
||||
int main() { uint64_t x = be64toh(1); return 0; }
|
||||
])],
|
||||
[has_native_be64=yes; endian_header_file='<endian.h>'],
|
||||
[has_native_be64=no]
|
||||
)
|
||||
AC_MSG_RESULT([$has_native_be64])
|
||||
|
||||
dnl 3. If missing, test if it works via <sys/endian.h> (True BSD systems like FreeBSD)
|
||||
if test "$has_native_be64" = "no"; then
|
||||
AC_MSG_CHECKING([for native be64toh via sys/endian.h])
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([
|
||||
#include <stdint.h>
|
||||
#ifdef HAVE_SYS_ENDIAN_H
|
||||
# include <sys/endian.h>
|
||||
#endif
|
||||
int main() { uint64_t x = be64toh(1); return 0; }
|
||||
])],
|
||||
[has_native_be64=yes; endian_header_file='<sys/endian.h>'],
|
||||
[has_native_be64=no]
|
||||
)
|
||||
AC_MSG_RESULT([$has_native_be64])
|
||||
fi
|
||||
|
||||
dnl 4. Route definitions cleanly based on actual testing results
|
||||
if test "$has_native_be64" = "yes"; then
|
||||
dnl Linux or true BSD environment
|
||||
AC_DEFINE_UNQUOTED([CUSTOM_ENDIAN_HEADER], [$endian_header_file], [The header containing native endian macros.])
|
||||
AC_DEFINE([custom_ntohll(x)], [(be64toh(x))], [Use native platform be64toh])
|
||||
AC_DEFINE([custom_htonll(x)], [(htobe64(x))], [Use native platform htobe64])
|
||||
else
|
||||
dnl Fallback block: Check if we are on macOS using Apple's optimized hardware libraries
|
||||
if test "$ac_cv_header_libkern_OSByteOrder_h" = "yes"; then
|
||||
AC_DEFINE([CUSTOM_ENDIAN_HEADER], [<libkern/OSByteOrder.h>], [The header containing native endian macros.])
|
||||
AC_DEFINE([custom_ntohll(x)], [(OSSwapBigToHostInt64(x))], [Map missing be64toh to macOS native swap])
|
||||
AC_DEFINE([custom_htonll(x)], [(OSSwapHostToBigInt64(x))], [Map missing htobe64 to macOS native swap])
|
||||
else
|
||||
dnl Fallback for platforms with no built-in architecture macros (compiler built-ins)
|
||||
AC_DEFINE([CUSTOM_ENDIAN_HEADER], [<stdint.h>], [The header containing native endian macros.])
|
||||
if test "$ac_cv_c_bigendian" = "yes"; then
|
||||
AC_DEFINE([custom_ntohll(x)], [((uint64_t)(x))], [Fallback 64-bit conversion.])
|
||||
AC_DEFINE([custom_htonll(x)], [((uint64_t)(x))], [Fallback 64-bit conversion.])
|
||||
else
|
||||
AC_DEFINE([custom_ntohll(x)], [(__builtin_bswap64((uint64_t)(x)))], [Fallback 64-bit conversion.])
|
||||
AC_DEFINE([custom_htonll(x)], [(__builtin_bswap64((uint64_t)(x)))], [Fallback 64-bit conversion.])
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_ENABLE_INSTRUMENTATION], [
|
||||
AC_MSG_CHECKING([if instrumentation should be included])
|
||||
|
||||
AC_ARG_ENABLE(instrumentation,
|
||||
AS_HELP_STRING([--disable-instrumentation],
|
||||
[disable instrumentation [[default=enabled]]]),
|
||||
AS_HELP_STRING([--enable-instrumentation],
|
||||
[enable instrumentation [[default=disabled]]]),
|
||||
[
|
||||
if test "$enableval" = "yes"; then
|
||||
AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation)
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
],[
|
||||
AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation)
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_MSG_RESULT(no)
|
||||
])
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_ENABLE_INTERRUPT_SOCKET], [
|
||||
AC_ARG_ENABLE(interrupt-socket,
|
||||
AS_HELP_STRING([--enable-interrupt-socket],
|
||||
[enable interrupt socket [[default=no]]]),
|
||||
[
|
||||
if test "$enableval" = "yes"; then
|
||||
AC_DEFINE(USE_INTERRUPT_SOCKET, 1, Use interrupt socket instead of pthread_kill)
|
||||
fi
|
||||
]
|
||||
)
|
||||
AC_DEFUN([TORRENT_ENABLE_CUSTOM_STACK_SIZE], [
|
||||
AC_ARG_ENABLE([pthread-setstacksize],
|
||||
[AS_HELP_STRING([--enable-pthread-setstacksize], [explicitly set pthread stack size (auto-enabled for musl)])],
|
||||
[enable_pthread_setstacksize=$enableval],
|
||||
[enable_pthread_setstacksize=auto])
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
if test "x$enable_pthread_setstacksize" = "xauto"; then
|
||||
case "$host" in
|
||||
*-musl*)
|
||||
enable_pthread_setstacksize=yes
|
||||
;;
|
||||
*)
|
||||
enable_pthread_setstacksize=no
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test "x$enable_pthread_setstacksize" = "xyes"; then
|
||||
AC_DEFINE([USE_PTHREAD_SETSTACKSIZE], [1], [Use explicit pthread stack size])
|
||||
AC_DEFINE([DEFAULT_PTHREAD_STACKSIZE], [(8 * 1024 * 1024)], [Default pthread stack size in bytes])
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_DISABLE_IPV6], [
|
||||
AC_ARG_ENABLE(ipv6,
|
||||
AS_HELP_STRING([--enable-ipv6],
|
||||
|
||||
+14
-2
@@ -17,7 +17,6 @@ libsub_root_a_SOURCES = \
|
||||
core/http_queue.h \
|
||||
core/manager.cc \
|
||||
core/manager.h \
|
||||
core/range_map.h \
|
||||
core/view.cc \
|
||||
core/view.h \
|
||||
core/view_manager.cc \
|
||||
@@ -169,9 +168,14 @@ libsub_root_a_SOURCES = \
|
||||
utils/file_status_cache.cc \
|
||||
utils/file_status_cache.h \
|
||||
utils/functional.h \
|
||||
utils/list_focus.h \
|
||||
utils/gzip.cc \
|
||||
utils/gzip.h \
|
||||
utils/lockfile.cc \
|
||||
utils/lockfile.h \
|
||||
utils/waitpid_queue.cc \
|
||||
utils/waitpid_queue.h \
|
||||
utils/watch_ready_queue.cc \
|
||||
utils/watch_ready_queue.h \
|
||||
\
|
||||
command_download.cc \
|
||||
command_dynamic.cc \
|
||||
@@ -188,6 +192,8 @@ libsub_root_a_SOURCES = \
|
||||
command_throttle.cc \
|
||||
command_tracker.cc \
|
||||
command_scheduler.cc \
|
||||
command_string.cc \
|
||||
command_system.cc \
|
||||
command_ui.cc \
|
||||
control.cc \
|
||||
control.h \
|
||||
@@ -195,8 +201,14 @@ libsub_root_a_SOURCES = \
|
||||
globals.h \
|
||||
option_parser.cc \
|
||||
option_parser.h \
|
||||
setup.cc \
|
||||
setup.h \
|
||||
signal_handler.cc \
|
||||
signal_handler.h
|
||||
|
||||
|
||||
if NO_NCURSES
|
||||
libsub_root_a_SOURCES += display/curses_stub.cc display/curses_stub.h
|
||||
endif
|
||||
|
||||
AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -DPACKAGE_DATADIR=\"$(pkgdatadir)\"
|
||||
|
||||
+148
-90
@@ -5,12 +5,10 @@
|
||||
#include <functional>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <rak/string_manip.h>
|
||||
#include <rak/regex.h>
|
||||
#include <fnmatch.h>
|
||||
#include <torrent/rate.h>
|
||||
#include <torrent/throttle.h>
|
||||
#include <torrent/tracker/tracker.h>
|
||||
#include <torrent/connection_manager.h>
|
||||
#include <torrent/data/download_data.h>
|
||||
#include <torrent/data/file.h>
|
||||
#include <torrent/data/file_list.h>
|
||||
@@ -19,9 +17,11 @@
|
||||
#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/manager.h"
|
||||
@@ -32,29 +32,34 @@
|
||||
#include "control.h"
|
||||
#include "command_helpers.h"
|
||||
|
||||
std::string
|
||||
torrent::string_utf8
|
||||
retrieve_d_base_path(core::Download* download) {
|
||||
if (download->file_list()->is_multi_file())
|
||||
return download->file_list()->frozen_root_dir();
|
||||
else
|
||||
return download->file_list()->empty() ? std::string() : download->file_list()->at(0)->frozen_path();
|
||||
|
||||
if (download->file_list()->empty())
|
||||
return {};
|
||||
|
||||
return download->file_list()->at(0)->frozen_path();
|
||||
}
|
||||
|
||||
std::string
|
||||
torrent::string_utf8
|
||||
retrieve_d_base_filename(core::Download* download) {
|
||||
const std::string* base;
|
||||
torrent::string_utf8 base_path;
|
||||
|
||||
if (download->file_list()->is_multi_file())
|
||||
base = &download->file_list()->frozen_root_dir();
|
||||
base_path = download->file_list()->frozen_root_dir();
|
||||
else if (!download->file_list()->empty())
|
||||
base_path = download->file_list()->at(0)->frozen_path();
|
||||
else
|
||||
base = &download->file_list()->at(0)->frozen_path();
|
||||
return {};
|
||||
|
||||
std::string::size_type split = base->rfind('/');
|
||||
auto split = base_path.str().rfind('/');
|
||||
|
||||
if (split == std::string::npos)
|
||||
return *base;
|
||||
else
|
||||
return base->substr(split + 1);
|
||||
return base_path;
|
||||
|
||||
return torrent::string_utf8::from_string(base_path.str().substr(split + 1));
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
@@ -84,7 +89,7 @@ apply_d_change_link(core::Download* download, const torrent::Object::list_type&
|
||||
|
||||
// } else if (type == "directory_path") {
|
||||
// target = rpc::call_command_string("d.directory", rpc::make_target(download));
|
||||
// link = rak::path_expand(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
|
||||
// link = path_expand(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
|
||||
|
||||
} else if (type == "tied") {
|
||||
link = expand_path(rpc::call_command_string("d.tied_to_file", rpc::make_target(download)));
|
||||
@@ -137,16 +142,6 @@ apply_d_delete_tied(core::Download* download) {
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
void
|
||||
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());
|
||||
else
|
||||
download->set_root_directory(name + "/" + download->info()->name());
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_d_connection_type(core::Download* download, const std::string& name) {
|
||||
torrent::Download::ConnectionType t =
|
||||
@@ -158,8 +153,8 @@ apply_d_connection_type(core::Download* download, const std::string& name) {
|
||||
|
||||
torrent::Object
|
||||
apply_d_choke_heuristics(core::Download* download, const std::string& name, bool is_down) {
|
||||
torrent::Download::HeuristicType t =
|
||||
(torrent::Download::HeuristicType)torrent::option_find_string(torrent::OPTION_CHOKE_HEURISTICS, name.c_str());
|
||||
torrent::heuristics_enum t =
|
||||
static_cast<torrent::heuristics_enum>(torrent::option_find_string(torrent::OPTION_CHOKE_HEURISTICS, name.c_str()));
|
||||
|
||||
if (is_down)
|
||||
download->download()->set_download_choke_heuristic(t);
|
||||
@@ -269,16 +264,6 @@ retrieve_d_custom_map(core::Download* download, bool keys_only, const torrent::O
|
||||
return result;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
retrieve_d_bitfield(core::Download* download) {
|
||||
const torrent::Bitfield* bitField = download->download()->file_list()->bitfield();
|
||||
|
||||
if (bitField->empty())
|
||||
return torrent::Object("");
|
||||
|
||||
return torrent::Object(rak::transform_hex(bitField->begin(), bitField->end()));
|
||||
}
|
||||
|
||||
void
|
||||
apply_d_add_peer(core::Download* download, const std::string& arg) {
|
||||
int port, ret;
|
||||
@@ -303,8 +288,10 @@ apply_d_add_peer(core::Download* download, const std::string& arg) {
|
||||
|
||||
assert(std::this_thread::get_id() == torrent::main_thread::thread_id());
|
||||
|
||||
auto callback_id = torrent::system::make_callback_id();
|
||||
|
||||
// Currently discarding SOCK_STREAM.
|
||||
torrent::this_thread::resolver()->resolve_preferred(NULL, host, AF_UNSPEC, AF_INET, [download, port](torrent::c_sa_shared_ptr sa, int err) {
|
||||
torrent::this_thread::resolver()->resolve_preferred(callback_id, host, AF_UNSPEC, AF_INET, [download, port](torrent::c_sa_shared_ptr sa, int err) {
|
||||
if (sa == nullptr) {
|
||||
lt_log_print(torrent::LOG_TORRENT_WARN, "could not resolve hostname for added peer: %s", gai_strerror(err));
|
||||
return;
|
||||
@@ -326,7 +313,8 @@ d_chunks_seen(core::Download* download) {
|
||||
std::string result;
|
||||
result.resize(size * 2);
|
||||
|
||||
rak::transform_hex((const char*)seen, (const char*)seen + size, result.begin());
|
||||
torrent::utils::transform_to_hex(seen, seen + size, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -342,7 +330,7 @@ f_multicall(core::Download* download, const torrent::Object::list_type& args) {
|
||||
// parsing and searching command map for every single call.
|
||||
torrent::Object resultRaw = torrent::Object::create_list();
|
||||
torrent::Object::list_type& result = resultRaw.as_list();
|
||||
std::vector<rak::regex> regex_list;
|
||||
std::vector<std::string> regex_list;
|
||||
|
||||
bool use_regex = true;
|
||||
|
||||
@@ -356,7 +344,7 @@ f_multicall(core::Download* download, const torrent::Object::list_type& args) {
|
||||
|
||||
for (const auto& file : *download->file_list()) {
|
||||
if (use_regex &&
|
||||
std::none_of(regex_list.begin(), regex_list.end(), [&file](const auto& r) { return r(file->path()->as_string()); }))
|
||||
std::none_of(regex_list.begin(), regex_list.end(), [&file](const auto& pattern) { return fnmatch(pattern.c_str(), file->path()->as_string().c_str(), 0) == 0; }))
|
||||
continue;
|
||||
|
||||
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
||||
@@ -380,18 +368,19 @@ t_multicall(core::Download* download, const torrent::Object::list_type& args) {
|
||||
|
||||
// Add some pre-parsing of the commands, so we don't spend time
|
||||
// parsing and searching command map for every single call.
|
||||
torrent::Object result_raw = torrent::Object::create_list();
|
||||
torrent::Object::list_type& result = result_raw.as_list();
|
||||
|
||||
auto result_raw = torrent::Object::create_list();
|
||||
auto& result = result_raw.as_list();
|
||||
|
||||
for (uint32_t idx = 0, last = download->tracker_list_size(); idx < last; idx++) {
|
||||
auto& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
||||
auto& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
||||
auto tracker = download->tracker_controller().at(idx);
|
||||
|
||||
if (!tracker.is_valid())
|
||||
continue;
|
||||
|
||||
for (torrent::Object::list_const_iterator cItr = ++args.begin(); cItr != args.end(); cItr++) {
|
||||
const std::string& cmd = cItr->as_string();
|
||||
for (auto cItr = ++args.begin(); cItr != args.end(); cItr++) {
|
||||
auto& cmd = cItr->as_string();
|
||||
|
||||
row.push_back(rpc::parse_command(rpc::make_target(&tracker), cmd.c_str(), cmd.c_str() + cmd.size()).first);
|
||||
}
|
||||
@@ -410,16 +399,24 @@ p_multicall(core::Download* download, const torrent::Object::list_type& args) {
|
||||
|
||||
// Add some pre-parsing of the commands, so we don't spend time
|
||||
// parsing and searching command map for every single call.
|
||||
torrent::Object resultRaw = torrent::Object::create_list();
|
||||
torrent::Object::list_type& result = resultRaw.as_list();
|
||||
auto resultRaw = torrent::Object::create_list();
|
||||
auto& result = resultRaw.as_list();
|
||||
|
||||
for (const auto& connection : *download->connection_list()) {
|
||||
auto* connection_list = download->connection_list();
|
||||
const auto change_counter = connection_list->change_counter();
|
||||
|
||||
for (const auto& connection : *connection_list) {
|
||||
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
||||
|
||||
for (torrent::Object::list_const_iterator cItr = ++args.begin(); cItr != args.end(); cItr++) {
|
||||
for (auto cItr = ++args.begin(); cItr != args.end(); cItr++) {
|
||||
const std::string& cmd = cItr->as_string();
|
||||
|
||||
row.push_back(rpc::parse_command(rpc::make_target(connection), cmd.c_str(), cmd.c_str() + cmd.size()).first);
|
||||
|
||||
// Erasing a peer frees it and swaps the last element into its place, so
|
||||
// neither this peer nor the iteration survives a change to the list.
|
||||
if (connection_list->change_counter() != change_counter)
|
||||
throw torrent::input_error("Command changed the connection list during p.multicall.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,11 +439,16 @@ p_call_target(const torrent::Object::list_type& args) {
|
||||
const std::string& peer_id = itr++->as_string();
|
||||
const std::string& command_key = itr++->as_string();
|
||||
|
||||
if (download == nullptr)
|
||||
throw torrent::input_error("invalid parameters: info-hash not found");
|
||||
|
||||
torrent::HashString hash;
|
||||
|
||||
if (peer_id.size() != 40 ||
|
||||
torrent::hash_string_from_hex_c_str(peer_id.c_str(), hash) == peer_id.c_str())
|
||||
throw torrent::input_error("Not a hash string.");
|
||||
if (peer_id.size() != 40)
|
||||
throw torrent::input_error("invalid argument: peer id target is not 40 bytes long");
|
||||
|
||||
if (torrent::utils::transform_from_hex(peer_id.c_str(), peer_id.c_str() + 40, hash) != hash.end())
|
||||
throw torrent::input_error("invalid argument: peer id target is not a hex string");
|
||||
|
||||
torrent::ConnectionList::iterator peerItr = download->connection_list()->find(hash.c_str());
|
||||
|
||||
@@ -467,12 +469,14 @@ download_tracker_insert(core::Download* download, const torrent::Object::list_ty
|
||||
if (args.size() != 2)
|
||||
throw torrent::input_error("Wrong argument count.");
|
||||
|
||||
int64_t group;
|
||||
int64_t group = 0;
|
||||
|
||||
if (args.front().is_string())
|
||||
rpc::parse_whole_value_nothrow(args.front().as_string().c_str(), &group);
|
||||
else
|
||||
if (args.front().is_string()) {
|
||||
if (!rpc::parse_whole_value_nothrow(args.front().as_string().c_str(), &group))
|
||||
throw torrent::input_error("Invalid tracker group number.");
|
||||
} else {
|
||||
group = args.front().as_value();
|
||||
}
|
||||
|
||||
if (group < 0 || group > 32)
|
||||
throw torrent::input_error("Tracker group number invalid.");
|
||||
@@ -641,30 +645,50 @@ void cg_d_group_set(core::Download* download, const torrent::Objec
|
||||
|
||||
void
|
||||
initialize_command_download() {
|
||||
CMD2_DL("d.hash", std::bind(&rak::transform_hex_str<torrent::HashString>, CMD2_ON_INFO(hash)));
|
||||
CMD2_DL("d.local_id", std::bind(&rak::transform_hex_str<torrent::HashString>, CMD2_ON_INFO(local_id)));
|
||||
CMD2_DL("d.local_id_html", std::bind(&rak::copy_escape_html_str<torrent::HashString>, CMD2_ON_INFO(local_id)));
|
||||
CMD2_DL("d.bitfield", std::bind(&retrieve_d_bitfield, std::placeholders::_1));
|
||||
CMD2_DL("d.base_path", std::bind(&retrieve_d_base_path, std::placeholders::_1));
|
||||
CMD2_DL("d.base_filename", std::bind(&retrieve_d_base_filename, std::placeholders::_1));
|
||||
CMD2_DL("d.hash", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->hash()); });
|
||||
CMD2_DL("d.local_id", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->local_id()); });
|
||||
CMD2_DL("d.local_id_html", [](auto* download, auto) { return torrent::utils::copy_escape_html_str(download->info()->local_id()); });
|
||||
CMD2_DL("d.bitfield", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(*download->download()->file_list()->bitfield()); });
|
||||
CMD2_DL("d.base_path", [](auto* download, auto) { return retrieve_d_base_path(download).str(); });
|
||||
CMD2_DL("d.base_path.hex", [](auto* download, auto) { return retrieve_d_base_path(download).object_hex(); });
|
||||
CMD2_DL("d.base_path.base64", [](auto* download, auto) { return retrieve_d_base_path(download).object_base64(); });
|
||||
CMD2_DL("d.base_path.base64_as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_base64_as_binary(); });
|
||||
CMD2_DL("d.base_path.as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_as_binary(); });
|
||||
CMD2_DL("d.base_path.or_base64", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_base64(); });
|
||||
CMD2_DL("d.base_path.or_as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_as_binary(); });
|
||||
CMD2_DL("d.base_path.realpath.or_empty", [](auto* download, auto) { return resolve_path(retrieve_d_base_path(download).str()); });
|
||||
CMD2_DL("d.base_path.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(retrieve_d_base_path(download).str()); });
|
||||
CMD2_DL("d.base_filename", [](auto* download, auto) { return retrieve_d_base_filename(download).str(); });
|
||||
CMD2_DL("d.base_filename.hex", [](auto* download, auto) { return retrieve_d_base_filename(download).object_hex(); });
|
||||
CMD2_DL("d.base_filename.base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_base64(); });
|
||||
CMD2_DL("d.base_filename.base64_as_binary", [](auto* download, auto) { return retrieve_d_base_filename(download).object_base64_as_binary(); });
|
||||
CMD2_DL("d.base_filename.as_binary", [](auto* download, auto) { return retrieve_d_base_filename(download).object_as_binary(); });
|
||||
CMD2_DL("d.base_filename.or_base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_utf8_or_base64(); });
|
||||
CMD2_DL("d.base_filename.or_as_binary", [](auto* download, auto) { return retrieve_d_base_filename(download).object_utf8_or_as_binary(); });
|
||||
|
||||
CMD2_DL("d.name", CMD2_ON_INFO(name));
|
||||
CMD2_DL("d.creation_date", CMD2_ON_INFO(creation_date));
|
||||
CMD2_DL("d.load_date", CMD2_ON_INFO(load_date));
|
||||
CMD2_DL("d.name", [](auto* download, auto) { return download->info()->name().str(); });
|
||||
CMD2_DL("d.name.hex", [](auto* download, auto) { return download->info()->name().object_hex(); });
|
||||
CMD2_DL("d.name.base64", [](auto* download, auto) { return download->info()->name().object_base64(); });
|
||||
CMD2_DL("d.name.base64_as_binary", [](auto* download, auto) { return download->info()->name().object_base64_as_binary(); });
|
||||
CMD2_DL("d.name.as_binary", [](auto* download, auto) { return download->info()->name().object_as_binary(); });
|
||||
CMD2_DL("d.name.or_base64", [](auto* download, auto) { return download->info()->name().object_utf8_or_base64(); });
|
||||
CMD2_DL("d.name.or_as_binary", [](auto* download, auto) { return download->info()->name().object_utf8_or_as_binary(); });
|
||||
CMD2_DL("d.creation_date", [](auto* download, auto) { return download->info()->creation_date(); });
|
||||
CMD2_DL("d.load_date", [](auto* download, auto) { return download->info()->load_date(); });
|
||||
|
||||
//
|
||||
// Network related:
|
||||
//
|
||||
|
||||
CMD2_DL ("d.up.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(up_rate)));
|
||||
CMD2_DL ("d.up.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(up_rate)));
|
||||
CMD2_DL ("d.down.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(down_rate)));
|
||||
CMD2_DL ("d.down.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(down_rate)));
|
||||
CMD2_DL ("d.skip.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(skip_rate)));
|
||||
CMD2_DL ("d.skip.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(skip_rate)));
|
||||
CMD2_DL ("d.up.rate", [](auto* download, auto) { return download->info()->up_rate()->rate(); });
|
||||
CMD2_DL ("d.up.total", [](auto* download, auto) { return download->info()->up_rate()->total(); });
|
||||
CMD2_DL ("d.down.rate", [](auto* download, auto) { return download->info()->down_rate()->rate(); });
|
||||
CMD2_DL ("d.down.total", [](auto* download, auto) { return download->info()->down_rate()->total(); });
|
||||
CMD2_DL ("d.skip.rate", [](auto* download, auto) { return download->info()->skip_rate()->rate(); });
|
||||
CMD2_DL ("d.skip.total", [](auto* download, auto) { return download->info()->skip_rate()->total(); });
|
||||
|
||||
CMD2_DL ("d.peer_exchange", CMD2_ON_INFO(is_pex_enabled));
|
||||
CMD2_DL_VALUE_V ("d.peer_exchange.set", std::bind(&torrent::Download::set_pex_enabled, CMD2_BIND_DL, std::placeholders::_2));
|
||||
CMD2_DL ("d.peer_exchange", [](auto* download, auto) { return download->info()->is_pex_enabled(); });
|
||||
CMD2_DL_VALUE_V ("d.peer_exchange.set", [](auto* download, auto value) { download->download()->set_pex_enabled(value); });
|
||||
|
||||
CMD2_DL_LIST ("d.create_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 0));
|
||||
CMD2_DL_LIST ("d.delete_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 1));
|
||||
@@ -680,22 +704,22 @@ initialize_command_download() {
|
||||
// Control functinos:
|
||||
//
|
||||
|
||||
CMD2_DL ("d.is_open", CMD2_ON_INFO(is_open));
|
||||
CMD2_DL ("d.is_active", CMD2_ON_INFO(is_active));
|
||||
CMD2_DL ("d.is_open", [](auto* download, auto) { return download->info()->is_open(); });
|
||||
CMD2_DL ("d.is_active", [](auto* download, auto) { return download->info()->is_active(); });
|
||||
CMD2_DL ("d.is_hash_checked", std::bind(&torrent::Download::is_hash_checked, CMD2_BIND_DL));
|
||||
CMD2_DL ("d.is_hash_checking", std::bind(&torrent::Download::is_hash_checking, CMD2_BIND_DL));
|
||||
CMD2_DL ("d.is_multi_file", std::bind(&torrent::FileList::is_multi_file, CMD2_BIND_FL));
|
||||
CMD2_DL ("d.is_private", CMD2_ON_INFO(is_private));
|
||||
CMD2_DL ("d.is_pex_active", CMD2_ON_INFO(is_pex_active));
|
||||
CMD2_DL ("d.is_private", [](auto* download, auto) { return download->info()->is_private(); });
|
||||
CMD2_DL ("d.is_pex_active", [](auto* download, auto) { return download->info()->is_pex_active(); });
|
||||
CMD2_DL ("d.is_partially_done", CMD2_ON_DATA(is_partially_done));
|
||||
CMD2_DL ("d.is_not_partially_done", CMD2_ON_DATA(is_not_partially_done));
|
||||
CMD2_DL ("d.is_meta", CMD2_ON_INFO(is_meta_download));
|
||||
CMD2_DL ("d.is_meta", [](auto* download, auto) { return download->info()->is_meta_download(); });
|
||||
|
||||
CMD2_DL_V ("d.resume", std::bind(&core::DownloadList::resume_default, control->core()->download_list(), std::placeholders::_1));
|
||||
CMD2_DL_V ("d.pause", std::bind(&core::DownloadList::pause_default, control->core()->download_list(), std::placeholders::_1));
|
||||
CMD2_DL_V ("d.open", std::bind(&core::DownloadList::open_throw, control->core()->download_list(), std::placeholders::_1));
|
||||
CMD2_DL_V ("d.close", std::bind(&core::DownloadList::close_throw, control->core()->download_list(), std::placeholders::_1));
|
||||
CMD2_DL_V ("d.close.directly", std::bind(&core::DownloadList::close_directly, control->core()->download_list(), std::placeholders::_1));
|
||||
CMD2_DL_V ("d.close.directly", std::bind(&core::DownloadList::close_files, control->core()->download_list(), std::placeholders::_1));
|
||||
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));
|
||||
|
||||
@@ -750,6 +774,11 @@ initialize_command_download() {
|
||||
CMD2_DL_VAR_STRING_PUBLIC("d.tied_to_file", "rtorrent", "tied_to_file");
|
||||
CMD2_DL_VAR_STRING("d.loaded_file", "rtorrent", "loaded_file");
|
||||
|
||||
CMD2_DL("d.tied_to_file.realpath.or_empty", [](auto* download, auto) { return resolve_path(rpc::convert_to_string(download_get_variable(download, "rtorrent", "tied_to_file"))); });
|
||||
CMD2_DL("d.tied_to_file.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(rpc::convert_to_string(download_get_variable(download, "rtorrent", "tied_to_file"))); });
|
||||
CMD2_DL("d.loaded_file.realpath.or_empty", [](auto* download, auto) { return resolve_path(rpc::convert_to_string(download_get_variable(download, "rtorrent", "loaded_file"))); });
|
||||
CMD2_DL("d.loaded_file.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(rpc::convert_to_string(download_get_variable(download, "rtorrent", "loaded_file"))); });
|
||||
|
||||
// The "state_changed" variable is required to be a valid unix time
|
||||
// value, it indicates the last time the torrent changed its state,
|
||||
// resume/pause.
|
||||
@@ -760,15 +789,15 @@ initialize_command_download() {
|
||||
CMD2_DL_TIMESTAMP("d.timestamp.started", "rtorrent", "timestamp.started");
|
||||
CMD2_DL_TIMESTAMP("d.timestamp.finished", "rtorrent", "timestamp.finished");
|
||||
|
||||
CMD2_DL ("d.connection_current", std::bind(&torrent::option_as_string, torrent::OPTION_CONNECTION_TYPE, CMD2_ON_DL(connection_type)));
|
||||
CMD2_DL_STRING("d.connection_current.set", std::bind(&apply_d_connection_type, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL ("d.connection_current", [](auto* d, auto) { return torrent::option_to_c_str_or_throw(torrent::OPTION_CONNECTION_TYPE, d->download()->connection_type()); });
|
||||
CMD2_DL_STRING_V("d.connection_current.set", [](auto* d, auto arg) { apply_d_connection_type(d, arg); });
|
||||
|
||||
CMD2_DL_VAR_STRING("d.connection_leech", "rtorrent", "connection_leech");
|
||||
CMD2_DL_VAR_STRING("d.connection_seed", "rtorrent", "connection_seed");
|
||||
|
||||
CMD2_DL ("d.up.choke_heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, CMD2_ON_DL(upload_choke_heuristic)));
|
||||
CMD2_DL ("d.up.choke_heuristics", [](auto* d, auto) { return torrent::option_to_c_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, d->download()->upload_choke_heuristic()); });
|
||||
CMD2_DL_STRING("d.up.choke_heuristics.set", std::bind(&apply_d_choke_heuristics, std::placeholders::_1, std::placeholders::_2, false));
|
||||
CMD2_DL ("d.down.choke_heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, CMD2_ON_DL(download_choke_heuristic)));
|
||||
CMD2_DL ("d.down.choke_heuristics", [](auto* d, auto) { return torrent::option_to_c_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, d->download()->download_choke_heuristic()); });
|
||||
CMD2_DL_STRING("d.down.choke_heuristics.set", std::bind(&apply_d_choke_heuristics, std::placeholders::_1, std::placeholders::_2, true));
|
||||
|
||||
CMD2_DL_VAR_STRING("d.up.choke_heuristics.leech", "rtorrent", "choke_heuristics.up.leech");
|
||||
@@ -823,7 +852,7 @@ initialize_command_download() {
|
||||
CMD2_DL ("d.bytes_done", CMD2_ON_DL(bytes_done));
|
||||
CMD2_DL ("d.ratio", std::bind(&retrieve_d_ratio, std::placeholders::_1));
|
||||
CMD2_DL ("d.chunks_hashed", CMD2_ON_DL(chunks_hashed));
|
||||
CMD2_DL ("d.free_diskspace", CMD2_ON_FL(free_diskspace));
|
||||
CMD2_DL ("d.free_diskspace", [](auto* download, auto) { return download->file_list()->free_diskspace_no_cache(); });
|
||||
|
||||
CMD2_DL ("d.size_files", CMD2_ON_FL(size_files));
|
||||
CMD2_DL ("d.size_bytes", CMD2_ON_FL(size_bytes));
|
||||
@@ -856,10 +885,11 @@ initialize_command_download() {
|
||||
CMD2_DL_LIST ("d.tracker.insert", std::bind(&download_tracker_insert, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL_VALUE_V ("d.tracker.send_scrape", [](auto download, uint64_t arg) { download->tracker_controller().scrape_request(arg); });
|
||||
|
||||
CMD2_DL ("d.directory", CMD2_ON_FL(root_dir));
|
||||
CMD2_DL_STRING_V("d.directory.set", std::bind(&apply_d_directory, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL ("d.directory_base", CMD2_ON_FL(root_dir));
|
||||
CMD2_DL_STRING_V("d.directory_base.set", std::bind(&core::Download::set_root_directory, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL ("d.directory", CMD2_ON_FL(root_dir));
|
||||
CMD2_DL ("d.directory.realpath.or_empty", [](auto* download, auto) { return resolve_path(download->file_list()->root_dir()); });
|
||||
CMD2_DL ("d.directory.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(download->file_list()->root_dir()); });
|
||||
CMD2_DL_STRING_V("d.directory.set", [](auto* download, auto arg) { download->set_directory(arg); });
|
||||
CMD2_DL_STRING_V("d.directory.base.set", [](auto* download, auto arg) { download->set_base_directory(arg); });
|
||||
|
||||
CMD2_DL ("d.priority", std::bind(&core::Download::priority, std::placeholders::_1));
|
||||
CMD2_DL ("d.priority_str", std::bind(&retrieve_d_priority_str, std::placeholders::_1));
|
||||
@@ -891,10 +921,35 @@ initialize_command_download() {
|
||||
rpc::rpc.mark_safe("d.local_id_html");
|
||||
rpc::rpc.mark_safe("d.bitfield");
|
||||
rpc::rpc.mark_safe("d.base_path");
|
||||
rpc::rpc.mark_safe("d.base_path.realpath.or_empty");
|
||||
rpc::rpc.mark_safe("d.base_path.realpath.or_throw");
|
||||
rpc::rpc.mark_safe("d.directory.realpath.or_empty");
|
||||
rpc::rpc.mark_safe("d.directory.realpath.or_throw");
|
||||
rpc::rpc.mark_safe("d.tied_to_file.realpath.or_empty");
|
||||
rpc::rpc.mark_safe("d.tied_to_file.realpath.or_throw");
|
||||
rpc::rpc.mark_safe("d.loaded_file.realpath.or_empty");
|
||||
rpc::rpc.mark_safe("d.loaded_file.realpath.or_throw");
|
||||
rpc::rpc.mark_safe("d.base_path.hex");
|
||||
rpc::rpc.mark_safe("d.base_path.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");
|
||||
@@ -934,6 +989,9 @@ initialize_command_download() {
|
||||
rpc::rpc.mark_safe("d.size_chunks");
|
||||
rpc::rpc.mark_safe("d.size_pex");
|
||||
rpc::rpc.mark_safe("d.completed_bytes");
|
||||
rpc::rpc.mark_safe("d.complete");
|
||||
rpc::rpc.mark_safe("d.timestamp.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");
|
||||
|
||||
+17
-11
@@ -157,7 +157,7 @@ system_method_insert_object(const torrent::Object::list_type& args, int flags) {
|
||||
torrent::Object valueList = torrent::Object::create_list();
|
||||
torrent::Object::list_type& valueListType = valueList.as_list();
|
||||
|
||||
if ((itrArgs)->is_list())
|
||||
if (itrArgs != args.end() && (itrArgs)->is_list())
|
||||
valueListType = (itrArgs)->as_list();
|
||||
|
||||
control->object_storage()->insert_str(raw_key, valueList, flags);
|
||||
@@ -444,16 +444,20 @@ initialize_command_dynamic() {
|
||||
|
||||
CMD2_ANY ("catch", std::bind(&cmd_catch, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY ("strings.choke_heuristics", std::bind(&torrent::option_list_strings, torrent::OPTION_CHOKE_HEURISTICS));
|
||||
CMD2_ANY ("strings.choke_heuristics.upload", std::bind(&torrent::option_list_strings, torrent::OPTION_CHOKE_HEURISTICS_UPLOAD));
|
||||
CMD2_ANY ("strings.choke_heuristics.download", std::bind(&torrent::option_list_strings, torrent::OPTION_CHOKE_HEURISTICS_DOWNLOAD));
|
||||
CMD2_ANY ("strings.connection_type", std::bind(&torrent::option_list_strings, torrent::OPTION_CONNECTION_TYPE));
|
||||
CMD2_ANY ("strings.encryption", std::bind(&torrent::option_list_strings, torrent::OPTION_ENCRYPTION));
|
||||
CMD2_ANY ("strings.ip_filter", std::bind(&torrent::option_list_strings, torrent::OPTION_IP_FILTER));
|
||||
CMD2_ANY ("strings.ip_tos", std::bind(&torrent::option_list_strings, torrent::OPTION_IP_TOS));
|
||||
CMD2_ANY ("strings.log_group", std::bind(&torrent::option_list_strings, torrent::OPTION_LOG_GROUP));
|
||||
CMD2_ANY ("strings.tracker_event", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_EVENT));
|
||||
CMD2_ANY ("strings.tracker_mode", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_MODE));
|
||||
CMD2_ANY_STRING ("enum.log_group", [](auto, const auto& str) { return torrent::option_find_string_str(torrent::OPTION_LOG_GROUP, str); });
|
||||
|
||||
CMD2_ANY ("strings.choke_heuristics", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS); });
|
||||
CMD2_ANY ("strings.choke_heuristics.upload", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_UPLOAD); });
|
||||
CMD2_ANY ("strings.choke_heuristics.download", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_DOWNLOAD); });
|
||||
CMD2_ANY ("strings.connection_type", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CONNECTION_TYPE); });
|
||||
CMD2_ANY ("strings.encryption", [](auto, auto) { return torrent::Object::create_list(); });
|
||||
CMD2_ANY ("strings.encryption.handshake", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_HANDSHAKE); });
|
||||
CMD2_ANY ("strings.encryption.stream", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_STREAM); });
|
||||
CMD2_ANY ("strings.ip_filter", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_FILTER); });
|
||||
CMD2_ANY ("strings.ip_tos", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_TOS); });
|
||||
CMD2_ANY ("strings.log_group", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_LOG_GROUP); });
|
||||
CMD2_ANY ("strings.tracker_event", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_TRACKER_EVENT); });
|
||||
CMD2_ANY ("strings.tracker_mode", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_TRACKER_MODE); });
|
||||
// clang-format on
|
||||
|
||||
#ifdef HAVE_XMLRPC_TINYXML2
|
||||
@@ -468,6 +472,8 @@ initialize_command_dynamic() {
|
||||
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");
|
||||
|
||||
+91
-47
@@ -2,12 +2,14 @@
|
||||
|
||||
#include <functional>
|
||||
#include <cstdio>
|
||||
#include <rak/string_manip.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <torrent/rate.h>
|
||||
#include <torrent/hash_string.h>
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/directory_events.h>
|
||||
#include <torrent/utils/file_stat.h>
|
||||
#include <torrent/utils/string_manip.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "control.h"
|
||||
@@ -19,6 +21,7 @@
|
||||
#include "rpc/command_scheduler.h"
|
||||
#include "rpc/parse.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "utils/watch_ready_queue.h"
|
||||
|
||||
torrent::Object
|
||||
apply_on_ratio(const torrent::Object& rawArgs) {
|
||||
@@ -48,7 +51,7 @@ apply_on_ratio(const torrent::Object& rawArgs) {
|
||||
!(max_ratio > 0 && total_upload * 100 > total_done * max_ratio))
|
||||
continue;
|
||||
|
||||
downloads.push_back(*itr);
|
||||
downloads.push_back(itr->get());
|
||||
}
|
||||
|
||||
auto ratio_command = "group." + group_name + ".ratio.command";
|
||||
@@ -125,7 +128,7 @@ apply_remove_untied() {
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_schedule(const torrent::Object::list_type& args) {
|
||||
apply_schedule(const torrent::Object::list_type& args, bool if_absent) {
|
||||
if (args.size() != 4)
|
||||
throw torrent::input_error("Wrong number of arguments.");
|
||||
|
||||
@@ -135,6 +138,9 @@ apply_schedule(const torrent::Object::list_type& args) {
|
||||
auto& arg2 = (itr++)->as_string();
|
||||
auto& arg3 = (itr++)->as_string();
|
||||
|
||||
if (if_absent && control->command_scheduler()->find(arg1) != control->command_scheduler()->end())
|
||||
return torrent::Object();
|
||||
|
||||
control->command_scheduler()->parse(arg1, arg2, arg3, *itr);
|
||||
|
||||
return torrent::Object();
|
||||
@@ -165,12 +171,14 @@ torrent::Object
|
||||
apply_close_low_diskspace(int64_t arg, uint32_t skip_priority) {
|
||||
bool closed = false;
|
||||
|
||||
for (auto download : *control->core()->download_list()) {
|
||||
torrent::FileList::cache_list cache;
|
||||
|
||||
for (const auto& download : *control->core()->download_list()) {
|
||||
if (!download->is_downloading())
|
||||
continue;
|
||||
if (download->priority() >= skip_priority)
|
||||
continue;
|
||||
if (download->file_list()->free_diskspace() >= (uint64_t)arg)
|
||||
if (download->file_list()->free_diskspace(cache) >= (uint64_t)arg)
|
||||
continue;
|
||||
|
||||
control->core()->download_list()->close(download);
|
||||
@@ -208,7 +216,7 @@ apply_download_list(const torrent::Object::list_type& args) {
|
||||
for (core::View::const_iterator itr = (*view_itr)->begin_visible(), last = (*view_itr)->end_visible(); itr != last; itr++) {
|
||||
const torrent::HashString* hashString = &(*itr)->info()->hash();
|
||||
|
||||
resultList.push_back(rak::transform_hex(hashString->begin(), hashString->end()));
|
||||
resultList.push_back(torrent::utils::transform_to_hex_str(*hashString));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -232,15 +240,25 @@ 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());
|
||||
// Hold a reference to each download so a command that erases one does not
|
||||
// leave the rest of the loop dispatching on freed memory.
|
||||
core::View::base_type dlist((*view_itr)->begin_visible(), (*view_itr)->end_visible());
|
||||
|
||||
torrent::Object resultRaw = torrent::Object::create_list();
|
||||
torrent::Object::list_type& result = resultRaw.as_list();
|
||||
|
||||
for (auto download : dlist) {
|
||||
for (const auto& download : dlist) {
|
||||
if (download.use_count() == 1)
|
||||
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++) {
|
||||
// A command may erase this download, which destroys the torrent object it
|
||||
// wraps; the list dropping its reference is what tells us.
|
||||
if (download.use_count() == 1)
|
||||
break;
|
||||
|
||||
auto& cmd = cItr->as_string();
|
||||
row.push_back(rpc::parse_command(rpc::make_target(download), cmd.c_str(), cmd.c_str() + cmd.size()).first);
|
||||
}
|
||||
@@ -253,30 +271,40 @@ torrent::Object
|
||||
d_multicall_filtered(const torrent::Object::list_type& args) {
|
||||
if (args.size() < 2)
|
||||
throw torrent::input_error("d.multicall.filtered requires at least 2 arguments.");
|
||||
torrent::Object::list_const_iterator arg = args.begin();
|
||||
|
||||
auto arg = args.begin();
|
||||
|
||||
// Find the given view
|
||||
core::ViewManager* viewManager = control->view_manager();
|
||||
core::ViewManager::iterator view_itr = viewManager->find(arg->as_string().empty() ? "default" : arg->as_string());
|
||||
auto* viewManager = control->view_manager();
|
||||
auto view_itr = viewManager->find(arg->as_string().empty() ? "default" : arg->as_string());
|
||||
|
||||
if (view_itr == viewManager->end())
|
||||
throw torrent::input_error("Could not find view '" + arg->as_string() + "'.");
|
||||
|
||||
// Make a filtered copy of the current item list
|
||||
// Make a filtered copy of the current item list, holding a reference to each
|
||||
// download so a command that erases one cannot free it under us.
|
||||
core::View::base_type dlist;
|
||||
(*view_itr)->filter_by(*++arg, dlist);
|
||||
|
||||
|
||||
// Generate result by iterating over all items
|
||||
torrent::Object resultRaw = torrent::Object::create_list();
|
||||
torrent::Object::list_type& result = resultRaw.as_list();
|
||||
auto resultRaw = torrent::Object::create_list();
|
||||
auto& result = resultRaw.as_list();
|
||||
|
||||
++arg; // skip to first command
|
||||
|
||||
for (const auto& item : dlist) {
|
||||
if (item.use_count() == 1)
|
||||
continue;
|
||||
|
||||
// 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++) {
|
||||
if (item.use_count() == 1)
|
||||
break;
|
||||
|
||||
auto& cmdstr = command->as_string();
|
||||
row.push_back(rpc::parse_command(rpc::make_target(item), cmdstr.c_str(), cmdstr.c_str() + cmdstr.size()).first);
|
||||
}
|
||||
@@ -291,59 +319,75 @@ call_watch_command(const std::string& command, const std::string& path) {
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
directory_watch_added(const torrent::Object::list_type& args) {
|
||||
directory_watch(const torrent::Object::list_type& args, int flags) {
|
||||
if (args.size() != 2)
|
||||
throw torrent::input_error("Too few arguments.");
|
||||
|
||||
auto& path = args.front().as_string();
|
||||
auto& command = args.back().as_string();
|
||||
std::string expanded_path = expand_path(path);
|
||||
|
||||
if (!control->directory_events()->open())
|
||||
throw torrent::input_error("Could not open inotify:" + std::string(std::strerror(errno)));
|
||||
|
||||
control->directory_events()->notify_on(path.c_str(),
|
||||
torrent::directory_events::flag_on_added | torrent::directory_events::flag_on_updated,
|
||||
std::bind(&call_watch_command, command, std::placeholders::_1));
|
||||
torrent::watch_descriptor::slot_string slot =
|
||||
flags == torrent::directory_events::flag_on_ready ?
|
||||
torrent::watch_descriptor::slot_string([command](const auto& arg) { control->watch_ready_queue()->push(command, arg); }) :
|
||||
torrent::watch_descriptor::slot_string(std::bind(&call_watch_command, command, std::placeholders::_1));
|
||||
|
||||
control->directory_events()->notify_on(expanded_path.c_str(), flags, slot);
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
directory_watch_added(const torrent::Object::list_type& args) {
|
||||
return directory_watch(args,
|
||||
torrent::directory_events::flag_on_added |
|
||||
torrent::directory_events::flag_on_updated);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
directory_watch_ready(const torrent::Object::list_type& args) {
|
||||
return directory_watch(args,
|
||||
torrent::directory_events::flag_on_ready);
|
||||
}
|
||||
|
||||
void
|
||||
initialize_command_events() {
|
||||
CMD2_ANY_STRING ("on_ratio", std::bind(&apply_on_ratio, std::placeholders::_2));
|
||||
CMD2_ANY_STRING ("on_ratio", [](auto, auto& args) { return apply_on_ratio(args); });
|
||||
|
||||
CMD2_ANY ("start_tied", std::bind(&apply_start_tied));
|
||||
CMD2_ANY ("stop_untied", std::bind(&apply_stop_untied));
|
||||
CMD2_ANY ("close_untied", std::bind(&apply_close_untied));
|
||||
CMD2_ANY ("remove_untied", std::bind(&apply_remove_untied));
|
||||
CMD2_ANY ("start_tied", [](auto, auto) { return apply_start_tied(); });
|
||||
CMD2_ANY ("stop_untied", [](auto, auto) { return apply_stop_untied(); });
|
||||
CMD2_ANY ("close_untied", [](auto, auto) { return apply_close_untied(); });
|
||||
CMD2_ANY ("remove_untied", [](auto, auto) { return apply_remove_untied(); });
|
||||
|
||||
// TODO: Deprecate schedule2 in the future.
|
||||
CMD2_ANY_LIST ("schedule", std::bind(&apply_schedule, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("schedule2", std::bind(&apply_schedule, std::placeholders::_2));
|
||||
CMD2_ANY_STRING_V("schedule.remove", std::bind(&rpc::CommandScheduler::erase_str, control->command_scheduler(), std::placeholders::_2));
|
||||
CMD2_ANY_STRING_V("schedule_remove2", std::bind(&rpc::CommandScheduler::erase_str, control->command_scheduler(), std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("schedule", [](auto, auto& args) { return apply_schedule(args, false); });
|
||||
CMD2_ANY_LIST ("schedule.if_absent", [](auto, auto& args) { return apply_schedule(args, true); });
|
||||
CMD2_ANY_STRING_V("schedule.remove", [](auto, auto& str) { return control->command_scheduler()->erase_str(str); });
|
||||
|
||||
CMD2_ANY_STRING_V("import", std::bind(&apply_import, std::placeholders::_2));
|
||||
CMD2_ANY_STRING_V("try_import", std::bind(&apply_try_import, std::placeholders::_2));
|
||||
CMD2_ANY_STRING_V("import", [](auto, auto& str) { return apply_import(str); });
|
||||
CMD2_ANY_STRING_V("try_import", [](auto, auto& str) { return apply_try_import(str); });
|
||||
|
||||
CMD2_ANY_LIST ("load.normal", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_quiet | core::Manager::create_tied));
|
||||
CMD2_ANY_LIST ("load.verbose", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_tied));
|
||||
CMD2_ANY_LIST ("load.start", std::bind(&apply_load, std::placeholders::_2,
|
||||
core::Manager::create_quiet | core::Manager::create_tied | core::Manager::create_start));
|
||||
CMD2_ANY_LIST ("load.start_verbose", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_tied | core::Manager::create_start));
|
||||
CMD2_ANY_LIST ("load.raw", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_quiet | core::Manager::create_raw_data));
|
||||
CMD2_ANY_LIST ("load.raw_verbose", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_raw_data));
|
||||
CMD2_ANY_LIST ("load.raw_start", std::bind(&apply_load, std::placeholders::_2,
|
||||
core::Manager::create_quiet | core::Manager::create_start | core::Manager::create_raw_data));
|
||||
CMD2_ANY_LIST ("load.raw_start_verbose", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_start | core::Manager::create_raw_data));
|
||||
CMD2_ANY_LIST ("load.normal", [](auto, auto& args) { return apply_load(args, core::Manager::create_quiet | core::Manager::create_tied); });
|
||||
CMD2_ANY_LIST ("load.verbose", [](auto, auto& args) { return apply_load(args, core::Manager::create_tied); });
|
||||
CMD2_ANY_LIST ("load.start", [](auto, auto& args) { return apply_load(args, core::Manager::create_quiet | core::Manager::create_tied | core::Manager::create_start); });
|
||||
CMD2_ANY_LIST ("load.start_verbose", [](auto, auto& args) { return apply_load(args, core::Manager::create_tied | core::Manager::create_start); });
|
||||
CMD2_ANY_LIST ("load.raw", [](auto, auto& args) { return apply_load(args, core::Manager::create_quiet | core::Manager::create_raw_data); });
|
||||
CMD2_ANY_LIST ("load.raw_verbose", [](auto, auto& args) { return apply_load(args, core::Manager::create_raw_data); });
|
||||
CMD2_ANY_LIST ("load.raw_start", [](auto, auto& args) { return apply_load(args, core::Manager::create_quiet | core::Manager::create_start | core::Manager::create_raw_data); });
|
||||
CMD2_ANY_LIST ("load.raw_start_verbose", [](auto, auto& args) { return apply_load(args, core::Manager::create_start | core::Manager::create_raw_data); });
|
||||
|
||||
CMD2_ANY_VALUE ("close_low_diskspace", std::bind(&apply_close_low_diskspace, std::placeholders::_2, 99));
|
||||
CMD2_ANY_VALUE ("close_low_diskspace.normal", std::bind(&apply_close_low_diskspace, std::placeholders::_2, 3));
|
||||
CMD2_ANY_VALUE ("close_low_diskspace", [](auto, auto& arg) { return apply_close_low_diskspace(arg, 99); });
|
||||
CMD2_ANY_VALUE ("close_low_diskspace.normal", [](auto, auto& arg) { return apply_close_low_diskspace(arg, 3); });
|
||||
|
||||
CMD2_ANY_LIST ("download_list", std::bind(&apply_download_list, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("d.multicall2", std::bind(&d_multicall, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("d.multicall.filtered", std::bind(&d_multicall_filtered, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("download_list", [](auto, auto& args) { return apply_download_list(args); });
|
||||
|
||||
CMD2_ANY_LIST ("directory.watch.added", std::bind(&directory_watch_added, std::placeholders::_2));
|
||||
// TODO: Deprecate d.multicall2. (6/2026)
|
||||
CMD2_ANY_LIST ("d.multicall", [](auto, auto& args) { return d_multicall(args); });
|
||||
CMD2_ANY_LIST ("d.multicall.filtered", [](auto, auto& args) { return d_multicall_filtered(args); });
|
||||
|
||||
CMD2_ANY_LIST ("directory.watch.added", [](auto, auto& args) { return directory_watch_added(args); });
|
||||
CMD2_ANY_LIST ("directory.watch.ready", [](auto, auto& args) { return directory_watch_ready(args); });
|
||||
|
||||
rpc::rpc.mark_safe("start_tied");
|
||||
rpc::rpc.mark_safe("stop_untied");
|
||||
@@ -353,6 +397,6 @@ initialize_command_events() {
|
||||
rpc::rpc.mark_safe("close_low_diskspace");
|
||||
rpc::rpc.mark_safe("close_low_diskspace.normal");
|
||||
rpc::rpc.mark_safe("download_list");
|
||||
rpc::rpc.mark_safe("d.multicall2");
|
||||
rpc::rpc.mark_safe("d.multicall");
|
||||
rpc::rpc.mark_safe("d.multicall.filtered");
|
||||
}
|
||||
|
||||
+54
-23
@@ -24,24 +24,34 @@ apply_f_path(torrent::File* file) {
|
||||
if (file->path()->empty())
|
||||
return std::string();
|
||||
|
||||
torrent::Object resultRaw(*file->path()->begin());
|
||||
torrent::Object::string_type& result = resultRaw.as_string();
|
||||
torrent::Object result_raw(file->path()->begin()->str());
|
||||
auto& result = result_raw.as_string();
|
||||
|
||||
for (torrent::Path::const_iterator itr = ++file->path()->begin(), last = file->path()->end(); itr != last; itr++)
|
||||
result += '/' + *itr;
|
||||
result += '/' + itr->str();
|
||||
|
||||
return resultRaw;
|
||||
return result_raw;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_f_path_components(torrent::File* file) {
|
||||
torrent::Object resultRaw = torrent::Object::create_list();
|
||||
torrent::Object::list_type& result = resultRaw.as_list();
|
||||
apply_f_path_components(torrent::File* file, int output_type) {
|
||||
auto result_raw = torrent::Object::create_list();
|
||||
auto& result = result_raw.as_list();
|
||||
|
||||
for (const auto& itr : *file->path())
|
||||
result.push_back(itr);
|
||||
for (const auto& itr : *file->path()) {
|
||||
switch (output_type) {
|
||||
case 0: result.push_back(itr.str()); break;
|
||||
case 1: result.push_back(itr.object_hex()); break;
|
||||
case 2: result.push_back(itr.object_base64()); break;
|
||||
case 3: result.push_back(itr.object_base64_as_binary()); break;
|
||||
case 4: result.push_back(itr.object_as_binary()); break;
|
||||
case 5: result.push_back(itr.object_utf8_or_base64()); break;
|
||||
case 6: result.push_back(itr.object_utf8_or_as_binary()); break;
|
||||
default: throw torrent::input_error("apply_f_path_components(): invalid output type");
|
||||
};
|
||||
}
|
||||
|
||||
return resultRaw;
|
||||
return result_raw;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
@@ -57,7 +67,7 @@ apply_fi_filename_last(torrent::FileListIterator* itr) {
|
||||
if (itr->depth() >= itr->file()->path()->size())
|
||||
return "ERROR";
|
||||
|
||||
return itr->file()->path()->at(itr->depth());
|
||||
return itr->file()->path()->at(itr->depth()).str();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -80,21 +90,34 @@ initialize_command_file() {
|
||||
CMD2_FILE_V("f.prioritize_last.enable", std::bind(&torrent::File::set_flags, std::placeholders::_1, torrent::File::flag_prioritize_last));
|
||||
CMD2_FILE_V("f.prioritize_last.disable", std::bind(&torrent::File::unset_flags, std::placeholders::_1, torrent::File::flag_prioritize_last));
|
||||
|
||||
CMD2_FILE("f.size_bytes", std::bind(&torrent::File::size_bytes, std::placeholders::_1));
|
||||
CMD2_FILE("f.size_chunks", std::bind(&torrent::File::size_chunks, std::placeholders::_1));
|
||||
CMD2_FILE("f.completed_chunks", std::bind(&torrent::File::completed_chunks, std::placeholders::_1));
|
||||
CMD2_FILE("f.size_bytes", [](auto* file, auto) { return file->size_bytes(); });
|
||||
CMD2_FILE("f.size_chunks", [](auto* file, auto) { return file->size_chunks(); });
|
||||
CMD2_FILE("f.completed_chunks", [](auto* file, auto) { return file->completed_chunks(); });
|
||||
|
||||
CMD2_FILE("f.offset", std::bind(&torrent::File::offset, std::placeholders::_1));
|
||||
CMD2_FILE("f.range_first", std::bind(&torrent::File::range_first, std::placeholders::_1));
|
||||
CMD2_FILE("f.range_second", std::bind(&torrent::File::range_second, std::placeholders::_1));
|
||||
CMD2_FILE("f.offset", [](auto* file, auto) { return file->offset(); });
|
||||
CMD2_FILE("f.range_first", [](auto* file, auto) { return file->range_first(); });
|
||||
CMD2_FILE("f.range_second", [](auto* file, auto) { return file->range_second(); });
|
||||
|
||||
CMD2_FILE("f.priority", std::bind(&torrent::File::priority, std::placeholders::_1));
|
||||
CMD2_FILE_VALUE_V("f.priority.set", std::bind(&apply_f_set_priority, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_FILE("f.priority", [](auto* file, auto) { return file->priority(); });
|
||||
CMD2_FILE_VALUE_V("f.priority.set", [](auto* file, auto v) { apply_f_set_priority(file, v); });
|
||||
|
||||
CMD2_FILE("f.path", std::bind(&apply_f_path, std::placeholders::_1));
|
||||
CMD2_FILE("f.path_components", std::bind(&apply_f_path_components, std::placeholders::_1));
|
||||
CMD2_FILE("f.path_depth", std::bind(&apply_f_path_depth, std::placeholders::_1));
|
||||
CMD2_FILE("f.frozen_path", std::bind(&torrent::File::frozen_path, std::placeholders::_1));
|
||||
CMD2_FILE("f.path", [](auto* file, auto) { return apply_f_path(file); });
|
||||
CMD2_FILE("f.path_components", [](auto* file, auto) { return apply_f_path_components(file, 0); });
|
||||
CMD2_FILE("f.path_components.hex", [](auto* file, auto) { return apply_f_path_components(file, 1); });
|
||||
CMD2_FILE("f.path_components.base64", [](auto* file, auto) { return apply_f_path_components(file, 2); });
|
||||
CMD2_FILE("f.path_components.base64_as_binary", [](auto* file, auto) { return apply_f_path_components(file, 3); });
|
||||
CMD2_FILE("f.path_components.as_binary", [](auto* file, auto) { return apply_f_path_components(file, 4); });
|
||||
CMD2_FILE("f.path_components.or_base64", [](auto* file, auto) { return apply_f_path_components(file, 5); });
|
||||
CMD2_FILE("f.path_components.or_as_binary", [](auto* file, auto) { return apply_f_path_components(file, 6); });
|
||||
CMD2_FILE("f.path_depth", [](auto* file, auto) { return apply_f_path_depth(file); });
|
||||
CMD2_FILE("f.frozen_path", [](auto* file, auto) { return file->frozen_path().str(); });
|
||||
CMD2_FILE("f.frozen_path.hex", [](auto* file, auto) { return file->frozen_path().object_hex(); });
|
||||
CMD2_FILE("f.frozen_path.base64", [](auto* file, auto) { return file->frozen_path().object_base64(); });
|
||||
CMD2_FILE("f.frozen_path.as_binary", [](auto* file, auto) { return file->frozen_path().object_as_binary(); });
|
||||
CMD2_FILE("f.frozen_path.or_base64", [](auto* file, auto) { return file->frozen_path().object_utf8_or_base64(); });
|
||||
CMD2_FILE("f.frozen_path.or_as_binary", [](auto* file, auto) { return file->frozen_path().object_utf8_or_as_binary(); });
|
||||
CMD2_FILE("f.frozen_path.realpath.or_empty", [](auto* file, auto) { return resolve_path(file->frozen_path().str()); });
|
||||
CMD2_FILE("f.frozen_path.realpath.or_throw", [](auto* file, auto) { return resolve_path_or_throw(file->frozen_path().str()); });
|
||||
|
||||
CMD2_FILE("f.match_depth_prev", std::bind(&torrent::File::match_depth_prev, std::placeholders::_1));
|
||||
CMD2_FILE("f.match_depth_next", std::bind(&torrent::File::match_depth_next, std::placeholders::_1));
|
||||
@@ -108,6 +131,14 @@ initialize_command_file() {
|
||||
rpc::rpc.mark_safe("f.path_components");
|
||||
rpc::rpc.mark_safe("f.path_depth");
|
||||
rpc::rpc.mark_safe("f.frozen_path");
|
||||
rpc::rpc.mark_safe("f.frozen_path.realpath.or_empty");
|
||||
rpc::rpc.mark_safe("f.frozen_path.realpath.or_throw");
|
||||
rpc::rpc.mark_safe("f.frozen_path.hex");
|
||||
rpc::rpc.mark_safe("f.frozen_path.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");
|
||||
|
||||
+45
-42
@@ -1,5 +1,8 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
#include <torrent/download/resource_manager.h>
|
||||
#include <torrent/download/choke_group.h>
|
||||
#include <torrent/download/choke_queue.h>
|
||||
@@ -111,7 +114,7 @@ apply_cg_all_update_balance(bool is_up) {
|
||||
//
|
||||
#else
|
||||
|
||||
std::vector<torrent::choke_group*> cg_list_hack;
|
||||
std::vector<std::unique_ptr<torrent::choke_group>> cg_list_hack;
|
||||
|
||||
int64_t
|
||||
cg_get_index(const torrent::Object& raw_args) {
|
||||
@@ -121,7 +124,7 @@ cg_get_index(const torrent::Object& raw_args) {
|
||||
|
||||
if (arg.is_string()) {
|
||||
if (!rpc::parse_whole_value_nothrow(arg.as_string().c_str(), &index)) {
|
||||
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](torrent::choke_group* cg) { return arg.as_string() == cg->name(); });
|
||||
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](const auto& cg) { return arg.as_string() == cg->name(); });
|
||||
|
||||
if (itr == cg_list_hack.end())
|
||||
throw torrent::input_error("Choke group not found.");
|
||||
@@ -149,7 +152,7 @@ cg_get_group(const torrent::Object& raw_args) {
|
||||
if ((size_t)index >= cg_list_hack.size())
|
||||
throw torrent::input_error("Choke group not found.");
|
||||
|
||||
return cg_list_hack.at(index);
|
||||
return cg_list_hack.at(index).get();
|
||||
}
|
||||
|
||||
int64_t cg_d_group(core::Download* download) { return download->group(); }
|
||||
@@ -162,7 +165,7 @@ torrent::Object
|
||||
apply_cg_list() {
|
||||
torrent::Object::list_type result;
|
||||
|
||||
for (auto itr : cg_list_hack)
|
||||
for (const auto& itr : cg_list_hack)
|
||||
result.push_back(itr->name());
|
||||
|
||||
return torrent::Object::from_list(result);
|
||||
@@ -180,21 +183,21 @@ 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::any_of(cg_list_hack.begin(), cg_list_hack.end(), [&arg](const auto& cg) { return arg == cg->name(); }))
|
||||
throw torrent::input_error("Duplicate name for choke group.");
|
||||
|
||||
cg_list_hack.push_back(new torrent::choke_group());
|
||||
cg_list_hack.push_back(std::make_unique<torrent::choke_group>());
|
||||
cg_list_hack.back()->set_name(arg);
|
||||
|
||||
cg_list_hack.back()->up_queue()->set_heuristics(torrent::choke_queue::HEURISTICS_UPLOAD_LEECH);
|
||||
cg_list_hack.back()->down_queue()->set_heuristics(torrent::choke_queue::HEURISTICS_DOWNLOAD_LEECH);
|
||||
cg_list_hack.back()->up_queue()->set_heuristics(torrent::HEURISTICS_UPLOAD_LEECH);
|
||||
cg_list_hack.back()->down_queue()->set_heuristics(torrent::HEURISTICS_DOWNLOAD_LEECH);
|
||||
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_cg_index_of(const std::string& arg) {
|
||||
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](torrent::choke_group* cg) { return arg == cg->name(); });
|
||||
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](const auto& cg) { return arg == cg->name(); });
|
||||
|
||||
if (itr == cg_list_hack.end())
|
||||
throw torrent::input_error("Choke group not found.");
|
||||
@@ -206,7 +209,7 @@ torrent::Object
|
||||
apply_cg_all_update_balance(bool is_up) {
|
||||
LT_LOG_SUBSYSTEM("apply update balance: hack is_up:%i", (int)is_up);
|
||||
|
||||
for (auto itr : cg_list_hack) {
|
||||
for (const auto& itr : cg_list_hack) {
|
||||
if (is_up)
|
||||
itr->up_queue()->balance();
|
||||
else
|
||||
@@ -230,6 +233,9 @@ apply_cg_max_set(const torrent::Object::list_type& args, bool is_up) {
|
||||
int64_t second_arg = 0;
|
||||
rpc::parse_whole_value(args.back().as_string().c_str(), &second_arg);
|
||||
|
||||
if (second_arg < -1 || second_arg > std::numeric_limits<uint32_t>::max())
|
||||
throw torrent::input_error("Max unchoked must be between -1 and 4294967295.");
|
||||
|
||||
if (is_up)
|
||||
cg_get_group(args.front())->up_queue()->set_max_unchoked(second_arg);
|
||||
else
|
||||
@@ -337,54 +343,51 @@ options.
|
||||
|
||||
void
|
||||
initialize_command_groups() {
|
||||
CMD2_ANY ("choke_group.list", std::bind(&apply_cg_list));
|
||||
CMD2_ANY_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2));
|
||||
CMD_ANY ("choke_group.list", std::bind(&apply_cg_list));
|
||||
CMD_ANY_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2));
|
||||
|
||||
#if USE_CHOKE_GROUP
|
||||
CMD2_ANY ("choke_group.size", std::bind(&torrent::ResourceManager::group_size, torrent::resource_manager()));
|
||||
CMD2_ANY_STRING ("choke_group.index_of", std::bind(&torrent::ResourceManager::group_index_of, torrent::resource_manager(), std::placeholders::_2));
|
||||
CMD_ANY ("choke_group.size", std::bind(&torrent::ResourceManager::group_size, torrent::resource_manager()));
|
||||
CMD_ANY_STRING ("choke_group.index_of", std::bind(&torrent::ResourceManager::group_index_of, torrent::resource_manager(), std::placeholders::_2));
|
||||
#else
|
||||
apply_cg_insert("default");
|
||||
|
||||
CMD2_ANY ("choke_group.size", std::bind(&std::vector<torrent::choke_group*>::size, cg_list_hack));
|
||||
CMD2_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2));
|
||||
CMD_ANY ("choke_group.size", [](auto, auto) { return (int64_t)cg_list_hack.size(); });
|
||||
CMD_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2));
|
||||
#endif
|
||||
|
||||
// Commands specific for a group. Supports as the first argument the
|
||||
// name, the index or a negative index.
|
||||
CMD2_ANY ("choke_group.general.size", std::bind(&torrent::choke_group::size, CG_GROUP_AT()));
|
||||
CMD_ANY ("choke_group.general.size", std::bind(&torrent::choke_group::size, CG_GROUP_AT()));
|
||||
|
||||
CMD2_ANY ("choke_group.tracker.mode", std::bind(&torrent::option_as_string, torrent::OPTION_TRACKER_MODE,
|
||||
std::bind(&torrent::choke_group::tracker_mode, CG_GROUP_AT())));
|
||||
CMD2_ANY_LIST ("choke_group.tracker.mode.set", std::bind(&apply_cg_tracker_mode_set, std::placeholders::_2));
|
||||
CMD_ANY ("choke_group.tracker.mode", [](auto, auto arg) { return torrent::option_to_str_or_throw(torrent::OPTION_TRACKER_MODE, cg_get_group(arg)->tracker_mode()); });
|
||||
CMD_ANY_LIST ("choke_group.tracker.mode.set", [](auto, auto arg) { return apply_cg_tracker_mode_set(arg); });
|
||||
|
||||
CMD2_ANY ("choke_group.all.up.update_balance", std::bind(&apply_cg_all_update_balance, true));
|
||||
CMD2_ANY ("choke_group.all.down.update_balance", std::bind(&apply_cg_all_update_balance, false));
|
||||
CMD_ANY ("choke_group.all.up.update_balance", std::bind(&apply_cg_all_update_balance, true));
|
||||
CMD_ANY ("choke_group.all.down.update_balance", std::bind(&apply_cg_all_update_balance, false));
|
||||
|
||||
CMD2_ANY ("choke_group.up.rate", std::bind(&torrent::choke_group::up_rate, CG_GROUP_AT()));
|
||||
CMD2_ANY ("choke_group.down.rate", std::bind(&torrent::choke_group::down_rate, CG_GROUP_AT()));
|
||||
CMD_ANY ("choke_group.up.rate", std::bind(&torrent::choke_group::up_rate, CG_GROUP_AT()));
|
||||
CMD_ANY ("choke_group.down.rate", std::bind(&torrent::choke_group::down_rate, CG_GROUP_AT()));
|
||||
|
||||
CMD2_ANY ("choke_group.up.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::up_queue)));
|
||||
CMD2_ANY ("choke_group.up.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::up_queue)));
|
||||
CMD2_ANY_LIST ("choke_group.up.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, true));
|
||||
CMD_ANY ("choke_group.up.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::up_queue)));
|
||||
CMD_ANY ("choke_group.up.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::up_queue)));
|
||||
CMD_ANY_LIST ("choke_group.up.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, true));
|
||||
|
||||
CMD2_ANY ("choke_group.up.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::up_queue)));
|
||||
CMD2_ANY ("choke_group.up.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::up_queue)));
|
||||
CMD2_ANY ("choke_group.up.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::up_queue)));
|
||||
CMD2_ANY ("choke_group.up.heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS,
|
||||
std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::up_queue))));
|
||||
CMD2_ANY_LIST ("choke_group.up.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, true));
|
||||
CMD_ANY ("choke_group.up.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::up_queue)));
|
||||
CMD_ANY ("choke_group.up.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::up_queue)));
|
||||
CMD_ANY ("choke_group.up.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::up_queue)));
|
||||
CMD_ANY ("choke_group.up.heuristics", [](auto, auto arg) { return torrent::option_to_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, cg_get_group(arg)->up_queue()->heuristics()); });
|
||||
CMD_ANY_LIST ("choke_group.up.heuristics.set", [](auto, auto arg) { return apply_cg_heuristics_set(arg, true); });
|
||||
|
||||
CMD2_ANY ("choke_group.down.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::down_queue)));
|
||||
CMD2_ANY ("choke_group.down.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::down_queue)));
|
||||
CMD2_ANY_LIST ("choke_group.down.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, false));
|
||||
CMD_ANY ("choke_group.down.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::down_queue)));
|
||||
CMD_ANY ("choke_group.down.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::down_queue)));
|
||||
CMD_ANY_LIST ("choke_group.down.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, false));
|
||||
|
||||
CMD2_ANY ("choke_group.down.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::down_queue)));
|
||||
CMD2_ANY ("choke_group.down.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::down_queue)));
|
||||
CMD2_ANY ("choke_group.down.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::down_queue)));
|
||||
CMD2_ANY ("choke_group.down.heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS,
|
||||
std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::down_queue))));
|
||||
CMD2_ANY_LIST ("choke_group.down.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, false));
|
||||
CMD_ANY ("choke_group.down.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::down_queue)));
|
||||
CMD_ANY ("choke_group.down.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::down_queue)));
|
||||
CMD_ANY ("choke_group.down.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::down_queue)));
|
||||
CMD_ANY ("choke_group.down.heuristics", [](auto, auto arg) { return torrent::option_to_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, cg_get_group(arg)->down_queue()->heuristics()); });
|
||||
CMD_ANY_LIST ("choke_group.down.heuristics.set", [](auto, auto arg) { return apply_cg_heuristics_set(arg, false); });
|
||||
|
||||
rpc::rpc.mark_safe("choke_group.list");
|
||||
rpc::rpc.mark_safe("choke_group.size");
|
||||
|
||||
@@ -19,6 +19,8 @@ void initialize_command_groups();
|
||||
void initialize_command_throttle();
|
||||
void initialize_command_tracker();
|
||||
void initialize_command_scheduler();
|
||||
void initialize_command_string();
|
||||
void initialize_command_system();
|
||||
void initialize_command_ui();
|
||||
|
||||
void
|
||||
@@ -37,4 +39,6 @@ initialize_commands() {
|
||||
initialize_command_throttle();
|
||||
initialize_command_tracker();
|
||||
initialize_command_scheduler();
|
||||
initialize_command_string();
|
||||
initialize_command_system();
|
||||
}
|
||||
|
||||
+32
-1
@@ -1,12 +1,44 @@
|
||||
#ifndef RTORRENT_UTILS_COMMAND_HELPERS_H
|
||||
#define RTORRENT_UTILS_COMMAND_HELPERS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "rpc/command.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "rpc/object_storage.h"
|
||||
|
||||
void initialize_commands();
|
||||
|
||||
inline uint16_t
|
||||
checked_port_value(int64_t value, const char* label) {
|
||||
if (value < 0 || value > std::numeric_limits<uint16_t>::max())
|
||||
throw torrent::input_error(std::string("Invalid ") + label + " port number.");
|
||||
|
||||
return static_cast<uint16_t>(value);
|
||||
}
|
||||
|
||||
//
|
||||
// Aliases with CMD_* for the below
|
||||
//
|
||||
|
||||
#define CMD_ANY(key, slot) CMD2_ANY(key, slot)
|
||||
#define CMD_ANY_P(key, slot) CMD2_ANY_P(key, slot)
|
||||
#define CMD_REDIRECT(key, slot) CMD2_REDIRECT(key, slot)
|
||||
#define CMD_REDIRECT_NO_EXPORT(key, slot) CMD2_REDIRECT_NO_EXPORT(key, slot)
|
||||
#define CMD_ANY_STRING(key, slot) CMD2_ANY_STRING(key, slot)
|
||||
#define CMD_ANY_STRING_V(key, slot) CMD2_ANY_STRING_V(key, slot)
|
||||
#define CMD_ANY_LIST(key, slot) CMD2_ANY_LIST(key, slot)
|
||||
#define CMD_VAR_VALUE(key, value) CMD2_VAR_VALUE(key, value)
|
||||
#define CMD_VAR_BOOL(key, value) CMD2_VAR_BOOL(key, value)
|
||||
#define CMD_VAR_STRING(key, value) CMD2_VAR_STRING(key, value)
|
||||
#define CMD_VAR_C_STRING(key, value) CMD2_VAR_C_STRING(key, value)
|
||||
#define CMD_VAR_LIST(key) CMD2_VAR_LIST(key)
|
||||
#define CMD_ANY_V(key, slot) CMD2_ANY_V(key, slot)
|
||||
#define CMD_ANY_VALUE_V(key, slot) CMD2_ANY_VALUE_V(key, slot)
|
||||
|
||||
//
|
||||
// New std::function based command_base helper functions:
|
||||
//
|
||||
@@ -115,7 +147,6 @@ void initialize_commands();
|
||||
#define CMD2_REDIRECT_TRACKER(from_key, to_key) \
|
||||
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_tracker_target | rpc::CommandMap::flag_dont_delete);
|
||||
|
||||
|
||||
//
|
||||
// Conversion of return types:
|
||||
//
|
||||
|
||||
+20
-7
@@ -29,7 +29,7 @@ torrent::Object
|
||||
apply_ip_tables_size_data(const std::string& args) {
|
||||
rpc::ip_table_list::const_iterator itr = ip_tables.find(args);
|
||||
|
||||
if (itr != ip_tables.end())
|
||||
if (itr == ip_tables.end())
|
||||
throw torrent::input_error("IP table does not exist.");
|
||||
|
||||
uint32_t size = itr->table.sizeof_data();
|
||||
@@ -131,7 +131,7 @@ ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address
|
||||
// copy everything up to '#' to address_copy and work from there
|
||||
while(address[address_start_index] != '#' && address[address_start_index] != '\r' &&
|
||||
address[address_start_index] != '\n' && address[address_start_index] != '\0' &&
|
||||
address_start_index < 4096 ) {
|
||||
address_start_index < 4095 ) {
|
||||
|
||||
address_copy[address_start_index] = address[address_start_index];
|
||||
address_start_index++;
|
||||
@@ -203,9 +203,17 @@ ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address
|
||||
uint32_t mask=0;
|
||||
uint32_t end_mask=0;
|
||||
|
||||
mask = (~mask) << (32-mask_bits);
|
||||
if (mask_bits == 0) {
|
||||
mask = 0;
|
||||
end_mask = ~(uint32_t)0;
|
||||
} else if (mask_bits == 32) {
|
||||
mask = ~(uint32_t)0;
|
||||
end_mask = 0;
|
||||
} else {
|
||||
mask = (~mask) << (32-mask_bits);
|
||||
end_mask = (~end_mask) >> mask_bits;
|
||||
}
|
||||
*address_start = ip & mask;
|
||||
end_mask = (~end_mask) >> mask_bits;
|
||||
*address_end = (ip & mask) | end_mask;
|
||||
|
||||
valid=true;
|
||||
@@ -311,6 +319,11 @@ apply_ipv4_filter_load(const torrent::Object::list_type& args) {
|
||||
ipv4_filter_parse(buffer, value);
|
||||
}
|
||||
|
||||
if (file.fail() && !file.eof()) {
|
||||
lineNumber++;
|
||||
throw torrent::input_error("Exceeded max line length.");
|
||||
}
|
||||
|
||||
} catch (torrent::input_error& e) {
|
||||
snprintf(buffer, 2048, "Error in ip filter file: %s:%u: %s", filename.c_str(), lineNumber, e.what());
|
||||
|
||||
@@ -331,8 +344,8 @@ apply_ipv4_filter_dump() {
|
||||
torrent::Object raw_result = torrent::Object::create_list();
|
||||
torrent::Object::list_type& result = raw_result.as_list();
|
||||
|
||||
torrent::ipv4_table::range_map_type range_map = torrent::PeerList::ipv4_filter()->range_map;
|
||||
torrent::ipv4_table::range_map_type::iterator iter = range_map.begin();
|
||||
const torrent::ipv4_table::range_map_type& range_map = torrent::PeerList::ipv4_filter()->range_map;
|
||||
torrent::ipv4_table::range_map_type::const_iterator iter = range_map.begin();
|
||||
|
||||
while(iter != range_map.end()) {
|
||||
char buffer[64];
|
||||
@@ -348,7 +361,7 @@ apply_ipv4_filter_dump() {
|
||||
inet_ntop(AF_INET, &net_start, start_str, INET_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET, &net_end, end_str, INET_ADDRSTRLEN);
|
||||
|
||||
snprintf(buffer, 64, "%s-%s %s", start_str, end_str, torrent::option_as_string(torrent::OPTION_IP_FILTER, value));
|
||||
snprintf(buffer, 64, "%s-%s %s", start_str, end_str, torrent::option_to_c_str_or_throw(torrent::OPTION_IP_FILTER, value));
|
||||
|
||||
result.push_back((std::string)buffer);
|
||||
|
||||
|
||||
+77
-249
@@ -1,32 +1,16 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <fcntl.h>
|
||||
#include <functional>
|
||||
#include <stdio.h>
|
||||
#include <unistd.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/utils/chrono.h>
|
||||
#include <torrent/utils/option_strings.h>
|
||||
#include <torrent/runtime/memory_manager.h>
|
||||
#include <torrent/utils/log.h>
|
||||
|
||||
#include "command_helpers.h"
|
||||
#include "control.h"
|
||||
#include "core/download.h"
|
||||
#include "core/download_list.h"
|
||||
#include "core/manager.h"
|
||||
#include "rak/string_manip.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "rpc/lua.h"
|
||||
#include "rpc/scgi.h"
|
||||
#include "session/session_manager.h"
|
||||
#include "utils/file_status_cache.h"
|
||||
|
||||
#include "globals.h"
|
||||
#include "rpc/lua.h"
|
||||
#include "control.h"
|
||||
#include "command_helpers.h"
|
||||
|
||||
typedef torrent::ChunkManager CM_t;
|
||||
typedef torrent::FileManager FM_t;
|
||||
@@ -42,49 +26,6 @@ apply_pieces_stats_total_size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_env(const torrent::Object::string_type& arg) {
|
||||
if (arg.empty())
|
||||
throw torrent::input_error("system.env: Missing variable name.");
|
||||
|
||||
char* val = getenv(arg.c_str());
|
||||
return std::string(val ? val : "");
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_hostname() {
|
||||
char buffer[1024];
|
||||
|
||||
if (gethostname(buffer, 1023) == -1)
|
||||
throw torrent::input_error("Unable to read hostname.");
|
||||
|
||||
// if (shorten)
|
||||
// *std::find(buffer, buffer + 1023, '.') = '\0';
|
||||
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_get_cwd() {
|
||||
char* buffer = getcwd(NULL, 0);
|
||||
|
||||
if (buffer == NULL)
|
||||
throw torrent::input_error("Unable to read cwd.");
|
||||
|
||||
torrent::Object result = torrent::Object(std::string(buffer));
|
||||
free(buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_set_cwd(const torrent::Object::string_type& rawArgs) {
|
||||
if (::chdir(rawArgs.c_str()) != 0)
|
||||
throw torrent::input_error("Could not change current working directory.");
|
||||
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
inline torrent::Object::list_const_iterator
|
||||
post_increment(torrent::Object::list_const_iterator& itr, const torrent::Object::list_const_iterator& last) {
|
||||
if (itr == last)
|
||||
@@ -95,8 +36,12 @@ post_increment(torrent::Object::list_const_iterator& itr, const torrent::Object:
|
||||
|
||||
inline const std::string&
|
||||
check_name(const std::string& str) {
|
||||
if (!rak::is_all_name(str))
|
||||
throw torrent::input_error("Non-alphanumeric characters found.");
|
||||
auto itr = std::find_if(str.begin(), str.end(), [](char c) {
|
||||
return !std::isalnum(c, std::locale::classic()) && c != '_';
|
||||
});
|
||||
|
||||
if (itr != str.end())
|
||||
throw torrent::input_error("Invalid characters found in name.");
|
||||
|
||||
return str;
|
||||
}
|
||||
@@ -110,9 +55,9 @@ group_insert(const torrent::Object::list_type& args) {
|
||||
const std::string& view = check_name(post_increment(itr, last)->as_string());
|
||||
|
||||
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.enable", "simple",
|
||||
"schedule2=group." + name + ".ratio,5,60,on_ratio=" + name));
|
||||
"schedule=group." + name + ".ratio,5,60,on_ratio=" + name));
|
||||
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.disable", "simple",
|
||||
"schedule_remove2=group." + name + ".ratio"));
|
||||
"schedule.remove=group." + name + ".ratio"));
|
||||
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.command", "simple",
|
||||
"d.try_close= ;d.ignore_commands.set=1"));
|
||||
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".view", "string", view));
|
||||
@@ -129,219 +74,102 @@ group_insert(const torrent::Object::list_type& args) {
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.upload");
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.upload.set");
|
||||
|
||||
if (rpc::call_command_value("method.use_intermediate") == 3) {
|
||||
// Cleaned up in 0.16.1:
|
||||
|
||||
CMD2_REDIRECT_MUTABLE("group2." + name + ".view", "group." + name + ".view");
|
||||
CMD2_REDIRECT_MUTABLE("group2." + name + ".view.set", "group." + name + ".view.set");
|
||||
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.min", "group." + name + ".ratio.min");
|
||||
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.min.set", "group." + name + ".ratio.min.set");
|
||||
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.max", "group." + name + ".ratio.max");
|
||||
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.max.set", "group." + name + ".ratio.max.set");
|
||||
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.upload", "group." + name + ".ratio.upload");
|
||||
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.upload.set", "group." + name + ".ratio.upload.set");
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static const int file_print_use_space = 0x1;
|
||||
static const int file_print_delim_space = 0x2;
|
||||
|
||||
void
|
||||
file_print_list(torrent::Object::list_const_iterator first, torrent::Object::list_const_iterator last, FILE* output, int flags) {
|
||||
while (first != last) {
|
||||
switch (first->type()) {
|
||||
case torrent::Object::TYPE_STRING:
|
||||
fprintf(output, (const char*)" %s" + !(flags & file_print_use_space), first->as_string().c_str());
|
||||
break;
|
||||
case torrent::Object::TYPE_VALUE:
|
||||
fprintf(output, (const char*)" %" PRIi64 + !(flags & file_print_use_space), first->as_value());
|
||||
break;
|
||||
case torrent::Object::TYPE_LIST:
|
||||
file_print_list(first->as_list().begin(), first->as_list().end(), output, 0);
|
||||
break;
|
||||
case torrent::Object::TYPE_NONE:
|
||||
break;
|
||||
default:
|
||||
throw torrent::input_error("Invalid type.");
|
||||
}
|
||||
|
||||
flags |= (flags & file_print_delim_space) >> 1;
|
||||
first++;
|
||||
}
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
cmd_file_append(const torrent::Object::list_type& args) {
|
||||
if (args.empty())
|
||||
throw torrent::input_error("Invalid number of arguments.");
|
||||
|
||||
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));
|
||||
|
||||
try {
|
||||
file_print_list(++args.begin(), args.end(), output, file_print_delim_space);
|
||||
fprintf(output, "\n");
|
||||
} catch (...) {
|
||||
fclose(output);
|
||||
throw;
|
||||
}
|
||||
fclose(output);
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
void
|
||||
initialize_command_local() {
|
||||
core::DownloadList* dList = control->core()->download_list();
|
||||
torrent::ChunkManager* chunkManager = torrent::chunk_manager();
|
||||
torrent::FileManager* fileManager = torrent::file_manager();
|
||||
core::DownloadList* dList = control->core()->download_list();
|
||||
|
||||
CMD2_ANY ("system.hostname", std::bind(&system_hostname));
|
||||
CMD2_ANY ("system.pid", std::bind(&getpid));
|
||||
|
||||
CMD2_VAR_C_STRING("system.api_version", (int64_t)API_VERSION);
|
||||
CMD2_VAR_C_STRING("system.client_version", PACKAGE_VERSION);
|
||||
CMD2_VAR_C_STRING("system.library_version", torrent::version());
|
||||
|
||||
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");
|
||||
|
||||
CMD2_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size,
|
||||
(utils::FileStatusCache::base_type*)control->core()->file_status_cache()));
|
||||
CMD2_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache()));
|
||||
|
||||
CMD2_VAR_BOOL ("file.prioritize_toc", 0);
|
||||
CMD2_VAR_LIST ("file.prioritize_toc.first");
|
||||
CMD2_VAR_LIST ("file.prioritize_toc.last");
|
||||
|
||||
CMD2_ANY ("system.files.advise_random", std::bind(&FM_t::advise_random, fileManager));
|
||||
CMD2_ANY_VALUE_V ("system.files.advise_random.set", std::bind(&FM_t::set_advise_random, fileManager, std::placeholders::_2));
|
||||
CMD2_ANY ("system.files.advise_random.hashing", std::bind(&FM_t::advise_random_hashing, fileManager));
|
||||
CMD2_ANY_VALUE_V ("system.files.advise_random.hashing.set", std::bind(&FM_t::set_advise_random_hashing, fileManager, std::placeholders::_2));
|
||||
CMD2_ANY ("system.files.session.fdatasync", [](auto, auto) { return session_thread::manager()->use_fsyncdisk(); });
|
||||
CMD2_ANY_VALUE_V ("system.files.session.fdatasync.set", [](auto, auto& value) { return session_thread::manager()->set_use_fsyncdisk(value); });
|
||||
|
||||
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));
|
||||
|
||||
CMD2_ANY_STRING ("system.env", std::bind(&system_env, std::placeholders::_2));
|
||||
|
||||
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 ("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 0; });
|
||||
CMD_ANY_VALUE_V ("pieces.sync.timeout_safe.set", [](auto, auto) {
|
||||
lt_log_print(torrent::LOG_WARN, "pieces.sync.timeout_safe.set is no longer supported and does nothing.");
|
||||
});
|
||||
CMD_ANY ("pieces.sync.queue_size", [](auto, auto) { return torrent::runtime::memory_manager()->sync_queue_block_count(); });
|
||||
|
||||
CMD2_ANY_VALUE_V ("system.umask.set", std::bind(&umask, std::placeholders::_2));
|
||||
CMD_ANY ("pieces.preload.type", [](auto, auto) { return torrent::runtime::memory_manager()->preload_type(); });
|
||||
CMD_ANY_VALUE_V ("pieces.preload.type.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_preload_type(value); });
|
||||
CMD_ANY ("pieces.preload.min_size", [](auto, auto) { return torrent::runtime::memory_manager()->preload_min_size(); });
|
||||
CMD_ANY_VALUE_V ("pieces.preload.min_size.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_preload_min_size(value); });
|
||||
CMD_ANY ("pieces.preload.min_rate", [](auto, auto) { return torrent::runtime::memory_manager()->preload_required_rate(); });
|
||||
CMD_ANY_VALUE_V ("pieces.preload.min_rate.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_preload_required_rate(value); });
|
||||
|
||||
CMD2_VAR_BOOL ("system.daemon", false);
|
||||
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));
|
||||
|
||||
CMD2_ANY_V ("system.shutdown.normal", std::bind(&Control::receive_normal_shutdown, control));
|
||||
CMD2_ANY_V ("system.shutdown.quick", std::bind(&Control::receive_quick_shutdown, control));
|
||||
CMD2_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
|
||||
CMD_ANY ("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); });
|
||||
|
||||
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 ("pieces.hash.queue_size", std::bind(&torrent::main_thread::hash_queue_size));
|
||||
CMD_VAR_BOOL ("pieces.hash.on_completion", true);
|
||||
|
||||
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_VAR_STRING ("directory.default", "./");
|
||||
CMD_ANY ("directory.default.realpath.or_empty", [](auto, auto) { return resolve_path(rpc::call_command_string("directory.default")); });
|
||||
CMD_ANY ("directory.default.realpath.or_throw", [](auto, auto) { return resolve_path_or_throw(rpc::call_command_string("directory.default")); });
|
||||
|
||||
CMD2_ANY ("pieces.preload.type", std::bind(&CM_t::preload_type, chunkManager));
|
||||
CMD2_ANY_VALUE_V ("pieces.preload.type.set", std::bind(&CM_t::set_preload_type, chunkManager, std::placeholders::_2));
|
||||
CMD2_ANY ("pieces.preload.min_size", std::bind(&CM_t::preload_min_size, chunkManager));
|
||||
CMD2_ANY_VALUE_V ("pieces.preload.min_size.set", std::bind(&CM_t::set_preload_min_size, chunkManager, std::placeholders::_2));
|
||||
CMD2_ANY ("pieces.preload.min_rate", std::bind(&CM_t::preload_required_rate, chunkManager));
|
||||
CMD2_ANY_VALUE_V ("pieces.preload.min_rate.set", std::bind(&CM_t::set_preload_required_rate, chunkManager, std::placeholders::_2));
|
||||
CMD_VAR_STRING ("session.name", "");
|
||||
CMD_ANY ("session.path", [](auto, auto) { return session_thread::manager()->path(); });
|
||||
CMD_ANY ("session.path.realpath.or_empty", [](auto, auto) { return resolve_path(session_thread::manager()->path()); });
|
||||
CMD_ANY ("session.path.realpath.or_throw", [](auto, auto) { return resolve_path_or_throw(session_thread::manager()->path()); });
|
||||
CMD_ANY_STRING_V("session.path.set", [](auto, auto& str) { return session_thread::manager()->set_path(str); });
|
||||
CMD_ANY ("session.use_lock", [](auto, auto) { return session_thread::manager()->use_lock(); });
|
||||
CMD_ANY_VALUE_V ("session.use_lock.set", [](auto, auto& value) { return session_thread::manager()->set_use_lock(value); });
|
||||
CMD_VAR_BOOL ("session.on_completion", true);
|
||||
|
||||
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_V ("session.save", [dList](auto, auto) { return dList->session_save(); });
|
||||
|
||||
CMD2_ANY ("pieces.stats.total_size", std::bind(&apply_pieces_stats_total_size));
|
||||
|
||||
CMD2_ANY ("pieces.hash.queue_size", std::bind(&torrent::main_thread::hash_queue_size));
|
||||
CMD2_VAR_BOOL ("pieces.hash.on_completion", true);
|
||||
|
||||
CMD2_VAR_STRING ("directory.default", "./");
|
||||
|
||||
CMD2_VAR_STRING ("session.name", "");
|
||||
CMD2_ANY ("session.path", [](auto, auto) { return session_thread::manager()->path(); });
|
||||
CMD2_ANY_STRING_V("session.path.set", [](auto, auto& str) { return session_thread::manager()->set_path(str); });
|
||||
CMD2_ANY ("session.use_lock", [](auto, auto) { return session_thread::manager()->use_lock(); });
|
||||
CMD2_ANY_VALUE_V ("session.use_lock.set", [](auto, auto& value) { return session_thread::manager()->set_use_lock(value); });
|
||||
CMD2_VAR_BOOL ("session.on_completion", true);
|
||||
|
||||
CMD2_ANY_V ("session.save", [dList](auto, auto) { return dList->session_save(); });
|
||||
|
||||
CMD2_ANY ("magnet.path", [](auto, auto) { return control->core()->magnet_path(); });
|
||||
CMD2_ANY_STRING_V("magnet.path.set", [](auto, auto& str) { return control->core()->set_magnet_path(str); });
|
||||
CMD_ANY ("magnet.path", [](auto, auto) { return control->core()->magnet_path(); });
|
||||
CMD_ANY_STRING_V("magnet.path.set", [](auto, auto& str) { return control->core()->set_magnet_path(str); });
|
||||
|
||||
#ifdef HAVE_LUA
|
||||
rpc::LuaEngine* lua_engine = control->lua_engine();
|
||||
|
||||
CMD2_ANY ("lua.execute", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, 0));
|
||||
CMD2_ANY ("lua.execute.str", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, rpc::LuaEngine::flag_string));
|
||||
CMD_ANY ("lua.execute", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, 0));
|
||||
CMD_ANY ("lua.execute.str", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, rpc::LuaEngine::flag_string));
|
||||
#endif
|
||||
|
||||
#define CMD2_EXECUTE(key, flags) \
|
||||
CMD2_ANY(key, std::bind(&rpc::ExecFile::execute_object, &rpc::execFile, std::placeholders::_2, flags));
|
||||
#define CMD_EXECUTE(key, flags) \
|
||||
CMD_ANY(key, std::bind(&rpc::ExecFile::execute_object, &rpc::execFile, std::placeholders::_2, flags));
|
||||
|
||||
CMD2_EXECUTE ("execute", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
|
||||
CMD2_EXECUTE ("execute2", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
|
||||
CMD2_EXECUTE ("execute.throw", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
|
||||
CMD2_EXECUTE ("execute.throw.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background);
|
||||
CMD2_EXECUTE ("execute.nothrow", rpc::ExecFile::flag_expand_tilde);
|
||||
CMD2_EXECUTE ("execute.nothrow.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_background);
|
||||
CMD2_EXECUTE ("execute.raw", rpc::ExecFile::flag_throw);
|
||||
CMD2_EXECUTE ("execute.raw.bg", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background);
|
||||
CMD2_EXECUTE ("execute.raw_nothrow", 0);
|
||||
CMD2_EXECUTE ("execute.raw_nothrow.bg", rpc::ExecFile::flag_background);
|
||||
CMD2_EXECUTE ("execute.capture", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
|
||||
CMD2_EXECUTE ("execute.capture_nothrow", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
|
||||
|
||||
CMD2_ANY_LIST ("file.append", std::bind(&cmd_file_append, std::placeholders::_2));
|
||||
CMD_EXECUTE ("execute", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
|
||||
CMD_EXECUTE ("execute.throw", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
|
||||
CMD_EXECUTE ("execute.throw.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background);
|
||||
CMD_EXECUTE ("execute.nothrow", rpc::ExecFile::flag_expand_tilde);
|
||||
CMD_EXECUTE ("execute.nothrow.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_background);
|
||||
CMD_EXECUTE ("execute.raw", rpc::ExecFile::flag_throw);
|
||||
CMD_EXECUTE ("execute.raw.bg", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background);
|
||||
CMD_EXECUTE ("execute.raw_nothrow", 0);
|
||||
CMD_EXECUTE ("execute.raw_nothrow.bg", rpc::ExecFile::flag_background);
|
||||
CMD_EXECUTE ("execute.capture", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
|
||||
CMD_EXECUTE ("execute.capture_nothrow", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
|
||||
|
||||
// TODO: Convert to new command types:
|
||||
*rpc::command_base::argument(0) = "placeholder.0";
|
||||
*rpc::command_base::argument(1) = "placeholder.1";
|
||||
*rpc::command_base::argument(2) = "placeholder.2";
|
||||
*rpc::command_base::argument(3) = "placeholder.3";
|
||||
CMD2_ANY_P("argument.0", std::bind(&rpc::command_base::argument_ref, 0));
|
||||
CMD2_ANY_P("argument.1", std::bind(&rpc::command_base::argument_ref, 1));
|
||||
CMD2_ANY_P("argument.2", std::bind(&rpc::command_base::argument_ref, 2));
|
||||
CMD2_ANY_P("argument.3", std::bind(&rpc::command_base::argument_ref, 3));
|
||||
CMD_ANY_P("argument.0", std::bind(&rpc::command_base::argument_ref, 0));
|
||||
CMD_ANY_P("argument.1", std::bind(&rpc::command_base::argument_ref, 1));
|
||||
CMD_ANY_P("argument.2", std::bind(&rpc::command_base::argument_ref, 2));
|
||||
CMD_ANY_P("argument.3", std::bind(&rpc::command_base::argument_ref, 3));
|
||||
|
||||
CMD2_ANY_LIST ("group.insert", std::bind(&group_insert, std::placeholders::_2));
|
||||
|
||||
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");
|
||||
CMD_ANY_LIST ("group.insert", std::bind(&group_insert, std::placeholders::_2));
|
||||
|
||||
rpc::rpc.mark_safe("directory.default");
|
||||
rpc::rpc.mark_safe("session.path");
|
||||
rpc::rpc.mark_safe("session.path.realpath.or_empty");
|
||||
rpc::rpc.mark_safe("session.path.realpath.or_throw");
|
||||
rpc::rpc.mark_safe("directory.default.realpath.or_empty");
|
||||
rpc::rpc.mark_safe("directory.default.realpath.or_throw");
|
||||
rpc::rpc.mark_safe("session.use_lock");
|
||||
rpc::rpc.mark_safe("session.on_completion");
|
||||
|
||||
|
||||
+45
-41
@@ -1,6 +1,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <iterator>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <torrent/data/chunk_utils.h>
|
||||
@@ -8,53 +9,24 @@
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/option_strings.h>
|
||||
|
||||
#include "globals.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 "rpc/parse_commands.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;
|
||||
static const int log_flag_flush = 0x8;
|
||||
|
||||
void
|
||||
log_add_group_output_str(const char* group_name, const char* output_id) {
|
||||
int log_group = torrent::option_find_string(torrent::OPTION_LOG_GROUP, group_name);
|
||||
torrent::log_add_group_output(log_group, output_id);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_log_open(int output_flags, const torrent::Object::list_type& args) {
|
||||
if (args.size() < 2)
|
||||
throw torrent::input_error("Invalid number of arguments.");
|
||||
apply_log_open(int output_flags, const torrent::Object::list_type& raw_args) {
|
||||
std::vector<std::string> args;
|
||||
|
||||
torrent::Object::list_const_iterator itr = args.begin();
|
||||
|
||||
auto output_id = (itr++)->as_string();
|
||||
auto file_name = expand_path((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);
|
||||
bool flush = (output_flags & log_flag_flush);
|
||||
|
||||
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, flush);
|
||||
|
||||
while (itr != args.end())
|
||||
log_add_group_output_str((itr++)->as_string().c_str(), output_id.c_str());
|
||||
for (const auto& arg : raw_args)
|
||||
args.push_back(arg.as_string());
|
||||
|
||||
apply_log_open_str(output_flags, args);
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
@@ -63,12 +35,37 @@ 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();
|
||||
}
|
||||
|
||||
// TODO: Deprecated.
|
||||
torrent::Object
|
||||
apply_log(const torrent::Object::string_type& arg, int logType) {
|
||||
@@ -118,11 +115,17 @@ log_vmmap_dump(const std::string& str) {
|
||||
|
||||
FILE* log_file = fopen(str.c_str(), "w");
|
||||
|
||||
if (log_file == NULL)
|
||||
throw torrent::input_error("Could not open log file: " + str);
|
||||
|
||||
for (auto& all_mapping : all_mappings) {
|
||||
fprintf(log_file, "%8p-%8p [%5llxk]\n", all_mapping.ptr, (char*)all_mapping.ptr + all_mapping.length, (long long unsigned int)(all_mapping.length / 1024));
|
||||
}
|
||||
|
||||
fclose(log_file);
|
||||
// The buffered output only reaches the kernel here.
|
||||
if (fclose(log_file) != 0)
|
||||
throw torrent::input_error("Could not write log file: " + str);
|
||||
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
@@ -135,11 +138,12 @@ initialize_command_logging() {
|
||||
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.append_gz_file", std::bind(&apply_log_open, log_flag_append_file | log_flag_use_gz, 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_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));
|
||||
|
||||
+236
-122
@@ -3,18 +3,18 @@
|
||||
#include <functional>
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_SYSTEMD
|
||||
#include <sys/socket.h>
|
||||
#include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
#include <rak/address_info.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/network_config.h>
|
||||
#include <torrent/net/socket_address.h>
|
||||
#include <torrent/runtime/client_config.h>
|
||||
#include <torrent/runtime/network_config.h>
|
||||
#include <torrent/runtime/network_manager.h>
|
||||
#include <torrent/runtime/proxy_manager.h>
|
||||
#include <torrent/runtime/runtime.h>
|
||||
#include <torrent/runtime/socket_manager.h>
|
||||
#include <torrent/tracker/tracker.h>
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/option_strings.h>
|
||||
@@ -29,22 +29,121 @@
|
||||
#include "rpc/parse.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
#include <sys/socket.h>
|
||||
#include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
|
||||
torrent::Object
|
||||
apply_encryption(const torrent::Object::list_type& args) {
|
||||
uint32_t options_mask = torrent::net::NetworkConfig::encryption_none;
|
||||
listen_port_range() {
|
||||
auto port_range = torrent::runtime::client_config()->listen_port_range();
|
||||
|
||||
for (const auto& arg : args) {
|
||||
uint32_t opt = torrent::option_find_string(torrent::OPTION_ENCRYPTION, arg.as_string().c_str());
|
||||
return std::to_string(port_range.first) + "-" + std::to_string(port_range.second);
|
||||
}
|
||||
|
||||
if (opt == torrent::net::NetworkConfig::encryption_none)
|
||||
options_mask = torrent::net::NetworkConfig::encryption_none;
|
||||
else
|
||||
options_mask |= opt;
|
||||
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 + "'");
|
||||
}
|
||||
}
|
||||
|
||||
torrent::config::network_config()->set_encryption_options(options_mask);
|
||||
lt_log_print(torrent::LOG_WARN, "Obsolete encryption options used, use 'handshake_{deny,allow,prefer,require}, stream_{deny,allow,prefer,require}' instead.");
|
||||
|
||||
return torrent::Object();
|
||||
torrent::runtime::network_config()->set_encryption_modes(handshake_mode, stream_mode);
|
||||
return {};
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_encryption(const torrent::Object::list_type& args) {
|
||||
if (args.empty())
|
||||
throw torrent::input_error("No encryption options specified.");
|
||||
|
||||
torrent::encryption_mode encryption_mode, handshake_mode, stream_mode;
|
||||
|
||||
if (args.size() == 1) {
|
||||
try {
|
||||
encryption_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_MODE, args.front().as_string()));
|
||||
|
||||
} catch (torrent::input_error& e) {
|
||||
return apply_obsolete_encryption(args);
|
||||
}
|
||||
|
||||
torrent::runtime::network_config()->set_encryption_modes(encryption_mode, encryption_mode);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (args.size() != 2)
|
||||
return apply_obsolete_encryption(args);
|
||||
|
||||
try {
|
||||
handshake_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_HANDSHAKE, args.front().as_string()));
|
||||
stream_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_STREAM, args.back().as_string()));
|
||||
|
||||
} catch (torrent::input_error& e) {
|
||||
return apply_obsolete_encryption(args);
|
||||
}
|
||||
|
||||
torrent::runtime::network_config()->set_encryption_modes(handshake_mode, stream_mode);
|
||||
return {};
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
@@ -54,13 +153,11 @@ apply_tos(const torrent::Object::string_type& arg) {
|
||||
if (!rpc::parse_whole_value_nothrow(arg.c_str(), &value, 16, 1))
|
||||
value = torrent::option_find_string(torrent::OPTION_IP_TOS, arg.c_str());
|
||||
|
||||
torrent::config::network_config()->set_priority(value);
|
||||
torrent::runtime::network_config()->set_priority(value);
|
||||
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
torrent::Object apply_encoding_list(const std::string& arg) { torrent::encoding_list()->push_back(arg); return torrent::Object(); }
|
||||
|
||||
void
|
||||
initialize_rpc_handlers() {
|
||||
rpc::rpc.initialize_handlers();
|
||||
@@ -85,14 +182,13 @@ apply_scgi(const std::string& arg, int type) {
|
||||
|
||||
initialize_rpc_handlers();
|
||||
|
||||
rpc::SCgi* scgi = new rpc::SCgi;
|
||||
|
||||
rak::address_info* ai = NULL;
|
||||
torrent::sa_unique_ptr sa;
|
||||
|
||||
auto scgi = std::make_unique<rpc::SCgi>();
|
||||
|
||||
try {
|
||||
int port, err;
|
||||
char dummy;
|
||||
int port{};
|
||||
char dummy{};
|
||||
char address[1024];
|
||||
std::string path;
|
||||
|
||||
@@ -106,10 +202,11 @@ apply_scgi(const std::string& arg, int type) {
|
||||
} 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)) + ".");
|
||||
|
||||
sa = torrent::sa_copy(ai->c_addrinfo()->ai_addr);
|
||||
try {
|
||||
sa = torrent::sa_copy(torrent::sa_lookup_address(address, AF_UNSPEC).get());
|
||||
} catch (torrent::input_error& e) {
|
||||
throw torrent::input_error("Could not bind address: " + std::string(e.what()));
|
||||
}
|
||||
|
||||
lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is bound to an address and might be a security risk");
|
||||
|
||||
@@ -134,16 +231,11 @@ apply_scgi(const std::string& arg, int type) {
|
||||
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);
|
||||
scgi_thread::set_scgi(scgi.release());
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
@@ -220,106 +312,124 @@ apply_xmlrpc_dialect(const std::string& arg) {
|
||||
|
||||
void
|
||||
initialize_command_network() {
|
||||
auto cm = torrent::connection_manager();
|
||||
auto file_manager = torrent::file_manager();
|
||||
auto http_stack = torrent::net_thread::http_stack();
|
||||
auto nw_config = torrent::config::network_config();
|
||||
auto file_manager = torrent::file_manager();
|
||||
auto http_stack = torrent::net_thread::http_stack();
|
||||
auto nw_config = torrent::runtime::network_config();
|
||||
|
||||
CMD2_ANY_STRING ("encoding.add", std::bind(&apply_encoding_list, std::placeholders::_2));
|
||||
CMD_ANY ("network.listen.port", [](auto, auto) { return torrent::runtime::network_manager()->listen_port(); });
|
||||
CMD_ANY_VALUE_V ("network.listen.port.set", [](auto, auto& value) { return torrent::runtime::network_manager()->set_listen_port(checked_port_value(value, "listen")); });
|
||||
CMD_ANY ("network.listen.port.random", [](auto, auto) { return torrent::runtime::client_config()->listen_port_random(); });
|
||||
CMD_ANY_VALUE_V ("network.listen.port.random.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_listen_port_random(value); });
|
||||
CMD_ANY ("network.listen.port.range", [](auto, auto) { return listen_port_range(); });
|
||||
CMD_ANY_STRING_V("network.listen.port.range.set", [](auto, auto& value) { return set_listen_port_range(value); });
|
||||
CMD_ANY ("network.listen.backlog", [](auto, auto) { return torrent::runtime::network_config()->listen_backlog(); });
|
||||
CMD_ANY_VALUE_V ("network.listen.backlog.set", [](auto, auto& value) { return torrent::runtime::network_config()->set_listen_backlog(value); });
|
||||
|
||||
// Isn't port_open used?
|
||||
CMD2_VAR_BOOL ("network.port_open", true);
|
||||
CMD2_VAR_BOOL ("network.port_random", true);
|
||||
CMD2_VAR_STRING ("network.port_range", "6881-6999");
|
||||
CMD_ANY ("protocol.pex", [](auto, auto) { return torrent::runtime::client_config()->is_pex_enabled(); });
|
||||
CMD_ANY_VALUE_V ("protocol.pex.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_pex_enabled(value); });
|
||||
|
||||
CMD2_ANY ("network.listen.port", [](auto, auto) { return torrent::runtime::listen_port(); });
|
||||
CMD2_ANY ("network.listen.backlog", [nw_config](auto, auto) { return nw_config->listen_backlog(); });
|
||||
CMD2_ANY_VALUE_V ("network.listen.backlog.set", [nw_config](auto, auto& value) { return nw_config->set_listen_backlog(value); });
|
||||
CMD_ANY_LIST ("protocol.encryption", [](auto, auto) { return get_encryption(); });
|
||||
CMD_ANY_LIST ("protocol.encryption.set", [](auto, auto& args) { return apply_encryption(args); });
|
||||
CMD_ANY_LIST ("protocol.encryption.handshake", [](auto, auto) { return get_handshake_encryption(); });
|
||||
CMD_ANY_LIST ("protocol.encryption.stream", [](auto, auto) { return get_stream_encryption(); });
|
||||
|
||||
CMD2_VAR_BOOL ("protocol.pex", true);
|
||||
CMD2_ANY_LIST ("protocol.encryption.set", [](auto, auto& args) { return apply_encryption(args); });
|
||||
CMD_VAR_STRING ("protocol.connection.leech", "leech");
|
||||
CMD_VAR_STRING ("protocol.connection.seed", "seed");
|
||||
|
||||
CMD2_VAR_STRING ("protocol.connection.leech", "leech");
|
||||
CMD2_VAR_STRING ("protocol.connection.seed", "seed");
|
||||
CMD_VAR_STRING ("protocol.choke_heuristics.up.leech", "upload_leech");
|
||||
CMD_VAR_STRING ("protocol.choke_heuristics.up.seed", "upload_leech");
|
||||
CMD_VAR_STRING ("protocol.choke_heuristics.down.leech", "download_leech");
|
||||
CMD_VAR_STRING ("protocol.choke_heuristics.down.seed", "download_leech");
|
||||
|
||||
CMD2_VAR_STRING ("protocol.choke_heuristics.up.leech", "upload_leech");
|
||||
CMD2_VAR_STRING ("protocol.choke_heuristics.up.seed", "upload_leech");
|
||||
CMD2_VAR_STRING ("protocol.choke_heuristics.down.leech", "download_leech");
|
||||
CMD2_VAR_STRING ("protocol.choke_heuristics.down.seed", "download_leech");
|
||||
CMD_ANY ("network.http.cacert", [http_stack](auto, auto) { return http_stack->http_cacert(); });
|
||||
CMD_ANY_STRING_V("network.http.cacert.set", [http_stack](auto, auto& str) { return http_stack->set_http_cacert(str); });
|
||||
CMD_ANY ("network.http.capath", [http_stack](auto, auto) { return http_stack->http_capath(); });
|
||||
CMD_ANY_STRING_V("network.http.capath.set", [http_stack](auto, auto& str) { return http_stack->set_http_capath(str); });
|
||||
CMD_ANY ("network.http.dns_cache_timeout", [http_stack](auto, auto) { return http_stack->dns_timeout(); });
|
||||
CMD_ANY_VALUE_V ("network.http.dns_cache_timeout.set", [http_stack](auto, auto& value) { return http_stack->set_dns_timeout(value); });
|
||||
CMD_ANY ("network.http.current_open", [http_stack](auto, auto) { return http_stack->size(); });
|
||||
CMD_ANY ("network.http.max_cache_connections", [http_stack](auto, auto) { return http_stack->max_cache_connections(); });
|
||||
CMD_ANY_VALUE_V ("network.http.max_cache_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_cache_connections(value); });
|
||||
CMD_ANY ("network.http.max_host_connections", [http_stack](auto, auto) { return http_stack->max_host_connections(); });
|
||||
CMD_ANY_VALUE_V ("network.http.max_host_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_host_connections(value); });
|
||||
CMD_ANY ("network.http.max_total_connections", [http_stack](auto, auto) { return http_stack->max_total_connections(); });
|
||||
|
||||
CMD2_ANY ("network.http.cacert", [http_stack](auto, auto) { return http_stack->http_cacert(); });
|
||||
CMD2_ANY_STRING_V("network.http.cacert.set", [http_stack](auto, auto& str) { return http_stack->set_http_cacert(str); });
|
||||
CMD2_ANY ("network.http.capath", [http_stack](auto, auto) { return http_stack->http_capath(); });
|
||||
CMD2_ANY_STRING_V("network.http.capath.set", [http_stack](auto, auto& str) { return http_stack->set_http_capath(str); });
|
||||
CMD2_ANY ("network.http.dns_cache_timeout", [http_stack](auto, auto) { return http_stack->dns_timeout(); });
|
||||
CMD2_ANY_VALUE_V ("network.http.dns_cache_timeout.set", [http_stack](auto, auto& value) { return http_stack->set_dns_timeout(value); });
|
||||
CMD2_ANY ("network.http.current_open", [http_stack](auto, auto) { return http_stack->size(); });
|
||||
CMD2_ANY ("network.http.max_cache_connections", [http_stack](auto, auto) { return http_stack->max_cache_connections(); });
|
||||
CMD2_ANY_VALUE_V ("network.http.max_cache_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_cache_connections(value); });
|
||||
CMD2_ANY ("network.http.max_host_connections", [http_stack](auto, auto) { return http_stack->max_host_connections(); });
|
||||
CMD2_ANY_VALUE_V ("network.http.max_host_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_host_connections(value); });
|
||||
CMD2_ANY ("network.http.max_total_connections", [http_stack](auto, auto) { return http_stack->max_total_connections(); });
|
||||
CMD2_ANY_VALUE_V ("network.http.max_total_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_total_connections(value); });
|
||||
CMD2_ANY ("network.http.proxy_address", [http_stack](auto, auto) { return http_stack->http_proxy(); });
|
||||
CMD2_ANY_STRING_V("network.http.proxy_address.set", [http_stack](auto, auto& str) { return http_stack->set_http_proxy(str); });
|
||||
CMD2_ANY ("network.http.ssl_verify_host", [http_stack](auto, auto) { return http_stack->ssl_verify_host(); });
|
||||
CMD2_ANY_VALUE_V ("network.http.ssl_verify_host.set", [http_stack](auto, auto& value) { return http_stack->set_ssl_verify_host(value); });
|
||||
CMD2_ANY ("network.http.ssl_verify_peer", [http_stack](auto, auto) { return http_stack->ssl_verify_peer(); });
|
||||
CMD2_ANY_VALUE_V ("network.http.ssl_verify_peer.set", [http_stack](auto, auto& value) { return http_stack->set_ssl_verify_peer(value); });
|
||||
CMD_ANY ("network.http.ssl_verify_host", [http_stack](auto, auto) { return http_stack->ssl_verify_host(); });
|
||||
CMD_ANY_VALUE_V ("network.http.ssl_verify_host.set", [http_stack](auto, auto& value) { return http_stack->set_ssl_verify_host(value); });
|
||||
CMD_ANY ("network.http.ssl_verify_peer", [http_stack](auto, auto) { return http_stack->ssl_verify_peer(); });
|
||||
CMD_ANY_VALUE_V ("network.http.ssl_verify_peer.set", [http_stack](auto, auto& value) { return http_stack->set_ssl_verify_peer(value); });
|
||||
|
||||
CMD2_ANY ("network.send_buffer.size", [nw_config](auto, auto) { return nw_config->send_buffer_size(); });
|
||||
CMD2_ANY_VALUE_V ("network.send_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_send_buffer_size(value); });
|
||||
CMD2_ANY ("network.receive_buffer.size", [nw_config](auto, auto) { return nw_config->receive_buffer_size(); });
|
||||
CMD2_ANY_VALUE_V ("network.receive_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_receive_buffer_size(value); });
|
||||
CMD2_ANY_STRING ("network.tos.set", [](auto, auto& str) { return apply_tos(str); });
|
||||
CMD_ANY ("network.send_buffer.size", [nw_config](auto, auto) { return nw_config->send_buffer_size(); });
|
||||
CMD_ANY_VALUE_V ("network.send_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_send_buffer_size(value); });
|
||||
CMD_ANY ("network.receive_buffer.size", [nw_config](auto, auto) { return nw_config->receive_buffer_size(); });
|
||||
CMD_ANY_VALUE_V ("network.receive_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_receive_buffer_size(value); });
|
||||
CMD_ANY_STRING ("network.tos.set", [](auto, auto& str) { return apply_tos(str); });
|
||||
|
||||
CMD2_ANY ("network.bind_address", [nw_config](auto, auto) { return nw_config->bind_address_best_match_str(); });
|
||||
CMD2_ANY_STRING_V("network.bind_address.set", [nw_config](auto, auto& str) { return nw_config->set_bind_address_str(str); });
|
||||
CMD2_ANY ("network.bind_address.ipv4", [nw_config](auto, auto) { return nw_config->bind_inet_address_str(); });
|
||||
CMD2_ANY_STRING_V("network.bind_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet_address_str(str); });
|
||||
CMD2_ANY ("network.bind_address.ipv6", [nw_config](auto, auto) { return nw_config->bind_inet6_address_str(); });
|
||||
CMD2_ANY_STRING_V("network.bind_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet6_address_str(str); });
|
||||
CMD_ANY ("network.bind_address", [nw_config](auto, auto) { return nw_config->bind_address_best_match_str(); });
|
||||
CMD_ANY_STRING_V("network.bind_address.set", [nw_config](auto, auto& str) { return nw_config->set_bind_address(str); });
|
||||
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); });
|
||||
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); });
|
||||
|
||||
CMD2_ANY ("network.local_address", [nw_config](auto, auto) { return nw_config->local_address_best_match_str(); });
|
||||
CMD2_ANY_STRING_V("network.local_address.set", [nw_config](auto, auto& str) { return nw_config->set_local_address_str(str); });
|
||||
CMD2_ANY ("network.local_address.ipv4", [nw_config](auto, auto) { return nw_config->local_inet_address_str(); });
|
||||
CMD2_ANY_STRING_V("network.local_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet_address_str(str); });
|
||||
CMD2_ANY ("network.local_address.ipv6", [nw_config](auto, auto) { return nw_config->local_inet6_address_str(); });
|
||||
CMD2_ANY_STRING_V("network.local_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet6_address_str(str); });
|
||||
CMD_ANY_STRING_V("network.bind_device.set", [nw_config](auto, auto& str) { return nw_config->set_bind_device_name(str); });
|
||||
CMD_ANY ("network.bind_device.ipv4", [nw_config](auto, auto) { return nw_config->bind_inet_device_name(); });
|
||||
CMD_ANY_STRING_V("network.bind_device.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet_device_name(str); });
|
||||
CMD_ANY ("network.bind_device.ipv6", [nw_config](auto, auto) { return nw_config->bind_inet6_device_name(); });
|
||||
CMD_ANY_STRING_V("network.bind_device.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet6_device_name(str); });
|
||||
|
||||
CMD2_ANY ("network.proxy_address", [nw_config](auto, auto) { return nw_config->proxy_address_str(); });
|
||||
CMD2_ANY_STRING_V("network.proxy_address.set", [](auto, auto& str) { return control->core()->set_proxy_address(str); });
|
||||
CMD_ANY ("network.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); });
|
||||
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); });
|
||||
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); });
|
||||
|
||||
CMD2_ANY ("network.open_files", [file_manager](auto, auto) { return file_manager->open_files(); });
|
||||
CMD2_ANY ("network.max_open_files", [file_manager](auto, auto) { return file_manager->max_open_files(); });
|
||||
CMD2_ANY_VALUE_V ("network.max_open_files.set", [file_manager](auto, auto& value) { return file_manager->set_max_open_files(value); });
|
||||
CMD2_ANY ("network.total_handshakes", [](auto, auto) { return torrent::total_handshakes(); });
|
||||
CMD2_ANY ("network.open_sockets", [cm](auto, auto) { return cm->size(); });
|
||||
CMD2_ANY ("network.max_open_sockets", [cm](auto, auto) { return cm->max_size(); });
|
||||
CMD2_ANY_VALUE_V ("network.max_open_sockets.set", [cm](auto, auto& value) { return cm->set_max_size(value); });
|
||||
CMD_ANY ("network.local_port", [nw_config](auto, auto) { return nw_config->local_port_best_match(); });
|
||||
CMD_ANY_VALUE_V ("network.local_port.set", [nw_config](auto, auto& value) { return nw_config->set_local_port(checked_port_value(value, "local")); });
|
||||
CMD_ANY ("network.local_port.ipv4", [nw_config](auto, auto) { return nw_config->local_inet_port(); });
|
||||
CMD_ANY_VALUE_V ("network.local_port.ipv4.set", [nw_config](auto, auto& value) { return nw_config->set_local_inet_port(checked_port_value(value, "local ipv4")); });
|
||||
CMD_ANY ("network.local_port.ipv6", [nw_config](auto, auto) { return nw_config->local_inet6_port(); });
|
||||
CMD_ANY_VALUE_V ("network.local_port.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_local_inet6_port(checked_port_value(value, "local ipv6")); });
|
||||
|
||||
CMD2_ANY_STRING ("network.scgi.open_port", std::bind(&apply_scgi, std::placeholders::_2, 1));
|
||||
CMD2_ANY_STRING ("network.scgi.open_local", std::bind(&apply_scgi, std::placeholders::_2, 2));
|
||||
CMD2_VAR_BOOL ("network.scgi.dont_route", false);
|
||||
CMD2_ANY ("network.scgi.open_systemd", [](auto, auto) { return apply_scgi_systemd(); });
|
||||
CMD_ANY ("network.proxy.global", [](auto, auto) { return torrent::runtime::proxy_manager()->proxy_url(); });
|
||||
CMD_ANY_STRING_V("network.proxy.global.set", [](auto, auto& str) { return torrent::runtime::proxy_manager()->set_proxy_url(str); });
|
||||
CMD_ANY ("network.proxy.http", [](auto, auto) { return torrent::runtime::proxy_manager()->http_proxy_url(); });
|
||||
CMD_ANY_STRING_V("network.proxy.http.set", [](auto, auto& str) { return torrent::runtime::proxy_manager()->set_http_proxy_url(str); });
|
||||
|
||||
CMD2_ANY_STRING ("network.xmlrpc.dialect.set", [](const auto&, const auto& arg) { return apply_xmlrpc_dialect(arg); })
|
||||
CMD2_ANY ("network.xmlrpc.size_limit", [](const auto&, const auto&) { return rpc::rpc.size_limit(); });
|
||||
CMD2_ANY_VALUE_V ("network.xmlrpc.size_limit.set", [](const auto&, const auto& arg) { return rpc::rpc.set_size_limit(arg); });
|
||||
CMD_ANY ("network.open_files", [file_manager](auto, auto) { return file_manager->open_files(); });
|
||||
CMD_ANY ("network.max_open_files", [file_manager](auto, auto) { return file_manager->max_open_files(); });
|
||||
CMD_ANY ("network.total_handshakes", [](auto, auto) { return torrent::runtime::total_handshakes(); });
|
||||
|
||||
CMD2_VAR_BOOL ("network.rpc.use_xmlrpc", true);
|
||||
CMD2_VAR_BOOL ("network.rpc.use_jsonrpc", true);
|
||||
CMD_ANY_STRING ("network.scgi.open_port", [](auto, auto& arg) { return apply_scgi(arg, 1); });
|
||||
CMD_ANY_STRING ("network.scgi.open_local", [](auto, auto& arg) { return apply_scgi(arg, 2); });
|
||||
CMD_VAR_BOOL ("network.scgi.dont_route", false);
|
||||
CMD_ANY ("network.scgi.open_systemd", [](auto, auto) { return apply_scgi_systemd(); });
|
||||
|
||||
CMD2_ANY ("network.block.ipv4", [nw_config](auto, auto) { return nw_config->is_block_ipv4(); });
|
||||
CMD2_ANY_VALUE_V ("network.block.ipv4.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4(value); });
|
||||
CMD2_ANY ("network.block.ipv6", [nw_config](auto, auto) { return nw_config->is_block_ipv6(); });
|
||||
CMD2_ANY_VALUE_V ("network.block.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv6(value); });
|
||||
CMD2_ANY ("network.block.ipv4in6", [nw_config](auto, auto) { return nw_config->is_block_ipv4in6(); });
|
||||
CMD2_ANY_VALUE_V ("network.block.ipv4in6.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4in6(value); });
|
||||
CMD2_ANY ("network.block.outgoing", [nw_config](auto, auto) { return nw_config->is_block_outgoing(); });
|
||||
CMD2_ANY_VALUE_V ("network.block.outgoing.set", [nw_config](auto, auto& value) { return nw_config->set_block_outgoing(value); });
|
||||
CMD2_ANY ("network.prefer.ipv6", [nw_config](auto, auto) { return nw_config->is_prefer_ipv6(); });
|
||||
CMD2_ANY_VALUE_V ("network.prefer.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_prefer_ipv6(value); });
|
||||
CMD_ANY ("network.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); });
|
||||
|
||||
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");
|
||||
@@ -332,22 +442,26 @@ initialize_command_network() {
|
||||
rpc::rpc.mark_safe("network.http.max_host_connections");
|
||||
rpc::rpc.mark_safe("network.http.max_total_connections");
|
||||
|
||||
rpc::rpc.mark_safe("network.total_handshakes");
|
||||
rpc::rpc.mark_safe("network.open_files");
|
||||
rpc::rpc.mark_safe("network.max_open_files");
|
||||
rpc::rpc.mark_safe("network.max_open_sockets");
|
||||
rpc::rpc.mark_safe("network.total_handshakes");
|
||||
|
||||
rpc::rpc.mark_safe("network.send_buffer.size");
|
||||
rpc::rpc.mark_safe("network.receive_buffer.size");
|
||||
rpc::rpc.mark_safe("network.bind_address");
|
||||
rpc::rpc.mark_safe("network.local_address");
|
||||
rpc::rpc.mark_safe("network.local_port");
|
||||
rpc::rpc.mark_safe("network.local_port.ipv4");
|
||||
rpc::rpc.mark_safe("network.local_port.ipv6");
|
||||
rpc::rpc.mark_safe("network.xmlrpc.size_limit");
|
||||
rpc::rpc.mark_safe("network.open_sockets");
|
||||
|
||||
rpc::rpc.mark_safe("network.http.cacert");
|
||||
rpc::rpc.mark_safe("network.http.capath");
|
||||
rpc::rpc.mark_safe("network.http.proxy_address");
|
||||
rpc::rpc.mark_safe("network.proxy_address");
|
||||
rpc::rpc.mark_safe("network.proxy.global");
|
||||
rpc::rpc.mark_safe("network.proxy.http");
|
||||
rpc::rpc.mark_safe("network.scgi.dont_route");
|
||||
|
||||
rpc::rpc.mark_safe("protocol.pex");
|
||||
|
||||
rpc::rpc.mark_safe("network.rpc.use_xmlrpc");
|
||||
|
||||
+8
-28
@@ -1,33 +1,18 @@
|
||||
#include "config.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 "core/manager.h"
|
||||
#include "display/utils.h"
|
||||
#include <torrent/utils/string_manip.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());
|
||||
}
|
||||
#include "control.h"
|
||||
#include "core/manager.h"
|
||||
#include "display/utils.h"
|
||||
|
||||
torrent::Object
|
||||
retrieve_p_address(torrent::Peer* peer) {
|
||||
@@ -40,11 +25,6 @@ retrieve_p_address(torrent::Peer* peer) {
|
||||
return addr_str;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
retrieve_p_port(torrent::Peer* peer) {
|
||||
return torrent::sa_port(peer->peer_info()->socket_address());
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
retrieve_p_client_version(torrent::Peer* peer) {
|
||||
char buf[128];
|
||||
@@ -55,7 +35,7 @@ retrieve_p_client_version(torrent::Peer* peer) {
|
||||
|
||||
torrent::Object
|
||||
retrieve_p_options_str(torrent::Peer* peer) {
|
||||
return rak::transform_hex(peer->peer_info()->options(), peer->peer_info()->options() + 8);
|
||||
return torrent::utils::transform_to_hex_str(peer->peer_info()->options(), peer->peer_info()->options() + 8);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
@@ -67,8 +47,8 @@ retrieve_p_completed_percent(torrent::Peer* peer) {
|
||||
|
||||
void
|
||||
initialize_command_peer() {
|
||||
CMD2_PEER("p.id", std::bind(&retrieve_p_id, std::placeholders::_1));
|
||||
CMD2_PEER("p.id_html", std::bind(&retrieve_p_id_html, std::placeholders::_1));
|
||||
CMD2_PEER("p.id", [](auto* peer, auto) { return torrent::utils::transform_to_hex_str(peer->id()); });
|
||||
CMD2_PEER("p.id_html", [](auto* peer, auto) { return torrent::utils::copy_escape_html_str(peer->id()); });
|
||||
CMD2_PEER("p.client_version", std::bind(&retrieve_p_client_version, std::placeholders::_1));
|
||||
|
||||
CMD2_PEER("p.options_str", std::bind(&retrieve_p_options_str, std::placeholders::_1));
|
||||
@@ -82,7 +62,7 @@ initialize_command_peer() {
|
||||
CMD2_PEER("p.is_preferred", std::bind(&torrent::PeerInfo::is_preferred, std::bind(&torrent::Peer::peer_info, std::placeholders::_1)));
|
||||
|
||||
CMD2_PEER("p.address", std::bind(&retrieve_p_address, std::placeholders::_1));
|
||||
CMD2_PEER("p.port", std::bind(&retrieve_p_port, std::placeholders::_1));
|
||||
CMD2_PEER("p.port", [](auto* peer, auto) { return torrent::sa_port(peer->peer_info()->socket_address()); });
|
||||
|
||||
CMD2_PEER("p.completed_percent", std::bind(&retrieve_p_completed_percent, std::placeholders::_1));
|
||||
|
||||
|
||||
@@ -28,9 +28,10 @@ cmd_scheduler_simple_removed(core::Download* download) {
|
||||
control->core()->download_list()->pause(download);
|
||||
|
||||
core::View* viewActive = *control->view_manager()->find("active");
|
||||
unsigned int numActive = viewActive->size_visible();
|
||||
int64_t maxActive = rpc::call_command("scheduler.max_active", torrent::Object()).as_value();
|
||||
|
||||
if ((int64_t)viewActive->size_visible() >= maxActive)
|
||||
if ((int64_t)numActive >= maxActive)
|
||||
return torrent::Object();
|
||||
|
||||
// The 'started' view contains all the views we may choose amongst.
|
||||
@@ -41,6 +42,9 @@ cmd_scheduler_simple_removed(core::Download* download) {
|
||||
continue;
|
||||
|
||||
control->core()->download_list()->resume(*itr);
|
||||
|
||||
if ((int64_t)++numActive >= maxActive)
|
||||
break;
|
||||
}
|
||||
|
||||
return torrent::Object();
|
||||
|
||||
@@ -0,0 +1,365 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/object.h>
|
||||
|
||||
#include "rpc/parse.h"
|
||||
#include "rpc/rpc_manager.h"
|
||||
|
||||
#include "globals.h"
|
||||
#include "control.h"
|
||||
#include "command_helpers.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int64_t max_string_pad_size = 1 << 14;
|
||||
|
||||
const std::string whitespace_characters = " \t\n\r\f\v";
|
||||
|
||||
// The byte offset of every utf-8 character in 'text', terminated by the offset
|
||||
// past the last character. Bytes that are not valid utf-8 lead bytes are
|
||||
// treated as single characters.
|
||||
std::vector<size_t>
|
||||
utf8_offsets(const std::string& text) {
|
||||
std::vector<size_t> offsets;
|
||||
|
||||
for (size_t i = 0; i < text.size(); i++)
|
||||
if (i == 0 || (static_cast<unsigned char>(text[i]) & 0xC0) != 0x80)
|
||||
offsets.push_back(i);
|
||||
|
||||
offsets.push_back(text.size());
|
||||
return offsets;
|
||||
}
|
||||
|
||||
int64_t
|
||||
utf8_length(const std::string& text) {
|
||||
int64_t result = 0;
|
||||
|
||||
for (size_t i = 0; i < text.size(); i++)
|
||||
if (i == 0 || (static_cast<unsigned char>(text[i]) & 0xC0) != 0x80)
|
||||
result++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string
|
||||
utf8_substr(const std::string& text, const std::vector<size_t>& offsets, size_t first, size_t last) {
|
||||
return text.substr(offsets[first], offsets[last] - offsets[first]);
|
||||
}
|
||||
|
||||
std::set<std::string>
|
||||
utf8_character_set(const std::string& text) {
|
||||
auto offsets = utf8_offsets(text);
|
||||
std::set<std::string> result;
|
||||
|
||||
for (size_t i = 0; i + 1 < offsets.size(); i++)
|
||||
result.insert(utf8_substr(text, offsets, i, i + 1));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string
|
||||
ascii_lowercase(std::string text) {
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
return text;
|
||||
}
|
||||
|
||||
// A 'max_count' of zero means the command accepts any number of arguments.
|
||||
void
|
||||
verify_argument_count(const char* name, const torrent::Object::list_type& args, size_t min_count, size_t max_count) {
|
||||
if (args.size() < min_count || (max_count != 0 && args.size() > max_count))
|
||||
throw torrent::input_error(std::string(name) + ": invalid number of arguments.");
|
||||
}
|
||||
|
||||
const torrent::Object&
|
||||
argument_at(const torrent::Object::list_type& args, size_t index) {
|
||||
return *std::next(args.begin(), index);
|
||||
}
|
||||
|
||||
void
|
||||
flatten_argument(const torrent::Object& arg, std::vector<std::string>* dest) {
|
||||
if (!arg.is_list()) {
|
||||
dest->push_back(rpc::convert_to_string(arg));
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& child : arg.as_list())
|
||||
flatten_argument(child, dest);
|
||||
}
|
||||
|
||||
// The {old, new} pairs shared by 'string.map' and 'string.replace'.
|
||||
std::pair<std::string, std::string>
|
||||
argument_to_pair(const char* name, const torrent::Object& arg) {
|
||||
if (!arg.is_list() || arg.as_list().size() != 2)
|
||||
throw torrent::input_error(std::string(name) + ": arguments after the text must be {old, new} pairs.");
|
||||
|
||||
return {rpc::convert_to_string(arg.as_list().front()), rpc::convert_to_string(arg.as_list().back())};
|
||||
}
|
||||
|
||||
// Compares the first argument against every remaining argument, returning true
|
||||
// as soon as one of them matches.
|
||||
torrent::Object
|
||||
apply_string_predicate(const char* name, const torrent::Object::list_type& args, bool (*predicate)(const std::string&, const std::string&)) {
|
||||
verify_argument_count(name, args, 2, 0);
|
||||
|
||||
auto text = rpc::convert_to_string(args.front());
|
||||
|
||||
for (auto itr = std::next(args.begin()); itr != args.end(); itr++)
|
||||
if (predicate(text, rpc::convert_to_string(*itr)))
|
||||
return int64_t(1);
|
||||
|
||||
return int64_t(0);
|
||||
}
|
||||
|
||||
bool
|
||||
text_equals(const std::string& text, const std::string& other) {
|
||||
return text == other;
|
||||
}
|
||||
|
||||
bool
|
||||
text_starts_with(const std::string& text, const std::string& prefix) {
|
||||
return text.size() >= prefix.size() && text.compare(0, prefix.size(), prefix) == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
text_ends_with(const std::string& text, const std::string& tail) {
|
||||
return text.size() >= tail.size() && text.compare(text.size() - tail.size(), tail.size(), tail) == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
text_contains(const std::string& text, const std::string& needle) {
|
||||
return text.find(needle) != std::string::npos;
|
||||
}
|
||||
|
||||
bool
|
||||
text_contains_i(const std::string& text, const std::string& needle) {
|
||||
return ascii_lowercase(text).find(ascii_lowercase(needle)) != std::string::npos;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_string_length(const torrent::Object::list_type& args) {
|
||||
verify_argument_count("string.length", args, 1, 1);
|
||||
|
||||
return utf8_length(rpc::convert_to_string(args.front()));
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_string_substr(const torrent::Object::list_type& args) {
|
||||
verify_argument_count("string.substr", args, 1, 4);
|
||||
|
||||
auto text = rpc::convert_to_string(args.front());
|
||||
auto offsets = utf8_offsets(text);
|
||||
auto text_length = static_cast<int64_t>(offsets.size() - 1);
|
||||
|
||||
auto position = args.size() > 1 ? rpc::convert_to_value(argument_at(args, 1)) : 0;
|
||||
auto fallback = args.size() > 3 ? rpc::convert_to_string(argument_at(args, 3)) : std::string();
|
||||
|
||||
// Negative positions are relative to the end of the string.
|
||||
if (position < 0)
|
||||
position += text_length;
|
||||
|
||||
if (position < 0 || position > text_length)
|
||||
return fallback;
|
||||
|
||||
auto last = text_length;
|
||||
|
||||
if (args.size() > 2) {
|
||||
auto count = rpc::convert_to_value(argument_at(args, 2));
|
||||
|
||||
if (count < 0)
|
||||
throw torrent::input_error("string.substr: the character count cannot be negative.");
|
||||
|
||||
last = std::min(text_length, position + std::min(count, text_length));
|
||||
}
|
||||
|
||||
return utf8_substr(text, offsets, position, last);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_string_split(const torrent::Object::list_type& args) {
|
||||
verify_argument_count("string.split", args, 2, 2);
|
||||
|
||||
auto text = rpc::convert_to_string(args.front());
|
||||
auto delim = rpc::convert_to_string(args.back());
|
||||
auto result = torrent::Object::create_list();
|
||||
|
||||
// An empty delimiter splits the text into its utf-8 characters.
|
||||
if (delim.empty()) {
|
||||
auto offsets = utf8_offsets(text);
|
||||
|
||||
for (size_t i = 0; i + 1 < offsets.size(); i++)
|
||||
result.as_list().push_back(utf8_substr(text, offsets, i, i + 1));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t first = 0;
|
||||
|
||||
while (true) {
|
||||
auto pos = text.find(delim, first);
|
||||
|
||||
if (pos == std::string::npos) {
|
||||
result.as_list().push_back(text.substr(first));
|
||||
return result;
|
||||
}
|
||||
|
||||
result.as_list().push_back(text.substr(first, pos - first));
|
||||
first = pos + delim.size();
|
||||
}
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_string_join(const torrent::Object::list_type& args) {
|
||||
verify_argument_count("string.join", args, 1, 0);
|
||||
|
||||
auto delim = rpc::convert_to_string(args.front());
|
||||
|
||||
std::vector<std::string> parts;
|
||||
|
||||
for (auto itr = std::next(args.begin()); itr != args.end(); itr++)
|
||||
flatten_argument(*itr, &parts);
|
||||
|
||||
std::string result;
|
||||
|
||||
for (auto itr = parts.begin(); itr != parts.end(); itr++) {
|
||||
if (itr != parts.begin())
|
||||
result += delim;
|
||||
|
||||
result += *itr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_string_pad(const char* name, const torrent::Object::list_type& args, bool pad_start) {
|
||||
verify_argument_count(name, args, 2, 3);
|
||||
|
||||
auto text = rpc::convert_to_string(args.front());
|
||||
auto pad_size = rpc::convert_to_value(argument_at(args, 1));
|
||||
auto padding = args.size() > 2 ? rpc::convert_to_string(argument_at(args, 2)) : std::string(" ");
|
||||
|
||||
auto text_length = utf8_length(text);
|
||||
|
||||
if (pad_size <= text_length || padding.empty())
|
||||
return text;
|
||||
|
||||
if (pad_size - text_length > max_string_pad_size)
|
||||
throw torrent::input_error(std::string(name) + ": padding is too large.");
|
||||
|
||||
auto padding_offsets = utf8_offsets(padding);
|
||||
auto padding_length = static_cast<int64_t>(padding_offsets.size() - 1);
|
||||
|
||||
std::string result;
|
||||
result.reserve(pad_size - text_length);
|
||||
|
||||
for (int64_t i = 0; i < pad_size - text_length; i++) {
|
||||
auto index = static_cast<size_t>(i % padding_length);
|
||||
result += utf8_substr(padding, padding_offsets, index, index + 1);
|
||||
}
|
||||
|
||||
return pad_start ? result + text : text + result;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_string_strip(const char* name, const torrent::Object::list_type& args, bool strip_start, bool strip_end) {
|
||||
verify_argument_count(name, args, 1, 0);
|
||||
|
||||
auto text = rpc::convert_to_string(args.front());
|
||||
|
||||
std::string strippable;
|
||||
|
||||
for (auto itr = std::next(args.begin()); itr != args.end(); itr++)
|
||||
strippable += rpc::convert_to_string(*itr);
|
||||
|
||||
if (args.size() == 1)
|
||||
strippable = whitespace_characters;
|
||||
|
||||
auto characters = utf8_character_set(strippable);
|
||||
auto offsets = utf8_offsets(text);
|
||||
|
||||
size_t first = 0;
|
||||
size_t last = offsets.size() - 1;
|
||||
|
||||
while (strip_start && first < last && characters.count(utf8_substr(text, offsets, first, first + 1)) != 0)
|
||||
first++;
|
||||
|
||||
while (strip_end && last > first && characters.count(utf8_substr(text, offsets, last - 1, last)) != 0)
|
||||
last--;
|
||||
|
||||
return utf8_substr(text, offsets, first, last);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_string_map(const torrent::Object::list_type& args) {
|
||||
verify_argument_count("string.map", args, 2, 0);
|
||||
|
||||
auto text = rpc::convert_to_string(args.front());
|
||||
|
||||
for (auto itr = std::next(args.begin()); itr != args.end(); itr++) {
|
||||
auto pair = argument_to_pair("string.map", *itr);
|
||||
|
||||
if (text == pair.first)
|
||||
return pair.second;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_string_replace(const torrent::Object::list_type& args) {
|
||||
verify_argument_count("string.replace", args, 2, 0);
|
||||
|
||||
auto text = rpc::convert_to_string(args.front());
|
||||
|
||||
for (auto itr = std::next(args.begin()); itr != args.end(); itr++) {
|
||||
auto pair = argument_to_pair("string.replace", *itr);
|
||||
|
||||
if (pair.first.empty())
|
||||
throw torrent::input_error("string.replace: the replaced text cannot be empty.");
|
||||
|
||||
for (auto pos = text.find(pair.first); pos != std::string::npos; pos = text.find(pair.first, pos + pair.second.size()))
|
||||
text.replace(pos, pair.first.size(), pair.second);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
initialize_command_string() {
|
||||
// clang-format off
|
||||
CMD2_ANY_LIST("string.length", [](auto, const auto& args) { return apply_string_length(args); });
|
||||
CMD2_ANY_LIST("string.substr", [](auto, const auto& args) { return apply_string_substr(args); });
|
||||
CMD2_ANY_LIST("string.split", [](auto, const auto& args) { return apply_string_split(args); });
|
||||
CMD2_ANY_LIST("string.join", [](auto, const auto& args) { return apply_string_join(args); });
|
||||
CMD2_ANY_LIST("string.map", [](auto, const auto& args) { return apply_string_map(args); });
|
||||
CMD2_ANY_LIST("string.replace", [](auto, const auto& args) { return apply_string_replace(args); });
|
||||
|
||||
CMD2_ANY_LIST("string.equals", [](auto, const auto& args) { return apply_string_predicate("string.equals", args, &text_equals); });
|
||||
CMD2_ANY_LIST("string.starts_with", [](auto, const auto& args) { return apply_string_predicate("string.starts_with", args, &text_starts_with); });
|
||||
CMD2_ANY_LIST("string.ends_with", [](auto, const auto& args) { return apply_string_predicate("string.ends_with", args, &text_ends_with); });
|
||||
CMD2_ANY_LIST("string.contains", [](auto, const auto& args) { return apply_string_predicate("string.contains", args, &text_contains); });
|
||||
CMD2_ANY_LIST("string.contains_i", [](auto, const auto& args) { return apply_string_predicate("string.contains_i", args, &text_contains_i); });
|
||||
|
||||
CMD2_ANY_LIST("string.lpad", [](auto, const auto& args) { return apply_string_pad("string.lpad", args, true); });
|
||||
CMD2_ANY_LIST("string.rpad", [](auto, const auto& args) { return apply_string_pad("string.rpad", args, false); });
|
||||
|
||||
CMD2_ANY_LIST("string.strip", [](auto, const auto& args) { return apply_string_strip("string.strip", args, true, true); });
|
||||
CMD2_ANY_LIST("string.lstrip", [](auto, const auto& args) { return apply_string_strip("string.lstrip", args, true, false); });
|
||||
CMD2_ANY_LIST("string.rstrip", [](auto, const auto& args) { return apply_string_strip("string.rstrip", args, false, true); });
|
||||
// clang-format on
|
||||
|
||||
for (const auto name : {"string.length", "string.substr", "string.split", "string.join", "string.map", "string.replace",
|
||||
"string.equals", "string.starts_with", "string.ends_with", "string.contains", "string.contains_i",
|
||||
"string.lpad", "string.rpad", "string.strip", "string.lstrip", "string.rstrip"})
|
||||
rpc::rpc.mark_safe(name);
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <torrent/data/file_manager.h>
|
||||
#include <torrent/runtime/client_config.h>
|
||||
#include <torrent/runtime/memory_manager.h>
|
||||
#include <torrent/runtime/socket_manager.h>
|
||||
#include <torrent/runtime/runtime.h>
|
||||
#include <torrent/utils/chrono.h>
|
||||
#include <torrent/utils/option_strings.h>
|
||||
|
||||
#include "command_helpers.h"
|
||||
#include "control.h"
|
||||
#include "globals.h"
|
||||
#include "core/manager.h"
|
||||
#include "session/session_manager.h"
|
||||
#include "utils/file_status_cache.h"
|
||||
|
||||
|
||||
torrent::Object
|
||||
system_env(const torrent::Object::string_type& arg) {
|
||||
if (arg.empty())
|
||||
throw torrent::input_error("system.env: Missing variable name.");
|
||||
|
||||
char* val = getenv(arg.c_str());
|
||||
return std::string(val ? val : "");
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_hostname() {
|
||||
char buffer[1024];
|
||||
|
||||
if (gethostname(buffer, 1023) == -1)
|
||||
throw torrent::input_error("Unable to read hostname.");
|
||||
|
||||
// if (shorten)
|
||||
// *std::find(buffer, buffer + 1023, '.') = '\0';
|
||||
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_get_cwd() {
|
||||
char* buffer = getcwd(NULL, 0);
|
||||
|
||||
if (buffer == NULL)
|
||||
throw torrent::input_error("Unable to read cwd.");
|
||||
|
||||
torrent::Object result = torrent::Object(std::string(buffer));
|
||||
free(buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_set_cwd(const torrent::Object::string_type& rawArgs) {
|
||||
if (::chdir(rawArgs.c_str()) != 0)
|
||||
throw torrent::input_error("Could not change current working directory.");
|
||||
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
checked_socket_value(int64_t value, const char* label) {
|
||||
if (value < 0 || value > std::numeric_limits<uint32_t>::max())
|
||||
throw torrent::input_error(std::string("Invalid ") + label + " value.");
|
||||
|
||||
return static_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
void
|
||||
initialize_command_system() {
|
||||
CMD_ANY ("system.hostname", std::bind(&system_hostname));
|
||||
CMD_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());
|
||||
|
||||
CMD_ANY ("system.torrent_name.use_sanitized", [](auto, auto) { return torrent::runtime::client_config()->torrent_name_use_sanitized(); });
|
||||
CMD_ANY_VALUE_V ("system.torrent_name.use_sanitized.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_torrent_name_use_sanitized(value); });
|
||||
|
||||
CMD_VAR_VALUE ("system.file.allocate", 0);
|
||||
CMD_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
|
||||
CMD_VAR_VALUE ("system.file.split_size", -1);
|
||||
CMD_VAR_STRING ("system.file.split_suffix", ".part");
|
||||
|
||||
CMD_ANY ("system.file_name.replace_slash", [](auto, auto) { return torrent::runtime::client_config()->file_name_replace_slash(); });
|
||||
CMD_ANY_STRING_V("system.file_name.replace_slash.set", [](auto, auto& str) { return torrent::runtime::client_config()->set_file_name_replace_slash(str); });
|
||||
CMD_ANY ("system.file_name.allow_legacy_utf8", [](auto, auto) { return torrent::runtime::client_config()->file_name_allow_legacy_utf8(); });
|
||||
CMD_ANY_VALUE_V ("system.file_name.allow_legacy_utf8.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_file_name_allow_legacy_utf8(value); });
|
||||
|
||||
CMD_ANY ("system.file_status_cache.size", [](auto, auto) { return control->core()->file_status_cache()->size(); });
|
||||
CMD_ANY_V ("system.file_status_cache.prune", [](auto, auto) { return control->core()->file_status_cache()->prune(); });
|
||||
|
||||
CMD_VAR_BOOL ("file.prioritize_toc", 0);
|
||||
CMD_VAR_LIST ("file.prioritize_toc.first");
|
||||
CMD_VAR_LIST ("file.prioritize_toc.last");
|
||||
|
||||
CMD_ANY ("system.files.advise_random", [](auto, auto) { return torrent::file_manager()->advise_random(); });
|
||||
CMD_ANY_VALUE_V ("system.files.advise_random.set", [](auto, auto& value) { return torrent::file_manager()->set_advise_random(value); });
|
||||
CMD_ANY ("system.files.advise_random.hashing", [](auto, auto) { return torrent::file_manager()->advise_random_hashing(); });
|
||||
CMD_ANY_VALUE_V ("system.files.advise_random.hashing.set", [](auto, auto& value) { return torrent::file_manager()->set_advise_random_hashing(value); });
|
||||
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); });
|
||||
|
||||
CMD_ANY ("system.files.close_idle", [](auto, auto) { return torrent::file_manager()->close_idle_timeout().count(); });
|
||||
CMD_ANY_VALUE_V ("system.files.close_idle.set", [](auto, auto& value) { return torrent::file_manager()->set_close_idle_timeout(value * 1s); });
|
||||
|
||||
CMD_ANY ("system.files.opened_counter", [](auto, auto) { return torrent::file_manager()->files_opened_counter(); });
|
||||
CMD_ANY ("system.files.closed_counter", [](auto, auto) { return torrent::file_manager()->files_closed_counter(); });
|
||||
CMD_ANY ("system.files.failed_counter", [](auto, auto) { return torrent::file_manager()->files_failed_counter(); });
|
||||
|
||||
CMD_ANY_STRING ("system.env", [](auto, auto& str) { return system_env(str); });
|
||||
|
||||
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(); });
|
||||
|
||||
CMD_ANY_VALUE_V ("system.umask.set", [](auto, auto& value) { return ::umask(value); });
|
||||
|
||||
CMD_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(); });
|
||||
|
||||
CMD_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
|
||||
|
||||
CMD_ANY ("system.cwd", [](auto, auto) { return system_get_cwd(); });
|
||||
CMD_ANY_STRING ("system.cwd.set", [](auto, auto& str) { return system_set_cwd(str); });
|
||||
|
||||
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(checked_socket_value(value, "socket max size"));
|
||||
});
|
||||
CMD_ANY_V ("system.sockets.adjust_alloc", [](auto, auto) { torrent::runtime::socket_manager()->adjust_allocation(); });
|
||||
CMD_ANY ("system.sockets.reserved_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->reserved_allocation(); });
|
||||
CMD_ANY ("system.sockets.available_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->available_allocation(); });
|
||||
|
||||
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);
|
||||
|
||||
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); });
|
||||
|
||||
if (i == 0) {
|
||||
CMD_ANY (category_name + ".min_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->generic_min_allocation(); });
|
||||
continue;
|
||||
}
|
||||
|
||||
CMD_ANY (category_name + ".max_alloc.limit", [category](auto, auto) { return torrent::runtime::socket_manager()->category_alloc_limit(category); });
|
||||
CMD_ANY (category_name + ".min_alloc.limit", [category](auto, auto) { return torrent::runtime::socket_manager()->category_alloc_minimum(category); });
|
||||
CMD_ANY (category_name + ".min_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_min_allocation(category); });
|
||||
CMD_ANY (category_name + ".max_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_allocation(category); });
|
||||
CMD_ANY_VALUE_V(category_name + ".min_alloc.set", [category](auto, auto& value) {
|
||||
torrent::runtime::socket_manager()->set_category_min_allocation(category, checked_socket_value(value, "socket min alloc"));
|
||||
});
|
||||
CMD_ANY_VALUE_V(category_name + ".max_alloc.set", [category](auto, auto& value) {
|
||||
torrent::runtime::socket_manager()->set_category_max_allocation(category, checked_socket_value(value, "socket max alloc"));
|
||||
});
|
||||
}
|
||||
|
||||
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.time");
|
||||
rpc::rpc.mark_safe("system.time_seconds");
|
||||
rpc::rpc.mark_safe("system.time_usec");
|
||||
rpc::rpc.mark_safe("system.torrent_name.use_sanitized");
|
||||
rpc::rpc.mark_safe("system.file.allocate");
|
||||
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.file_name.replace_slash");
|
||||
rpc::rpc.mark_safe("system.file_name.allow_legacy_utf8");
|
||||
|
||||
rpc::rpc.mark_safe("system.sockets.size");
|
||||
rpc::rpc.mark_safe("system.sockets.max_size");
|
||||
rpc::rpc.mark_safe("system.sockets.reserved_alloc");
|
||||
rpc::rpc.mark_safe("system.sockets.available_alloc");
|
||||
|
||||
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
|
||||
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
|
||||
|
||||
rpc::rpc.mark_safe(category_name + ".size");
|
||||
rpc::rpc.mark_safe(category_name + ".max_size");
|
||||
|
||||
if (i == 0) {
|
||||
rpc::rpc.mark_safe(category_name + ".min_alloc");
|
||||
continue;
|
||||
}
|
||||
|
||||
rpc::rpc.mark_safe(category_name + ".max_alloc.limit");
|
||||
rpc::rpc.mark_safe(category_name + ".min_alloc.limit");
|
||||
rpc::rpc.mark_safe(category_name + ".min_alloc");
|
||||
rpc::rpc.mark_safe(category_name + ".max_alloc");
|
||||
}
|
||||
}
|
||||
+45
-84
@@ -1,14 +1,12 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
#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"
|
||||
#include "rak/address_info.h"
|
||||
#include "rpc/parse.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
@@ -16,82 +14,42 @@
|
||||
#include "control.h"
|
||||
#include "command_helpers.h"
|
||||
|
||||
std::pair<uint32_t, uint32_t>
|
||||
parse_address_range(const torrent::Object::list_type& args, torrent::Object::list_type::const_iterator itr) {
|
||||
unsigned int prefixWidth, ret;
|
||||
char dummy;
|
||||
char host[1024];
|
||||
rak::address_info* ai;
|
||||
|
||||
ret = std::sscanf(itr->as_string().c_str(), "%1023[^/]/%d%c", host, &prefixWidth, &dummy);
|
||||
|
||||
if (ret < 1 || rak::address_info::get_address_info(host, PF_INET, SOCK_STREAM, &ai) != 0)
|
||||
throw torrent::input_error("Could not resolve host.");
|
||||
|
||||
uint32_t begin, end;
|
||||
auto sa = torrent::sa_copy(ai->c_addrinfo()->ai_addr);
|
||||
auto sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr);
|
||||
|
||||
begin = end = sa_addr;
|
||||
|
||||
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)
|
||||
throw torrent::input_error("Invalid address/prefix.");
|
||||
|
||||
end = sa_addr | ~netmask;
|
||||
|
||||
} else if (++itr != args.end()) {
|
||||
if (rak::address_info::get_address_info(itr->as_string().c_str(), PF_INET, SOCK_STREAM, &ai) != 0)
|
||||
throw torrent::input_error("Could not resolve host.");
|
||||
|
||||
sa = torrent::sa_copy(ai->c_addrinfo()->ai_addr);
|
||||
sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr);
|
||||
|
||||
rak::address_info::free_address_info(ai);
|
||||
end = sa_addr;
|
||||
}
|
||||
|
||||
// convert to [begin, end) making sure the end doesn't overflow
|
||||
// (this precludes 255.255.255.255 from ever matching, but that's not a real IP anyway)
|
||||
return std::make_pair((uint32_t)begin, (uint32_t)std::max(end, end + 1));
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_throttle(const torrent::Object::list_type& args, bool up) {
|
||||
torrent::Object::list_const_iterator argItr = args.begin();
|
||||
auto arg_itr = args.begin();
|
||||
|
||||
if (argItr == args.end())
|
||||
if (arg_itr == args.end())
|
||||
throw torrent::input_error("Missing throttle name.");
|
||||
|
||||
const std::string& name = argItr->as_string();
|
||||
const std::string& name = arg_itr->as_string();
|
||||
if (name.empty() || name == "NULL")
|
||||
throw torrent::input_error("Invalid throttle name '" + name + "'.");
|
||||
|
||||
if (++argItr == args.end() || argItr->as_string().empty())
|
||||
if (++arg_itr == args.end() || arg_itr->as_string().empty())
|
||||
throw torrent::input_error("Missing throttle rate for '" + name + "'.");
|
||||
|
||||
int64_t rate;
|
||||
rpc::parse_whole_value_nothrow(argItr->as_string().c_str(), &rate);
|
||||
|
||||
if (!rpc::parse_whole_value_nothrow(arg_itr->as_string().c_str(), &rate))
|
||||
throw torrent::input_error("Invalid throttle rate for '" + name + "'.");
|
||||
|
||||
if (rate < 0)
|
||||
throw torrent::input_error("Throttle rate must be non-negative.");
|
||||
|
||||
core::ThrottleMap::iterator itr = control->core()->throttles().find(name);
|
||||
if (itr == control->core()->throttles().end())
|
||||
itr = control->core()->throttles().insert(std::make_pair(name, torrent::ThrottlePair(NULL, NULL))).first;
|
||||
if (rate > (std::numeric_limits<int64_t>::max() >> 10))
|
||||
throw torrent::input_error("Throttle rate is too large.");
|
||||
|
||||
torrent::Throttle*& throttle = up ? itr->second.first : itr->second.second;
|
||||
if (rate != 0 && throttle == NULL)
|
||||
auto 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;
|
||||
|
||||
auto*& throttle = up ? itr->second.first : itr->second.second;
|
||||
|
||||
if (rate != 0 && throttle == nullptr)
|
||||
throttle = (up ? torrent::up_throttle_global() : torrent::down_throttle_global())->create_slave();
|
||||
|
||||
if (throttle != NULL)
|
||||
if (throttle != nullptr)
|
||||
throttle->set_max_rate(rate * 1024);
|
||||
|
||||
return torrent::Object();
|
||||
@@ -104,10 +62,10 @@ static const int throttle_info_rate = (1 << 3);
|
||||
|
||||
torrent::Object
|
||||
retrieve_throttle_info(const torrent::Object::string_type& name, int flags) {
|
||||
core::ThrottleMap::iterator itr = control->core()->throttles().find(name);
|
||||
torrent::ThrottlePair throttles = itr == control->core()->throttles().end() ? torrent::ThrottlePair(NULL, NULL) : itr->second;
|
||||
torrent::Throttle* throttle = flags & throttle_info_down ? throttles.second : throttles.first;
|
||||
torrent::Throttle* global = flags & throttle_info_down ? torrent::down_throttle_global() : torrent::up_throttle_global();
|
||||
auto itr = control->core()->throttles().find(name);
|
||||
auto throttles = (itr == control->core()->throttles().end()) ? core::ThrottlePair(nullptr, nullptr) : itr->second;
|
||||
auto* throttle = flags & throttle_info_down ? throttles.second : throttles.first;
|
||||
auto* global = flags & throttle_info_down ? torrent::down_throttle_global() : torrent::up_throttle_global();
|
||||
|
||||
if (throttle == NULL && name.empty())
|
||||
throttle = global;
|
||||
@@ -122,20 +80,6 @@ retrieve_throttle_info(const torrent::Object::string_type& name, int flags) {
|
||||
return (int64_t)throttle->max_rate();
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_address_throttle(const torrent::Object::list_type& args) {
|
||||
if (args.size() < 2 || args.size() > 3)
|
||||
throw torrent::input_error("Incorrect number of arguments.");
|
||||
|
||||
std::pair<uint32_t, uint32_t> range = parse_address_range(args, ++args.begin());
|
||||
core::ThrottleMap::iterator throttleItr = control->core()->throttles().find(args.begin()->as_string().c_str());
|
||||
if (throttleItr == control->core()->throttles().end())
|
||||
throw torrent::input_error("Throttle not found.");
|
||||
|
||||
control->core()->set_address_throttle(range.first, range.second, throttleItr->second);
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
throttle_update(const char* variable, int64_t value) {
|
||||
rpc::commands.call_command(variable, value);
|
||||
@@ -145,6 +89,24 @@ throttle_update(const char* variable, int64_t value) {
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
throttle_rate_to_kb(int64_t rate) {
|
||||
if (rate < 0 || rate > std::numeric_limits<unsigned int>::max() - 1)
|
||||
throw torrent::input_error("Throttle rate must be between 0 and 4294967294.");
|
||||
|
||||
return static_cast<unsigned int>(rate >> 10);
|
||||
}
|
||||
|
||||
static void
|
||||
set_up_throttle_i64(ui::Root* root, int64_t rate) {
|
||||
root->set_up_throttle(throttle_rate_to_kb(rate));
|
||||
}
|
||||
|
||||
static void
|
||||
set_down_throttle_i64(ui::Root* root, int64_t rate) {
|
||||
root->set_down_throttle(throttle_rate_to_kb(rate));
|
||||
}
|
||||
|
||||
void
|
||||
initialize_command_throttle() {
|
||||
CMD2_ANY ("throttle.unchoked_uploads", std::bind(&torrent::ResourceManager::currently_upload_unchoked, torrent::resource_manager()));
|
||||
@@ -181,19 +143,18 @@ initialize_command_throttle() {
|
||||
CMD2_ANY ("throttle.global_up.rate", std::bind(&torrent::Rate::rate, torrent::up_rate()));
|
||||
CMD2_ANY ("throttle.global_up.total", std::bind(&torrent::Rate::total, torrent::up_rate()));
|
||||
CMD2_ANY ("throttle.global_up.max_rate", std::bind(&torrent::Throttle::max_rate, torrent::up_throttle_global()));
|
||||
CMD2_ANY_VALUE_V ("throttle.global_up.max_rate.set", std::bind(&ui::Root::set_up_throttle_i64, control->ui(), std::placeholders::_2));
|
||||
CMD2_ANY_VALUE_KB("throttle.global_up.max_rate.set_kb", std::bind(&ui::Root::set_up_throttle_i64, control->ui(), std::placeholders::_2));
|
||||
CMD2_ANY_VALUE_V ("throttle.global_up.max_rate.set", std::bind(&set_up_throttle_i64, control->ui(), std::placeholders::_2));
|
||||
CMD2_ANY_VALUE_KB("throttle.global_up.max_rate.set_kb", std::bind(&set_up_throttle_i64, control->ui(), std::placeholders::_2));
|
||||
CMD2_ANY ("throttle.global_down.rate", std::bind(&torrent::Rate::rate, torrent::down_rate()));
|
||||
CMD2_ANY ("throttle.global_down.total", std::bind(&torrent::Rate::total, torrent::down_rate()));
|
||||
CMD2_ANY ("throttle.global_down.max_rate", std::bind(&torrent::Throttle::max_rate, torrent::down_throttle_global()));
|
||||
CMD2_ANY_VALUE_V ("throttle.global_down.max_rate.set", std::bind(&ui::Root::set_down_throttle_i64, control->ui(), std::placeholders::_2));
|
||||
CMD2_ANY_VALUE_KB("throttle.global_down.max_rate.set_kb", std::bind(&ui::Root::set_down_throttle_i64, control->ui(), std::placeholders::_2));
|
||||
CMD2_ANY_VALUE_V ("throttle.global_down.max_rate.set", std::bind(&set_down_throttle_i64, control->ui(), std::placeholders::_2));
|
||||
CMD2_ANY_VALUE_KB("throttle.global_down.max_rate.set_kb", std::bind(&set_down_throttle_i64, control->ui(), std::placeholders::_2));
|
||||
|
||||
// Temporary names, need to change this to accept real rates rather
|
||||
// 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));
|
||||
|
||||
+52
-57
@@ -3,9 +3,10 @@
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <netdb.h>
|
||||
#include <torrent/net/network_config.h>
|
||||
#include <torrent/net/resolver.h>
|
||||
#include <torrent/runtime/network_config.h>
|
||||
#include <torrent/runtime/network_manager.h>
|
||||
#include <torrent/system/callbacks.h>
|
||||
#include <torrent/tracker/dht_controller.h>
|
||||
#include <torrent/tracker/tracker.h>
|
||||
#include <torrent/utils/log.h>
|
||||
@@ -48,8 +49,10 @@ apply_dht_add_node(const std::string& arg) {
|
||||
|
||||
// TODO: Move this lookup to DhtController.
|
||||
|
||||
auto callback_id = torrent::system::make_callback_id();
|
||||
|
||||
// Currently discarding SOCK_STREAM.
|
||||
torrent::this_thread::resolver()->resolve_specific(nullptr, host_str, PF_INET, [host_str, port](torrent::c_sa_shared_ptr sa, int err) {
|
||||
torrent::this_thread::resolver()->resolve_specific(callback_id, host_str, PF_INET, [host_str, port](torrent::c_sa_shared_ptr sa, int err) {
|
||||
if (sa == nullptr) {
|
||||
lt_log_print(torrent::LOG_DHT_ERROR, "dht.add_node : could not resolve host : %s (%s)", gai_strerror(err), host_str.c_str());
|
||||
return;
|
||||
@@ -65,21 +68,11 @@ 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())
|
||||
for (const auto& download : *control->core()->download_list())
|
||||
download->tracker_controller().for_each([](auto& tracker) { tracker.disable(); });
|
||||
|
||||
} else if (rpc::call_command_value("trackers.use_udp") == 0) {
|
||||
for (auto download : *control->core()->download_list()) {
|
||||
download->tracker_controller().for_each([](auto& tracker) {
|
||||
if (tracker.type() == torrent::TRACKER_UDP)
|
||||
tracker.disable();
|
||||
else
|
||||
tracker.enable();
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
for (auto download : *control->core()->download_list())
|
||||
for (const auto& download : *control->core()->download_list())
|
||||
download->tracker_controller().for_each([](auto& tracker) { tracker.enable(); });
|
||||
}
|
||||
|
||||
@@ -88,69 +81,71 @@ apply_enable_trackers(int64_t arg) {
|
||||
|
||||
void
|
||||
initialize_command_tracker() {
|
||||
CMD2_TRACKER ("t.is_busy", std::bind(&torrent::tracker::Tracker::is_busy, std::placeholders::_1));
|
||||
CMD2_TRACKER ("t.is_enabled", std::bind(&torrent::tracker::Tracker::is_enabled, std::placeholders::_1));
|
||||
CMD2_TRACKER ("t.is_extra_tracker", std::bind(&torrent::tracker::Tracker::is_extra_tracker, std::placeholders::_1));
|
||||
CMD2_TRACKER ("t.is_open", std::bind(&torrent::tracker::Tracker::is_busy, std::placeholders::_1));
|
||||
CMD2_TRACKER ("t.is_scrapable", std::bind(&torrent::tracker::Tracker::is_scrapable, std::placeholders::_1));
|
||||
CMD2_TRACKER ("t.is_usable", std::bind(&torrent::tracker::Tracker::is_usable, std::placeholders::_1));
|
||||
CMD2_TRACKER ("t.is_busy", [](auto* tracker, auto) { return tracker->is_requesting(); });
|
||||
CMD2_TRACKER ("t.is_enabled", [](auto* tracker, auto) { return tracker->is_enabled(); });
|
||||
CMD2_TRACKER ("t.is_extra_tracker", [](auto* tracker, auto) { return tracker->is_extra_tracker(); });
|
||||
CMD2_TRACKER ("t.is_open", [](auto* tracker, auto) { return tracker->is_requesting(); });
|
||||
CMD2_TRACKER ("t.is_scrapable", [](auto* tracker, auto) { return tracker->is_scrapable(); });
|
||||
CMD2_TRACKER ("t.is_usable", [](auto* tracker, auto) { return tracker->is_usable(); });
|
||||
|
||||
// TODO: Deprecate.
|
||||
CMD2_TRACKER ("t.can_scrape", std::bind(&torrent::tracker::Tracker::is_scrapable, std::placeholders::_1));
|
||||
CMD2_TRACKER ("t.can_scrape", [](auto* tracker, auto) { return tracker->is_scrapable(); });
|
||||
|
||||
CMD2_TRACKER_V ("t.enable", std::bind(&torrent::tracker::Tracker::enable, std::placeholders::_1));
|
||||
CMD2_TRACKER_V ("t.disable", std::bind(&torrent::tracker::Tracker::disable, std::placeholders::_1));
|
||||
CMD2_TRACKER_V ("t.enable", [](auto* tracker, auto) { tracker->enable(); });
|
||||
CMD2_TRACKER_V ("t.disable", [](auto* tracker, auto) { tracker->disable(); });
|
||||
|
||||
CMD2_TRACKER_VALUE_V("t.is_enabled.set", std::bind(&tracker_set_enabled, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_TRACKER_VALUE_V("t.is_enabled.set", [](auto* tracker, auto& value) { return tracker_set_enabled(tracker, value); });
|
||||
|
||||
CMD2_TRACKER ("t.url", std::bind(&torrent::tracker::Tracker::url, std::placeholders::_1));
|
||||
CMD2_TRACKER ("t.group", std::bind(&torrent::tracker::Tracker::group, std::placeholders::_1));
|
||||
CMD2_TRACKER ("t.type", std::bind(&torrent::tracker::Tracker::type, std::placeholders::_1));
|
||||
CMD2_TRACKER ("t.id", std::bind(&torrent::tracker::Tracker::tracker_id, std::placeholders::_1));
|
||||
CMD2_TRACKER ("t.url", [](auto* tracker, auto) { return tracker->url(); });
|
||||
CMD2_TRACKER ("t.group", [](auto* tracker, auto) { return tracker->group(); });
|
||||
CMD2_TRACKER ("t.type", [](auto* tracker, auto) { return tracker->type(); });
|
||||
CMD2_TRACKER ("t.id", [](auto* tracker, auto) { return tracker->tracker_id(); });
|
||||
|
||||
CMD2_TRACKER ("t.latest_event", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().latest_event(); });
|
||||
CMD2_TRACKER ("t.latest_new_peers", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().latest_new_peers(); });
|
||||
CMD2_TRACKER ("t.latest_sum_peers", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().latest_sum_peers(); });
|
||||
CMD2_TRACKER ("t.latest_event", [](auto* tracker, auto) { return tracker->state().latest_event(); });
|
||||
CMD2_TRACKER ("t.latest_new_peers", [](auto* tracker, auto) { return tracker->state().latest_new_peers(); });
|
||||
CMD2_TRACKER ("t.latest_sum_peers", [](auto* tracker, auto) { return tracker->state().latest_sum_peers(); });
|
||||
|
||||
CMD2_TRACKER ("t.normal_interval", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().normal_interval(); });
|
||||
CMD2_TRACKER ("t.min_interval", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().min_interval(); });
|
||||
CMD2_TRACKER ("t.normal_interval", [](auto* tracker, auto) { return tracker->state().normal_interval().count(); });
|
||||
CMD2_TRACKER ("t.min_interval", [](auto* tracker, auto) { return tracker->state().min_interval().count(); });
|
||||
|
||||
CMD2_TRACKER ("t.activity_time_next", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().activity_time_next(); });
|
||||
CMD2_TRACKER ("t.activity_time_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().activity_time_last(); });
|
||||
CMD2_TRACKER ("t.activity_time_next", [](auto* tracker, auto) { return tracker->state().activity_time_next().count(); });
|
||||
CMD2_TRACKER ("t.activity_time_last", [](auto* tracker, auto) { return tracker->state().activity_time_last().count(); });
|
||||
|
||||
CMD2_TRACKER ("t.success_time_next", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().success_time_next(); });
|
||||
CMD2_TRACKER ("t.success_time_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().success_time_last(); });
|
||||
CMD2_TRACKER ("t.success_counter", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().success_counter(); });
|
||||
CMD2_TRACKER ("t.success_time_next", [](auto* tracker, auto) { return tracker->state().success_time_next().count(); });
|
||||
CMD2_TRACKER ("t.success_time_last", [](auto* tracker, auto) { return tracker->state().success_time_last().count(); });
|
||||
CMD2_TRACKER ("t.success_counter", [](auto* tracker, auto) { return tracker->state().success_counter(); });
|
||||
|
||||
CMD2_TRACKER ("t.failed_time_next", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().failed_time_next(); });
|
||||
CMD2_TRACKER ("t.failed_time_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().failed_time_last(); });
|
||||
CMD2_TRACKER ("t.failed_counter", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().failed_counter(); });
|
||||
CMD2_TRACKER ("t.failed_time_next", [](auto* tracker, auto) { return tracker->state().failed_time_next().count(); });
|
||||
CMD2_TRACKER ("t.failed_time_last", [](auto* tracker, auto) { return tracker->state().failed_time_last().count(); });
|
||||
CMD2_TRACKER ("t.failed_counter", [](auto* tracker, auto) { return tracker->state().failed_counter(); });
|
||||
|
||||
CMD2_TRACKER ("t.scrape_time_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_time_last(); });
|
||||
CMD2_TRACKER ("t.scrape_counter", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_counter(); });
|
||||
CMD2_TRACKER ("t.scrape_time_last", [](auto* tracker, auto) { return tracker->state().scrape_time_last().count(); });
|
||||
CMD2_TRACKER ("t.scrape_counter", [](auto* tracker, auto) { return tracker->state().scrape_counter(); });
|
||||
|
||||
CMD2_TRACKER ("t.scrape_complete", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_complete(); });
|
||||
CMD2_TRACKER ("t.scrape_incomplete", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_incomplete(); });
|
||||
CMD2_TRACKER ("t.scrape_downloaded", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_downloaded(); });
|
||||
CMD2_TRACKER ("t.scrape_complete", [](auto* tracker, auto) { return tracker->state().scrape_complete(); });
|
||||
CMD2_TRACKER ("t.scrape_incomplete", [](auto* tracker, auto) { return tracker->state().scrape_incomplete(); });
|
||||
CMD2_TRACKER ("t.scrape_downloaded", [](auto* tracker, auto) { return tracker->state().scrape_downloaded(); });
|
||||
|
||||
CMD2_ANY_VALUE ("trackers.enable", std::bind(&apply_enable_trackers, int64_t(1)));
|
||||
CMD2_ANY_VALUE ("trackers.disable", std::bind(&apply_enable_trackers, int64_t(0)));
|
||||
CMD2_ANY_VALUE ("trackers.enable", [](auto, auto) { return apply_enable_trackers(1); });
|
||||
CMD2_ANY_VALUE ("trackers.disable", [](auto, auto) { return apply_enable_trackers(0); });
|
||||
CMD2_VAR_BOOL ("trackers.delay_scrape", false);
|
||||
CMD2_VAR_VALUE ("trackers.numwant", -1);
|
||||
CMD2_VAR_BOOL ("trackers.use_udp", true);
|
||||
CMD2_VAR_VALUE ("trackers.numwant", -1);
|
||||
|
||||
auto dht_manager = control->dht_manager();
|
||||
CMD2_ANY ("trackers.use_udp", [](auto, auto) { return true; });
|
||||
CMD2_ANY_VALUE_V ("trackers.use_udp.set", [](auto, auto) {
|
||||
lt_log_print(torrent::LOG_ERROR, "trackers.use_udp.set is no longer supported", 0);
|
||||
})
|
||||
|
||||
CMD2_ANY_STRING_V ("dht.mode.set", std::bind(&core::DhtManager::set_mode, control->dht_manager(), std::placeholders::_2));
|
||||
CMD2_ANY ("dht.port", [](auto, auto) { return torrent::runtime::network_manager()->dht_controller()->port(); });
|
||||
CMD2_ANY_STRING_V ("dht.mode.set", [](auto, auto& str) { return control->dht_manager()->set_mode_by_user(str); });
|
||||
CMD2_ANY ("dht.port", [](auto, auto) { return torrent::runtime::network_manager()->dht_controller()->port(); });
|
||||
CMD2_ANY_VALUE_V ("dht.port.set", [](auto, auto) {
|
||||
lt_log_print(torrent::LOG_DHT_ERROR, "dht.port.set is no longer supported, use dht.override_port.set", 0);
|
||||
});
|
||||
CMD2_ANY ("dht.override_port", std::bind(&torrent::net::NetworkConfig::override_dht_port, torrent::config::network_config()));
|
||||
CMD2_ANY_VALUE_V ("dht.override_port.set", std::bind(&torrent::net::NetworkConfig::set_override_dht_port, torrent::config::network_config(), std::placeholders::_2));
|
||||
CMD2_ANY ("dht.override_port", [](auto, auto) { return torrent::runtime::network_config()->override_dht_port(); });
|
||||
CMD2_ANY_VALUE_V ("dht.override_port.set", [](auto, auto& value) { return torrent::runtime::network_manager()->set_dht_port(checked_port_value(value, "DHT override")); });
|
||||
|
||||
CMD2_ANY_STRING ("dht.add_node", std::bind(&apply_dht_add_node, std::placeholders::_2));
|
||||
CMD2_ANY ("dht.statistics", std::bind(&core::DhtManager::dht_statistics, dht_manager));
|
||||
CMD2_ANY_STRING ("dht.add_node", [](auto, auto& str) { return apply_dht_add_node(str); });
|
||||
CMD2_ANY ("dht.statistics", [](auto, auto) { return control->dht_manager()->dht_statistics(); });
|
||||
|
||||
rpc::rpc.mark_safe("t.url");
|
||||
rpc::rpc.mark_safe("t.group");
|
||||
|
||||
+108
-65
@@ -2,9 +2,10 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
#include <regex>
|
||||
#include <rak/algorithm.h>
|
||||
#include <torrent/utils/log.h>
|
||||
|
||||
#include "core/manager.h"
|
||||
@@ -126,11 +127,20 @@ apply_value([[maybe_unused]] rpc::target_type target, const torrent::Object::lis
|
||||
if (args.front().is_value()) {
|
||||
val = args.front().as_value();
|
||||
} else {
|
||||
int base = args.size() > 1 ? args.back().is_value() ?
|
||||
args.back().as_value() : strtol(args.back().as_string().c_str(), NULL, 10) : 10;
|
||||
int64_t base = args.size() > 1 ? args.back().is_value() ?
|
||||
args.back().as_value() : strtoll(args.back().as_string().c_str(), NULL, 10) : 10;
|
||||
|
||||
if (base != 0 && (base < 2 || base > 36))
|
||||
throw torrent::input_error("'value' base must be 0 or between 2 and 36!");
|
||||
|
||||
char* endptr = 0;
|
||||
errno = 0;
|
||||
|
||||
val = strtoll(args.front().as_string().c_str(), &endptr, base);
|
||||
|
||||
if (errno == ERANGE)
|
||||
throw torrent::input_error("Number out of range: " + args.front().as_string());
|
||||
|
||||
while (*endptr == ' ' || *endptr == '\n') ++endptr;
|
||||
if (*endptr)
|
||||
throw torrent::input_error("Junk at end of number: " + args.front().as_string());
|
||||
@@ -408,8 +418,9 @@ apply_to_time(const torrent::Object& rawArgs, int flags) {
|
||||
torrent::Object
|
||||
apply_to_elapsed_time(const torrent::Object& rawArgs) {
|
||||
auto cached_seconds = torrent::this_thread::cached_seconds().count();
|
||||
auto value = rawArgs.as_value();
|
||||
|
||||
uint64_t arg = cached_seconds - rawArgs.as_value();
|
||||
uint64_t arg = value >= 0 && value <= cached_seconds ? cached_seconds - value : 0;
|
||||
|
||||
char buffer[48];
|
||||
snprintf(buffer, 48, "%2d:%02d:%02d", (int)(arg / 3600), (int)((arg / 60) % 60), (int)(arg % 60));
|
||||
@@ -473,11 +484,14 @@ apply_to_throttle(const torrent::Object& rawArgs) {
|
||||
// if (cond1) { branch1 } else if (cond2) { branch2 } else { branch3 }
|
||||
// <cond1>,<branch1>,<cond2>,<branch2>,<branch3>
|
||||
torrent::Object
|
||||
apply_if(rpc::target_type target, const torrent::Object& rawArgs, int flags) {
|
||||
const torrent::Object::list_type& args = rawArgs.as_list();
|
||||
torrent::Object::list_const_iterator itr = args.begin();
|
||||
apply_if(rpc::target_type target, const torrent::Object& raw_args, int flags) {
|
||||
auto& args = raw_args.as_list();
|
||||
auto itr = args.begin();
|
||||
|
||||
while (itr != args.end() && itr != --args.end()) {
|
||||
if (args.empty())
|
||||
throw torrent::input_error("Empty argument list to " + std::string((flags & 0x1) ? "branch" : "if") + ".");
|
||||
|
||||
{
|
||||
torrent::Object tmp;
|
||||
const torrent::Object* conditional;
|
||||
|
||||
@@ -501,40 +515,44 @@ apply_if(rpc::target_type target, const torrent::Object& rawArgs, int flags) {
|
||||
result = false;
|
||||
break;
|
||||
default:
|
||||
throw torrent::input_error("Type not supported by 'if'.");
|
||||
throw torrent::input_error("Type not supported by " + std::string((flags & 0x1) ? "branch" : "if") + ".");
|
||||
};
|
||||
|
||||
itr++;
|
||||
|
||||
if (result)
|
||||
break;
|
||||
|
||||
itr++;
|
||||
if (!result && itr != args.end())
|
||||
itr++;
|
||||
}
|
||||
|
||||
if (itr == args.end())
|
||||
return torrent::Object();
|
||||
|
||||
if (flags & 0x1 && itr->is_string()) {
|
||||
return rpc::parse_command(target, itr->as_string().c_str(), itr->as_string().c_str() + itr->as_string().size()).first;
|
||||
if (flags & 0x1) {
|
||||
if (itr->is_string())
|
||||
return rpc::parse_command(target, itr->as_string().c_str(), itr->as_string().c_str() + itr->as_string().size()).first;
|
||||
|
||||
} else if (flags & 0x1 && itr->is_dict_key()) {
|
||||
return rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target);
|
||||
if (itr->is_dict_key())
|
||||
return rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target);
|
||||
|
||||
} else if (flags & 0x1 && itr->is_list()) {
|
||||
// Move this into a special function or something. Also, might be
|
||||
// nice to have a parse_command function that takes list
|
||||
// iterator...
|
||||
if (itr->is_list()) {
|
||||
for (const auto& cmd_itr : itr->as_list()) {
|
||||
if (cmd_itr.is_string())
|
||||
rpc::parse_command(target, cmd_itr.as_string().c_str(), cmd_itr.as_string().c_str() + cmd_itr.as_string().size());
|
||||
|
||||
for (const auto& cmdItr : itr->as_list())
|
||||
if (cmdItr.is_string())
|
||||
rpc::parse_command(target, cmdItr.as_string().c_str(), cmdItr.as_string().c_str() + cmdItr.as_string().size());
|
||||
else if (cmd_itr.is_dict_key())
|
||||
rpc::commands.call_command(cmd_itr.as_dict_key().c_str(), cmd_itr.as_dict_obj(), target);
|
||||
|
||||
return torrent::Object();
|
||||
else
|
||||
throw torrent::input_error("Invalid command type in branch list.");
|
||||
}
|
||||
|
||||
} else {
|
||||
return *itr;
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
throw torrent::input_error("Invalid command type in branch.");
|
||||
}
|
||||
|
||||
return *itr;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
@@ -668,8 +686,13 @@ apply_math_basic(const char* name, Comp op, const torrent::Object::list_type& ar
|
||||
throw torrent::input_error(std::string(name) + ": Wrong argument type");
|
||||
}
|
||||
|
||||
if (divides && !rhs && itr != args.begin())
|
||||
throw torrent::input_error(std::string(name) + ": Division by zero!");
|
||||
if (divides && itr != args.begin()) {
|
||||
if (rhs == 0)
|
||||
throw torrent::input_error(std::string(name) + ": Division by zero!");
|
||||
|
||||
if (val == std::numeric_limits<int64_t>::min() && rhs == -1)
|
||||
throw torrent::input_error(std::string(name) + ": Division overflow!");
|
||||
}
|
||||
|
||||
val = itr == args.begin() ? rhs : op(val, rhs);
|
||||
|
||||
@@ -730,6 +753,26 @@ apply_arith_count(const torrent::Object::list_type& args) {
|
||||
return val;
|
||||
}
|
||||
|
||||
// Get the median of an unordered set of numbers of arbitrary
|
||||
// type by modifing the underlying dataset
|
||||
template <typename T = double, typename _InputIter>
|
||||
T median(_InputIter __first, _InputIter __last) {
|
||||
T __med;
|
||||
|
||||
unsigned int __size = __last - __first;
|
||||
unsigned int __middle = __size / 2;
|
||||
_InputIter __target1 = __first + __middle;
|
||||
std::nth_element(__first, __target1, __last);
|
||||
__med = *__target1;
|
||||
|
||||
if (__size % 2 == 0) {
|
||||
_InputIter __target2 = std::max_element(__first, __target1);
|
||||
__med = (__med + *__target2) / 2.0;
|
||||
}
|
||||
|
||||
return __med;
|
||||
}
|
||||
|
||||
int64_t
|
||||
apply_arith_other(const char* op, const torrent::Object::list_type& args) {
|
||||
if (args.size() == 0)
|
||||
@@ -739,7 +782,8 @@ apply_arith_other(const char* op, const torrent::Object::list_type& args) {
|
||||
return (int64_t)(apply_math_basic(op, std::plus<int64_t>(), args) / apply_arith_count(args));
|
||||
} else if (strcmp(op, "median") == 0) {
|
||||
std::vector<int64_t> result = as_vector(args);
|
||||
return (int64_t)rak::median(result.begin(), result.end());
|
||||
|
||||
return (int64_t)median(result.begin(), result.end());
|
||||
} else {
|
||||
throw torrent::input_error("Wrong operation supplied to apply_arith_other.");
|
||||
}
|
||||
@@ -775,61 +819,59 @@ apply_set_color(int color_id, const torrent::Object::string_type& color_str) {
|
||||
|
||||
void
|
||||
initialize_command_ui() {
|
||||
CMD2_VAR_STRING("keys.layout", "qwerty");
|
||||
CMD2_VAR_STRING ("keys.layout", "qwerty");
|
||||
|
||||
CMD2_ANY_STRING("view.add", object_convert_void(std::bind(&core::ViewManager::insert_throw, control->view_manager(), std::placeholders::_2)));
|
||||
CMD2_ANY_STRING ("view.add", object_convert_void(std::bind(&core::ViewManager::insert_throw, control->view_manager(), std::placeholders::_2)));
|
||||
|
||||
CMD2_ANY_L ("view.list", std::bind(&apply_view_list));
|
||||
CMD2_ANY_LIST("view.set", std::bind(&apply_view_set, std::placeholders::_2));
|
||||
CMD2_ANY_L ("view.list", std::bind(&apply_view_list));
|
||||
CMD2_ANY_LIST ("view.set", std::bind(&apply_view_set, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_LIST ("view.filter", std::bind(&apply_view_event, &core::ViewManager::set_filter, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("view.filter_on", std::bind(&apply_view_filter_on, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("view.filter.temp", std::bind(&apply_view_event, &core::ViewManager::set_filter_temp, std::placeholders::_2));
|
||||
CMD2_VAR_STRING("view.filter.temp.excluded", "default,started,stopped");
|
||||
CMD2_VAR_BOOL ("view.filter.temp.log", 0);
|
||||
CMD2_ANY_LIST ("view.filter", std::bind(&apply_view_event, &core::ViewManager::set_filter, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("view.filter_on", std::bind(&apply_view_filter_on, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("view.filter.temp", std::bind(&apply_view_event, &core::ViewManager::set_filter_temp, std::placeholders::_2));
|
||||
CMD2_VAR_STRING ("view.filter.temp.excluded", "default,started,stopped");
|
||||
CMD2_VAR_BOOL ("view.filter.temp.log", 0);
|
||||
|
||||
CMD2_ANY_LIST("view.sort", std::bind(&apply_view_sort, std::placeholders::_2));
|
||||
CMD2_ANY_LIST("view.sort_new", std::bind(&apply_view_event, &core::ViewManager::set_sort_new, std::placeholders::_2));
|
||||
CMD2_ANY_LIST("view.sort_current", std::bind(&apply_view_event, &core::ViewManager::set_sort_current, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("view.sort", std::bind(&apply_view_sort, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("view.sort_new", std::bind(&apply_view_event, &core::ViewManager::set_sort_new, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("view.sort_current", std::bind(&apply_view_event, &core::ViewManager::set_sort_current, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_LIST("view.event_added", std::bind(&apply_view_event, &core::ViewManager::set_event_added, std::placeholders::_2));
|
||||
CMD2_ANY_LIST("view.event_removed", std::bind(&apply_view_event, &core::ViewManager::set_event_removed, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("view.event_added", std::bind(&apply_view_event, &core::ViewManager::set_event_added, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("view.event_removed", std::bind(&apply_view_event, &core::ViewManager::set_event_removed, std::placeholders::_2));
|
||||
|
||||
// Cleanup and add . to view.
|
||||
|
||||
CMD2_ANY_STRING("view.size", std::bind(&cmd_view_size, std::placeholders::_2));
|
||||
CMD2_ANY_STRING("view.size_not_visible", std::bind(&cmd_view_size_not_visible, std::placeholders::_2));
|
||||
CMD2_ANY_STRING("view.persistent", std::bind(&cmd_view_persistent, std::placeholders::_2));
|
||||
CMD2_ANY_STRING ("view.size", std::bind(&cmd_view_size, std::placeholders::_2));
|
||||
CMD2_ANY_STRING ("view.size_not_visible", std::bind(&cmd_view_size_not_visible, std::placeholders::_2));
|
||||
CMD2_ANY_STRING ("view.persistent", std::bind(&cmd_view_persistent, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_STRING_V("view.filter_all", std::bind(&core::View::filter, std::bind(&core::ViewManager::find_ptr_throw, control->view_manager(), std::placeholders::_2)));
|
||||
|
||||
CMD2_DL_STRING ("view.filter_download", std::bind(&cmd_view_filter_download, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL_STRING ("view.set_visible", std::bind(&cmd_view_set_visible, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL_STRING ("view.set_not_visible", std::bind(&cmd_view_set_not_visible, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL_STRING ("view.filter_download", std::bind(&cmd_view_filter_download, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL_STRING ("view.set_visible", std::bind(&cmd_view_set_visible, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL_STRING ("view.set_not_visible", std::bind(&cmd_view_set_not_visible, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
// Commands that affect the default rtorrent UI.
|
||||
CMD2_DL ("ui.unfocus_download", std::bind(&cmd_ui_unfocus_download, std::placeholders::_1));
|
||||
CMD2_ANY ("ui.current_view", std::bind(&cmd_ui_current_view));
|
||||
CMD2_ANY_STRING("ui.current_view.set", std::bind(&cmd_ui_set_view, std::placeholders::_2));
|
||||
CMD2_DL ("ui.unfocus_download", std::bind(&cmd_ui_unfocus_download, std::placeholders::_1));
|
||||
CMD2_ANY ("ui.current_view", std::bind(&cmd_ui_current_view));
|
||||
CMD2_ANY_STRING ("ui.current_view.set", std::bind(&cmd_ui_set_view, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY ("ui.input.history.size", std::bind(&ui::Root::get_input_history_size, control->ui()));
|
||||
CMD2_ANY_VALUE_V("ui.input.history.size.set", std::bind(&ui::Root::set_input_history_size, control->ui(), std::placeholders::_2));
|
||||
CMD2_ANY_V ("ui.input.history.clear", std::bind(&ui::Root::clear_input_history, control->ui()));
|
||||
CMD2_ANY ("ui.input.history.size", std::bind(&ui::Root::get_input_history_size, control->ui()));
|
||||
CMD2_ANY_VALUE_V ("ui.input.history.size.set", std::bind(&ui::Root::set_input_history_size, control->ui(), std::placeholders::_2));
|
||||
CMD2_ANY_V ("ui.input.history.clear", std::bind(&ui::Root::clear_input_history, control->ui()));
|
||||
|
||||
CMD2_VAR_VALUE ("ui.throttle.global.step.small", 5);
|
||||
CMD2_VAR_VALUE ("ui.throttle.global.step.medium", 50);
|
||||
CMD2_VAR_VALUE ("ui.throttle.global.step.large", 500);
|
||||
CMD2_VAR_VALUE ("ui.throttle.global.step.small", 5);
|
||||
CMD2_VAR_VALUE ("ui.throttle.global.step.medium", 50);
|
||||
CMD2_VAR_VALUE ("ui.throttle.global.step.large", 500);
|
||||
|
||||
CMD2_VAR_VALUE ("ui.focus.page_size", 0);
|
||||
CMD2_VAR_VALUE ("ui.focus.page_size", 0);
|
||||
|
||||
CMD2_ANY_LIST ("ui.status.throttle.up.set", std::bind(&cmd_status_throttle_names, true, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("ui.status.throttle.down.set", std::bind(&cmd_status_throttle_names, false, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("ui.status.throttle.up.set", std::bind(&cmd_status_throttle_names, true, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("ui.status.throttle.down.set", std::bind(&cmd_status_throttle_names, false, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY ("ui.keymap.style", std::bind(&ui::Root::keymap_style, control->ui()));
|
||||
CMD2_ANY_STRING_V("ui.keymap.style.set", std::bind(&ui::Root::set_keymap_style, control->ui(), std::placeholders::_2));
|
||||
|
||||
// TODO: Add 'option_string' for rtorrent-specific options.
|
||||
CMD2_VAR_STRING("ui.torrent_list.layout", "full");
|
||||
CMD2_VAR_STRING ("ui.torrent_list.layout", "full");
|
||||
|
||||
// Move.
|
||||
CMD2_ANY("print", &apply_print);
|
||||
@@ -891,6 +933,7 @@ initialize_command_ui() {
|
||||
|
||||
rpc::rpc.mark_safe("view.set_visible");
|
||||
rpc::rpc.mark_safe("view.set_not_visible");
|
||||
rpc::rpc.mark_safe("view.list");
|
||||
|
||||
rpc::rpc.mark_safe("cat");
|
||||
rpc::rpc.mark_safe("if");
|
||||
|
||||
+33
-12
@@ -6,6 +6,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <torrent/net/http_stack.h>
|
||||
#include <torrent/runtime/network_manager.h>
|
||||
#include <torrent/runtime/runtime.h>
|
||||
#include <torrent/utils/directory_events.h>
|
||||
|
||||
#include "core/dht_manager.h"
|
||||
@@ -27,6 +28,7 @@
|
||||
#include "rpc/object_storage.h"
|
||||
#include "session/session_manager.h"
|
||||
#include "ui/root.h"
|
||||
#include "utils/watch_ready_queue.h"
|
||||
|
||||
Control::Control()
|
||||
: m_ui(new ui::Root()),
|
||||
@@ -36,7 +38,8 @@ Control::Control()
|
||||
m_commandScheduler(new rpc::CommandScheduler()),
|
||||
m_objectStorage(new rpc::object_storage()),
|
||||
m_lua_engine(new rpc::LuaEngine()),
|
||||
m_directory_events(new torrent::directory_events()) {
|
||||
m_directory_events(new torrent::directory_events()),
|
||||
m_watch_ready_queue(new utils::WatchReadyQueue()) {
|
||||
|
||||
m_core = std::make_unique<core::Manager>();
|
||||
m_view_manager = std::make_unique<core::ViewManager>();
|
||||
@@ -44,7 +47,8 @@ Control::Control()
|
||||
|
||||
m_inputStdin->slot_pressed(std::bind(&input::Manager::pressed, m_input.get(), std::placeholders::_1));
|
||||
|
||||
m_task_shutdown.slot() = std::bind(&Control::handle_shutdown, this);
|
||||
m_task_shutdown.slot() = [this] { handle_shutdown(); };
|
||||
m_task_shutdown_clear_requests.slot() = [this] { handle_shutdown_clear_requests(); };
|
||||
|
||||
m_commandScheduler->set_slot_error_message([this](const std::string& msg) { m_core->push_log_std(msg); });
|
||||
}
|
||||
@@ -66,15 +70,12 @@ Control::initialize() {
|
||||
display::Window::slot_unschedule([this](display::Window* w) { m_display->unschedule(w); });
|
||||
display::Window::slot_adjust([this]() { m_display->adjust_layout(); });
|
||||
|
||||
torrent::net_thread::http_stack()->set_user_agent(USER_AGENT);
|
||||
|
||||
m_core->listen_open();
|
||||
m_core->set_hashing_view(*m_view_manager->find_throw("hashing"));
|
||||
|
||||
m_ui->init(this);
|
||||
|
||||
if(!display::Canvas::daemon())
|
||||
m_inputStdin->insert(torrent::this_thread::poll());
|
||||
m_inputStdin->insert();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -82,9 +83,12 @@ Control::cleanup() {
|
||||
rpc::rpc.cleanup();
|
||||
|
||||
torrent::this_thread::scheduler()->erase(&m_task_shutdown);
|
||||
torrent::this_thread::scheduler()->erase(&m_task_shutdown_clear_requests);
|
||||
|
||||
if(!display::Canvas::daemon())
|
||||
m_inputStdin->remove(torrent::this_thread::poll());
|
||||
m_inputStdin->remove();
|
||||
|
||||
m_directory_events->close();
|
||||
|
||||
if (scgi_thread::thread()->is_active())
|
||||
scgi_thread::thread()->stop_thread_wait();
|
||||
@@ -109,7 +113,7 @@ Control::cleanup_exception() {
|
||||
|
||||
bool
|
||||
Control::is_shutdown_completed() {
|
||||
if (!m_shutdownQuick)
|
||||
if (!m_shutdown_quick)
|
||||
return false;
|
||||
|
||||
// Tracker requests can be disowned, so wait for these to
|
||||
@@ -121,18 +125,21 @@ Control::is_shutdown_completed() {
|
||||
if (torrent::net_thread::http_stack()->size() != 0 || !core()->http_queue()->empty())
|
||||
return false;
|
||||
|
||||
return torrent::is_inactive();
|
||||
return core()->is_download_shutdown_completed();
|
||||
}
|
||||
|
||||
void
|
||||
Control::handle_shutdown() {
|
||||
m_watch_ready_queue->shutdown();
|
||||
|
||||
rpc::commands.call_catch("event.system.shutdown", rpc::make_target(), "shutdown", "System shutdown event action failed: ");
|
||||
|
||||
if (scgi_thread::thread()->is_active())
|
||||
scgi_thread::thread()->stop_thread_wait();
|
||||
|
||||
if (!m_shutdownQuick) {
|
||||
if (!m_shutdown_quick) {
|
||||
torrent::runtime::network_manager()->listen_close();
|
||||
torrent::runtime::shutdown();
|
||||
|
||||
m_directory_events->close();
|
||||
m_core->shutdown(false);
|
||||
@@ -141,10 +148,24 @@ Control::handle_shutdown() {
|
||||
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown, 5s);
|
||||
|
||||
} else {
|
||||
torrent::runtime::quick_shutdown();
|
||||
m_core->shutdown(true);
|
||||
}
|
||||
|
||||
m_shutdownQuick = true;
|
||||
m_shutdownReceived = false;
|
||||
if (!m_task_shutdown_clear_requests.is_scheduled())
|
||||
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown_clear_requests, 10s);
|
||||
|
||||
m_shutdown_quick = true;
|
||||
m_shutdown_received = false;
|
||||
}
|
||||
|
||||
void
|
||||
Control::handle_shutdown_clear_requests() {
|
||||
torrent::net_thread::http_stack()->clear_requests();
|
||||
|
||||
// Use 5s for the initial wait to ensure trackers get a chance to finish both IPv4 and IPv6 requests.
|
||||
if (m_clear_requests_count++ == 0)
|
||||
torrent::this_thread::scheduler()->wait_for(&m_task_shutdown_clear_requests, 5s);
|
||||
else
|
||||
torrent::this_thread::scheduler()->wait_for(&m_task_shutdown_clear_requests, 1s);
|
||||
}
|
||||
|
||||
+22
-8
@@ -6,7 +6,7 @@
|
||||
#include <memory>
|
||||
#include <sys/types.h>
|
||||
#include <torrent/torrent.h>
|
||||
#include <torrent/utils/scheduler.h>
|
||||
#include <torrent/system/scheduler.h>
|
||||
|
||||
namespace ui {
|
||||
class Root;
|
||||
@@ -38,23 +38,28 @@ namespace torrent {
|
||||
class directory_events;
|
||||
}
|
||||
|
||||
namespace utils {
|
||||
class WatchReadyQueue;
|
||||
}
|
||||
|
||||
class Control {
|
||||
public:
|
||||
Control();
|
||||
~Control();
|
||||
|
||||
bool is_shutdown_completed();
|
||||
bool is_shutdown_received() { return m_shutdownReceived; }
|
||||
bool is_shutdown_started() { return m_shutdownQuick; }
|
||||
bool is_shutdown_received() { return m_shutdown_received; }
|
||||
bool is_shutdown_started() { return m_shutdown_quick; }
|
||||
|
||||
void initialize();
|
||||
void cleanup();
|
||||
void cleanup_exception();
|
||||
|
||||
void handle_shutdown();
|
||||
void handle_shutdown_clear_requests();
|
||||
|
||||
void receive_normal_shutdown() { m_shutdownReceived = true; }
|
||||
void receive_quick_shutdown() { m_shutdownReceived = true; m_shutdownQuick = true; }
|
||||
void receive_normal_shutdown() { m_shutdown_received = true; }
|
||||
void receive_quick_shutdown() { m_shutdown_received = true; m_shutdown_quick = true; }
|
||||
|
||||
core::Manager* core() { return m_core.get(); }
|
||||
core::ViewManager* view_manager() { return m_view_manager.get(); }
|
||||
@@ -70,6 +75,7 @@ public:
|
||||
rpc::LuaEngine* lua_engine() { return m_lua_engine.get(); }
|
||||
|
||||
torrent::directory_events* directory_events() { return m_directory_events.get(); }
|
||||
utils::WatchReadyQueue* watch_ready_queue() { return m_watch_ready_queue.get(); }
|
||||
|
||||
uint64_t tick() const { return m_tick; }
|
||||
void inc_tick() { m_tick++; }
|
||||
@@ -94,16 +100,24 @@ private:
|
||||
std::unique_ptr<rpc::object_storage> m_objectStorage;
|
||||
std::unique_ptr<rpc::LuaEngine> m_lua_engine;
|
||||
std::unique_ptr<torrent::directory_events> m_directory_events;
|
||||
std::unique_ptr<utils::WatchReadyQueue> m_watch_ready_queue;
|
||||
|
||||
uint64_t m_tick{};
|
||||
|
||||
mode_t m_umask;
|
||||
std::string m_workingDirectory;
|
||||
|
||||
torrent::utils::SchedulerEntry m_task_shutdown;
|
||||
torrent::system::SchedulerEntry m_task_shutdown;
|
||||
torrent::system::SchedulerEntry m_task_shutdown_clear_requests;
|
||||
|
||||
std::atomic<bool> m_shutdownReceived{};
|
||||
std::atomic<bool> m_shutdownQuick{};
|
||||
int m_clear_requests_count{};
|
||||
|
||||
align_cacheline
|
||||
|
||||
std::atomic<bool> m_shutdown_received{};
|
||||
std::atomic<bool> m_shutdown_quick{};
|
||||
};
|
||||
|
||||
extern Control* control;
|
||||
|
||||
#endif
|
||||
|
||||
+54
-12
@@ -4,10 +4,12 @@
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <torrent/exceptions.h>
|
||||
#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>
|
||||
|
||||
@@ -61,7 +63,14 @@ DhtManager::load_dht_cache() {
|
||||
LT_LOG("could not open cache file (path:%s)", cache_filename.c_str());
|
||||
}
|
||||
|
||||
torrent::runtime::network_manager()->dht_controller()->initialize(cache);
|
||||
try {
|
||||
torrent::runtime::network_manager()->dht_controller()->initialize(cache);
|
||||
|
||||
} catch (const torrent::base_error& e) {
|
||||
LT_LOG_ERROR("cache file rejected, discarding (path:%s errmsg:%s)", cache_filename.c_str(), e.what());
|
||||
|
||||
torrent::runtime::network_manager()->dht_controller()->initialize(torrent::Object::create_map());
|
||||
}
|
||||
|
||||
if (m_start == dht_on)
|
||||
start_dht();
|
||||
@@ -131,26 +140,42 @@ DhtManager::save_dht_cache() {
|
||||
torrent::Object cache = torrent::Object::create_map();
|
||||
cache_file << *torrent::runtime::network_manager()->dht_controller()->store_cache(&cache);
|
||||
|
||||
// The data only reaches the kernel here, so this is where a full disk is seen.
|
||||
cache_file.close();
|
||||
|
||||
if (!cache_file.good())
|
||||
return;
|
||||
|
||||
cache_file.close();
|
||||
|
||||
::rename(filename_tmp.c_str(), filename.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
DhtManager::set_mode(const std::string& arg) {
|
||||
int i;
|
||||
for (i = 0; i < dht_settings_num; i++) {
|
||||
if (arg == dht_settings[i]) {
|
||||
m_start = i;
|
||||
break;
|
||||
}
|
||||
DhtManager::set_mode_by_user(const std::string& arg) {
|
||||
unsigned int mode = [arg]() {
|
||||
for (int i = 0; i < dht_settings_num; i++) {
|
||||
if (arg == dht_settings[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
throw torrent::input_error("Invalid dht mode: " + arg);
|
||||
}();
|
||||
|
||||
m_set_by_user = true;
|
||||
|
||||
if (!torrent::runtime::is_network_initialized()) {
|
||||
m_start = mode;
|
||||
return;
|
||||
}
|
||||
|
||||
if (i == dht_settings_num)
|
||||
throw torrent::input_error("Invalid argument.");
|
||||
set_mode_directly(mode);
|
||||
}
|
||||
|
||||
void
|
||||
DhtManager::set_mode_directly(unsigned int mode) {
|
||||
if (mode >= dht_settings_num)
|
||||
throw torrent::input_error("Invalid dht mode.");
|
||||
|
||||
m_start = mode;
|
||||
|
||||
if (m_start == dht_off)
|
||||
stop_dht();
|
||||
@@ -158,6 +183,23 @@ DhtManager::set_mode(const std::string& arg) {
|
||||
start_dht();
|
||||
}
|
||||
|
||||
void
|
||||
DhtManager::set_auto_if_untouched_and_has_session() {
|
||||
if (m_set_by_user) {
|
||||
set_mode_directly(m_start);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rpc::call_command_string("session.path").empty()) {
|
||||
LT_LOG("DHT auto-start disabled, session path not set.", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
set_mode_directly(dht_auto);
|
||||
|
||||
LT_LOG("DHT auto-start enabled.", 0);
|
||||
}
|
||||
|
||||
void
|
||||
DhtManager::update() {
|
||||
if (!torrent::runtime::network_manager()->is_dht_active())
|
||||
|
||||
+15
-11
@@ -2,12 +2,17 @@
|
||||
#define RTORRENT_CORE_DHT_MANAGER_H
|
||||
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/utils/scheduler.h>
|
||||
#include <torrent/system/scheduler.h>
|
||||
|
||||
namespace core {
|
||||
|
||||
class DhtManager {
|
||||
public:
|
||||
static constexpr int dht_disable = 0;
|
||||
static constexpr int dht_off = 1;
|
||||
static constexpr int dht_auto = 2;
|
||||
static constexpr int dht_on = 3;
|
||||
|
||||
~DhtManager();
|
||||
|
||||
void load_dht_cache();
|
||||
@@ -18,16 +23,14 @@ public:
|
||||
void stop_dht();
|
||||
void auto_start() { if (m_start == dht_auto) start_dht(); }
|
||||
|
||||
void set_mode(const std::string& arg);
|
||||
void set_mode_by_user(const std::string& arg);
|
||||
void set_mode_directly(unsigned int mode);
|
||||
|
||||
void set_auto_if_untouched_and_has_session();
|
||||
|
||||
private:
|
||||
static const int dht_disable = 0;
|
||||
static const int dht_off = 1;
|
||||
static const int dht_auto = 2;
|
||||
static const int dht_on = 3;
|
||||
|
||||
static const int dht_settings_num = 4;
|
||||
static const char* dht_settings[dht_settings_num];
|
||||
static constexpr int dht_settings_num = 4;
|
||||
static const char* dht_settings[dht_settings_num];
|
||||
|
||||
void update();
|
||||
bool log_statistics(bool force);
|
||||
@@ -39,10 +42,11 @@ private:
|
||||
uint64_t m_dhtPrevBytesUp;
|
||||
uint64_t m_dhtPrevBytesDown;
|
||||
|
||||
torrent::utils::SchedulerEntry m_update_timeout;
|
||||
torrent::utils::SchedulerEntry m_stop_timeout;
|
||||
torrent::system::SchedulerEntry m_update_timeout;
|
||||
torrent::system::SchedulerEntry m_stop_timeout;
|
||||
|
||||
bool m_warned{};
|
||||
bool m_set_by_user{};
|
||||
int m_start{dht_off};
|
||||
};
|
||||
|
||||
|
||||
+18
-22
@@ -32,21 +32,6 @@ Download::~Download() {
|
||||
m_download = download_type();
|
||||
}
|
||||
|
||||
void
|
||||
Download::enable_udp_trackers(bool state) {
|
||||
for (int idx = 0, end = m_download.tracker_controller().size(); idx < end; ++idx) {
|
||||
auto tracker = m_download.tracker_controller().at(idx);
|
||||
|
||||
if (tracker.type() != torrent::TRACKER_UDP)
|
||||
continue;
|
||||
|
||||
if (state)
|
||||
tracker.enable();
|
||||
else
|
||||
tracker.disable();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Download::priority() {
|
||||
return bencode()->get_key("rtorrent").get_key_value("priority");
|
||||
@@ -103,19 +88,31 @@ Download::distributed_copies() const {
|
||||
}
|
||||
|
||||
void
|
||||
Download::set_throttle_name(const std::string& throttleName) {
|
||||
Download::set_throttle_name(const std::string& name) {
|
||||
if (m_download.info()->is_active())
|
||||
throw torrent::input_error("Cannot set throttle on active download.");
|
||||
|
||||
torrent::ThrottlePair throttles = control->core()->get_throttle(throttleName);
|
||||
auto throttles = control->core()->get_throttle(name);
|
||||
|
||||
m_download.set_upload_throttle(throttles.first);
|
||||
m_download.set_download_throttle(throttles.second);
|
||||
|
||||
m_download.bencode()->get_key("rtorrent").insert_key("throttle_name", throttleName);
|
||||
m_download.bencode()->get_key("rtorrent").insert_key("throttle_name", name);
|
||||
}
|
||||
|
||||
void
|
||||
Download::set_root_directory(const std::string& path) {
|
||||
Download::set_directory(const std::string& path) {
|
||||
if (!m_download.file_list()->is_multi_file())
|
||||
return set_base_directory(path);
|
||||
|
||||
if (path.empty() || *path.rbegin() == '/')
|
||||
return set_base_directory(path + m_download.info()->name_sanitized());
|
||||
|
||||
set_base_directory(path + "/" + m_download.info()->name_sanitized());
|
||||
}
|
||||
|
||||
void
|
||||
Download::set_base_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.
|
||||
//
|
||||
@@ -131,16 +128,15 @@ Download::set_root_directory(const std::string& path) {
|
||||
|
||||
(file_list->is_multi_file() ?
|
||||
!file_list->is_root_dir_created() :
|
||||
!file_stat.update(file_list->front()->frozen_path()))) {
|
||||
!file_stat.update(file_list->front()->frozen_path().str()))) {
|
||||
|
||||
set_message("Cannot change the directory of an open download after the files have been moved.");
|
||||
rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(this));
|
||||
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.");
|
||||
}
|
||||
|
||||
control->core()->download_list()->close_directly(this);
|
||||
control->core()->download_list()->close_files(this);
|
||||
file_list->set_root_dir(expand_path(path));
|
||||
|
||||
bencode()->get_key("rtorrent").insert_key("directory", path);
|
||||
|
||||
+17
-5
@@ -1,6 +1,7 @@
|
||||
#ifndef RTORRENT_CORE_DOWNLOAD_H
|
||||
#define RTORRENT_CORE_DOWNLOAD_H
|
||||
|
||||
#include <memory>
|
||||
#include <torrent/common.h>
|
||||
#include <torrent/download.h>
|
||||
#include <torrent/download_info.h>
|
||||
@@ -22,6 +23,8 @@ public:
|
||||
typedef torrent::ConnectionList connection_list_type;
|
||||
typedef download_type::ConnectionType connection_type;
|
||||
|
||||
static constexpr uint32_t default_resume_flags = ~uint32_t{} & ~torrent::Download::open_enable_fallocate;
|
||||
|
||||
static const int variable_hashing_stopped = 0;
|
||||
static const int variable_hashing_initial = 1;
|
||||
static const int variable_hashing_last = 2;
|
||||
@@ -48,6 +51,14 @@ public:
|
||||
bool is_hash_checking() const { return m_download.is_hash_checking(); }
|
||||
|
||||
bool is_hash_failed() const { return m_hashFailed; }
|
||||
|
||||
// Expires once the download is erased, even if other owners keep the
|
||||
// object alive. Take it before triggering events that may erase.
|
||||
std::weak_ptr<void> lifetime() const { return m_lifetime; }
|
||||
void release_lifetime() { m_lifetime.reset(); }
|
||||
|
||||
bool is_erasing() const { return m_erasing; }
|
||||
void set_erasing() { m_erasing = true; }
|
||||
void set_hash_failed(bool v) { m_hashFailed = v; }
|
||||
|
||||
download_type* download() { return &m_download; }
|
||||
@@ -70,17 +81,16 @@ 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);
|
||||
|
||||
uint32_t resume_flags() { return m_resumeFlags; }
|
||||
void set_resume_flags(uint32_t flags) { m_resumeFlags = flags; }
|
||||
|
||||
void set_root_directory(const std::string& path);
|
||||
void set_directory(const std::string& path);
|
||||
void set_base_directory(const std::string& path);
|
||||
|
||||
void set_throttle_name(const std::string& throttleName);
|
||||
void set_throttle_name(const std::string& name);
|
||||
|
||||
bool operator == (const std::string& str) const;
|
||||
|
||||
@@ -101,8 +111,10 @@ private:
|
||||
// Store the FileList instance so we can use slots etc on it.
|
||||
download_type m_download;
|
||||
bool m_hashFailed{};
|
||||
bool m_erasing{};
|
||||
std::shared_ptr<void> m_lifetime{std::make_shared<char>()};
|
||||
std::string m_message;
|
||||
uint32_t m_resumeFlags{~uint32_t{}};
|
||||
uint32_t m_resumeFlags{default_resume_flags};
|
||||
unsigned int m_group{};
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <sys/stat.h>
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/resume.h>
|
||||
#include <torrent/object.h>
|
||||
@@ -15,6 +16,8 @@
|
||||
#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"
|
||||
@@ -33,8 +36,15 @@ is_network_uri(const std::string& uri) {
|
||||
std::strncmp(uri.c_str(), "ftp://", 6) == 0;
|
||||
}
|
||||
|
||||
static constexpr const char* session_invalid_message = "Session data is invalid, ignoring it";
|
||||
|
||||
static std::unique_ptr<torrent::Object>
|
||||
download_factory_load_stream(const char* filename) {
|
||||
download_factory_load_stream(const char* filename, bool* is_invalid) {
|
||||
struct stat sb;
|
||||
|
||||
if (stat(filename, &sb) != 0 || !S_ISREG(sb.st_mode))
|
||||
return std::unique_ptr<torrent::Object>();
|
||||
|
||||
std::fstream stream(filename, std::ios::in | std::ios::binary);
|
||||
|
||||
if (!stream.is_open())
|
||||
@@ -43,8 +53,10 @@ download_factory_load_stream(const char* filename) {
|
||||
auto obj = std::make_unique<torrent::Object>();
|
||||
stream >> *obj;
|
||||
|
||||
if (!stream.good())
|
||||
if (!stream.good() || !obj->is_map()) {
|
||||
*is_invalid = true;
|
||||
return std::unique_ptr<torrent::Object>();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
@@ -103,40 +115,42 @@ DownloadFactory::receive_load() {
|
||||
throw torrent::internal_error("DownloadFactory::load*() called on an object with m_stream != NULL");
|
||||
|
||||
if (is_network_uri(m_uri)) {
|
||||
// Http handling here.
|
||||
m_stream.reset(new std::stringstream);
|
||||
|
||||
HttpQueue::iterator itr = m_manager->http_queue()->insert(m_uri, m_stream);
|
||||
auto done_fn = [this]() { receive_loaded(); };
|
||||
auto failed_fn = [this](const std::string& error) { receive_failed(error); };
|
||||
|
||||
itr->add_done_slot([this]() { receive_loaded(); });
|
||||
itr->add_failed_slot([this](const std::string& error) { receive_failed(error); });
|
||||
m_manager->http_queue()->insert(m_uri, m_stream, done_fn, failed_fn);
|
||||
|
||||
m_variables["tied_to_file"] = (int64_t)false;
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (is_magnet_uri(m_uri)) {
|
||||
if (is_magnet_uri(m_uri)) {
|
||||
// DEBUG: Use m_object.
|
||||
m_stream.reset(new std::stringstream());
|
||||
*m_stream << "d10:magnet-uri" << m_uri.length() << ":" << m_uri << "e";
|
||||
|
||||
m_variables["tied_to_file"] = (int64_t)false;
|
||||
receive_loaded();
|
||||
|
||||
} else {
|
||||
std::fstream stream(expand_path(m_uri).c_str(), std::ios::in | std::ios::binary);
|
||||
|
||||
if (!stream.is_open())
|
||||
return receive_failed("Could not open file");
|
||||
|
||||
m_object = new torrent::Object;
|
||||
stream >> *m_object;
|
||||
|
||||
if (!stream.good())
|
||||
return receive_failed("Reading torrent file failed");
|
||||
|
||||
m_isFile = true;
|
||||
|
||||
receive_loaded();
|
||||
return;
|
||||
}
|
||||
|
||||
std::fstream stream(expand_path(m_uri).c_str(), std::ios::in | std::ios::binary);
|
||||
|
||||
if (!stream.is_open())
|
||||
return receive_failed("Could not open file");
|
||||
|
||||
m_object = new torrent::Object;
|
||||
stream >> *m_object;
|
||||
|
||||
if (!stream.good())
|
||||
return receive_failed("Reading torrent file failed");
|
||||
|
||||
m_isFile = true;
|
||||
|
||||
receive_loaded();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -157,8 +171,21 @@ 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());
|
||||
bool session_invalid = false;
|
||||
|
||||
std::unique_ptr<torrent::Object> rtorrent_object;
|
||||
std::unique_ptr<torrent::Object> libtorrent_resume_object;
|
||||
|
||||
if (m_session) {
|
||||
if (m_uri.empty())
|
||||
throw torrent::input_error("Session torrent URI is empty.");
|
||||
|
||||
rtorrent_object = download_factory_load_stream((expand_path(m_uri) + ".rtorrent").c_str(), &session_invalid);
|
||||
libtorrent_resume_object = download_factory_load_stream((expand_path(m_uri) + ".libtorrent_resume").c_str(), &session_invalid);
|
||||
|
||||
if (session_invalid)
|
||||
lt_log_print(torrent::LOG_ERROR, "%s: %s", session_invalid_message, m_uri.c_str());
|
||||
}
|
||||
|
||||
uint32_t tracker_key;
|
||||
|
||||
@@ -180,6 +207,14 @@ DownloadFactory::receive_success() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (session_invalid) {
|
||||
download->set_hash_failed(true);
|
||||
download->set_message(session_invalid_message);
|
||||
|
||||
if (m_printLog)
|
||||
m_manager->push_log_std(std::string(session_invalid_message) + ": \"" + m_uri + "\"");
|
||||
}
|
||||
|
||||
torrent::Object* root = download->bencode();
|
||||
|
||||
if (download->download()->info()->is_meta_download()) {
|
||||
@@ -237,9 +272,6 @@ DownloadFactory::receive_success() {
|
||||
rpc::call_command("d.peers_max.set", rpc::call_command("throttle.max_peers.seed"), rpc::make_target(download));
|
||||
}
|
||||
|
||||
if (!rpc::call_command_value("trackers.use_udp"))
|
||||
download->enable_udp_trackers(false);
|
||||
|
||||
// Skip forcing trackers to scrape when rtorrent starts
|
||||
if (m_initLoad && rpc::call_command_value("trackers.delay_scrape"))
|
||||
download->set_resume_flags(torrent::Download::start_skip_tracker);
|
||||
@@ -271,11 +303,24 @@ DownloadFactory::receive_success() {
|
||||
if (!m_session && m_variables["tied_to_file"].as_value())
|
||||
rpc::call_command("d.tied_to_file.set", m_uri.empty() ? m_variables["tied_file"] : m_uri, rpc::make_target(download));
|
||||
|
||||
rpc::call_command("d.peer_exchange.set", rpc::call_command_value("protocol.pex"), rpc::make_target(download));
|
||||
rpc::call_command("d.peer_exchange.set", torrent::runtime::client_config()->is_pex_enabled(), rpc::make_target(download));
|
||||
|
||||
torrent::resume_load_addresses(*download->download(), resumeObject);
|
||||
torrent::resume_load_file_priorities(*download->download(), resumeObject);
|
||||
torrent::resume_load_tracker_settings(*download->download(), resumeObject);
|
||||
try {
|
||||
torrent::resume_load_addresses(*download->download(), resumeObject);
|
||||
torrent::resume_load_file_priorities(*download->download(), resumeObject);
|
||||
torrent::resume_load_tracker_settings(*download->download(), resumeObject);
|
||||
|
||||
} catch (const torrent::input_error& e) {
|
||||
std::string msg = std::string(session_invalid_message) + ": " + e.what();
|
||||
|
||||
lt_log_print(torrent::LOG_ERROR, "%s: %s", msg.c_str(), m_uri.c_str());
|
||||
|
||||
if (m_printLog)
|
||||
m_manager->push_log_std(msg + ": \"" + m_uri + "\"");
|
||||
|
||||
download->set_hash_failed(true);
|
||||
download->set_message(msg);
|
||||
}
|
||||
|
||||
// The action of inserting might cause the torrent to be
|
||||
// opened/started or such. Figure out a nicer way of handling this.
|
||||
@@ -327,7 +372,7 @@ void
|
||||
DownloadFactory::log_created(Download* download, torrent::Object* rtorrent) {
|
||||
std::stringstream dump;
|
||||
|
||||
dump << "info_hash = " << torrent::hash_string_to_hex_str(download->info()->hash()) << std::endl;
|
||||
dump << "info_hash = " << torrent::utils::transform_to_hex_str(download->info()->hash()) << std::endl;
|
||||
dump << "session = " << (m_session ? "true" : "false") << std::endl;
|
||||
|
||||
if (download->download()->info()->is_meta_download())
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <iosfwd>
|
||||
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/utils/scheduler.h>
|
||||
#include <torrent/system/scheduler.h>
|
||||
|
||||
#include "http_queue.h"
|
||||
|
||||
@@ -79,9 +79,9 @@ private:
|
||||
command_list_type m_commands;
|
||||
torrent::Object::map_type m_variables;
|
||||
|
||||
slot_void m_slot_finished;
|
||||
torrent::utils::SchedulerEntry m_task_load;
|
||||
torrent::utils::SchedulerEntry m_task_commit;
|
||||
slot_void m_slot_finished;
|
||||
torrent::system::SchedulerEntry m_task_load;
|
||||
torrent::system::SchedulerEntry m_task_commit;
|
||||
};
|
||||
|
||||
bool is_network_uri(const std::string& uri);
|
||||
|
||||
+139
-41
@@ -1,9 +1,9 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <rak/string_manip.h>
|
||||
#include <torrent/data/file.h>
|
||||
#include <torrent/utils/resume.h>
|
||||
#include <torrent/exceptions.h>
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <torrent/object_stream.h>
|
||||
#include <torrent/torrent.h>
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/string_manip.h>
|
||||
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
@@ -36,7 +37,7 @@ namespace core {
|
||||
inline void
|
||||
DownloadList::check_contains([[maybe_unused]] Download* d) {
|
||||
#ifdef USE_EXTRA_DEBUG
|
||||
if (std::find(begin(), end(), d) == end())
|
||||
if (std::find_if(begin(), end(), [d](const auto& entry) { return entry.get() == d; }) == end())
|
||||
throw torrent::internal_error("DownloadList::check_contains(...) failed.");
|
||||
#endif
|
||||
}
|
||||
@@ -53,7 +54,6 @@ DownloadList::clear() {
|
||||
base_type::pop_back();
|
||||
|
||||
torrent::download_remove(*download->download());
|
||||
delete download;
|
||||
|
||||
} catch (torrent::internal_error& e) {
|
||||
lt_log_print(torrent::LOG_ERROR, "DownloadList::clear() failed to close or remove download: %s", e.what());
|
||||
@@ -69,7 +69,7 @@ DownloadList::clear() {
|
||||
void
|
||||
DownloadList::session_save() {
|
||||
for (auto& download : *this)
|
||||
session_thread::manager()->save_resume_download(download);
|
||||
session_thread::manager()->save_resume_download(download.get());
|
||||
|
||||
control->dht_manager()->save_dht_cache();
|
||||
control->ui()->save_input_history();
|
||||
@@ -77,24 +77,27 @@ DownloadList::session_save() {
|
||||
|
||||
DownloadList::iterator
|
||||
DownloadList::find(const torrent::HashString& hash) {
|
||||
return std::find_if(begin(), end(), [hash](Download* d) { return hash == d->info()->hash(); });
|
||||
return std::find_if(begin(), end(), [hash](const auto& d) { return hash == d->info()->hash(); });
|
||||
}
|
||||
|
||||
DownloadList::iterator
|
||||
DownloadList::find_hex(const char* hash) {
|
||||
if (strlen(hash) != 40)
|
||||
return end();
|
||||
|
||||
torrent::HashString key;
|
||||
|
||||
for (torrent::HashString::iterator itr = key.begin(), last = key.end(); itr != last; itr++, hash += 2)
|
||||
*itr = (rak::hexchar_to_value(*hash) << 4) + rak::hexchar_to_value(*(hash + 1));
|
||||
if (torrent::utils::transform_from_hex(hash, hash + 40, key) != key.end())
|
||||
return end();
|
||||
|
||||
return std::find_if(begin(), end(), [key](Download* d) { return key == d->info()->hash(); });
|
||||
return std::find_if(begin(), end(), [key](const auto& d) { return key == d->info()->hash(); });
|
||||
}
|
||||
|
||||
Download*
|
||||
DownloadList::find_hex_ptr(const char* hash) {
|
||||
iterator itr = find_hex(hash);
|
||||
|
||||
return itr != end() ? *itr : NULL;
|
||||
return itr != end() ? itr->get() : NULL;
|
||||
}
|
||||
|
||||
Download*
|
||||
@@ -155,7 +158,7 @@ DownloadList::create(std::istream* str, uint32_t tracker_key, bool printLog) {
|
||||
|
||||
DownloadList::iterator
|
||||
DownloadList::insert(Download* download) {
|
||||
iterator itr = base_type::insert(end(), download);
|
||||
iterator itr = base_type::insert(end(), std::shared_ptr<Download>(download));
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Inserting download.");
|
||||
|
||||
@@ -166,7 +169,7 @@ 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);
|
||||
v->insert(*itr);
|
||||
for (auto v : *control->view_manager())
|
||||
v->filter_download(download);
|
||||
|
||||
@@ -183,7 +186,7 @@ DownloadList::insert(Download* download) {
|
||||
|
||||
void
|
||||
DownloadList::erase_ptr(Download* download) {
|
||||
erase(std::find(begin(), end(), download));
|
||||
erase(std::find_if(begin(), end(), [download](const auto& entry) { return entry.get() == download; }));
|
||||
}
|
||||
|
||||
DownloadList::iterator
|
||||
@@ -191,21 +194,27 @@ DownloadList::erase(iterator itr) {
|
||||
if (itr == end())
|
||||
throw torrent::internal_error("DownloadList::erase(...) could not find download.");
|
||||
|
||||
// An event handler below may erase the same download again.
|
||||
if ((*itr)->is_erasing())
|
||||
return std::next(itr);
|
||||
|
||||
(*itr)->set_erasing();
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, (*itr)->info(), "download_list", "Erasing download.");
|
||||
|
||||
// Makes sure close doesn't restart hashing of this download.
|
||||
(*itr)->set_hash_failed(true);
|
||||
|
||||
close(*itr);
|
||||
session_thread::manager()->remove_download(*itr);
|
||||
session_thread::manager()->remove_download(itr->get());
|
||||
|
||||
DL_TRIGGER_EVENT(*itr, "event.download.erased");
|
||||
|
||||
for (auto v : *control->view_manager())
|
||||
v->erase(*itr);
|
||||
v->erase(itr->get());
|
||||
|
||||
(*itr)->release_lifetime();
|
||||
torrent::download_remove(*(*itr)->download());
|
||||
delete *itr;
|
||||
|
||||
return base_type::erase(itr);
|
||||
}
|
||||
@@ -252,9 +261,11 @@ DownloadList::close(Download* download) {
|
||||
}
|
||||
}
|
||||
|
||||
// Releases the files without changing the download's state, for callers that
|
||||
// need the files closed and will keep using the download.
|
||||
void
|
||||
DownloadList::close_directly(Download* download) {
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download directly.");
|
||||
DownloadList::close_files(Download* download) {
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download files.");
|
||||
|
||||
if (download->download()->info()->is_active()) {
|
||||
download->download()->stop(torrent::Download::stop_skip_tracker);
|
||||
@@ -267,6 +278,53 @@ DownloadList::close_directly(Download* download) {
|
||||
download->download()->close();
|
||||
}
|
||||
|
||||
void
|
||||
DownloadList::close_directly(Download* download) {
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download directly.");
|
||||
|
||||
auto lifetime = download->lifetime();
|
||||
bool was_active = download->download()->info()->is_active();
|
||||
bool was_open = download->download()->info()->is_open();
|
||||
|
||||
close_files(download);
|
||||
set_state_stopped(download);
|
||||
|
||||
if (was_active) {
|
||||
DL_TRIGGER_EVENT(download, "event.download.paused");
|
||||
|
||||
if (lifetime.expired())
|
||||
return;
|
||||
|
||||
update_paused_state(download);
|
||||
}
|
||||
|
||||
if (was_open) {
|
||||
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
|
||||
|
||||
if (lifetime.expired())
|
||||
return;
|
||||
|
||||
DL_TRIGGER_EVENT(download, "event.download.closed");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DownloadList::set_state_stopped(Download* download) {
|
||||
control->view_manager()->find_ptr_throw("stopped")->set_visible(download);
|
||||
rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(download));
|
||||
}
|
||||
|
||||
void
|
||||
DownloadList::update_paused_state(Download* download) {
|
||||
rpc::call_command("d.state_changed.set", torrent::this_thread::cached_seconds().count(), rpc::make_target(download));
|
||||
rpc::call_command("d.state_counter.set", rpc::call_command_value("d.state_counter", rpc::make_target(download)) + 1, rpc::make_target(download));
|
||||
|
||||
// If initial seeding is complete, don't try it again when restarting.
|
||||
if (download->is_done() &&
|
||||
rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)).as_string() == "initial_seed")
|
||||
rpc::call_command("d.connection_seed.set", rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)), rpc::make_target(download));
|
||||
}
|
||||
|
||||
void
|
||||
DownloadList::close_quick(Download* download) {
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download quickly.");
|
||||
@@ -288,8 +346,13 @@ DownloadList::close_throw(Download* download) {
|
||||
// When pause gets called it will clear the initial hash check state
|
||||
// and set hash failed. This should ensure hashing doesn't restart
|
||||
// until resume gets called.
|
||||
auto lifetime = download->lifetime();
|
||||
|
||||
pause(download);
|
||||
|
||||
if (lifetime.expired())
|
||||
return;
|
||||
|
||||
// Check for is_open after pause due to hashing.
|
||||
if (!download->is_open())
|
||||
return;
|
||||
@@ -309,6 +372,10 @@ DownloadList::close_throw(Download* download) {
|
||||
throw torrent::internal_error("DownloadList::close_throw(...) called but we're going into a hashing loop.");
|
||||
|
||||
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
|
||||
|
||||
if (lifetime.expired())
|
||||
return;
|
||||
|
||||
DL_TRIGGER_EVENT(download, "event.download.closed");
|
||||
}
|
||||
|
||||
@@ -327,7 +394,7 @@ DownloadList::resume(Download* download, int flags) {
|
||||
|
||||
// We need to make sure the flags aren't reset if someone decideds
|
||||
// to call resume() while it is hashing, etc.
|
||||
if (download->resume_flags() == ~uint32_t())
|
||||
if (download->resume_flags() == Download::default_resume_flags)
|
||||
download->set_resume_flags(flags);
|
||||
|
||||
// Manual or end-of-download rehashing clears the resume data so
|
||||
@@ -400,7 +467,7 @@ DownloadList::resume(Download* download, int flags) {
|
||||
download->set_priority(download->priority());
|
||||
download->download()->start(download->resume_flags());
|
||||
|
||||
download->set_resume_flags(~uint32_t());
|
||||
download->set_resume_flags(Download::default_resume_flags);
|
||||
|
||||
DL_TRIGGER_EVENT(download, "event.download.resumed");
|
||||
|
||||
@@ -415,9 +482,11 @@ DownloadList::pause(Download* download, int flags) {
|
||||
|
||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Pausing download: flags:%0x.", flags);
|
||||
|
||||
auto lifetime = download->lifetime();
|
||||
|
||||
try {
|
||||
|
||||
download->set_resume_flags(~uint32_t());
|
||||
download->set_resume_flags(Download::default_resume_flags);
|
||||
|
||||
rpc::parse_command_single(rpc::make_target(download), "view.set_not_visible=active");
|
||||
|
||||
@@ -428,6 +497,9 @@ DownloadList::pause(Download* download, int flags) {
|
||||
rpc::call_command_set_value("d.hashing.set", Download::variable_hashing_stopped, rpc::make_target(download));
|
||||
|
||||
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
|
||||
|
||||
if (lifetime.expired())
|
||||
return;
|
||||
}
|
||||
|
||||
if (!download->download()->info()->is_active())
|
||||
@@ -441,15 +513,10 @@ DownloadList::pause(Download* download, int flags) {
|
||||
// view.
|
||||
DL_TRIGGER_EVENT(download, "event.download.paused");
|
||||
|
||||
auto cached_seconds = torrent::this_thread::cached_seconds().count();
|
||||
if (lifetime.expired())
|
||||
return;
|
||||
|
||||
rpc::call_command("d.state_changed.set", cached_seconds, rpc::make_target(download));
|
||||
rpc::call_command("d.state_counter.set", rpc::call_command_value("d.state_counter", rpc::make_target(download)), rpc::make_target(download));
|
||||
|
||||
// If initial seeding is complete, don't try it again when restarting.
|
||||
if (download->is_done() &&
|
||||
rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)).as_string() == "initial_seed")
|
||||
rpc::call_command("d.connection_seed.set", rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)), rpc::make_target(download));
|
||||
update_paused_state(download);
|
||||
|
||||
// Save the state after all the slots, etc have been called so we
|
||||
// include the modifications they may make.
|
||||
@@ -488,7 +555,11 @@ DownloadList::hash_done(Download* download) {
|
||||
|
||||
if (!download->is_hash_checked()) {
|
||||
download->set_hash_failed(true);
|
||||
|
||||
|
||||
auto& msg = download->download()->hash_error_message();
|
||||
if (!msg.empty())
|
||||
download->set_message(msg);
|
||||
|
||||
DL_TRIGGER_EVENT(download, "event.download.hash_failed");
|
||||
return;
|
||||
}
|
||||
@@ -516,7 +587,7 @@ DownloadList::hash_done(Download* download) {
|
||||
// If the download was previously completed but the files were
|
||||
// f.ex deleted, then we clear the state and complete.
|
||||
if (rpc::call_command_value("d.complete", rpc::make_target(download)) && !download->is_done()) {
|
||||
rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(download));
|
||||
set_state_stopped(download);
|
||||
download->set_message("Download registered as completed, but hash check returned unfinished chunks.");
|
||||
}
|
||||
|
||||
@@ -525,9 +596,15 @@ DownloadList::hash_done(Download* download) {
|
||||
rpc::call_command("d.complete.set", (int64_t)download->is_done(), rpc::make_target(download));
|
||||
torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
|
||||
|
||||
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 1)
|
||||
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 1) {
|
||||
auto lifetime = download->lifetime();
|
||||
|
||||
resume(download, download->resume_flags());
|
||||
|
||||
if (lifetime.expired())
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Download::variable_hashing_last:
|
||||
@@ -564,11 +641,24 @@ DownloadList::hash_queue(Download* download, int type) {
|
||||
|
||||
// HACK
|
||||
if (download->is_open()) {
|
||||
auto lifetime = download->lifetime();
|
||||
|
||||
pause(download, torrent::Download::stop_skip_tracker);
|
||||
|
||||
if (lifetime.expired())
|
||||
return;
|
||||
|
||||
download->download()->close();
|
||||
|
||||
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
|
||||
|
||||
if (lifetime.expired())
|
||||
return;
|
||||
|
||||
DL_TRIGGER_EVENT(download, "event.download.closed");
|
||||
|
||||
if (lifetime.expired())
|
||||
return;
|
||||
}
|
||||
|
||||
torrent::resume_clear_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
|
||||
@@ -619,6 +709,10 @@ DownloadList::confirm_finished(Download* download) {
|
||||
if (choke_up.is_string_empty()) choke_up = rpc::call_command("protocol.choke_heuristics.up.seed", torrent::Object(), rpc::make_target(download));
|
||||
if (choke_down.is_string_empty()) choke_down = rpc::call_command("protocol.choke_heuristics.down.seed", torrent::Object(), rpc::make_target(download));
|
||||
|
||||
// libtorrent refuses initial seeding on an active download.
|
||||
if (download->is_active() && conn_current.as_string() == "initial_seed")
|
||||
conn_current = "seed";
|
||||
|
||||
rpc::call_command("d.connection_current.set", conn_current, rpc::make_target(download));
|
||||
rpc::call_command("d.up.choke_heuristics.set", choke_up, rpc::make_target(download));
|
||||
rpc::call_command("d.down.choke_heuristics.set", choke_down, rpc::make_target(download));
|
||||
@@ -644,16 +738,16 @@ DownloadList::confirm_finished(Download* download) {
|
||||
// up/downloaded baseline.
|
||||
download->download()->send_completed();
|
||||
|
||||
// Save the hash in case the finished event erases it.
|
||||
torrent::HashString infohash = download->info()->hash();
|
||||
// The finished event may erase the download.
|
||||
auto lifetime = download->lifetime();
|
||||
|
||||
DL_TRIGGER_EVENT(download, "event.download.finished");
|
||||
|
||||
if (find(infohash) == end())
|
||||
if (lifetime.expired())
|
||||
return;
|
||||
|
||||
// if (download->resume_flags() != ~uint32_t())
|
||||
// throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != ~uint32_t().");
|
||||
// if (download->resume_flags() != Download::default_resume_flags)
|
||||
// throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != Download::default_resume_flags.");
|
||||
|
||||
// See #1292.
|
||||
//
|
||||
@@ -663,7 +757,7 @@ DownloadList::confirm_finished(Download* download) {
|
||||
//
|
||||
// TODO: Add a check when setting the flags to see if the torrent is
|
||||
// being hashed.
|
||||
download->set_resume_flags(~uint32_t());
|
||||
download->set_resume_flags(Download::default_resume_flags);
|
||||
|
||||
if (!download->is_active() && rpc::call_command_value("d.state", rpc::make_target(download)) == 1)
|
||||
resume(download,
|
||||
@@ -679,20 +773,23 @@ DownloadList::process_meta_download(Download* download) {
|
||||
rpc::call_command("d.stop", torrent::Object(), rpc::make_target(download));
|
||||
rpc::call_command("d.close", torrent::Object(), rpc::make_target(download));
|
||||
|
||||
std::string metafile = (*download->file_list()->begin())->frozen_path();
|
||||
std::string metafile = (*download->file_list()->begin())->frozen_path().str();
|
||||
|
||||
std::fstream file(metafile.c_str(), std::ios::in | std::ios::binary);
|
||||
|
||||
if (!file.is_open()) {
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not read download metadata.");
|
||||
return;
|
||||
}
|
||||
|
||||
torrent::Object* bencode = new torrent::Object(torrent::Object::create_map());
|
||||
auto bencode = std::make_unique<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.
|
||||
@@ -703,7 +800,8 @@ 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);
|
||||
|
||||
control->core()->try_create_download_from_meta_download(std::move(bencode), metafile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <iosfwd>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace torrent {
|
||||
@@ -20,9 +21,9 @@ class Download;
|
||||
//
|
||||
// Fix apply_on_ratio if the base_type is changed.
|
||||
|
||||
class DownloadList : private std::list<Download*> {
|
||||
class DownloadList : private std::list<std::shared_ptr<Download>> {
|
||||
public:
|
||||
typedef std::list<Download*> base_type;
|
||||
typedef std::list<std::shared_ptr<Download>> base_type;
|
||||
|
||||
using base_type::iterator;
|
||||
using base_type::const_iterator;
|
||||
@@ -64,8 +65,17 @@ public:
|
||||
bool open(Download* d);
|
||||
void open_throw(Download* d);
|
||||
|
||||
// Overloads for the list's own entries, so callers iterating it do not need
|
||||
// to unwrap.
|
||||
bool open(const value_type& d) { return open(d.get()); }
|
||||
void close(const value_type& d) { close(d.get()); }
|
||||
void close_quick(const value_type& d) { close_quick(d.get()); }
|
||||
void pause(const value_type& d) { pause(d.get()); }
|
||||
void resume(const value_type& d) { resume(d.get()); }
|
||||
|
||||
void close(Download* d);
|
||||
void close_directly(Download* d);
|
||||
void close_files(Download* d);
|
||||
void close_quick(Download* d);
|
||||
void close_throw(Download* d);
|
||||
|
||||
@@ -123,6 +133,9 @@ private:
|
||||
void hash_done(Download* d);
|
||||
void hash_queue(Download* d, int type);
|
||||
|
||||
void set_state_stopped(Download* d);
|
||||
void update_paused_state(Download* d);
|
||||
|
||||
inline void check_contains(Download* d);
|
||||
|
||||
void received_finished(Download* d);
|
||||
|
||||
@@ -9,17 +9,21 @@
|
||||
namespace core {
|
||||
|
||||
HttpQueue::iterator
|
||||
HttpQueue::insert(const std::string& url, std::shared_ptr<std::ostream> stream) {
|
||||
HttpQueue::insert(const std::string& url, std::shared_ptr<std::ostream> stream,
|
||||
std::function<void()> done_fn, std::function<void(const std::string&)> failed_fn) {
|
||||
auto itr = base_type::insert(end(), torrent::net::HttpGet(url, stream));
|
||||
|
||||
itr->set_max_file_size(15 << 20);
|
||||
itr->set_redirect_only_http_https();
|
||||
|
||||
for (auto& slot : m_signal_insert)
|
||||
slot(*itr);
|
||||
|
||||
itr->add_done_slot([this, itr]() { erase(itr); });
|
||||
itr->add_failed_slot([this, itr](auto) { erase(itr); });
|
||||
itr->add_done_slot(torrent::this_thread::thread(), std::move(done_fn));
|
||||
itr->add_done_slot(torrent::this_thread::thread(), [this, itr]() { erase(itr); });
|
||||
|
||||
// TODO: Downloading http torrents doesn't seem to work.
|
||||
// TODO: Quitting no longer works.
|
||||
itr->add_failed_slot(torrent::this_thread::thread(), std::move(failed_fn));
|
||||
itr->add_failed_slot(torrent::this_thread::thread(), [this, itr](auto) { erase(itr); });
|
||||
|
||||
torrent::net_thread::http_stack()->start_get(*itr);
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ public:
|
||||
//
|
||||
// Consider adding a flag to indicate whetever HttpQueue should
|
||||
// delete the stream.
|
||||
iterator insert(const std::string& url, std::shared_ptr<std::ostream> stream);
|
||||
iterator insert(const std::string& url, std::shared_ptr<std::ostream> stream,
|
||||
std::function<void()> done_fn, std::function<void(const std::string&)> failed_fn);
|
||||
void erase(iterator itr);
|
||||
|
||||
void clear();
|
||||
|
||||
+101
-107
@@ -8,21 +8,18 @@
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include <rak/address_info.h>
|
||||
#include <rak/regex.h>
|
||||
#include <rak/string_manip.h>
|
||||
#include <fnmatch.h>
|
||||
#include <torrent/utils/resume.h>
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/connection_manager.h>
|
||||
#include <torrent/error.h>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/object_stream.h>
|
||||
#include <torrent/throttle.h>
|
||||
#include <torrent/net/http_stack.h>
|
||||
#include <torrent/net/network_config.h>
|
||||
#include <torrent/net/socket_address.h>
|
||||
#include <torrent/runtime/network_config.h>
|
||||
#include <torrent/runtime/network_manager.h>
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/string_manip.h>
|
||||
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "utils/directory.h"
|
||||
@@ -36,6 +33,8 @@
|
||||
#include "core/http_queue.h"
|
||||
#include "core/view.h"
|
||||
|
||||
#include <torrent/runtime/client_config.h>
|
||||
|
||||
namespace core {
|
||||
|
||||
const int Manager::create_start;
|
||||
@@ -66,6 +65,20 @@ Manager::~Manager() {
|
||||
torrent::Throttle::destroy_throttle(m_throttles["NULL"].first);
|
||||
}
|
||||
|
||||
bool
|
||||
Manager::is_download_shutdown_completed() {
|
||||
for (const auto& download : *download_list()) {
|
||||
// TODO: Why is this being checked?
|
||||
// if (download->tracker_controller().is_active())
|
||||
// return false;
|
||||
|
||||
if (download->tracker_controller().has_active_trackers_not_dht_scrape_disownable())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Manager::set_hashing_view(View* v) {
|
||||
if (v == nullptr || m_hashingView != nullptr)
|
||||
@@ -75,10 +88,10 @@ Manager::set_hashing_view(View* v) {
|
||||
m_hashingView->signal_changed().push_back(std::bind(&Manager::receive_hashing_changed, this));
|
||||
}
|
||||
|
||||
torrent::ThrottlePair
|
||||
ThrottlePair
|
||||
Manager::get_throttle(const std::string& name) {
|
||||
ThrottleMap::const_iterator itr = m_throttles.find(name);
|
||||
torrent::ThrottlePair throttles = (itr == m_throttles.end() ? torrent::ThrottlePair(nullptr, nullptr) : itr->second);
|
||||
auto itr = m_throttles.find(name);
|
||||
auto throttles = (itr == m_throttles.end() ? ThrottlePair(nullptr, nullptr) : itr->second);
|
||||
|
||||
if (throttles.first == nullptr)
|
||||
throttles.first = torrent::up_throttle_global();
|
||||
@@ -89,20 +102,6 @@ Manager::get_throttle(const std::string& name) {
|
||||
return throttles;
|
||||
}
|
||||
|
||||
void
|
||||
Manager::set_address_throttle(uint32_t begin, uint32_t end, torrent::ThrottlePair throttles) {
|
||||
m_addressThrottles.set_merge(begin, end, throttles);
|
||||
torrent::connection_manager()->address_throttle() = std::bind(&core::Manager::get_address_throttle, control->core(), std::placeholders::_1);
|
||||
}
|
||||
|
||||
torrent::ThrottlePair
|
||||
Manager::get_address_throttle(const sockaddr* addr) {
|
||||
if (addr->sa_family != AF_INET)
|
||||
return torrent::ThrottlePair(nullptr, nullptr);
|
||||
|
||||
return m_addressThrottles.get(ntohl(reinterpret_cast<const sockaddr_in*>(addr)->sin_addr.s_addr), torrent::ThrottlePair(nullptr, nullptr));
|
||||
}
|
||||
|
||||
int64_t
|
||||
Manager::retrieve_throttle_value(const torrent::Object::string_type& name, bool rate, bool up) {
|
||||
ThrottleMap::iterator itr = throttles().find(name);
|
||||
@@ -154,78 +153,12 @@ Manager::cleanup() {
|
||||
|
||||
void
|
||||
Manager::shutdown(bool force) {
|
||||
if (!force)
|
||||
for (auto d : *m_download_list)
|
||||
m_download_list->pause_default(d);
|
||||
else
|
||||
for (auto d : *m_download_list)
|
||||
m_download_list->close_quick(d);
|
||||
}
|
||||
|
||||
void
|
||||
Manager::listen_open() {
|
||||
// This stuff really should be moved outside of manager, make it
|
||||
// part of the init script.
|
||||
if (!rpc::call_command_value("network.port_open"))
|
||||
return;
|
||||
|
||||
int portFirst, portLast;
|
||||
torrent::Object portRange = rpc::call_command("network.port_range");
|
||||
|
||||
if (!portRange.is_string())
|
||||
throw torrent::input_error("Invalid port_range argument type.");
|
||||
|
||||
if (std::sscanf(portRange.as_string().c_str(), "%i-%i", &portFirst, &portLast) != 2)
|
||||
throw torrent::input_error("Invalid port_range argument.");
|
||||
|
||||
if (portFirst > portLast || portLast >= (1 << 16))
|
||||
throw torrent::input_error("Invalid port range.");
|
||||
|
||||
if (rpc::call_command_value("network.port_random")) {
|
||||
int boundary = portFirst + random() % (portLast - portFirst + 1);
|
||||
|
||||
if (torrent::runtime::network_manager()->listen_open(boundary, portLast) ||
|
||||
torrent::runtime::network_manager()->listen_open(portFirst, boundary))
|
||||
return;
|
||||
|
||||
if (!force) {
|
||||
for (const auto& d : *m_download_list)
|
||||
m_download_list->pause_default(d.get());
|
||||
} else {
|
||||
if (torrent::runtime::network_manager()->listen_open(portFirst, portLast))
|
||||
return;
|
||||
}
|
||||
|
||||
throw torrent::input_error("Could not open/bind port for listening: " + std::string(std::strerror(errno)));
|
||||
}
|
||||
|
||||
void
|
||||
Manager::set_proxy_address(const std::string& addr) {
|
||||
int port;
|
||||
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 {
|
||||
|
||||
auto sa = torrent::sa_copy(ai->c_addrinfo()->ai_addr);
|
||||
torrent::sap_set_port(sa, port);
|
||||
|
||||
torrent::config::network_config()->set_proxy_address(sa.get());
|
||||
|
||||
rak::address_info::free_address_info(ai);
|
||||
|
||||
} catch (torrent::input_error& e) {
|
||||
rak::address_info::free_address_info(ai);
|
||||
throw e;
|
||||
for (const auto& d : *m_download_list)
|
||||
m_download_list->close_quick(d);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,9 +174,10 @@ is_data_uri(const std::string& uri) {
|
||||
|
||||
std::string
|
||||
decode_data_uri(const std::string& uri) {
|
||||
const auto start = uri.find("base64,", 5) + 7;
|
||||
if (start == std::string::npos)
|
||||
const auto pos = uri.find("base64,", 5);
|
||||
if (pos == std::string::npos)
|
||||
throw torrent::input_error("Invalid data uri: not base64 encoded.");
|
||||
const auto start = pos + 7;
|
||||
if (start >= uri.size())
|
||||
throw torrent::input_error("Empty base64.");
|
||||
return utils::decode_base64(uri.substr(start));
|
||||
@@ -261,14 +195,14 @@ Manager::try_create_download(const std::string& uri, int flags, const command_li
|
||||
return;
|
||||
|
||||
// Adding download.
|
||||
DownloadFactory* f = new DownloadFactory(this);
|
||||
auto f = std::make_unique<DownloadFactory>(this);
|
||||
|
||||
f->variables()["tied_to_file"] = (int64_t)(bool)(flags & create_tied);
|
||||
f->commands().insert(f->commands().end(), commands.begin(), commands.end());
|
||||
|
||||
f->set_start(flags & create_start);
|
||||
f->set_print_log(!(flags & create_quiet));
|
||||
f->slot_finished([f]() { delete f; });
|
||||
f->slot_finished([factory = f.get()]() { delete factory; });
|
||||
|
||||
if (is_data_uri(uri)) {
|
||||
// Allow the use of data URIs, primarily for JSON-RPC which
|
||||
@@ -282,10 +216,13 @@ Manager::try_create_download(const std::string& uri, int flags, const command_li
|
||||
}
|
||||
|
||||
f->commit();
|
||||
|
||||
// From here the finished slot deletes it.
|
||||
f.release();
|
||||
}
|
||||
|
||||
void
|
||||
Manager::try_create_download_from_meta_download(torrent::Object* bencode, const std::string& metafile) {
|
||||
Manager::try_create_download_from_meta_download(std::unique_ptr<torrent::Object> bencode, const std::string& metafile) {
|
||||
DownloadFactory* f = new DownloadFactory(this);
|
||||
|
||||
f->variables()["tied_to_file"] = (int64_t)true;
|
||||
@@ -314,14 +251,70 @@ path_expand_transform(std::string path, const utils::directory_entry& entry) {
|
||||
return path + entry.s_name;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Consider rewritting such that m_seq is replaced by first/last.
|
||||
template <typename Sequence>
|
||||
class split_iterator_t {
|
||||
public:
|
||||
typedef typename Sequence::const_iterator const_iterator;
|
||||
typedef typename Sequence::value_type value_type;
|
||||
|
||||
split_iterator_t() {}
|
||||
|
||||
split_iterator_t(const Sequence& seq, value_type delim) :
|
||||
m_seq(&seq),
|
||||
m_delim(delim),
|
||||
m_pos(seq.begin()),
|
||||
m_next(std::find(seq.begin(), seq.end(), delim)) {
|
||||
}
|
||||
|
||||
Sequence operator * () { return Sequence(m_pos, m_next); }
|
||||
|
||||
split_iterator_t& operator ++ () {
|
||||
m_pos = m_next;
|
||||
|
||||
if (m_pos == m_seq->end())
|
||||
return *this;
|
||||
|
||||
m_pos++;
|
||||
m_next = std::find(m_pos, m_seq->end(), m_delim);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator == (const split_iterator_t&) const { return m_pos == m_seq->end(); }
|
||||
bool operator != (const split_iterator_t&) const { return m_pos != m_seq->end(); }
|
||||
|
||||
private:
|
||||
const Sequence* m_seq;
|
||||
value_type m_delim;
|
||||
const_iterator m_pos;
|
||||
const_iterator m_next;
|
||||
};
|
||||
|
||||
template <typename Sequence>
|
||||
inline split_iterator_t<Sequence>
|
||||
split_iterator(const Sequence& seq, typename Sequence::value_type delim) {
|
||||
return split_iterator_t<Sequence>(seq, delim);
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
inline split_iterator_t<Sequence>
|
||||
split_iterator(const Sequence&) {
|
||||
return split_iterator_t<Sequence>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Move this somewhere better.
|
||||
void
|
||||
path_expand(std::vector<std::string>* paths, const std::string& pattern) {
|
||||
std::vector<utils::Directory> currentCache;
|
||||
std::vector<utils::Directory> nextCache;
|
||||
|
||||
rak::split_iterator_t<std::string> first = rak::split_iterator(pattern, '/');
|
||||
rak::split_iterator_t<std::string> last = rak::split_iterator(pattern);
|
||||
auto first = split_iterator(pattern, '/');
|
||||
auto last = split_iterator(pattern);
|
||||
|
||||
if (first == last)
|
||||
return;
|
||||
@@ -330,7 +323,7 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
|
||||
if ((*first).empty()) {
|
||||
currentCache.push_back(utils::Directory("/"));
|
||||
++first;
|
||||
} else if (rak::trim(*first) == "~") {
|
||||
} else if (torrent::utils::trim_spaces_str(*first) == "~") {
|
||||
currentCache.push_back(utils::Directory("~"));
|
||||
++first;
|
||||
} else {
|
||||
@@ -340,9 +333,9 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
|
||||
// Might be an idea to use depth-first search instead.
|
||||
|
||||
for (; first != last; ++first) {
|
||||
rak::regex r(*first);
|
||||
const std::string& pattern = *first;
|
||||
|
||||
if (r.pattern().empty())
|
||||
if (pattern.empty())
|
||||
continue;
|
||||
|
||||
// Special case for ".."?
|
||||
@@ -350,8 +343,8 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
|
||||
for (auto& itr : currentCache) {
|
||||
// Only include filenames starting with '.' if the pattern
|
||||
// starts with the same.
|
||||
itr.update((r.pattern()[0] != '.') ? utils::Directory::update_hide_dot : 0);
|
||||
itr.erase(std::remove_if(itr.begin(), itr.end(), [r](const utils::directory_entry& entry) { return !r(entry.s_name); }), itr.end());
|
||||
itr.update((pattern[0] != '.') ? utils::Directory::update_hide_dot : 0);
|
||||
itr.erase(std::remove_if(itr.begin(), itr.end(), [&pattern](const utils::directory_entry& entry) { return fnmatch(pattern.c_str(), entry.s_name.c_str(), 0) != 0; }), itr.end());
|
||||
|
||||
for (const auto& cache : itr)
|
||||
nextCache.push_back(path_expand_transform(itr.path() + (itr.path() == "/" ? "" : "/"), cache));
|
||||
@@ -415,7 +408,7 @@ Manager::receive_hashing_changed() {
|
||||
continue;
|
||||
|
||||
try {
|
||||
m_download_list->open_throw(*itr);
|
||||
m_download_list->open_throw(itr->get());
|
||||
|
||||
// Since the bitfield is allocated on loading of resume load or
|
||||
// hash start, and unallocated on close, we know that if it it
|
||||
@@ -446,6 +439,7 @@ Manager::receive_hashing_changed() {
|
||||
|
||||
} else {
|
||||
(*itr)->set_hash_failed(true);
|
||||
(*itr)->set_message("Hashing failed: " + std::string(e.what()));
|
||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Hashing failed: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
+13
-23
@@ -5,11 +5,9 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <torrent/utils/log_buffer.h>
|
||||
#include <torrent/connection_manager.h>
|
||||
#include <torrent/object.h>
|
||||
|
||||
#include "download_list.h"
|
||||
#include "range_map.h"
|
||||
|
||||
namespace torrent {
|
||||
class Bencode;
|
||||
@@ -23,7 +21,8 @@ namespace core {
|
||||
|
||||
class HttpQueue;
|
||||
|
||||
typedef std::map<std::string, torrent::ThrottlePair> ThrottleMap;
|
||||
using ThrottlePair = std::pair<torrent::Throttle*, torrent::Throttle*>;
|
||||
using ThrottleMap = std::map<std::string, ThrottlePair>;
|
||||
|
||||
class View;
|
||||
|
||||
@@ -35,32 +34,26 @@ public:
|
||||
Manager();
|
||||
~Manager();
|
||||
|
||||
DownloadList* download_list() { return m_download_list.get(); }
|
||||
FileStatusCache* file_status_cache() { return m_file_status_cache.get(); }
|
||||
bool is_download_shutdown_completed();
|
||||
|
||||
HttpQueue* http_queue() { return m_http_queue.get(); }
|
||||
DownloadList* download_list() { return m_download_list.get(); }
|
||||
FileStatusCache* file_status_cache() { return m_file_status_cache.get(); }
|
||||
|
||||
View* hashing_view() { return m_hashingView; }
|
||||
HttpQueue* http_queue() { return m_http_queue.get(); }
|
||||
|
||||
View* hashing_view() { return m_hashingView; }
|
||||
void set_hashing_view(View* v);
|
||||
|
||||
torrent::log_buffer* log_important() { return m_log_important.get(); }
|
||||
torrent::log_buffer* log_complete() { return m_log_complete.get(); }
|
||||
auto* log_important() { return m_log_important.get(); }
|
||||
auto* log_complete() { return m_log_complete.get(); }
|
||||
|
||||
ThrottleMap& throttles() { return m_throttles; }
|
||||
torrent::ThrottlePair get_throttle(const std::string& name);
|
||||
ThrottleMap& throttles() { return m_throttles; }
|
||||
ThrottlePair get_throttle(const std::string& name);
|
||||
|
||||
int64_t retrieve_throttle_value(const torrent::Object::string_type& name, bool rate, bool up);
|
||||
|
||||
// Use custom throttle for the given range of IP addresses.
|
||||
void set_address_throttle(uint32_t begin, uint32_t end, torrent::ThrottlePair throttles);
|
||||
torrent::ThrottlePair get_address_throttle(const sockaddr* addr);
|
||||
|
||||
void cleanup();
|
||||
|
||||
void listen_open();
|
||||
|
||||
void set_proxy_address(const std::string& addr);
|
||||
|
||||
const std::string& magnet_path();
|
||||
void set_magnet_path(const std::string& path);
|
||||
|
||||
@@ -82,11 +75,9 @@ public:
|
||||
// Temporary, find a better place for this.
|
||||
void try_create_download(const std::string& uri, int flags, const command_list_type& commands);
|
||||
void try_create_download_expand(const std::string& uri, int flags, command_list_type commands = command_list_type());
|
||||
void try_create_download_from_meta_download(torrent::Object* bencode, const std::string& metafile);
|
||||
void try_create_download_from_meta_download(std::unique_ptr<torrent::Object> bencode, const std::string& metafile);
|
||||
|
||||
private:
|
||||
typedef RangeMap<uint32_t, torrent::ThrottlePair> AddressThrottleMap;
|
||||
|
||||
void create_http(const std::string& uri);
|
||||
void create_final(std::istream* s);
|
||||
|
||||
@@ -102,7 +93,6 @@ private:
|
||||
View* m_hashingView{};
|
||||
|
||||
ThrottleMap m_throttles;
|
||||
AddressThrottleMap m_addressThrottles;
|
||||
|
||||
torrent::log_buffer_ptr m_log_important;
|
||||
torrent::log_buffer_ptr m_log_complete;
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
// rTorrent - BitTorrent client
|
||||
// Copyright (C) 2005-2008, 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_CORE_RANGE_MAP_H
|
||||
#define RTORRENT_CORE_RANGE_MAP_H
|
||||
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace core {
|
||||
|
||||
// Associate values with a range of keys, and retrieve for any key in the range.
|
||||
|
||||
// The template arguments have the same semantics as std::map.
|
||||
// Exception: if set_merge is used, the value type must have a defined operator ==.
|
||||
template<typename Key, typename T, typename Compare = std::less<Key>,
|
||||
typename Alloc = std::allocator<std::pair<const Key, T> > >
|
||||
class RangeMap : private std::map<Key, std::pair<Key, T>, Compare,
|
||||
typename std::allocator_traits<Alloc>::template rebind_alloc<std::pair<const Key, std::pair<Key, T>>>> {
|
||||
|
||||
typedef std::map<Key, std::pair<Key, T>, Compare,
|
||||
typename std::allocator_traits<Alloc>::template rebind_alloc<std::pair<const Key, std::pair<Key, T>>>> base_type;
|
||||
|
||||
//std::allocator_traits<Alloc>::template rebind_alloc<std::pair<const Key, std::pair<Key, T>>>
|
||||
|
||||
public:
|
||||
RangeMap() = default;
|
||||
RangeMap(const Compare& c) : base_type(c) {}
|
||||
|
||||
typedef typename base_type::iterator iterator;
|
||||
typedef typename base_type::reverse_iterator reverse_iterator;
|
||||
typedef typename base_type::const_iterator const_iterator;
|
||||
typedef typename base_type::const_reverse_iterator const_reverse_iterator;
|
||||
|
||||
// using typename base_type::const_iterator;
|
||||
// using typename base_type::const_reverse_iterator;
|
||||
|
||||
using base_type::clear;
|
||||
using base_type::swap;
|
||||
|
||||
using base_type::size;
|
||||
using base_type::empty;
|
||||
|
||||
using base_type::begin;
|
||||
using base_type::end;
|
||||
using base_type::rbegin;
|
||||
using base_type::rend;
|
||||
|
||||
using base_type::key_comp;
|
||||
using base_type::value_comp;
|
||||
|
||||
// Store a value for the range [begin, end). Returns iterator for the range.
|
||||
const_iterator set_range(const Key& begin, const Key& end, const T& value);
|
||||
|
||||
// Same, but merge adjacent ranges having the same value. Returns iterator for the merged range.
|
||||
const_iterator set_merge(Key begin, const Key& end, const T& value);
|
||||
|
||||
// Find range containing the given key, or end().
|
||||
const_iterator find(const Key& key) const;
|
||||
|
||||
// Retrieve value for key in a range, throw std::out_of_range if range does not exist.
|
||||
const T& get(const Key& key) const;
|
||||
|
||||
// Retrieve value for key in a range, return def if range does not exist.
|
||||
T get(const Key& key, T def) const;
|
||||
|
||||
private:
|
||||
iterator crop_overlap(const Key& begin, const Key& end);
|
||||
};
|
||||
|
||||
// Semantics of an entry:
|
||||
// .first End of range (exclusive), map key.
|
||||
// .second.first Beginning of range.
|
||||
// .second.second Value.
|
||||
|
||||
template<typename Key, typename T, typename C, typename A>
|
||||
inline typename RangeMap<Key,T,C,A>::iterator
|
||||
RangeMap<Key,T,C,A>::crop_overlap(const Key& _begin, const Key& _end) {
|
||||
typename RangeMap::iterator itr = base_type::upper_bound(_begin);
|
||||
|
||||
while (itr != end() && key_comp()(itr->second.first, _end)) {
|
||||
// There's a subrange before the new begin: need new entry (new range end means new key).
|
||||
if (key_comp()(itr->second.first, _begin))
|
||||
base_type::insert(itr, typename RangeMap::value_type(_begin, itr->second));
|
||||
|
||||
// Old end is within our range: erase entry.
|
||||
if (!key_comp()(_end, itr->first)) {
|
||||
base_type::erase(itr++);
|
||||
|
||||
// Otherwise simply set the new begin of the old range.
|
||||
} else {
|
||||
itr->second.first = _end;
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
return itr;
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename C, typename A>
|
||||
inline typename RangeMap<Key,T,C,A>::const_iterator
|
||||
RangeMap<Key,T,C,A>::set_merge(Key _begin, const Key& _end, const T& value) {
|
||||
if (!key_comp()(_begin, _end))
|
||||
return end();
|
||||
|
||||
// Crop overlapping ranges and return iterator to first range after the one we're inserting.
|
||||
typename RangeMap::iterator itr = crop_overlap(_begin, _end);
|
||||
|
||||
// Check if range before new one is adjacent and has same value: if so erase it and use its beginning.
|
||||
if (itr != begin()) {
|
||||
typename RangeMap::iterator prev = itr;
|
||||
if (!key_comp()((--prev)->first, _begin) && prev->second.second == value) {
|
||||
_begin = prev->second.first;
|
||||
base_type::erase(prev);
|
||||
}
|
||||
}
|
||||
|
||||
// Range after new one is adjacent and has same value: set new beginning.
|
||||
if (itr != end() && !key_comp()(_end, itr->second.first) && itr->second.second == value) {
|
||||
itr->second.first = _begin;
|
||||
return itr;
|
||||
}
|
||||
|
||||
// Otherwise, this range isn't mergeable, make new entry.
|
||||
return base_type::insert(itr, typename RangeMap::value_type(_end, typename RangeMap::mapped_type(_begin, value)));
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename C, typename A>
|
||||
inline typename RangeMap<Key,T,C,A>::const_iterator
|
||||
RangeMap<Key,T,C,A>::set_range(const Key& _begin, const Key& _end, const T& value) {
|
||||
if (!key_comp()(_begin, _end))
|
||||
return end();
|
||||
|
||||
return base_type::insert(crop_overlap(_begin, _end), typename RangeMap::value_type(_end, typename RangeMap::mapped_type(_begin, value)));
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename C, typename A>
|
||||
inline typename RangeMap<Key,T,C,A>::const_iterator
|
||||
RangeMap<Key,T,C,A>::find(const Key& key) const {
|
||||
typename RangeMap::const_iterator itr = base_type::upper_bound(key);
|
||||
|
||||
if (itr != end() && key_comp()(key, itr->second.first))
|
||||
itr = end();
|
||||
|
||||
return itr;
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename C, typename A>
|
||||
inline const T&
|
||||
RangeMap<Key,T,C,A>::get(const Key& key) const {
|
||||
typename RangeMap::const_iterator itr = find(key);
|
||||
|
||||
if (itr == end())
|
||||
throw std::out_of_range("RangeMap::get");
|
||||
|
||||
return itr->second.second;
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename C, typename A>
|
||||
inline T
|
||||
RangeMap<Key,T,C,A>::get(const Key& key, T def) const {
|
||||
typename RangeMap::const_iterator itr = find(key);
|
||||
return (itr == end() ? def : itr->second.second);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+42
-18
@@ -16,10 +16,20 @@
|
||||
namespace core {
|
||||
|
||||
// Also add focus thingie here?
|
||||
// Matches the list entry that owns a given download.
|
||||
inline auto
|
||||
entry_is(Download* download) {
|
||||
return [download](const std::shared_ptr<Download>& entry) { return entry.get() == download; };
|
||||
}
|
||||
|
||||
struct view_downloads_compare {
|
||||
view_downloads_compare(const torrent::Object& cmd) :
|
||||
m_command(cmd) {}
|
||||
|
||||
bool operator()(const std::shared_ptr<Download>& d1, const std::shared_ptr<Download>& d2) const {
|
||||
return (*this)(d1.get(), d2.get());
|
||||
}
|
||||
|
||||
bool operator()(Download* d1, Download* d2) const {
|
||||
try {
|
||||
if (m_command.is_empty())
|
||||
@@ -54,6 +64,10 @@ struct view_downloads_filter {
|
||||
view_downloads_filter(const torrent::Object& cmd, const torrent::Object& cmd2) :
|
||||
m_command(cmd), m_command2(cmd2) {}
|
||||
|
||||
bool operator()(const std::shared_ptr<Download>& d1) const {
|
||||
return (*this)(d1.get());
|
||||
}
|
||||
|
||||
bool operator()(Download* d1) const {
|
||||
return this->evalCmd(m_command, d1) && this->evalCmd(m_command2, d1);
|
||||
}
|
||||
@@ -152,7 +166,7 @@ View::initialize(const std::string& name) {
|
||||
|
||||
void
|
||||
View::erase(Download* download) {
|
||||
iterator itr = std::find(base_type::begin(), base_type::end(), download);
|
||||
iterator itr = std::find_if(base_type::begin(), base_type::end(), entry_is(download));
|
||||
|
||||
if (itr >= end_visible()) {
|
||||
erase_internal(itr);
|
||||
@@ -165,22 +179,24 @@ View::erase(Download* download) {
|
||||
|
||||
void
|
||||
View::set_visible(Download* download) {
|
||||
iterator itr = std::find(begin_filtered(), end_filtered(), download);
|
||||
iterator itr = std::find_if(begin_filtered(), end_filtered(), entry_is(download));
|
||||
|
||||
if (itr == end_filtered())
|
||||
return;
|
||||
|
||||
// Don't optimize erase since we want to keep the order of the
|
||||
// non-visible elements.
|
||||
auto entry = *itr;
|
||||
|
||||
base_type::erase(itr);
|
||||
insert_visible(download);
|
||||
insert_visible(entry);
|
||||
|
||||
rpc::call_object_nothrow(m_event_added, rpc::make_target(download));
|
||||
}
|
||||
|
||||
void
|
||||
View::set_not_visible(Download* download) {
|
||||
iterator itr = std::find(begin_visible(), end_visible(), download);
|
||||
iterator itr = std::find_if(begin_visible(), end_visible(), entry_is(download));
|
||||
|
||||
if (itr == end_visible())
|
||||
return;
|
||||
@@ -190,15 +206,17 @@ View::set_not_visible(Download* download) {
|
||||
|
||||
// Don't optimize erase since we want to keep the order of the
|
||||
// non-visible elements.
|
||||
auto entry = *itr;
|
||||
|
||||
base_type::erase(itr);
|
||||
base_type::push_back(download);
|
||||
base_type::push_back(entry);
|
||||
|
||||
rpc::call_object_nothrow(m_event_removed, rpc::make_target(download));
|
||||
}
|
||||
|
||||
void
|
||||
View::next_focus(unsigned int i) {
|
||||
if (empty())
|
||||
if (empty_visible())
|
||||
return;
|
||||
|
||||
// If at the boundary, roll over
|
||||
@@ -221,7 +239,7 @@ View::next_focus(unsigned int i) {
|
||||
|
||||
void
|
||||
View::prev_focus(unsigned int i) {
|
||||
if (empty())
|
||||
if (empty_visible())
|
||||
return;
|
||||
|
||||
// If at the boundary, roll over
|
||||
@@ -241,12 +259,12 @@ View::prev_focus(unsigned int i) {
|
||||
|
||||
void
|
||||
View::sort() {
|
||||
Download* curFocus = focus() != end_visible() ? *focus() : NULL;
|
||||
Download* curFocus = focus() != end_visible() ? focus()->get() : NULL;
|
||||
|
||||
// Don't go randomly switching around equivalent elements.
|
||||
std::stable_sort(begin(), end_visible(), view_downloads_compare(m_sortCurrent));
|
||||
|
||||
m_focus = position(std::find(begin(), end_visible(), curFocus));
|
||||
m_focus = position(std::find_if(begin(), end_visible(), entry_is(curFocus)));
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
@@ -280,10 +298,10 @@ View::filter() {
|
||||
// set the elements to NULL as we trigger commands on them. Or
|
||||
// perhaps always clear them, thus not throwing anything.
|
||||
if (!m_event_removed.is_empty())
|
||||
std::for_each(changed.begin(), splitChanged, std::bind(&rpc::call_object_d_nothrow, m_event_removed, std::placeholders::_1));
|
||||
std::for_each(changed.begin(), splitChanged, [this](const auto& d) { rpc::call_object_d_nothrow(m_event_removed, d.get()); });
|
||||
|
||||
if (!m_event_added.is_empty())
|
||||
std::for_each(changed.begin(), splitChanged, std::bind(&rpc::call_object_d_nothrow, m_event_added, std::placeholders::_1));
|
||||
std::for_each(splitChanged, changed.end(), [this](const auto& d) { rpc::call_object_d_nothrow(m_event_added, d.get()); });
|
||||
|
||||
emit_changed();
|
||||
}
|
||||
@@ -294,21 +312,23 @@ View::filter_by(const torrent::Object& condition, View::base_type& result) {
|
||||
view_downloads_filter matches = view_downloads_filter(condition, m_temp_filter);
|
||||
|
||||
for (iterator itr = begin_visible(); itr != end_visible(); ++itr)
|
||||
if (matches(*itr))
|
||||
if (matches(itr->get()))
|
||||
result.push_back(*itr);
|
||||
}
|
||||
|
||||
void
|
||||
View::filter_download(core::Download* download) {
|
||||
iterator itr = std::find(base_type::begin(), base_type::end(), download);
|
||||
iterator itr = std::find_if(base_type::begin(), base_type::end(), entry_is(download));
|
||||
|
||||
if (itr == base_type::end())
|
||||
throw torrent::internal_error("View::filter_download(...) could not find download.");
|
||||
|
||||
if (view_downloads_filter(m_filter, m_temp_filter)(download)) {
|
||||
if (itr >= end_visible()) {
|
||||
auto entry = *itr;
|
||||
|
||||
erase_internal(itr);
|
||||
insert_visible(download);
|
||||
insert_visible(entry);
|
||||
|
||||
rpc::call_object_nothrow(m_event_added, rpc::make_target(download));
|
||||
|
||||
@@ -317,16 +337,20 @@ View::filter_download(core::Download* download) {
|
||||
// already visible.
|
||||
//
|
||||
// Consider removing this.
|
||||
auto entry = *itr;
|
||||
|
||||
erase_internal(itr);
|
||||
insert_visible(download);
|
||||
insert_visible(entry);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (itr >= end_visible())
|
||||
return;
|
||||
|
||||
auto entry = *itr;
|
||||
|
||||
erase_internal(itr);
|
||||
base_type::push_back(download);
|
||||
base_type::push_back(entry);
|
||||
|
||||
rpc::call_object_nothrow(m_event_removed, rpc::make_target(download));
|
||||
}
|
||||
@@ -345,8 +369,8 @@ 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); });
|
||||
View::insert_visible(const std::shared_ptr<Download>& d) {
|
||||
auto itr = std::find_if(begin_visible(), end_visible(), [this, &d](const auto& d2) { return view_downloads_compare(m_sortNew)(d.get(), d2.get()); });
|
||||
|
||||
m_size++;
|
||||
m_focus += (m_focus >= position(itr));
|
||||
|
||||
+9
-8
@@ -15,10 +15,11 @@
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/utils/scheduler.h>
|
||||
#include <torrent/system/scheduler.h>
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
@@ -26,9 +27,9 @@ namespace core {
|
||||
|
||||
class Download;
|
||||
|
||||
class View : private std::vector<Download*> {
|
||||
class View : private std::vector<std::shared_ptr<Download>> {
|
||||
public:
|
||||
typedef std::vector<Download*> base_type;
|
||||
typedef std::vector<std::shared_ptr<Download>> base_type;
|
||||
typedef std::function<void()> slot_void;
|
||||
typedef std::list<slot_void> signal_void;
|
||||
|
||||
@@ -72,7 +73,7 @@ public:
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
void insert(Download* download) { base_type::push_back(download); }
|
||||
void insert(const std::shared_ptr<Download>& download) { base_type::push_back(download); }
|
||||
void erase(Download* download);
|
||||
|
||||
void set_visible(Download* download);
|
||||
@@ -124,9 +125,9 @@ private:
|
||||
View(const View&);
|
||||
void operator=(const View&);
|
||||
|
||||
void push_back(Download* d) { base_type::push_back(d); }
|
||||
void push_back(const std::shared_ptr<Download>& d) { base_type::push_back(d); }
|
||||
|
||||
inline void insert_visible(Download* d);
|
||||
inline void insert_visible(const std::shared_ptr<Download>& d);
|
||||
inline void erase_internal(iterator itr);
|
||||
|
||||
void emit_changed();
|
||||
@@ -153,8 +154,8 @@ private:
|
||||
|
||||
std::chrono::microseconds m_last_changed{};
|
||||
|
||||
signal_void m_signal_changed;
|
||||
torrent::utils::SchedulerEntry m_delay_changed;
|
||||
signal_void m_signal_changed;
|
||||
torrent::system::SchedulerEntry m_delay_changed;
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
|
||||
@@ -35,7 +35,8 @@ ViewManager::insert(const std::string& name) {
|
||||
View* view = new View();
|
||||
view->initialize(name);
|
||||
|
||||
return base_type::insert(end(), view);
|
||||
base_type::push_back(view);
|
||||
return --end();
|
||||
}
|
||||
|
||||
ViewManager::iterator
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
#define RTORRENT_CORE_VIEW_MANAGER_H
|
||||
|
||||
#include <string>
|
||||
#include <rak/unordered_vector.h>
|
||||
#include <torrent/utils/unordered_vector.h>
|
||||
|
||||
#include "view.h"
|
||||
|
||||
namespace core {
|
||||
|
||||
class ViewManager : public rak::unordered_vector<View*> {
|
||||
class ViewManager : public torrent::utils::unordered_vector<View*> {
|
||||
public:
|
||||
typedef rak::unordered_vector<View*> base_type;
|
||||
typedef std::list<std::string> filter_args;
|
||||
typedef torrent::utils::unordered_vector<View*> base_type;
|
||||
typedef std::list<std::string> filter_args;
|
||||
|
||||
using base_type::iterator;
|
||||
using base_type::const_iterator;
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(HAVE_NCURSESW_CURSES_H)
|
||||
#if defined(HAVE_NO_NCURSES)
|
||||
#include "curses_stub.h"
|
||||
#elif defined(HAVE_NCURSESW_CURSES_H)
|
||||
#include <ncursesw/curses.h>
|
||||
#elif defined(HAVE_NCURSESW_H)
|
||||
#include <ncursesw.h>
|
||||
|
||||
@@ -132,8 +132,7 @@ Canvas::resize_term(std::pair<int, int> dim) {
|
||||
|
||||
inline unsigned int
|
||||
Canvas::get_x() {
|
||||
int x;
|
||||
[[maybe_unused]] int y;
|
||||
[[maybe_unused]] int x, y;
|
||||
if (!m_daemon) {
|
||||
getyx(m_window, y, x);
|
||||
} else {
|
||||
@@ -144,7 +143,7 @@ Canvas::get_x() {
|
||||
|
||||
inline unsigned int
|
||||
Canvas::get_y() {
|
||||
int x, y;
|
||||
[[maybe_unused]] int x, y;
|
||||
if (!m_daemon) {
|
||||
getyx(m_window, y, x);
|
||||
} else {
|
||||
@@ -155,8 +154,7 @@ Canvas::get_y() {
|
||||
|
||||
inline unsigned int
|
||||
Canvas::width() {
|
||||
int x;
|
||||
[[maybe_unused]] int y;
|
||||
[[maybe_unused]] int x, y;
|
||||
if (!m_daemon) {
|
||||
getmaxyx(m_window, y, x);
|
||||
} else {
|
||||
@@ -167,7 +165,7 @@ Canvas::width() {
|
||||
|
||||
inline unsigned int
|
||||
Canvas::height() {
|
||||
int x, y;
|
||||
[[maybe_unused]] int x, y;
|
||||
if (!m_daemon) {
|
||||
getmaxyx(m_window, y, x);
|
||||
} else {
|
||||
@@ -257,8 +255,7 @@ Canvas::set_default_attributes(int attr) {
|
||||
|
||||
inline int
|
||||
Canvas::get_screen_width() {
|
||||
int x;
|
||||
[[maybe_unused]] int y;
|
||||
[[maybe_unused]] int x, y;
|
||||
if (!m_daemon) {
|
||||
getmaxyx(stdscr, y, x);
|
||||
} else {
|
||||
@@ -269,7 +266,7 @@ Canvas::get_screen_width() {
|
||||
|
||||
inline int
|
||||
Canvas::get_screen_height() {
|
||||
int x, y;
|
||||
[[maybe_unused]] int x, y;
|
||||
if (!m_daemon) {
|
||||
getmaxyx(stdscr, y, x);
|
||||
} else {
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
#include <array>
|
||||
#include <map>
|
||||
|
||||
#if defined(HAVE_NO_NCURSES)
|
||||
#include "curses_stub.h"
|
||||
#else
|
||||
#include <curses.h>
|
||||
#endif
|
||||
|
||||
namespace display {
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Dummy ncurses stub: provides the stdscr symbol expected by the codebase.
|
||||
|
||||
#include "curses_stub.h"
|
||||
|
||||
// Define stdscr as a null pointer; all ncurses calls are no-ops in this stub.
|
||||
WINDOW* stdscr = nullptr;
|
||||
@@ -0,0 +1,108 @@
|
||||
// Dummy ncurses stub header for building rtorrent without ncurses.
|
||||
// Provides types, macros, and no-op function stubs so the codebase
|
||||
// compiles without a real curses library. All display functions become
|
||||
// no-ops; use with system.daemon.set = true.
|
||||
|
||||
#ifndef RTORRENT_DISPLAY_CURSES_STUB_H
|
||||
#define RTORRENT_DISPLAY_CURSES_STUB_H
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstddef>
|
||||
|
||||
// Opaque window type
|
||||
typedef void* WINDOW;
|
||||
|
||||
// Character type used by curses
|
||||
typedef unsigned int chtype;
|
||||
typedef unsigned int attr_t;
|
||||
|
||||
// The standard screen
|
||||
extern WINDOW* stdscr;
|
||||
|
||||
// Boolean constants and return codes
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
#ifndef ERR
|
||||
#define ERR (-1)
|
||||
#endif
|
||||
|
||||
// Attribute constants
|
||||
#define A_NORMAL 0
|
||||
#define A_STANDOUT (1U << 8)
|
||||
#define A_UNDERLINE (1U << 9)
|
||||
#define A_REVERSE (1U << 10)
|
||||
#define A_BLINK (1U << 11)
|
||||
#define A_DIM (1U << 12)
|
||||
#define A_BOLD (1U << 13)
|
||||
|
||||
// Color support
|
||||
#define COLORS 0
|
||||
#define COLOR_BLACK 0
|
||||
#define COLOR_RED 1
|
||||
#define COLOR_GREEN 2
|
||||
#define COLOR_YELLOW 3
|
||||
#define COLOR_BLUE 4
|
||||
#define COLOR_MAGENTA 5
|
||||
#define COLOR_CYAN 6
|
||||
#define COLOR_WHITE 7
|
||||
|
||||
#define COLOR_PAIR(n) 0
|
||||
|
||||
// Key constants
|
||||
#define KEY_DOWN 0x102
|
||||
#define KEY_UP 0x103
|
||||
#define KEY_LEFT 0x104
|
||||
#define KEY_RIGHT 0x105
|
||||
#define KEY_HOME 0x106
|
||||
#define KEY_BACKSPACE 0x107
|
||||
#define KEY_DC 0x14a
|
||||
#define KEY_ENTER 0x157
|
||||
#define KEY_NPAGE 0x152
|
||||
#define KEY_PPAGE 0x153
|
||||
#define KEY_END 0x168
|
||||
#define KEY_MOUSE 0x199
|
||||
#define KEY_RESIZE 0x19a
|
||||
|
||||
// Stubbed functions
|
||||
inline WINDOW* initscr() { return nullptr; }
|
||||
inline int endwin() { return 0; }
|
||||
inline WINDOW* newwin(int, int, int, int) { return nullptr; }
|
||||
inline int delwin(WINDOW*) { return 0; }
|
||||
inline int wresize(WINDOW*, int, int) { return 0; }
|
||||
inline int mvwin(WINDOW*, int, int) { return 0; }
|
||||
inline int wnoutrefresh(WINDOW*) { return 0; }
|
||||
inline int redrawwin(WINDOW*) { return 0; }
|
||||
inline int werase(WINDOW*) { return 0; }
|
||||
inline int wmove(WINDOW*, int, int) { return 0; }
|
||||
inline int vw_printw(WINDOW*, char*, va_list) { return 0; }
|
||||
inline int waddch(WINDOW*, const chtype) { return 0; }
|
||||
inline int mvwaddch(WINDOW*, int, int, const chtype) { return 0; }
|
||||
inline int mvwchgat(WINDOW*, int, int, int, attr_t, short, const void*) { return 0; }
|
||||
inline int wattrset(WINDOW*, int) { return 0; }
|
||||
inline int wattr_get(WINDOW*, attr_t*, short*, void*) { return 0; }
|
||||
inline int wattr_set(WINDOW*, attr_t, short, void*) { return 0; }
|
||||
inline int doupdate() { return 0; }
|
||||
inline int start_color() { return 0; }
|
||||
inline int use_default_colors() { return 0; }
|
||||
inline int raw() { return 0; }
|
||||
inline int noraw() { return 0; }
|
||||
inline int noecho() { return 0; }
|
||||
inline int nodelay(WINDOW*, bool) { return 0; }
|
||||
inline int keypad(WINDOW*, bool) { return 0; }
|
||||
inline int curs_set(int) { return 0; }
|
||||
inline int init_pair(short, short, short) { return 0; }
|
||||
inline int pair_content(short, short*, short*) { return 0; }
|
||||
inline int resizeterm(int, int) { return 0; }
|
||||
inline int getch() { return ERR; }
|
||||
inline int set_escdelay(int) { return 0; }
|
||||
|
||||
// getyx / getmaxyx are macros in real ncurses; stub them as no-ops
|
||||
#define getyx(w, y, x) do { (y) = 0; (x) = 0; } while (0)
|
||||
#define getmaxyx(w, y, x) do { (y) = 24; (x) = 80; } while (0)
|
||||
|
||||
#endif // RTORRENT_DISPLAY_CURSES_STUB_H
|
||||
@@ -80,10 +80,14 @@ Manager::schedule_update(std::chrono::microseconds min_interval) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_task_update.is_scheduled() && m_task_update.time() <= m_scheduler.front()->time())
|
||||
auto cached = torrent::this_thread::cached_time();
|
||||
m_scheduler.external_set_cached_time(cached);
|
||||
auto next = cached + m_scheduler.next_timeout(cached);
|
||||
|
||||
if (m_task_update.is_scheduled() && m_task_update.time_or_zero() <= next)
|
||||
return;
|
||||
|
||||
torrent::this_thread::scheduler()->update_wait_until(&m_task_update, std::max(m_scheduler.front()->time(), m_time_last_update + min_interval));
|
||||
torrent::this_thread::scheduler()->update_wait_until(&m_task_update, std::max(next, m_time_last_update + min_interval));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef RTORRENT_DISPLAY_MANAGER_H
|
||||
#define RTORRENT_DISPLAY_MANAGER_H
|
||||
|
||||
#include <torrent/utils/scheduler.h>
|
||||
#include <torrent/system/scheduler.h>
|
||||
|
||||
#include "display/frame.h"
|
||||
|
||||
@@ -31,9 +31,9 @@ private:
|
||||
bool m_force_redraw{false};
|
||||
Frame m_root_frame;
|
||||
|
||||
std::chrono::microseconds m_time_last_update{};
|
||||
torrent::utils::ExternalScheduler m_scheduler;
|
||||
torrent::utils::SchedulerEntry m_task_update;
|
||||
std::chrono::microseconds m_time_last_update{};
|
||||
torrent::system::ExternalScheduler m_scheduler;
|
||||
torrent::system::SchedulerEntry m_task_update;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,43 +1,10 @@
|
||||
// rTorrent - BitTorrent client
|
||||
// Copyright (C) 2005-2011, Jari Sundell
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for
|
||||
// all of the code used other than OpenSSL. If you modify file(s)
|
||||
// with this exception, you may extend this exception to your version
|
||||
// of the file(s), but you are not obligated to do so. If you do not
|
||||
// wish to do so, delete this exception statement from your version.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <sundell.software@gmail.com>
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <rak/string_manip.h>
|
||||
#include "display/text_element_string.h"
|
||||
|
||||
#include <torrent/utils/string_manip.h>
|
||||
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "text_element_string.h"
|
||||
|
||||
namespace display {
|
||||
|
||||
@@ -53,17 +20,17 @@ TextElementStringBase::print(char* first, char* last, Canvas::attributes_list* a
|
||||
auto buffer = std::make_unique<char[]>(last - first);
|
||||
char* bufferLast = copy_string(buffer.get(), buffer.get() + (last - first), target);
|
||||
|
||||
first = rak::transform_hex(buffer.get(), bufferLast, first, last);
|
||||
first = torrent::utils::transform_to_hex(buffer.get(), 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);
|
||||
|
||||
first = rak::copy_escape_html(buffer.get(), bufferLast, first, last);
|
||||
first = torrent::utils::copy_escape_html(buffer.get(), bufferLast, first, last);
|
||||
|
||||
} else {
|
||||
first = copy_string(first, last, target);
|
||||
}
|
||||
}
|
||||
|
||||
push_attribute(attributes, Attributes(first, baseAttribute));
|
||||
|
||||
@@ -71,16 +38,16 @@ TextElementStringBase::print(char* first, char* last, Canvas::attributes_list* a
|
||||
}
|
||||
|
||||
char*
|
||||
TextElementString::copy_string(char* first, char* last, rpc::target_type target) {
|
||||
TextElementString::copy_string(char* first, char* last, [[maybe_unused]] rpc::target_type target) {
|
||||
extent_type length = std::min<extent_type>(last - first, m_string.size());
|
||||
|
||||
|
||||
std::memcpy(first, m_string.c_str(), length);
|
||||
|
||||
return first + length;
|
||||
}
|
||||
|
||||
char*
|
||||
TextElementCString::copy_string(char* first, char* last, rpc::target_type target) {
|
||||
TextElementCString::copy_string(char* first, char* last, [[maybe_unused]] rpc::target_type target) {
|
||||
extent_type length = std::min<extent_type>(last - first, m_length);
|
||||
|
||||
std::memcpy(first, m_string, length);
|
||||
@@ -104,22 +71,22 @@ TextElementCommand::print(char* first, char* last, Canvas::attributes_list* attr
|
||||
const std::string& str = result.as_string();
|
||||
|
||||
if (m_flags & flag_escape_hex) {
|
||||
first = rak::transform_hex(str.c_str(), str.c_str() + str.size(), first, last);
|
||||
first = torrent::utils::transform_to_hex(str.c_str(), str.c_str() + str.size(), first, last);
|
||||
|
||||
} else if (m_flags & flag_escape_html) {
|
||||
first = rak::copy_escape_html(str.c_str(), str.c_str() + str.size(), first, last);
|
||||
first = torrent::utils::copy_escape_html(str.c_str(), str.c_str() + str.size(), first, last);
|
||||
|
||||
} else {
|
||||
size_t length = std::min<size_t>(str.size(), std::distance(first, last));
|
||||
|
||||
std::memcpy(first, str.c_str(), length);
|
||||
first += std::min<size_t>(str.size(), length);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case torrent::Object::TYPE_VALUE:
|
||||
{
|
||||
{
|
||||
first += std::min<ptrdiff_t>(std::max(snprintf(first, last - first + 1, "%lld", (long long int)result.as_value()), 0), last - first + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,37 +1,3 @@
|
||||
// rTorrent - BitTorrent client
|
||||
// Copyright (C) 2005-2011, Jari Sundell
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for
|
||||
// all of the code used other than OpenSSL. If you modify file(s)
|
||||
// with this exception, you may extend this exception to your version
|
||||
// of the file(s), but you are not obligated to do so. If you do not
|
||||
// wish to do so, delete this exception statement from your version.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <sundell.software@gmail.com>
|
||||
|
||||
|
||||
#ifndef RTORRENT_DISPLAY_TEXT_ELEMENT_STRING_H
|
||||
#define RTORRENT_DISPLAY_TEXT_ELEMENT_STRING_H
|
||||
|
||||
|
||||
+12
-11
@@ -7,7 +7,6 @@
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/connection_manager.h>
|
||||
#include <torrent/rate.h>
|
||||
#include <torrent/throttle.h>
|
||||
#include <torrent/torrent.h>
|
||||
@@ -16,9 +15,11 @@
|
||||
#include <torrent/data/file_manager.h>
|
||||
#include <torrent/download/resource_manager.h>
|
||||
#include <torrent/net/http_stack.h>
|
||||
#include <torrent/net/network_config.h>
|
||||
#include <torrent/net/socket_address.h>
|
||||
#include <torrent/peer/client_info.h>
|
||||
#include <torrent/runtime/network_config.h>
|
||||
#include <torrent/runtime/runtime.h>
|
||||
#include <torrent/runtime/socket_manager.h>
|
||||
|
||||
#include "control.h"
|
||||
#include "globals.h"
|
||||
@@ -154,7 +155,7 @@ print_download_status(char* first, char* last, core::Download* d) {
|
||||
|
||||
} else if (d->tracker_controller().has_active_trackers_not_scrape()) {
|
||||
auto tracker = d->tracker_controller().find_if([](const auto& t) {
|
||||
return t.is_busy_not_scrape();
|
||||
return t.is_requesting_not_scrape();
|
||||
});
|
||||
|
||||
if (tracker.is_valid()) {
|
||||
@@ -344,7 +345,7 @@ print_status_info(char* first, char* last) {
|
||||
if (!torrent::up_throttle_global()->is_throttled()) {
|
||||
first = print_buffer(first, last, "[Throttle off");
|
||||
} else {
|
||||
first = print_buffer(first, last, "[Throttle %3i", torrent::up_throttle_global()->max_rate() / 1024);
|
||||
first = print_buffer(first, last, "[Throttle %3i", (int)(torrent::up_throttle_global()->max_rate() / 1024));
|
||||
|
||||
if (!throttle_up_names.empty())
|
||||
first = print_status_throttle_limit(first, last, true, throttle_up_names);
|
||||
@@ -353,7 +354,7 @@ print_status_info(char* first, char* last) {
|
||||
if (!torrent::down_throttle_global()->is_throttled()) {
|
||||
first = print_buffer(first, last, " / off KB]");
|
||||
} else {
|
||||
first = print_buffer(first, last, " / %3i", torrent::down_throttle_global()->max_rate() / 1024);
|
||||
first = print_buffer(first, last, " / %3i", (int)(torrent::down_throttle_global()->max_rate() / 1024));
|
||||
|
||||
if (!throttle_down_names.empty())
|
||||
first = print_status_throttle_limit(first, last, false, throttle_down_names);
|
||||
@@ -376,7 +377,7 @@ print_status_info(char* first, char* last) {
|
||||
first = print_buffer(first, last, " KB]");
|
||||
first = print_buffer(first, last, " [Port: %i]", (unsigned int)torrent::runtime::listen_port());
|
||||
|
||||
auto local_address = torrent::config::network_config()->local_address_best_match();
|
||||
auto local_address = torrent::runtime::network_config()->local_address_best_match();
|
||||
|
||||
if (!torrent::sa_is_any(local_address.get())) {
|
||||
first = print_buffer(first, last, " [Local ");
|
||||
@@ -387,8 +388,8 @@ print_status_info(char* first, char* last) {
|
||||
if (first > last)
|
||||
throw torrent::internal_error("print_status_info(...) wrote past end of the buffer.");
|
||||
|
||||
auto bind_inet_address = torrent::config::network_config()->bind_inet_address();
|
||||
auto bind_inet6_address = torrent::config::network_config()->bind_inet6_address();
|
||||
auto bind_inet_address = torrent::runtime::network_config()->bind_inet_address();
|
||||
auto bind_inet6_address = torrent::runtime::network_config()->bind_inet6_address();
|
||||
bool show_bind_inet = !torrent::sa_is_any(bind_inet_address.get());
|
||||
bool show_bind_inet6 = !torrent::sa_is_any(bind_inet6_address.get());
|
||||
|
||||
@@ -426,9 +427,9 @@ print_status_extra(char* first, char* last) {
|
||||
torrent::net_thread::http_stack()->max_total_connections());
|
||||
|
||||
first = print_buffer(first, last, " [S %i/%i/%i]",
|
||||
torrent::total_handshakes(),
|
||||
torrent::connection_manager()->size(),
|
||||
torrent::connection_manager()->max_size());
|
||||
torrent::runtime::total_handshakes(),
|
||||
torrent::runtime::socket_manager()->size(),
|
||||
torrent::runtime::socket_manager()->max_size());
|
||||
|
||||
first = print_buffer(first, last, " [F %i/%i]",
|
||||
torrent::file_manager()->open_files(),
|
||||
|
||||
@@ -16,15 +16,12 @@ namespace utils {
|
||||
|
||||
namespace torrent {
|
||||
class ClientInfo;
|
||||
class Entry;
|
||||
}
|
||||
|
||||
class Control;
|
||||
|
||||
namespace display {
|
||||
|
||||
char* print_string(char* first, char* last, char* str);
|
||||
|
||||
char* print_hhmmss(char* first, char* last, time_t t);
|
||||
char* print_hhmmss_local(char* first, char* last, time_t t);
|
||||
char* print_ddhhmm(char* first, char* last, time_t t);
|
||||
@@ -42,9 +39,6 @@ char* print_download_percentage_done(char* first, char* last, core::Downlo
|
||||
|
||||
char* print_client_version(char* first, char* last, const torrent::ClientInfo& clientInfo);
|
||||
|
||||
char* print_entry_tags(char* first, char* last);
|
||||
char* print_entry_file(char* first, char* last, const torrent::Entry& entry);
|
||||
|
||||
char* print_status_throttle_limit(char* first, char* last, bool up, const std::vector<std::string>& throttle_names);
|
||||
char* print_status_throttle_rate(char* first, char* last, bool up, const std::vector<std::string>& throttle_names, const double& global_rate);
|
||||
|
||||
|
||||
+60
-3
@@ -2,8 +2,8 @@
|
||||
#define RTORRENT_WINDOW_BASE_H
|
||||
|
||||
#include <functional>
|
||||
#include <torrent/utils/scheduler.h>
|
||||
#include <torrent/utils/thread.h>
|
||||
#include <torrent/system/scheduler.h>
|
||||
#include <torrent/system/thread.h>
|
||||
|
||||
#include "canvas.h"
|
||||
#include "globals.h"
|
||||
@@ -99,9 +99,66 @@ protected:
|
||||
extent_type m_max_width;
|
||||
extent_type m_max_height;
|
||||
|
||||
torrent::utils::SchedulerEntry m_task_update;
|
||||
torrent::system::SchedulerEntry m_task_update;
|
||||
};
|
||||
|
||||
// Return a range with a distance of no more than __distance and
|
||||
// between __first and __last, centered on __middle1.
|
||||
template <typename _InputIter, typename _Distance>
|
||||
std::pair<_InputIter, _InputIter>
|
||||
advance_bidirectional(_InputIter __first, _InputIter __middle1, _InputIter __last, _Distance __distance) {
|
||||
_InputIter __middle2 = __middle1;
|
||||
|
||||
do {
|
||||
if (!__distance)
|
||||
break;
|
||||
|
||||
if (__middle2 != __last) {
|
||||
++__middle2;
|
||||
--__distance;
|
||||
|
||||
} else if (__middle1 == __first) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!__distance)
|
||||
break;
|
||||
|
||||
if (__middle1 != __first) {
|
||||
--__middle1;
|
||||
--__distance;
|
||||
|
||||
} else if (__middle2 == __last) {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
return std::make_pair(__middle1, __middle2);
|
||||
}
|
||||
|
||||
template <typename _InputIter, typename _Distance>
|
||||
_InputIter
|
||||
advance_forward(_InputIter __first, _InputIter __last, _Distance __distance) {
|
||||
while (__first != __last && __distance != 0) {
|
||||
__first++;
|
||||
__distance--;
|
||||
}
|
||||
|
||||
return __first;
|
||||
}
|
||||
|
||||
template <typename _InputIter, typename _Distance>
|
||||
_InputIter
|
||||
advance_backward(_InputIter __first, _InputIter __last, _Distance __distance) {
|
||||
while (__first != __last && __distance != 0) {
|
||||
__first--;
|
||||
__distance--;
|
||||
}
|
||||
|
||||
return __first;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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) :
|
||||
@@ -57,7 +57,8 @@ WindowDownloadChunksSeen::redraw() {
|
||||
const torrent::TransferList* transfers = m_download->download()->transfer_list();
|
||||
std::vector<torrent::BlockList*> transferChunks(transfers->begin(), transfers->end());
|
||||
|
||||
std::sort(transferChunks.begin(), transferChunks.end());
|
||||
std::sort(transferChunks.begin(), transferChunks.end(),
|
||||
[](const auto& left, const auto& right) { return left->index() < right->index(); });
|
||||
|
||||
std::vector<torrent::BlockList*>::const_iterator itrTransfer = transferChunks.begin();
|
||||
|
||||
@@ -82,7 +83,7 @@ WindowDownloadChunksSeen::redraw() {
|
||||
attr = A_BOLD;
|
||||
}
|
||||
|
||||
m_canvas->print_char(attr | rak::value_to_hexchar<0>(std::min<uint8_t>(*chunk, 0xF)));
|
||||
m_canvas->print_char(attr | torrent::utils::value_to_hex0(std::min<uint8_t>(*chunk, 0xF)));
|
||||
chunk++;
|
||||
|
||||
if ((chunk - seen) % 10 == 0) {
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "display/color_map.h"
|
||||
#include <rak/algorithm.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "core/download.h"
|
||||
#include "core/view.h"
|
||||
#include "display/canvas.h"
|
||||
#include "display/utils.h"
|
||||
#include "display/window_download_list.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
#include "canvas.h"
|
||||
#include "globals.h"
|
||||
#include "utils.h"
|
||||
#include "window_download_list.h"
|
||||
|
||||
namespace display {
|
||||
|
||||
WindowDownloadList::WindowDownloadList() :
|
||||
@@ -39,7 +37,7 @@ WindowDownloadList::set_view(core::View* l) {
|
||||
// Return a pair of ints, representing a) the ncurses attributes and b) the ncurses color pair ID to use
|
||||
std::pair<int, int>
|
||||
WindowDownloadList::get_attr_color(core::View::iterator selected) {
|
||||
core::Download* item = *selected;
|
||||
core::Download* item = selected->get();
|
||||
unsigned long focus_attr = selected == m_view->focus() ? m_canvas->attr_map().at(RCOLOR_FOCUS) : 0;
|
||||
int offset = (((selected - m_view->begin_visible()) & 1) + 1) * RCOLOR_MAX; // Determine the even/odd offset for the color pair
|
||||
bool active = item->is_open() && item->is_active();
|
||||
@@ -104,10 +102,10 @@ WindowDownloadList::redraw() {
|
||||
|
||||
typedef std::pair<core::View::iterator, core::View::iterator> Range;
|
||||
|
||||
Range range = rak::advance_bidirectional(m_view->begin_visible(),
|
||||
m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(),
|
||||
m_view->end_visible(),
|
||||
page_size(layout_name));
|
||||
Range range = advance_bidirectional(m_view->begin_visible(),
|
||||
m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(),
|
||||
m_view->end_visible(),
|
||||
page_size(layout_name));
|
||||
|
||||
// Make sure we properly fill out the last lines so it looks like
|
||||
// there are more torrents, yet don't hide it if we got the last one
|
||||
@@ -134,15 +132,15 @@ WindowDownloadList::redraw() {
|
||||
ColorKind focus_color = is_focused ? RCOLOR_FOCUS : RCOLOR_LABEL;
|
||||
auto attr_color = get_attr_color(range.first);
|
||||
|
||||
print_download_title(buffer.data(), last, *range.first);
|
||||
print_download_title(buffer.data(), last, range.first->get());
|
||||
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
||||
m_canvas->set_attr(2, pos++, -1, attr_color.first, attr_color.second);
|
||||
|
||||
print_download_info_full(buffer.data(), last, *range.first);
|
||||
print_download_info_full(buffer.data(), last, range.first->get());
|
||||
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
||||
m_canvas->set_attr(2, pos++, -1, focus_color);
|
||||
|
||||
print_download_status(buffer.data(), last, *range.first);
|
||||
print_download_status(buffer.data(), last, range.first->get());
|
||||
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
||||
m_canvas->set_attr(2, pos++, -1, focus_color);
|
||||
|
||||
@@ -154,7 +152,7 @@ WindowDownloadList::redraw() {
|
||||
char focus_char = range.first == m_view->focus() ? '*' : ' ';
|
||||
auto attr_color = get_attr_color(range.first);
|
||||
|
||||
print_download_info_compact(buffer.data(), last, *range.first);
|
||||
print_download_info_compact(buffer.data(), last, range.first->get());
|
||||
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
||||
m_canvas->set_attr(2, pos++, -1, attr_color.first, attr_color.second);
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
#include <rak/string_manip.h>
|
||||
#include <torrent/data/block_list.h>
|
||||
#include <torrent/data/transfer_list.h>
|
||||
|
||||
|
||||
@@ -25,7 +25,10 @@ WindowFileList::WindowFileList(const ui::ElementFileList* element) :
|
||||
// Convert std::string to std::wstring of given width (in screen positions),
|
||||
// taking into account that some characters may be occupying two screen positions.
|
||||
std::wstring
|
||||
wstring_width(const std::string& i_str, [[maybe_unused]] int width) {
|
||||
wstring_width(const std::string& i_str, int width) {
|
||||
if (width < 0)
|
||||
width = 0;
|
||||
|
||||
std::wstring result(width + 1, L' ');
|
||||
size_t length = std::mbstowcs(result.data(), i_str.c_str(), width);
|
||||
|
||||
@@ -135,8 +138,11 @@ WindowFileList::redraw() {
|
||||
m_canvas->print(0, pos, "%*c%-*s", 16, ' ', filenameWidth, "EMPTY");
|
||||
|
||||
} else if (itr.is_entering()) {
|
||||
m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\',
|
||||
itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN");
|
||||
if (itr.depth() < itr.file()->path()->size())
|
||||
m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\',
|
||||
wstring_width(itr.file()->path()->at(itr.depth()).str(), filenameWidth - itr.depth() - 1).c_str());
|
||||
else
|
||||
m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\', L"UNKNOWN");
|
||||
|
||||
} else if (itr.is_leaving()) {
|
||||
m_canvas->print(0, pos, "%*c %-*s", 16 + (itr.depth() - 1), '/', filenameWidth - (itr.depth() - 1), "");
|
||||
@@ -166,8 +172,12 @@ WindowFileList::redraw() {
|
||||
else
|
||||
m_canvas->print(8, pos, "%5.1f T", (double)val / (int64_t(1) << 40));
|
||||
|
||||
m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|',
|
||||
itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN");
|
||||
if (itr.depth() < itr.file()->path()->size())
|
||||
m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|',
|
||||
wstring_width(itr.file()->path()->at(itr.depth()).str(), filenameWidth - itr.depth() - 1).c_str());
|
||||
else
|
||||
m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|', L"UNKNOWN");
|
||||
|
||||
|
||||
} else {
|
||||
m_canvas->print(0, pos, "BORK BORK");
|
||||
|
||||
@@ -47,7 +47,7 @@ private:
|
||||
signal_curl_get::iterator m_conn_insert;
|
||||
signal_curl_get::iterator m_conn_erase;
|
||||
|
||||
torrent::utils::SchedulerEntry m_task_deactivate;
|
||||
torrent::system::SchedulerEntry m_task_deactivate;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+31
-12
@@ -1,8 +1,11 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <torrent/torrent.h>
|
||||
#include <torrent/utils/thread.h>
|
||||
#include <torrent/system/callbacks.h>
|
||||
#include <torrent/system/thread.h>
|
||||
|
||||
#include "canvas.h"
|
||||
#include "utils.h"
|
||||
@@ -14,16 +17,18 @@ WindowLog::WindowLog(torrent::log_buffer* l) :
|
||||
Window(new Canvas, 0, 0, 0, extent_full, extent_static),
|
||||
m_log(l) {
|
||||
|
||||
m_task_update.slot() = std::bind(&WindowLog::receive_update, this);
|
||||
m_task_update.slot() = [this]() { receive_update(); };
|
||||
|
||||
unsigned int signal_index = torrent::main_thread::thread()->signal_bitfield()->add_signal(std::bind(&WindowLog::receive_update, this));
|
||||
m_log->lock_and_set_update_slot([this]() {
|
||||
if (m_log_updating.exchange(true) == true)
|
||||
return;
|
||||
|
||||
m_log->lock_and_set_update_slot([signal_index]() {
|
||||
torrent::main_thread::thread()->send_event_signal(signal_index, false);
|
||||
});
|
||||
torrent::main_thread::callback([this]() { receive_update(); });
|
||||
});
|
||||
}
|
||||
|
||||
WindowLog::~WindowLog() {
|
||||
m_log->lock_and_set_update_slot(nullptr);
|
||||
torrent::this_thread::scheduler()->erase(&m_task_update);
|
||||
}
|
||||
|
||||
@@ -38,13 +43,20 @@ WindowLog::redraw() {
|
||||
|
||||
int pos = m_canvas->height();
|
||||
|
||||
for (iterator itr = m_log->end(), last = find_older(); itr != last && pos > 0; --pos) {
|
||||
itr--;
|
||||
std::vector<torrent::log_entry> entries;
|
||||
|
||||
{
|
||||
std::lock_guard<torrent::log_buffer> guard(*m_log);
|
||||
|
||||
for (iterator itr = m_log->end(), last = find_older(); itr != last && entries.size() < static_cast<size_t>(pos); )
|
||||
entries.push_back(*--itr);
|
||||
}
|
||||
|
||||
for (const auto& entry : entries) {
|
||||
char buffer[16];
|
||||
print_hhmmss_local(buffer, buffer + 16, static_cast<time_t>(itr->timestamp));
|
||||
print_hhmmss_local(buffer, buffer + 16, static_cast<time_t>(entry.timestamp));
|
||||
|
||||
m_canvas->print(0, pos - 1, "(%s) %s", buffer, itr->message.c_str());
|
||||
m_canvas->print(0, --pos, "(%s) %s", buffer, entry.message.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,11 +64,18 @@ WindowLog::redraw() {
|
||||
// gets updated.
|
||||
void
|
||||
WindowLog::receive_update() {
|
||||
m_log_updating = false;
|
||||
|
||||
if (!is_active())
|
||||
return;
|
||||
|
||||
iterator itr = find_older();
|
||||
extent_type height = std::min(std::distance(itr, (iterator)m_log->end()), (std::iterator_traits<iterator>::difference_type)10);
|
||||
std::iterator_traits<iterator>::difference_type height;
|
||||
|
||||
{
|
||||
std::lock_guard<torrent::log_buffer> guard(*m_log);
|
||||
|
||||
height = std::min(std::distance(find_older(), (iterator)m_log->end()), (std::iterator_traits<iterator>::difference_type)10);
|
||||
}
|
||||
|
||||
if (height != m_max_height) {
|
||||
m_min_height = height != 0 ? 1 : 0;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef RTORRENT_DISPLAY_WINDOW_LOG_H
|
||||
#define RTORRENT_DISPLAY_WINDOW_LOG_H
|
||||
|
||||
#include <torrent/system/scheduler.h>
|
||||
#include <torrent/utils/log_buffer.h>
|
||||
#include <torrent/utils/scheduler.h>
|
||||
|
||||
#include "window.h"
|
||||
|
||||
@@ -22,8 +22,12 @@ public:
|
||||
private:
|
||||
inline iterator find_older();
|
||||
|
||||
torrent::log_buffer* m_log;
|
||||
torrent::utils::SchedulerEntry m_task_update;
|
||||
torrent::log_buffer* m_log;
|
||||
torrent::system::SchedulerEntry m_task_update;
|
||||
|
||||
align_cacheline
|
||||
|
||||
std::atomic<bool> m_log_updating{};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "canvas.h"
|
||||
#include "utils.h"
|
||||
@@ -29,24 +31,34 @@ WindowLogComplete::redraw() {
|
||||
|
||||
int pos = m_canvas->height();
|
||||
|
||||
for (iterator itr = m_log->end(), last = m_log->begin(); itr != last && pos > 0; ) {
|
||||
itr--;
|
||||
std::vector<torrent::log_entry> entries;
|
||||
|
||||
{
|
||||
std::lock_guard<torrent::log_buffer> guard(*m_log);
|
||||
|
||||
for (iterator itr = m_log->end(), last = m_log->begin(); itr != last && entries.size() < static_cast<size_t>(pos); )
|
||||
entries.push_back(*--itr);
|
||||
}
|
||||
|
||||
for (const auto& entry : entries) {
|
||||
if (pos <= 0)
|
||||
break;
|
||||
|
||||
char buffer[16];
|
||||
|
||||
// Use an arbitrary min width of 60 for allowing multiple
|
||||
// lines. This should ensure we don't mess up the display when the
|
||||
// screen is shrunk too much.
|
||||
unsigned int timeWidth = 3 + print_hhmmss_local(buffer, buffer + 16, static_cast<time_t>(itr->timestamp)) - buffer;
|
||||
unsigned int timeWidth = 3 + print_hhmmss_local(buffer, buffer + 16, static_cast<time_t>(entry.timestamp)) - buffer;
|
||||
|
||||
unsigned int logWidth = m_canvas->width() > 60 ? (m_canvas->width() - timeWidth) : (60 - timeWidth);
|
||||
unsigned int logHeight = (itr->message.size() + logWidth - 1) / logWidth;
|
||||
unsigned int logHeight = (entry.message.size() + logWidth - 1) / logWidth;
|
||||
|
||||
for (unsigned int j = logHeight; j > 0 && pos > 0; --j, --pos)
|
||||
if (j == 1)
|
||||
m_canvas->print(0, pos - 1, "(%s) %s", buffer, itr->message.substr(0, m_canvas->width() - timeWidth).c_str());
|
||||
m_canvas->print(0, pos - 1, "(%s) %s", buffer, entry.message.substr(0, m_canvas->width() - timeWidth).c_str());
|
||||
else
|
||||
m_canvas->print(timeWidth, pos - 1, "%s", itr->message.substr(logWidth * (j - 1), m_canvas->width() - timeWidth).c_str());
|
||||
m_canvas->print(timeWidth, pos - 1, "%s", entry.message.substr(logWidth * (j - 1), m_canvas->width() - timeWidth).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "display/window_peer_list.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <torrent/rate.h>
|
||||
#include <torrent/data/block_transfer.h>
|
||||
@@ -9,11 +11,8 @@
|
||||
#include <torrent/peer/peer_info.h>
|
||||
|
||||
#include "core/download.h"
|
||||
#include "rak/algorithm.h"
|
||||
|
||||
#include "canvas.h"
|
||||
#include "utils.h"
|
||||
#include "window_peer_list.h"
|
||||
#include "display/canvas.h"
|
||||
#include "display/utils.h"
|
||||
|
||||
namespace display {
|
||||
|
||||
@@ -50,10 +49,10 @@ WindowPeerList::redraw() {
|
||||
|
||||
typedef std::pair<PList::iterator, PList::iterator> Range;
|
||||
|
||||
Range range = rak::advance_bidirectional(m_list->begin(),
|
||||
*m_focus != m_list->end() ? *m_focus : m_list->begin(),
|
||||
m_list->end(),
|
||||
m_canvas->height() - y);
|
||||
Range range = advance_bidirectional(m_list->begin(),
|
||||
*m_focus != m_list->end() ? *m_focus : m_list->begin(),
|
||||
m_list->end(),
|
||||
m_canvas->height() - y);
|
||||
|
||||
if (m_download->download()->file_list()->size_chunks() <= 0)
|
||||
throw std::logic_error("WindowPeerList::redraw() m_slotChunksTotal() returned invalid value");
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
#include "window_tracker_list.h"
|
||||
|
||||
#include <rak/algorithm.h>
|
||||
#include <rak/string_manip.h>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/tracker/tracker.h>
|
||||
#include <torrent/utils/string_manip.h>
|
||||
|
||||
#include "core/download.h"
|
||||
|
||||
@@ -37,7 +36,7 @@ WindowTrackerList::redraw() {
|
||||
if (tc_size == 0 || *m_focus >= tc_size)
|
||||
return;
|
||||
|
||||
auto range = rak::advance_bidirectional<unsigned int>(0, *m_focus, tc_size, (m_canvas->height() - 1) / 2);
|
||||
auto range = advance_bidirectional<unsigned int>(0, *m_focus, tc_size, (m_canvas->height() - 1) / 2);
|
||||
auto group = tc.at(range.first).group();
|
||||
|
||||
while (range.first != range.second) {
|
||||
@@ -52,9 +51,9 @@ WindowTrackerList::redraw() {
|
||||
if (pos < m_canvas->height()) {
|
||||
const char* state;
|
||||
|
||||
if (tracker.is_busy_not_scrape())
|
||||
if (tracker.is_requesting_not_scrape())
|
||||
state = "req ";
|
||||
else if (tracker.is_busy())
|
||||
else if (tracker.is_requesting())
|
||||
state = "scr ";
|
||||
else
|
||||
state = " ";
|
||||
@@ -63,7 +62,7 @@ WindowTrackerList::redraw() {
|
||||
|
||||
m_canvas->print(0, pos++, "%s Id: %s Counters: %uf / %us (%u) %s S/L/D: %u/%u/%u (%u/%u)",
|
||||
state,
|
||||
rak::copy_escape_html(tracker.tracker_id()).c_str(),
|
||||
torrent::utils::copy_escape_html_str(tracker.tracker_id()).c_str(),
|
||||
tracker_state.failed_counter(),
|
||||
tracker_state.success_counter(),
|
||||
tracker_state.scrape_counter(),
|
||||
@@ -80,7 +79,7 @@ WindowTrackerList::redraw() {
|
||||
m_canvas->set_attr(4, pos - 1, m_canvas->width(), is_focused() ? A_REVERSE : A_BOLD, COLOR_PAIR(0));
|
||||
}
|
||||
|
||||
if (tracker.is_busy()) {
|
||||
if (tracker.is_requesting()) {
|
||||
m_canvas->set_attr(0, pos - 2, 4, A_REVERSE, COLOR_PAIR(0));
|
||||
m_canvas->set_attr(0, pos - 1, 4, A_REVERSE, COLOR_PAIR(0));
|
||||
}
|
||||
|
||||
+34
-3
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <stdlib.h>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
rpc::ip_table_list ip_tables;
|
||||
@@ -14,16 +16,45 @@ expand_path(const std::string& path) {
|
||||
return std::string();
|
||||
|
||||
if (path[0] == '~') {
|
||||
if (path.size() < 2 || path[1] != '/')
|
||||
throw torrent::input_error("Could not expand ~ in session path, only ~/<path> is supported.");
|
||||
if (path.size() >= 2 && path[1] != '/')
|
||||
throw torrent::input_error("Could not expand ~ in path, only '~' or '~/...' is supported.");
|
||||
|
||||
const char* home = std::getenv("HOME");
|
||||
|
||||
if (home == nullptr || *home == '\0')
|
||||
throw torrent::input_error("Could not expand ~ in session path, HOME environment variable not set.");
|
||||
throw torrent::input_error("Could not expand ~ in path, HOME environment variable not set.");
|
||||
|
||||
return home + path.substr(1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
// Resolves a path to a canonical one with no symlinks or relative components,
|
||||
// so it can safely be handed to an external script. Returns an empty string if
|
||||
// the path does not name an existing file or directory.
|
||||
std::string
|
||||
resolve_path(const std::string& path) {
|
||||
if (path.empty())
|
||||
return std::string();
|
||||
|
||||
char* resolved = ::realpath(expand_path(path).c_str(), nullptr);
|
||||
|
||||
if (resolved == nullptr)
|
||||
return std::string();
|
||||
|
||||
std::string result(resolved);
|
||||
std::free(resolved);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string
|
||||
resolve_path_or_throw(const std::string& path) {
|
||||
auto result = resolve_path(path);
|
||||
|
||||
if (result.empty())
|
||||
throw torrent::input_error("Could not resolve path: '" + path + "'.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+13
-13
@@ -11,6 +11,8 @@ extern rpc::ip_table_list ip_tables;
|
||||
extern Control* control;
|
||||
|
||||
std::string expand_path(const std::string& path);
|
||||
std::string resolve_path(const std::string& path);
|
||||
std::string resolve_path_or_throw(const std::string& path);
|
||||
|
||||
namespace rpc {
|
||||
class SCgi;
|
||||
@@ -22,28 +24,26 @@ class SessionManager;
|
||||
|
||||
namespace scgi_thread {
|
||||
|
||||
torrent::utils::Thread* thread();
|
||||
std::thread::id thread_id();
|
||||
torrent::system::Thread* thread();
|
||||
std::thread::id thread_id();
|
||||
|
||||
void callback(void* target, std::function<void ()>&& fn);
|
||||
void cancel_callback(void* target);
|
||||
void cancel_callback_and_wait(void* target);
|
||||
void callback_interrupt(torrent::system::callback_id& id, std::function<void ()>&& fn);
|
||||
|
||||
rpc::SCgi* scgi();
|
||||
void set_scgi(rpc::SCgi* scgi);
|
||||
void set_rpc_log(const std::string& filename);
|
||||
rpc::SCgi* scgi();
|
||||
void set_scgi(rpc::SCgi* scgi);
|
||||
void set_rpc_log(const std::string& filename);
|
||||
|
||||
} // namespace torrent::scgi_thread
|
||||
|
||||
|
||||
namespace session_thread {
|
||||
|
||||
torrent::utils::Thread* thread();
|
||||
std::thread::id thread_id();
|
||||
torrent::system::Thread* thread();
|
||||
std::thread::id thread_id();
|
||||
|
||||
void callback(void* target, std::function<void ()>&& fn);
|
||||
void cancel_callback(void* target);
|
||||
void cancel_callback_and_wait(void* target);
|
||||
void callback(std::function<void ()>&& fn);
|
||||
void callback(torrent::system::callback_id& id, std::function<void ()>&& fn);
|
||||
void cancel_callback(torrent::system::callback_id& id);
|
||||
|
||||
session::SessionManager* manager();
|
||||
std::string session_path();
|
||||
|
||||
@@ -69,8 +69,6 @@ public:
|
||||
|
||||
bool pressed(int key);
|
||||
|
||||
void ignore(int key) { (*this)[key] = slot_void(); }
|
||||
|
||||
private:
|
||||
bool m_enabled{true};
|
||||
};
|
||||
|
||||
@@ -2,20 +2,26 @@
|
||||
|
||||
#include "input_event.h"
|
||||
|
||||
#include <torrent/common.h>
|
||||
#include <torrent/system/poll.h>
|
||||
|
||||
#include "display/attributes.h"
|
||||
|
||||
namespace input {
|
||||
|
||||
void
|
||||
InputEvent::insert(torrent::net::Poll* p) {
|
||||
p->open(this);
|
||||
p->insert_read(this);
|
||||
InputEvent::insert() {
|
||||
torrent::this_thread::poll()->open(this);
|
||||
torrent::this_thread::poll()->insert_read(this);
|
||||
}
|
||||
|
||||
void
|
||||
InputEvent::remove(torrent::net::Poll* p) {
|
||||
p->remove_read(this);
|
||||
p->close(this);
|
||||
InputEvent::remove() {
|
||||
if (!is_open())
|
||||
return;
|
||||
|
||||
torrent::this_thread::poll()->remove_and_close(this);
|
||||
set_file_descriptor(-1);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -32,6 +38,8 @@ InputEvent::event_write() {
|
||||
|
||||
void
|
||||
InputEvent::event_error() {
|
||||
torrent::this_thread::poll()->remove_and_close(this);
|
||||
set_file_descriptor(-1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,22 +2,20 @@
|
||||
#define RTORRENT_INPUT_INPUT_EVENT_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <torrent/event.h>
|
||||
#include <torrent/net/poll.h>
|
||||
#include <torrent/system/event.h>
|
||||
|
||||
namespace input {
|
||||
|
||||
class InputEvent : public torrent::Event {
|
||||
class InputEvent : public torrent::system::Event {
|
||||
public:
|
||||
typedef std::function<void (int)> slot_int;
|
||||
|
||||
InputEvent(int fd) { m_fileDesc = fd; }
|
||||
InputEvent(int fd) { set_file_descriptor(fd); }
|
||||
|
||||
const char* type_name() const override { return "input"; }
|
||||
|
||||
void insert(torrent::net::Poll* p);
|
||||
void remove(torrent::net::Poll* p);
|
||||
void insert();
|
||||
void remove();
|
||||
|
||||
void event_read() override;
|
||||
void event_write() override;
|
||||
|
||||
+49
-3
@@ -1,7 +1,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <rak/algorithm.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -10,6 +10,52 @@
|
||||
|
||||
#include "path_input.h"
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename _Value>
|
||||
struct compare_base {
|
||||
bool operator () (const _Value& complete, const _Value& base) const {
|
||||
return !complete.compare(0, base.size(), base);
|
||||
}
|
||||
};
|
||||
|
||||
// Count the number of elements from the start of the containers to
|
||||
// the first inequal element.
|
||||
template <typename _InputIter1, typename _InputIter2>
|
||||
typename std::iterator_traits<_InputIter1>::difference_type
|
||||
count_base(_InputIter1 __first1, _InputIter1 __last1,
|
||||
_InputIter2 __first2, _InputIter2 __last2) {
|
||||
|
||||
typename std::iterator_traits<_InputIter1>::difference_type __n = 0;
|
||||
|
||||
for ( ;__first1 != __last1 && __first2 != __last2; ++__first1, ++__first2, ++__n)
|
||||
if (*__first1 != *__first2)
|
||||
return __n;
|
||||
|
||||
return __n;
|
||||
}
|
||||
|
||||
template <typename _Return, typename _InputIter, typename _Ftor>
|
||||
_Return
|
||||
make_base(_InputIter __first, _InputIter __last, _Ftor __ftor) {
|
||||
if (__first == __last)
|
||||
return "";
|
||||
|
||||
_Return __base = __ftor(*__first++);
|
||||
|
||||
for ( ;__first != __last; ++__first) {
|
||||
auto __pos = count_base(__base.begin(), __base.end(),
|
||||
__ftor(*__first).begin(), __ftor(*__first).end());
|
||||
|
||||
if (__pos < (typename std::iterator_traits<_InputIter>::difference_type)__base.size())
|
||||
__base.resize(__pos);
|
||||
}
|
||||
|
||||
return __base;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace input {
|
||||
|
||||
bool
|
||||
@@ -54,12 +100,12 @@ PathInput::receive_do_complete() {
|
||||
entry.s_name += '/';
|
||||
}
|
||||
|
||||
range_type r = find_incomplete(dir, str().substr(dirEnd, get_pos()));
|
||||
range_type r = find_incomplete(dir, str().substr(dirEnd, get_pos() - dirEnd));
|
||||
|
||||
if (r.first == r.second)
|
||||
return; // Show some nice colors here.
|
||||
|
||||
std::string base = rak::make_base<std::string>(r.first, r.second, std::mem_fn(&utils::directory_entry::s_name));
|
||||
std::string base = make_base<std::string>(r.first, r.second, [](auto& de) { return de.s_name; });
|
||||
|
||||
// Clear the path after the cursor to make this code cleaner. It's
|
||||
// not really nessesary to add the complexity just because someone
|
||||
|
||||
+188
-200
@@ -11,6 +11,9 @@
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/data/chunk_utils.h>
|
||||
#include <torrent/net/fd.h>
|
||||
#include <torrent/net/http_stack.h>
|
||||
#include <torrent/runtime/memory_manager.h>
|
||||
#include <torrent/runtime/runtime.h>
|
||||
#include <torrent/utils/chrono.h>
|
||||
#include <torrent/utils/log.h>
|
||||
|
||||
@@ -20,7 +23,6 @@
|
||||
|
||||
#include "core/dht_manager.h"
|
||||
#include "core/download.h"
|
||||
#include "core/download_factory.h"
|
||||
#include "core/manager.h"
|
||||
#include "display/canvas.h"
|
||||
#include "display/window.h"
|
||||
@@ -30,15 +32,15 @@
|
||||
#include "rpc/command_scheduler_item.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "scgi/thread_scgi.h"
|
||||
#include "session/download_storer.h"
|
||||
#include "session/thread_session.h"
|
||||
#include "session/session_manager.h"
|
||||
#include "session/thread_session.h"
|
||||
#include "ui/root.h"
|
||||
#include "utils/directory.h"
|
||||
|
||||
#include "control.h"
|
||||
#include "command_helpers.h"
|
||||
#include "globals.h"
|
||||
#include "setup.h"
|
||||
#include "signal_handler.h"
|
||||
#include "option_parser.h"
|
||||
|
||||
@@ -50,37 +52,6 @@ void do_panic(int signum);
|
||||
void print_help();
|
||||
void initialize_commands();
|
||||
|
||||
void do_nothing() {}
|
||||
void do_nothing_str(const std::string&) {}
|
||||
|
||||
int
|
||||
parse_options(int argc, char** argv) {
|
||||
try {
|
||||
OptionParser optionParser;
|
||||
|
||||
// Converted.
|
||||
optionParser.insert_flag('h', std::bind(&print_help));
|
||||
optionParser.insert_flag('n', std::bind(&do_nothing_str, std::placeholders::_1));
|
||||
optionParser.insert_flag('D', std::bind(&do_nothing_str, std::placeholders::_1));
|
||||
optionParser.insert_flag('I', std::bind(&do_nothing_str, std::placeholders::_1));
|
||||
optionParser.insert_flag('K', std::bind(&do_nothing_str, std::placeholders::_1));
|
||||
|
||||
optionParser.insert_option('b', std::bind(&rpc::call_command_set_string, "network.bind_address.set", std::placeholders::_1));
|
||||
optionParser.insert_option('d', std::bind(&rpc::call_command_set_string, "directory.default.set", std::placeholders::_1));
|
||||
optionParser.insert_option('i', std::bind(&rpc::call_command_set_string, "ip", std::placeholders::_1));
|
||||
optionParser.insert_option('p', std::bind(&rpc::call_command_set_string, "network.port_range.set", std::placeholders::_1));
|
||||
optionParser.insert_option('s', std::bind(&rpc::call_command_set_string, "session", std::placeholders::_1));
|
||||
|
||||
optionParser.insert_option('O', std::bind(&rpc::parse_command_single_std, std::placeholders::_1));
|
||||
optionParser.insert_option_list('o', std::bind(&rpc::call_command_set_std_string, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
return optionParser.process(argc, argv);
|
||||
|
||||
} catch (torrent::input_error& e) {
|
||||
throw torrent::input_error("Failed to parse command line option: " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
initialize_rpc_slots() {
|
||||
rpc::rpc.slot_find_download() = [](const char* hash) {
|
||||
@@ -110,43 +81,6 @@ initialize_rpc_slots() {
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
load_session_torrents() {
|
||||
utils::Directory entries = session::DownloadStorer::get_formated_entries(session_thread::manager()->path());
|
||||
|
||||
for (const auto& entry : entries) {
|
||||
// We don't really support session torrents that are links. These
|
||||
// would be overwritten anyway on exit, and thus not really be
|
||||
// useful.
|
||||
if (!entry.is_file())
|
||||
continue;
|
||||
|
||||
core::DownloadFactory* f = new core::DownloadFactory(control->core());
|
||||
|
||||
// Replace with session torrent flag.
|
||||
f->set_session(true);
|
||||
f->set_init_load(true);
|
||||
f->slot_finished([f](){ delete f; });
|
||||
f->load(entries.path() + entry.s_name);
|
||||
f->commit();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
load_arg_torrents(char** first, char** last) {
|
||||
//std::for_each(begin, end, std::bind1st(std::mem_fun(&core::Manager::insert), &control->get_core()));
|
||||
for (; first != last; ++first) {
|
||||
core::DownloadFactory* f = new core::DownloadFactory(control->core());
|
||||
|
||||
// Replace with session torrent flag.
|
||||
f->set_start(true);
|
||||
f->set_init_load(true);
|
||||
f->slot_finished([f](){ delete f; });
|
||||
f->load(*first);
|
||||
f->commit();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
client_perform() {
|
||||
// Use throw exclusively.
|
||||
@@ -176,34 +110,50 @@ main(int argc, char** argv) {
|
||||
srandom(random_seed);
|
||||
srand48(random_seed);
|
||||
|
||||
torrent::initialize_main_thread();
|
||||
torrent::log_initialize();
|
||||
|
||||
control = new Control;
|
||||
// TODO: Create a fake thread object for initializing other processes and enabling logging.
|
||||
torrent::initialize_main_thread();
|
||||
|
||||
SignalHandler::set_ignore(SIGPIPE);
|
||||
SignalHandler::set_handler(SIGINT, std::bind(&Control::receive_normal_shutdown, control));
|
||||
SignalHandler::set_handler(SIGHUP, std::bind(&Control::receive_normal_shutdown, control));
|
||||
SignalHandler::set_handler(SIGTERM, std::bind(&Control::receive_quick_shutdown, control));
|
||||
SignalHandler::set_handler(SIGWINCH, std::bind(&display::Manager::force_redraw, control->display()));
|
||||
// Block SIGCHLD until all threads are created, then unblock on main-thread, to avoid SIGCHLD
|
||||
// interrupting other threads.
|
||||
//
|
||||
// This means only main-thread can fork and wait for child processes.
|
||||
|
||||
SignalHandler::set_block(SIGALRM);
|
||||
SignalHandler::set_block(SIGPIPE);
|
||||
SignalHandler::set_block(SIGCHLD);
|
||||
|
||||
// All signal handlers must restore errno if they return.
|
||||
SignalHandler::set_handler(SIGSEGV, std::bind(&do_panic, SIGSEGV));
|
||||
SignalHandler::set_handler(SIGILL, std::bind(&do_panic, SIGILL));
|
||||
SignalHandler::set_handler(SIGFPE, std::bind(&do_panic, SIGFPE));
|
||||
|
||||
// Limited list of commands with the following format:
|
||||
//
|
||||
// cat ~/.rtorrent.rc:
|
||||
//
|
||||
// # do:log.open_file=system,/usr/rakshasa/system.log
|
||||
// # do:log.add_output=system,system
|
||||
//
|
||||
parse_config_file(argc, argv, [](auto& path) {
|
||||
if (path.empty())
|
||||
return;
|
||||
|
||||
parse_config_file_comments(path);
|
||||
});
|
||||
|
||||
control = new Control;
|
||||
|
||||
SignalHandler::set_handler(SIGINT, std::bind(&Control::receive_normal_shutdown, control));
|
||||
SignalHandler::set_handler(SIGHUP, std::bind(&Control::receive_normal_shutdown, control));
|
||||
SignalHandler::set_handler(SIGTERM, std::bind(&Control::receive_quick_shutdown, control));
|
||||
SignalHandler::set_handler(SIGWINCH, std::bind(&display::Manager::force_redraw, control->display()));
|
||||
|
||||
SignalHandler::set_sigaction_handler(SIGBUS, &handle_sigbus);
|
||||
|
||||
// SIGUSR1 is used for interrupting polling, forcing the target
|
||||
// thread to process new non-socket events.
|
||||
//
|
||||
// LibTorrent uses sockets for this purpose on Solaris and other
|
||||
// platforms that do not properly pass signals to the target
|
||||
// threads. Use '--enable-interrupt-socket' when configuring
|
||||
// LibTorrent to enable this workaround.
|
||||
if (torrent::utils::Thread::should_handle_sigusr1())
|
||||
SignalHandler::set_handler(SIGUSR1, std::bind(&do_nothing));
|
||||
|
||||
torrent::log_add_group_output(torrent::LOG_NOTICE, "important");
|
||||
torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "important");
|
||||
torrent::log_add_group_output(torrent::LOG_NOTICE, "important");
|
||||
torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "important");
|
||||
|
||||
torrent::log_add_group_output(torrent::LOG_INFO, "complete");
|
||||
torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "complete");
|
||||
@@ -212,11 +162,13 @@ main(int argc, char** argv) {
|
||||
initialize_rpc_slots();
|
||||
|
||||
torrent::initialize();
|
||||
torrent::set_main_thread_slots(std::bind(&client_perform));
|
||||
torrent::main_thread::set_client_callback(&client_perform);
|
||||
|
||||
scgi::ThreadScgi::create_thread();
|
||||
session::ThreadSession::create_thread();
|
||||
|
||||
SignalHandler::set_unblock(SIGCHLD);
|
||||
|
||||
// Initialize option handlers after libtorrent to ensure
|
||||
// torrent::ConnectionManager* are valid etc.
|
||||
initialize_commands();
|
||||
@@ -261,7 +213,14 @@ main(int argc, char** argv) {
|
||||
"method.insert = event.download.hash_queued,multi|rlookup|static\n"
|
||||
|
||||
"method.set_key = event.download.inserted, 1_send_scrape, ((d.tracker.send_scrape,30))\n"
|
||||
"method.set_key = event.download.inserted_new, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) ),(d.save_full_session)}\n"
|
||||
"method.set_key = event.download.inserted_new, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) )}\n"
|
||||
// d.save_full_session runs LAST (~ prefix is ASCII 0x7E, sorts after all alphanumerics,
|
||||
// matching the existing ~_delete_tied precedent on event.download.erased) so the on-disk
|
||||
// snapshot includes custom fields written by other inserted_new handlers (addtime,
|
||||
// seedingtime, etc.). Was previously inside 1_prepare alongside view setup, which
|
||||
// snapshotted bencode before later handlers ran, so d.set_custom=addtime did not reach
|
||||
// .rtorrent until the next periodic resume save — a window where rtorrent restart lost them.
|
||||
"method.set_key = event.download.inserted_new, ~_save_full, ((d.save_full_session))\n"
|
||||
"method.set_key = event.download.inserted_session, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) )}\n"
|
||||
|
||||
"method.set_key = event.download.inserted, 1_prioritize_toc, \"branch=file.prioritize_toc=,{\\\"f.multicall=(file.prioritize_toc.first),f.prioritize_first.enable=\\\",\\\"f.multicall=(file.prioritize_toc.last),f.prioritize_last.enable=\\\",d.update_priorities=}\"\n"
|
||||
@@ -335,14 +294,12 @@ main(int argc, char** argv) {
|
||||
"view.filter = leeching,((and,((d.state)),((not,((d.complete))))))\n"
|
||||
"view.filter_on = leeching,event.download.resumed,event.download.paused,event.download.finished\n"
|
||||
|
||||
"schedule2 = view.main,10,10,((view.sort,main,20))\n"
|
||||
"schedule2 = view.name,10,10,((view.sort,name,20))\n"
|
||||
"schedule = view.main,10,10,((view.sort,main,20))\n"
|
||||
"schedule = view.name,10,10,((view.sort,name,20))\n"
|
||||
|
||||
"schedule2 = session_save,1200,1200,((session.save))\n"
|
||||
"schedule2 = low_diskspace,5,60,((close_low_diskspace,500M))\n"
|
||||
"schedule2 = prune_file_status,3600,86400,((system.file_status_cache.prune))\n"
|
||||
|
||||
"protocol.encryption.set=allow_incoming,prefer_plaintext,enable_retry\n"
|
||||
"schedule = session_save,1200,1200,((session.save))\n"
|
||||
"schedule = low_diskspace,5,60,((close_low_diskspace,500M))\n"
|
||||
"schedule = prune_file_status,3600,86400,((system.file_status_cache.prune))\n"
|
||||
|
||||
"ui.color.focus.set=reverse\n"
|
||||
);
|
||||
@@ -350,101 +307,136 @@ main(int argc, char** argv) {
|
||||
// Functions that might not get depracted as they are nice for
|
||||
// configuration files, and thus might do with just some
|
||||
// cleanup.
|
||||
CMD2_REDIRECT("upload_rate", "throttle.global_up.max_rate.set_kb");
|
||||
CMD2_REDIRECT("download_rate", "throttle.global_down.max_rate.set_kb");
|
||||
CMD_REDIRECT("upload_rate", "throttle.global_up.max_rate.set_kb");
|
||||
CMD_REDIRECT("download_rate", "throttle.global_down.max_rate.set_kb");
|
||||
|
||||
CMD2_REDIRECT("ratio.enable", "group.seeding.ratio.enable");
|
||||
CMD2_REDIRECT("ratio.disable", "group.seeding.ratio.disable");
|
||||
CMD2_REDIRECT("ratio.min", "group.seeding.ratio.min");
|
||||
CMD2_REDIRECT("ratio.max", "group.seeding.ratio.max");
|
||||
CMD2_REDIRECT("ratio.upload", "group.seeding.ratio.upload");
|
||||
CMD2_REDIRECT("ratio.min.set", "group.seeding.ratio.min.set");
|
||||
CMD2_REDIRECT("ratio.max.set", "group.seeding.ratio.max.set");
|
||||
CMD2_REDIRECT("ratio.upload.set", "group.seeding.ratio.upload.set");
|
||||
CMD_REDIRECT("ratio.enable", "group.seeding.ratio.enable");
|
||||
CMD_REDIRECT("ratio.disable", "group.seeding.ratio.disable");
|
||||
CMD_REDIRECT("ratio.min", "group.seeding.ratio.min");
|
||||
CMD_REDIRECT("ratio.max", "group.seeding.ratio.max");
|
||||
CMD_REDIRECT("ratio.upload", "group.seeding.ratio.upload");
|
||||
CMD_REDIRECT("ratio.min.set", "group.seeding.ratio.min.set");
|
||||
CMD_REDIRECT("ratio.max.set", "group.seeding.ratio.max.set");
|
||||
CMD_REDIRECT("ratio.upload.set", "group.seeding.ratio.upload.set");
|
||||
|
||||
CMD2_REDIRECT("encryption", "protocol.encryption.set");
|
||||
CMD2_REDIRECT("encoding_list", "encoding.add");
|
||||
CMD_REDIRECT("encryption", "protocol.encryption.set");
|
||||
|
||||
CMD2_REDIRECT("connection_leech", "protocol.connection.leech.set");
|
||||
CMD2_REDIRECT("connection_seed", "protocol.connection.seed.set");
|
||||
CMD_REDIRECT("check_hash", "pieces.hash.on_completion.set");
|
||||
|
||||
CMD2_REDIRECT("min_peers", "throttle.min_peers.normal.set");
|
||||
CMD2_REDIRECT("max_peers", "throttle.max_peers.normal.set");
|
||||
CMD2_REDIRECT("min_peers_seed", "throttle.min_peers.seed.set");
|
||||
CMD2_REDIRECT("max_peers_seed", "throttle.max_peers.seed.set");
|
||||
CMD_REDIRECT("connection_leech", "protocol.connection.leech.set");
|
||||
CMD_REDIRECT("connection_seed", "protocol.connection.seed.set");
|
||||
|
||||
CMD2_REDIRECT("min_uploads", "throttle.min_uploads.set");
|
||||
CMD2_REDIRECT("max_uploads", "throttle.max_uploads.set");
|
||||
CMD2_REDIRECT("min_downloads", "throttle.min_downloads.set");
|
||||
CMD2_REDIRECT("max_downloads", "throttle.max_downloads.set");
|
||||
CMD_REDIRECT("min_peers", "throttle.min_peers.normal.set");
|
||||
CMD_REDIRECT("max_peers", "throttle.max_peers.normal.set");
|
||||
CMD_REDIRECT("min_peers_seed", "throttle.min_peers.seed.set");
|
||||
CMD_REDIRECT("max_peers_seed", "throttle.max_peers.seed.set");
|
||||
|
||||
CMD2_REDIRECT("max_uploads_div", "throttle.max_uploads.div.set");
|
||||
CMD2_REDIRECT("max_uploads_global", "throttle.max_uploads.global.set");
|
||||
CMD2_REDIRECT("max_downloads_div", "throttle.max_downloads.div.set");
|
||||
CMD2_REDIRECT("max_downloads_global", "throttle.max_downloads.global.set");
|
||||
CMD_REDIRECT("min_uploads", "throttle.min_uploads.set");
|
||||
CMD_REDIRECT("max_uploads", "throttle.max_uploads.set");
|
||||
CMD_REDIRECT("min_downloads", "throttle.min_downloads.set");
|
||||
CMD_REDIRECT("max_downloads", "throttle.max_downloads.set");
|
||||
|
||||
CMD2_REDIRECT("max_memory_usage", "pieces.memory.max.set");
|
||||
CMD_REDIRECT("max_uploads_div", "throttle.max_uploads.div.set");
|
||||
CMD_REDIRECT("max_uploads_global", "throttle.max_uploads.global.set");
|
||||
CMD_REDIRECT("max_downloads_div", "throttle.max_downloads.div.set");
|
||||
CMD_REDIRECT("max_downloads_global", "throttle.max_downloads.global.set");
|
||||
|
||||
CMD2_REDIRECT("bind", "network.bind_address.set");
|
||||
CMD2_REDIRECT("ip", "network.local_address.set");
|
||||
CMD2_REDIRECT("port_range", "network.port_range.set");
|
||||
CMD_REDIRECT("directory", "directory.default.set");
|
||||
CMD_REDIRECT("session", "session.path.set");
|
||||
|
||||
CMD2_REDIRECT("dht", "dht.mode.set");
|
||||
CMD_REDIRECT_NO_EXPORT("port_range", "network.listen.port.range.set");
|
||||
CMD_REDIRECT_NO_EXPORT("scgi_port", "network.scgi.open_port");
|
||||
CMD_REDIRECT_NO_EXPORT("scgi_local", "network.scgi.open_local");
|
||||
|
||||
CMD2_REDIRECT("port_random", "network.port_random.set");
|
||||
CMD2_REDIRECT("proxy_address", "network.proxy_address.set");
|
||||
CMD_REDIRECT("to_gm_time", "convert.gm_time");
|
||||
CMD_REDIRECT("to_gm_date", "convert.gm_date");
|
||||
CMD_REDIRECT("to_time", "convert.time");
|
||||
CMD_REDIRECT("to_date", "convert.date");
|
||||
CMD_REDIRECT("to_elapsed_time", "convert.elapsed_time");
|
||||
CMD_REDIRECT("to_kb", "convert.kb");
|
||||
CMD_REDIRECT("to_mb", "convert.mb");
|
||||
CMD_REDIRECT("to_xb", "convert.xb");
|
||||
CMD_REDIRECT("to_throttle", "convert.throttle");
|
||||
|
||||
CMD2_REDIRECT("scgi_port", "network.scgi.open_port");
|
||||
CMD2_REDIRECT("scgi_local", "network.scgi.open_local");
|
||||
// TODO: Deprecate these at some point a while after 1.1 release.
|
||||
|
||||
CMD2_REDIRECT("directory", "directory.default.set");
|
||||
CMD2_REDIRECT("session", "session.path.set");
|
||||
CMD_REDIRECT("d.multicall2", "d.multicall");
|
||||
CMD_REDIRECT("network.open_sockets", "system.sockets.size");
|
||||
CMD_REDIRECT("network.max_open_sockets", "system.sockets.max_size");
|
||||
CMD_REDIRECT("network.max_open_sockets.set", "system.sockets.max_size.set");
|
||||
CMD_REDIRECT("network.http.proxy_address", "network.proxy.http");
|
||||
CMD_REDIRECT("network.http.proxy_address.set", "network.proxy.http.set");
|
||||
|
||||
CMD2_REDIRECT("check_hash", "pieces.hash.on_completion.set");
|
||||
rpc::rpc.mark_safe("d.multicall2");
|
||||
rpc::rpc.mark_safe("network.max_open_sockets");
|
||||
rpc::rpc.mark_safe("network.http.proxy_address");
|
||||
|
||||
CMD2_REDIRECT("key_layout", "keys.layout.set");
|
||||
CMD2_ANY_VALUE_V("network.http.max_total_connections.set", [](auto, auto) {
|
||||
lt_log_print(torrent::LOG_WARN, "network.http.max_total_connections.set is deprecated, use system.sockets.http.min_alloc.set instead.");
|
||||
});
|
||||
|
||||
CMD2_REDIRECT("to_gm_time", "convert.gm_time");
|
||||
CMD2_REDIRECT("to_gm_date", "convert.gm_date");
|
||||
CMD2_REDIRECT("to_time", "convert.time");
|
||||
CMD2_REDIRECT("to_date", "convert.date");
|
||||
CMD2_REDIRECT("to_elapsed_time", "convert.elapsed_time");
|
||||
CMD2_REDIRECT("to_kb", "convert.kb");
|
||||
CMD2_REDIRECT("to_mb", "convert.mb");
|
||||
CMD2_REDIRECT("to_xb", "convert.xb");
|
||||
CMD2_REDIRECT("to_throttle", "convert.throttle");
|
||||
CMD2_ANY_VALUE_V("network.max_open_files.set", [](auto, auto) {
|
||||
lt_log_print(torrent::LOG_WARN, "network.max_open_files.set is deprecated, use system.sockets.files.min_alloc.set instead.");
|
||||
});
|
||||
|
||||
CMD2_REDIRECT("torrent_list_layout", "ui.torrent_list.layout.set");
|
||||
// TODO: Keep d.directory_base for a while as it is widely used.
|
||||
CMD_REDIRECT("d.directory_base", "d.directory");
|
||||
CMD_REDIRECT("d.directory_base.set", "d.directory.base.set");
|
||||
|
||||
// Deprecate:
|
||||
|
||||
CMD2_VAR_STRING("dht.throttle.name", "deprecated");
|
||||
rpc::rpc.mark_safe("dht.throttle.name");
|
||||
|
||||
CMD2_REDIRECT("network.http.max_open", "network.http.max_total_connections");
|
||||
CMD2_REDIRECT("network.http.max_open.set", "network.http.max_total_connections.set");
|
||||
|
||||
// Deprecated commands. Don't use these anymore.
|
||||
//
|
||||
// It has been so long that we now re-create these commands with the new (old by now) command
|
||||
// call style, where the first argument is the target.
|
||||
rpc::rpc.mark_safe("d.directory_base");
|
||||
|
||||
// if (rpc::call_command_value("method.use_intermediate") == 1) {
|
||||
// CMD2_REDIRECT("execute", "execute2");
|
||||
|
||||
// CMD2_REDIRECT("schedule", "schedule2");
|
||||
// CMD2_REDIRECT("schedule_remove", "schedule_remove2");
|
||||
|
||||
// } else if (rpc::call_command_value("method.use_intermediate") == 2) {
|
||||
// Allow for use in config files, etc, just don't export it.
|
||||
// CMD2_REDIRECT_NO_EXPORT("execute", "execute2");
|
||||
|
||||
// CMD2_REDIRECT_NO_EXPORT("schedule", "schedule2");
|
||||
// CMD2_REDIRECT_NO_EXPORT("schedule_remove", "schedule_remove2");
|
||||
// }
|
||||
|
||||
// if (rpc::call_command_value("method.use_deprecated") == 1) {
|
||||
// }
|
||||
if (rpc::call_command_value("method.use_deprecated") == 1) {
|
||||
CMD_REDIRECT("execute2", "execute");
|
||||
CMD_REDIRECT("schedule2", "schedule");
|
||||
CMD_REDIRECT("schedule_remove2", "schedule.remove");
|
||||
|
||||
CMD_REDIRECT("bind", "network.bind_address.set");
|
||||
CMD_REDIRECT("ip", "network.local_address.set");
|
||||
|
||||
// TODO: Check if dht is on by default.
|
||||
CMD_REDIRECT("dht", "dht.mode.set");
|
||||
|
||||
CMD_REDIRECT("port_random", "network.listen.port.random.set");
|
||||
CMD_REDIRECT("proxy_address", "network.proxy.http.set");
|
||||
|
||||
CMD_REDIRECT("key_layout", "keys.layout.set");
|
||||
|
||||
CMD_REDIRECT("torrent_list_layout", "ui.torrent_list.layout.set");
|
||||
|
||||
CMD2_VAR_STRING("dht.throttle.name", "deprecated");
|
||||
rpc::rpc.mark_safe("dht.throttle.name");
|
||||
|
||||
CMD_REDIRECT("network.http.max_open", "network.http.max_total_connections");
|
||||
CMD_REDIRECT("network.http.max_open.set", "network.http.max_total_connections.set");
|
||||
|
||||
// Users should check their setups to see if they need to modify their use of these options.
|
||||
CMD_REDIRECT("max_memory_usage", "pieces.memory.max.set");
|
||||
|
||||
CMD_ANY_LIST("throttle.ip", []( auto, auto) {
|
||||
lt_log_print(torrent::LOG_WARN, "The 'throttle.ip' command is deprecated and does nothing.");
|
||||
return torrent::Object();
|
||||
});
|
||||
|
||||
CMD_ANY("network.port_open", [](auto, auto) {
|
||||
lt_log_print(torrent::LOG_WARN, "The 'network.port_open' command is deprecated and does nothing.");
|
||||
return torrent::Object();
|
||||
});
|
||||
CMD_ANY("network.port_open.set", [](auto, auto) {
|
||||
lt_log_print(torrent::LOG_WARN, "The 'network.port_open.set' command is deprecated and does nothing.");
|
||||
return torrent::Object();
|
||||
});
|
||||
|
||||
CMD_REDIRECT("network.port_random", "network.listen.port.random");
|
||||
CMD_REDIRECT("network.port_random.set", "network.listen.port.random.set");
|
||||
CMD_REDIRECT("network.port_range", "network.listen.port.range");
|
||||
CMD_REDIRECT("network.port_range.set", "network.listen.port.range.set");
|
||||
}
|
||||
|
||||
{
|
||||
auto fd = torrent::fd_open_family(torrent::fd_flag_stream, AF_INET6);
|
||||
@@ -459,54 +451,49 @@ main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
int firstArg = parse_options(argc, argv);
|
||||
int firstArg = parse_main_options(argc, argv);
|
||||
|
||||
if (OptionParser::has_flag('n', argc, argv)) {
|
||||
lt_log_print(torrent::LOG_WARN, "Ignoring rtorrent.rc.");
|
||||
} else {
|
||||
char* config_dir = std::getenv("XDG_CONFIG_HOME");
|
||||
char* home_dir = std::getenv("HOME");
|
||||
parse_config_file(argc, argv, [](auto& path) {
|
||||
if (path.empty()) {
|
||||
lt_log_print(torrent::LOG_WARN, "Ignoring rtorrent.rc.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (config_dir != NULL && config_dir[0] == '/' &&
|
||||
access((std::string(config_dir) + "/rtorrent/rtorrent.rc").c_str(), F_OK) != -1) {
|
||||
rpc::parse_command_single(rpc::make_target(), "try_import = " + std::string(config_dir) + "/rtorrent/rtorrent.rc");
|
||||
} else if (home_dir != NULL && access((std::string(home_dir) + "/.config/rtorrent/rtorrent.rc").c_str(), F_OK) != -1) {
|
||||
rpc::parse_command_single(rpc::make_target(), "try_import = ~/.config/rtorrent/rtorrent.rc");
|
||||
} else {
|
||||
rpc::parse_command_single(rpc::make_target(), "try_import = ~/.rtorrent.rc");
|
||||
}
|
||||
}
|
||||
rpc::parse_command_single(rpc::make_target(), "try_import=" + path);
|
||||
});
|
||||
|
||||
LT_LOG("seeded srandom and srand48 (seed:%u)", random_seed);
|
||||
LT_LOG("max memory usage: %" PRIu64, torrent::runtime::memory_manager()->max_memory_usage());
|
||||
|
||||
control->initialize();
|
||||
control->ui()->load_input_history();
|
||||
|
||||
// Load session torrents and perform scheduled tasks to ensure
|
||||
// session torrents are loaded before arg torrents.
|
||||
torrent::net_thread::http_stack()->set_user_agent(USER_AGENT);
|
||||
torrent::runtime::initialize_network();
|
||||
|
||||
// Load session torrents and perform scheduled tasks to ensure session torrents are loaded
|
||||
// before arg torrents.
|
||||
control->dht_manager()->set_auto_if_untouched_and_has_session();
|
||||
control->dht_manager()->load_dht_cache();
|
||||
load_session_torrents();
|
||||
|
||||
// TODO: Check if this is required.
|
||||
// rak::priority_queue_perform(&taskScheduler, cachedTime);
|
||||
|
||||
load_session_torrents(session_thread::manager()->path());
|
||||
load_arg_torrents(argv + firstArg, argv + argc);
|
||||
|
||||
// Make sure we update the display before any scheduled tasks can
|
||||
// run, so that loading of torrents doesn't look like it hangs on
|
||||
// startup.
|
||||
// Make sure we update the display before any scheduled tasks can run, so that loading of
|
||||
// torrents doesn't look like it hangs on startup.
|
||||
control->display()->adjust_layout();
|
||||
control->display()->receive_update();
|
||||
|
||||
rpc::commands.call_catch("event.system.startup_done", rpc::make_target(), "startup_done", "System startup_done event action failed: ");
|
||||
|
||||
torrent::utils::Thread::self()->event_loop();
|
||||
torrent::system::Thread::self()->event_loop();
|
||||
|
||||
control->core()->download_list()->session_save();
|
||||
control->cleanup();
|
||||
|
||||
} catch (torrent::internal_error& e) {
|
||||
control->cleanup_exception();
|
||||
if (control != nullptr)
|
||||
control->cleanup_exception();
|
||||
|
||||
std::cout << "rtorrent: caught torrent::internal_error: "
|
||||
<< e.what() << std::endl
|
||||
@@ -519,7 +506,8 @@ main(int argc, char** argv) {
|
||||
return -1;
|
||||
|
||||
} catch (std::exception& e) {
|
||||
control->cleanup_exception();
|
||||
if (control != nullptr)
|
||||
control->cleanup_exception();
|
||||
|
||||
std::cout << "rtorrent: caught" << typeid(e).name() << " : " << e.what() << std::endl;
|
||||
|
||||
@@ -564,8 +552,6 @@ handle_sigbus(int signum, siginfo_t* sa, [[maybe_unused]] void* ptr) {
|
||||
#else
|
||||
output << "Stack dump not enabled." << std::endl;
|
||||
#endif
|
||||
output << std::endl << "Error: " << std::strerror(sa->si_errno) << std::endl;
|
||||
|
||||
const char* signal_reason;
|
||||
|
||||
switch (sa->si_code) {
|
||||
@@ -616,7 +602,9 @@ do_panic(int signum) {
|
||||
// Use the default signal handler in the future to avoid infinit
|
||||
// loops.
|
||||
SignalHandler::set_default(signum);
|
||||
display::Canvas::cleanup();
|
||||
|
||||
if (control != nullptr)
|
||||
display::Canvas::cleanup();
|
||||
|
||||
std::stringstream output;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "option_parser.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
@@ -8,8 +10,6 @@
|
||||
#include <stdexcept>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "option_parser.h"
|
||||
|
||||
void
|
||||
OptionParser::insert_flag(char c, slot_string s) {
|
||||
m_container[c].m_slot = s;
|
||||
|
||||
@@ -1,37 +1,3 @@
|
||||
// rTorrent - BitTorrent client
|
||||
// Copyright (C) 2005-2011, Jari Sundell
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for
|
||||
// all of the code used other than OpenSSL. If you modify file(s)
|
||||
// with this exception, you may extend this exception to your version
|
||||
// of the file(s), but you are not obligated to do so. If you do not
|
||||
// wish to do so, delete this exception statement from your version.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <sundell.software@gmail.com>
|
||||
|
||||
|
||||
#ifndef RTORRENT_OPTION_PARSER_H
|
||||
#define RTORRENT_OPTION_PARSER_H
|
||||
|
||||
|
||||
+39
-31
@@ -1,36 +1,38 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "core/download.h"
|
||||
#include "parse.h"
|
||||
|
||||
#include "command.h"
|
||||
|
||||
#define COMMAND_BASE_TEMPLATE_DEFINE(func_name) \
|
||||
template const torrent::Object func_name<target_type>(command_base* rawCommand, target_type target, const torrent::Object& args); \
|
||||
template const torrent::Object func_name<core::Download*>(command_base* rawCommand, target_type target, const torrent::Object& args); \
|
||||
template const torrent::Object func_name<torrent::Peer*>(command_base* rawCommand, target_type target, const torrent::Object& args); \
|
||||
template const torrent::Object func_name<torrent::tracker::Tracker*>(command_base* rawCommand, target_type target, const torrent::Object& args); \
|
||||
template const torrent::Object func_name<torrent::File*>(command_base* rawCommand, target_type target, const torrent::Object& args); \
|
||||
template const torrent::Object func_name<torrent::FileListIterator*>(command_base* rawCommand, target_type target, const torrent::Object& args);
|
||||
template const torrent::Object func_name<target_type>(command_base* command_raw, target_type target, const torrent::Object& args); \
|
||||
template const torrent::Object func_name<core::Download*>(command_base* command_raw, target_type target, const torrent::Object& args); \
|
||||
template const torrent::Object func_name<torrent::Peer*>(command_base* command_raw, target_type target, const torrent::Object& args); \
|
||||
template const torrent::Object func_name<torrent::tracker::Tracker*>(command_base* command_raw, target_type target, const torrent::Object& args); \
|
||||
template const torrent::Object func_name<torrent::File*>(command_base* command_raw, target_type target, const torrent::Object& args); \
|
||||
template const torrent::Object func_name<torrent::FileListIterator*>(command_base* command_raw, target_type target, const torrent::Object& args);
|
||||
|
||||
namespace rpc {
|
||||
|
||||
template <typename T> const torrent::Object
|
||||
command_base_call(command_base* rawCommand, target_type target, const torrent::Object& args) {
|
||||
command_base_call(command_base* command_raw, target_type target, const torrent::Object& args) {
|
||||
if (!is_target_compatible<T>(target))
|
||||
throw torrent::input_error("Target of wrong type to command.");
|
||||
throw torrent::input_error("Target of wrong type to generic command.");
|
||||
|
||||
return command_base::_call<typename command_function<T>::type, T>(rawCommand, target, args);
|
||||
return command_base::_call<typename command_function<T>::type, T>(command_raw, target, args);
|
||||
}
|
||||
|
||||
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call);
|
||||
|
||||
template <typename T> const torrent::Object
|
||||
command_base_call_value_base(command_base* rawCommand, target_type target, const torrent::Object& rawArgs, int base, int unit) {
|
||||
command_base_call_value_base(command_base* command_raw, target_type target, const torrent::Object& args_raw, int base, int unit) {
|
||||
if (!is_target_compatible<T>(target))
|
||||
throw torrent::input_error("Target of wrong type to command.");
|
||||
throw torrent::input_error("Target of wrong type to value command.");
|
||||
|
||||
const torrent::Object& arg = convert_to_single_argument(rawArgs);
|
||||
auto& arg = convert_to_single_argument(args_raw);
|
||||
|
||||
if (arg.type() == torrent::Object::TYPE_STRING) {
|
||||
torrent::Object::value_type val;
|
||||
@@ -38,55 +40,61 @@ command_base_call_value_base(command_base* rawCommand, target_type target, const
|
||||
if (!parse_whole_value_nothrow(arg.as_string().c_str(), &val, base, unit))
|
||||
throw torrent::input_error("Not a value.");
|
||||
|
||||
return command_base::_call<typename command_value_function<T>::type, T>(rawCommand, target, val);
|
||||
return command_base::_call<typename command_value_function<T>::type, T>(command_raw, target, val);
|
||||
}
|
||||
|
||||
return command_base::_call<typename command_value_function<T>::type, T>(rawCommand, target, unit * arg.as_value());
|
||||
auto value = arg.as_value();
|
||||
|
||||
if (value > std::numeric_limits<int64_t>::max() / unit ||
|
||||
value < std::numeric_limits<int64_t>::min() / unit)
|
||||
throw torrent::input_error("Value out of range.");
|
||||
|
||||
return command_base::_call<typename command_value_function<T>::type, T>(command_raw, target, unit * value);
|
||||
}
|
||||
|
||||
template <typename T> const torrent::Object
|
||||
command_base_call_value(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) {
|
||||
return command_base_call_value_base<T>(rawCommand, target, rawArgs, 0, 1);
|
||||
command_base_call_value(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
|
||||
return command_base_call_value_base<T>(command_raw, target, args_raw, 0, 1);
|
||||
}
|
||||
|
||||
template <typename T> const torrent::Object
|
||||
command_base_call_value_kb(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) {
|
||||
return command_base_call_value_base<T>(rawCommand, target, rawArgs, 0, 1024);
|
||||
command_base_call_value_kb(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
|
||||
return command_base_call_value_base<T>(command_raw, target, args_raw, 0, 1024);
|
||||
}
|
||||
|
||||
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_value);
|
||||
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_value_kb);
|
||||
|
||||
template <typename T> const torrent::Object
|
||||
command_base_call_string(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) {
|
||||
command_base_call_string(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
|
||||
if (!is_target_compatible<T>(target))
|
||||
throw torrent::input_error("Target of wrong type to command.");
|
||||
throw torrent::input_error("Target of wrong type to string command.");
|
||||
|
||||
const torrent::Object& arg = convert_to_single_argument(rawArgs);
|
||||
auto& arg = convert_to_single_argument(args_raw);
|
||||
|
||||
if (arg.type() == torrent::Object::TYPE_RAW_STRING)
|
||||
return command_base::_call<typename command_string_function<T>::type, T>(rawCommand, target, arg.as_raw_string().as_string());
|
||||
return command_base::_call<typename command_string_function<T>::type, T>(command_raw, target, arg.as_raw_string().as_string());
|
||||
|
||||
return command_base::_call<typename command_string_function<T>::type, T>(rawCommand, target, arg.as_string());
|
||||
return command_base::_call<typename command_string_function<T>::type, T>(command_raw, target, arg.as_string());
|
||||
}
|
||||
|
||||
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_string);
|
||||
|
||||
template <typename T> const torrent::Object
|
||||
command_base_call_list(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) {
|
||||
command_base_call_list(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
|
||||
if (!is_target_compatible<T>(target))
|
||||
throw torrent::input_error("Target of wrong type to command.");
|
||||
throw torrent::input_error("Target of wrong type to list command.");
|
||||
|
||||
if (rawArgs.type() != torrent::Object::TYPE_LIST) {
|
||||
if (args_raw.type() != torrent::Object::TYPE_LIST) {
|
||||
torrent::Object::list_type arg;
|
||||
|
||||
if (!rawArgs.is_empty())
|
||||
arg.push_back(rawArgs);
|
||||
|
||||
return command_base::_call<typename command_list_function<T>::type, T>(rawCommand, target, arg);
|
||||
if (!args_raw.is_empty())
|
||||
arg.push_back(args_raw);
|
||||
|
||||
return command_base::_call<typename command_list_function<T>::type, T>(command_raw, target, arg);
|
||||
}
|
||||
|
||||
return command_base::_call<typename command_list_function<T>::type, T>(rawCommand, target, rawArgs.as_list());
|
||||
return command_base::_call<typename command_list_function<T>::type, T>(command_raw, target, args_raw.as_list());
|
||||
}
|
||||
|
||||
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_list);
|
||||
|
||||
+81
-52
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <new>
|
||||
#include <torrent/common.h>
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/data/file_list_iterator.h>
|
||||
@@ -28,7 +29,6 @@ struct target_wrapper<void> {
|
||||
typedef no_type* cleaned_type;
|
||||
};
|
||||
|
||||
// Since c++0x isn't out yet...
|
||||
template <typename T1, typename T2, typename T3>
|
||||
struct rt_triple : private std::pair<T1, T2> {
|
||||
typedef std::pair<T1, T2> base_type;
|
||||
@@ -55,15 +55,11 @@ struct rt_triple : private std::pair<T1, T2> {
|
||||
base_type(src.first, src.second), third(src.third) {}
|
||||
};
|
||||
|
||||
// Since it gets used so many places we might as well put it in the
|
||||
// rpc namespace.
|
||||
//typedef std::pair<int, void*> target_type;
|
||||
typedef rt_triple<int, void*, void*> target_type;
|
||||
|
||||
class command_base;
|
||||
|
||||
typedef const torrent::Object (*command_base_call_type)(command_base*, target_type, const torrent::Object&);
|
||||
typedef std::function<torrent::Object (target_type, const torrent::Object&)> base_function;
|
||||
using target_type = rt_triple<int, void*, void*>;
|
||||
using base_function = std::function<torrent::Object (target_type, const torrent::Object&)>;
|
||||
using command_base_call_type = const torrent::Object (command_base*, target_type, const torrent::Object&);
|
||||
|
||||
template <typename tmpl> struct command_base_is_valid {};
|
||||
template <command_base_call_type tmpl_func> struct command_base_is_type {};
|
||||
@@ -87,17 +83,18 @@ public:
|
||||
|
||||
typedef const torrent::Object (*download_pair_slot) (command_base*, core::Download*, core::Download*, const torrent::Object&);
|
||||
|
||||
static const int target_generic = 0;
|
||||
static const int target_any = 1;
|
||||
static const int target_download = 2;
|
||||
static const int target_peer = 3;
|
||||
static const int target_tracker = 4;
|
||||
static const int target_file = 5;
|
||||
static const int target_file_itr = 6;
|
||||
static constexpr int target_generic = 0;
|
||||
static constexpr int target_any = 1;
|
||||
static constexpr int target_download = 2;
|
||||
static constexpr int target_peer = 3;
|
||||
static constexpr int target_tracker = 4;
|
||||
static constexpr int target_file = 5;
|
||||
static constexpr int target_file_itr = 6;
|
||||
static constexpr int target_download_pair = 7;
|
||||
|
||||
static const int target_download_pair = 7;
|
||||
static constexpr unsigned int max_arguments = 10;
|
||||
|
||||
static const unsigned int max_arguments = 10;
|
||||
static constexpr std::size_t optimal_alignment = std::max(alignof(std::max_align_t), alignof(base_function));
|
||||
|
||||
struct stack_type {
|
||||
torrent::Object* begin() { return reinterpret_cast<torrent::Object*>(buffer); }
|
||||
@@ -111,13 +108,51 @@ public:
|
||||
|
||||
static stack_type* from_data(char* data) { return reinterpret_cast<stack_type*>(data); }
|
||||
|
||||
char buffer[sizeof(torrent::Object) * max_arguments];
|
||||
alignas(optimal_alignment) char buffer[sizeof(torrent::Object) * max_arguments];
|
||||
};
|
||||
|
||||
command_base() { new (&_pod<base_function>()) base_function(); }
|
||||
command_base(const command_base& src) { new (&_pod<base_function>()) base_function(src._pod<base_function>()); }
|
||||
command_base() : m_copy_helper(nullptr), m_dest_helper(nullptr) {}
|
||||
|
||||
~command_base() { _pod<base_function>().~base_function(); }
|
||||
command_base(const command_base& src) {
|
||||
m_copy_helper = src.m_copy_helper;
|
||||
m_dest_helper = src.m_dest_helper;
|
||||
if (src.m_copy_helper)
|
||||
src.m_copy_helper(t_pod, src.t_pod);
|
||||
}
|
||||
|
||||
command_base& operator=(const command_base& src) {
|
||||
if (this != &src) {
|
||||
if (m_dest_helper) m_dest_helper(t_pod);
|
||||
m_copy_helper = src.m_copy_helper;
|
||||
m_dest_helper = src.m_dest_helper;
|
||||
if (src.m_copy_helper)
|
||||
src.m_copy_helper(t_pod, src.t_pod);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
command_base(command_base&& src) noexcept {
|
||||
m_copy_helper = src.m_copy_helper;
|
||||
m_dest_helper = src.m_dest_helper;
|
||||
if (src.m_copy_helper)
|
||||
src.m_copy_helper(t_pod, src.t_pod);
|
||||
}
|
||||
|
||||
command_base& operator=(command_base&& src) noexcept {
|
||||
if (this != &src) {
|
||||
if (m_dest_helper) m_dest_helper(t_pod);
|
||||
m_copy_helper = src.m_copy_helper;
|
||||
m_dest_helper = src.m_dest_helper;
|
||||
if (src.m_copy_helper)
|
||||
src.m_copy_helper(t_pod, src.t_pod);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~command_base() {
|
||||
if (m_dest_helper)
|
||||
m_dest_helper(t_pod);
|
||||
}
|
||||
|
||||
static torrent::Object* argument(unsigned int index) { return current_stack.begin() + index; }
|
||||
static torrent::Object& argument_ref(unsigned int index) { return *(current_stack.begin() + index); }
|
||||
@@ -132,55 +167,51 @@ public:
|
||||
static void pop_stack(stack_type* stack, torrent::Object* last_stack);
|
||||
|
||||
template <typename T>
|
||||
void set_function(T s, [[maybe_unused]] int value = command_base_is_valid<T>::value) { _pod<T>() = s; }
|
||||
void set_function(T s, [[maybe_unused]] int value = command_base_is_valid<T>::value) {
|
||||
static_assert(sizeof(T) <= sizeof(t_pod), "t_pod storage overflow");
|
||||
static_assert(optimal_alignment >= alignof(T), "t_pod alignment insufficient for type");
|
||||
|
||||
if (m_dest_helper)
|
||||
m_dest_helper(t_pod);
|
||||
|
||||
::new (t_pod) T(std::move(s));
|
||||
|
||||
m_copy_helper = [](void* dest, const void* src) {
|
||||
::new (dest) T(*static_cast<const T*>(src));
|
||||
};
|
||||
m_dest_helper = [](void* ptr) {
|
||||
static_cast<T*>(ptr)->~T();
|
||||
};
|
||||
}
|
||||
|
||||
template <command_base_call_type T>
|
||||
void set_function_2(typename command_base_is_type<T>::type s, [[maybe_unused]] int value = command_base_is_valid<typename command_base_is_type<T>::type>::value) {
|
||||
_pod<typename command_base_is_type<T>::type>() = s;
|
||||
set_function<typename command_base_is_type<T>::type>(std::move(s));
|
||||
}
|
||||
|
||||
// The std::function object in GCC is castable between types with a
|
||||
// pointer to a struct of ctor/dtor/calls for non-POD slots. As such
|
||||
// it should be safe to cast between different std::function
|
||||
// template types, yet what the C++0x standard will say about this I
|
||||
// have no idea atm.
|
||||
template <typename tmpl> tmpl& _pod() { return reinterpret_cast<tmpl&>(t_pod); }
|
||||
template <typename tmpl> const tmpl& _pod() const { return reinterpret_cast<const tmpl&>(t_pod); }
|
||||
|
||||
template <typename Func, typename T, typename Args>
|
||||
static const torrent::Object _call(command_base* cmd, target_type target, Args args);
|
||||
|
||||
command_base& operator = (const command_base& src) {
|
||||
_pod<base_function>() = src._pod<base_function>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
// For use by functions that need to use placeholders to arguments
|
||||
// within commands. E.d. callable command strings where one of the
|
||||
// arguments within the command needs to be supplied by the caller.
|
||||
using copy_fn_t = void (*)(void* dest, const void* src);
|
||||
using dest_fn_t = void (*)(void* ptr);
|
||||
|
||||
#ifdef HAVE_CXX11
|
||||
union {
|
||||
base_function t_pod;
|
||||
// char t_pod[sizeof(base_function)];
|
||||
};
|
||||
#else
|
||||
union {
|
||||
char t_pod[sizeof(base_function)];
|
||||
};
|
||||
#endif
|
||||
alignas(optimal_alignment) char t_pod[sizeof(base_function)];
|
||||
|
||||
copy_fn_t m_copy_helper;
|
||||
dest_fn_t m_dest_helper;
|
||||
};
|
||||
|
||||
template <typename T1 = void, typename T2 = void>
|
||||
struct target_type_id {
|
||||
// Nothing here, so we cause an error.
|
||||
};
|
||||
|
||||
template <typename T> inline bool
|
||||
is_target_compatible(const target_type& target) { return target.first == target_type_id<T>::value; }
|
||||
|
||||
// Splitting pairs into separate targets.
|
||||
inline bool is_target_pair(const target_type& target) { return target.first >= command_base::target_download_pair; }
|
||||
|
||||
template <typename T> inline T
|
||||
@@ -203,7 +234,7 @@ command_base::_call(command_base* cmd, target_type target, Args args) {
|
||||
|
||||
#define COMMAND_BASE_TEMPLATE_TYPE(func_type, func_parm) \
|
||||
template <typename T, int proper = target_type_id<T>::proper_type> struct func_type { typedef std::function<func_parm> type; }; \
|
||||
\
|
||||
\
|
||||
template <> struct command_base_is_valid<func_type<target_type>::type> { static const int value = 1; }; \
|
||||
template <> struct command_base_is_valid<func_type<core::Download*>::type> { static const int value = 1; }; \
|
||||
template <> struct command_base_is_valid<func_type<torrent::Peer*>::type> { static const int value = 1; }; \
|
||||
@@ -211,8 +242,6 @@ command_base::_call(command_base* cmd, target_type target, Args args) {
|
||||
template <> struct command_base_is_valid<func_type<torrent::File*>::type> { static const int value = 1; }; \
|
||||
template <> struct command_base_is_valid<func_type<torrent::FileListIterator*>::type> { static const int value = 1; };
|
||||
|
||||
// template <typename Q> struct command_base_is_valid<typename func_type<Q>::type > { static const int value = 1; };
|
||||
|
||||
COMMAND_BASE_TEMPLATE_TYPE(command_function, torrent::Object (T, const torrent::Object&));
|
||||
COMMAND_BASE_TEMPLATE_TYPE(command_value_function, torrent::Object (T, const torrent::Object::value_type&));
|
||||
COMMAND_BASE_TEMPLATE_TYPE(command_string_function, torrent::Object (T, const std::string&));
|
||||
@@ -220,7 +249,7 @@ COMMAND_BASE_TEMPLATE_TYPE(command_list_function, torrent::Object (T, const to
|
||||
|
||||
#define COMMAND_BASE_TEMPLATE_CALL(func_name, func_type) \
|
||||
template <typename T> const torrent::Object func_name(command_base* rawCommand, target_type target, const torrent::Object& args); \
|
||||
\
|
||||
\
|
||||
template <> struct command_base_is_type<func_name<target_type> > { static const int value = 1; typedef func_type<target_type>::type type; }; \
|
||||
template <> struct command_base_is_type<func_name<core::Download*> > { static const int value = 1; typedef func_type<core::Download*>::type type; }; \
|
||||
template <> struct command_base_is_type<func_name<torrent::Peer*> > { static const int value = 1; typedef func_type<torrent::Peer*>::type type; }; \
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef RTORRENT_RPC_COMMAND_MAP_H
|
||||
#define RTORRENT_RPC_COMMAND_MAP_H
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
@@ -104,6 +105,10 @@ inline target_type make_target(T target) {
|
||||
return target_type((int)target_type_id<T>::value, target);
|
||||
}
|
||||
|
||||
inline target_type make_target(const std::shared_ptr<core::Download>& target) {
|
||||
return make_target(target.get());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline target_type make_target_pair(T target1, T target2) {
|
||||
return target_type((int)target_type_id<T, T>::value, target1, target2);
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <time.h>
|
||||
#include <rak/string_manip.h>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/utils/chrono.h>
|
||||
|
||||
@@ -62,14 +61,20 @@ CommandScheduler::call_item(value_type item) {
|
||||
// Remove the item before calling the command if it should be
|
||||
// removed.
|
||||
|
||||
std::string key = item->key();
|
||||
|
||||
try {
|
||||
rpc::call_object(item->command());
|
||||
|
||||
} catch (torrent::input_error& e) {
|
||||
if (m_slotErrorMessage)
|
||||
m_slotErrorMessage("Scheduled command failed: " + item->key() + ": " + e.what());
|
||||
m_slotErrorMessage("Scheduled command failed: " + key + ": " + e.what());
|
||||
}
|
||||
|
||||
// The command is allowed to erase or replace this item, which deletes it.
|
||||
if (std::find(begin(), end(), item) == end())
|
||||
return;
|
||||
|
||||
// Still schedule if we caught a torrrent::input_error?
|
||||
auto next = item->next_time_scheduled();
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/utils/scheduler.h>
|
||||
#include <torrent/system/scheduler.h>
|
||||
|
||||
namespace rpc {
|
||||
|
||||
@@ -43,7 +43,7 @@ private:
|
||||
uint32_t m_interval{};
|
||||
std::chrono::microseconds m_time_scheduled;
|
||||
|
||||
torrent::utils::SchedulerEntry m_task;
|
||||
torrent::system::SchedulerEntry m_task;
|
||||
|
||||
// Flags for various things.
|
||||
};
|
||||
|
||||
+58
-126
@@ -1,16 +1,24 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <torrent/utils/thread.h>
|
||||
#include <memory>
|
||||
|
||||
#include "exec_file.h"
|
||||
#include "parse.h"
|
||||
#include "rpc/exec_file.h"
|
||||
|
||||
// #include <cassert>
|
||||
// #include <cerrno>
|
||||
// #include <cstring>
|
||||
// #include <fcntl.h>
|
||||
// #include <spawn.h>
|
||||
// #include <string>
|
||||
#include <unistd.h>
|
||||
// #include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
// #include <torrent/net/fd.h>
|
||||
#include <torrent/system/thread.h>
|
||||
#include <torrent/system/spawn_process.h>
|
||||
#include <torrent/system/types.h>
|
||||
|
||||
#include "rpc/parse.h"
|
||||
|
||||
namespace rpc {
|
||||
|
||||
@@ -18,133 +26,56 @@ namespace rpc {
|
||||
|
||||
int
|
||||
ExecFile::execute(const char* file, char* const* argv, int flags) {
|
||||
// Write the executed command and its parameters to the log fd.
|
||||
[[maybe_unused]] int result;
|
||||
torrent::system::SpawnProcess spawn_process;
|
||||
|
||||
spawn_process.set_log_fd(m_log_fd);
|
||||
spawn_process.set_background(flags & flag_background);
|
||||
spawn_process.set_capture_output(flags & flag_capture);
|
||||
|
||||
if (m_log_fd != -1) {
|
||||
for (char* const* itr = argv; *itr != NULL; itr++) {
|
||||
if (itr == argv)
|
||||
result = write(m_log_fd, "\n---\n", sizeof("\n---\n"));
|
||||
else
|
||||
result = write(m_log_fd, " ", 1);
|
||||
std::vector<struct iovec> iovecs;
|
||||
iovecs.reserve(32);
|
||||
iovecs.push_back({const_cast<char*>("\n---\n"), 5});
|
||||
|
||||
result = write(m_log_fd, *itr, std::strlen(*itr));
|
||||
for (auto* itr = argv; *itr != nullptr; itr++) {
|
||||
if (itr != argv)
|
||||
iovecs.push_back({const_cast<char*>(" "), 1});
|
||||
|
||||
iovecs.push_back({*itr, std::strlen(*itr)});
|
||||
}
|
||||
|
||||
result = write(m_log_fd, "\n---\n", sizeof("\n---\n"));
|
||||
iovecs.push_back({const_cast<char*>("\n---\n"), 5});
|
||||
|
||||
[[maybe_unused]] int result = ::writev(m_log_fd, iovecs.data(), iovecs.size());
|
||||
}
|
||||
|
||||
int pipeFd[2];
|
||||
|
||||
if ((flags & flag_capture) && pipe(pipeFd))
|
||||
throw torrent::input_error("ExecFile::execute(...) Pipe creation failed.");
|
||||
|
||||
pid_t childPid = fork();
|
||||
|
||||
if (childPid == -1) {
|
||||
if (flags & flag_capture) {
|
||||
::close(pipeFd[0]);
|
||||
::close(pipeFd[1]);
|
||||
}
|
||||
throw torrent::input_error("ExecFile::execute(...) Fork failed.");
|
||||
}
|
||||
|
||||
if (childPid == 0) {
|
||||
if (flags & flag_background) {
|
||||
pid_t detached_pid = fork();
|
||||
|
||||
if (detached_pid == -1)
|
||||
_exit(-1);
|
||||
|
||||
if (detached_pid != 0) {
|
||||
if (m_log_fd != -1)
|
||||
result = write(m_log_fd, "\n--- Background task ---\n", sizeof("\n--- Background task ---\n"));
|
||||
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
m_log_fd = -1;
|
||||
flags &= ~flag_capture;
|
||||
}
|
||||
|
||||
int devNull = open("/dev/null", O_RDWR);
|
||||
|
||||
if (devNull != -1)
|
||||
dup2(devNull, 0);
|
||||
else
|
||||
::close(0);
|
||||
|
||||
if (flags & flag_capture)
|
||||
dup2(pipeFd[1], 1);
|
||||
else if (m_log_fd != -1)
|
||||
dup2(m_log_fd, 1);
|
||||
else if (devNull != -1)
|
||||
dup2(devNull, 1);
|
||||
else
|
||||
::close(1);
|
||||
|
||||
if (m_log_fd != -1)
|
||||
dup2(m_log_fd, 2);
|
||||
else if (devNull != -1)
|
||||
dup2(devNull, 2);
|
||||
else
|
||||
::close(2);
|
||||
|
||||
// Close all fd's.
|
||||
for (int i = 3, last = sysconf(_SC_OPEN_MAX); i != last; i++)
|
||||
::close(i);
|
||||
|
||||
result = execvp(file, argv);
|
||||
|
||||
_exit(result);
|
||||
}
|
||||
|
||||
if (flags & flag_capture) {
|
||||
m_capture = std::string();
|
||||
::close(pipeFd[1]);
|
||||
|
||||
char buffer[4096];
|
||||
ssize_t length;
|
||||
|
||||
do {
|
||||
length = read(pipeFd[0], buffer, sizeof(buffer));
|
||||
|
||||
if (length > 0)
|
||||
m_capture += std::string(buffer, length);
|
||||
} while (length > 0);
|
||||
|
||||
::close(pipeFd[0]);
|
||||
int spawn_status = spawn_process.execute(file, argv);
|
||||
|
||||
if (spawn_status != 0) {
|
||||
if (m_log_fd != -1) {
|
||||
result = write(m_log_fd, "Captured output:\n", sizeof("Captured output:\n"));
|
||||
result = write(m_log_fd, m_capture.data(), m_capture.length());
|
||||
auto prefix = "\n--- posix_spawn failed: ";
|
||||
auto errno_str = torrent::system::errno_enum_str(spawn_status) + " ---\n";
|
||||
|
||||
struct iovec iovecs[2] = {
|
||||
{const_cast<char*>(prefix), std::strlen(prefix)},
|
||||
{const_cast<char*>(errno_str.c_str()), errno_str.size()},
|
||||
};
|
||||
|
||||
[[maybe_unused]] int result = ::writev(m_log_fd, iovecs, 2);
|
||||
}
|
||||
|
||||
throw torrent::input_error("ExecFile::execute() posix_spawn failed: " + torrent::system::errno_enum_str(spawn_status));
|
||||
}
|
||||
|
||||
int status;
|
||||
|
||||
while (waitpid(childPid, &status, 0) == -1) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
continue;
|
||||
case ECHILD:
|
||||
throw torrent::internal_error("ExecFile::execute(...) waitpid failed with ECHILD, child process not found.");
|
||||
case EINVAL:
|
||||
throw torrent::internal_error("ExecFile::execute(...) waitpid failed with EINVAL.");
|
||||
default:
|
||||
throw torrent::internal_error("ExecFile::execute(...) waitpid failed with unexpected error: " + std::string(std::strerror(errno)));
|
||||
}
|
||||
};
|
||||
|
||||
// Check return value?
|
||||
if (m_log_fd != -1) {
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
|
||||
result = write(m_log_fd, "\n--- Success ---\n", sizeof("\n--- Success ---\n"));
|
||||
else
|
||||
result = write(m_log_fd, "\n--- Error ---\n", sizeof("\n--- Error ---\n"));
|
||||
if (flags & flag_background) {
|
||||
m_waitpid_queue.close_pid(spawn_process.child_pid());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return status;
|
||||
if (flags & flag_capture)
|
||||
m_capture = spawn_process.capture_child_output();
|
||||
|
||||
return spawn_process.wait_for_child();
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
@@ -152,8 +83,9 @@ ExecFile::execute_object(const torrent::Object& rawArgs, int flags) {
|
||||
char* argsBuffer[max_args];
|
||||
char** argsCurrent = argsBuffer;
|
||||
|
||||
// Size of value strings are less than 24.
|
||||
char valueBuffer[buffer_size+1];
|
||||
// On the heap: a buffer of buffer_size does not belong on the stack of whichever thread runs the command.
|
||||
auto valueStorage = std::make_unique<char[]>(buffer_size + 1);
|
||||
char* valueBuffer = valueStorage.get();
|
||||
char* valueCurrent = valueBuffer;
|
||||
|
||||
if (rawArgs.is_list()) {
|
||||
@@ -204,4 +136,4 @@ ExecFile::execute_object(const torrent::Object& rawArgs, int flags) {
|
||||
return torrent::Object((int64_t)status);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace rpc
|
||||
|
||||
+5
-1
@@ -3,12 +3,14 @@
|
||||
|
||||
#include <torrent/object.h>
|
||||
|
||||
#include "utils/waitpid_queue.h"
|
||||
|
||||
namespace rpc {
|
||||
|
||||
class ExecFile {
|
||||
public:
|
||||
static constexpr unsigned int max_args = 128;
|
||||
static constexpr unsigned int buffer_size = 4096;
|
||||
static constexpr unsigned int buffer_size = 128 * 1024;
|
||||
|
||||
static constexpr int flag_expand_tilde = 0x1;
|
||||
static constexpr int flag_throw = 0x2;
|
||||
@@ -24,6 +26,8 @@ public:
|
||||
private:
|
||||
int m_log_fd{-1};
|
||||
std::string m_capture;
|
||||
|
||||
utils::WaitpidQueue m_waitpid_queue;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+95
-15
@@ -2,16 +2,20 @@
|
||||
|
||||
#include "rpc/jsonrpc.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <torrent/common.h>
|
||||
#include <torrent/torrent.h>
|
||||
#include <torrent/utils/string_manip.h>
|
||||
|
||||
#include "rpc/rpc_manager.h"
|
||||
#include "rpc/command.h"
|
||||
#include "rpc/command_map.h"
|
||||
#include "rpc/nlohmann/json.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "rpc/scgi_task.h"
|
||||
#include "torrent/exceptions.h"
|
||||
#include "torrent/object.h"
|
||||
#include "utils/functional.h"
|
||||
@@ -25,32 +29,45 @@ constexpr int JSONRPC_METHOD_NOT_FOUND_ERROR = -32601;
|
||||
constexpr int JSONRPC_INVALID_PARAMS_ERROR = -32602;
|
||||
constexpr int JSONRPC_INTERNAL_ERROR = -32000;
|
||||
|
||||
constexpr uint32_t max_json_depth = 1024;
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
torrent::Object
|
||||
json_to_object(const json& value) {
|
||||
json_to_object(const json& value, uint32_t depth = 0) {
|
||||
switch (value.type()) {
|
||||
case json::value_t::number_unsigned:
|
||||
case json::value_t::number_integer:
|
||||
return torrent::Object(value.get<int64_t>());
|
||||
|
||||
case json::value_t::boolean:
|
||||
return value.get<bool>() ? torrent::Object(int64_t(1)) : torrent::Object(int64_t(0));
|
||||
|
||||
case json::value_t::string:
|
||||
return torrent::Object(value.get<std::string>());
|
||||
|
||||
case json::value_t::array: {
|
||||
if (++depth >= max_json_depth)
|
||||
throw torrent::input_error("invalid parameters: max depth reached");
|
||||
|
||||
auto array_raw = torrent::Object::create_list();
|
||||
auto& array = array_raw.as_list();
|
||||
for (const auto& entry : value) {
|
||||
array.push_back(json_to_object(entry));
|
||||
}
|
||||
|
||||
for (const auto& entry : value)
|
||||
array.push_back(json_to_object(entry, depth));
|
||||
|
||||
return array_raw;
|
||||
}
|
||||
case json::value_t::object: {
|
||||
if (++depth >= max_json_depth)
|
||||
throw torrent::input_error("invalid parameters: max depth reached");
|
||||
|
||||
auto map_raw = torrent::Object::create_map();
|
||||
auto& map = map_raw.as_map();
|
||||
for (const auto& entry : value.items()) {
|
||||
map[entry.key()] = json_to_object(entry.value());
|
||||
}
|
||||
|
||||
for (const auto& entry : value.items())
|
||||
map[entry.key()] = json_to_object(entry.value(), depth);
|
||||
|
||||
return map_raw;
|
||||
}
|
||||
case json::value_t::number_float:
|
||||
@@ -61,17 +78,38 @@ json_to_object(const json& value) {
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Only throw rpc_errors here.
|
||||
|
||||
json
|
||||
object_to_json(const torrent::Object& object) noexcept {
|
||||
object_to_json(const torrent::Object& object) {
|
||||
switch (object.type()) {
|
||||
case torrent::Object::TYPE_VALUE:
|
||||
return object.as_value();
|
||||
|
||||
case torrent::Object::TYPE_STRING:
|
||||
if (object.flags() & torrent::Object::flag_as_binary) {
|
||||
|
||||
// We should optimize our imported json library to support copying base64 strings directly.
|
||||
if (object.flags() & torrent::Object::flag_base64) {
|
||||
auto binary_data = torrent::utils::transform_from_base64_unsafe(object.as_string());
|
||||
|
||||
if (!binary_data.has_value())
|
||||
throw rpc_error(JSONRPC_INTERNAL_ERROR, "invalid base64 string in base64-as-binary object");
|
||||
|
||||
return json::binary(*binary_data);
|
||||
}
|
||||
|
||||
return json::binary({object.as_string().begin(), object.as_string().end()});
|
||||
}
|
||||
|
||||
return object.as_string();
|
||||
|
||||
case torrent::Object::TYPE_LIST: {
|
||||
json result = json::array();
|
||||
|
||||
for (const auto& obj : object.as_list())
|
||||
result.push_back(object_to_json(obj));
|
||||
|
||||
return result;
|
||||
}
|
||||
case torrent::Object::TYPE_MAP: {
|
||||
@@ -111,7 +149,7 @@ jsonrpc_call_command(const std::string& method, const json& params) {
|
||||
|
||||
CommandMap::iterator itr = commands.find(method.c_str());
|
||||
|
||||
if (itr == commands.end()) {
|
||||
if (itr == commands.end() || !(itr->second.m_flags & CommandMap::flag_public_rpc)) {
|
||||
throw rpc_error(JSONRPC_METHOD_NOT_FOUND_ERROR, "method not found: " + method);
|
||||
}
|
||||
|
||||
@@ -135,7 +173,9 @@ jsonrpc_call_command(const std::string& method, const json& params) {
|
||||
|
||||
try {
|
||||
const auto& result = rpc::commands.call_command(itr, params_object, target);
|
||||
|
||||
return object_to_json(result);
|
||||
|
||||
} catch (untrusted_error& e) {
|
||||
throw rpc_error(JSONRPC_METHOD_NOT_FOUND_ERROR, e.what());
|
||||
}
|
||||
@@ -185,13 +225,49 @@ handle_notification(const json& request) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using json_input_adapter = decltype(nlohmann::detail::input_adapter(std::declval<const char*>(), std::declval<const char*>()));
|
||||
using json_dom_parser = nlohmann::detail::json_sax_dom_parser<json, json_input_adapter>;
|
||||
|
||||
class json_depth_limited_parser : public json_dom_parser {
|
||||
public:
|
||||
explicit json_depth_limited_parser(json& root) : json_dom_parser(root) {}
|
||||
|
||||
bool start_object(std::size_t length) { return enter() && json_dom_parser::start_object(length); }
|
||||
bool start_array(std::size_t length) { return enter() && json_dom_parser::start_array(length); }
|
||||
|
||||
bool end_object() { m_depth--; return json_dom_parser::end_object(); }
|
||||
bool end_array() { m_depth--; return json_dom_parser::end_array(); }
|
||||
|
||||
private:
|
||||
bool enter() { return ++m_depth <= max_json_depth; }
|
||||
|
||||
uint32_t m_depth{0};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool
|
||||
JsonRpc::process(const char* in_buffer, uint32_t length, slot_write callback) {
|
||||
json response;
|
||||
json body;
|
||||
|
||||
if (length > m_size_limit) {
|
||||
auto err_str = json_error(JSONRPC_INVALID_REQUEST_ERROR, "content size exceeds maximum RPC limit", nullptr).dump();
|
||||
|
||||
return callback(err_str.c_str(), err_str.size());
|
||||
}
|
||||
|
||||
try {
|
||||
body = json::parse(in_buffer, in_buffer + length);
|
||||
json_depth_limited_parser handler(body);
|
||||
|
||||
if (!json::sax_parse(in_buffer, in_buffer + length, &handler)) {
|
||||
auto err_str = json_error(JSONRPC_INVALID_REQUEST_ERROR, "maximum nesting depth exceeded", nullptr).dump();
|
||||
|
||||
return callback(err_str.c_str(), err_str.size());
|
||||
}
|
||||
|
||||
switch (body.type()) {
|
||||
case json::value_t::object: {
|
||||
if (!body.contains("id")) {
|
||||
@@ -227,13 +303,17 @@ JsonRpc::process(const char* in_buffer, uint32_t length, slot_write callback) {
|
||||
|
||||
std::string response_str = response.dump();
|
||||
|
||||
if (response_str.size() > SCgiTask::max_response_size) {
|
||||
const auto& id = response.is_object() && response.contains("id") ? response["id"] : json(nullptr);
|
||||
auto err_str = json_error(JSONRPC_INTERNAL_ERROR, "response size exceeds maximum RPC limit", id).dump();
|
||||
|
||||
return callback(err_str.c_str(), err_str.size());
|
||||
}
|
||||
|
||||
return callback(response_str.c_str(), response_str.size());
|
||||
|
||||
} catch (json::parse_error& e) {
|
||||
auto err_str = json_error(JSONRPC_PARSE_ERROR, e.what(), nullptr).dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
return callback(err_str.c_str(), err_str.size());
|
||||
} catch (json::type_error& e) {
|
||||
// Type errors may be caused by invalid UTF-8 strings in exception strings, hence the ::replace
|
||||
} catch (json::exception& e) {
|
||||
// Exception strings may contain invalid UTF-8, hence the ::replace
|
||||
auto err_str = json_error(JSONRPC_PARSE_ERROR, e.what(), nullptr).dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
return callback(err_str.c_str(), err_str.size());
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "rpc/scgi_task.h"
|
||||
|
||||
namespace rpc {
|
||||
|
||||
class JsonRpc {
|
||||
@@ -17,6 +19,11 @@ public:
|
||||
bool process(const char* in_buffer, uint32_t length, slot_write callback);
|
||||
|
||||
void insert_command(const char* name, const char* parm, const char* doc) {};
|
||||
|
||||
void set_size_limit(uint64_t size) { m_size_limit = size; }
|
||||
|
||||
private:
|
||||
uint64_t m_size_limit{SCgiTask::max_content_size};
|
||||
};
|
||||
|
||||
} // namespace rpc
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user