Compare commits

..

40 Commits

Author SHA1 Message Date
Jari Sundell b2fbbcbfc4 Update libtorrent and rtorrent version to 0.16.13 2026-06-04 18:21:48 +09:00
Jari Sundell 2ca10396b4 Update links to libtorrent and rtorrent versions 2026-05-01 19:51:54 +09:00
Jari Sundell d15dc9d677 Create CNAME 2026-01-01 18:36:54 +09:00
Jari Sundell 356e8041de Upgrade libtorrent and rtorrent links to 0.16.5
Updated links to libtorrent and rtorrent to version 0.16.5.
2025-12-03 06:20:03 +09:00
Jari Sundell 24c44fbb11 Update links to libtorrent and rtorrent versions 2025-11-25 20:12:21 +09:00
Jari Sundell c933cee460 Update download links to version 0.16.3
Updated download links for libtorrent and rtorrent to version 0.16.3.
2025-11-21 23:58:35 +09:00
Jari Sundell a6ab3f0ede Update development links to version 0.16.2
Updated links to libtorrent and rtorrent to version 0.16.2.
2025-11-05 05:06:03 +09:00
Jari Sundell 8adbba62a9 Update download links to version 0.16.1
Updated download links for libtorrent and rtorrent to version 0.16.1.
2025-10-16 18:53:59 +09:00
Jari Sundell 96b2dc6693 Add links for libtorrent and rtorrent 0.16.0
Added links for libtorrent and rtorrent version 0.16.0 under the Development section.
2025-09-05 19:00:04 +09:00
Jari Sundell 7498e77e90 Update stable links to version 0.15.7 2025-09-05 17:06:01 +09:00
Jari Sundell 0e3ade18ad Update stable links and remove unstable versions
Updated stable version links for libtorrent and rtorrent to 0.15.6. Removed unstable version links.
2025-08-31 15:15:17 +09:00
Jari Sundell dba865549f Update index.md 2025-01-22 03:19:19 +09:00
Josh Ellithorpe 5c8087494e Update donate.md Ethereum spelling. 2024-10-29 22:45:10 +09:00
Jari Sundell 2d7ae2c834 Update index.md 2024-09-29 22:59:53 +09:00
Jari Sundell 6c7d75cadb Updated paypal link 2022-09-08 05:03:45 +09:00
Jari Sundell 73544b9d9e Updated paypal link 2022-09-08 05:03:13 +09:00
Jari Sundell 2dd794f982 Added rbEdit to front page. 2021-12-13 04:08:31 +09:00
Jari Sundell 77bb87a9e9 Updated ADA donation address. 2021-11-25 02:14:07 +09:00
Jari Sundell 582bb8a2ad Updated ADA donation address. 2021-11-25 02:13:48 +09:00
Jari Sundell be2a52f825 Updated package links. 2021-08-04 14:24:05 +09:00
Jari Sundell 2b5361e6af Update index.md 2021-07-20 10:34:39 +09:00
Jari Sundell 1419a6664e Update donate.md 2021-07-20 10:34:13 +09:00
Jari Sundell 5547d048e4 Marked 0.9.8 as stable. 2021-03-24 15:53:55 +09:00
Jari Sundell 2663f8050c Update donate.md 2019-12-27 12:45:38 +09:00
Jari Sundell 98d5c169fa Update index.md 2019-12-25 23:41:35 +09:00
Jari Sundell 1478213e90 Update donate.md 2019-12-25 23:40:35 +09:00
Jari Sundell 6b5bdd4d9e Added donation button. 2019-10-03 13:15:50 +09:00
rakshasa e736672f55 Confirmation. 2019-09-22 18:48:21 +09:00
rakshasa 852f6de363 Added donation information to index page. 2019-08-21 23:37:15 +09:00
rakshasa dd85b663a7 Bumped version. 2019-07-19 14:27:49 +02:00
rakshasa 04ddd9af22 Fixed donate links. 2019-07-19 12:58:37 +02:00
rakshasa 1e37d9db6a Added donate page. 2019-07-19 12:56:07 +02:00
Jari Sundell 0c9969960f Update index.md 2019-07-19 12:27:37 +02:00
Jari Sundell da7bbeedb1 Fixed theme 2019-07-19 12:25:07 +02:00
Jari Sundell 836dc60116 Set theme jekyll-theme-minimal and migrate Page Generator content 2019-07-19 12:22:53 +02:00
Jari Sundell b7e0a68eed Update index.html
Links to 0.8.7 added.
2018-07-19 14:47:02 +09:00
Jari Sundell 839fb0cf0b Create gh-pages branch via GitHub 2015-09-04 20:10:20 +09:00
Jari Sundell 9e6a8f39ce Create gh-pages branch via GitHub 2014-05-16 01:17:26 +09:00
Jari Sundell b23b65c907 Create gh-pages branch via GitHub 2014-05-15 23:20:48 +09:00
Jari Sundell 0fb89786a3 Create gh-pages branch via GitHub 2014-05-15 22:41:43 +09:00
307 changed files with 948 additions and 73113 deletions
-33
View File
@@ -1,33 +0,0 @@
---
BasedOnStyle: LLVM
Standard: c++14
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AlwaysBreakAfterReturnType: TopLevelDefinitions
BinPackArguments: false
BinPackParameters: false
BreakConstructorInitializers: AfterColon
BreakStringLiterals: false
ColumnLimit: 0
ContinuationIndentWidth: 2
IndentCaseLabels: false
IndentWidth: 2
PenaltyReturnTypeOnItsOwnLine: 130
PointerAlignment: Left
AlignEscapedNewlines: Right
AlignConsecutiveDeclarations:
Enabled: true
AcrossEmptyLines: true
AcrossComments: false
AlignConsecutiveMacros:
Enabled: true
AlignConsecutiveAssignments:
Enabled: true
IncludeCategories:
- Regex: "^(config|globals)\\.h"
Priority: -1
- Regex: "^torrent/.*"
Priority: 1
-16
View File
@@ -1,16 +0,0 @@
---
Checks: '-*,readability-identifier-naming'
FormatStyle: 'file'
CheckOptions:
- key: readability-identifier-naming.LocalVariableCase
value: lower_case
- key: readability-identifier-naming.ParameterCase
value: lower_case
- key: readability-identifier-naming.FunctionCase
value: lower_case
- key: readability-identifier-naming.PrivateMemberPrefix
value: m_
- key: readability-identifier-naming.PrivateMemberCase
value: lower_case
- key: readability-identifier-naming.ClassConstantCase
value: lower_case
-7
View File
@@ -1,7 +0,0 @@
;;; Directory Local Variables
;;; For more information see (info "(emacs) Directory Variables")
((c++-mode
(flycheck-clang-language-standard . "c++11")
(flycheck-gcc-language-standard . "c++11")))
-13
View File
@@ -1,13 +0,0 @@
# These are supported funding model platforms
github: [rakshasa]
patreon: rtorrent
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: ['https://rakshasa.github.io/rtorrent/donate.html']
-65
View File
@@ -1,65 +0,0 @@
name: "Lint Commit Message Size"
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
check-commit-bounds:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Validate Line Count, Width, and Spacing
run: |
# Fetch commit hashes unique to this PR branch
COMMITS=$(git log --no-merges --pretty=format:"%H" origin/${{ github.base_ref }}..HEAD)
MAX_LINES=3
MAX_CHARS=90
FAILED=0
for commit in $COMMITS; do
SUBJECT=$(git log --format="%s" -n 1 $commit)
# Extract clean commit message, trimming trailing blank lines
RAW_MSG=$(git log --format="%B" -n 1 $commit)
CLEAN_MSG=$(echo "$RAW_MSG" | awk '{msg[NR]=$0} END {while(NR>0 && msg[NR]=="") NR--; for(i=1;i<=NR;i++) print msg[i]}')
# 1. Check total line count
LINE_COUNT=$(echo "$CLEAN_MSG" | wc -l)
if [ "$LINE_COUNT" -gt "$MAX_LINES" ]; then
echo "❌ Error: Commit message has too many lines ($LINE_COUNT/$MAX_LINES)."
echo " Commit: '$SUBJECT'"
FAILED=1
fi
# 2. Check for multiple consecutive blank lines
# This regex looks for 2 or more empty lines anywhere in the message
if echo "$CLEAN_MSG" | grep -pz '(\r?\n){3,}'; then
echo "❌ Error: Commit message contains multiple consecutive line breaks."
echo " Commit: '$SUBJECT'"
FAILED=1
fi
# 3. Check maximum width of any individual line
while IFS= read -r line; do
LINE_LENGTH=${#line}
if [ "$LINE_LENGTH" -gt "$MAX_CHARS" ]; then
echo "❌ Error: Line length exceeds limit ($LINE_LENGTH / $MAX_CHARS chars)."
echo " Offending line: '$line'"
FAILED=1
fi
done <<< "$CLEAN_MSG"
done
# Fail the job if any check failed
if [ "$FAILED" -ne 0 ]; then
exit 1
fi
echo "✅ All commit messages passed style rules!"
@@ -1,96 +0,0 @@
# Secure workflow with access to repository secrets and GitHub token for posting analysis results
name: Post the static analysis results
on:
workflow_run:
workflows: [ "Static analysis" ]
types: [ completed ]
jobs:
clang-tidy-results:
# Trigger the job only if the previous (insecure) workflow completed successfully
if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-22.04
permissions:
pull-requests: write
# OPTIONAL: auto-closing conversations requires the `contents` permission
contents: read
steps:
- name: Sleep for 30 seconds
run: sleep 30s
shell: bash
- name: Download analysis results
uses: actions/github-script@v7
with:
script: |
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{ github.event.workflow_run.id }},
});
const matchArtifact = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "clang-tidy-result"
})[0];
const download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: "zip",
});
const fs = require("fs");
fs.writeFileSync("${{ github.workspace }}/clang-tidy-result.zip", Buffer.from(download.data));
- name: Extract analysis results
run: |
mkdir clang-tidy-result
unzip -j clang-tidy-result.zip -d clang-tidy-result
- name: Set environment variables
uses: actions/github-script@v7
with:
script: |
const assert = require("node:assert").strict;
const fs = require("fs");
function exportVar(varName, fileName, regEx) {
const val = fs.readFileSync("${{ github.workspace }}/clang-tidy-result/" + fileName, {
encoding: "ascii"
}).trimEnd();
assert.ok(regEx.test(val), "Invalid value format for " + varName);
core.exportVariable(varName, val);
}
exportVar("PR_ID", "pr-id.txt", /^[0-9]+$/);
exportVar("PR_HEAD_REPO", "pr-head-repo.txt", /^[-./0-9A-Z_a-z]+$/);
exportVar("PR_HEAD_SHA", "pr-head-sha.txt", /^[0-9A-Fa-f]+$/);
- uses: actions/checkout@v4
with:
repository: ${{ env.PR_HEAD_REPO }}
ref: ${{ env.PR_HEAD_SHA }}
persist-credentials: false
- name: Redownload analysis results
uses: actions/github-script@v7
with:
script: |
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{ github.event.workflow_run.id }},
});
const matchArtifact = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "clang-tidy-result"
})[0];
const download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: "zip",
});
const fs = require("fs");
fs.writeFileSync("${{ github.workspace }}/clang-tidy-result.zip", Buffer.from(download.data));
- name: Extract analysis results
run: |
mkdir clang-tidy-result
unzip -j clang-tidy-result.zip -d clang-tidy-result
- name: Run clang-tidy-pr-comments action
uses: platisd/clang-tidy-pr-comments@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
clang_tidy_fixes: clang-tidy-result/fixes.yml
pull_request_id: ${{ env.PR_ID }}
-84
View File
@@ -1,84 +0,0 @@
name: Static analysis
on: pull_request
jobs:
clang-tidy:
runs-on: ubuntu-22.04
steps:
- name: Update Packages
run: |
sudo apt-get update
- name: Install Dependencies
run: |
sudo apt-get install -y \
bear \
clang-tidy \
libcurl4-openssl-dev \
zlib1g-dev
- name: Fetch libtorrent
run: |
git clone https://github.com/rakshasa/libtorrent
- name: Build libtorrent
run: |
cd libtorrent
libtoolize
aclocal -I scripts
autoconf -i
autoheader
automake --add-missing
./configure
make
sudo make install
cd ..
rm -rf libtorrent
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Fetch base branch
run: |
git remote add upstream "https://github.com/${{ github.event.pull_request.base.repo.full_name }}"
git fetch --no-tags --no-recurse-submodules upstream "${{ github.event.pull_request.base.ref }}"
- name: Configure Project
run: |
libtoolize
aclocal -I scripts
autoconf -i
autoheader
automake --add-missing
./configure
- name: Prepare compile_commands.json
run: |
bear -- make
- name: Create results directory
run: |
mkdir clang-tidy-result
- name: Analyze
run: |
git diff -U0 "$(git merge-base HEAD "upstream/${{ github.event.pull_request.base.ref }}")" | clang-tidy-diff -p1 -path build -export-fixes clang-tidy-result/fixes.yml "-extra-arg=-include/${PWD}/config.h"
- name: Save PR metadata
run: |
echo "${{ github.event.number }}" > clang-tidy-result/pr-id.txt
echo "${{ github.event.pull_request.head.repo.full_name }}" > clang-tidy-result/pr-head-repo.txt
echo "${{ github.event.pull_request.head.sha }}" > clang-tidy-result/pr-head-sha.txt
- uses: actions/upload-artifact@v4
with:
name: clang-tidy-result
path: clang-tidy-result/
# - name: Run clang-tidy-pr-comments action
# uses: platisd/clang-tidy-pr-comments@v1
# with:
# # The GitHub token (or a personal access token)
# github_token: ${{ secrets.GITHUB_TOKEN }}
# # The path to the clang-tidy fixes generated previously
# clang_tidy_fixes: clang-tidy-result/fixes.yml
# # Optionally set to true if you want the Action to request
# # changes in case warnings are found
# request_changes: true
# # Optionally set the number of comments per review
# # to avoid GitHub API timeouts for heavily loaded
# # pull requests
# suggestions_per_comment: 10
-107
View File
@@ -1,107 +0,0 @@
name: Static analysis
on: pull_request
jobs:
ubuntu-base:
runs-on: ubuntu-22.04
steps:
- name: Update Packages
run: |
sudo apt-get update
- name: Install Dependencies
run: |
sudo apt-get install -y \
libcppunit-dev \
zlib1g-dev \
libcurl4-openssl-dev
- name: Fetch libtorrent
run: |
git clone https://github.com/rakshasa/libtorrent
- name: Build libtorrent
run: |
cd libtorrent
autoreconf -fiv
./configure
make
sudo make install DESTDIR=$GITHUB_WORKSPACE/libtorrent-dist
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: installed-libtorrent
path: libtorrent-dist/
unit-tests-default:
runs-on: ubuntu-22.04
needs: ubuntu-base
steps:
- name: Update Packages
run: |
sudo apt-get update
- name: Install Dependencies
run: |
sudo apt-get install -y \
libcppunit-dev \
zlib1g-dev \
libcurl4-openssl-dev
- name: Download Libtorrent Artifacts
uses: actions/download-artifact@v4
with:
name: installed-libtorrent
path: ./libtorrent-dist
- name: Move Artifacts to System Path
run: |
# Elevate permissions with sudo to safely place the files
sudo cp -r ./libtorrent-dist/usr/local/* /usr/local/
rm -rf ./libtorrent-dist
- uses: actions/checkout@v4
- name: Configure Project
run: |
autoreconf -fiv
./configure
make
ls /usr/local/lib/
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" make check
- name: Archive test/test-suite.log
if: success() || failure()
uses: actions/upload-artifact@v4
with:
name: test-suite.log
path: test/test-suite.log
unit-tests-variants:
runs-on: ubuntu-22.04
needs: unit-tests-default
strategy:
matrix:
config_flag: ["--with-xmlrpc-c", "--with-xmlrpc-tinyxml2"]
steps:
- name: Update Packages
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
-74
View File
@@ -1,74 +0,0 @@
# Compiled source #
###################
*.o
*.lo
*.a
*.la
*.so
*.in
*.sub
*.pc
*.orig
*.rej
# Autoconf files #
##################
.deps
.libs
Makefile
aclocal.m4
actmp.*
ar-lib
autom4te.cache
compile
config.h
config.guess
config.status
confdefs.h
conftest.dir
configure
depcomp
install-sh
libtool
ltmain.sh
missing
stamp-h1
src/rtorrent
scripts/libtool.m4
scripts/lt*.m4
# Editor poo #
##############
.#*
\#*#
*~
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.gz
*.tar
*.zip
# Logs and databases #
######################
*.log
# OS generated files #
######################
.DS_Store?
.dirstamp
ehthumbs.db
Icon?
Thumbs.db
TAGS
# rTorrent specific files
###########################
src/rtorrent
test/rtorrent_Test*
test/test-suite.log.tmp
test-driver
-1
View File
@@ -1 +0,0 @@
Jari Sundell <sundell.software@gmail.com>
+1
View File
@@ -0,0 +1 @@
rtorrent.net
-340
View File
@@ -1,340 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
-368
View File
@@ -1,368 +0,0 @@
Installation Instructions
*************************
Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software
Foundation, Inc.
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 warranty of any kind.
Basic Installation
==================
Briefly, the shell command './configure && make && make install'
should configure, build, and install this package. The following
more-detailed instructions are generic; see the 'README' file for
instructions specific to this package. Some packages provide this
'INSTALL' file but do not implement all of the features documented
below. The lack of an optional feature in a given package is not
necessarily a bug. More recommendations for GNU packages can be found
in *note Makefile Conventions: (standards)Makefile Conventions.
The 'configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a 'Makefile' in each directory of the package.
It may also create one or more '.h' files containing system-dependent
definitions. Finally, it creates a shell script 'config.status' that
you can run in the future to recreate the current configuration, and a
file 'config.log' containing compiler output (useful mainly for
debugging 'configure').
It can also use an optional file (typically called 'config.cache' and
enabled with '--cache-file=config.cache' or simply '-C') that saves the
results of its tests to speed up reconfiguring. Caching is disabled by
default to prevent problems with accidental use of stale cache files.
If you need to do unusual things to compile the package, please try
to figure out how 'configure' could check whether to do them, and mail
diffs or instructions to the address given in the 'README' so they can
be considered for the next release. If you are using the cache, and at
some point 'config.cache' contains results you don't want to keep, you
may remove or edit it.
The file 'configure.ac' (or 'configure.in') is used to create
'configure' by a program called 'autoconf'. You need 'configure.ac' if
you want to change it or regenerate 'configure' using a newer version of
'autoconf'.
The simplest way to compile this package is:
1. 'cd' to the directory containing the package's source code and type
'./configure' to configure the package for your system.
Running 'configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type 'make' to compile the package.
3. Optionally, type 'make check' to run any self-tests that come with
the package, generally using the just-built uninstalled binaries.
4. Type 'make install' to install the programs and any data files and
documentation. When installing into a prefix owned by root, it is
recommended that the package be configured and built as a regular
user, and only the 'make install' phase executed with root
privileges.
5. Optionally, type 'make installcheck' to repeat any self-tests, but
this time using the binaries in their final installed location.
This target does not install anything. Running this target as a
regular user, particularly if the prior 'make install' required
root privileges, verifies that the installation completed
correctly.
6. You can remove the program binaries and object files from the
source code directory by typing 'make clean'. To also remove the
files that 'configure' created (so you can compile the package for
a different kind of computer), type 'make distclean'. There is
also a 'make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
7. Often, you can also type 'make uninstall' to remove the installed
files again. In practice, not all packages have tested that
uninstallation works correctly, even though it is required by the
GNU Coding Standards.
8. Some packages, particularly those that use Automake, provide 'make
distcheck', which can by used by developers to test that all other
targets like 'make install' and 'make uninstall' work correctly.
This target is generally not run by end users.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the 'configure' script does not know about. Run './configure --help'
for details on some of the pertinent environment variables.
You can give 'configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here is
an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU 'make'. 'cd' to the
directory where you want the object files and executables to go and run
the 'configure' script. 'configure' automatically checks for the source
code in the directory that 'configure' is in and in '..'. This is known
as a "VPATH" build.
With a non-GNU 'make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use 'make distclean' before
reconfiguring for another architecture.
On MacOS X 10.5 and later systems, you can create libraries and
executables that work on multiple system types--known as "fat" or
"universal" binaries--by specifying multiple '-arch' options to the
compiler but only a single '-arch' option to the preprocessor. Like
this:
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CPP="gcc -E" CXXCPP="g++ -E"
This is not guaranteed to produce working output in all cases, you
may have to build one architecture at a time and combine the results
using the 'lipo' tool if you have problems.
Installation Names
==================
By default, 'make install' installs the package's commands under
'/usr/local/bin', include files under '/usr/local/include', etc. You
can specify an installation prefix other than '/usr/local' by giving
'configure' the option '--prefix=PREFIX', where PREFIX must be an
absolute file name.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option '--exec-prefix=PREFIX' to 'configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like '--bindir=DIR' to specify different values for particular
kinds of files. Run 'configure --help' for a list of the directories
you can set and what kinds of files go in them. In general, the default
for these options is expressed in terms of '${prefix}', so that
specifying just '--prefix' will affect all of the other directory
specifications that were not explicitly provided.
The most portable way to affect installation locations is to pass the
correct locations to 'configure'; however, many packages provide one or
both of the following shortcuts of passing variable assignments to the
'make install' command line to change installation locations without
having to reconfigure or recompile.
The first method involves providing an override variable for each
affected directory. For example, 'make install
prefix=/alternate/directory' will choose an alternate location for all
directory configuration variables that were expressed in terms of
'${prefix}'. Any directories that were specified during 'configure',
but not in terms of '${prefix}', must each be overridden at install time
for the entire installation to be relocated. The approach of makefile
variable overrides for each directory variable is required by the GNU
Coding Standards, and ideally causes no recompilation. However, some
platforms have known limitations with the semantics of shared libraries
that end up requiring recompilation when using this method, particularly
noticeable in packages that use GNU Libtool.
The second method involves providing the 'DESTDIR' variable. For
example, 'make install DESTDIR=/alternate/directory' will prepend
'/alternate/directory' before all installation names. The approach of
'DESTDIR' overrides is not required by the GNU Coding Standards, and
does not work on platforms that have drive letters. On the other hand,
it does better at avoiding recompilation issues, and works well even
when some directory options were not specified in terms of '${prefix}'
at 'configure' time.
Optional Features
=================
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving 'configure' the
option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'.
Some packages pay attention to '--enable-FEATURE' options to
'configure', where FEATURE indicates an optional part of the package.
They may also pay attention to '--with-PACKAGE' options, where PACKAGE
is something like 'gnu-as' or 'x' (for the X Window System). The
'README' should mention any '--enable-' and '--with-' options that the
package recognizes.
For packages that use the X Window System, 'configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the 'configure' options '--x-includes=DIR' and
'--x-libraries=DIR' to specify their locations.
Some packages offer the ability to configure how verbose the
execution of 'make' will be. For these packages, running './configure
--enable-silent-rules' sets the default to minimal output, which can be
overridden with 'make V=1'; while running './configure
--disable-silent-rules' sets the default to verbose, which can be
overridden with 'make V=0'.
Particular systems
==================
On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC
is not installed, it is recommended to use the following options in
order to use an ANSI C compiler:
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
HP-UX 'make' updates targets which have the same time stamps as their
prerequisites, which makes it generally unusable when shipped generated
files such as 'configure' are involved. Use GNU 'make' instead.
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
parse its '<wchar.h>' header file. The option '-nodtk' can be used as a
workaround. If GNU CC is not installed, it is therefore recommended to
try
./configure CC="cc"
and if that doesn't work, try
./configure CC="cc -nodtk"
On Solaris, don't put '/usr/ucb' early in your 'PATH'. This
directory contains several dysfunctional programs; working variants of
these programs are available in '/usr/bin'. So, if you need '/usr/ucb'
in your 'PATH', put it _after_ '/usr/bin'.
On Haiku, software installed for all users goes in '/boot/common',
not '/usr/local'. It is recommended to use the following options:
./configure --prefix=/boot/common
Specifying the System Type
==========================
There may be some features 'configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on. Usually, assuming the package is built to be run on the
_same_ architectures, 'configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
'--build=TYPE' option. TYPE can either be a short name for the system
type, such as 'sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS
KERNEL-OS
See the file 'config.sub' for the possible values of each field. If
'config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option '--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with '--host=TYPE'.
Sharing Defaults
================
If you want to set default values for 'configure' scripts to share,
you can create a site shell script called 'config.site' that gives
default values for variables like 'CC', 'cache_file', and 'prefix'.
'configure' looks for 'PREFIX/share/config.site' if it exists, then
'PREFIX/etc/config.site' if it exists. Or, you can set the
'CONFIG_SITE' environment variable to the location of the site script.
A warning: not all 'configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to 'configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the 'configure' command line, using 'VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified 'gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an
Autoconf limitation. Until the limitation is lifted, you can use this
workaround:
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
'configure' Invocation
======================
'configure' recognizes the following options to control how it
operates.
'--help'
'-h'
Print a summary of all of the options to 'configure', and exit.
'--help=short'
'--help=recursive'
Print a summary of the options unique to this package's
'configure', and exit. The 'short' variant lists options used only
in the top level, while the 'recursive' variant lists options also
present in any nested packages.
'--version'
'-V'
Print the version of Autoconf used to generate the 'configure'
script, and exit.
'--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally 'config.cache'. FILE defaults to '/dev/null' to
disable caching.
'--config-cache'
'-C'
Alias for '--cache-file=config.cache'.
'--quiet'
'--silent'
'-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to '/dev/null' (any error
messages will still be shown).
'--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
'configure' can determine that directory automatically.
'--prefix=DIR'
Use DIR as the installation prefix. *note Installation Names:: for
more details, including other options available for fine-tuning the
installation locations.
'--no-create'
'-n'
Run the configure checks, but stop before creating any output
files.
'configure' also accepts some other, not widely useful, options. Run
'configure --help' for more details.
-14
View File
@@ -1,14 +0,0 @@
SUBDIRS = \
doc \
src \
test
nobase_dist_pkgdata_DATA = \
lua/rtorrent.lua
EXTRA_DIST= \
scripts/checks.m4 \
scripts/common.m4 \
scripts/attributes.m4
ACLOCAL_AMFLAGS = -I scripts
-88
View File
@@ -1,88 +0,0 @@
RTorrent BitTorrent Client
========
Introduction
------------
A ncurses-based command line torrent client for high performance.
To learn how to use rTorrent visit the [Wiki](https://github.com/rakshasa/rtorrent/wiki).
Download the [latest stable release](https://github.com/rakshasa/rtorrent/releases/latest)
Related Projects
----------------
* [https://github.com/rakshasa/rbedit](https://github.com/rakshasa/rbedit): A dependency-free bencode editor.
Donate to rTorrent development
------------------------------
* [Paypal](https://paypal.me/jarisundellno)
* [Patreon](https://www.patreon.com/rtorrent)
* [SubscribeStar](https://www.subscribestar.com/rtorrent)
* Bitcoin: 1MpmXm5AHtdBoDaLZstJw8nupJJaeKu8V8
* Ethereum: 0x9AB1e3C3d8a875e870f161b3e9287Db0E6DAfF78
* Litecoin: LdyaVR67LBnTf6mAT4QJnjSG2Zk67qxmfQ
* Cardano: addr1qytaslmqmk6dspltw06sp0zf83dh09u79j49ceh5y26zdcccgq4ph7nmx6kgmzeldauj43254ey97f3x4xw49d86aguqwfhlte
Help keep rTorrent development going by donating to its creator.
BUILDING
--------
Jump into the github cloned directory
```
cd rtorrent
```
## Install build dependencies
Install [libtorrent](https://github.com/rakshasa/libtorrent) with the same version rTorrent.
Generate configure scripts:
```
autoreconf -ivf
```
Optionally, generate man pages:
```
docbook2man rtorrent.1.xml
```
Man pages output to "doc/rtorrent.1".
RTorrent follows the development of [libtorrent](https://github.com/rakshasa/libtorrent) closely, and thus the versions must be in sync.
## USAGE
Refer to User Guide: https://github.com/rakshasa/rtorrent/wiki/User-Guide
## LICENSE
GNU GPL, see COPYING. "libtorrent/src/utils/sha_fast.{cc,h}" is
originally from the Mozilla NSS and is under a triple license; MPL,
LGPL and GPL. An exception to non-NSS code has been added for linking to OpenSSL as requested by Debian, though the author considers that library to be part of the Operative System and thus linking is allowed according to the GPL.
Use whatever fits your purpose, the code required to compile with
Mozilla's NSS implementation of SHA1 has been retained and can be
compiled if the user wishes to avoid using OpenSSL.
## DEPENDENCIES
* libcurl >= 7.12.0
* libtorrent = (same version)
* ncurses
## BUILD DEPENDENCIES
* libtoolize
* aclocal
* autoconf
* autoheader
* automake
+8
View File
@@ -0,0 +1,8 @@
title: Rtorrent
description: rTorrent BitTorrent client
google_analytics:
show_downloads: true
theme: jekyll-theme-minimal
gems:
- jekyll-mentions
-85
View File
@@ -1,85 +0,0 @@
m4_pattern_allow([PKG_CHECK_EXISTS])
AC_INIT([rtorrent],[0.16.19],[sundell.software@gmail.com])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIRS([scripts])
AM_INIT_AUTOMAKE([foreign subdir-objects])
AM_PROG_AR
LT_INIT
AC_PROG_CXX
AX_CXX_COMPILE_STDCXX(20, noext, mandatory)
PKG_PROG_PKG_CONFIG
AC_DEFINE([API_VERSION], [24], [api version])
RAK_CHECK_CFLAGS
RAK_CHECK_CXXFLAGS
RAK_ENABLE_DEBUG
RAK_ENABLE_EXTRA_DEBUG
RAK_ENABLE_WERROR
AC_ARG_ENABLE(execinfo,
AS_HELP_STRING([--disable-execinfo],
[disable libexecinfo [[default=enable]]]),
[
if test "$enableval" = "yes"; then
AX_EXECINFO
fi
],[
AX_EXECINFO
])
AX_PTHREAD([], AC_MSG_ERROR([requires pthread]))
TORRENT_WITHOUT_NCURSES
if test "x$with_ncurses" != xno; then
AX_WITH_CURSES
if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
AC_MSG_ERROR([requires either NcursesW or Ncurses library])
fi
fi
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
PKG_CHECK_MODULES([ZLIB], [zlib])
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.19])
AC_LANG_PUSH(C++)
TORRENT_WITH_XMLRPC_C
AC_LANG_POP(C++)
dnl We don't have LuaJIT support, and the script breaks if not disabled.
AM_CONDITIONAL([LUAJIT], [false])
TORRENT_WITH_LUA
TORRENT_WITH_TINYXML2
TORRENT_WITH_SYSTEMD
if test ${with_xmlrpc_c+y} && test ${with_xmlrpc_tinyxml2+y}; then
AC_MSG_ERROR([--with-xmlrpc-c and --with-xmlrpc-tinyxml2 cannot be used together. Please choose only one])
fi
AC_DEFINE(USER_AGENT, [std::string(PACKAGE "/" VERSION)], 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 $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_CACHELINE
TORRENT_CHECK_POSIX_SPAWN_ADDCLOSEFROM_NP
AC_CONFIG_FILES([
Makefile
doc/Makefile
src/Makefile
test/Makefile
])
AC_OUTPUT
+4
View File
@@ -0,0 +1,4 @@
Reddit
======
Yes, I'm 'rasatsu'.
-2
View File
@@ -1,2 +0,0 @@
EXTRA_DIST= \
rtorrent.rc
-11
View File
@@ -1,11 +0,0 @@
=== Dumping core files on mac ==
ulimit -c unlimited
sysctl kern.corefile
schedule = log_vmmap,5,600,"execute_nothrow=/Users/rakshasa/trunk/rtorrent/doc/log_vmmap.sh,\"$cat=/Users/rakshasa/Downloads/session/vmmap.system.,$system.pid="; log.vmmap.dump=\"$cat=/Users/rakshasa/Downloads/session/vmmap.rtorrent.,$system.pid=\""
log.vmmap.dump="$cat=/Users/rakshasa/Downloads/session/vmmap.rtorrent.,$system.pid="
print="$cat=/Users/rakshasa/Downloads/session/vmmap.rtorrent.,$system.pid="
-106
View File
@@ -1,106 +0,0 @@
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
<article>
<title>rTorrent Frequently Asked Questions</title>
<para>
This is the user FAQ for Rakshasa's BitTorrent client. RTorrent is
an ncurses based client that uses libTorrent and is written in
C++. Efficiency and good code is the main goal of both projects.
</para>
<qandaset>
<qandaentry>
<question><para>
How do I add new torrents while the client is running?
</para></question>
<answer><para>
Use the backspace key to enter file input mode. The bottom
line will change into a prompt which allows you to enter URL's
or file paths to torrents. File name completion and directory
listing is supported with the tab key.
</para></answer>
</qandaentry>
<qandaentry>
<question><para>
I got the message "Could not create download, failed to parse the bencoded data" when adding a torrent, what does it mean?
</para></question>
<answer><para>
The torrent data the library received was not a valid torrent
file. If this was a direct http download, try downloading the
torrent file and add it manually. This usually happens when
the torrent link uses some form of redirection that libcurl
does not automatically redirect. If the failure still happens,
make sure the file is infact an uncorrupted torrent.
</para></answer>
</qandaentry>
<qandaentry>
<question><para>
How does the session feature work?
</para></question>
<answer><para>
When you start the client you can use the "-s" switch to
specify a session directory. The client will save all
currently running torrents in the session directory and fast
resume data will be written when the client exits. Upon
restarting the client it will resume all the torrents in the
session directory.
</para></answer>
</qandaentry>
<qandaentry>
<question><para>
How do i query the tracker for more peers?
</para></question>
<answer><para>
Go into the download view screen and press 't' to initiate a
new tracker request. If the tracker requires a minimum timeout
between requests you can override this by using the capital
'T'. Don't abuse this feature.
</para></answer>
</qandaentry>
<qandaentry>
<question><para>
Why do I sometimes need to press ctrl-Q twice to exit the client?
</para></question>
<answer><para>
The first time you press ctrl-q you initiate the
shutdown. This causes rTorrent to send a stop message to the
trackers of each torrent, and if the tracker is slow it might
take more than a few seconds or until the requests time
out. The second time ctrl-q is pressed forces the client to
shutdown.
</para></answer>
</qandaentry>
<qandaentry>
<question><para>
It doesn't help how much i press ctrl-q, it still won't quit.
</para></question>
<answer><para>
Perhaps your terminal sends a different key-code to the
application. There's no configuration files for keys yet, so
modify rtorrent/src/ui/root.cc:56 to use whatever key you want
for quitting.
</para></answer>
</qandaentry>
<qandaentry>
<question><para>
When I try downloading a torrent the client aborts (SIGABRT), what should I do?
</para></question>
<answer><para>
Make sure you arn't using "-fomit-frame-pointer" when
compiling libtorrent and rtorrent, this is known to produce
bad code for C++ exception handling. If this doesn't help, run
the client in gdb and use "bt -20" to get a backtrace. Send
this with a bug report.
</para></answer>
</qandaentry>
</qandaset>
</article>
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
cat /proc/$PPID/maps > $1
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
args=($@)
echo "${args[@]:1}" >> $1
-149
View File
@@ -1,149 +0,0 @@
#/bin/bash
gnuplot << EOF
# 1) system.time_seconds=
#
# 2) throttle.global_up.rate
# 3) throttle.global_up.total
# 4) throttle.global_down.rate
# 5) throttle.global_down.total
#
# 6) pieces.memory.current
# 7) pieces.memory.sync_queue
# 8) pieces.memory.block_count
# 9) pieces.stats.total_size
# 10) pieces.hash.queue_size
#
# file.append = (cat,/foo/bandwidth_stats.,(system.pid)),"timestamp","\"upload rate\"","\"upload total\"","\"download rate\"","\"download total\"","\"memory usage\"","\"sync queue\"","\"block count\"","\"total size\"","\"hash queue size\""
# schedule = log_bandwidth_stats,5,10,((file.append,((cat,/foo/bandwidth_stats.,((system.pid)))),((system.time_seconds)),((throttle.global_up.rate)),((throttle.global_up.total)),((throttle.global_down.rate)),((throttle.global_down.total)),((pieces.memory.current)),((pieces.memory.sync_queue)),((pieces.memory.block_count)),((pieces.stats.total_size)),((pieces.hash.queue_size))))
#
# 1) system.time_seconds
#
# 2) throttle.unchoked_uploads
# 3) throttle.unchoked_downloads
# 4) network.open_sockets
#
# 5) view.size,leeching
# 6) view.size,seeding
# 7) view.size,active
#
# file.append = (cat,/foo/peer_stats.,(system.pid)),"timestamp","\"upload unchoked\"","\"download unchoked\"","\"open sockets\"","leeching","seeding","active"
# schedule = log_peer_stats,5,10,((file.append,((cat,/foo/peer_stats.,((system.pid)))),((system.time_seconds)),((throttle.unchoked_uploads)),((throttle.unchoked_downloads)),((network.open_sockets)),((view.size,leeching)),((view.size,seeding)),((view.size,active))))
#
# Choke groups:
#
# file.append = (cat,/foo/choke_group_stats.,(system.pid)),"timestamp",\
# "\"default up unchoked\"","\"default up queued\"","\"default up rate\"",\
# "\"default down unchoked\"","\"default down queued\"","\"default down rate\"","\"default torrents\"",\
# "\"leech up unchoked\"","\"leech up queued\"","\"leech up rate\"",\
# "\"leech down unchoked\"","\"leech down queued\"","\"leech down rate\"","\"leech torrents\"",\
# "\"seed boost up unchoked\"","\"seed boost up queued\"","\"seed boost up rate\"",\
# "\"seed boost down unchoked\"","\"seed boost down queued\"","\"seed boost down rate\"","\"seed boost torrents\"",\
# "\"seed up unchoked\"","\"seed up queued\"","\"seed up rate\"",\
# "\"seed down unchoked\"","\"seed down queued\"","\"seed down rate\"","\"seed torrents\""
#
# schedule = log_choke_group_stats,5,10,((file.append,((cat,/foo/choke_group_stats.,((system.pid)))),((system.time_seconds)),\
# ((choke_group.up.unchoked,0)),((choke_group.up.queued,0)),((choke_group.up.rate,0)),((choke_group.down.unchoked,0)),((choke_group.down.queued,0)),((choke_group.down.rate,0)),((choke_group.size,0)),\
# ((choke_group.up.unchoked,1)),((choke_group.up.queued,1)),((choke_group.up.rate,1)),((choke_group.down.unchoked,1)),((choke_group.down.queued,1)),((choke_group.down.rate,1)),((choke_group.size,1)),\
# ((choke_group.up.unchoked,2)),((choke_group.up.queued,2)),((choke_group.up.rate,2)),((choke_group.down.unchoked,2)),((choke_group.down.queued,2)),((choke_group.down.rate,2)),((choke_group.size,2)),\
# ((choke_group.up.unchoked,3)),((choke_group.up.queued,3)),((choke_group.up.rate,3)),((choke_group.down.unchoked,3)),((choke_group.down.queued,3)),((choke_group.down.rate,3)),((choke_group.size,3)) ))
#
# Mincore stats:
#
# file.append = (cat,/foo/mincore_stats.,(system.pid)),"timestamp","\"incore cont. /10s\"","\"not incore cont. /10s\"","\"incore new /10s\"","\"not incore new /10s\"","\"incore break /10s\"","\"sync success /10s\"","\"sync failed /10s\"","\"sync not synced /10s\"","\"sync not deallocated /10s\"","\"alloc failed /10s\"","\"allocate velocity /10s\"","\"deallocate velocity /10s\""
# log.libtorrent = mincore_stats,(cat,"/foo/mincore_stats.",(system.pid))
set terminal png size 1024,768 enhanced
#set terminal png size 2*1024,768 enhanced
set xdata time
set timefmt "%s"
set format x "%H:%M"
set format y2 "%.0f"
set y2tics
set autoscale xfix
set key autotitle columnhead
set output "output_$1_sockets.png"
plot "peer_stats.$1" using 1:7 smooth sbezier with lines lw 4,\
"peer_stats.$1" using 1:5 smooth sbezier with lines lw 4,\
"peer_stats.$1" using 1:6 smooth sbezier with lines lw 4,\
"<awk '{x=1000; if (\$4 < 1000) x=\$4; print \$1,x}' peer_stats.$1" using 1:2 smooth sbezier with lines title "open sockets" lw 2 axis x1y2,\
"peer_stats.$1" using 1:2 smooth sbezier with lines lw 2 axis x1y2,\
"peer_stats.$1" using 1:3 smooth sbezier with lines lw 2 axis x1y2
set output "output_$1_groups.png"
plot "choke_group_stats.$1" \
using 1:8 smooth sbezier with lines lw 4,\
"" using 1:8+7*1 smooth sbezier with lines lw 4,\
"" using 1:8+7*2 smooth sbezier with lines lw 4,\
"" using 1:8+7*3 smooth sbezier with lines lw 4\
set format y "%.0s %cb"
set output "output_$1_bandwidth.png"
plot "bandwidth_stats.$1" using 1:2 smooth sbezier with lines lw 4,\
"bandwidth_stats.$1" using 1:4 smooth sbezier with lines lw 4,\
"choke_group_stats.$1" using 1:4+7*1 smooth sbezier with lines lw 3,\
"choke_group_stats.$1" using 1:7+7*1 smooth sbezier with lines lw 3,\
"choke_group_stats.$1" using 1:4+7*2 smooth sbezier with lines lw 3,\
"choke_group_stats.$1" using 1:4+7*3 smooth sbezier with lines lw 3,\
"peer_stats.$1" using 1:2 smooth sbezier with lines lw 2 axis x1y2,\
"peer_stats.$1" using 1:3 smooth sbezier with lines lw 2 axis x1y2
set output "output_$1_memory.png"
plot "bandwidth_stats.$1" using 1:6 smooth sbezier with lines lw 4 axis x1y1,\
"peer_stats.$1" using 1:2 smooth sbezier with lines lw 2 axis x1y2,\
"peer_stats.$1" using 1:3 smooth sbezier with lines lw 2 axis x1y2
#set yrange [0:3900000000.0]
#set y2range [0:2000]
set output "output_$1_pieces.png"
plot "bandwidth_stats.$1" using 1:6 smooth sbezier with lines lw 4 axis x1y1,\
"bandwidth_stats.$1" using 1:7 smooth sbezier with lines lw 4 axis x1y1,\
"<awk '{x=\$12*10; print \$1,x}' mincore_stats.$1" using 1:2 smooth sbezier with lines lw 4 title 'alloc /100s' axis x1y1,\
"<awk '{x=\$13*10; print \$1,x}' mincore_stats.$1" using 1:2 smooth sbezier with lines lw 4 title 'dealloc /100s' axis x1y1,\
"mincore_stats.$1" using 1:7 smooth sbezier with lines lw 2 axis x1y2,\
"mincore_stats.$1" using 1:8 smooth sbezier with lines lw 2 axis x1y2,\
"mincore_stats.$1" using 1:11 smooth sbezier with lines lw 2 axis x1y2,\
"<awk '{x=\$8/10; print \$1,x}' bandwidth_stats.$1" using 1:2 smooth sbezier with lines lw 2 title 'block count /10' axis x1y2,\
"bandwidth_stats.$1" using 1:10 smooth sbezier with lines lw 2 axis x1y2
#set yrange [*:*]
#set y2range [*:*]
set format y2 "%.0s %cb"
set output "output_$1_torrents.png"
plot "bandwidth_stats.$1" using 1:6 smooth sbezier with lines lw 4 axis x1y1,\
"bandwidth_stats.$1" using 1:9 smooth sbezier with lines lw 2 axis x1y2
set format y "%.0f"
set format y2 "%.0f"
set output "output_$1_incore.png"
#plot "bandwidth_stats.$1" using 1:(0) smooth sbezier with lines,
plot \
"mincore_stats.$1" using 1:2 smooth sbezier with lines lw 2 axis x1y1,\
"mincore_stats.$1" using 1:3 smooth sbezier with lines lw 4 axis x1y2,\
"mincore_stats.$1" using 1:4 smooth sbezier with lines lw 2 axis x1y1,\
"mincore_stats.$1" using 1:5 smooth sbezier with lines lw 4 axis x1y2
#
# Updated:
#
set output "output_$1_choke.png"
set style histogram columnstacked
plot "choke_group_stats.$1" \
using 1:2+7*1 smooth sbezier with lines lw 2 axis x1y2,\
"" using 1:3+7*1 smooth sbezier with lines lw 2 axis x1y2,\
"" using 1:5+7*1 smooth sbezier with lines lw 2 axis x1y2,\
"" using 1:6+7*1 smooth sbezier with lines lw 2 axis x1y2,\
"" using 1:2+7*2 smooth sbezier with lines lw 2 axis x1y2,\
"" using 1:3+7*2 smooth sbezier with lines lw 2 axis x1y2,\
"" using 1:2+7*3 smooth sbezier with lines lw 2 axis x1y2,\
"" using 1:3+7*3 smooth sbezier with lines lw 2 axis x1y2
EOF
-2
View File
@@ -1,2 +0,0 @@
#!/bin/bash
vmmap -interleaved $PPID > $1
-28
View File
@@ -1,28 +0,0 @@
# Choke Groups
## Settings
choke_group.insert = "leech_fast"
Create a new group named "leech_fast", accessible by the string or
index / reverse index according to order of insertion. E.g. '-1'
refers to the last inserted choke group, while 0 refers to the first.
All commands that applies to a group requires the first argument to be
the index, reverse index or the group name.
choke_group.tracker.mode.set = -1,"aggressive"
Set the tracker mode for torrents in this group.
choke_group.up.heuristics.set = -1,"upload_leech_experimental"
choke_group.down.heuristics.set = -1,"download_leech"
Set the heuristics used when deciding on which peers to choke and
unchoke. Use "strings.choke_heuristics{,.upload,.download}" to get a
list of the available heuristics.
choke_group.up.max.set = -1,250
choke_group.down.max.set = -1,500
Set the max total number of unchoked peers for all torrents in this choke group.
-64
View File
@@ -1,64 +0,0 @@
# IP filtering
## Introduction
## Make a new ip table
ip_tables.insert_table = <table_name>
Create a new empty table with the name table\_name, with the default
value returned being $0$.
There is currently no use of the generic ip tables commands.
## Add a new address block
ip_tables.add_address = <table_name>, 10.0.0.0/8, <value>
Set <value\> for all addresses in the address block, overwriting prior
values.
## Add a new address block
ip_tables.load = <table_name>, ~/foo.txt, <value>
Set <value\> for all addresses in the file foo.txt separated by
newline, similar to add\_address.
## Get value for address
ip_tables.get = <table_name>, 10.10.10.10
Returns the value set for an address, or the address block it belongs
to. The default is $0$.
## Size of data structures
ip_tables.size_data = <table_name>
Returns the size in bytes of all data structures for this table,
excluding the root class object itself. Note that the in-memory table is
dynamically consolidated, as such memory use will always be based on
actual fragmentation.
The table is a b-tree with 1024 nodes per branch.
## IPv4 filtering table
ipv4_filter.add_address = 10.0.0.0/8, unwanted
ipv4_filter.add_address = 11.0.0.0/8, preferred
ipv4_filter.load = ~/filters.txt, unwanted
ipv4_filter.get = 10.10.10.10
ipv4_filter.size_data =
The main ip filter, currently supporting unwanted (do not allow
connections) and preferred (currently used only in private code).
## Constants
strings.ip_filter =
=>
{ "unwanted", PeerInfo::flag_unwanted },
{ "preferred", PeerInfo::flag_preferred },
Constants used by ipv4\_filter values.
-85
View File
@@ -1,85 +0,0 @@
\section{IP filtering}
\subsection{Introduction}
\subsection{Make a new ip table}
\begin{verbatim}
ip_tables.insert_table = <table_name>
\end{verbatim}
Create a new empty table with the name 'table\_name', with the default
value returned being $0$.
There is currently no use of the generic ip tables commands.
\subsection{Add a new address block}
\begin{verbatim}
ip_tables.add_address = <table_name>, 10.0.0.0/8, <value>
\end{verbatim}
Set <value> for all addresses in the address block, overwriting prior
values.
\subsection{Add a new address block}
\begin{verbatim}
ip_tables.load = <table_name>, ~/foo.txt, <value>
\end{verbatim}
Set <value> for all addresses in the file 'foo.txt' separated by
newline, similar to 'add\_address'.
\subsection{Get value for address}
\begin{verbatim}
ip_tables.get = <table_name>, 10.10.10.10
\end{verbatim}
Returns the value set for an address, or the address block it belongs
to. The default is $0$.
\subsection{Size of data structures}
\begin{verbatim}
ip_tables.size_data = <table_name>
\end{verbatim}
Returns the size in bytes of all data structures for this table,
excluding the root class object itself. Note that the in-memory table
is dynamically consolidated, as such memory use will always be based
on actual fragmentation.
The table is a b-tree with 1024 nodes per branch.
\subsection{IPv4 filtering table}
\begin{verbatim}
ipv4_filter.add_address = 10.0.0.0/8, unwanted
ipv4_filter.add_address = 11.0.0.0/8, preferred
ipv4_filter.load = ~/filters.txt, unwanted
ipv4_filter.get = 10.10.10.10
ipv4_filter.size_data =
\end{verbatim}
The main ip filter, currently supporting 'unwanted' (do not allow
connections) and 'preferred' (currently used only in private code).
\subsection{Constants}
\begin{verbatim}
strings.ip_filter =
=>
{ "unwanted", PeerInfo::flag_unwanted },
{ "preferred", PeerInfo::flag_preferred },
\end{verbatim}
Constants used by ipv4_filter values.
-60
View File
@@ -1,60 +0,0 @@
# Logging
## Opening log files
# log.open_file = "log name", "file path"
log.open_file = "rtorrent.log", (cat,/tmp/rtorrent.log.,(system.pid))
A newly opened log file is not connected to any logging events.
Some control over formatting will be provided at a later date.
## Appending to log files
log.append_file = "rtorrent.log", "/tmp/rtorrent.log"
The `log.open_file` clears any existing contents of the file. If you'd
prefer to have a single log file that persists across application
restarts, you can use the `log.append_file` in place of the
`log.open_file` configuration key.
## Adding outputs to events
# log.add_output = "logging event", "log name"
log.add_output = "info", "rtorrent.log"
log.add_output = "dht_all", "tracker.log"
log.add_output = "tracker_events", "tracker.log"
log.add_output = "tracker_requests", "tracker.log"
Each log handle can be added to multiple different logging events.
## Logging events
"critical"
"error"
"warn"
"notice"
"info"
"debug"
The above events receive logging events from all the sub-groups
displayed below, and each event also receiving events from the event
above in importance.
Thus some high-volume sub-group events such as “tracker\_debug” are not
part of “debug” and every “warn” event will receive events from “error”,
“critical”.
"connection_*"
"dht_*"
"peer_*"
"rpc_*"
"storage_*"
"thread_*"
"tracker_*"
"torrent_*"
All sub-groups have events from “critical” to “debug” defined.
-62
View File
@@ -1,62 +0,0 @@
\section{Logging}
\subsection{Opening log files}
\begin{verbatim}
# log.open_file = "log name", "file path"
log.open_file = "rtorrent.log", (cat,/tmp/rtorrent.log.,(system.pid))
\end{verbatim}
A newly opened log file is not connected to any logging events.
Some control over formatting will be provided at a later date.
\subsection{Adding outputs to events}
\begin{verbatim}
# log.add_output = "logging event", "log name"
log.add_output = "info", "rtorrent.log"
log.add_output = "dht_all", "tracker.log"
log.add_output = "tracker_events", "tracker.log"
log.add_output = "tracker_requests", "tracker.log"
\end{verbatim}
Each log handle can be added to multiple different logging events.
\subsection{Logging events}
\begin{verbatim}
"critical"
"error"
"warn"
"notice"
"info"
"debug"
\end{verbatim}
The above events receive logging events from all the sub-groups
displayed below, and each event also reciving events from the event
above in importance.
Thus some high-volume sub-group events such as ``tracker\_debug'' are
not part of ``debug'' and every ``warn'' event will receive events
from ``error'', ``critical''.
\begin{verbatim}
"connection_*"
"dht_*"
"peer_*"
"rpc_*"
"storage_*"
"thread_*"
"tracker_*"
"torrent_*"
\end{verbatim}
All sub-groups have events from ``critical'' to ``debug'' defined.
-559
View File
@@ -1,559 +0,0 @@
.\" This manpage has been automatically generated by docbook2man
.\" from a DocBook document. This tool can be found at:
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
.\" Please send any bug reports, improvements, comments, patches,
.\" etc. to Steve Cheng <steve@ggi-project.org>.
.TH "RTORRENT" "1" "14 May 2009" "BitTorrent client for ncurses" ""
.SH NAME
rtorrent \- a BitTorrent client for ncurses
.SH SYNOPSIS
\fBrtorrent\fR [ \fB-h\fR ] [ \fB-n\fR ] [ \fB-o key1=opt1,...\fR ] [ \fB-O key=opt\fR ] [ \fBURL | FILE\fR\fI ...\fR ]
.SH "DESCRIPTION"
.PP
\fBrtorrent\fR is a BitTorrent client for ncurses, using
the \fBlibtorrent\fR library. The client and library is
written in C++ with emphasis on speed and efficiency, while delivering
equivalent features to those found in GUI based clients in an ncurses
client.
.PP
Most of the options below have their own default unit in addition to
supporting B, K, M and G suffixes.
.SH "KEYBOARD CONTROL"
.PP
.SS "GLOBAL KEYS"
.TP
\fB^q\fR
Initiate shutdown, press again to force the shutdown and
skip sending the stop signal to trackers.
.TP
\fBup | down | left | right arrow keys\fR
.TP
\fB^P | ^N | ^B | ^F\fR
Select entries or change windows. The right arrow key or ^F is often
used for viewing details about the selected entry, while the left
arrow key or ^B often returns to the previous screen.
.TP
\fBa | s | d\fR
Increase the upload throttle by 1/5/50 KB.
.TP
\fBA | S | D\fR
Increase the download throttle by 1/5/50 KB.
.TP
\fBz | x | c\fR
Decrease the upload throttle by 1/5/50 KB.
.TP
\fBZ | X | C\fR
Decrease the download throttle by 1/5/50 KB.
.SS "MAIN VIEW KEYS"
.TP
\fB->\fR
View download.
.TP
\fB0 - 9\fR
Change view.
.TP
\fB^S\fR
Start download.
.TP
\fB^D\fR
Stop an active download, or remove a stopped download.
.TP
\fB^K\fR
Close a torrent and its files.
.TP
\fB^E\fR
Set the 'create/resize queued' flags on all files in a torrent. This
is necessary if the underlying files in a torrent have been deleted or
truncated, and thus rtorrent must recreate them.
.TP
\fB^R\fR
Initiate hash check of torrent.
.TP
\fB^O\fR
Change the destination directory of the download. The torrent must be
closed.
.TP
\fB^X\fR
Call commands or change settings.
.TP
\fB^B\fR
Set download to perform initial seeding. Only use when
you are the first and only seeder so far for the download.
.TP
\fB+ | -\fR
Change the priority of the download.
.TP
\fBbackspace\fR
Add torrent using an URL or file path. Use
\fBtab\fR to view directory content and do
auto-complete.
.TP
\fBl\fR
View log. Exit by pressing the space-bar.
.TP
\fBU\fR
Delete the file the torrent is tied to, and clear the association.
.TP
\fBI\fR
Toggle whether torrent ignores ratio settings.
.TP
\fBF\fR
Add a temporary name based regex filter to the current view.
.SS "DOWNLOAD VIEW KEYS"
.TP
\fB->\fR
View torrent file list. Use the space-bar to change the file priority
and \fB*\fR to change the priority of all
files. Use \fB/\fR to collapse the directories. OUTDATED
.TP
\fB1 | 2\fR
Adjust max uploads.
.TP
\fB3 | 4\fR
Adjust min peers.
.TP
\fB5 | 6\fR
Adjust max peers.
.TP
\fBu\fR
Display transfering blocks.
.TP
\fBi\fR
Display chunk rarity.
.TP
\fBo\fR
Display the tracker list. Cycle the trackers in a group with the
space-bar.
.TP
\fBp\fR
View peer and torrent information.
.TP
\fBt | T\fR
Initiate tracker request. Use capital T to force the request, ignoring
the "min interval" set by the tracker.
.TP
\fBk\fR
Disconnect peer.
.TP
\fB*\fR
Choke/Snub peer.
.SH "OPTIONS"
.TP
\fB-b \fIa.b.c.d\fB\fR
Bind listening socket and outgoing connections to this network
interface address.
.TP
\fB-d \fIdirectory\fB\fR
Set the default download directory. Defaults to "./".
.TP
\fB-h\fR
Display help and exit.
.TP
\fB-i \fIa.b.c.d\fB\fR
Set the address reported to the tracker.
.TP
\fB-n\fR
Don't load ~/.rtorrent.rc on startup.
.TP
\fB-o key1=opt1,...\fR
.TP
\fB-O key=opt\fR
Set any number of options, see the SETTINGS section. The options given
here override the resource files. Use capital \fB-O\fR
to allow comma in the option.
.TP
\fB-p \fIa-b\fB\fR
Try to open a listening port in the range \fBa\fR up to
and including \fBb\fR\&.
.TP
\fB-s \fIdirectory\fB\fR
Session management will be enabled and the torrent files for all open
downloads will be stored in this directory. Only one instance of
rtorrent should be used with each session directory, though at the
moment no locking is done. An empty string will disable the session
directory.
.SH "GENERAL SETTINGS"
.PP
.TP
\fBbind = \fIa.b.c.d\fB\fR
Bind listening socket and outgoing connections to this network
interface address.
.TP
\fBip = \fIa.b.c.d\fB\fR
.TP
\fBip = \fIhostname\fB\fR
Set the address reported to the tracker.
.TP
\fBport_range = \fIa-b\fB\fR
Try to open a listening port in the range \fBa\fR up to
and including \fBb\fR\&.
.TP
\fBport_random = \fIyes | no\fB\fR
Open the listening port at a random position in the port range.
.TP
\fBcheck_hash = \fIyes | no\fB\fR
Perform hash check on torrents that have finished downloading.
.TP
\fBdirectory = \fIdirectory\fB\fR
Set the default download directory. Defaults to "./".
.TP
\fBsession = \fIdirectory\fB\fR
Session management will be enabled and the torrent files for all open
downloads will be stored in this directory. Only one instance of
rtorrent can be used per session directory. An empty string will
disable the session directory.
.TP
\fBhttp_proxy = \fIurl\fB\fR
Use a http proxy. Use an empty string to disable.
.TP
\fBencoding_list = \fIencoding\fB\fR
Add a preferred filename encoding to the list. The encodings are
attempted in the order they are inserted, if none match the torrent
default is used.
.TP
\fBencryption = \fIoption\fB,\fI\&...\fB\fR
Set how rtorrent should deal with encrypted Bittorrent connections. By
default, encryption is disabled, equivalent to specifying the option
\fBnone\fR\&. Alternatively, any number of the following
options may be specified:
\fBallow_incoming\fR (allow incoming encrypted connections),
\fBtry_outgoing\fR (use encryption for outgoing connections),
\fBrequire\fR (disable unencrypted handshakes),
\fBrequire_RC4\fR (also disable plaintext transmission after the
initial encrypted handshake),
\fBenable_retry\fR (if the initial outgoing connection fails, retry
with encryption turned on if it was off or off if it was on),
\fBprefer_plaintext\fR (choose plaintext when peer offers a choice
between plaintext transmission and RC4 encryption, otherwise RC4 will be used).
.TP
\fBpeer_exchange = \fIyes | no\fB\fR
Enable/disable peer exchange for torrents that aren't marked private. Disabled by default.
.TP
\fBschedule = \fIid\fB,\fIstart\fB,\fIinterval\fB,\fIcommand\fB\fR
Call \fBcommand\fR every \fBinterval\fR
seconds, starting from \fBstart\fR\&. An
\fBinterval\fR of zero calls the task once, while a
\fBstart\fR of zero calls it immediately. Currently
\fBcommand\fR is forwarded to the option handler.
\fBstart\fR and \fBinterval\fR may
optionally use a time format, \fBdd:hh:mm:ss\fR\&. F.ex to
start a task every day at \fB18:00\fR, use
\fB18:00:00,24:00:00\fR\&.
.TP
\fBschedule_remove = \fIid\fB\fR
Delete \fBid\fR from the scheduler.
.TP
\fBstart_tied =\fR
Start torrents that are tied to filenames that have been re-added.
.TP
\fBstop_untied =\fR
.TP
\fBclose_untied =\fR
.TP
\fBremove_untied =\fR
Stop, close or remove the torrents that are tied to filenames that
have been deleted. Clear the association with the 'U' key.
.TP
\fBclose_low_diskspace = \fIspace\fB\fR
Close any active torrents on filesystems with less than
\fBspace\fR diskspace left. Use with
the \fBschedule\fR option. A default scheduled event
with id \fBlow_diskspace\fR is set to 500Mb.
.TP
\fBload = \fIfile\fB\fR
.TP
\fBload_verbose = \fIfile\fB\fR
.TP
\fBload_start = \fIfile\fB\fR
.TP
\fBload_start_verbose = \fIfile\fB\fR
Load and possibly start a file, or possibly multiple files by using the
wild-card "*". This is meant for use with
\fBschedule\fR, though ensure that the
\fBstart\fR is non-zero. The loaded file will be tied
to the filename provided.
.TP
\fBimport = \fIfile\fB\fR
.TP
\fBtry_import = \fIfile\fB\fR
Load a resource file. \fBtry_import\fR does not throw
torrent::input_error exception on bad input.
.TP
\fBstop_on_ratio = \fImin_ratio\fB\fR
.TP
\fBstop_on_ratio = \fImin_ratio\fB,\fImin_upload\fB\fR
.TP
\fBstop_on_ratio = \fImin_ratio\fB,\fImin_upload\fB,\fImax_ratio\fB\fR
Stop torrents when they reach the given upload ratio
\fBmin_ratio\fR in percent. If the optional
\fBmin_upload\fR is given, require a total
upload amount of this many bytes as well. If the optional
\fBmax_ratio\fR is given, stop the torrent
when reaching this ratio regardless of the total upload
amount. Exclude certain torrent by pressing
\fBShift+I\fR in the downlist list.
Use with the \fBschedule\fR option.
.TP
\fBon_insert = \fIid\fB,\fIcommand\fB\fR
.TP
\fBon_erase = \fIid\fB,\fIcommand\fB\fR
.TP
\fBon_open = \fIid\fB,\fIcommand\fB\fR
.TP
\fBon_close = \fIid\fB,\fIcommand\fB\fR
.TP
\fBon_start = \fIid\fB,\fIcommand\fB\fR
.TP
\fBon_stop = \fIid\fB,\fIcommand\fB\fR
.TP
\fBon_hash_queued = \fIid\fB,\fIcommand\fB\fR
.TP
\fBon_hash_removed = \fIid\fB,\fIcommand\fB\fR
.TP
\fBon_hash_done = \fIid\fB,\fIcommand\fB\fR
.TP
\fBon_finished = \fIid\fB,\fIcommand\fB\fR
Call a command on a download when its state changes. Only a subset of
commands are available.
.SH "THROTTLE SETTINGS"
.TP
\fBupload_rate = \fIKB\fB\fR
.TP
\fBdownload_rate = \fIKB\fB\fR
Set the maximum global uploand and download rates.
.TP
\fBmin_peers = \fIvalue\fB\fR
.TP
\fBmax_peers = \fIvalue\fB\fR
Set the minimum and maximum number of peers to allow in each download.
.TP
\fBmin_peers_seed = \fIvalue\fB\fR
.TP
\fBmax_peers_seed = \fIvalue\fB\fR
Set the minimum nad maximum number of peers to allow while seeding, or
-1 (default) to use max_peers.
.TP
\fBmax_uploads = \fIvalue\fB\fR
Set the maximum number of simultaneous uploads per download.
.TP
\fBmax_uploads_div = \fIvalue\fB\fR
.TP
\fBmax_downloads_div = \fIvalue\fB\fR
Change the divider used to calculate the max upload and download slots
to use when the throttle is changed. Disable by
setting \fB0\fR\&.
.TP
\fBmax_uploads_global = \fIvalue\fB\fR
.TP
\fBmax_downloads_global = \fIvalue\fB\fR
Max upload and download slots allowed. Disable by
setting \fB0\fR\&.
.TP
\fBthrottle_up = \fIname\fB, \fIupload_rate\fB\fR
.TP
\fBthrottle_down = \fIname\fB, \fIdownload_rate\fB\fR
Define secondary throttle and/or set the given upload or download rate. Attach to a download with the d.set_throttle_name=name command
or switch throttles with Ctrl-T. Download must be stopped when changing throttles. Note that secondary throttles only work if the
global upload/download is throttled. Setting a download to use the \fBNULL\fR throttle makes the download unthrottled
even when there is a global throttle. Note that this special case bypasses the global throttle entirely, and as such its rate and
transfer amounts are not included in the global statistics.
.TP
\fBthrottle_ip = \fIname\fB, \fIhost\fB\fR
.TP
\fBthrottle_ip = \fIname\fB, \fInetwork/prefix\fB\fR
.TP
\fBthrottle_ip = \fIname\fB, \fIstart\fB, \fIend\fB\fR
Use the given secondary throttle for a host, CIDR network or IP range. All peers with a matching IP will use this throttle instead
of the global throttle or a custom download throttle. The name may be \fBNULL\fR to make these peers unthrottled, with
the same caveats as explained above.
.SH "TRACKER RELATED SETTINGS"
.PP
Tracker related settings.
.TP
\fBenable_trackers = \fIyes\fB\fR
Set to \fBno\fR to disable all tracker requests. Useful
for disabling rtorrent with the \fBschedule\fR command.
.TP
\fBtracker_dump = \fIfilename\fB\fR
Dump tracker requests to \fBfilename\fR, disable by
supplying an empty string. Only torrents loaded while
\fBtracker_dump\fR contains a non-empty string will be
logged at the moment, although disabling it will work as expected.
.TP
\fBtracker_numwant = \fInumber\fB\fR
Set the numwant field sent to the tracker, which indicates how many
peers we want. A negative value disables this feature.
.TP
\fBuse_udp_trackers = \fIyes\fB\fR
Use UDP trackers. Disable if you are behind a firewall, etc, that does
not allow connections to UDP trackers.
.TP
\fBdht = \fIdisabled|off|auto|on\fB\fR
Support for querying the distributed hash table (DHT) to find peers for trackerless
torrents or when all trackers are down. Set to \fBdisable\fR to completely
disable DHT, \fBoff\fR (default) to enable DHT but to not start the
DHT server, \fBauto\fR to automatically start and stop the DHT server
as needed or \fBon\fR for permanently keeping the DHT server running.
When set to automatic, the DHT server will start up when the first non-private torrent
is started, and will stop 15-30 minutes after the last non-private torrent is
stopped (or when rTorrent quits). For DHT to work, a session directory must be set (for
saving the DHT cache).
.TP
\fBdht_port = \fInumber\fB\fR
Set the UDP listen port for DHT. Defaults to 6881.
.TP
\fBdht_add_node = \fIhost[:port]\fB\fR
Not intended for use in the configuration file but as one-time option in the
client or on the command line to bootstrap an empty DHT node table. Contacts
the given node and attempts to bootstrap from it if it replies.
The port is optional, with port 6881 being used by default.
.TP
\fBhttp_capath = \fIpath\fB\fR
.TP
\fBhttp_cacert = \fIfilename\fB\fR
Set the certificates to use in http requests. See Curl's
CURLOPT_CAPATH and CURLOPT_CAINFO options for further information.
.SH "USER-INTERFACE SETTINGS"
.PP
Display related settings.
.TP
\fBview_add = \fIname\fB\fR
Create a new view.
.TP
\fBview_sort = \fIname\fB\fR
.TP
\fBview_sort = \fIname\fB,\fIseconds\fB\fR
Sort a view according the the criteria set by
\fBview_sort_current\fR\&. If the optional argument is
supplied, the view is not sorted if a change happened during the last
\fBseconds\fR\&. This command is meant to be used with
\fBschedule\fR\&.
.TP
\fBview_sort_new = \fIname\fB,\fI\&...\fB\fR
.TP
\fBview_sort_current = \fIname\fB,\fI\&...\fB\fR
Set the sorting criteria for when new elements inserted or
\fBview_sort\fR is called. The list can contain any
number of criteria, including zero, from the following:
\fBname\fR, \fBname_reverse\fR,
\fBstopped\fR, \fBstarted\fR,
\fBcomplete\fR, \fBincomplete\fR,
\fBstate_changed\fR,
\fBstate_changed_reverse\fR
.TP
\fBkey_layout = \fIqwerty|azerty|qwertz|dvorak\fB\fR
Change the key-bindings.
.SH "FILE-SYSTEM SETTINGS"
.PP
File-system related settings.
.TP
\fBmax_file_size = \fIsize\fB\fR
Set the maximum size a file can have. Disable by
passing \fB-1\fR\&.
.TP
\fBsplit_file_size = \fIsize\fB\fR
Split files in a torrent larger than \fBsize\fR into
seperate files. Disable by passing \fB-1\fR\&.
.TP
\fBsplit_suffix = \fIstring\fB\fR
Set the suffix used on split files. Defaults
to \fB\&.part\fR\&.
.SH "DOWNLOAD SETTINGS"
.PP
Settings that require a download as a target, the options need to be
called through f.ex \fBon_finished\fR\&.
.TP
\fBcreate_link = \fItype\fB,\fIpath\fB,\fIsuffix\fB\fR
.TP
\fBdelete_link = \fItype\fB,\fIpath\fB,\fIsuffix\fB\fR
Create or delete a symbolic link. The link path is the concatenation
of \fBpath\fR, the result of
the \fBtype\fR on the download,
and \fBsuffix\fR\&.
Available types are; \fBbase_path\fR uses the base path
of the download, \fBbase_filename\fR uses the base
filename of the download, \fBtied\fR uses the path of
the file the download is tied to, see \fBstart_tied\fR\&.
.SH "ADVANCED SETTINGS"
.PP
This list contains settings users shouldn't need to touch, some may
even cause crashes or similar if incorrectly set.
.TP
\fBhash_read_ahead = \fIMB\fB\fR
Configure how far ahead we ask the kernel to read when doing hash
checking. The hash checker uses madvise(..., MADV_WILLNEED) for the
requests.
.TP
\fBhash_interval = \fIms\fB\fR
Interval between attempts to check the hash when the chunk is not in
memory, in milliseconds.
.TP
\fBhash_max_tries = \fItries\fB\fR
Number of attempts to check the hash while using the mincore status,
before forcing. Overworked systems might need lower values to get a
decent hash checking rate.
.TP
\fBmax_open_files = \fIvalue\fB\fR
Number of files to simultaneously keep open. LibTorrent dynamically
opens and closes files as necessary when mapping files to
memory. Default is based on sysconf(_SC_OPEN_MAX).
You probably only think you know what this option does, so don't touch
it.
.TP
\fBmax_open_sockets = \fIvalue\fB\fR
Number of network sockets to simultaneously keep open. This value is
set to a reasonable value based on \fBsysconf(_SC_OPEN_MAX)\fR\&.
.TP
\fBmax_open_http = \fIvalue\fB\fR
Number of sockets to simultaneously keep open. This value is set
to \fB32\fR by default.
.TP
\fBmax_memory_usage = \fIbytes\fB\fR
Set the max amount of memory space used to mapping file chunks. This
may also be set using \fBulimit -m\fR where 3/4 will be
allocated to file chunks.
.TP
\fBsend_buffer_size = \fIvalue\fB\fR
.TP
\fBreceive_buffer_size = \fIvalue\fB\fR
Adjust the send and receive buffer size for socket.
.TP
\fBumask = \fI0022\fB\fR
Set the umask for this process, which is applied to all files created
by the program.
.TP
\fBcwd = \fIdirectory\fB\fR
Changes the working directory of the process using
\fBchdir\fR\&.
.TP
\fBsession_on_completion = \fIyes\fB\fR
Controls if the session torrent is saved when a torrent finishes. By
default on.
.TP
\fBsession_lock = \fIyes\fB\fR
Controls if a lock file is created in the session directory on startup.
.TP
\fBsession_save = \fR
Save the session files for all downloads.
.TP
\fBtos = \fIdefault|lowdelay|throughput|reliability|mincost\fB\fR
.TP
\fBtos = \fIhex\fB\fR
Change the TOS of peer connections, by default set to
\fBthroughput\fR\&. If the option is set to
\fBdefault\fR then the system default TOS is used. A
hex value may be used for non-standard settings.
.TP
\fBhandshake_log = \fIyes\fB\fR
Enable logging of the peer handshake. This generates a large number of
log messages, but may be useful to debug connection problems.
.SH "AUTHORS"
.PP
Jari "Rakshasa" Sundell
File diff suppressed because it is too large Load Diff
-30
View File
@@ -1,30 +0,0 @@
#/bin/bash
gnuplot << EOF
set terminal png size 1024 * 1, 768 enhanced
set xdata time
set timefmt "%s"
set format x "%H:%M"
set y2tics
set autoscale xfix
set key autotitle columnhead
set format y "%.0f"
set format y2 "%.0f"
set output "output_$1_incore_sync.png"
plot \
"instrumentation_mincore.log.$1" using 1:6 title 'success' with lines lw 4 axis x1y1,\
"instrumentation_mincore.log.$1" using 1:7 title 'failed' with lines lw 4 axis x1y1,\
"instrumentation_mincore.log.$1" using 1:8 title 'not synced' with lines lw 4 axis x1y2,\
"instrumentation_mincore.log.$1" using 1:9 title 'not deallocated' with lines lw 4 axis x1y2
set format y "%.0g"
set output "output_$1_incore_alloc.png"
plot \
"instrumentation_mincore.log.$1" using 1:12 title 'allocations' with lines lw 2 axis x1y1,\
"instrumentation_mincore.log.$1" using 1:13 title 'deallocations' with lines lw 4 axis x1y1,\
"instrumentation_mincore.log.$1" using 1:10 title 'alloc failed' with lines lw 2 axis x1y2
EOF
-49
View File
@@ -1,49 +0,0 @@
#/bin/bash
gnuplot << EOF
set terminal png size 1024,768 enhanced
set xdata time
set timefmt "%s"
set format x "%H:%M"
set y2tics
set autoscale xfix
set key autotitle columnhead
# set datafile missing '0'
set format y "%.0f"
set format y2 "%.0f"
set yrange [0:]
set y2range [0:]
set output "output_$1_transfer_requests.png"
plot \
"instrumentation_transfers.log.$1" using 1:2 title 'delegated' with lines lw 4 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:3 title 'downloading' with lines lw 4 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:4 title 'finished' with lines lw 4 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:5 title 'skipped' with lines lw 2 axis x1y2,\
"instrumentation_transfers.log.$1" using 1:6 title 'unknown' with lines lw 2 axis x1y2,\
"instrumentation_transfers.log.$1" using 1:7 title 'unordered' with lines lw 2 axis x1y2,\
"instrumentation_transfers.log.$1" using 1:19 title 'unaccounted' with lines lw 2 axis x1y2
set output "output_$1_transfer_queues.png"
plot \
"instrumentation_transfers.log.$1" using 1:8 title 'queue added' with lines lw 2 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:9 title 'queue moved' with lines lw 2 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:10 title 'queue removed' with lines lw 2 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:11 title 'queue total' with lines lw 4 axis x1y2,\
"instrumentation_transfers.log.$1" using 1:12 title 'unordered added' with lines lw 2 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:13 title 'unordered moved' with lines lw 2 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:14 title 'unordered removed' with lines lw 2 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:15 title 'unordered total' with lines lw 4 axis x1y2,\
"instrumentation_transfers.log.$1" using 1:16 title 'stalled added' with lines lw 2 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:17 title 'stalled moved' with lines lw 2 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:18 title 'stalled removed' with lines lw 2 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:19 title 'stalled total' with lines lw 4 axis x1y2,\
"instrumentation_transfers.log.$1" using 1:20 title 'choked added' with lines lw 2 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:21 title 'choked moved' with lines lw 2 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:22 title 'choked removed' with lines lw 2 axis x1y1,\
"instrumentation_transfers.log.$1" using 1:23 title 'choked total' with lines lw 4 axis x1y2
EOF
-140
View File
@@ -1,140 +0,0 @@
#############################################################################
# This is an (old) example resource file for rTorrent.
# Copy to ~/.rtorrent.rc and enable/modify the options as needed.
# Remember to uncomment the options you wish to enable.
#
# See 'rtorrent.rc-example' for a newer, basic configuration.
#
# Sample: https://github.com/rakshasa/rtorrent/wiki/CONFIG-Template
# Complete: https://rtorrent-docs.readthedocs.io/en/latest/cmd-ref.html
# Useful: https://rtorrent-docs.readthedocs.io/en/latest/use-cases.html
# Manual: https://rtorrent-docs.readthedocs.io/en/latest/
# Convert: https://github.com/rakshasa/rtorrent/wiki/rTorrent-0.9-Comprehensive-Command-list-(WIP)
# Handbook: https://media.readthedocs.org/pdf/rtorrent-docs/latest/rtorrent-docs.pdf
# File: https://github.com/rakshasa/rtorrent/blob/master/doc/rtorrent.rc
#############################################################################
# Maximum and minimum number of peers to connect to per torrent.
#
#throttle.min_peers.normal.set = 40
#throttle.max_peers.normal.set = 100
# Same as above but for seeding completed torrents.
# "-1" = same as downloading.
#
#throttle.min_peers.seed.set = 10
#throttle.max_peers.seed.set = 50
# Maximum number of simultaneous uploads per torrent.
#
#throttle.max_uploads.set = 15
# Global upload and download rate in KiB.
# "0" for unlimited.
#
#throttle.global_down.max_rate.set_kb = 0
#throttle.global_up.max_rate.set_kb = 0
# Default directory to save the downloaded torrents.
#
#directory.default.set = ./
# Default session directory. Make sure you don't run multiple instance
# of rTorrent using the same session directory. Perhaps using a
# relative path?
#
#session.path.set = ./session
# Watch a directory for new torrents, and stop those that have been
# deleted. Use directory.watch.ready for network shares or other watch
# directories where files may be copied or written directly into place.
# Do not mix directory.watch.ready and directory.watch.added on the same
# watch directory.
#
#directory.watch.ready = ./watch, load.start
#schedule2 = watch_directory,5,5,load.start=./watch/*.torrent
# Close torrents when disk-space is low.
#
#schedule2 = low_diskspace,5,60,close_low_diskspace=100M
# The IP address reported to the tracker.
#
#network.local_address.set = 127.0.0.1
#network.local_address.set = rakshasa.no
# The IP address the listening socket and outgoing connections is
# bound to.
#
#network.bind_address.set = 127.0.0.1
#network.bind_address.set = rakshasa.no
# Port range to use for listening.
#
#network.port_range.set = 6890-6999
# Start opening ports at a random position within the port range.
#
#network.port_random.set = no
# Set RPC type
#network.rpc.use_xmlrpc.set = true
#network.rpc.use_jsonrpc.set = true
# Check hash for finished torrents. Might be useful until the bug is
# fixed that causes lack of disk-space not to be properly reported.
#
#pieces.hash.on_completion.set = no
# Set whether the client should try to connect to UDP trackers.
#
#trackers.use_udp.set = yes
# Alternative calls to bind and IP that should handle dynamic IP's.
#
#schedule2 = ip_tick,0,1800,ip=rakshasa
#schedule2 = bind_tick,0,1800,bind=rakshasa
# Encryption options, set to none (default) or any combination of the following:
# allow_incoming, try_outgoing, require, require_RC4, enable_retry, prefer_plaintext
#
# The example value allows incoming encrypted connections, starts unencrypted
# outgoing connections but retries with encryption if they fail, preferring
# plain-text to RC4 encryption after the encrypted handshake.
#
# protocol.encryption.set = allow_incoming,enable_retry,prefer_plaintext
# Enable DHT support for trackerless torrents or when all trackers are down.
# May be set to "disable" (completely disable DHT), "off" (do not start DHT),
# "auto" (start and stop DHT as needed), or "on" (start DHT immediately).
# The default is "off". For DHT to work, a session directory must be defined.
#
# dht.mode.set = auto
# UDP port to use for DHT.
#
#dht.port.set = 6881
# Enable peer exchange (for torrents not marked private).
#
#protocol.pex.set = yes
# Set download list layout style ("full", "compact").
#
#ui.torrent_list.layout.set = "full"
# Set navigation keymap style ("vi", "emacs"). Default is "emacs".
#
#ui.keymap.style.set = "emacs"
# Run rTorrent as a daemon, controlled via XMLRPC.
#
#system.daemon.set = false
# SCGI Connectivity (for alternative rtorrent interfaces, XMLRPC)
# Use a IP socket with scgi_port, or a Unix socket with scgi_local.
# schedule can be used to set permissions on the unix socket.
#
#network.scgi.open_port = "127.0.0.1:5000"
#network.scgi.open_local = (cat,(session.path),/rpc.sock)
#schedule2 = socket_chmod, 0, 0, "execute.nothrow=chmod,770,(cat,(session.path),/rpc.sock)"
-111
View File
@@ -1,111 +0,0 @@
#############################################################################
# A minimal rTorrent configuration that provides the basic features
# you want to have in addition to the built-in defaults.
#
# See https://github.com/rakshasa/rtorrent/wiki/CONFIG-Template
# for an up-to-date version.
#############################################################################
# Instance layout (base paths)
method.insert = cfg.basedir, private|const|string, (cat,"/home/USERNAME/rtorrent/")
method.insert = cfg.download, private|const|string, (cat,(cfg.basedir),"download/")
method.insert = cfg.logs, private|const|string, (cat,(cfg.basedir),"log/")
method.insert = cfg.logfile, private|const|string, (cat,(cfg.logs),"rtorrent-",(system.time),".log")
method.insert = cfg.session, private|const|string, (cat,(cfg.basedir),".session/")
method.insert = cfg.watch, private|const|string, (cat,(cfg.basedir),"watch/")
# Create instance directories
execute.throw = sh, -c, (cat,\
"mkdir -p \"",(cfg.download),"\" ",\
"\"",(cfg.logs),"\" ",\
"\"",(cfg.session),"\" ",\
"\"",(cfg.watch),"/load\" ",\
"\"",(cfg.watch),"/start\" ")
# Listening port for incoming peer traffic (fixed; you can also randomize it)
network.port_range.set = 50000-50000
network.port_random.set = no
# Tracker-less torrent and UDP tracker support
# (conservative settings for 'private' trackers, change for 'public')
dht.mode.set = disable
protocol.pex.set = no
trackers.use_udp.set = no
# Peer settings
throttle.max_uploads.set = 100
throttle.max_uploads.global.set = 250
throttle.min_peers.normal.set = 20
throttle.max_peers.normal.set = 60
throttle.min_peers.seed.set = 30
throttle.max_peers.seed.set = 80
trackers.numwant.set = 80
protocol.encryption.set = allow_incoming,try_outgoing,enable_retry
# Limits for file handle resources, this is optimized for
# an `ulimit` of 1024 (a common default). You MUST leave
# a ceiling of handles reserved for rTorrent's internal needs!
network.http.max_open.set = 50
network.max_open_files.set = 600
network.max_open_sockets.set = 300
# Memory resource usage (increase if you have a large number of items loaded,
# and/or the available resources to spend)
pieces.memory.max.set = 1800M
network.xmlrpc.size_limit.set = 4M
# Basic operational settings (no need to change these)
session.path.set = (cat, (cfg.session))
directory.default.set = (cat, (cfg.download))
log.execute = (cat, (cfg.logs), "execute.log")
##log.xmlrpc = (cat, (cfg.logs), "xmlrpc.log")
execute.nothrow = sh, -c, (cat, "echo >",\
(session.path), "rtorrent.pid", " ", (system.pid))
# Other operational settings (check & adapt)
encoding.add = utf8
system.umask.set = 0027
system.cwd.set = (directory.default)
network.http.dns_cache_timeout.set = 25
schedule2 = monitor_diskspace, 15, 60, ((close_low_diskspace, 1000M))
##pieces.hash.on_completion.set = no
##view.sort_current = seeding, greater=d.ratio=
##keys.layout.set = qwerty
##network.http.capath.set = "/etc/ssl/certs"
##network.http.ssl_verify_peer.set = 0
##network.http.ssl_verify_host.set = 0
#network.rpc.use_xmlrpc.set = true
#network.rpc.use_jsonrpc.set = true
# Some additional values and commands
method.insert = system.startup_time, value|const, (system.time)
method.insert = d.data_path, simple,\
"if=(d.is_multi_file),\
(cat, (d.directory), /),\
(cat, (d.directory), /, (d.name))"
method.insert = d.session_file, simple, "cat=(session.path), (d.hash), .torrent"
# Watch directories (add more as you like, but use unique schedule names)
schedule2 = watch_start, 10, 10, ((load.start_verbose, (cat, (cfg.watch), "start/*.torrent")))
schedule2 = watch_load, 11, 10, ((load.verbose, (cat, (cfg.watch), "load/*.torrent")))
# Run the rTorrent process as a daemon in the background
# (and control via XMLRPC sockets)
#system.daemon.set = true
#network.scgi.open_local = (cat,(session.path),rtorrent.sock)
#execute.nothrow = chmod,770,(cat,(session.path),rtorrent.sock)
# Logging:
# Levels = critical error warn notice info debug
# Groups = connection_* dht_* peer_* rpc_* storage_* thread_* tracker_* torrent_*
print = (cat, "Logging to ", (cfg.logfile))
log.open_file = "log", (cfg.logfile)
log.add_output = "info", "log"
#log.add_output = "tracker_events", "log"
#log.add_output = "tracker_requests", "log"
### END of rtorrent.rc ###
-142
View File
@@ -1,142 +0,0 @@
--[[
A minimal rTorrent configuration that provides the basic features
you want to have in addition to the built-in defaults.
See https://github.com/rakshasa/rtorrent/wiki/CONFIG-Template
for an up-to-date version.
How to use this file:
1. Copy to destination, remove '-example' suffix
2. Create rtorrent.rc, containing single command, which executes THIS file.
For example:
lua.execute = (cat, (system.env, HOME), "/.config/rtorrent/rtorrent.rc.lua")
]]--
--[[ Bootstrap START ]]--
local rtorrent = require('rtorrent')
rc = rtorrent.autocall
--[[ Helper functions ]]--
local function makeArgs(cmdline)
return 'sh', '-c', "'"..table.concat(cmdline, "' '").."'"
end
-- Instance layout (base paths)
local home = os.getenv('HOME')
local cfg = {}
cfg.basedir = home..'/rtorrent/'
cfg.download = cfg.basedir..'download/'
cfg.logs = cfg.basedir..'log/'
cfg.logfile = cfg.logs..'rtorrent-'..rc.system.time()..'.log'
cfg.session = cfg.basedir..'.session/'
cfg.watch = cfg.basedir..'watch/'
rc.method.insert('cfg.download', 'private|const|string', cfg.download)
rc.method.insert('cfg.logs', 'private|const|string', cfg.logs)
rc.method.insert('cfg.logfile', 'private|const|string', cfg.logfile)
rc.method.insert('cfg.session', 'private|const|string', cfg.session)
rc.method.insert('cfg.watch', 'private|const|string', cfg.watch)
-- Create instance directories
rc.execute.throw(
makeArgs({'mkdir', '-p',
cfg.download,
cfg.logs,
cfg.session,
cfg.watch..'/load',
cfg.watch..'/start'}))
-- Listening port for incoming peer traffic (fixed; you can also randomize it)
rc.network.port_range = '50000-50000'
rc.network.port_random = false
-- Tracker-less torrent and UDP tracker support
-- (conservative settings for 'private' trackers, change for 'public')
rc.dht.mode = 'disable'
rc.protocol.pex = false
rc.trackers.use_udp = false
-- Peer settings
rc.throttle.max_uploads = 100
rc.throttle.max_uploads.global = 250
rc.throttle.min_peers.normal = 20
rc.throttle.max_peers.normal = 60
rc.throttle.min_peers.seed = 30
rc.throttle.max_peers.seed = 80
rc.trackers.numwant = 80
rc.protocol.encryption.set('allow_incoming', 'try_outgoing', 'enable_retry')
-- Limits for file handle resources, this is optimized for
-- an `ulimit` of 1024 (a common default). You MUST leave
-- a ceiling of handles reserved for rTorrent's internal needs!
rc.network.http.max_open = 50
rc.network.max_open_files = 600
rc.network.max_open_sockets = 300
-- Memory resource usage (increase if you have a large number of items loaded,
-- and/or the available resources to spend)
rc.pieces.memory.max = '1800M'
rc.network.xmlrpc.size_limit = '4M'
-- Basic operational settings (no need to change these)
rc.session.path = cfg.session
rc.directory.default = cfg.download
rc.log.execute(cfg.logs.."execute.log")
--rc.log.xmlrpc(cfg.logs.."xmlrpc.log")
rc.execute.nothrow(
"sh", "-c", table.concat(
{"echo >", rc.session.path(), "rtorrent.pid", " ", rc.system.pid()
}))
-- Other operational settings (check & adapt)
rc.encoding.add('utf8')
rc.system.umask = 0027
rc.system.cwd = rc.directory.default()
rc.network.http.dns_cache_timeout = 25
rc.schedule2('monitor_diskspace', '15', '60', 'close_low_diskspace=1000M')
--rc.pieces.hash.on_completion = false
--rc.view.sort_current('seeding', 'greater=d.ratio=')
--rc.keys.layout = 'qwerty'
--rc.network.http.capath = '/etc/ssl/certs'
--rc.network.http.ssl_verify_peer = 0
--rc.network.http.ssl_verify_host = 0
--rc.network.rpc.use_xmlrpc = true
--rc.network.rpc.use_jsonrpc = true
-- Some additional values and commands
-- NOTE: just common names, not commands
rc.method.insert('system.startup_time', 'value|const', rc.system.time())
rc.method.insert('d.data_path', 'simple',
[[if=(d.is_multi_file),
(cat, (d.directory), /),
(cat, (d.directory), /, (d.name))]])
rc.method.insert('d.session_file', 'simple', 'cat=(session.path), (d.hash), .torrent')
-- Watch directories (add more as you like, but use unique schedule names)
rc.schedule2('watch_start', '10', '10', 'load.start_verbose=(cat, (cfg.watch), "start/*.torrent")')
rc.schedule2('watch_load', '11', '10', 'load.verbose=(cat, (cfg.watch), "load/*.torrent")')
-- Run the rTorrent process as a daemon in the background
-- (and control via XMLRPC sockets)
--rc.system.daemon = true
--rc.network.scgi.open_local(cfg.session..'rtorrent.sock')
--rc.execute.nothrow('chmod', '770', cfg.session..'rtorrent.sock')
-- Logging:
-- Levels = critical error warn notice info debug
-- Groups = connection_* dht_* peer_* rpc_* storage_* thread_* tracker_* torrent_*
rc.print('Logging to '..rc.cfg.logfile())
rc.log.open_file('log', rc.cfg.logfile())
rc.log.add_output('info', 'log')
--rc.log.add_output('tracker_events', 'log')
--rc.log.add_output('tracker_requests', 'log')
--[[ END of rtorrent.rc.lua ]]--
-131
View File
@@ -1,131 +0,0 @@
#!/usr/bin/perl
# Perl script to add rTorrent fast resume data to torrent files.
#
# Usage:
# rtorrent_fast_resume.pl [base-directory] < plain.torrent > with_fast_resume.torrent
# -OR-
# rtorrent_fast_resume.pl [base-directory] plain.torrent [with_fast_resume.torrent]
use strict;
use warnings;
use Convert::Bencode_XS qw(bencode bdecode);
use File::Spec; # core module
use POSIX; # core module
$/ = undef;
$| = 1;
# Process ARGV
my $d = $ARGV[0];
if ($d and not -d $d) {
if (-f $d and -s $d and not $ARGV[2]) { # missing directory, but has file
$ARGV[2] = $ARGV[1];
$ARGV[1] = $ARGV[0];
$d = '';
}
else { die "$d is not a directory\n"; }
}
$d ||= ".";
$d .= "/" unless $d =~ m#/$#;
my ($in, $out, $msg);
my ($in_file, $out_file) = ('', '');
if ($ARGV[1]) {
$in_file = $ARGV[1];
open($in, ($ARGV[2] ? '<' : '+<'), $in_file) || die "Cannot open $in_file for input!\n";
unless ($ARGV[2]) {
$out = $in;
$out_file = $in_file;
}
}
else { $in = *STDIN; }
if ($ARGV[2]) {
$out_file = $ARGV[2];
open($out, '>', $out_file) || die "Cannot open $out_file for output!\n";
$msg = *STDOUT;
}
elsif (!$ARGV[1]) { $out = *STDOUT; }
$msg //= *STDERR;
print {$msg} "Total input torrent size: ".sprintf('%.2f', (-s $in_file) / 1024)." KB\n" if $in_file;
print {$msg} "Decoding ".($in_file || 'torrent from standard input')."...";
my $t = bdecode(scalar <$in>);
die "No info key.\n" unless ref $t eq "HASH" and exists $t->{info};
my $psize = $t->{info}{"piece length"} or die "No piece length key.\n";
print {$msg} "done\n\n";
my @files;
my $tsize = 0;
if (exists $t->{info}{files}) {
print {$msg} "Multi file torrent: $t->{info}{name}\n";
for (@{$t->{info}{files}}) {
push @files, join "/", $t->{info}{name},@{$_->{path}};
$tsize += $_->{length};
}
}
else {
print {$msg} "Single file torrent: $t->{info}{name}\n";
@files = ($t->{info}{name});
$tsize = $t->{info}{length};
}
my $chunks = int(($tsize + $psize - 1) / $psize);
print {$msg} "Total size: ".sprintf('%.2f', $tsize / 1024**2)." MB; $chunks chunks; ", scalar @files, " files.\n\n";
die "Inconsistent piece information!\n" if $chunks*20 != length $t->{info}{pieces};
print {$msg} "Adding fast resume information...";
# flags => 1+16+
my $pmod = 0;
$t->{libtorrent_resume}{bitfield} = $chunks;
foreach my $f (0..$#files) {
die "$d$files[$f] not found.\n" unless -e "$d$files[$f]";
my $mtime = (stat "$d$files[$f]")[9];
# Compute number of chunks per file
my $fsize = (exists $t->{info}{files}) ? $t->{info}{files}[$f]{length} : 1;
my $fchunks = ($pmod ? 1 : 0);
if ($pmod >= $fsize) { ($fsize, $pmod ) = (0, $pmod-$fsize); }
else { ($pmod, $fsize) = (0, $fsize-$pmod); }
$fchunks += ceil($fsize / $psize);
$pmod ||= $psize - ($fsize % $psize);
$t->{libtorrent_resume}{files}[$f] = {
priority => 0, # Don't download; we already have the file, so don't clobber it!
mtime => $mtime,
completed => $fchunks,
};
};
$t->{libtorrent_resume}{'uncertain_pieces.timestamp'} = time;
# Some extra information to re-enforce the fact that this is a finished torrent
if (exists $t->{info}{files}) {
$d .= $t->{info}{name};
}
$t->{rtorrent} = {
state => 1, # started
state_changed => time,
state_counter => 1,
chunks_wanted => 0,
chunks_done => $chunks,
complete => 1,
hashing => 0, # Not hashing
directory => File::Spec->file_name_is_absolute($d) ? $d : File::Spec->rel2abs($d),
((tied_to_file => File::Spec->file_name_is_absolute($out_file) ? $out_file : File::Spec->rel2abs($out_file)) x!! $out_file),
'timestamp.finished' => 0,
'timestamp.started' => time,
};
print {$msg} "done\n";
print {$msg} "Encoding ".($out_file || 'torrent from standard output')."...";
seek($out, 0, 0); # just in case files are the same
print {$out} bencode($t);
close($in);
close($out);
print {$msg} "done\n";
exit;
-164
View File
@@ -1,164 +0,0 @@
#!/bin/bash
#############
###<Notes>###
#############
# This script depends on screen.
# For the stop function to work, you must set an
# explicit session directory using absolute paths (no, ~ is not absolute) in your rtorrent.rc.
# If you typically just start rtorrent with just "rtorrent" on the
# command line, all you need to change is the "user" option.
# Attach to the screen session as your user with
# "screen -dr rtorrent". Change "rtorrent" with srnname option.
# Licensed under the GPLv2 by lostnihilist: lostnihilist _at_ gmail _dot_ com
##############
###</Notes>###
##############
#######################
##Start Configuration##
#######################
# You can specify your configuration in a different file
# (so that it is saved with upgrades, saved in your home directory,
# or whatever reason you want to)
# by commenting out/deleting the configuration lines and placing them
# in a text file (say /home/user/.rtorrent.init.conf) exactly as you would
# have written them here (you can leave the comments if you desire
# and then uncommenting the following line correcting the path/filename
# for the one you used. note the space after the ".".
# . /etc/rtorrent.init.conf
#Do not put a space on either side of the equal signs e.g.
# user = user
# will not work
# system user to run as (can only use one)
user="user"
# system user to run as # not implemented, see d_start for beginning implementation
# group=$(id -ng "$user")
# the full path to the filename where you store your rtorrent configuration
# must keep parentheses around the entire statement, quotations around each config file
config=("$(su -c 'echo $HOME' $user)/.rtorrent.rc")
# Examples:
# config=("/home/user/.rtorrent.rc")
# config=("/home/user/.rtorrent.rc" "/mnt/some/drive/.rtorrent2.rc")
# config=("/home/user/.rtorrent.rc"
# "/mnt/some/drive/.rtorrent2.rc"
# "/mnt/another/drive/.rtorrent3.rc")
# set of options to run with each instance, separated by a new line
# must keep parentheses around the entire statement
#if no special options, specify with: ""
options=("")
# Examples:
# starts one instance, sourcing both .rtorrent.rc and .rtorrent2.rc
# options=("-o import=~/.rtorrent2.rc")
# starts two instances, ignoring .rtorrent.rc for both, and using
# .rtorrent2.rc for the first, and .rtorrent3.rc for the second
# we do not check for valid options
# options=("-n -o import=~/.rtorrent2.rc" "-n -o import=~/rtorrent3.rc")
# default directory for screen, needs to be an absolute path
base=$(su -c 'echo $HOME' $user)
# name of screen session
srnname="rtorrent"
# file to log to (makes for easier debugging if something goes wrong)
logfile="/var/log/rtorrentInit.log"
#######################
###END CONFIGURATION###
#######################
PATH=/usr/bin:/usr/local/bin:/usr/local/sbin:/sbin:/bin:/usr/sbin
DESC="rtorrent"
NAME=rtorrent
DAEMON=$NAME
SCRIPTNAME=/etc/init.d/$NAME
checkcnfg() {
exists=0
for i in `echo "$PATH" | tr ':' '\n'` ; do
if [ -f $i/$NAME ] ; then
exists=1
break
fi
done
if [ $exists -eq 0 ] ; then
echo "cannot find $NAME binary in PATH: $PATH" | tee -a "$logfile" >&2
exit 3
fi
for (( i=0 ; i < ${#config[@]} ; i++ )) ; do
if ! [ -r "${config[i]}" ] ; then
echo "cannot find readable config ${config[i]}. check that it is there and permissions are appropriate" | tee -a "$logfile" >&2
exit 3
fi
session=$(getsession "${config[i]}")
if ! [ -d "${session}" ] ; then
echo "cannot find readable session directory ${session} from config ${config[i]}. check permissions" | tee -a "$logfile" >&2
exit 3
fi
done
}
d_start() {
[ -d "${base}" ] && cd "${base}"
stty stop undef && stty start undef
su -c "screen -S "${srnname}" -X screen rtorrent ${options} 2>&1 1>/dev/null" ${user} | tee -a "$logfile" >&2
# this works for the screen command, but starting rtorrent below adopts screen session gid
# even if it is not the screen session we started (e.g. running under an undesirable gid
#su -c "screen -ls | grep -sq "\.${srnname}[[:space:]]" " ${user} || su -c "sg \"$group\" -c \"screen -fn -dm -S ${srnname} 2>&1 1>/dev/null\"" ${user} | tee -a "$logfile" >&2
for (( i=0 ; i < ${#options[@]} ; i++ )) ; do
sleep 3
su -c "screen -S "${srnname}" -X screen rtorrent ${options[i]} 2>&1 1>/dev/null" ${user} | tee -a "$logfile" >&2
done
}
d_stop() {
for (( i=0 ; i < ${#config[@]} ; i++ )) ; do
session=$(getsession "${config[i]}")
if ! [ -s ${session}/rtorrent.lock ] ; then
return
fi
pid=$(cat ${session}/rtorrent.lock | awk -F: '{print($2)}' | sed "s/[^0-9]//g")
# make sure the pid doesn't belong to another process
if ps -A | grep -sq ${pid}.*rtorrent ; then
kill -s INT ${pid}
fi
done
}
getsession() {
session=$(cat "$1" | grep "^[[:space:]]*session.path.set[[:space:]]*=" | sed "s/^[[:space:]]*session.path.set[[:space:]]*=[[:space:]]*//" )
#session=${session/#~/`getent passwd ${user}|cut -d: -f6`}
echo $session
}
checkcnfg
case "$1" in
start)
echo -n "Starting $DESC: $NAME"
d_start
echo "."
;;
stop)
echo -n "Stopping $DESC: $NAME"
d_stop
echo "."
;;
restart|force-reload)
echo -n "Restarting $DESC: $NAME"
d_stop
sleep 1
d_start
echo "."
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0
-137
View File
@@ -1,137 +0,0 @@
#!/bin/sh
#############
###<Notes>###
#############
# This script depends on screen.
# For the stop function to work, you must set an
# explicit session directory using ABSOLUTE paths (no, ~ is not absolute) in your rtorrent.rc.
# If you typically just start rtorrent with just "rtorrent" on the
# command line, all you need to change is the "user" option.
# Attach to the screen session as your user with
# "screen -dr rtorrent". Change "rtorrent" with srnname option.
# Licensed under the GPLv2 by lostnihilist: lostnihilist _at_ gmail _dot_ com
##############
###</Notes>###
##############
#######################
##Start Configuration##
#######################
# You can specify your configuration in a different file
# (so that it is saved with upgrades, saved in your home directory,
# or whateve reason you want to)
# by commenting out/deleting the configuration lines and placing them
# in a text file (say /home/user/.rtorrent.init.conf) exactly as you would
# have written them here (you can leave the comments if you desire
# and then uncommenting the following line correcting the path/filename
# for the one you used. note the space after the ".".
# . /etc/rtorrent.init.conf
#Do not put a space on either side of the equal signs e.g.
# user = user
# will not work
# system user to run as
user="user"
# the system group to run as, not implemented, see d_start for beginning implementation
# group=`id -ng "$user"`
# the full path to the filename where you store your rtorrent configuration
config="`su -c 'echo $HOME' $user`/.rtorrent.rc"
# set of options to run with
options=""
# default directory for screen, needs to be an absolute path
base="`su -c 'echo $HOME' $user`"
# name of screen session
srnname="rtorrent"
# file to log to (makes for easier debugging if something goes wrong)
logfile="/var/log/rtorrentInit.log"
#######################
###END CONFIGURATION###
#######################
PATH=/usr/bin:/usr/local/bin:/usr/local/sbin:/sbin:/bin:/usr/sbin
DESC="rtorrent"
NAME=rtorrent
DAEMON=$NAME
SCRIPTNAME=/etc/init.d/$NAME
checkcnfg() {
exists=0
for i in `echo "$PATH" | tr ':' '\n'` ; do
if [ -f $i/$NAME ] ; then
exists=1
break
fi
done
if [ $exists -eq 0 ] ; then
echo "cannot find rtorrent binary in PATH $PATH" | tee -a "$logfile" >&2
exit 3
fi
if ! [ -r "${config}" ] ; then
echo "cannot find readable config ${config}. check that it is there and permissions are appropriate" | tee -a "$logfile" >&2
exit 3
fi
session=`getsession "$config"`
if ! [ -d "${session}" ] ; then
echo "cannot find readable session directory ${session} from config ${config}. check permissions" | tee -a "$logfile" >&2
exit 3
fi
}
d_start() {
[ -d "${base}" ] && cd "${base}"
stty stop undef && stty start undef
su -c "screen -ls | grep -sq "\.${srnname}[[:space:]]" " ${user} || su -c "screen -dm -S ${srnname} 2>&1 1>/dev/null" ${user} | tee -a "$logfile" >&2
# this works for the screen command, but starting rtorrent below adopts screen session gid
# even if it is not the screen session we started (e.g. running under an undesirable gid
#su -c "screen -ls | grep -sq "\.${srnname}[[:space:]]" " ${user} || su -c "sg \"$group\" -c \"screen -fn -dm -S ${srnname} 2>&1 1>/dev/null\"" ${user} | tee -a "$logfile" >&2
su -c "screen -S "${srnname}" -X screen rtorrent ${options} 2>&1 1>/dev/null" ${user} | tee -a "$logfile" >&2
}
d_stop() {
session=`getsession "$config"`
if ! [ -s ${session}/rtorrent.lock ] ; then
return
fi
pid=`cat ${session}/rtorrent.lock | awk -F: '{print($2)}' | sed "s/[^0-9]//g"`
if ps -A | grep -sq ${pid}.*rtorrent ; then # make sure the pid doesn't belong to another process
kill -s INT ${pid}
fi
}
getsession() {
session=`cat "$1" | grep "^[[:space:]]*session.path.set[[:space:]]*=" | sed "s/^[[:space:]]*session.path.set[[:space:]]*=[[:space:]]*//" `
echo $session
}
checkcnfg
case "$1" in
start)
echo -n "Starting $DESC: $NAME"
d_start
echo "."
;;
stop)
echo -n "Stopping $DESC: $NAME"
d_stop
echo "."
;;
restart|force-reload)
echo -n "Restarting $DESC: $NAME"
d_stop
sleep 1
d_start
echo "."
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0
-166
View File
@@ -1,166 +0,0 @@
#!/bin/bash
### BEGIN INIT INFO
# Provides: rtorrent_autostart
# Required-Start: $local_fs $remote_fs $network $syslog $netdaemons
# Required-Stop: $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: rtorrent script using tmux
# Description: rtorrent script using tmux
### END INIT INFO
#############
###<Notes>###
#############
# This script depends on tmux and is based on 'rtorrentInitScreen.sh' script
# with the following enhancements:
# - init script 'start' option can be called without breaking anything
# - init script 'status' option can be used in scripts to determine whether rtorrent is running or not
# - auto removes damaged/stuck 'rtorrent.lock' file if necessary
# - saves session (just in case) before stopping with 'rtxmlrp' if it exists
# - provides examples how to script tmux windows
#
# For the stop function to work, you must set an explicit session directory
# using ABSOLUTE paths (no, ~ is not absolute) in your rtorrent.rc and with
# "sessiondir" option.
# If you typically just start rtorrent with just "rtorrent" on the
# command line, all you need to change is the "user" and "sessiondir" option.
# Attach to the tmux session as your user with
# "tmux -2u new-session -A -s rtorrent". Change "rtorrent" with "tmuxname" option.
# Licensed under the GPLv2 by lostnihilist: lostnihilist _at_ gmail _dot_ com
##############
###</Notes>###
##############
#######################
##Start Configuration##
#######################
# You can specify your configuration in a different file.
# (so that it is saved with upgrades, saved in your home directory,
# or whatever reason you want to)
# by commenting out/deleting the configuration lines and placing them
# in a text file (say /home/user/.rtorrent.init.conf) exactly as you would
# have written them here (you can leave the comments if you desire
# and then uncommenting the following line correcting the path/filename.
# for the one you used. note the space after the ".".
# . /etc/rtorrent.init.conf
# system user to run as
user="username"
# default directory for tmux, needs to be an absolute path
base=$(su -c 'echo $HOME' $user)
# the full path to the filename where you store your rtorrent configuration
config="$base/.rtorrent.rc"
# the full path to the session directory of rtorrent
sessiondir="/mnt/Torrents/.rtorrent/.session"
# options to pass to rtorrent; e.g. don't read config from $HOME but load alternate
#options="-n -O import=$config"
options=""
# name of tmux session
tmuxname="rtorrent"
# name of window in tmux session
tmuxwindowname="rT"
#######################
###END CONFIGURATION###
#######################
PATH=/usr/bin:/usr/local/bin:/usr/local/sbin:/sbin:/bin:/usr/sbin
NAME=rtorrent
DAEMON=$NAME
SCRIPTNAME=/etc/init.d/$NAME
RTXMLRPCBIN="$base/bin/rtxmlrpc"
checkcnfg() {
if [ -z "$(which $DAEMON)" ] ; then
echo "Cannot find $DAEMON binary in PATH: $PATH"
exit 3
fi
if ! [ -r "$config" ] ; then
echo "Cannot find readable config $config. Check that it is there and permissions are appropriate"
exit 3
fi
if ! [ -d "$sessiondir" ] ; then
echo "Cannot find readable session directory $sessiondir from config $config. Check permissions"
exit 3
fi
}
status() {
if [ -e "${sessiondir}/rtorrent.lock" ] ; then
pid=`cat ${sessiondir}/rtorrent.lock | awk -F: '{print($2)}' | sed "s/[^0-9]//g"`
# make sure the pid isn't empty and doesn't belong to another process : this will match lines containing rtorrent, which grep '[r]torrent' does not!
# if there is no process as the 'pid' suggests then delete the stuck "rtorrent.lock" file (to be able to start rtorrent)
[[ -n "$pid" ]] && ps aux | grep -sq ${pid}.*[r]torrent && echo -e ${pid} || rm -f "${sessiondir}/rtorrent.lock"
fi
}
d_start() {
[ -d "$base" ] && cd "$base"
# if STDIN is a terminal (we are using interactive mode)
[ -t 0 ] && stty stop undef && stty start undef
# start the default 2 tmux window (bash and mc) if there isn't tmux session called "tmuxname" option (rtorrent)
if ! su -c "tmux ls | grep -sq ${tmuxname}: " $user ; then
# 1st window (0): split it into 3 panes, display 'date' in the last one
su -c "tmux -2u new-session -d -s ${tmuxname} -n 'shell1'" $user
su -c "tmux -2u split-window -v -t ${tmuxname}:0 'bash'" $user
su -c "tmux -2u split-window -h -t ${tmuxname}:0 'date; bash'" $user
# 2nd window (1): start 'mc' if it exists
su -c "tmux -2u new-window -t ${tmuxname}:1 -n 'mc1' 'command which mc && mc ~/; bash'" $user
fi
# start rtorrent always in the 3rd tmux window (2) if it's not running and leave shell behind to be able to see reason of a crash
if [ "$(status)" == "" ]; then
su -c "tmux -2u list-panes -t ${tmuxname}:2 &>/dev/null && tmux -2u respawn-pane -t ${tmuxname}:2 -k \"${DAEMON} ${options}; bash\" || tmux -2u new-window -t ${tmuxname}:2 -n ${tmuxwindowname} \"${DAEMON} ${options}; bash\"" $user
fi
}
d_stop() {
pid=$(status)
if [ "$pid" != "" ]; then
# save session before stopping explicitly (just in case) if rtxmlrpc util exists then wait for 5 seconds to be able to complete it
[ -L "$RTXMLRPCBIN" ] && "$RTXMLRPCBIN" session.save &>/dev/null && sleep 5
# INT (2, Interrupt from keyboard): normal shutdown
kill -s INT $pid
fi
}
checkcnfg
case "$1" in
start)
echo -n "Starting $tmuxwindowname: $NAME"
d_start
echo "."
;;
stop)
echo -n "Stopping $tmuxwindowname: $NAME"
d_stop
echo "."
;;
restart|force-reload)
echo -n "Restarting $tmuxwindowname: $NAME"
d_stop
sleep 1
d_start
echo "."
;;
status)
status
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload|status}" >&2
exit 1
;;
esac
exit 0
-23
View File
@@ -1,23 +0,0 @@
#!/usr/bin/env bash
PORT_NUMBER=${1:-5001}
echo "Option Strings"
echo "=============="
echo
echo "Introduction"
echo "------------"
echo
echo "Client version: " `xmlrpc2scgi.py -p scgi://127.0.0.1:${PORT_NUMBER} system.client_version`
echo
echo "Generated by 'rtorrent/doc/scripts/print_option_string.sh' on `date -u`."
for i in strings.choke_heuristics strings.choke_heuristics.upload strings.choke_heuristics.download strings.connection_type strings.encryption strings.ip_filter strings.ip_tos strings.log_group strings.tracker_event strings.tracker_mode; do
echo
echo $i
echo `echo $i | tr 'a-z_.' '-'`
echo
echo '```'
xmlrpc2scgi.py -p scgi://127.0.0.1:${PORT_NUMBER} $i | tr , '\n' | tr '[' ' ' | tr ']' ' '
echo '```'
done
File diff suppressed because it is too large Load Diff
-23
View File
@@ -1,23 +0,0 @@
#!/usr/bin/perl
# Perl script to read torrent data
use strict;
use warnings;
use Convert::Bencode_XS qw(bdecode);
use Data::Dumper;
$/ = undef;
$| = 1;
my $in;
my $in_file = $ARGV[0];
if ($in_file) { open($in, '<', $in_file) || die "Cannot open $in_file for input!\n"; }
else { $in = *STDIN; }
print "Total input torrent size: ".sprintf('%.2f', (-s $in_file) / 1024)." KB\n" if $in_file;
print "Decoding ".($in_file || 'torrent from standard input')."...";
my $t = bdecode(scalar <$in>);
print "done\n\n";
print Data::Dumper->new([$t], ['*Torrent'])->Indent(1)->Useqq(1)->Quotekeys(0)->Sortkeys(1)->Dump;
-1
View File
@@ -1 +0,0 @@
valgrind --leak-check=full --track-origins=yes --show-reachable=yes --suppressions=/Users/rakshasa/projects/rtorrent/doc/valgrind.suppression --gen-suppressions=all --log-file=valgrind.log /usr/local/bin/rtorrent
-343
View File
@@ -1,343 +0,0 @@
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:__76-*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:__76-*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:__80-*
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:__93-*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:__93-*
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:__108-*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:__108-*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:__108-*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsPlistSource setDomainIdentifier:]
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:-[CFPrefsSearchListSource alreadylocked_copyValueForKey:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsSearchListSource alreadylocked_copyValueForKey:]
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:-[CFPrefsSearchListSource alreadylocked_copyValueForKey:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsSearchListSource addNamedVolatileSourceForIdentifier:]
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:-[CFPrefsSearchListSource handleReply:toRequestNewDataMessage:onConnection:retryCount:error:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsSearchListSource addSourceForIdentifier:user:byHost:container:]
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:-[CFPrefsSearchListSource handleReply:toRequestNewDataMessage:onConnection:retryCount:error:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsSearchListSource handleReply:toRequestNewDataMessage:onConnection:retryCount:error:]
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:-[CFPrefsSearchListSource handleReply:toRequestNewDataMessage:onConnection:retryCount:error:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[_CFXPreferences copyAppValueForKey:identifier:container:configurationURL:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[_CFXPreferences withSearchListForIdentifier:container:cloudConfigurationURL:perform:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[_CFXPreferences withSearchLists:]
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:-[CFPrefsSource copyValueForKey:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[OS_xpc_object dealloc]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_CF*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:__CF*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_NS*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:getSuperclassMetadata
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_ZN5dyld*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_ZNK5dyld*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:___ZNK5dyld*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_dyld_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:*_dyld_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:___SC_getApplicationBundleID_block_invoke
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:xpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:xpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_xpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:_xpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_libxpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:si_module_with_name
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:_dispatch_*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:_dispatch_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_dispatch_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:*_swift_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:libdispatch_init
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_os_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_od_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_objc_init
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_notify_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:bootstrap_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:$ss*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:loadlocale
fun:setlocale
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:initscr
fun:_ZN7display6Canvas10initializeEv
fun:_ZN7Control10initializeEv
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:assume_default_colors_sp
fun:_ZN7display6Canvas10initializeEv
fun:_ZN7Control10initializeEv
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:start_color_sp
fun:_ZN7display6Canvas10initializeEv
fun:_ZN7Control10initializeEv
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_ZN7display6Canvas10initializeEv
}
+14
View File
@@ -0,0 +1,14 @@
Donate to rTorrent development
==============================
* [Paypal](https://paypal.me/jarisundellno)
* [Patreon](https://www.patreon.com/rtorrent)
* [SubscribeStar](https://www.subscribestar.com/rtorrent)
* Bitcoin: 1MpmXm5AHtdBoDaLZstJw8nupJJaeKu8V8
* Ethereum: 0x9AB1e3C3d8a875e870f161b3e9287Db0E6DAfF78
* Litecoin: LdyaVR67LBnTf6mAT4QJnjSG2Zk67qxmfQ
* Cardano: addr1qytaslmqmk6dspltw06sp0zf83dh09u79j49ceh5y26zdcccgq4ph7nmx6kgmzeldauj43254ey97f3x4xw49d86aguqwfhlte
Help keep rTorrent development going by donating to its creator.
(Note the old paypal account is defunct, please use the new one when sending donations)
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="96" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="96" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h49v20H0z"/><path fill="#97ca00" d="M49 0h47v20H49z"/><path fill="url(#b)" d="M0 0h96v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="255" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="390">Donate</text><text x="255" y="140" transform="scale(.1)" textLength="390">Donate</text><text x="715" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="370">PayPal</text><text x="715" y="140" transform="scale(.1)" textLength="370">PayPal</text></g> </svg>

After

Width:  |  Height:  |  Size: 954 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 943 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+39
View File
@@ -0,0 +1,39 @@
RTorrent
========
Introduction
------------
To learn how to use rTorrent visit the [Wiki](https://github.com/rakshasa/rtorrent/wiki).
[Donate](https://rakshasa.github.io/rtorrent/donate.html) to help fund the development of this client.
Stable
------
* [libtorrent-0.16.11.tar.gz](https://github.com/rakshasa/rtorrent-archive/raw/master/libtorrent-0.16.11.tar.gz)
* [rtorrent-0.16.11.tar.gz](https://github.com/rakshasa/rtorrent-archive/raw/master/rtorrent-0.16.11.tar.gz)
Development
-----------
* [libtorrent-0.16.13.tar.gz](https://github.com/rakshasa/rtorrent-archive/raw/master/libtorrent-0.16.13.tar.gz)
* [rtorrent-0.16.13.tar.gz](https://github.com/rakshasa/rtorrent-archive/raw/master/rtorrent-0.16.13.tar.gz)
Related Projects
----------------
* [https://github.com/rakshasa/rbedit](https://github.com/rakshasa/rbedit): A dependency-free bencode editor.
Donate to rTorrent development
------------------------------
* [Paypal](https://paypal.me/jarisundellno)
* [Patreon](https://www.patreon.com/rtorrent)
* [SubscribeStar](https://www.subscribestar.com/rtorrent)
* Bitcoin: 1MpmXm5AHtdBoDaLZstJw8nupJJaeKu8V8
* Ethereum: 0x9AB1e3C3d8a875e870f161b3e9287Db0E6DAfF78
* Litecoin: LdyaVR67LBnTf6mAT4QJnjSG2Zk67qxmfQ
* Cardano: addr1qytaslmqmk6dspltw06sp0zf83dh09u79j49ceh5y26zdcccgq4ph7nmx6kgmzeldauj43254ey97f3x4xw49d86aguqwfhlte
Help keep rTorrent development going by donating to its creator.
+1
View File
@@ -0,0 +1 @@
console.log('This would be the main JS file.');
+17
View File
@@ -0,0 +1,17 @@
var metas = document.getElementsByTagName('meta');
var i;
if (navigator.userAgent.match(/iPhone/i)) {
for (i=0; i<metas.length; i++) {
if (metas[i].name == "viewport") {
metas[i].content = "width=device-width, minimum-scale=1.0, maximum-scale=1.0";
}
}
document.addEventListener("gesturestart", gestureStart, false);
}
function gestureStart() {
for (i=0; i<metas.length; i++) {
if (metas[i].name == "viewport") {
metas[i].content = "width=device-width, minimum-scale=0.25, maximum-scale=1.6";
}
}
}
-127
View File
@@ -1,127 +0,0 @@
-- The "rtorrent" table is passed in by the C++ code, modify and
-- return it for loading.
local args = {...}
local rtorrent = args[1]
local math = require('math')
-- Autocall
-- Passes an empty first argument implicitly, as "".
-- Allows syntax like:
-- - `rtorrent.autocall.system.hostname()` or
-- - `rtorrent.autocall.session.directory.set("/tmp/")`
-- Has assignment operator, syntax sugar for `.set()` with single argument
-- - `rtorrent.autocall.session.directory = "/tmp/"`
-- Autocall-chains can be reused as aliases:
-- ```lua
-- a = rtorrent.autocall.system
-- a.hostname() -- same as `rtorrent.autocall.system.hostname()`
-- a.pid() -- same as `rtorrent.autocall.system.pid()`
local mt = {}
function mt.__call (t, ...)
name = table.concat(rawget(t, "__namestack"), ".")
tg = rawget(t, "__target") or ""
success, ret = pcall(rtorrent.call, name, tg, ...)
if not success then error(name..": "..ret, 2) end
return ret
end
function mt.__index (t, key)
ns = {table.unpack(rawget(t, "__namestack") or {})}
tg = rawget(t, "__target") or nil
table.insert(ns, key)
return setmetatable({__namestack=ns, __target=tg}, mt)
end
function mt.__newindex (t, key, value)
t[key].set(value)
end
rtorrent["autocall"] = setmetatable({}, mt)
-- Target-object
-- Sets first argment for Autocall, for commands that require target.
-- Second argument allows to add prefix to command.
-- Allows syntax like:
-- - `rtorrent.Target("some_infohash").d.name()` or
-- - `rtorrent.Target:new("some_infohash").d.name()`
-- Also can be stored as variable
local Target = setmetatable({
new = function (self, target, prefix)
return self(target, prefix)
end;
}, {
__call = function (self, target, prefix)
return setmetatable(
{__namestack={prefix}, __target=target}, mt)
end
})
rtorrent["Target"] = Target
-- Some aliases for Target-commands
for i, p in ipairs({ "d", "f", "p", "t", "load" }) do
rtorrent[p] = function (target) return Target(target, p) end
end
-- Insert Lua method
-- Allow insert global lua-finction `func_name` at rtorrent slot `name`
-- @param name string rtorrent's slot
-- @param func_name string name of global lua-function
-- @param method_args number|string[]|number[]
-- rtorrent's arguments like `(d.name)` to be passed to lua-function when it be
-- called by rtorrent:
--
-- 1) non-negative integer - number of first arguments to be passed
--
-- example:
-- rtorrent.insert_lua_method('d.watch_handler', 'watch_handler', 1)
--
-- gives following rtorrent event handler:
-- d.watch_handler=$argument.0
--
-- 2) table (array) of strings - concrete rtorrent strings, passed in specified
-- order
--
-- example:
-- rtorrent.insert_lua_method('d.watch_handler', 'watch_handler', { '$argument.0=' })
--
-- gives following rtorrent event handler:
-- d.watch_handler=$argument.0
--
-- 3) table (array) of non-negative integer - indexes, converted to
-- $argument.0, $argument.1 ..., passed in specified order
--
-- example:
-- rtorrent.insert_lua_method('d.watch_handler', 'watch_handler', { 1, 0 })
-- or:
-- rtorrent.insert_lua_method('d.watch_handler', 'watch_handler', { [1]=1, [2]=0 })
--
-- gives following rtorrent event handler:
-- d.watch_handler=$argument.1,$argument.0
--
-- NOTE: first argement of func_name is always target, may be empty string
rtorrent["insert_lua_method"] = function (name, func_name, method_args)
local args = {}
if method_args == nil then method_args = 0 end
if math.type(method_args) == 'integer' and method_args >= 0 then
for i = 1, method_args do
args[i] = "$argument." .. i-1 .. "="
end
elseif type(method_args) == 'table' then
for i, v in ipairs(method_args) do
if type(v) == 'string' then
args[i] = v
elseif math.type(v) == 'integer' and v >= 0 then
args[i] = "$argument." .. v .. "="
else
error("Incorrect arguments")
end
end
else
error("Incorrect arguments")
end
local arg_str = table.concat(args, ",")
if #arg_str > 0 then arg_str = ","..arg_str end
rtorrent.autocall.method.insert(name, "simple", 'lua.execute.str="return '..func_name..'(...)"'..arg_str)
end
return rtorrent
-131
View File
@@ -1,131 +0,0 @@
# Functions to check for attributes support in compiler
AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [
AC_CACHE_CHECK([if compiler supports __attribute__((constructor))],
[cc_cv_attribute_constructor],
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([
void ctor() __attribute__((constructor));
void ctor() { };
])],
[cc_cv_attribute_constructor=yes],
[cc_cv_attribute_constructor=no])
])
if test "x$cc_cv_attribute_constructor" = "xyes"; then
AC_DEFINE([SUPPORT_ATTRIBUTE_CONSTRUCTOR], 1, [Define this if the compiler supports the constructor attribute])
$1
else
true
$2
fi
])
AC_DEFUN([CC_ATTRIBUTE_FORMAT], [
AC_CACHE_CHECK([if compiler supports __attribute__((format(printf, n, n)))],
[cc_cv_attribute_format],
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([
void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { }
])],
[cc_cv_attribute_format=yes],
[cc_cv_attribute_format=no])
])
if test "x$cc_cv_attribute_format" = "xyes"; then
AC_DEFINE([SUPPORT_ATTRIBUTE_FORMAT], 1, [Define this if the compiler supports the format attribute])
$1
else
true
$2
fi
])
AC_DEFUN([CC_ATTRIBUTE_INTERNAL], [
AC_CACHE_CHECK([if compiler supports __attribute__((visibility("internal")))],
[cc_cv_attribute_internal],
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([
void __attribute__((visibility("internal"))) internal_function() { }
])],
[cc_cv_attribute_internal=yes],
[cc_cv_attribute_internal=no])
])
if test "x$cc_cv_attribute_internal" = "xyes"; then
AC_DEFINE([SUPPORT_ATTRIBUTE_INTERNAL], 1, [Define this if the compiler supports the internal visibility attribute])
$1
else
true
$2
fi
])
AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [
AC_LANG_PUSH(C++)
tmp_CXXFLAGS=$CXXFLAGS
CXXFLAGS="$CXXFLAGS -fvisibility=hidden"
AC_CACHE_CHECK([if compiler supports __attribute__((visibility("default")))],
[cc_cv_attribute_visibility],
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([
void __attribute__((visibility("default"))) visibility_function() { }
])],
[cc_cv_attribute_visibility=yes],
[cc_cv_attribute_visibility=no])
])
CXXFLAGS=$tmp_CXXFLAGS
AC_LANG_POP(C++)
if test "x$cc_cv_attribute_visibility" = "xyes"; then
AC_DEFINE([SUPPORT_ATTRIBUTE_VISIBILITY], 1, [Define this if the compiler supports the visibility attributes.])
CXXFLAGS="$CXXFLAGS -fvisibility=hidden"
$1
else
true
$2
fi
])
AC_DEFUN([CC_ATTRIBUTE_NONNULL], [
AC_CACHE_CHECK([if compiler supports __attribute__((nonnull()))],
[cc_cv_attribute_nonnull],
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([
void some_function(void *foo, void *bar) __attribute__((nonnull()));
void some_function(void *foo, void *bar) { }
])],
[cc_cv_attribute_nonnull=yes],
[cc_cv_attribute_nonnull=no])
])
if test "x$cc_cv_attribute_nonnull" = "xyes"; then
AC_DEFINE([SUPPORT_ATTRIBUTE_NONNULL], 1, [Define this if the compiler supports the nonnull attribute])
$1
else
true
$2
fi
])
AC_DEFUN([CC_FUNC_EXPECT], [
AC_CACHE_CHECK([if compiler has __builtin_expect function],
[cc_cv_func_expect],
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([
int some_function()
{
int a = 3;
return (int)__builtin_expect(a, 3);
}
])],
[cc_cv_func_expect=yes],
[cc_cv_func_expect=no])
])
if test "x$cc_cv_func_expect" = "xyes"; then
AC_DEFINE([SUPPORT__BUILTIN_EXPECT], 1, [Define this if the compiler supports __builtin_expect() function])
$1
else
true
$2
fi
])
-141
View File
@@ -1,141 +0,0 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_check_zlib.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CHECK_ZLIB([action-if-found], [action-if-not-found])
#
# DESCRIPTION
#
# This macro searches for an installed zlib library. If nothing was
# specified when calling configure, it searches first in /usr/local and
# then in /usr, /opt/local and /sw. If the --with-zlib=DIR is specified,
# it will try to find it in DIR/include/zlib.h and DIR/lib/libz.a. If
# --without-zlib is specified, the library is not searched at all.
#
# If either the header file (zlib.h) or the library (libz) is not found,
# shell commands 'action-if-not-found' is run. If 'action-if-not-found' is
# not specified, the configuration exits on error, asking for a valid zlib
# installation directory or --without-zlib.
#
# If both header file and library are found, shell commands
# 'action-if-found' is run. If 'action-if-found' is not specified, the
# default action appends '-I${ZLIB_HOME}/include' to CPFLAGS, appends
# '-L$ZLIB_HOME}/lib' to LDFLAGS, prepends '-lz' to LIBS, and calls
# AC_DEFINE(HAVE_LIBZ). You should use autoheader to include a definition
# for this symbol in a config.h file. Sample usage in a C/C++ source is as
# follows:
#
# #ifdef HAVE_LIBZ
# #include <zlib.h>
# #endif /* HAVE_LIBZ */
#
# LICENSE
#
# Copyright (c) 2008 Loic Dachary <loic@senga.org>
# Copyright (c) 2010 Bastien Chevreux <bach@chevreux.org>
#
# 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, see <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 16
AU_ALIAS([CHECK_ZLIB], [AX_CHECK_ZLIB])
AC_DEFUN([AX_CHECK_ZLIB],
#
# Handle user hints
#
[AC_MSG_CHECKING(if zlib is wanted)
zlib_places="/usr/local /usr /opt/local /sw"
AC_ARG_WITH([zlib],
[ --with-zlib=DIR root directory path of zlib installation @<:@defaults to
/usr/local or /usr if not found in /usr/local@:>@
--without-zlib to disable zlib usage completely],
[if test "$withval" != no ; then
AC_MSG_RESULT(yes)
if test -d "$withval"
then
zlib_places="$withval $zlib_places"
else
AC_MSG_WARN([Sorry, $withval does not exist, checking usual places])
fi
else
zlib_places=
AC_MSG_RESULT(no)
fi],
[AC_MSG_RESULT(yes)])
#
# Locate zlib, if wanted
#
if test -n "${zlib_places}"
then
# check the user supplied or any other more or less 'standard' place:
# Most UNIX systems : /usr/local and /usr
# MacPorts / Fink on OSX : /opt/local respectively /sw
for ZLIB_HOME in ${zlib_places} ; do
if test -f "${ZLIB_HOME}/include/zlib.h"; then break; fi
ZLIB_HOME=""
done
ZLIB_OLD_LDFLAGS=$LDFLAGS
ZLIB_OLD_CPPFLAGS=$CPPFLAGS
if test -n "${ZLIB_HOME}"; then
LDFLAGS="$LDFLAGS -L${ZLIB_HOME}/lib"
CPPFLAGS="$CPPFLAGS -I${ZLIB_HOME}/include"
fi
AC_LANG_PUSH([C])
AC_CHECK_LIB([z], [inflateEnd], [zlib_cv_libz=yes], [zlib_cv_libz=no])
AC_CHECK_HEADER([zlib.h], [zlib_cv_zlib_h=yes], [zlib_cv_zlib_h=no])
AC_LANG_POP([C])
if test "$zlib_cv_libz" = "yes" && test "$zlib_cv_zlib_h" = "yes"
then
#
# If both library and header were found, action-if-found
#
m4_ifblank([$1],[
CPPFLAGS="$CPPFLAGS -I${ZLIB_HOME}/include"
LDFLAGS="$LDFLAGS -L${ZLIB_HOME}/lib"
LIBS="-lz $LIBS"
AC_DEFINE([HAVE_LIBZ], [1],
[Define to 1 if you have `z' library (-lz)])
],[
# Restore variables
LDFLAGS="$ZLIB_OLD_LDFLAGS"
CPPFLAGS="$ZLIB_OLD_CPPFLAGS"
$1
])
else
#
# If either header or library was not found, action-if-not-found
#
m4_default([$2],[
AC_MSG_ERROR([either specify a valid zlib installation with --with-zlib=DIR or disable zlib usage with --without-zlib])
])
fi
fi
])
File diff suppressed because it is too large Load Diff
-67
View File
@@ -1,67 +0,0 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_execinfo.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_EXECINFO([ACTION-IF-EXECINFO-H-IS-FOUND], [ACTION-IF-EXECINFO-H-IS-NOT-FOUND], [ADDITIONAL-TYPES-LIST])
#
# DESCRIPTION
#
# Checks for execinfo.h header and if the len parameter/return type can be
# found from a list, also define backtrace_size_t to that type.
#
# By default the list of types to try contains int and size_t, but should
# some yet undiscovered system use e.g. unsigned, the 3rd argument can be
# used for extensions. I'd like to hear of further suggestions.
#
# Executes ACTION-IF-EXECINFO-H-IS-FOUND when present and the execinfo.h
# header is found or ACTION-IF-EXECINFO-H-IS-NOT-FOUND in case the header
# seems unavailable.
#
# Also adds -lexecinfo to LIBS on BSD if needed.
#
# LICENSE
#
# Copyright (c) 2014 Thomas Jahns <jahns@dkrz.de>
#
# 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 2
AC_DEFUN([AX_EXECINFO],
[AC_CHECK_HEADERS([execinfo.h])
AS_IF([test x"$ac_cv_header_execinfo_h" = xyes],
[AC_CACHE_CHECK([size parameter type for backtrace()],
[ax_cv_proto_backtrace_type],
[AC_LANG_PUSH([C])
for ax_cv_proto_backtrace_type in size_t int m4_ifnblank([$3],[$3 ])none; do
AS_IF([test "${ax_cv_proto_backtrace_type}" = none],
[ax_cv_proto_backtrace_type= ; break])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
#include <execinfo.h>
extern
${ax_cv_proto_backtrace_type} backtrace(void **addrlist, ${ax_cv_proto_backtrace_type} len);
char **backtrace_symbols(void *const *buffer, ${ax_cv_proto_backtrace_type} size);
])],
[break])
done
AC_LANG_POP([C])])])
AS_IF([test x${ax_cv_proto_backtrace_type} != x],
[AC_DEFINE_UNQUOTED([backtrace_size_t], [$ax_cv_proto_backtrace_type],
[Defined to return type of backtrace().])])
AC_SEARCH_LIBS([backtrace],[execinfo])
AS_IF([test x"${ax_cv_proto_backtrace_type}" != x -a x"$ac_cv_header_execinfo_h" = xyes -a x"$ac_cv_search_backtrace" != xno],
[AC_DEFINE([HAVE_BACKTRACE],[1],
[Defined if backtrace() could be fully identified.])
]m4_ifnblank([$1],[$1
]),m4_ifnblank([$2],[$2
]))])
dnl
dnl Local Variables:
dnl mode: autoconf
dnl End:
dnl
-710
View File
@@ -1,710 +0,0 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_lua.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
# AX_LUA_HEADERS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
# AX_LUA_LIBS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
# AX_LUA_READLINE[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
#
# DESCRIPTION
#
# Detect a Lua interpreter, optionally specifying a minimum and maximum
# version number. Set up important Lua paths, such as the directories in
# which to install scripts and modules (shared libraries).
#
# Also detect Lua headers and libraries. The Lua version contained in the
# header is checked to match the Lua interpreter version exactly. When
# searching for Lua libraries, the version number is used as a suffix.
# This is done with the goal of supporting multiple Lua installs (5.1,
# 5.2, 5.3, and 5.4 side-by-side).
#
# A note on compatibility with previous versions: This file has been
# mostly rewritten for serial 18. Most developers should be able to use
# these macros without needing to modify configure.ac. Care has been taken
# to preserve each macro's behavior, but there are some differences:
#
# 1) AX_WITH_LUA is deprecated; it now expands to the exact same thing as
# AX_PROG_LUA with no arguments.
#
# 2) AX_LUA_HEADERS now checks that the version number defined in lua.h
# matches the interpreter version. AX_LUA_HEADERS_VERSION is therefore
# unnecessary, so it is deprecated and does not expand to anything.
#
# 3) The configure flag --with-lua-suffix no longer exists; the user
# should instead specify the LUA precious variable on the command line.
# See the AX_PROG_LUA description for details.
#
# Please read the macro descriptions below for more information.
#
# This file was inspired by Andrew Dalke's and James Henstridge's
# python.m4 and Tom Payne's, Matthieu Moy's, and Reuben Thomas's ax_lua.m4
# (serial 17). Basically, this file is a mash-up of those two files. I
# like to think it combines the best of the two!
#
# AX_PROG_LUA: Search for the Lua interpreter, and set up important Lua
# paths. Adds precious variable LUA, which may contain the path of the Lua
# 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
# version number greater or equal to MINIMUM-VERSION and less than
# TOO-BIG-VERSION will be accepted.
#
# The Lua version number, LUA_VERSION, is found from the interpreter, and
# substituted. LUA_PLATFORM is also found, but not currently supported (no
# standard representation).
#
# Finally, the macro finds four paths:
#
# luadir Directory to install Lua scripts.
# pkgluadir $luadir/$PACKAGE
# luaexecdir Directory to install Lua modules.
# pkgluaexecdir $luaexecdir/$PACKAGE
#
# These paths are found based on $prefix, $exec_prefix, Lua's
# package.path, and package.cpath. The first path of package.path
# beginning with $prefix is selected as luadir. The first path of
# package.cpath beginning with $exec_prefix is used as luaexecdir. This
# should work on all reasonable Lua installations. If a path cannot be
# determined, a default path is used. Of course, the user can override
# these later when invoking make.
#
# luadir Default: $prefix/share/lua/$LUA_VERSION
# luaexecdir Default: $exec_prefix/lib/lua/$LUA_VERSION
#
# These directories can be used by Automake as install destinations. The
# variable name minus 'dir' needs to be used as a prefix to the
# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES.
#
# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is
# performed, otherwise ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-
# FOUND is blank, then it will default to printing an error. To prevent
# the default behavior, give ':' as an action.
#
# AX_LUA_HEADERS: Search for Lua headers. Requires that AX_PROG_LUA be
# expanded before this macro. Adds precious variable LUA_INCLUDE, which
# may contain Lua specific include flags, e.g. -I/usr/include/lua5.1. If
# LUA_INCLUDE is blank, then this macro will attempt to find suitable
# flags.
#
# LUA_INCLUDE can be used by Automake to compile Lua modules or
# executables with embedded interpreters. The *_CPPFLAGS variables should
# be used for this purpose, e.g. myprog_CPPFLAGS = $(LUA_INCLUDE).
#
# This macro searches for the header lua.h (and others). The search is
# performed with a combination of CPPFLAGS, CPATH, etc, and LUA_INCLUDE.
# If the search is unsuccessful, then some common directories are tried.
# If the headers are then found, then LUA_INCLUDE is set accordingly.
#
# The paths automatically searched are:
#
# * /usr/include/luaX.Y
# * /usr/include/lua/X.Y
# * /usr/include/luaXY
# * /usr/local/include/luaX.Y
# * /usr/local/include/lua-X.Y
# * /usr/local/include/lua/X.Y
# * /usr/local/include/luaXY
#
# (Where X.Y is the Lua version number, e.g. 5.1.)
#
# The Lua version number found in the headers is always checked to match
# the Lua interpreter's version number. Lua headers with mismatched
# version numbers are not accepted.
#
# If headers are found, then ACTION-IF-FOUND is 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,
# set the action to ':'.
#
# AX_LUA_LIBS: Search for Lua libraries. Requires that AX_PROG_LUA be
# expanded before this macro. Adds precious variable LUA_LIB, which may
# contain Lua specific linker flags, e.g. -llua5.1. If LUA_LIB is blank,
# then this macro will attempt to find suitable flags.
#
# LUA_LIB can be used by Automake to link Lua modules or executables with
# embedded interpreters. The *_LIBADD and *_LDADD variables should be used
# for this purpose, e.g. mymod_LIBADD = $(LUA_LIB).
#
# This macro searches for the Lua library. More technically, it searches
# for a library containing the function lua_load. The search is performed
# with a combination of LIBS, LIBRARY_PATH, and LUA_LIB.
#
# If the search determines that some linker flags are missing, then those
# flags will be added to LUA_LIB.
#
# If libraries are found, then ACTION-IF-FOUND is 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,
# set the action to ':'.
#
# AX_LUA_READLINE: Search for readline headers and libraries. Requires the
# AX_LIB_READLINE macro, which is provided by ax_lib_readline.m4 from the
# Autoconf Archive.
#
# If a readline compatible library is found, then ACTION-IF-FOUND is
# performed, otherwise ACTION-IF-NOT-FOUND is performed.
#
# 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>
#
# 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 3 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, see <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 47
dnl =========================================================================
dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION],
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl =========================================================================
AC_DEFUN([AX_PROG_LUA],
[
dnl Check for required tools.
AC_REQUIRE([AC_PROG_GREP])
AC_REQUIRE([AC_PROG_SED])
dnl Make LUA a precious variable.
AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1])
dnl Find a Lua interpreter.
AM_COND_IF([LUAJIT],
[_ax_lua_interpreter_list='luajit luajit-2.1.0-beta3 luajit-2.0.5 luajit-2.0.4 luajit-2.0.3'],
[_ax_lua_interpreter_list='lua lua5.4 lua54 lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua5.0 lua50'])
m4_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], [:])])
ax_display_LUA='lua'
AS_IF([test "x$LUA" != 'x:'],
[ dnl At least check if this is a Lua interpreter.
AC_MSG_CHECKING([if $LUA is a Lua interpreter])
_AX_LUA_CHK_IS_INTRP([$LUA],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([not a Lua interpreter])
])
])
],
[ dnl A version check is needed.
AS_IF([test "x$LUA" != 'x'],
[ dnl Check if this is a Lua interpreter.
AC_MSG_CHECKING([if $LUA is a Lua interpreter])
_AX_LUA_CHK_IS_INTRP([$LUA],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([not a Lua interpreter])
])
dnl Check the version.
m4_if([$2], [],
[_ax_check_text="whether $LUA version >= $1"],
[_ax_check_text="whether $LUA version >= $1, < $2"])
AC_MSG_CHECKING([$_ax_check_text])
_AX_LUA_CHK_VER([$LUA], [$1], [$2],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([version is out of range for specified LUA])])
ax_display_LUA=$LUA
],
[ dnl Try each interpreter until we find one that satisfies VERSION.
m4_if([$2], [],
[_ax_check_text="for a Lua interpreter with version >= $1"],
[_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
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])
done
])
dnl Set $LUA to the absolute path of $ax_cv_pathless_LUA.
AS_IF([test "x$ax_cv_pathless_LUA" = 'xnone'],
[LUA=':'],
[AC_PATH_PROG([LUA], [$ax_cv_pathless_LUA])])
ax_display_LUA=$ax_cv_pathless_LUA
])
])
AS_IF([test "x$LUA" = 'x:'],
[ dnl Run any user-specified action, or abort.
m4_default([$4], [AC_MSG_ERROR([cannot find suitable Lua interpreter])])
],
[ dnl Query Lua for its version number.
AC_CACHE_CHECK([for $ax_display_LUA version],
[ax_cv_lua_version],
[ dnl Get the interpreter version in X.Y format. This should work for
dnl interpreters version 5.0 and beyond.
ax_cv_lua_version=[`$LUA -e '
-- return a version number in X.Y format
local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)")
print(ver or "")'`]
])
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.
AC_CACHE_CHECK([for $ax_display_LUA platform],
[ax_cv_lua_platform],
[ax_cv_lua_platform=[`$LUA -e 'print("unknown")'`]])
AC_SUBST([LUA_PLATFORM], [$ax_cv_lua_platform])
dnl Use the values of $prefix and $exec_prefix for the corresponding
dnl values of LUA_PREFIX and LUA_EXEC_PREFIX. These are made distinct
dnl variables so they can be overridden if need be. However, the general
dnl consensus is that you shouldn't need this ability.
AC_SUBST([LUA_PREFIX], ['${prefix}'])
AC_SUBST([LUA_EXEC_PREFIX], ['${exec_prefix}'])
dnl Lua provides no way to query the script directory, and instead
dnl provides LUA_PATH. However, we should be able to make a safe educated
dnl guess. If the built-in search path contains a directory which is
dnl prefixed by $prefix, then we can store scripts there. The first
dnl matching path will be used.
AC_CACHE_CHECK([for $ax_display_LUA script directory],
[ax_cv_lua_luadir],
[ AS_IF([test "x$prefix" = 'xNONE'],
[ax_lua_prefix=$ac_default_prefix],
[ax_lua_prefix=$prefix])
dnl Initialize to the default path.
ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION"
dnl Try to find a path with the prefix.
_AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [script])
AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
[ dnl Fix the prefix.
_ax_strip_prefix=`echo "$ax_lua_prefix" | $SED 's|.|.|g'`
ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \
$SED "s|^$_ax_strip_prefix|$LUA_PREFIX|"`
])
])
AC_SUBST([luadir], [$ax_cv_lua_luadir])
AC_SUBST([pkgluadir], [\${luadir}/$PACKAGE])
dnl Lua provides no way to query the module directory, and instead
dnl provides LUA_PATH. However, we should be able to make a safe educated
dnl guess. If the built-in search path contains a directory which is
dnl prefixed by $exec_prefix, then we can store modules there. The first
dnl matching path will be used.
AC_CACHE_CHECK([for $ax_display_LUA module directory],
[ax_cv_lua_luaexecdir],
[ AS_IF([test "x$exec_prefix" = 'xNONE'],
[ax_lua_exec_prefix=$ax_lua_prefix],
[ax_lua_exec_prefix=$exec_prefix])
dnl Initialize to the default path.
ax_cv_lua_luaexecdir="$LUA_EXEC_PREFIX/lib/lua/$LUA_VERSION"
dnl Try to find a path with the prefix.
_AX_LUA_FND_PRFX_PTH([$LUA],
[$ax_lua_exec_prefix], [module])
AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
[ dnl Fix the prefix.
_ax_strip_prefix=`echo "$ax_lua_exec_prefix" | $SED 's|.|.|g'`
ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \
$SED "s|^$_ax_strip_prefix|$LUA_EXEC_PREFIX|"`
])
])
AC_SUBST([luaexecdir], [$ax_cv_lua_luaexecdir])
AC_SUBST([pkgluaexecdir], [\${luaexecdir}/$PACKAGE])
dnl Run any user specified action.
$3
])
])
dnl AX_WITH_LUA is now the same thing as AX_PROG_LUA.
AC_DEFUN([AX_WITH_LUA],
[
AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA instead]])
AX_PROG_LUA
])
dnl =========================================================================
dnl _AX_LUA_CHK_IS_INTRP(PROG, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
dnl =========================================================================
AC_DEFUN([_AX_LUA_CHK_IS_INTRP],
[
dnl A minimal Lua factorial to prove this is an interpreter. This should work
dnl for Lua interpreters version 5.0 and beyond.
_ax_lua_factorial=[`$1 2>/dev/null -e '
-- a simple factorial
function fact (n)
if n == 0 then
return 1
else
return n * fact(n-1)
end
end
print("fact(5) is " .. fact(5))'`]
AS_IF([test "$_ax_lua_factorial" = 'fact(5) is 120'],
[$2], [$3])
])
dnl =========================================================================
dnl _AX_LUA_CHK_VER(PROG, MINIMUM-VERSION, [TOO-BIG-VERSION],
dnl [ACTION-IF-TRUE], [ACTION-IF-FALSE])
dnl =========================================================================
AC_DEFUN([_AX_LUA_CHK_VER],
[
dnl Check that the Lua version is within the bounds. Only the major and minor
dnl version numbers are considered. This should work for Lua interpreters
dnl version 5.0 and beyond.
_ax_lua_good_version=[`$1 -e '
-- a script to compare versions
function verstr2num(verstr)
local _, _, majorver, minorver = string.find(verstr, "^(%d+)%.(%d+)")
if majorver and minorver then
return tonumber(majorver) * 100 + tonumber(minorver)
end
end
local minver = verstr2num("$2")
local _, _, trimver = string.find(_VERSION, "^Lua (.*)")
local ver = verstr2num(trimver)
local maxver = verstr2num("$3") or 1e9
if minver <= ver and ver < maxver then
print("yes")
else
print("no")
end'`]
AS_IF([test "x$_ax_lua_good_version" = "xyes"],
[$4], [$5])
])
dnl =========================================================================
dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, SCRIPT-OR-MODULE-DIR)
dnl =========================================================================
AC_DEFUN([_AX_LUA_FND_PRFX_PTH],
[
dnl Get the script or module directory by querying the Lua interpreter,
dnl filtering on the given prefix, and selecting the shallowest path. If no
dnl path is found matching the prefix, the result will be an empty string.
dnl The third argument determines the type of search, it can be 'script' or
dnl 'module'. Supplying 'script' will perform the search with package.path
dnl and LUA_PATH, and supplying 'module' will search with package.cpath and
dnl LUA_CPATH. This is done for compatibility with Lua 5.0.
ax_lua_prefixed_path=[`$1 -e '
-- get the path based on search type
local searchtype = "$3"
local paths = ""
if searchtype == "script" then
paths = (package and package.path) or LUA_PATH
elseif searchtype == "module" then
paths = (package and package.cpath) or LUA_CPATH
end
-- search for the prefix
local prefix = "'$2'"
local minpath = ""
local mindepth = 1e9
string.gsub(paths, "(@<:@^;@:>@+)",
function (path)
path = string.gsub(path, "%?.*$", "")
path = string.gsub(path, "/@<:@^/@:>@*$", "")
if string.find(path, prefix) then
local depth = string.len(string.gsub(path, "@<:@^/@:>@", ""))
if depth < mindepth then
minpath = path
mindepth = depth
end
end
end)
print(minpath)'`]
])
dnl =========================================================================
dnl AX_LUA_HEADERS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl =========================================================================
AC_DEFUN([AX_LUA_HEADERS],
[
dnl Check for LUA_VERSION.
AC_MSG_CHECKING([if LUA_VERSION is defined])
AS_IF([test "x$LUA_VERSION" != 'x'],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION])
])
AM_COND_IF([LUAJIT],[
dnl Check for LUAJIT_VERSION.
AC_MSG_CHECKING([if LUAJIT_VERSION is defined])
AS_IF([test "x$LUAJIT_VERSION" != 'x'],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([cannot check Lua jit headers without knowing LUAJIT_VERSION])
])
])
dnl Make LUA_INCLUDE a precious variable.
AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1])
dnl Some default directories to search.
AM_COND_IF([LUAJIT],
[_ax_lua_include_list="
/usr/include/luajit-$LUAJIT_VERSION
/usr/include/luajit-$LUAJIT_SHORT_VERSION
/usr/local/include/luajit-$LUAJIT_VERSION
/usr/local/include/luajit-$LUAJIT_SHORT_VERSION"],
[_ax_lua_include_list="
/usr/include/lua$LUA_VERSION
/usr/include/lua-$LUA_VERSION
/usr/include/lua/$LUA_VERSION
/usr/include/lua$LUA_SHORT_VERSION
/usr/local/include/lua$LUA_VERSION
/usr/local/include/lua-$LUA_VERSION
/usr/local/include/lua/$LUA_VERSION
/usr/local/include/lua$LUA_SHORT_VERSION"])
dnl Try to find the headers.
_ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
AM_COND_IF([LUAJIT], [AC_CHECK_HEADERS([luajit.h])])
CPPFLAGS=$_ax_lua_saved_cppflags
dnl Try some other directories if LUA_INCLUDE was not set.
AS_IF([test "x$LUA_INCLUDE" = 'x' &&
test "x$ac_cv_header_lua_h" != 'xyes' ||
test "x$with_luajit" != 'xno' &&
test "x$ac_cv_header_luajit_h" != 'xyes'],
[ dnl Try some common include paths.
for _ax_include_path in $_ax_lua_include_list; do
test ! -d "$_ax_include_path" && continue
AC_MSG_CHECKING([for Lua headers in])
AC_MSG_RESULT([$_ax_include_path])
AS_UNSET([ac_cv_header_lua_h])
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'],
[ LUA_INCLUDE="-I$_ax_include_path"
break
])
done
])
AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
[ dnl Make a program to print LUA_VERSION defined in the header.
dnl TODO It would be really nice if we could do this without compiling a
dnl program, then it would work when cross compiling. But I'm not sure how
dnl to do this reliably. For now, assume versions match when cross compiling.
AS_IF([test "x$cross_compiling" != 'xyes'],
[ AC_CACHE_CHECK([for Lua header version],
[ax_cv_lua_header_version],
[ _ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
AC_COMPUTE_INT(ax_cv_lua_header_version_major,[LUA_VERSION_NUM/100],[AC_INCLUDES_DEFAULT
#include <lua.h>
],[ax_cv_lua_header_version_major=unknown])
AC_COMPUTE_INT(ax_cv_lua_header_version_minor,[LUA_VERSION_NUM%100],[AC_INCLUDES_DEFAULT
#include <lua.h>
],[ax_cv_lua_header_version_minor=unknown])
AS_IF([test "x$ax_cv_lua_header_version_major" = xunknown || test "x$ax_cv_lua_header_version_minor" = xunknown],[
ax_cv_lua_header_version=unknown
],[
ax_cv_lua_header_version="$ax_cv_lua_header_version_major.$ax_cv_lua_header_version_minor"
])
CPPFLAGS=$_ax_lua_saved_cppflags
])
dnl Compare this to the previously found LUA_VERSION.
AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION])
AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"],
[ AC_MSG_RESULT([yes])
ax_header_version_match='yes'
],
[ AC_MSG_RESULT([no])
ax_header_version_match='no'
])
],
[ AC_MSG_WARN([cross compiling so assuming header version number matches])
ax_header_version_match='yes'
])
])
dnl Was LUA_INCLUDE specified?
AS_IF([test "x$ax_header_version_match" != 'xyes' &&
test "x$LUA_INCLUDE" != 'x'],
[AC_MSG_ERROR([cannot find headers for specified LUA_INCLUDE])])
dnl Test the final result and run user code.
AS_IF([test "x$ax_header_version_match" = 'xyes'], [$1],
[m4_default([$2], [AC_MSG_ERROR([cannot find Lua includes])])])
])
dnl AX_LUA_HEADERS_VERSION no longer exists, use AX_LUA_HEADERS.
AC_DEFUN([AX_LUA_HEADERS_VERSION],
[
AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS instead]])
])
dnl =========================================================================
dnl AX_LUA_LIBS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl =========================================================================
AC_DEFUN([AX_LUA_LIBS],
[
dnl TODO Should this macro also check various -L flags?
dnl Check for LUA_VERSION.
AC_MSG_CHECKING([if LUA_VERSION is defined])
AS_IF([test "x$LUA_VERSION" != 'x'],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([cannot check Lua libs without knowing LUA_VERSION])
])
dnl Make LUA_LIB a precious variable.
AC_ARG_VAR([LUA_LIB], [The Lua library, e.g. -llua5.1])
AS_IF([test "x$LUA_LIB" != 'x'],
[ dnl Check that LUA_LIBS works.
_ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB"
AC_SEARCH_LIBS([lua_load], [],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'])
LIBS=$_ax_lua_saved_libs
dnl Check the result.
AS_IF([test "x$_ax_found_lua_libs" != 'xyes'],
[AC_MSG_ERROR([cannot find libs for specified LUA_LIB])])
],
[ dnl First search for extra libs.
_ax_lua_extra_libs=''
_ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB"
AC_SEARCH_LIBS([exp], [m])
AC_SEARCH_LIBS([dlopen], [dl])
LIBS=$_ax_lua_saved_libs
AS_IF([test "x$ac_cv_search_exp" != 'xno' &&
test "x$ac_cv_search_exp" != 'xnone required'],
[_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_exp"])
AS_IF([test "x$ac_cv_search_dlopen" != 'xno' &&
test "x$ac_cv_search_dlopen" != 'xnone required'],
[_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_dlopen"])
dnl Try to find the Lua libs.
_ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB"
AM_COND_IF([LUAJIT],
[AC_SEARCH_LIBS([lua_load],
[ luajit$LUA_VERSION \
luajit$LUA_SHORT_VERSION \
luajit-$LUA_VERSION \
luajit-$LUA_SHORT_VERSION \
luajit],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'],
[$_ax_lua_extra_libs])],
[AC_SEARCH_LIBS([lua_load],
[ lua$LUA_VERSION \
lua$LUA_SHORT_VERSION \
lua-$LUA_VERSION \
lua-$LUA_SHORT_VERSION \
lua \
],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'],
[$_ax_lua_extra_libs])])
LIBS=$_ax_lua_saved_libs
AS_IF([test "x$ac_cv_search_lua_load" != 'xno' &&
test "x$ac_cv_search_lua_load" != 'xnone required'],
[LUA_LIB="$ac_cv_search_lua_load $_ax_lua_extra_libs"])
])
dnl Test the result and run user code.
AS_IF([test "x$_ax_found_lua_libs" = 'xyes'], [$1],
[m4_default([$2], [AC_MSG_ERROR([cannot find Lua libs])])])
])
dnl =========================================================================
dnl AX_LUA_READLINE([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl =========================================================================
AC_DEFUN([AX_LUA_READLINE],
[
AX_LIB_READLINE
AS_IF([test "x$ac_cv_header_readline_readline_h" != 'x' &&
test "x$ac_cv_header_readline_history_h" != 'x'],
[ LUA_LIBS_CFLAGS="-DLUA_USE_READLINE $LUA_LIBS_CFLAGS"
$1
],
[$2])
])
-522
View File
@@ -1,522 +0,0 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_pthread.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
#
# DESCRIPTION
#
# This macro figures out how to build C programs using POSIX threads. It
# sets the PTHREAD_LIBS output variable to the threads library and linker
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
# flags that are needed. (The user can also force certain compiler
# flags/libs to be tested by setting these environment variables.)
#
# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is
# needed for multi-threaded programs (defaults to the value of CC
# respectively CXX otherwise). (This is necessary on e.g. AIX to use the
# special cc_r/CC_r compiler alias.)
#
# NOTE: You are assumed to not only compile your program with these flags,
# but also to link with them as well. For example, you might link with
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
# $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
#
# If you are only building threaded programs, you may wish to use these
# variables in your default LIBS, CFLAGS, and CC:
#
# LIBS="$PTHREAD_LIBS $LIBS"
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS"
# CC="$PTHREAD_CC"
# CXX="$PTHREAD_CXX"
#
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
#
# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
# PTHREAD_PRIO_INHERIT symbol is defined when compiling with
# PTHREAD_CFLAGS.
#
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
# is not found. If ACTION-IF-FOUND is not specified, the default action
# will define HAVE_PTHREAD.
#
# Please let the authors know if this macro fails on any platform, or if
# you have any other suggestions or comments. This macro was based on work
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
# grateful for the helpful feedback of numerous users.
#
# Updated for Autoconf 2.68 by Daniel Richard G.
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
# Copyright (c) 2019 Marc Stevens <marc.stevens@cwi.nl>
#
# 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 3 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, see <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 31
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
AC_DEFUN([AX_PTHREAD], [
AC_REQUIRE([AC_CANONICAL_HOST])
AC_REQUIRE([AC_PROG_CC])
AC_REQUIRE([AC_PROG_SED])
AC_LANG_PUSH([C])
ax_pthread_ok=no
# We used to check for pthread.h first, but this fails if pthread.h
# requires special compiler flags (e.g. on Tru64 or Sequent).
# It gets checked for in the link test anyway.
# First of all, check if the user has set any of the PTHREAD_LIBS,
# etcetera environment variables, and if threads linking works using
# them:
if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
ax_pthread_save_CC="$CC"
ax_pthread_save_CFLAGS="$CFLAGS"
ax_pthread_save_LIBS="$LIBS"
AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"])
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes])
AC_MSG_RESULT([$ax_pthread_ok])
if test "x$ax_pthread_ok" = "xno"; then
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
fi
CC="$ax_pthread_save_CC"
CFLAGS="$ax_pthread_save_CFLAGS"
LIBS="$ax_pthread_save_LIBS"
fi
# We must check for the threads library under a number of different
# names; the ordering is very important because some systems
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
# libraries is broken (non-POSIX).
# Create a list of thread flags to try. Items with a "," contain both
# C compiler flags (before ",") and linker flags (after ","). Other items
# starting with a "-" are C compiler flags, and remaining items are
# library names, except for "none" which indicates that we try without
# any flags at all, and "pthread-config" which is a program returning
# the flags for the Pth emulation library.
ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
# pthreads: AIX (must check this before -lpthread)
# none: in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
# (Note: HP C rejects this with "bad form for `-t' option")
# -pthreads: Solaris/gcc (Note: HP C also rejects)
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
# doesn't hurt to check since this sometimes defines pthreads and
# -D_REENTRANT too), HP C (must be checked before -lpthread, which
# is present but should not be used directly; and before -mthreads,
# because the compiler interprets this as "-mt" + "-hreads")
# -mthreads: Mingw32/gcc, Lynx/gcc
# pthread: Linux, etcetera
# --thread-safe: KAI C++
# pthread-config: use pthread-config program (for GNU Pth library)
case $host_os in
freebsd*)
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
ax_pthread_flags="-kthread lthread $ax_pthread_flags"
;;
hpux*)
# From the cc(1) man page: "[-mt] Sets various -D flags to enable
# multi-threading and also sets -lpthread."
ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
;;
openedition*)
# IBM z/OS requires a feature-test macro to be defined in order to
# enable POSIX threads at all, so give the user a hint if this is
# not set. (We don't define these ourselves, as they can affect
# other portions of the system API in unpredictable ways.)
AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING],
[
# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
AX_PTHREAD_ZOS_MISSING
# endif
],
[AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])])
;;
solaris*)
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
# tests will erroneously succeed. (N.B.: The stubs are missing
# pthread_cleanup_push, or rather a function called by this macro,
# so we could check for that, but who knows whether they'll stub
# that too in a future libc.) So we'll check first for the
# standard Solaris way of linking pthreads (-mt -lpthread).
ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags"
;;
esac
# Are we compiling with Clang?
AC_CACHE_CHECK([whether $CC is Clang],
[ax_cv_PTHREAD_CLANG],
[ax_cv_PTHREAD_CLANG=no
# Note that Autoconf sets GCC=yes for Clang as well as GCC
if test "x$GCC" = "xyes"; then
AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
[/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
# if defined(__clang__) && defined(__llvm__)
AX_PTHREAD_CC_IS_CLANG
# endif
],
[ax_cv_PTHREAD_CLANG=yes])
fi
])
ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
# Note that for GCC and Clang -pthread generally implies -lpthread,
# except when -nostdlib is passed.
# This is problematic using libtool to build C++ shared libraries with pthread:
# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460
# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333
# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555
# To solve this, first try -pthread together with -lpthread for GCC
AS_IF([test "x$GCC" = "xyes"],
[ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"])
# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first
AS_IF([test "x$ax_pthread_clang" = "xyes"],
[ax_pthread_flags="-pthread,-lpthread -pthread"])
# The presence of a feature test macro requesting re-entrant function
# definitions is, on some systems, a strong hint that pthreads support is
# correctly enabled
case $host_os in
darwin* | hpux* | linux* | osf* | solaris*)
ax_pthread_check_macro="_REENTRANT"
;;
aix*)
ax_pthread_check_macro="_THREAD_SAFE"
;;
*)
ax_pthread_check_macro="--"
;;
esac
AS_IF([test "x$ax_pthread_check_macro" = "x--"],
[ax_pthread_check_cond=0],
[ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
if test "x$ax_pthread_ok" = "xno"; then
for ax_pthread_try_flag in $ax_pthread_flags; do
case $ax_pthread_try_flag in
none)
AC_MSG_CHECKING([whether pthreads work without any flags])
;;
*,*)
PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"`
PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"`
AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"])
;;
-*)
AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
PTHREAD_CFLAGS="$ax_pthread_try_flag"
;;
pthread-config)
AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
PTHREAD_CFLAGS="`pthread-config --cflags`"
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
;;
*)
AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
PTHREAD_LIBS="-l$ax_pthread_try_flag"
;;
esac
ax_pthread_save_CFLAGS="$CFLAGS"
ax_pthread_save_LIBS="$LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
# Check for various functions. We must include pthread.h,
# since some functions may be macros. (On the Sequent, we
# need a special flag -Kthread to make this header compile.)
# We check for pthread_join because it is in -lpthread on IRIX
# while pthread_create is in libc. We check for pthread_attr_init
# due to DEC craziness with -lpthreads. We check for
# pthread_cleanup_push because it is one of the few pthread
# functions on Solaris that doesn't have a non-functional libc stub.
# We try pthread_create on general principles.
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
# if $ax_pthread_check_cond
# error "$ax_pthread_check_macro must be defined"
# endif
static void *some_global = NULL;
static void routine(void *a)
{
/* To avoid any unused-parameter or
unused-but-set-parameter warning. */
some_global = a;
}
static void *start_routine(void *a) { return a; }],
[pthread_t th; pthread_attr_t attr;
pthread_create(&th, 0, start_routine, 0);
pthread_join(th, 0);
pthread_attr_init(&attr);
pthread_cleanup_push(routine, 0);
pthread_cleanup_pop(0) /* ; */])],
[ax_pthread_ok=yes],
[])
CFLAGS="$ax_pthread_save_CFLAGS"
LIBS="$ax_pthread_save_LIBS"
AC_MSG_RESULT([$ax_pthread_ok])
AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
done
fi
# Clang needs special handling, because older versions handle the -pthread
# option in a rather... idiosyncratic way
if test "x$ax_pthread_clang" = "xyes"; then
# Clang takes -pthread; it has never supported any other flag
# (Note 1: This will need to be revisited if a system that Clang
# supports has POSIX threads in a separate library. This tends not
# to be the way of modern systems, but it's conceivable.)
# (Note 2: On some systems, notably Darwin, -pthread is not needed
# to get POSIX threads support; the API is always present and
# active. We could reasonably leave PTHREAD_CFLAGS empty. But
# -pthread does define _REENTRANT, and while the Darwin headers
# ignore this macro, third-party headers might not.)
# However, older versions of Clang make a point of warning the user
# that, in an invocation where only linking and no compilation is
# taking place, the -pthread option has no effect ("argument unused
# during compilation"). They expect -pthread to be passed in only
# when source code is being compiled.
#
# Problem is, this is at odds with the way Automake and most other
# C build frameworks function, which is that the same flags used in
# compilation (CFLAGS) are also used in linking. Many systems
# supported by AX_PTHREAD require exactly this for POSIX threads
# support, and in fact it is often not straightforward to specify a
# flag that is used only in the compilation phase and not in
# linking. Such a scenario is extremely rare in practice.
#
# Even though use of the -pthread flag in linking would only print
# a warning, this can be a nuisance for well-run software projects
# that build with -Werror. So if the active version of Clang has
# this misfeature, we search for an option to squash it.
AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
# Create an alternate version of $ac_link that compiles and
# links in two steps (.c -> .o, .o -> exe) instead of one
# (.c -> exe), because the warning occurs only in the second
# step
ax_pthread_save_ac_link="$ac_link"
ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"`
ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
ax_pthread_save_CFLAGS="$CFLAGS"
for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
ac_link="$ax_pthread_save_ac_link"
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
[ac_link="$ax_pthread_2step_ac_link"
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
[break])
])
done
ac_link="$ax_pthread_save_ac_link"
CFLAGS="$ax_pthread_save_CFLAGS"
AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
])
case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
no | unknown) ;;
*) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
esac
fi # $ax_pthread_clang = yes
# Various other checks:
if test "x$ax_pthread_ok" = "xyes"; then
ax_pthread_save_CFLAGS="$CFLAGS"
ax_pthread_save_LIBS="$LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
AC_CACHE_CHECK([for joinable pthread attribute],
[ax_cv_PTHREAD_JOINABLE_ATTR],
[ax_cv_PTHREAD_JOINABLE_ATTR=unknown
for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
[int attr = $ax_pthread_attr; return attr /* ; */])],
[ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break],
[])
done
])
AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
test "x$ax_pthread_joinable_attr_defined" != "xyes"],
[AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE],
[$ax_cv_PTHREAD_JOINABLE_ATTR],
[Define to necessary symbol if this constant
uses a non-standard name on your system.])
ax_pthread_joinable_attr_defined=yes
])
AC_CACHE_CHECK([whether more special flags are required for pthreads],
[ax_cv_PTHREAD_SPECIAL_FLAGS],
[ax_cv_PTHREAD_SPECIAL_FLAGS=no
case $host_os in
solaris*)
ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
;;
esac
])
AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
test "x$ax_pthread_special_flags_added" != "xyes"],
[PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
ax_pthread_special_flags_added=yes])
AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
[ax_cv_PTHREAD_PRIO_INHERIT],
[AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
[[int i = PTHREAD_PRIO_INHERIT;
return i;]])],
[ax_cv_PTHREAD_PRIO_INHERIT=yes],
[ax_cv_PTHREAD_PRIO_INHERIT=no])
])
AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
test "x$ax_pthread_prio_inherit_defined" != "xyes"],
[AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])
ax_pthread_prio_inherit_defined=yes
])
CFLAGS="$ax_pthread_save_CFLAGS"
LIBS="$ax_pthread_save_LIBS"
# More AIX lossage: compile with *_r variant
if test "x$GCC" != "xyes"; then
case $host_os in
aix*)
AS_CASE(["x/$CC"],
[x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
[#handle absolute path differently from PATH based program lookup
AS_CASE(["x$CC"],
[x/*],
[
AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])
AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])])
],
[
AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])
AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])])
]
)
])
;;
esac
fi
fi
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX"
AC_SUBST([PTHREAD_LIBS])
AC_SUBST([PTHREAD_CFLAGS])
AC_SUBST([PTHREAD_CC])
AC_SUBST([PTHREAD_CXX])
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test "x$ax_pthread_ok" = "xyes"; then
ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
:
else
ax_pthread_ok=no
$2
fi
AC_LANG_POP
])dnl AX_PTHREAD
-37
View File
@@ -1,37 +0,0 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_require_defined.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_REQUIRE_DEFINED(MACRO)
#
# DESCRIPTION
#
# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have
# been defined and thus are available for use. This avoids random issues
# where a macro isn't expanded. Instead the configure script emits a
# non-fatal:
#
# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found
#
# It's like AC_REQUIRE except it doesn't expand the required macro.
#
# Here's an example:
#
# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
#
# LICENSE
#
# Copyright (c) 2014 Mike Frysinger <vapier@gentoo.org>
#
# 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 2
AC_DEFUN([AX_REQUIRE_DEFINED], [dnl
m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])])
])dnl AX_REQUIRE_DEFINED
-582
View File
@@ -1,582 +0,0 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_with_curses.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_WITH_CURSES
#
# DESCRIPTION
#
# This macro checks whether a SysV or X/Open-compatible Curses library is
# present, along with the associated header file. The NcursesW
# (wide-character) library is searched for first, followed by Ncurses,
# then the system-default plain Curses. The first library found is the
# one returned. Finding libraries will first be attempted by using
# pkg-config, and should the pkg-config files not be available, will
# fallback to combinations of known flags itself.
#
# The following options are understood: --with-ncursesw, --with-ncurses,
# --without-ncursesw, --without-ncurses. The "--with" options force the
# macro to use that particular library, terminating with an error if not
# found. The "--without" options simply skip the check for that library.
# The effect on the search pattern is:
#
# (no options) - NcursesW, Ncurses, Curses
# --with-ncurses --with-ncursesw - NcursesW only [*]
# --without-ncurses --with-ncursesw - NcursesW only [*]
# --with-ncursesw - NcursesW only [*]
# --with-ncurses --without-ncursesw - Ncurses only [*]
# --with-ncurses - NcursesW, Ncurses [**]
# --without-ncurses --without-ncursesw - Curses only
# --without-ncursesw - Ncurses, Curses
# --without-ncurses - NcursesW, Curses
#
# [*] If the library is not found, abort the configure script.
#
# [**] If the second library (Ncurses) is not found, abort configure.
#
# The following preprocessor symbols may be defined by this macro if the
# appropriate conditions are met:
#
# HAVE_CURSES - if any SysV or X/Open Curses library found
# HAVE_CURSES_ENHANCED - if library supports X/Open Enhanced functions
# HAVE_CURSES_COLOR - if library supports color (enhanced functions)
# HAVE_CURSES_OBSOLETE - if library supports certain obsolete features
# HAVE_NCURSESW - if NcursesW (wide char) library is to be used
# HAVE_NCURSES - if the Ncurses library is to be used
#
# HAVE_CURSES_H - if <curses.h> is present and should be used
# HAVE_NCURSESW_H - if <ncursesw.h> should be used
# HAVE_NCURSES_H - if <ncurses.h> should be used
# HAVE_NCURSESW_CURSES_H - if <ncursesw/curses.h> should be used
# HAVE_NCURSES_CURSES_H - if <ncurses/curses.h> should be used
#
# (These preprocessor symbols are discussed later in this document.)
#
# The following output variables are defined by this macro; they are
# precious and may be overridden on the ./configure command line:
#
# CURSES_LIBS - library to add to xxx_LDADD
# CURSES_CFLAGS - include paths to add to xxx_CPPFLAGS
#
# In previous versions of this macro, the flags CURSES_LIB and
# CURSES_CPPFLAGS were defined. These have been renamed, in keeping with
# AX_WITH_CURSES's close bigger brother, PKG_CHECK_MODULES, which should
# eventually supersede the use of AX_WITH_CURSES. Neither the library
# listed in CURSES_LIBS, nor the flags in CURSES_CFLAGS are added to LIBS,
# respectively CPPFLAGS, by default. You need to add both to the
# appropriate xxx_LDADD/xxx_CPPFLAGS line in your Makefile.am. For
# example:
#
# prog_LDADD = @CURSES_LIBS@
# prog_CPPFLAGS = @CURSES_CFLAGS@
#
# If CURSES_LIBS is set on the configure command line (such as by running
# "./configure CURSES_LIBS=-lmycurses"), then the only header searched for
# is <curses.h>. If the user needs to specify an alternative path for a
# library (such as for a non-standard NcurseW), the user should use the
# LDFLAGS variable.
#
# The following shell variables may be defined by this macro:
#
# ax_cv_curses - set to "yes" if any Curses library found
# ax_cv_curses_enhanced - set to "yes" if Enhanced functions present
# ax_cv_curses_color - set to "yes" if color functions present
# ax_cv_curses_obsolete - set to "yes" if obsolete features present
#
# ax_cv_ncursesw - set to "yes" if NcursesW library found
# ax_cv_ncurses - set to "yes" if Ncurses library found
# ax_cv_plaincurses - set to "yes" if plain Curses library found
# ax_cv_curses_which - set to "ncursesw", "ncurses", "plaincurses" or "no"
#
# These variables can be used in your configure.ac to determine the level
# of support you need from the Curses library. For example, if you must
# have either Ncurses or NcursesW, you could include:
#
# AX_WITH_CURSES
# if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
# AC_MSG_ERROR([requires either NcursesW or Ncurses library])
# fi
#
# If any Curses library will do (but one must be present and must support
# color), you could use:
#
# AX_WITH_CURSES
# if test "x$ax_cv_curses" != xyes || test "x$ax_cv_curses_color" != xyes; then
# AC_MSG_ERROR([requires an X/Open-compatible Curses library with color])
# fi
#
# Certain preprocessor symbols and shell variables defined by this macro
# can be used to determine various features of the Curses library. In
# particular, HAVE_CURSES and ax_cv_curses are defined if the Curses
# library found conforms to the traditional SysV and/or X/Open Base Curses
# definition. Any working Curses library conforms to this level.
#
# HAVE_CURSES_ENHANCED and ax_cv_curses_enhanced are defined if the
# library supports the X/Open Enhanced Curses definition. In particular,
# the wide-character types attr_t, cchar_t and wint_t, the functions
# wattr_set() and wget_wch() and the macros WA_NORMAL and _XOPEN_CURSES
# are checked. The Ncurses library does NOT conform to this definition,
# although NcursesW does.
#
# HAVE_CURSES_COLOR and ax_cv_curses_color are defined if the library
# supports color functions and macros such as COLOR_PAIR, A_COLOR,
# COLOR_WHITE, COLOR_RED and init_pair(). These are NOT part of the
# X/Open Base Curses definition, but are part of the Enhanced set of
# functions. The Ncurses library DOES support these functions, as does
# NcursesW.
#
# HAVE_CURSES_OBSOLETE and ax_cv_curses_obsolete are defined if the
# library supports certain features present in SysV and BSD Curses but not
# defined in the X/Open definition. In particular, the functions
# getattrs(), getcurx() and getmaxx() are checked.
#
# To use the HAVE_xxx_H preprocessor symbols, insert the following into
# your system.h (or equivalent) header file:
#
# #if defined HAVE_NCURSESW_CURSES_H
# # include <ncursesw/curses.h>
# #elif defined HAVE_NCURSESW_H
# # include <ncursesw.h>
# #elif defined HAVE_NCURSES_CURSES_H
# # include <ncurses/curses.h>
# #elif defined HAVE_NCURSES_H
# # include <ncurses.h>
# #elif defined HAVE_CURSES_H
# # include <curses.h>
# #else
# # error "SysV or X/Open-compatible Curses header file required"
# #endif
#
# For previous users of this macro: you should not need to change anything
# in your configure.ac or Makefile.am, as the previous (serial 10)
# semantics are still valid. However, you should update your system.h (or
# equivalent) header file to the fragment shown above. You are encouraged
# also to make use of the extended functionality provided by this version
# of AX_WITH_CURSES, as well as in the additional macros
# AX_WITH_CURSES_PANEL, AX_WITH_CURSES_MENU and AX_WITH_CURSES_FORM.
#
# LICENSE
#
# Copyright (c) 2009 Mark Pulford <mark@kyne.com.au>
# Copyright (c) 2009 Damian Pietras <daper@daper.net>
# Copyright (c) 2012 Reuben Thomas <rrt@sc3d.org>
# Copyright (c) 2011 John Zaitseff <J.Zaitseff@zap.org.au>
#
# 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 3 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, see <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 18
# internal function to factorize common code that is used by both ncurses
# and ncursesw
AC_DEFUN([_FIND_CURSES_FLAGS], [
AC_MSG_CHECKING([for $1 via pkg-config])
AX_REQUIRE_DEFINED([PKG_CHECK_EXISTS])
_PKG_CONFIG([_ax_cv_$1_libs], [libs], [$1])
_PKG_CONFIG([_ax_cv_$1_cppflags], [cflags], [$1])
AS_IF([test "x$pkg_failed" = "xyes" || test "x$pkg_failed" = "xuntried"],[
AC_MSG_RESULT([no])
# No suitable .pc file found, have to find flags via fallback
AC_CACHE_CHECK([for $1 via fallback], [ax_cv_$1], [
AS_ECHO()
pkg_cv__ax_cv_$1_libs="-l$1"
pkg_cv__ax_cv_$1_cppflags="-D_GNU_SOURCE $CURSES_CFLAGS"
LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_libs"
CPPFLAGS="$ax_saved_CPPFLAGS $pkg_cv__ax_cv_$1_cppflags"
AC_MSG_CHECKING([for initscr() with $pkg_cv__ax_cv_$1_libs])
AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])],
[
AC_MSG_RESULT([yes])
AC_MSG_CHECKING([for nodelay() with $pkg_cv__ax_cv_$1_libs])
AC_LINK_IFELSE([AC_LANG_CALL([], [nodelay])],[
ax_cv_$1=yes
],[
AC_MSG_RESULT([no])
m4_if(
[$1],[ncursesw],[pkg_cv__ax_cv_$1_libs="$pkg_cv__ax_cv_$1_libs -ltinfow"],
[$1],[ncurses],[pkg_cv__ax_cv_$1_libs="$pkg_cv__ax_cv_$1_libs -ltinfo"]
)
LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_libs"
AC_MSG_CHECKING([for nodelay() with $pkg_cv__ax_cv_$1_libs])
AC_LINK_IFELSE([AC_LANG_CALL([], [nodelay])],[
ax_cv_$1=yes
],[
ax_cv_$1=no
])
])
],[
ax_cv_$1=no
])
])
],[
AC_MSG_RESULT([yes])
# Found .pc file, using its information
LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_libs"
CPPFLAGS="$ax_saved_CPPFLAGS $pkg_cv__ax_cv_$1_cppflags"
ax_cv_$1=yes
])
])
AU_ALIAS([MP_WITH_CURSES], [AX_WITH_CURSES])
AC_DEFUN([AX_WITH_CURSES], [
AC_ARG_VAR([CURSES_LIBS], [linker library for Curses, e.g. -lcurses])
AC_ARG_VAR([CURSES_CFLAGS], [preprocessor flags for Curses, e.g. -I/usr/include/ncursesw])
AC_ARG_WITH([ncurses], [AS_HELP_STRING([--with-ncurses],
[force the use of Ncurses or NcursesW])],
[], [with_ncurses=check])
AC_ARG_WITH([ncursesw], [AS_HELP_STRING([--without-ncursesw],
[do not use NcursesW (wide character support)])],
[], [with_ncursesw=check])
ax_saved_LIBS=$LIBS
ax_saved_CPPFLAGS=$CPPFLAGS
AS_IF([test "x$with_ncurses" = xyes || test "x$with_ncursesw" = xyes],
[ax_with_plaincurses=no], [ax_with_plaincurses=check])
ax_cv_curses_which=no
# Test for NcursesW
AS_IF([test "x$CURSES_LIBS" = x && test "x$with_ncursesw" != xno], [
_FIND_CURSES_FLAGS([ncursesw])
AS_IF([test "x$ax_cv_ncursesw" = xno && test "x$with_ncursesw" = xyes], [
AC_MSG_ERROR([--with-ncursesw specified but could not find NcursesW library])
])
AS_IF([test "x$ax_cv_ncursesw" = xyes], [
ax_cv_curses=yes
ax_cv_curses_which=ncursesw
CURSES_LIBS="$pkg_cv__ax_cv_ncursesw_libs"
CURSES_CFLAGS="$pkg_cv__ax_cv_ncursesw_cppflags"
AC_DEFINE([HAVE_NCURSESW], [1], [Define to 1 if the NcursesW library is present])
AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present])
AC_CACHE_CHECK([for working ncursesw/curses.h], [ax_cv_header_ncursesw_curses_h], [
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@%:@define _XOPEN_SOURCE_EXTENDED 1
@%:@include <ncursesw/curses.h>
]], [[
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
attr_t d = WA_NORMAL;
cchar_t e;
wint_t f;
int g = getattrs(stdscr);
int h = getcurx(stdscr) + getmaxx(stdscr);
initscr();
init_pair(1, COLOR_WHITE, COLOR_RED);
wattr_set(stdscr, d, 0, NULL);
wget_wch(stdscr, &f);
]])],
[ax_cv_header_ncursesw_curses_h=yes],
[ax_cv_header_ncursesw_curses_h=no])
])
AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xyes], [
ax_cv_curses_enhanced=yes
ax_cv_curses_color=yes
ax_cv_curses_obsolete=yes
AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions])
AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features])
AC_DEFINE([HAVE_NCURSESW_CURSES_H], [1], [Define to 1 if <ncursesw/curses.h> is present])
])
AC_CACHE_CHECK([for working ncursesw.h], [ax_cv_header_ncursesw_h], [
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@%:@define _XOPEN_SOURCE_EXTENDED 1
@%:@include <ncursesw.h>
]], [[
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
attr_t d = WA_NORMAL;
cchar_t e;
wint_t f;
int g = getattrs(stdscr);
int h = getcurx(stdscr) + getmaxx(stdscr);
initscr();
init_pair(1, COLOR_WHITE, COLOR_RED);
wattr_set(stdscr, d, 0, NULL);
wget_wch(stdscr, &f);
]])],
[ax_cv_header_ncursesw_h=yes],
[ax_cv_header_ncursesw_h=no])
])
AS_IF([test "x$ax_cv_header_ncursesw_h" = xyes], [
ax_cv_curses_enhanced=yes
ax_cv_curses_color=yes
ax_cv_curses_obsolete=yes
AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions])
AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features])
AC_DEFINE([HAVE_NCURSESW_H], [1], [Define to 1 if <ncursesw.h> is present])
])
AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h_with_ncursesw], [
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@%:@define _XOPEN_SOURCE_EXTENDED 1
@%:@include <ncurses.h>
]], [[
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
attr_t d = WA_NORMAL;
cchar_t e;
wint_t f;
int g = getattrs(stdscr);
int h = getcurx(stdscr) + getmaxx(stdscr);
initscr();
init_pair(1, COLOR_WHITE, COLOR_RED);
wattr_set(stdscr, d, 0, NULL);
wget_wch(stdscr, &f);
]])],
[ax_cv_header_ncurses_h_with_ncursesw=yes],
[ax_cv_header_ncurses_h_with_ncursesw=no])
])
AS_IF([test "x$ax_cv_header_ncurses_h_with_ncursesw" = xyes], [
ax_cv_curses_enhanced=yes
ax_cv_curses_color=yes
ax_cv_curses_obsolete=yes
AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions])
AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features])
AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if <ncurses.h> is present])
])
AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xno && test "x$ax_cv_header_ncursesw_h" = xno && test "x$ax_cv_header_ncurses_h_with_ncursesw" = xno], [
AC_MSG_WARN([could not find a working ncursesw/curses.h, ncursesw.h or ncurses.h])
])
])
])
unset pkg_cv__ax_cv_ncursesw_libs
unset pkg_cv__ax_cv_ncursesw_cppflags
# Test for Ncurses
AS_IF([test "x$CURSES_LIBS" = x && test "x$with_ncurses" != xno && test "x$ax_cv_curses_which" = xno], [
_FIND_CURSES_FLAGS([ncurses])
AS_IF([test "x$ax_cv_ncurses" = xno && test "x$with_ncurses" = xyes], [
AC_MSG_ERROR([--with-ncurses specified but could not find Ncurses library])
])
AS_IF([test "x$ax_cv_ncurses" = xyes], [
ax_cv_curses=yes
ax_cv_curses_which=ncurses
CURSES_LIBS="$pkg_cv__ax_cv_ncurses_libs"
CURSES_CFLAGS="$pkg_cv__ax_cv_ncurses_cppflags"
AC_DEFINE([HAVE_NCURSES], [1], [Define to 1 if the Ncurses library is present])
AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present])
AC_CACHE_CHECK([for working ncurses/curses.h], [ax_cv_header_ncurses_curses_h], [
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@%:@include <ncurses/curses.h>
]], [[
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
int g = getattrs(stdscr);
int h = getcurx(stdscr) + getmaxx(stdscr);
initscr();
init_pair(1, COLOR_WHITE, COLOR_RED);
]])],
[ax_cv_header_ncurses_curses_h=yes],
[ax_cv_header_ncurses_curses_h=no])
])
AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xyes], [
ax_cv_curses_color=yes
ax_cv_curses_obsolete=yes
AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features])
AC_DEFINE([HAVE_NCURSES_CURSES_H], [1], [Define to 1 if <ncurses/curses.h> is present])
])
AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h], [
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@%:@include <ncurses.h>
]], [[
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
int g = getattrs(stdscr);
int h = getcurx(stdscr) + getmaxx(stdscr);
initscr();
init_pair(1, COLOR_WHITE, COLOR_RED);
]])],
[ax_cv_header_ncurses_h=yes],
[ax_cv_header_ncurses_h=no])
])
AS_IF([test "x$ax_cv_header_ncurses_h" = xyes], [
ax_cv_curses_color=yes
ax_cv_curses_obsolete=yes
AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features])
AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if <ncurses.h> is present])
])
AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xno && test "x$ax_cv_header_ncurses_h" = xno], [
AC_MSG_WARN([could not find a working ncurses/curses.h or ncurses.h])
])
])
])
unset pkg_cv__ax_cv_ncurses_libs
unset pkg_cv__ax_cv_ncurses_cppflags
# Test for plain Curses (or if CURSES_LIBS was set by user)
AS_IF([test "x$with_plaincurses" != xno && test "x$ax_cv_curses_which" = xno], [
AS_IF([test "x$CURSES_LIBS" != x], [
LIBS="$ax_saved_LIBS $CURSES_LIBS"
], [
LIBS="$ax_saved_LIBS -lcurses"
])
AC_CACHE_CHECK([for Curses library], [ax_cv_plaincurses], [
AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])],
[ax_cv_plaincurses=yes], [ax_cv_plaincurses=no])
])
AS_IF([test "x$ax_cv_plaincurses" = xyes], [
ax_cv_curses=yes
ax_cv_curses_which=plaincurses
AS_IF([test "x$CURSES_LIBS" = x], [
CURSES_LIBS="-lcurses"
])
AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present])
# Check for base conformance (and header file)
AC_CACHE_CHECK([for working curses.h], [ax_cv_header_curses_h], [
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@%:@include <curses.h>
]], [[
chtype a = A_BOLD;
int b = KEY_LEFT;
initscr();
]])],
[ax_cv_header_curses_h=yes],
[ax_cv_header_curses_h=no])
])
AS_IF([test "x$ax_cv_header_curses_h" = xyes], [
AC_DEFINE([HAVE_CURSES_H], [1], [Define to 1 if <curses.h> is present])
# Check for X/Open Enhanced conformance
AC_CACHE_CHECK([for X/Open Enhanced Curses conformance], [ax_cv_plaincurses_enhanced], [
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@%:@define _XOPEN_SOURCE_EXTENDED 1
@%:@include <curses.h>
@%:@ifndef _XOPEN_CURSES
@%:@error "this Curses library is not enhanced"
"this Curses library is not enhanced"
@%:@endif
]], [[
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
attr_t d = WA_NORMAL;
cchar_t e;
wint_t f;
initscr();
init_pair(1, COLOR_WHITE, COLOR_RED);
wattr_set(stdscr, d, 0, NULL);
wget_wch(stdscr, &f);
]])],
[ax_cv_plaincurses_enhanced=yes],
[ax_cv_plaincurses_enhanced=no])
])
AS_IF([test "x$ax_cv_plaincurses_enhanced" = xyes], [
ax_cv_curses_enhanced=yes
ax_cv_curses_color=yes
AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions])
AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
])
# Check for color functions
AC_CACHE_CHECK([for Curses color functions], [ax_cv_plaincurses_color], [
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@%:@define _XOPEN_SOURCE_EXTENDED 1
@%:@include <curses.h>
]], [[
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
initscr();
init_pair(1, COLOR_WHITE, COLOR_RED);
]])],
[ax_cv_plaincurses_color=yes],
[ax_cv_plaincurses_color=no])
])
AS_IF([test "x$ax_cv_plaincurses_color" = xyes], [
ax_cv_curses_color=yes
AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)])
])
# Check for obsolete functions
AC_CACHE_CHECK([for obsolete Curses functions], [ax_cv_plaincurses_obsolete], [
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@%:@include <curses.h>
]], [[
chtype a = A_BOLD;
int b = KEY_LEFT;
int g = getattrs(stdscr);
int h = getcurx(stdscr) + getmaxx(stdscr);
initscr();
]])],
[ax_cv_plaincurses_obsolete=yes],
[ax_cv_plaincurses_obsolete=no])
])
AS_IF([test "x$ax_cv_plaincurses_obsolete" = xyes], [
ax_cv_curses_obsolete=yes
AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features])
])
])
AS_IF([test "x$ax_cv_header_curses_h" = xno], [
AC_MSG_WARN([could not find a working curses.h])
])
])
])
AS_IF([test "x$ax_cv_curses" != xyes], [ax_cv_curses=no])
AS_IF([test "x$ax_cv_curses_enhanced" != xyes], [ax_cv_curses_enhanced=no])
AS_IF([test "x$ax_cv_curses_color" != xyes], [ax_cv_curses_color=no])
AS_IF([test "x$ax_cv_curses_obsolete" != xyes], [ax_cv_curses_obsolete=no])
LIBS=$ax_saved_LIBS
CPPFLAGS=$ax_saved_CPPFLAGS
unset ax_saved_LIBS
unset ax_saved_CPPFLAGS
])dnl
-350
View File
@@ -1,350 +0,0 @@
AC_DEFUN([TORRENT_CHECK_EPOLL], [
AC_MSG_CHECKING(for epoll support)
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <sys/epoll.h>
int main() {
int fd = epoll_create(100);
return 0;
}
])],
[
use_epoll=yes
AC_DEFINE(USE_EPOLL, 1, Use epoll.)
AC_MSG_RESULT(yes)
], [
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_WITHOUT_EPOLL], [
AC_ARG_WITH(epoll,
AS_HELP_STRING([--without-epoll],[do not check for epoll support]),
[
if test "$withval" = "yes"; then
TORRENT_CHECK_EPOLL
fi
], [
TORRENT_CHECK_EPOLL
])
])
AC_DEFUN([TORRENT_CHECK_KQUEUE], [
AC_MSG_CHECKING(for kqueue support)
AC_LINK_IFELSE([AC_LANG_SOURCE([
#include <sys/time.h> /* Because OpenBSD's sys/event.h fails to compile otherwise. Yeah... */
#include <sys/event.h>
int main() {
int fd = kqueue();
return 0;
}
])],
[
use_kqueue=yes
AC_DEFINE(USE_KQUEUE, 1, Use kqueue.)
AC_MSG_RESULT(yes)
], [
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_WITH_KQUEUE], [
AC_ARG_WITH(kqueue,
AS_HELP_STRING([--with-kqueue],[enable kqueue [[default=no]]]),
[
if test "$withval" = "yes"; then
TORRENT_CHECK_KQUEUE
fi
])
])
AC_DEFUN([TORRENT_WITHOUT_KQUEUE], [
AC_ARG_WITH(kqueue,
AS_HELP_STRING([--without-kqueue],[do not check for kqueue support]),
[
if test "$withval" = "yes"; then
TORRENT_CHECK_KQUEUE
fi
], [
TORRENT_CHECK_KQUEUE
])
])
AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [
AC_ARG_WITH(address-space,
AS_HELP_STRING([--with-address-space=MB],[change the default address space size [default=1024mb-or-32768mb]]),
[
if test ! -z $withval -a "$withval" != "yes" -a "$withval" != "no"; then
AC_DEFINE_UNQUOTED(DEFAULT_ADDRESS_SPACE_SIZE, [$withval])
else
AC_MSG_ERROR(--with-address-space requires a parameter.)
fi
],
[
AC_CHECK_SIZEOF(long)
if test $ac_cv_sizeof_long = 8; then
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_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_RESULT([no])
ATOMIC_LIBS=""
],
[
save_LIBS="$LIBS"
LIBS="$LIBS -latomic"
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.])
])
LIBS="$save_LIBS"
])
AC_LANG_POP(C++)
AC_SUBST([ATOMIC_LIBS])
])
AC_DEFUN([TORRENT_WITH_XMLRPC_C], [
AC_MSG_CHECKING(for XMLRPC-C)
AC_ARG_WITH(xmlrpc-c,
AS_HELP_STRING([--with-xmlrpc-c=PATH],[enable XMLRPC-C support]),
[
if test "$withval" = "no"; then
AC_MSG_RESULT(no)
else
if test "$withval" = "yes"; then
xmlrpc_cc_prg="xmlrpc-c-config"
else
xmlrpc_cc_prg="$withval"
fi
if eval $xmlrpc_cc_prg --version 2>/dev/null >/dev/null; then
CXXFLAGS="$CXXFLAGS `$xmlrpc_cc_prg --cflags server-util`"
LIBS="$LIBS `$xmlrpc_cc_prg server-util --libs`"
AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <xmlrpc-c/server.h>
]], [[ xmlrpc_registry_new(NULL); ]])],[
AC_MSG_RESULT(ok)
],[
AC_MSG_RESULT(failed)
AC_MSG_ERROR(Could not compile XMLRPC-C test.)
])
AC_DEFINE(HAVE_XMLRPC_C, 1, Support for XMLRPC-C.)
else
AC_MSG_RESULT(failed)
AC_MSG_ERROR(Could not compile XMLRPC-C test.)
fi
fi
],[
AC_MSG_RESULT(ignored)
])
])
AC_DEFUN([TORRENT_WITH_TINYXML2], [
AC_MSG_CHECKING(for tinyxml2)
AC_ARG_WITH(xmlrpc-tinyxml2,
AS_HELP_STRING([--with-xmlrpc-tinyxml2],[enable XMLRPC support via tinyxml2]),
[
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_XMLRPC_TINYXML2, 1, Support for XMLRPC via tinyxml2.)
],[
AC_MSG_RESULT(ignored)
])
])
AC_DEFUN([TORRENT_WITH_LUA], [
AC_ARG_WITH(lua,
AS_HELP_STRING([--with-lua],[enable LUA support]),
[
if test "$withval" = "no"; then
AC_MSG_RESULT(no)
else
AX_PROG_LUA
# 1. Override AX_LUA_LIBS default crash behavior
AX_LUA_LIBS([have_lua_libs=yes], [have_lua_libs=no])
# 2. Override AX_LUA_HEADERS default crash behavior
AX_LUA_HEADERS([have_lua_headers=yes], [have_lua_headers=no])
# 3. Only inject if both checks completely pass
if test "x$have_lua_libs" = "xyes" && test "x$have_lua_headers" = "xyes"; then
AC_DEFINE(HAVE_LUA, 1, Use LUA.)
AC_DEFINE(LUA_DATADIR, [PACKAGE_DATADIR "/lua"], [LUA data directory])
LIBS="$LIBS $LUA_LIB"
CXXFLAGS="$CXXFLAGS $LUA_INCLUDE"
else
# Throw fatal error ONLY if user strictly ran --with-lua=yes
if test "$withval" = "yes"; then
AC_MSG_ERROR([Lua support explicitly requested, but compatible Lua 5.3 libs/headers were not found.])
else
AC_MSG_WARN([Lua 5.3 libs or headers missing. Proceeding without Lua support.])
fi
fi
fi
],[
AC_MSG_RESULT(ignored)
])
])
AC_DEFUN([TORRENT_WITH_INOTIFY], [
AC_LANG_PUSH(C++)
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(USE_INOTIFY, 1, [sys/inotify.h exists and works correctly])
AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(failed)]
)
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>
]], [[
pthread_t t;
pthread_setname_np(t, "foo");
]])],[
AC_DEFINE(HAS_PTHREAD_SETNAME_NP_GENERIC, 1, The function to set pthread name has a pthread_t argumet.)
AC_MSG_RESULT(generic)
],[
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#include <pthread.h>
#include <sys/types.h>
]],[[
pthread_t t;
pthread_setname_np("foo");
]])],[
AC_DEFINE(HAS_PTHREAD_SETNAME_NP_DARWIN, 1, The function to set pthread name has no pthread argument.)
AC_MSG_RESULT(darwin)
],[
AC_MSG_RESULT(no)
])
])
])
AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [
AC_MSG_CHECKING([for pthread_setname_no])
AC_ARG_ENABLE(pthread-setname-np,
AS_HELP_STRING([--disable-pthread-setname-np],[disable pthread_setname_np]),
[
if test "$enableval" = "no"; then
AC_MSG_RESULT(disabled)
else
AC_MSG_RESULT(checking)
TORRENT_CHECK_PTHREAD_SETNAME_NP
fi
], [
TORRENT_CHECK_PTHREAD_SETNAME_NP
]
)
])
AC_DEFUN([TORRENT_CHECK_POSIX_SPAWN_ADDCLOSEFROM_NP], [
AC_MSG_CHECKING(for posix_spawn_file_actions_addclosefrom_np)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#define _GNU_SOURCE
#include <spawn.h>
]], [[
posix_spawn_file_actions_t actions;
posix_spawn_file_actions_addclosefrom_np(&actions, 3);
]])],[
AC_DEFINE(HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP, 1, [Define if posix_spawn_file_actions_addclosefrom_np is available.])
AC_MSG_RESULT(yes)
],[
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_WITH_SYSTEMD], [
AC_ARG_WITH(systemd,
AS_HELP_STRING([--with-systemd],[enable systemd socket activation support [[default=no]]]),
[
if test "$withval" = "yes"; then
PKG_CHECK_MODULES([SYSTEMD], [libsystemd],
[
CXXFLAGS="$CXXFLAGS $SYSTEMD_CFLAGS"
LIBS="$LIBS $SYSTEMD_LIBS"
AC_DEFINE(HAVE_SYSTEMD, 1, [Support for systemd socket activation.])
],
[AC_MSG_ERROR([libsystemd not found. Install libsystemd-dev (or the equivalent for your distribution).])])
fi
])
])
AC_DEFUN([TORRENT_WITHOUT_NCURSES], [
AC_ARG_WITH([ncurses],
[AS_HELP_STRING([--without-ncurses], [build without ncurses (daemon-only mode)])],
[with_ncurses=$withval],
[with_ncurses=yes])
if test "x$with_ncurses" = xno; then
AC_DEFINE([HAVE_NO_NCURSES], [1], [Define to 1 if building without ncurses])
CURSES_LIBS=""
CURSES_CFLAGS=""
CURSES_LIB=""
fi
AM_CONDITIONAL([NO_NCURSES], [test "x$with_ncurses" = xno])
])
-282
View File
@@ -1,282 +0,0 @@
AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [
AC_LANG_PUSH(C++)
AC_MSG_CHECKING(signedness of mincore parameter)
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
void f() { mincore((char*)0, 0, (unsigned char*)0); }
])],
[
AC_DEFINE(USE_MINCORE, 1, Use mincore)
AC_DEFINE(USE_MINCORE_UNSIGNED, 1, use unsigned char* in mincore)
AC_MSG_RESULT(unsigned)
],
[
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
void f() { mincore((char*)0, 0, (char*)0); }
])],
[
AC_DEFINE(USE_MINCORE, 1, Use mincore)
AC_DEFINE(USE_MINCORE_UNSIGNED, 0, use char* in mincore)
AC_MSG_RESULT(signed)
],
[
AC_MSG_ERROR([failed, do *not* attempt fix this with --disable-mincore unless you are running Win32 or OpenBSD.])
])
])
AC_LANG_POP(C++)
])
AC_DEFUN([TORRENT_MINCORE], [
AC_ARG_ENABLE(mincore,
AS_HELP_STRING([--disable-mincore],
[disable mincore check [[default=enable]]]),
[
if test "$enableval" = "yes"; then
TORRENT_MINCORE_SIGNEDNESS()
else
AC_MSG_CHECKING(for mincore)
AC_MSG_RESULT(disabled)
fi
],[
TORRENT_MINCORE_SIGNEDNESS()
])
])
AC_DEFUN([TORRENT_CHECK_MADVISE], [
AC_MSG_CHECKING(for madvise)
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <sys/types.h>
#include <sys/mman.h>
void f() { static char test@<:@1024@:>@; madvise((void *)test, sizeof(test), MADV_NORMAL); }
])],
[
AC_MSG_RESULT(yes)
AC_DEFINE(USE_MADVISE, 1, Use madvise)
], [
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_CHECK_POSIX_FADVISE], [
AC_MSG_CHECKING(for posix_fadvise)
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <fcntl.h>
void f() { posix_fadvise(0, 0, 0, POSIX_FADV_RANDOM); }
])],
[
AC_MSG_RESULT(yes)
AC_DEFINE(USE_POSIX_FADVISE, 1, Use posix_fadvise)
], [
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_CHECK_CACHELINE], [
AC_REQUIRE([AC_CANONICAL_HOST])
AC_MSG_CHECKING([for target cacheline size])
case "$host_os" in
linux*)
# REGION: Linux Kernel Extraction Loop
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
#include <stdlib.h>
#include <linux/cache.h>
void* vptr;
void f() {
int res = posix_memalign(&vptr, SMP_CACHE_BYTES, 42);
(void)res;
}
]])],[
# We need an explicit variable fallback condition inside AC_COMPUTE_INT
AC_COMPUTE_INT([torrent_cv_cacheline_size], [SMP_CACHE_BYTES], [#include <linux/cache.h>], [torrent_cv_cacheline_size=0])
if test "$torrent_cv_cacheline_size" -gt 0; then
AC_MSG_RESULT([linux builtin ($torrent_cv_cacheline_size bytes)])
AC_DEFINE_UNQUOTED([LT_SMP_CACHE_BYTES], [$torrent_cv_cacheline_size], [System-defined Linux L1 SMP cacheline size.])
else
# Handle scenarios where macro maps to a complex runtime expression or fails
AC_MSG_RESULT([failed to parse SMP_CACHE_BYTES value])
AC_MSG_FAILURE([Linux kernel headers found, but cacheline constant could not be computed at compile-time.])
fi
],[
# Explicitly validate the CPU type even on Linux if the header check fails
case "$host_cpu" in
x86_64*|amd64*|i386*|i486*|i586*|i686*)
AC_MSG_RESULT([linux fallback x86 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux x86 hardware.])
;;
arm*|aarch64*|powerpc*|ppc*|s390x*)
AC_MSG_RESULT([linux fallback enterprise 128 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 128, [Fallback 128-byte alignment for Linux enterprise hardware.])
;;
riscv32*|riscv64*)
AC_MSG_RESULT([linux fallback RISC-V 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux RISC-V hardware.])
;;
loongarch32*|loongarch64*)
AC_MSG_RESULT([linux fallback LoongArch 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux LoongArch hardware.])
;;
*)
AC_MSG_RESULT([unrecognized CPU arch on Linux header fallback])
AC_MSG_FAILURE([Unrecognized CPU architecture ($host_cpu) on Linux fallback path. Aborting build.])
;;
esac
])
;;
*)
# REGION: Cross-Platform Strict Hardware Mapping (macOS, FreeBSD, OpenBSD, NetBSD)
case "$host_cpu" in
x86_64*|amd64*|i386*|i486*|i586*|i686*)
# Explicit x86 desktop hardware baseline block
AC_MSG_RESULT([$host_os ($host_cpu) standard x86 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Standard 64-byte alignment for stable x86 hardware layout.])
;;
arm*|aarch64*|powerpc*|ppc*|s390x*)
# Explicit modern enterprise and Apple Silicon hardware baseline block
AC_MSG_RESULT([$host_os ($host_cpu) stable enterprise 128 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 128, [Optimized 128-byte alignment for newer high-performance chipsets.])
;;
*)
# STRICT ENFORCEMENT: Fail the build immediately if the CPU isn't explicitly known
AC_MSG_RESULT([unrecognized architecture])
AC_MSG_FAILURE([The target CPU architecture ($host_cpu) is unrecognized. Aborting configuration to prevent fatal runtime false-sharing or memory misalignment errors.])
;;
esac
;;
esac
])
AC_DEFUN([TORRENT_CHECK_CUSTOM_ENDIAN64], [
AC_CHECK_HEADERS([endian.h sys/endian.h libkern/OSByteOrder.h])
AC_C_BIGENDIAN
AH_TEMPLATE([CUSTOM_ENDIAN_HEADER], [The system header to include for endian conversions.])
AH_TEMPLATE([custom_ntohll], [Convert 64-bit integer from network to host byte order.])
AH_TEMPLATE([custom_htonll], [Convert 64-bit integer from host to network byte order.])
dnl 1. Initialize our header variable tracking
endian_header_file='<stdint.h>'
dnl 2. Test if native be64toh works natively via <endian.h> (Standard Linux/glibc)
AC_MSG_CHECKING([for native be64toh via endian.h])
AC_LINK_IFELSE([AC_LANG_SOURCE([
#include <stdint.h>
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif
int main() { uint64_t x = be64toh(1); return 0; }
])],
[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([--enable-instrumentation],
[enable instrumentation [[default=disabled]]]),
[
if test "$enableval" = "yes"; then
AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation)
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(no)
fi
],[
AC_MSG_RESULT(no)
])
])
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],
[enable ipv6 [[default=no]]]),
[
if test "$enableval" = "yes"; then
AC_DEFINE(RAK_USE_INET6, 1, enable ipv6 stuff)
fi
])
])
-63
View File
@@ -1,63 +0,0 @@
AC_DEFUN([RAK_CHECK_CFLAGS], [
AC_MSG_CHECKING([for user-defined CFLAGS])
if test "$CFLAGS" = ""; then
unset CFLAGS
AC_MSG_RESULT([undefined])
else
AC_MSG_RESULT([user-defined "$CFLAGS"])
fi
])
AC_DEFUN([RAK_CHECK_CXXFLAGS], [
AC_MSG_CHECKING([for user-defined CXXFLAGS])
if test "$CXXFLAGS" = ""; then
unset CXXFLAGS
AC_MSG_RESULT([undefined])
else
AC_MSG_RESULT([user-defined "$CXXFLAGS"])
fi
])
AC_DEFUN([RAK_ENABLE_DEBUG], [
AC_ARG_ENABLE(debug,
AS_HELP_STRING([--enable-debug],[enable debug information [[default=yes]]]),
[
if test "$enableval" = "yes"; then
CXXFLAGS="$CXXFLAGS -g -DDEBUG"
else
CXXFLAGS="$CXXFLAGS -DNDEBUG"
fi
],[
CXXFLAGS="$CXXFLAGS -g -DDEBUG"
])
])
AC_DEFUN([RAK_ENABLE_WERROR], [
AC_ARG_ENABLE(werror,
AS_HELP_STRING([--enable-werror],[enable the -Werror and -Wall flags [[default -Wall only]]]),
[
if test "$enableval" = "yes"; then
CXXFLAGS="$CXXFLAGS -Werror -Wall"
fi
],[
CXXFLAGS="$CXXFLAGS -Wall"
])
])
AC_DEFUN([RAK_ENABLE_EXTRA_DEBUG], [
AC_ARG_ENABLE(extra-debug,
AS_HELP_STRING([--enable-extra-debug],[enable extra debugging checks [[default=no]]]),
[
if test "$enableval" = "yes"; then
AC_DEFINE(USE_EXTRA_DEBUG, 1, Enable extra debugging checks.)
fi
])
])
-38
View File
@@ -1,38 +0,0 @@
AC_DEFUN([TORRENT_CHECK_OPENSSL],
[
PKG_CHECK_MODULES(OPENSSL, libcrypto,
CXXFLAGS="$CXXFLAGS $OPENSSL_CFLAGS";
LIBS="$LIBS $OPENSSL_LIBS")
AC_DEFINE(USE_OPENSSL, 1, Using OpenSSL.)
AC_DEFINE(USE_OPENSSL_SHA, 1, Using OpenSSL's SHA1 implementation.)
]
)
AC_DEFUN([TORRENT_ARG_OPENSSL],
[
AC_ARG_ENABLE(openssl,
[ --disable-openssl Don't use OpenSSL's SHA1 implementation.],
[
if test "$enableval" = "yes"; then
TORRENT_CHECK_OPENSSL
else
AC_DEFINE(USE_NSS_SHA, 1, Using Mozilla's SHA1 implementation.)
fi
],[
TORRENT_CHECK_OPENSSL
])
]
)
AC_DEFUN([TORRENT_ARG_CYRUS_RC4],
[
AC_ARG_ENABLE(cyrus-rc4,
[ --enable-cyrus-rc4=PFX Use Cyrus RC4 implementation.],
[
CXXFLAGS="$CXXFLAGS -I${enableval}/include";
LIBS="$LIBS -lrc4 -L${enableval}/lib"
AC_DEFINE(USE_CYRUS_RC4, 1, Using Cyrus RC4 implementation.)
])
]
)
-214
View File
@@ -1,214 +0,0 @@
noinst_LIBRARIES = libsub_root.a
bin_PROGRAMS = rtorrent
rtorrent_LDADD = libsub_root.a @PTHREAD_LIBS@
rtorrent_SOURCES = main.cc
libsub_root_a_SOURCES = \
core/dht_manager.cc \
core/dht_manager.h \
core/download.cc \
core/download.h \
core/download_factory.cc \
core/download_factory.h \
core/download_list.cc \
core/download_list.h \
core/http_queue.cc \
core/http_queue.h \
core/manager.cc \
core/manager.h \
core/range_map.h \
core/view.cc \
core/view.h \
core/view_manager.cc \
core/view_manager.h \
\
display/attributes.h \
display/canvas.cc \
display/canvas.h \
display/color_map.h \
display/frame.cc \
display/frame.h \
display/manager.cc \
display/manager.h \
display/utils.cc \
display/utils.h \
display/text_element.h \
display/text_element_list.cc \
display/text_element_list.h \
display/text_element_string.cc \
display/text_element_string.h \
display/text_element_value.cc \
display/text_element_value.h \
display/window.cc \
display/window.h \
display/window_download_chunks_seen.cc \
display/window_download_chunks_seen.h \
display/window_download_list.cc \
display/window_download_list.h \
display/window_download_statusbar.cc \
display/window_download_statusbar.h \
display/window_download_transfer_list.cc \
display/window_download_transfer_list.h \
display/window_file_list.cc \
display/window_file_list.h \
display/window_http_queue.cc \
display/window_http_queue.h \
display/window_input.cc \
display/window_input.h \
display/window_log.cc \
display/window_log.h \
display/window_log_complete.cc \
display/window_log_complete.h \
display/window_peer_list.cc \
display/window_peer_list.h \
display/window_statusbar.cc \
display/window_statusbar.h \
display/window_string_list.cc \
display/window_string_list.h \
display/window_text.cc \
display/window_text.h \
display/window_title.cc \
display/window_title.h \
display/window_tracker_list.cc \
display/window_tracker_list.h \
\
input/bindings.cc \
input/bindings.h \
input/input_event.cc \
input/input_event.h \
input/manager.cc \
input/manager.h \
input/path_input.cc \
input/path_input.h \
input/text_input.cc \
input/text_input.h \
\
rpc/command.h \
rpc/command.cc \
rpc/command_impl.h \
rpc/command_map.cc \
rpc/command_map.h \
rpc/command_scheduler.cc \
rpc/command_scheduler.h \
rpc/command_scheduler_item.cc \
rpc/command_scheduler_item.h \
rpc/exec_file.cc \
rpc/exec_file.h \
rpc/fixed_key.h \
rpc/ip_table_list.h \
rpc/lua.h \
rpc/lua.cc \
rpc/jsonrpc.cc \
rpc/jsonrpc.h \
rpc/rpc_manager.cc \
rpc/rpc_manager.h \
rpc/object_storage.cc \
rpc/object_storage.h \
rpc/parse.cc \
rpc/parse.h \
rpc/parse_commands.cc \
rpc/parse_commands.h \
rpc/parse_options.cc \
rpc/parse_options.h \
rpc/scgi.cc \
rpc/scgi.h \
rpc/scgi_task.cc \
rpc/scgi_task.h \
rpc/xmlrpc.h \
rpc/xmlrpc.cc \
rpc/xmlrpc_c.cc \
rpc/xmlrpc_tinyxml2.cc \
rpc/tinyxml2/tinyxml2.h \
rpc/tinyxml2/tinyxml2.cc \
rpc/nlohmann/json.h \
\
scgi/thread_scgi.cc \
scgi/thread_scgi.h \
\
session/download_storer.cc \
session/download_storer.h \
session/session_manager.cc \
session/session_manager.h \
session/thread_session.cc \
session/thread_session.h \
\
ui/download.cc \
ui/download.h \
ui/download_list.cc \
ui/download_list.h \
ui/element_base.h \
ui/element_base.cc \
ui/element_chunks_seen.cc \
ui/element_chunks_seen.h \
ui/element_download_list.cc \
ui/element_download_list.h \
ui/element_file_list.cc \
ui/element_file_list.h \
ui/element_log_complete.cc \
ui/element_log_complete.h \
ui/element_menu.cc \
ui/element_menu.h \
ui/element_peer_list.cc \
ui/element_peer_list.h \
ui/element_string_list.cc \
ui/element_string_list.h \
ui/element_text.cc \
ui/element_text.h \
ui/element_tracker_list.cc \
ui/element_tracker_list.h \
ui/element_transfer_list.cc \
ui/element_transfer_list.h \
ui/root.cc \
ui/root.h \
\
utils/base64.cc \
utils/base64.h \
utils/directory.cc \
utils/directory.h \
utils/file_status_cache.cc \
utils/file_status_cache.h \
utils/functional.h \
utils/gzip.cc \
utils/gzip.h \
utils/list_focus.h \
utils/lockfile.cc \
utils/lockfile.h \
utils/waitpid_queue.cc \
utils/waitpid_queue.h \
utils/watch_ready_queue.cc \
utils/watch_ready_queue.h \
\
command_download.cc \
command_dynamic.cc \
command_events.cc \
command_file.cc \
command_ip.cc \
command_helpers.cc \
command_helpers.h \
command_groups.cc \
command_local.cc \
command_logging.cc \
command_network.cc \
command_peer.cc \
command_throttle.cc \
command_tracker.cc \
command_scheduler.cc \
command_ui.cc \
control.cc \
control.h \
globals.cc \
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)\"
File diff suppressed because it is too large Load Diff
-487
View File
@@ -1,487 +0,0 @@
#include "config.h"
#include <algorithm>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "command_helpers.h"
#include "control.h"
#include "globals.h"
#include "rpc/parse.h"
#include "rpc/parse_options.h"
static std::vector<std::pair<const char*, int>> object_storage_flags = {
{"multi", rpc::object_storage::flag_multi_type},
{"simple", rpc::object_storage::flag_function_type},
{"value", rpc::object_storage::flag_value_type},
{"bool", rpc::object_storage::flag_bool_type},
{"string", rpc::object_storage::flag_string_type},
{"list", rpc::object_storage::flag_list_type},
{"static", rpc::object_storage::flag_static},
{"private", rpc::object_storage::flag_private},
{"const", rpc::object_storage::flag_constant},
{"rlookup", rpc::object_storage::flag_rlookup}};
static int
object_storage_parse_flag(const std::string& flag) {
for (auto [n, f] : object_storage_flags)
if (n == flag)
return f;
throw torrent::input_error("unknown flag");
}
std::string
system_method_generate_command(torrent::Object::list_const_iterator first, torrent::Object::list_const_iterator last) {
std::string command;
while (first != last) {
if (!command.empty())
command += " ;";
command += (first++)->as_string();
}
return command;
}
void
system_method_generate_command2(torrent::Object* object, torrent::Object::list_const_iterator first, torrent::Object::list_const_iterator last) {
if (first == last) {
// TODO: Use empty object.
*object = "";
return;
}
if (first->is_string()) {
std::string command;
while (first != last) {
if (!command.empty())
command += " ;";
command += (first++)->as_string();
}
*object = command;
return;
}
if (first + 1 == last) {
if (!first->is_dict_key())
throw torrent::input_error("New command of wrong type.");
*object = *first;
uint32_t flags = object->flags() & torrent::Object::mask_function;
object->unset_flags(torrent::Object::mask_function);
object->set_flags((flags >> 1) & torrent::Object::mask_function);
} else {
*object = torrent::Object::create_list();
while (first != last) {
if (!first->is_dict_key())
throw torrent::input_error("New command of wrong type.");
object->as_list().push_back(*first++);
uint32_t flags = object->as_list().back().flags() & torrent::Object::mask_function;
object->as_list().back().unset_flags(torrent::Object::mask_function);
object->as_list().back().set_flags((flags >> 1) & torrent::Object::mask_function);
}
}
}
// torrent::Object
// system_method_insert_function(const torrent::Object::list_type& args, int flags) {
// }
// This is only used by tinyxml2, xmlrpc-c intercepts the call internally
torrent::Object
system_listMethods() {
torrent::Object resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list();
result.push_back("system.multicall"); // Handled directly by the XMLRPC code
for (auto itr : rpc::commands) {
result.push_back(itr.first);
}
return resultRaw;
}
torrent::Object
system_method_insert_object(const torrent::Object::list_type& args, int flags) {
if (args.empty())
throw torrent::input_error("Invalid argument count.");
torrent::Object::list_const_iterator itrArgs = args.begin();
const std::string& raw_key = (itrArgs++)->as_string();
if (raw_key.empty() ||
control->object_storage()->find_raw_string(torrent::raw_string::from_string(raw_key)) != control->object_storage()->end() ||
rpc::commands.has(raw_key) || rpc::commands.has(raw_key + ".set"))
throw torrent::input_error("Invalid key.");
torrent::Object value;
switch (flags & rpc::object_storage::mask_type) {
case rpc::object_storage::flag_bool_type:
case rpc::object_storage::flag_value_type:
value = itrArgs != args.end() ? rpc::convert_to_value(*itrArgs) : int64_t();
break;
case rpc::object_storage::flag_string_type:
value = itrArgs != args.end() ? rpc::convert_to_string(*itrArgs) : "";
break;
case rpc::object_storage::flag_function_type:
system_method_generate_command2(&value, itrArgs, args.end());
break;
case rpc::object_storage::flag_list_type:
break;
case rpc::object_storage::flag_multi_type:
break;
default:
throw torrent::input_error("Invalid type.");
}
int cmd_flags = 0;
if (!(flags & rpc::object_storage::flag_static))
cmd_flags |= rpc::CommandMap::flag_modifiable;
if (!(flags & rpc::object_storage::flag_private))
cmd_flags |= rpc::CommandMap::flag_public_rpc;
if ((flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_list_type) {
torrent::Object valueList = torrent::Object::create_list();
torrent::Object::list_type& valueListType = valueList.as_list();
if ((itrArgs)->is_list())
valueListType = (itrArgs)->as_list();
control->object_storage()->insert_str(raw_key, valueList, flags);
} else {
control->object_storage()->insert_str(raw_key, value, flags);
}
if ((flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_function_type ||
(flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_multi_type) {
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call<rpc::target_type>>::type>(
raw_key,
std::bind(&rpc::object_storage::call_function_str, control->object_storage(), raw_key, std::placeholders::_1, std::placeholders::_2),
&rpc::command_base_call<rpc::target_type>,
cmd_flags,
NULL,
NULL);
} else {
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call<rpc::target_type>>::type>(
raw_key,
std::bind(&rpc::object_storage::get_str, control->object_storage(), raw_key),
&rpc::command_base_call<rpc::target_type>,
cmd_flags,
NULL,
NULL);
}
// Not the right argument.
// if (flags & rpc::object_storage::flag_rlookup) {
// rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_string<rpc::target_type> >::type>
// (create_new_key<9>(raw_key, ".rlookup"),
// std::bind(&rpc::object_storage::rlookup_obj_list, control->object_storage(), raw_key),
// &rpc::command_base_call_string<rpc::target_type>,
// cmd_flags, NULL, NULL);
// }
// TODO: Next... Make test class for this.
// // Ehm... no proper handling if these throw.
if (!(flags & rpc::object_storage::flag_constant)) {
switch (flags & rpc::object_storage::mask_type) {
case rpc::object_storage::flag_bool_type:
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_value<rpc::target_type>>::type>(
raw_key + ".set",
std::bind(&rpc::object_storage::set_str_bool, control->object_storage(), raw_key, std::placeholders::_2),
&rpc::command_base_call_value<rpc::target_type>,
cmd_flags,
NULL,
NULL);
break;
case rpc::object_storage::flag_value_type:
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_value<rpc::target_type>>::type>(
raw_key + ".set",
std::bind(&rpc::object_storage::set_str_value, control->object_storage(), raw_key, std::placeholders::_2),
&rpc::command_base_call_value<rpc::target_type>,
cmd_flags,
NULL,
NULL);
break;
case rpc::object_storage::flag_string_type:
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_string<rpc::target_type>>::type>(
raw_key + ".set",
std::bind(&rpc::object_storage::set_str_string, control->object_storage(), raw_key, std::placeholders::_2),
&rpc::command_base_call_string<rpc::target_type>,
cmd_flags,
NULL,
NULL);
break;
case rpc::object_storage::flag_list_type:
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_list<rpc::target_type>>::type>(
raw_key + ".set",
std::bind(&rpc::object_storage::set_str_list, control->object_storage(), raw_key, std::placeholders::_2),
&rpc::command_base_call_list<rpc::target_type>,
cmd_flags,
NULL,
NULL);
break;
case rpc::object_storage::flag_function_type:
case rpc::object_storage::flag_multi_type:
default:
break;
}
}
return torrent::Object();
}
// method.insert <generic> {name, "simple|private|const", ...}
// method.insert <generic> {name, "multi|private|const"}
// method.insert <generic> {name, "value|private|const"}
// method.insert <generic> {name, "value|private|const", value}
// method.insert <generic> {name, "bool|private|const"}
// method.insert <generic> {name, "bool|private|const", bool}
// method.insert <generic> {name, "string|private|const"}
// method.insert <generic> {name, "string|private|const", string}
//
// Add a new user-defined method called 'name' and any number of
// lines.
//
// TODO: Make a static version of this that doesn't need to be called
// as a command, and which takes advantage of static const char
// strings, etc.
torrent::Object
system_method_insert(const torrent::Object::list_type& args) {
if (args.empty() || ++args.begin() == args.end())
throw torrent::input_error("Invalid argument count.");
torrent::Object::list_const_iterator itrArgs = args.begin();
const std::string& raw_key = (itrArgs++)->as_string();
if (raw_key.empty() || rpc::commands.has(raw_key))
throw torrent::input_error("Invalid key.");
int new_flags = rpc::parse_option_flags(itrArgs->as_string(), std::bind(&object_storage_parse_flag, std::placeholders::_1));
torrent::Object::list_type new_args;
new_args.push_back(raw_key);
if ((new_flags & rpc::object_storage::flag_function_type) ||
(new_flags & rpc::object_storage::flag_multi_type)) {
new_args.push_back(system_method_generate_command(++itrArgs, args.end()));
} else if ((new_flags & rpc::object_storage::flag_value_type) ||
(new_flags & rpc::object_storage::flag_bool_type) ||
(new_flags & rpc::object_storage::flag_string_type) ||
(new_flags & rpc::object_storage::flag_list_type)) {
if (++itrArgs != args.end())
new_args.insert(new_args.end(), itrArgs, args.end());
} else {
throw torrent::input_error("No object type specified.");
}
return system_method_insert_object(new_args, new_flags);
}
// method.erase <> {name}
//
// Erase a modifiable method called 'name. Trying to remove methods
// that aren't modifiable, e.g. defined by rtorrent or set to
// read-only by the user, will result in an error.
torrent::Object
system_method_erase(const torrent::Object::string_type& args) {
rpc::CommandMap::iterator itr = rpc::commands.find(args.c_str());
if (itr == rpc::commands.end())
return torrent::Object();
if (!rpc::commands.is_modifiable(itr))
throw torrent::input_error("Command not modifiable.");
rpc::commands.erase(itr);
return torrent::Object();
}
torrent::Object
system_method_redirect(const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Invalid argument count.");
std::string new_key = torrent::object_create_string(args.front());
std::string dest_key = torrent::object_create_string(args.back());
rpc::commands.create_redirect(new_key, dest_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_modifiable);
return torrent::Object();
}
torrent::Object
system_method_set_function(const torrent::Object::list_type& args) {
if (args.empty())
throw torrent::input_error("Invalid argument count.");
rpc::object_storage::iterator itr =
control->object_storage()->find_raw_string(torrent::raw_string::from_string(args.front().as_string()));
if (itr == control->object_storage()->end() || itr->second.flags & rpc::object_storage::flag_constant)
throw torrent::input_error("Command is not modifiable.");
return control->object_storage()->set_str_function(args.front().as_string(),
system_method_generate_command(++args.begin(), args.end()));
}
torrent::Object
system_method_has_key(const torrent::Object::list_type& args) {
if (args.empty() || ++args.begin() == args.end())
throw torrent::input_error("Invalid argument count.");
torrent::Object::list_const_iterator itrArgs = args.begin();
const std::string& key = (itrArgs++)->as_string();
const std::string& cmd_key = (itrArgs++)->as_string();
return control->object_storage()->has_str_multi_key(key, cmd_key);
}
torrent::Object
system_method_set_key(const torrent::Object::list_type& args) {
if (args.empty() || ++args.begin() == args.end())
throw torrent::input_error("Invalid argument count.");
torrent::Object::list_const_iterator itrArgs = args.begin();
const std::string& key = (itrArgs++)->as_string();
const std::string& cmd_key = (itrArgs++)->as_string();
if (itrArgs == args.end()) {
control->object_storage()->erase_str_multi_key(key, cmd_key);
return torrent::Object();
}
if (itrArgs->is_dict_key() || itrArgs->is_list())
control->object_storage()->set_str_multi_key_obj(key.c_str(), cmd_key, *itrArgs);
else
control->object_storage()->set_str_multi_key(key, cmd_key, system_method_generate_command(itrArgs, args.end()));
return torrent::Object();
}
torrent::Object
system_method_list_keys(const torrent::Object::string_type& args) {
torrent::Object rawResult = torrent::Object::create_list();
torrent::Object::list_type& result = rawResult.as_list();
for (const auto& itr : control->object_storage()->get_str(args).as_map())
result.push_back(itr.first);
return rawResult;
}
torrent::Object
cmd_catch(rpc::target_type target, const torrent::Object& args) {
try {
return rpc::call_object(args, target);
} catch (torrent::input_error& e) {
lt_log_print(torrent::LOG_WARN, "Caught exception: '%s'.", e.what());
return torrent::Object();
}
}
#define CMD2_METHOD_INSERT(key, flags) \
CMD2_ANY_LIST(key, std::bind(&system_method_insert_object, std::placeholders::_2, flags));
void
initialize_command_dynamic() {
// clang-format off
#ifdef HAVE_XMLRPC_TINYXML2
CMD2_ANY ("system.listMethods", std::bind(&system_listMethods)); // only used by tinyxml2
#endif
// Keep these for future use when we deprecate more commands.
CMD2_VAR_BOOL ("method.use_deprecated", false);
CMD2_VAR_VALUE ("method.use_intermediate", 3);
CMD2_ANY_LIST ("method.insert", std::bind(&system_method_insert, std::placeholders::_2));
CMD2_ANY_LIST ("method.insert.value", std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_value_type));
CMD2_ANY_LIST ("method.insert.bool", std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_bool_type));
CMD2_ANY_LIST ("method.insert.string", std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_string_type));
CMD2_ANY_LIST ("method.insert.list", std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_list_type));
CMD2_METHOD_INSERT("method.insert.simple", rpc::object_storage::flag_function_type);
CMD2_METHOD_INSERT("method.insert.c_simple", rpc::object_storage::flag_constant | rpc::object_storage::flag_function_type);
CMD2_METHOD_INSERT("method.insert.s_c_simple", rpc::object_storage::flag_static |
rpc::object_storage::flag_constant |rpc::object_storage::flag_function_type);
CMD2_ANY_STRING ("method.erase", std::bind(&system_method_erase, std::placeholders::_2));
CMD2_ANY_LIST ("method.redirect", std::bind(&system_method_redirect, std::placeholders::_2));
CMD2_ANY_STRING ("method.get", std::bind(&rpc::object_storage::get_str, control->object_storage(),
std::placeholders::_2));
CMD2_ANY_LIST ("method.set", std::bind(&system_method_set_function, std::placeholders::_2));
CMD2_ANY_STRING ("method.const", std::bind(&rpc::object_storage::has_flag_str, control->object_storage(),
std::placeholders::_2, rpc::object_storage::flag_constant));
CMD2_ANY_STRING_V("method.const.enable", std::bind(&rpc::object_storage::enable_flag_str, control->object_storage(),
std::placeholders::_2, rpc::object_storage::flag_constant));
CMD2_ANY_LIST ("method.has_key", std::bind(&system_method_has_key, std::placeholders::_2));
CMD2_ANY_LIST ("method.set_key", std::bind(&system_method_set_key, std::placeholders::_2));
CMD2_ANY_STRING ("method.list_keys", std::bind(&system_method_list_keys, std::placeholders::_2));
CMD2_ANY_STRING ("method.rlookup", std::bind(&rpc::object_storage::rlookup_obj_list, control->object_storage(), std::placeholders::_2));
CMD2_ANY_STRING_V("method.rlookup.clear", std::bind(&rpc::object_storage::rlookup_clear, control->object_storage(), std::placeholders::_2));
CMD2_ANY ("catch", std::bind(&cmd_catch, std::placeholders::_1, std::placeholders::_2));
CMD2_ANY_STRING ("enum.log_group", [](auto, const auto& str) { return torrent::option_find_string_str(torrent::OPTION_LOG_GROUP, str); });
CMD2_ANY ("strings.choke_heuristics", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS); });
CMD2_ANY ("strings.choke_heuristics.upload", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_UPLOAD); });
CMD2_ANY ("strings.choke_heuristics.download", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_DOWNLOAD); });
CMD2_ANY ("strings.connection_type", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CONNECTION_TYPE); });
CMD2_ANY ("strings.encryption", [](auto, auto) { return torrent::Object::create_list(); });
CMD2_ANY ("strings.encryption.handshake", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_HANDSHAKE); });
CMD2_ANY ("strings.encryption.stream", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_STREAM); });
CMD2_ANY ("strings.ip_filter", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_FILTER); });
CMD2_ANY ("strings.ip_tos", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_TOS); });
CMD2_ANY ("strings.log_group", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_LOG_GROUP); });
CMD2_ANY ("strings.tracker_event", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_TRACKER_EVENT); });
CMD2_ANY ("strings.tracker_mode", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_TRACKER_MODE); });
// clang-format on
#ifdef HAVE_XMLRPC_TINYXML2
rpc::rpc.mark_safe("system.listMethods");
#endif
rpc::rpc.mark_safe("method.use_deprecated");
rpc::rpc.mark_safe("method.const");
rpc::rpc.mark_safe("method.has_key");
rpc::rpc.mark_safe("method.list_keys");
rpc::rpc.mark_safe("method.get");
rpc::rpc.mark_safe("method.rlookup");
rpc::rpc.mark_safe("catch");
rpc::rpc.mark_safe("enum.log_group");
rpc::rpc.mark_safe("strings.choke_heuristics");
rpc::rpc.mark_safe("strings.choke_heuristics.upload");
rpc::rpc.mark_safe("strings.choke_heuristics.download");
rpc::rpc.mark_safe("strings.connection_type");
rpc::rpc.mark_safe("strings.encryption");
rpc::rpc.mark_safe("strings.ip_filter");
rpc::rpc.mark_safe("strings.ip_tos");
rpc::rpc.mark_safe("strings.log_group");
rpc::rpc.mark_safe("strings.tracker_event");
rpc::rpc.mark_safe("strings.tracker_mode");
}
-380
View File
@@ -1,380 +0,0 @@
#include "config.h"
#include <functional>
#include <cstdio>
#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"
#include "command_helpers.h"
#include "core/download.h"
#include "core/download_list.h"
#include "core/manager.h"
#include "core/view_manager.h"
#include "rpc/command_scheduler.h"
#include "rpc/parse.h"
#include "rpc/parse_commands.h"
#include "utils/watch_ready_queue.h"
torrent::Object
apply_on_ratio(const torrent::Object& rawArgs) {
auto& group_name = rawArgs.as_string();
auto view_itr = control->view_manager()->find(rpc::commands.call("group." + group_name + ".view", rpc::make_target()).as_string());
if (view_itr == control->view_manager()->end())
throw torrent::input_error("Could not find view.");
// first argument: minimum ratio to reach
// second argument: minimum upload amount to reach [optional]
// third argument: maximum ratio to reach [optional]
int64_t min_ratio = rpc::commands.call("group." + group_name + ".ratio.min", rpc::make_target()).as_value();
int64_t max_ratio = rpc::commands.call("group." + group_name + ".ratio.max", rpc::make_target()).as_value();
int64_t min_upload = rpc::commands.call("group." + group_name + ".ratio.upload", rpc::make_target()).as_value();
std::vector<core::Download*> downloads;
for (auto itr = (*view_itr)->begin_visible(), last = (*view_itr)->end_visible(); itr != last; itr++) {
if (!(*itr)->is_seeding() || rpc::call_command_value("d.ignore_commands", rpc::make_target(*itr)) != 0)
continue;
int64_t total_done = (*itr)->download()->bytes_done();
int64_t total_upload = (*itr)->info()->up_rate()->total();
if (!(total_upload >= min_upload && total_upload * 100 >= total_done * min_ratio) &&
!(max_ratio > 0 && total_upload * 100 > total_done * max_ratio))
continue;
downloads.push_back(*itr);
}
auto ratio_command = "group." + group_name + ".ratio.command";
for (const auto& download : downloads)
rpc::commands.call_catch(ratio_command, rpc::make_target(download), torrent::Object(), "Ratio reached, but command failed: ");
return torrent::Object();
}
torrent::Object
apply_start_tied() {
for (const auto& download : *control->core()->download_list()) {
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 1)
continue;
torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
if (!tied_to_file.empty() && fs.update(expand_path(tied_to_file)))
rpc::parse_command_single(rpc::make_target(download), "d.try_start=");
}
return torrent::Object();
}
torrent::Object
apply_stop_untied() {
for (const auto& download : *control->core()->download_list()) {
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 0)
continue;
torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
if (!tied_to_file.empty() && !fs.update(expand_path(tied_to_file)))
rpc::parse_command_single(rpc::make_target(download), "d.try_stop=");
}
return torrent::Object();
}
torrent::Object
apply_close_untied() {
for (const auto& download : *control->core()->download_list()) {
torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
if (rpc::call_command_value("d.ignore_commands", rpc::make_target(download)) == 0 && !tied_to_file.empty() && !fs.update(expand_path(tied_to_file)))
rpc::parse_command_single(rpc::make_target(download), "d.try_close=");
}
return torrent::Object();
}
torrent::Object
apply_remove_untied() {
for (auto itr = control->core()->download_list()->begin(); itr != control->core()->download_list()->end(); ) {
torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(*itr));
if (!tied_to_file.empty() && !fs.update(expand_path(tied_to_file))) {
// Need to clear tied_to_file so it doesn't try to delete it.
rpc::call_command("d.tied_to_file.set", std::string(), rpc::make_target(*itr));
itr = control->core()->download_list()->erase(itr);
} else {
++itr;
}
}
return torrent::Object();
}
torrent::Object
apply_schedule(const torrent::Object::list_type& args) {
if (args.size() != 4)
throw torrent::input_error("Wrong number of arguments.");
torrent::Object::list_const_iterator itr = args.begin();
auto& arg1 = (itr++)->as_string();
auto& arg2 = (itr++)->as_string();
auto& arg3 = (itr++)->as_string();
control->command_scheduler()->parse(arg1, arg2, arg3, *itr);
return torrent::Object();
}
torrent::Object
apply_load(const torrent::Object::list_type& args, int flags) {
torrent::Object::list_const_iterator argsItr = args.begin();
if (argsItr == args.end())
throw torrent::input_error("Too few arguments.");
auto& filename = argsItr->as_string();
core::Manager::command_list_type commands;
while (++argsItr != args.end())
commands.push_back(argsItr->as_string());
control->core()->try_create_download_expand(filename, flags, commands);
return torrent::Object();
}
void apply_import(const std::string& path) { if (!rpc::parse_command_file(path)) throw torrent::input_error("Could not open option file: " + path); }
void apply_try_import(const std::string& path) { if (!rpc::parse_command_file(path)) control->core()->push_log_std("Could not read resource file: " + path); }
torrent::Object
apply_close_low_diskspace(int64_t arg, uint32_t skip_priority) {
bool closed = false;
torrent::FileList::cache_list cache;
for (auto download : *control->core()->download_list()) {
if (!download->is_downloading())
continue;
if (download->priority() >= skip_priority)
continue;
if (download->file_list()->free_diskspace(cache) >= (uint64_t)arg)
continue;
control->core()->download_list()->close(download);
download->set_hash_failed(true);
download->set_message(std::string("Low diskspace."));
closed = true;
}
if (closed)
lt_log_print(torrent::LOG_TORRENT_ERROR, "Closed torrents due to low diskspace.");
return torrent::Object();
}
torrent::Object
apply_download_list(const torrent::Object::list_type& args) {
torrent::Object::list_const_iterator argsItr = args.begin();
core::ViewManager* viewManager = control->view_manager();
core::ViewManager::iterator view_itr;
if (argsItr != args.end() && !argsItr->as_string().empty())
view_itr = viewManager->find((argsItr++)->as_string());
else
view_itr = viewManager->find("default");
if (view_itr == viewManager->end())
throw torrent::input_error("Could not find view.");
torrent::Object result = torrent::Object::create_list();
torrent::Object::list_type& resultList = result.as_list();
for (core::View::const_iterator itr = (*view_itr)->begin_visible(), last = (*view_itr)->end_visible(); itr != last; itr++) {
const torrent::HashString* hashString = &(*itr)->info()->hash();
resultList.push_back(torrent::utils::transform_to_hex_str(*hashString));
}
return result;
}
torrent::Object
d_multicall(const torrent::Object::list_type& args) {
if (args.empty())
throw torrent::input_error("Too few arguments.");
core::ViewManager* viewManager = control->view_manager();
core::ViewManager::iterator view_itr;
if (!args.front().as_string().empty())
view_itr = viewManager->find(args.front().as_string());
else
view_itr = viewManager->find("default");
if (view_itr == viewManager->end())
throw torrent::input_error("Could not find view.");
// 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());
torrent::Object resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list();
for (auto download : dlist) {
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++) {
auto& cmd = cItr->as_string();
row.push_back(rpc::parse_command(rpc::make_target(download), cmd.c_str(), cmd.c_str() + cmd.size()).first);
}
}
return resultRaw;
}
torrent::Object
d_multicall_filtered(const torrent::Object::list_type& args) {
if (args.size() < 2)
throw torrent::input_error("d.multicall.filtered requires at least 2 arguments.");
auto arg = args.begin();
// Find the given view
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
core::View::base_type dlist;
(*view_itr)->filter_by(*++arg, dlist);
// Generate result by iterating over all items
auto resultRaw = torrent::Object::create_list();
auto& result = resultRaw.as_list();
++arg; // skip to first command
for (const auto& item : dlist) {
// Add empty row to result
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
// Call the provided commands and assemble their results
for (torrent::Object::list_const_iterator command = arg; command != args.end(); command++) {
auto& cmdstr = command->as_string();
row.push_back(rpc::parse_command(rpc::make_target(item), cmdstr.c_str(), cmdstr.c_str() + cmdstr.size()).first);
}
}
return resultRaw;
}
static void
call_watch_command(const std::string& command, const std::string& path) {
rpc::commands.call_catch(command.c_str(), rpc::make_target(), path);
}
torrent::Object
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)));
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", [](auto, auto& args) { return apply_on_ratio(args); });
CMD2_ANY ("start_tied", [](auto, auto) { return apply_start_tied(); });
CMD2_ANY ("stop_untied", [](auto, auto) { return apply_stop_untied(); });
CMD2_ANY ("close_untied", [](auto, auto) { return apply_close_untied(); });
CMD2_ANY ("remove_untied", [](auto, auto) { return apply_remove_untied(); });
CMD2_ANY_LIST ("schedule", [](auto, auto& args) { return apply_schedule(args); });
CMD2_ANY_STRING_V("schedule.remove", [](auto, auto& str) { return control->command_scheduler()->erase_str(str); });
CMD2_ANY_STRING_V("import", [](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", [](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", [](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", [](auto, auto& args) { return apply_download_list(args); });
// TODO: Deprecate d.multicall2. (6/2026)
CMD2_ANY_LIST ("d.multicall", [](auto, auto& args) { return d_multicall(args); });
CMD2_ANY_LIST ("d.multicall.filtered", [](auto, auto& args) { return d_multicall_filtered(args); });
CMD2_ANY_LIST ("directory.watch.added", [](auto, auto& args) { return directory_watch_added(args); });
CMD2_ANY_LIST ("directory.watch.ready", [](auto, auto& args) { return directory_watch_ready(args); });
rpc::rpc.mark_safe("start_tied");
rpc::rpc.mark_safe("stop_untied");
rpc::rpc.mark_safe("close_untied");
rpc::rpc.mark_safe("remove_untied");
rpc::rpc.mark_safe("close_low_diskspace");
rpc::rpc.mark_safe("close_low_diskspace.normal");
rpc::rpc.mark_safe("download_list");
rpc::rpc.mark_safe("d.multicall");
rpc::rpc.mark_safe("d.multicall.filtered");
}
-161
View File
@@ -1,161 +0,0 @@
#include "config.h"
#include <torrent/data/file.h>
#include <torrent/data/file_list.h>
#include <torrent/data/file_list_iterator.h>
#include "core/manager.h"
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
void
apply_f_set_priority(torrent::File* file, uint32_t value) {
if (value > torrent::PRIORITY_HIGH)
throw torrent::input_error("Invalid value.");
file->set_priority(static_cast<torrent::priority_enum>(value));
}
// TODO: Redundant.
torrent::Object
apply_f_path(torrent::File* file) {
if (file->path()->empty())
return std::string();
torrent::Object result_raw(file->path()->begin()->str());
auto& result = result_raw.as_string();
for (torrent::Path::const_iterator itr = ++file->path()->begin(), last = file->path()->end(); itr != last; itr++)
result += '/' + itr->str();
return result_raw;
}
torrent::Object
apply_f_path_components(torrent::File* file, int output_type) {
auto result_raw = torrent::Object::create_list();
auto& result = result_raw.as_list();
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 result_raw;
}
torrent::Object
apply_f_path_depth(torrent::File* file) {
return (int64_t)file->path()->size();
}
torrent::Object
apply_fi_filename_last(torrent::FileListIterator* itr) {
if (itr->file()->path()->empty())
return "EMPTY";
if (itr->depth() >= itr->file()->path()->size())
return "ERROR";
return itr->file()->path()->at(itr->depth()).str();
}
void
initialize_command_file() {
CMD2_FILE("f.is_created", std::bind(&torrent::File::is_created, std::placeholders::_1));
CMD2_FILE("f.is_open", std::bind(&torrent::File::is_open, std::placeholders::_1));
CMD2_FILE("f.is_create_queued", std::bind(&torrent::File::is_create_queued, std::placeholders::_1));
CMD2_FILE("f.is_resize_queued", std::bind(&torrent::File::is_resize_queued, std::placeholders::_1));
CMD2_FILE_VALUE_V("f.set_create_queued", std::bind(&torrent::File::set_flags, std::placeholders::_1, torrent::File::flag_create_queued));
CMD2_FILE_VALUE_V("f.set_resize_queued", std::bind(&torrent::File::set_flags, std::placeholders::_1, torrent::File::flag_resize_queued));
CMD2_FILE_VALUE_V("f.unset_create_queued", std::bind(&torrent::File::unset_flags, std::placeholders::_1, torrent::File::flag_create_queued));
CMD2_FILE_VALUE_V("f.unset_resize_queued", std::bind(&torrent::File::unset_flags, std::placeholders::_1, torrent::File::flag_resize_queued));
CMD2_FILE ("f.prioritize_first", std::bind(&torrent::File::has_flags, std::placeholders::_1, torrent::File::flag_prioritize_first));
CMD2_FILE_V("f.prioritize_first.enable", std::bind(&torrent::File::set_flags, std::placeholders::_1, torrent::File::flag_prioritize_first));
CMD2_FILE_V("f.prioritize_first.disable", std::bind(&torrent::File::unset_flags, std::placeholders::_1, torrent::File::flag_prioritize_first));
CMD2_FILE ("f.prioritize_last", std::bind(&torrent::File::has_flags, std::placeholders::_1, torrent::File::flag_prioritize_last));
CMD2_FILE_V("f.prioritize_last.enable", std::bind(&torrent::File::set_flags, std::placeholders::_1, torrent::File::flag_prioritize_last));
CMD2_FILE_V("f.prioritize_last.disable", std::bind(&torrent::File::unset_flags, std::placeholders::_1, torrent::File::flag_prioritize_last));
CMD2_FILE("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", [](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", [](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", [](auto* file, auto) { return apply_f_path(file); });
CMD2_FILE("f.path_components", [](auto* file, auto) { return apply_f_path_components(file, 0); });
CMD2_FILE("f.path_components.hex", [](auto* file, auto) { return apply_f_path_components(file, 1); });
CMD2_FILE("f.path_components.base64", [](auto* file, auto) { return apply_f_path_components(file, 2); });
CMD2_FILE("f.path_components.base64_as_binary", [](auto* file, auto) { return apply_f_path_components(file, 3); });
CMD2_FILE("f.path_components.as_binary", [](auto* file, auto) { return apply_f_path_components(file, 4); });
CMD2_FILE("f.path_components.or_base64", [](auto* file, auto) { return apply_f_path_components(file, 5); });
CMD2_FILE("f.path_components.or_as_binary", [](auto* file, auto) { return apply_f_path_components(file, 6); });
CMD2_FILE("f.path_depth", [](auto* file, auto) { return apply_f_path_depth(file); });
CMD2_FILE("f.frozen_path", [](auto* file, auto) { return file->frozen_path().str(); });
CMD2_FILE("f.frozen_path.hex", [](auto* file, auto) { return file->frozen_path().object_hex(); });
CMD2_FILE("f.frozen_path.base64", [](auto* file, auto) { return file->frozen_path().object_base64(); });
CMD2_FILE("f.frozen_path.as_binary", [](auto* file, auto) { return file->frozen_path().object_as_binary(); });
CMD2_FILE("f.frozen_path.or_base64", [](auto* file, auto) { return file->frozen_path().object_utf8_or_base64(); });
CMD2_FILE("f.frozen_path.or_as_binary", [](auto* file, auto) { return file->frozen_path().object_utf8_or_as_binary(); });
CMD2_FILE("f.match_depth_prev", std::bind(&torrent::File::match_depth_prev, std::placeholders::_1));
CMD2_FILE("f.match_depth_next", std::bind(&torrent::File::match_depth_next, std::placeholders::_1));
CMD2_FILE("f.last_touched", std::bind(&torrent::File::last_touched, std::placeholders::_1));
CMD2_FILEITR("fi.filename_last", std::bind(&apply_fi_filename_last, std::placeholders::_1));
CMD2_FILEITR("fi.is_file", std::bind(&torrent::FileListIterator::is_file, std::placeholders::_1));
rpc::rpc.mark_safe("f.path");
rpc::rpc.mark_safe("f.path_components");
rpc::rpc.mark_safe("f.path_depth");
rpc::rpc.mark_safe("f.frozen_path");
rpc::rpc.mark_safe("f.frozen_path.hex");
rpc::rpc.mark_safe("f.frozen_path.base64");
rpc::rpc.mark_safe("f.frozen_path.base64_as_binary");
rpc::rpc.mark_safe("f.frozen_path.as_binary");
rpc::rpc.mark_safe("f.frozen_path.or_base64");
rpc::rpc.mark_safe("f.frozen_path.or_as_binary");
rpc::rpc.mark_safe("f.offset");
rpc::rpc.mark_safe("f.size_bytes");
rpc::rpc.mark_safe("f.size_chunks");
rpc::rpc.mark_safe("f.completed_chunks");
rpc::rpc.mark_safe("f.range_first");
rpc::rpc.mark_safe("f.range_second");
rpc::rpc.mark_safe("f.priority");
rpc::rpc.mark_safe("f.priority.set");
rpc::rpc.mark_safe("f.is_created");
rpc::rpc.mark_safe("f.is_open");
rpc::rpc.mark_safe("f.is_create_queued");
rpc::rpc.mark_safe("f.is_resize_queued");
rpc::rpc.mark_safe("f.prioritize_first");
rpc::rpc.mark_safe("f.prioritize_first.enable");
rpc::rpc.mark_safe("f.prioritize_first.disable");
rpc::rpc.mark_safe("f.prioritize_last");
rpc::rpc.mark_safe("f.prioritize_last.enable");
rpc::rpc.mark_safe("f.prioritize_last.disable");
rpc::rpc.mark_safe("f.last_touched");
rpc::rpc.mark_safe("f.match_depth_prev");
rpc::rpc.mark_safe("f.match_depth_next");
rpc::rpc.mark_safe("fi.filename_last");
rpc::rpc.mark_safe("fi.is_file");
}
-405
View File
@@ -1,405 +0,0 @@
#include "config.h"
#include <torrent/download/resource_manager.h>
#include <torrent/download/choke_group.h>
#include <torrent/download/choke_queue.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "ui/root.h"
#include "rpc/parse.h"
#include "rpc/parse_commands.h"
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
// For cg_d_group.
#include "core/download.h"
#define LT_LOG_SUBSYSTEM(log_fmt, ...) \
lt_log_print_subsystem(torrent::LOG_TORRENT_INFO, "choke_queue", log_fmt, __VA_ARGS__);
// A hack to allow testing of the new choke_group API without the
// working parts present.
#define USE_CHOKE_GROUP 0
#if USE_CHOKE_GROUP
int64_t
cg_get_index(const torrent::Object& raw_args) {
const torrent::Object& arg = (raw_args.is_list() && !raw_args.as_list().empty()) ? raw_args.as_list().front() : raw_args;
int64_t index = 0;
if (arg.is_string()) {
if (!rpc::parse_whole_value_nothrow(arg.as_string().c_str(), &index))
return torrent::resource_manager()->group_index_of(arg.as_string());
} else {
index = arg.as_value();
}
if (index < 0)
index = (int64_t)torrent::resource_manager()->group_size() + index;
return std::min<uint64_t>(index, torrent::resource_manager()->group_size());
}
torrent::choke_group*
cg_get_group(const torrent::Object& raw_args) {
return torrent::resource_manager()->group_at(cg_get_index(raw_args));
}
int64_t
cg_d_group(core::Download* download) {
return torrent::resource_manager()->entry_at(download->main()).group();
}
const std::string&
cg_d_group_name(core::Download* download) {
return torrent::resource_manager()->group_at(torrent::resource_manager()->entry_at(download->main()).group())->name();
}
void
cg_d_group_set(core::Download* download, const torrent::Object& arg) {
torrent::resource_manager()->set_group(torrent::resource_manager()->find_throw(download->main()), cg_get_index(arg));
}
torrent::Object
apply_cg_list() {
torrent::Object::list_type result;
for (torrent::ResourceManager::group_iterator
itr = torrent::resource_manager()->group_begin(),
last = torrent::resource_manager()->group_end(); itr != last; itr++)
result.push_back((*itr)->name());
return torrent::Object::from_list(result);
}
torrent::Object
apply_cg_insert(const std::string& arg) {
int64_t dummy;
if (rpc::parse_whole_value_nothrow(arg.c_str(), &dummy))
throw torrent::input_error("Cannot use a value string as choke group name.");
torrent::resource_manager()->push_group(arg);
return torrent::Object();
}
torrent::Object
apply_cg_all_update_balance(bool is_up) {
LT_LOG_SUBSYSTEM("apply update balance: resource_manager is_up:%i", (int)is_up);
for (torrent::ResourceManager::group_iterator
itr = torrent::resource_manager()->group_begin(),
last = torrent::resource_manager()->group_end(); itr != last; itr++) {
if (is_up)
itr->up_queue()->balance();
else
itr->down_queue()->balance();
}
return torrent::Object();
}
//
// The hacked version:
//
#else
std::vector<torrent::choke_group*> cg_list_hack;
int64_t
cg_get_index(const torrent::Object& raw_args) {
const torrent::Object& arg = (raw_args.is_list() && !raw_args.as_list().empty()) ? raw_args.as_list().front() : raw_args;
int64_t index = 0;
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(); });
if (itr == cg_list_hack.end())
throw torrent::input_error("Choke group not found.");
return std::distance(cg_list_hack.begin(), itr);
}
} else {
index = arg.as_value();
}
if (index < 0)
index = (int64_t)cg_list_hack.size() + index;
if ((size_t)index >= cg_list_hack.size())
throw torrent::input_error("Choke group not found.");
return index;
}
torrent::choke_group*
cg_get_group(const torrent::Object& raw_args) {
int64_t index = cg_get_index(raw_args);
if ((size_t)index >= cg_list_hack.size())
throw torrent::input_error("Choke group not found.");
return cg_list_hack.at(index);
}
int64_t cg_d_group(core::Download* download) { return download->group(); }
const std::string& cg_d_group_name(core::Download* download) {
return cg_list_hack.at(download->group())->name();
}
void cg_d_group_set(core::Download* download, const torrent::Object& arg) { download->set_group(cg_get_index(arg)); }
torrent::Object
apply_cg_list() {
torrent::Object::list_type result;
for (auto itr : cg_list_hack)
result.push_back(itr->name());
return torrent::Object::from_list(result);
}
int
cg_get_can_unchoke(torrent::choke_queue* cq) {
return cq->max_unchoked_signed() - (int)cq->size_unchoked();
}
torrent::Object
apply_cg_insert(const std::string& arg) {
int64_t dummy;
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(); }))
throw torrent::input_error("Duplicate name for choke group.");
cg_list_hack.push_back(new torrent::choke_group());
cg_list_hack.back()->set_name(arg);
cg_list_hack.back()->up_queue()->set_heuristics(torrent::HEURISTICS_UPLOAD_LEECH);
cg_list_hack.back()->down_queue()->set_heuristics(torrent::HEURISTICS_DOWNLOAD_LEECH);
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(); });
if (itr == cg_list_hack.end())
throw torrent::input_error("Choke group not found.");
return std::distance(cg_list_hack.begin(), itr);
}
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) {
if (is_up)
itr->up_queue()->balance();
else
itr->down_queue()->balance();
}
return torrent::Object();
}
//
// End of choke group hack.
//
#endif
torrent::Object
apply_cg_max_set(const torrent::Object::list_type& args, bool is_up) {
if (args.size() != 2)
throw torrent::input_error("Incorrect number of arguments.");
int64_t second_arg = 0;
rpc::parse_whole_value(args.back().as_string().c_str(), &second_arg);
if (is_up)
cg_get_group(args.front())->up_queue()->set_max_unchoked(second_arg);
else
cg_get_group(args.front())->down_queue()->set_max_unchoked(second_arg);
return torrent::Object();
}
torrent::Object
apply_cg_heuristics_set(const torrent::Object::list_type& args, bool is_up) {
if (args.size() != 2)
throw torrent::input_error("Incorrect number of arguments.");
int t = torrent::option_find_string(is_up ? torrent::OPTION_CHOKE_HEURISTICS_UPLOAD : torrent::OPTION_CHOKE_HEURISTICS_DOWNLOAD,
args.back().as_string().c_str());
if (is_up)
cg_get_group(args.front())->up_queue()->set_heuristics((torrent::choke_queue::heuristics_enum)t);
else
cg_get_group(args.front())->down_queue()->set_heuristics((torrent::choke_queue::heuristics_enum)t);
return torrent::Object();
}
torrent::Object
apply_cg_tracker_mode_set(const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Incorrect number of arguments.");
int t = torrent::option_find_string(torrent::OPTION_TRACKER_MODE, args.back().as_string().c_str());
cg_get_group(args.front())->set_tracker_mode((torrent::choke_group::tracker_mode_enum)t);
return torrent::Object();
}
#define CG_GROUP_AT() std::bind(&cg_get_group, std::placeholders::_2)
#define CHOKE_GROUP(direction) std::bind(direction, CG_GROUP_AT())
/*
<cg_index> -> '0'..'(choke_group.size)'
-> '-1'..'-(choke_group.size)'
-> '<group_name>'
(choke_group.list) -> List of group names.
(choke_group.size) -> Number of groups.
(choke_group.insert,"group_name")
Adds a new group with default settings, use index '-1' to accessing it
immediately afterwards.
(choke_group.index_of,"group_name") -> <group_index>
Throws if the group name was not found.
(choke_group.general.size,<cg_index>) -> <size>
Number of torrents in this group.
(choke_group.tracker.mode,<cg_index>) -> "tracker_mode"
(choke_group.tracker.mode.set,<cg_index>,"tracker_mode")
Decide on how aggressive a tracker should be, see
'strings.tracker_mode' for list of available options
(choke_group.up.rate,<cg_index>) -> <bytes/second>
(choke_group.down.rate,<cg_index>) -> <bytes/second>
Upload / download rate for the aggregate of all torrents in this
particular group.
(choke_group.up.max,<cg_index>) -> <max_upload_slots>
(choke_group.up.max.unlimited,<cg_index>) -> <max_upload_slots>
(choke_group.up.max.set,<cg_index>, <max_upload_slots>)
(choke_group.down.max,<cg_index>) -> <max_download_slots>
(choke_group.down.max.unlimited,<cg_index>) -> <max_download_slots>
(choke_group.down.max.set,<cg_index>, <max_download_slots)
Number of unchoked upload / download peers regulated on a group basis.
(choke_group.up.total,<cg_index>) -> <number of queued and unchoked interested peers>
(choke_group.up.queued,<cg_index>) -> <number of queued interested peers>
(choke_group.up.unchoked,<cg_index>) -> <number of unchoked uploads>
(choke_group.down.total,<cg_index>) -> <number of queued and unchoked interested peers>
(choke_group.down.queued,<cg_index>) -> <number of queued interested peers>
(choke_group.down.unchoked,<cg_index>) -> <number of unchoked uploads>
(choke_group.up.heuristics,<cg_index>) -> "heuristics"
(choke_group.up.heuristics.set,<cg_index>,"heuristics")
(choke_group.down.heuristics,<cg_index>) -> "heuristics"
(choke_group.down.heuristics.set,<cg_index>,"heuristics")
Heuristics are used for deciding what peers to choke and unchoke, see
'strings.choke_heuristics{,_download,_upload}' for a list of available
options.
(d.group) -> <choke_group_index>
(d.group.name) -> "choke_group_name"
(d.group.set,<cg_index>)
*/
void
initialize_command_groups() {
CMD_ANY ("choke_group.list", std::bind(&apply_cg_list));
CMD_ANY_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2));
#if USE_CHOKE_GROUP
CMD_ANY ("choke_group.size", std::bind(&torrent::ResourceManager::group_size, torrent::resource_manager()));
CMD_ANY_STRING ("choke_group.index_of", std::bind(&torrent::ResourceManager::group_index_of, torrent::resource_manager(), std::placeholders::_2));
#else
apply_cg_insert("default");
CMD_ANY ("choke_group.size", std::bind(&std::vector<torrent::choke_group*>::size, cg_list_hack));
CMD_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2));
#endif
// Commands specific for a group. Supports as the first argument the
// name, the index or a negative index.
CMD_ANY ("choke_group.general.size", std::bind(&torrent::choke_group::size, CG_GROUP_AT()));
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); });
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));
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()));
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));
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); });
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));
CMD_ANY ("choke_group.down.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD_ANY ("choke_group.down.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD_ANY ("choke_group.down.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD_ANY ("choke_group.down.heuristics", [](auto, auto arg) { return torrent::option_to_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, cg_get_group(arg)->down_queue()->heuristics()); });
CMD_ANY_LIST ("choke_group.down.heuristics.set", [](auto, auto arg) { return apply_cg_heuristics_set(arg, false); });
rpc::rpc.mark_safe("choke_group.list");
rpc::rpc.mark_safe("choke_group.size");
rpc::rpc.mark_safe("choke_group.index_of");
rpc::rpc.mark_safe("choke_group.general.size");
rpc::rpc.mark_safe("choke_group.tracker.mode");
rpc::rpc.mark_safe("choke_group.up.rate");
rpc::rpc.mark_safe("choke_group.down.rate");
rpc::rpc.mark_safe("choke_group.up.max");
rpc::rpc.mark_safe("choke_group.up.max.unlimited");
rpc::rpc.mark_safe("choke_group.up.total");
rpc::rpc.mark_safe("choke_group.up.queued");
rpc::rpc.mark_safe("choke_group.up.unchoked");
rpc::rpc.mark_safe("choke_group.up.heuristics");
rpc::rpc.mark_safe("choke_group.down.max");
rpc::rpc.mark_safe("choke_group.down.max.unlimited");
rpc::rpc.mark_safe("choke_group.down.total");
rpc::rpc.mark_safe("choke_group.down.queued");
rpc::rpc.mark_safe("choke_group.down.unchoked");
rpc::rpc.mark_safe("choke_group.down.heuristics");
}
-40
View File
@@ -1,40 +0,0 @@
#include "config.h"
#include <torrent/exceptions.h>
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
void initialize_command_dynamic();
void initialize_command_download();
void initialize_command_events();
void initialize_command_file();
void initialize_command_ip();
void initialize_command_peer();
void initialize_command_local();
void initialize_command_logging();
void initialize_command_network();
void initialize_command_groups();
void initialize_command_throttle();
void initialize_command_tracker();
void initialize_command_scheduler();
void initialize_command_ui();
void
initialize_commands() {
initialize_command_dynamic();
initialize_command_events();
initialize_command_network();
initialize_command_groups();
initialize_command_local();
initialize_command_logging();
initialize_command_ui();
initialize_command_download();
initialize_command_file();
initialize_command_ip();
initialize_command_peer();
initialize_command_throttle();
initialize_command_tracker();
initialize_command_scheduler();
}
-170
View File
@@ -1,170 +0,0 @@
#ifndef RTORRENT_UTILS_COMMAND_HELPERS_H
#define RTORRENT_UTILS_COMMAND_HELPERS_H
#include "rpc/command.h"
#include "rpc/parse_commands.h"
#include "rpc/object_storage.h"
void initialize_commands();
//
// Aliases with CMD_* for the below
//
#define CMD_ANY(key, slot) CMD2_ANY(key, slot)
#define CMD_ANY_P(key, slot) CMD2_ANY_P(key, slot)
#define CMD_REDIRECT(key, slot) CMD2_REDIRECT(key, slot)
#define CMD_REDIRECT_NO_EXPORT(key, slot) CMD2_REDIRECT_NO_EXPORT(key, slot)
#define CMD_ANY_STRING(key, slot) CMD2_ANY_STRING(key, slot)
#define CMD_ANY_STRING_V(key, slot) CMD2_ANY_STRING_V(key, slot)
#define CMD_ANY_LIST(key, slot) CMD2_ANY_LIST(key, slot)
#define CMD_VAR_VALUE(key, value) CMD2_VAR_VALUE(key, value)
#define CMD_VAR_BOOL(key, value) CMD2_VAR_BOOL(key, value)
#define CMD_VAR_STRING(key, value) CMD2_VAR_STRING(key, value)
#define CMD_VAR_C_STRING(key, value) CMD2_VAR_C_STRING(key, value)
#define CMD_VAR_LIST(key) CMD2_VAR_LIST(key)
#define CMD_ANY_V(key, slot) CMD2_ANY_V(key, slot)
#define CMD_ANY_VALUE_V(key, slot) CMD2_ANY_VALUE_V(key, slot)
//
// New std::function based command_base helper functions:
//
#define CMD2_A_FUNCTION(key, function, slot, parm, doc) \
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::function>::type>(key, slot, &rpc::function, \
rpc::CommandMap::flag_dont_delete | rpc::CommandMap::flag_public_rpc, NULL, NULL);
#define CMD2_A_FUNCTION_PRIVATE(key, function, slot, parm, doc) \
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::function>::type>(key, slot, &rpc::function, \
rpc::CommandMap::flag_dont_delete, NULL, NULL);
#define CMD2_ANY(key, slot) CMD2_A_FUNCTION(key, command_base_call<rpc::target_type>, slot, "i:", "")
#define CMD2_ANY_P(key, slot) CMD2_A_FUNCTION_PRIVATE(key, command_base_call<rpc::target_type>, slot, "i:", "")
#define CMD2_ANY_VOID(key, slot) CMD2_A_FUNCTION(key, command_base_call<rpc::target_type>, object_convert_void(slot), "i:", "")
#define CMD2_ANY_V(key, slot) CMD2_A_FUNCTION(key, command_base_call_list<rpc::target_type>, object_convert_void(slot), "i:", "")
#define CMD2_ANY_L(key, slot) CMD2_A_FUNCTION(key, command_base_call_list<rpc::target_type>, slot, "A:", "")
#define CMD2_ANY_VALUE(key, slot) CMD2_A_FUNCTION(key, command_base_call_value<rpc::target_type>, slot, "i:i", "")
#define CMD2_ANY_VALUE_V(key, slot) CMD2_A_FUNCTION(key, command_base_call_value<rpc::target_type>, object_convert_void(slot), "i:i", "")
#define CMD2_ANY_VALUE_KB(key, slot) CMD2_A_FUNCTION(key, command_base_call_value_kb<rpc::target_type>, object_convert_void(slot), "i:i", "")
#define CMD2_ANY_STRING(key, slot) CMD2_A_FUNCTION(key, command_base_call_string<rpc::target_type>, slot, "i:s", "")
#define CMD2_ANY_STRING_V(key, slot) CMD2_A_FUNCTION(key, command_base_call_string<rpc::target_type>, object_convert_void(slot), "i:s", "")
#define CMD2_ANY_LIST(key, slot) CMD2_A_FUNCTION(key, command_base_call_list<rpc::target_type>, slot, "i:", "")
#define CMD2_DL(key, slot) CMD2_A_FUNCTION(key, command_base_call<core::Download*>, slot, "i:", "")
#define CMD2_DL_V(key, slot) CMD2_A_FUNCTION(key, command_base_call<core::Download*>, object_convert_void(slot), "i:", "")
#define CMD2_DL_VALUE(key, slot) CMD2_A_FUNCTION(key, command_base_call_value<core::Download*>, slot, "i:", "")
#define CMD2_DL_VALUE_V(key, slot) CMD2_A_FUNCTION(key, command_base_call_value<core::Download*>, object_convert_void(slot), "i:", "")
#define CMD2_DL_STRING(key, slot) CMD2_A_FUNCTION(key, command_base_call_string<core::Download*>, slot, "i:", "")
#define CMD2_DL_STRING_V(key, slot) CMD2_A_FUNCTION(key, command_base_call_string<core::Download*>, object_convert_void(slot), "i:", "")
#define CMD2_DL_LIST(key, slot) CMD2_A_FUNCTION(key, command_base_call_list<core::Download*>, slot, "i:", "")
#define CMD2_DL_VALUE_P(key, slot) CMD2_A_FUNCTION_PRIVATE(key, command_base_call_value<core::Download*>, slot, "i:", "")
#define CMD2_DL_STRING_P(key, slot) CMD2_A_FUNCTION_PRIVATE(key, command_base_call_string<core::Download*>, slot, "i:", "")
#define CMD2_FILE(key, slot) CMD2_A_FUNCTION(key, command_base_call<torrent::File*>, slot, "i:", "")
#define CMD2_FILE_V(key, slot) CMD2_A_FUNCTION(key, command_base_call<torrent::File*>, object_convert_void(slot), "i:", "")
#define CMD2_FILE_VALUE_V(key, slot) CMD2_A_FUNCTION(key, command_base_call_value<torrent::File*>, object_convert_void(slot), "i:i", "")
#define CMD2_FILEITR(key, slot) CMD2_A_FUNCTION(key, command_base_call<torrent::FileListIterator*>, slot, "i:", "")
#define CMD2_PEER(key, slot) CMD2_A_FUNCTION(key, command_base_call<torrent::Peer*>, slot, "i:", "")
#define CMD2_PEER_V(key, slot) CMD2_A_FUNCTION(key, command_base_call<torrent::Peer*>, object_convert_void(slot), "i:", "")
#define CMD2_PEER_VALUE_V(key, slot) CMD2_A_FUNCTION(key, command_base_call_value<torrent::Peer*>, object_convert_void(slot), "i:i", "")
#define CMD2_TRACKER(key, slot) CMD2_A_FUNCTION(key, command_base_call<torrent::tracker::Tracker*>, slot, "i:", "")
#define CMD2_TRACKER_V(key, slot) CMD2_A_FUNCTION(key, command_base_call<torrent::tracker::Tracker*>, object_convert_void(slot), "i:", "")
#define CMD2_TRACKER_VALUE_V(key, slot) CMD2_A_FUNCTION(key, command_base_call_value<torrent::tracker::Tracker*>, object_convert_void(slot), "i:i", "")
#define CMD2_VAR_BOOL(key, value) \
control->object_storage()->insert_c_str(key, int64_t(value), rpc::object_storage::flag_bool_type); \
CMD2_ANY(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key))); \
CMD2_ANY_VALUE(key ".set", std::bind(&rpc::object_storage::set_bool, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2));
#define CMD2_VAR_VALUE(key, value) \
control->object_storage()->insert_c_str(key, int64_t(value), rpc::object_storage::flag_value_type); \
CMD2_ANY(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key))); \
CMD2_ANY_VALUE(key ".set", std::bind(&rpc::object_storage::set_value, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2));
#define CMD2_VAR_STRING(key, value) \
control->object_storage()->insert_c_str(key, value, rpc::object_storage::flag_string_type); \
CMD2_ANY(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key))); \
CMD2_ANY_STRING(key ".set", std::bind(&rpc::object_storage::set_string, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2));
#define CMD2_VAR_C_STRING(key, value) \
control->object_storage()->insert_c_str(key, value, rpc::object_storage::flag_string_type); \
CMD2_ANY(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key)));
#define CMD2_VAR_LIST(key) \
control->object_storage()->insert_c_str(key, torrent::Object::create_list(), rpc::object_storage::flag_list_type); \
CMD2_ANY(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key))); \
CMD2_ANY_LIST(key ".set", std::bind(&rpc::object_storage::set_list, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2)); \
CMD2_ANY_VOID(key ".push_back", std::bind(&rpc::object_storage::list_push_back, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2));
#define CMD2_FUNC_SINGLE(key, cmds) \
CMD2_ANY(key, std::bind(&rpc::command_function_call_object, torrent::Object(torrent::raw_string::from_c_str(cmds)), \
std::placeholders::_1, std::placeholders::_2));
#define CMD2_REDIRECT(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_dont_delete);
#define CMD2_REDIRECT_NO_EXPORT(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_dont_delete);
#define CMD2_REDIRECT_MUTABLE(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc);
#define CMD2_REDIRECT_STR(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc);
#define CMD2_REDIRECT_STR_NO_EXPORT(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, 0);
#define CMD2_REDIRECT_FILE(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_file_target | rpc::CommandMap::flag_dont_delete);
#define CMD2_REDIRECT_TRACKER(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_tracker_target | rpc::CommandMap::flag_dont_delete);
//
// Conversion of return types:
//
template <typename Functor, typename Result>
struct object_convert_type;
template <typename Functor>
struct object_convert_type<Functor, void> {
template <typename Signature> struct result {
typedef torrent::Object type;
};
object_convert_type(Functor s) : m_slot(s) {}
torrent::Object operator () () { m_slot(); return torrent::Object(); }
template <typename Arg1>
torrent::Object operator () (Arg1& arg1) { m_slot(arg1); return torrent::Object(); }
template <typename Arg1, typename Arg2>
torrent::Object operator () (const Arg1& arg1) { m_slot(arg1); return torrent::Object(); }
template <typename Arg1, typename Arg2>
torrent::Object operator () (Arg1& arg1, Arg2& arg2) { m_slot(arg1, arg2); return torrent::Object(); }
template <typename Arg1, typename Arg2>
torrent::Object operator () (const Arg1& arg1, const Arg2& arg2) { m_slot(arg1, arg2); return torrent::Object(); }
Functor m_slot;
};
template <typename T>
object_convert_type<T, void>
object_convert_void(T f) { return f; }
#endif
-378
View File
@@ -1,378 +0,0 @@
#include "config.h"
#include <fstream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <torrent/peer/peer_list.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "globals.h"
#include "command_helpers.h"
bool ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address_end);
torrent::Object
apply_ip_tables_insert_table(const std::string& args) {
if (ip_tables.find(args) != ip_tables.end())
throw torrent::input_error("IP table already exists.");
ip_tables.insert(args);
return torrent::Object();
}
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())
throw torrent::input_error("IP table does not exist.");
uint32_t size = itr->table.sizeof_data();
return size;
}
torrent::Object
apply_ip_tables_get(const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Incorrect number of arguments.");
torrent::Object::list_const_iterator args_itr = args.begin();
const std::string& name = (args_itr++)->as_string();
const std::string& address = (args_itr++)->as_string();
uint32_t address_start;
uint32_t address_end;
rpc::ip_table_list::iterator table_itr = ip_tables.find(name);
if (table_itr == ip_tables.end())
throw torrent::input_error("Could not find ip table.");
if (!ipv4_range_parse(address.c_str(), &address_start, &address_end))
throw torrent::input_error("Invalid address format.");
if(!table_itr->table.defined(address_start, address_end))
throw torrent::input_error("No value defined for specified IP(s).");
return table_itr->table.at(address_start, address_end);
}
torrent::Object
apply_ip_tables_add_address(const torrent::Object::list_type& args) {
if (args.size() != 3)
throw torrent::input_error("Incorrect number of arguments.");
torrent::Object::list_const_iterator args_itr = args.begin();
const std::string& name = (args_itr++)->as_string();
const std::string& address = (args_itr++)->as_string();
const std::string& value_str = (args_itr++)->as_string();
int value;
if (value_str == "block")
value = 1;
else
throw torrent::input_error("Invalid value.");
rpc::ip_table_list::iterator table_itr = ip_tables.find(name);
if (table_itr == ip_tables.end())
throw torrent::input_error("Could not find ip table.");
uint32_t address_start;
uint32_t address_end;
if (ipv4_range_parse(address.c_str(), &address_start, &address_end))
table_itr->table.insert(address_start, address_end, value);
else
throw torrent::input_error("Invalid address format.");
return torrent::Object();
}
//
// IPv4 filter functions:
//
///////////////////////////////////////////////////////////
// IPV4_RANGE_PARSE parses string into an ip range
//
// should be compatible with lines in p2p files
// everything in address before colon is ignored
//
// ip range can be single ip in which case start=end
// ip range can be cidr notation a.b.c.d/e
// ip range can be explicit range like in p2p line a.b.c.d-w.x.y.z
//
// returns false if line does not contain valid ip or ip range
// address_start and address_end will contain start and end ip
//
// addresses parsed are returned in host byte order
// to get network byte order call htonl(address)
///////////////////////////////////////////////////////////
bool
ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address_end) {
// same length as buffer used to do reads so no worries about overflow
char address_copy[4096];
bool valid = false;
char address_start_str[20];
int address_start_index=0;
struct sockaddr_in sa_start;
*address_start=0;
*address_end=0;
// get rid of everything after '#' comments
// 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_copy[address_start_index] = address[address_start_index];
address_start_index++;
}
address_copy[address_start_index] = '\0';
address_start_index=0;
// skip everything up to and including last ':' character and whitespace
const char* addr = strrchr(address_copy, ':');
addr = (addr == NULL) ? address_copy : addr + 1;
while(addr[0] == ' ' || addr[0] == '\t')
addr++;
while(((addr[0] >= '0' && addr[0] <= '9') || addr[0] == '.') && address_start_index < 19) {
address_start_str[address_start_index] = addr[0];
address_start_index++;
addr++;
}
address_start_str[address_start_index] = '\0';
if(strchr(addr, '-') != NULL) {
// explicit range
char address_end_str[20];
int address_end_index=0;
struct sockaddr_in sa_end;
while(addr[0] == '-' || addr[0] == ' ' || addr[0] == '\t')
addr++;
while(((addr[0] >= '0' && addr[0] <= '9') || addr[0] == '.') && address_end_index < 19) {
address_end_str[address_end_index] = addr[0];
address_end_index++;
addr++;
}
address_end_str[address_end_index] = '\0';
if(inet_pton(AF_INET, address_start_str, &(sa_start.sin_addr)) != 0 && inet_pton(AF_INET, address_end_str, &(sa_end.sin_addr)) != 0) {
*address_start = ntohl(sa_start.sin_addr.s_addr);
*address_end = ntohl(sa_end.sin_addr.s_addr);
if(*address_start <= *address_end)
valid=true;
}
} else if(strchr(addr, '/') != NULL) {
// cidr range
char mask_bits_str[20];
int mask_bits_index=0;
uint32_t mask_bits;
while(addr[0] == '/' || addr[0] == ' ' || addr[0] == '\t')
addr++;
while( (addr[0] >= '0' && addr[0] <= '9') && mask_bits_index < 19) {
mask_bits_str[mask_bits_index] = addr[0];
mask_bits_index++;
addr++;
}
mask_bits_str[mask_bits_index] = '\0';
if(inet_pton(AF_INET, address_start_str, &(sa_start.sin_addr)) != 0 && sscanf(mask_bits_str, "%u", &mask_bits) != 0) {
if(mask_bits <=32) {
uint32_t ip=ntohl(sa_start.sin_addr.s_addr);
uint32_t mask=0;
uint32_t end_mask=0;
if (mask_bits == 0) {
mask = 0;
end_mask = ~(uint32_t)0;
} else {
mask = (~mask) << (32-mask_bits);
end_mask = (~end_mask) >> mask_bits;
}
*address_start = ip & mask;
*address_end = (ip & mask) | end_mask;
valid=true;
}
}
} else {
// single ip
if(inet_pton(AF_INET, address_start_str, &(sa_start.sin_addr)) != 0) {
*address_start = ntohl(sa_start.sin_addr.s_addr);
*address_end = *address_start;
valid=true;
}
}
return valid;
}
///////////////////////////////////////////////////////////
// IPV4_FILTER_PARSE
//
// should now be compatible with lines in p2p files
//
// addresses in table MUST be in host byte order
// ntohl is called after parsing ip address(es)
///////////////////////////////////////////////////////////
void
ipv4_filter_parse(const char* address, int value) {
uint32_t address_start;
uint32_t address_end;
if (ipv4_range_parse(address, &address_start, &address_end) ) {
torrent::PeerList::ipv4_filter()->insert(address_start, address_end, value);
char start_str[INET_ADDRSTRLEN];
char end_str[INET_ADDRSTRLEN];
uint32_t net_start = htonl(address_start);
uint32_t net_end = htonl(address_end);
inet_ntop(AF_INET, &net_start, start_str, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &net_end, end_str, INET_ADDRSTRLEN);
lt_log_print(torrent::LOG_CONNECTION_FILTER, "Adding ip filter for %s-%s.", start_str, end_str);
}
}
torrent::Object
apply_ipv4_filter_size_data() {
return torrent::PeerList::ipv4_filter()->sizeof_data();
}
torrent::Object
apply_ipv4_filter_get(const std::string& args) {
uint32_t address_start;
uint32_t address_end;
if (!ipv4_range_parse(args.c_str(), &address_start, &address_end))
throw torrent::input_error("Invalid address format.");
if(!torrent::PeerList::ipv4_filter()->defined(address_start, address_end))
throw torrent::input_error("No value defined for specified IP(s).");
return torrent::PeerList::ipv4_filter()->at(address_start, address_end);
}
torrent::Object
apply_ipv4_filter_add_address(const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Incorrect number of arguments.");
ipv4_filter_parse(args.front().as_string().c_str(),
torrent::option_find_string(torrent::OPTION_IP_FILTER, args.back().as_string().c_str()));
return torrent::Object();
}
torrent::Object
apply_ipv4_filter_load(const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Incorrect number of arguments.");
std::string filename = args.front().as_string();
std::string value_name = args.back().as_string();
int value = torrent::option_find_string(torrent::OPTION_IP_FILTER, value_name.c_str());
std::fstream file(expand_path(filename).c_str(), std::ios::in);
if (!file.is_open())
throw torrent::input_error("Could not open ip filter file: " + filename);
unsigned int lineNumber = 0;
char buffer[4096];
try {
while (file.good() && !file.getline(buffer, 4096).fail()) {
if (file.gcount() == 0)
throw torrent::internal_error("parse_command_file(...) file.gcount() == 0.");
lineNumber++;
if (buffer[0] == '\0' || buffer[0] == '#')
continue;
ipv4_filter_parse(buffer, value);
}
} catch (torrent::input_error& e) {
snprintf(buffer, 2048, "Error in ip filter file: %s:%u: %s", filename.c_str(), lineNumber, e.what());
throw torrent::input_error(buffer);
}
lt_log_print(torrent::LOG_CONNECTION_FILTER, "loaded %u %s address blocks (%u kb in-memory) from '%s'",
lineNumber,
value_name.c_str(),
torrent::PeerList::ipv4_filter()->sizeof_data() / 1024,
filename.c_str());
return torrent::Object();
}
torrent::Object
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();
while(iter != range_map.end()) {
char buffer[64];
uint32_t address_start = iter->first;
uint32_t address_end = (iter->second).first;
int value = (iter->second).second;
char start_str[INET_ADDRSTRLEN];
char end_str[INET_ADDRSTRLEN];
uint32_t net_start = htonl(address_start);
uint32_t net_end = htonl(address_end);
inet_ntop(AF_INET, &net_start, start_str, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &net_end, end_str, INET_ADDRSTRLEN);
snprintf(buffer, 64, "%s-%s %s", start_str, end_str, torrent::option_to_c_str_or_throw(torrent::OPTION_IP_FILTER, value));
result.push_back((std::string)buffer);
iter++;
}
return raw_result;
}
void
initialize_command_ip() {
CMD2_ANY_STRING ("ip_tables.insert_table", std::bind(&apply_ip_tables_insert_table, std::placeholders::_2));
CMD2_ANY_STRING ("ip_tables.size_data", std::bind(&apply_ip_tables_size_data, std::placeholders::_2));
CMD2_ANY_LIST ("ip_tables.get", std::bind(&apply_ip_tables_get, std::placeholders::_2));
CMD2_ANY_LIST ("ip_tables.add_address", std::bind(&apply_ip_tables_add_address, std::placeholders::_2));
CMD2_ANY ("ipv4_filter.size_data", std::bind(&apply_ipv4_filter_size_data));
CMD2_ANY_STRING ("ipv4_filter.get", std::bind(&apply_ipv4_filter_get, std::placeholders::_2));
CMD2_ANY_LIST ("ipv4_filter.add_address", std::bind(&apply_ipv4_filter_add_address, std::placeholders::_2));
CMD2_ANY_LIST ("ipv4_filter.load", std::bind(&apply_ipv4_filter_load, std::placeholders::_2));
CMD2_ANY_LIST ("ipv4_filter.dump", std::bind(&apply_ipv4_filter_dump));
}
-386
View File
@@ -1,386 +0,0 @@
#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/data/file_manager.h>
#include <torrent/data/chunk_utils.h>
#include <torrent/runtime/runtime.h>
#include <torrent/runtime/memory_manager.h>
#include <torrent/runtime/socket_manager.h>
#include <torrent/utils/chrono.h>
#include <torrent/utils/option_strings.h>
#include "core/download.h"
#include "core/download_list.h"
#include "core/manager.h"
#include "rpc/parse_commands.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;
torrent::Object
apply_pieces_stats_total_size() {
uint64_t size = 0;
for (const auto& d : *control->core()->download_list())
if (d->is_active())
size += d->file_list()->size_bytes();
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)
throw torrent::input_error("Invalid number of arguments.");
return itr++;
}
inline const std::string&
check_name(const std::string& str) {
auto itr = std::find_if(str.begin(), str.end(), [](char c) {
return !std::isalnum(c, std::locale::classic()) && c != '_';
});
if (itr != str.end())
throw torrent::input_error("Invalid characters found in name.");
return str;
}
torrent::Object
group_insert(const torrent::Object::list_type& args) {
torrent::Object::list_const_iterator itr = args.begin();
torrent::Object::list_const_iterator last = args.end();
const std::string& name = check_name(post_increment(itr, last)->as_string());
const std::string& view = check_name(post_increment(itr, last)->as_string());
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.enable", "simple",
"schedule=group." + name + ".ratio,5,60,on_ratio=" + name));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.disable", "simple",
"schedule.remove=group." + name + ".ratio"));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.command", "simple",
"d.try_close= ;d.ignore_commands.set=1"));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".view", "string", view));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.min", "value", (int64_t)200));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.max", "value", (int64_t)300));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.upload", "value", (int64_t)20 << 20));
rpc::rpc.mark_safe("group." + name + ".view");
rpc::rpc.mark_safe("group." + name + ".view.set");
rpc::rpc.mark_safe("group." + name + ".ratio.min");
rpc::rpc.mark_safe("group." + name + ".ratio.min.set");
rpc::rpc.mark_safe("group." + name + ".ratio.max");
rpc::rpc.mark_safe("group." + name + ".ratio.max.set");
rpc::rpc.mark_safe("group." + name + ".ratio.upload");
rpc::rpc.mark_safe("group." + name + ".ratio.upload.set");
return name;
}
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::FileManager* fileManager = torrent::file_manager();
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_VAR_VALUE ("system.file.allocate", 0);
CMD_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
CMD_VAR_VALUE ("system.file.split_size", -1);
CMD_VAR_STRING ("system.file.split_suffix", ".part");
CMD_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size,
(utils::FileStatusCache::base_type*)control->core()->file_status_cache()));
CMD_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache()));
CMD_VAR_BOOL ("file.prioritize_toc", 0);
CMD_VAR_LIST ("file.prioritize_toc.first");
CMD_VAR_LIST ("file.prioritize_toc.last");
CMD_ANY ("system.files.advise_random", std::bind(&FM_t::advise_random, fileManager));
CMD_ANY_VALUE_V ("system.files.advise_random.set", std::bind(&FM_t::set_advise_random, fileManager, std::placeholders::_2));
CMD_ANY ("system.files.advise_random.hashing", std::bind(&FM_t::advise_random_hashing, fileManager));
CMD_ANY_VALUE_V ("system.files.advise_random.hashing.set", std::bind(&FM_t::set_advise_random_hashing, fileManager, std::placeholders::_2));
CMD_ANY ("system.files.session.fdatasync", [](auto, auto) { return session_thread::manager()->use_fsyncdisk(); });
CMD_ANY_VALUE_V ("system.files.session.fdatasync.set", [](auto, auto& value) { return session_thread::manager()->set_use_fsyncdisk(value); });
CMD_ANY ("system.files.opened_counter", std::bind(&FM_t::files_opened_counter, fileManager));
CMD_ANY ("system.files.closed_counter", std::bind(&FM_t::files_closed_counter, fileManager));
CMD_ANY ("system.files.failed_counter", std::bind(&FM_t::files_failed_counter, fileManager));
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(value); });
CMD_ANY_V ("system.sockets.adjust_alloc", [](auto, auto) { torrent::runtime::socket_manager()->adjust_allocation(); });
CMD_ANY ("system.sockets.reserved_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->reserved_allocation(); });
CMD_ANY ("system.sockets.available_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->available_allocation(); });
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 + ".min_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_min_allocation(category); });
CMD_ANY (category_name + ".max_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_allocation(category); });
CMD_ANY_VALUE_V(category_name + ".min_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_min_allocation(category, value); });
CMD_ANY_VALUE_V(category_name + ".max_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_max_allocation(category, value); });
}
CMD_ANY ("pieces.sync.always_safe", [](auto, auto) { return torrent::runtime::memory_manager()->safe_sync(); });
CMD_ANY_VALUE_V ("pieces.sync.always_safe.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_safe_sync(value); });
CMD_ANY ("pieces.sync.safe_free_diskspace", [](auto, auto) { return torrent::runtime::memory_manager()->sync_safe_free_diskspace(); });
CMD_ANY ("pieces.sync.timeout", [](auto, auto) { return torrent::runtime::memory_manager()->timeout_sync().count(); });
CMD_ANY_VALUE_V ("pieces.sync.timeout.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_timeout_sync(value); });
// CMD_ANY ("pieces.sync.timeout_safe", [](auto, auto) { return torrent::runtime::memory_manager()->timeout_safe_sync(); });
// CMD_ANY_VALUE_V ("pieces.sync.timeout_safe.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_timeout_safe_sync(value); });
CMD_ANY ("pieces.sync.timeout_safe", [](auto, auto) { return 0; });
CMD_ANY_VALUE_V ("pieces.sync.timeout_safe.set", [](auto, auto) { });
CMD_ANY ("pieces.sync.queue_size", [](auto, auto) { return torrent::runtime::memory_manager()->sync_queue_block_count(); });
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); });
CMD_ANY ("pieces.stats_preloaded", [](auto, auto) { return torrent::runtime::memory_manager()->stats_preloaded(); });
CMD_ANY ("pieces.stats_not_preloaded", [](auto, auto) { return torrent::runtime::memory_manager()->stats_not_preloaded(); });
CMD_ANY ("pieces.stats.total_size", std::bind(&apply_pieces_stats_total_size));
CMD_ANY ("pieces.memory.current", [](auto, auto) { return torrent::runtime::memory_manager()->memory_usage(); });
CMD_ANY ("pieces.memory.sync_queue", [](auto, auto) { return torrent::runtime::memory_manager()->sync_queue_memory_usage(); });
CMD_ANY ("pieces.memory.block_count", [](auto, auto) { return torrent::runtime::memory_manager()->memory_block_count(); });
CMD_ANY ("pieces.memory.max", [](auto, auto) { return torrent::runtime::memory_manager()->max_memory_usage(); });
CMD_ANY_VALUE_V ("pieces.memory.max.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_max_memory_usage(value); });
CMD_ANY ("pieces.hash.queue_size", std::bind(&torrent::main_thread::hash_queue_size));
CMD_VAR_BOOL ("pieces.hash.on_completion", true);
CMD_VAR_STRING ("directory.default", "./");
CMD_VAR_STRING ("session.name", "");
CMD_ANY ("session.path", [](auto, auto) { return session_thread::manager()->path(); });
CMD_ANY_STRING_V("session.path.set", [](auto, auto& str) { return session_thread::manager()->set_path(str); });
CMD_ANY ("session.use_lock", [](auto, auto) { return session_thread::manager()->use_lock(); });
CMD_ANY_VALUE_V ("session.use_lock.set", [](auto, auto& value) { return session_thread::manager()->set_use_lock(value); });
CMD_VAR_BOOL ("session.on_completion", true);
CMD_ANY_V ("session.save", [dList](auto, auto) { return dList->session_save(); });
CMD_ANY ("magnet.path", [](auto, auto) { return control->core()->magnet_path(); });
CMD_ANY_STRING_V("magnet.path.set", [](auto, auto& str) { return control->core()->set_magnet_path(str); });
#ifdef HAVE_LUA
rpc::LuaEngine* lua_engine = control->lua_engine();
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 CMD_EXECUTE(key, flags) \
CMD_ANY(key, std::bind(&rpc::ExecFile::execute_object, &rpc::execFile, std::placeholders::_2, flags));
CMD_EXECUTE ("execute", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
CMD_EXECUTE ("execute.throw", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
CMD_EXECUTE ("execute.throw.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background);
CMD_EXECUTE ("execute.nothrow", rpc::ExecFile::flag_expand_tilde);
CMD_EXECUTE ("execute.nothrow.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_background);
CMD_EXECUTE ("execute.raw", rpc::ExecFile::flag_throw);
CMD_EXECUTE ("execute.raw.bg", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background);
CMD_EXECUTE ("execute.raw_nothrow", 0);
CMD_EXECUTE ("execute.raw_nothrow.bg", rpc::ExecFile::flag_background);
CMD_EXECUTE ("execute.capture", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
CMD_EXECUTE ("execute.capture_nothrow", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
// TODO: Convert to new command types:
*rpc::command_base::argument(0) = "placeholder.0";
*rpc::command_base::argument(1) = "placeholder.1";
*rpc::command_base::argument(2) = "placeholder.2";
*rpc::command_base::argument(3) = "placeholder.3";
CMD_ANY_P("argument.0", std::bind(&rpc::command_base::argument_ref, 0));
CMD_ANY_P("argument.1", std::bind(&rpc::command_base::argument_ref, 1));
CMD_ANY_P("argument.2", std::bind(&rpc::command_base::argument_ref, 2));
CMD_ANY_P("argument.3", std::bind(&rpc::command_base::argument_ref, 3));
CMD_ANY_LIST ("group.insert", std::bind(&group_insert, std::placeholders::_2));
rpc::rpc.mark_safe("system.api_version");
rpc::rpc.mark_safe("system.client_version");
rpc::rpc.mark_safe("system.library_version");
rpc::rpc.mark_safe("system.file.max_size");
rpc::rpc.mark_safe("system.file.split_size");
rpc::rpc.mark_safe("system.file.split_suffix");
rpc::rpc.mark_safe("system.sockets.size");
rpc::rpc.mark_safe("system.sockets.max_size");
rpc::rpc.mark_safe("system.sockets.reserved_alloc");
rpc::rpc.mark_safe("system.sockets.available_alloc");
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
rpc::rpc.mark_safe(category_name + ".size");
rpc::rpc.mark_safe(category_name + ".max_size");
if (i == 0) {
rpc::rpc.mark_safe(category_name + ".min_alloc");
continue;
}
rpc::rpc.mark_safe(category_name + ".min_alloc");
rpc::rpc.mark_safe(category_name + ".max_alloc");
}
rpc::rpc.mark_safe("directory.default");
rpc::rpc.mark_safe("session.path");
rpc::rpc.mark_safe("session.use_lock");
rpc::rpc.mark_safe("session.on_completion");
rpc::rpc.mark_safe("pieces.sync.always_safe");
rpc::rpc.mark_safe("pieces.sync.timeout");
rpc::rpc.mark_safe("pieces.sync.timeout_safe");
rpc::rpc.mark_safe("pieces.preload.type");
rpc::rpc.mark_safe("pieces.preload.min_size");
rpc::rpc.mark_safe("pieces.preload.min_rate");
rpc::rpc.mark_safe("pieces.memory.max");
rpc::rpc.mark_safe("pieces.hash.on_completion");
}
-150
View File
@@ -1,150 +0,0 @@
#include "config.h"
#include <fcntl.h>
#include <iterator>
#include <stdio.h>
#include <unistd.h>
#include <torrent/data/chunk_utils.h>
#include <torrent/object.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "control.h"
#include "command_helpers.h"
#include "globals.h"
#include "setup.h"
#include "core/download.h"
#include "core/download_list.h"
#include "core/manager.h"
#include "rpc/parse.h"
#include "rpc/parse_commands.h"
torrent::Object
apply_log_open(int output_flags, const torrent::Object::list_type& raw_args) {
std::vector<std::string> args;
for (const auto& arg : raw_args)
args.push_back(arg.as_string());
apply_log_open_str(output_flags, args);
return torrent::Object();
}
torrent::Object
apply_log_add_output(const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Invalid number of arguments.");
log_add_group_output_str(args.front().as_string(), 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) {
if (rpc::execFile.log_fd() != -1) {
switch (logType) {
case 0: ::close(rpc::execFile.log_fd()); rpc::execFile.set_log_fd(-1); break;
case 1:
// if (control->scgi()) {
// ::close(control->scgi()->log_fd());
// control->scgi()->set_log_fd(-1);
// }
break;
default: break;
}
}
if (!arg.empty()) {
int logFd = open(expand_path(arg).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644);
if (logFd < 0)
throw torrent::input_error("Could not open execute log file.");
switch (logType) {
case 0: rpc::execFile.set_log_fd(logFd); break;
// case 1: if (control->scgi()) control->scgi()->set_log_fd(logFd); break;
default: break;
}
control->core()->push_log("Opened log file.");
} else {
control->core()->push_log("Closed log file.");
}
return torrent::Object();
}
torrent::Object
log_vmmap_dump(const std::string& str) {
std::vector<torrent::vm_mapping> all_mappings;
for (const auto& d : *control->core()->download_list()) {
std::vector<torrent::vm_mapping> tmp_mappings = torrent::chunk_list_mapping(d->download());
all_mappings.insert(all_mappings.end(), tmp_mappings.begin(), tmp_mappings.end());
}
FILE* log_file = fopen(str.c_str(), "w");
if (log_file == NULL)
throw torrent::input_error("Could not open log file: " + str);
for (auto& all_mapping : all_mappings) {
fprintf(log_file, "%8p-%8p [%5llxk]\n", all_mapping.ptr, (char*)all_mapping.ptr + all_mapping.length, (long long unsigned int)(all_mapping.length / 1024));
}
fclose(log_file);
return torrent::Object();
}
void
initialize_command_logging() {
CMD2_ANY_LIST ("log.open_file", std::bind(&apply_log_open, 0, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_file.flush", std::bind(&apply_log_open, log_flag_flush, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_gz_file", std::bind(&apply_log_open, log_flag_use_gz, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_file_pid", std::bind(&apply_log_open, log_flag_append_pid, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_gz_file_pid", std::bind(&apply_log_open, log_flag_append_pid | log_flag_use_gz, std::placeholders::_2));
CMD2_ANY_LIST ("log.append_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
CMD2_ANY_LIST ("log.append_file.flush", std::bind(&apply_log_open, log_flag_append_file | log_flag_flush, std::placeholders::_2));
CMD2_ANY_LIST ("log.append_gz_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
CMD2_ANY_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));
CMD2_ANY_STRING_V("log.rpc", [](const auto&, const auto& str) { scgi_thread::set_rpc_log(str); });
CMD2_REDIRECT ("log.xmlrpc", "log.rpc"); // For backwards compatibility
}
-453
View File
@@ -1,453 +0,0 @@
#include "config.h"
#include <functional>
#include <cstdio>
#include <unistd.h>
#include <torrent/torrent.h>
#include <torrent/rate.h>
#include <torrent/data/file_manager.h>
#include <torrent/download/resource_manager.h>
#include <torrent/net/http_stack.h>
#include <torrent/net/socket_address.h>
#include <torrent/runtime/client_config.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/runtime/proxy_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/runtime/socket_manager.h>
#include <torrent/tracker/tracker.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
#include "core/download.h"
#include "core/manager.h"
#include "rpc/scgi.h"
#include "ui/root.h"
#include "rpc/parse.h"
#include "rpc/parse_commands.h"
#ifdef HAVE_SYSTEMD
#include <sys/socket.h>
#include <systemd/sd-daemon.h>
#endif
torrent::Object
listen_port_range() {
auto port_range = torrent::runtime::client_config()->listen_port_range();
return std::to_string(port_range.first) + "-" + std::to_string(port_range.second);
}
void
set_listen_port_range(const std::string& arg) {
unsigned int port_first{}, port_last{};
if (std::sscanf(arg.c_str(), "%i-%i", &port_first, &port_last) != 2)
throw torrent::input_error("Invalid port_range argument.");
if (port_first >= (1 << 16) || port_last >= (1 << 16))
throw torrent::input_error("Port range out-of-bounds.");
torrent::runtime::client_config()->set_listen_port_range(port_first, port_last);
}
torrent::Object
get_encryption() {
auto encryption_modes = torrent::runtime::network_config()->encryption_modes();
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_HANDSHAKE, encryption_modes.first) + "," +
torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_STREAM, encryption_modes.second);
}
torrent::Object
get_handshake_encryption() {
auto encryption_modes = torrent::runtime::network_config()->encryption_modes();
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_MODE, encryption_modes.first);
}
torrent::Object
get_stream_encryption() {
auto encryption_modes = torrent::runtime::network_config()->encryption_modes();
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_MODE, encryption_modes.second);
}
torrent::Object
apply_obsolete_encryption(const torrent::Object::list_type& args) {
torrent::encryption_mode handshake_mode{torrent::ENCRYPTION_MODE_ALLOW};
torrent::encryption_mode stream_mode{torrent::ENCRYPTION_MODE_ALLOW};
for (auto& itr : args) {
auto arg = itr.as_string();
if (arg == "none") {
handshake_mode = torrent::ENCRYPTION_MODE_DENY;
stream_mode = torrent::ENCRYPTION_MODE_DENY;
break;
} else if (arg == "allow_incoming") {
} else if (arg == "try_outgoing") {
} else if (arg == "require") {
handshake_mode = torrent::ENCRYPTION_MODE_REQUIRE;
} else if (arg == "require_RC4" || arg == "require_rc4") {
handshake_mode = torrent::ENCRYPTION_MODE_REQUIRE;
stream_mode = torrent::ENCRYPTION_MODE_REQUIRE;
break;
} else if (arg == "enable_retry") {
} else if (arg == "prefer_plaintext") {
} else {
throw torrent::input_error("Invalid encryption option: '" + arg + "'");
}
}
lt_log_print(torrent::LOG_WARN, "Obsolete encryption options used, use 'handshake_{deny,allow,prefer,require}, stream_{deny,allow,prefer,require}' instead.");
torrent::runtime::network_config()->set_encryption_modes(handshake_mode, stream_mode);
return {};
}
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
apply_tos(const torrent::Object::string_type& arg) {
rpc::command_base::value_type value;
if (!rpc::parse_whole_value_nothrow(arg.c_str(), &value, 16, 1))
value = torrent::option_find_string(torrent::OPTION_IP_TOS, arg.c_str());
torrent::runtime::network_config()->set_priority(value);
return torrent::Object();
}
void
initialize_rpc_handlers() {
rpc::rpc.initialize_handlers();
unsigned int count = 0;
for (const auto& [name, cmd] : rpc::commands) {
if (!(cmd.m_flags & rpc::CommandMap::flag_public_rpc))
continue;
rpc::rpc.insert_command(name.c_str(), cmd.m_parm, cmd.m_doc);
++count;
}
lt_log_print(torrent::LOG_RPC_EVENTS, "RPC manager initialized with %u functions.", count);
}
torrent::Object
apply_scgi(const std::string& arg, int type) {
if (scgi_thread::scgi() != nullptr)
throw torrent::input_error("SCGI already enabled.");
initialize_rpc_handlers();
torrent::sa_unique_ptr sa;
auto scgi = std::make_unique<rpc::SCgi>();
try {
int port{};
char dummy{};
char address[1024];
std::string path;
switch (type) {
case 1:
if (std::sscanf(arg.c_str(), ":%i%c", &port, &dummy) == 1) {
sa = torrent::sa_make_inet();
lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is open to any address and is a security risk");
} else if (std::sscanf(arg.c_str(), "%1023[^:]:%i%c", address, &port, &dummy) == 2 ||
std::sscanf(arg.c_str(), "[%64[^]]]:%i%c", address, &port, &dummy) == 2) { // [xx::xx]:port format
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");
} else {
throw torrent::input_error("Could not parse address.");
}
if (port <= 0 || port >= (1 << 16))
throw torrent::input_error("Invalid port number.");
torrent::sap_set_port(sa, port);
scgi->open_port(sa.get(), torrent::sap_length(sa), rpc::call_command_value("network.scgi.dont_route"));
break;
case 2:
default:
path = expand_path(arg);
unlink(path.c_str());
scgi->open_named(path);
break;
}
} catch (torrent::local_error& e) {
throw torrent::input_error(e.what());
}
scgi_thread::set_scgi(scgi.release());
return torrent::Object();
}
torrent::Object
apply_scgi_systemd() {
#ifdef HAVE_SYSTEMD
if (scgi_thread::scgi() != nullptr)
throw torrent::input_error("SCGI already enabled.");
int n = sd_listen_fds(0);
if (n < 1)
throw torrent::input_error("No systemd socket(s) provided (sd_listen_fds returned " +
std::to_string(n) + ").");
// Iterate over all provided fds. Use the first listening stream socket;
// close the rest. The systemd docs say unused fds should be closed.
int selected_fd = -1;
for (int i = 0; i < n; i++) {
int fd = SD_LISTEN_FDS_START + i;
if (selected_fd != -1) {
::close(fd);
continue;
}
auto err = sd_is_socket(fd, AF_UNSPEC, SOCK_STREAM, 1);
if (err < 0) {
// Safe to ignore errors here - we just skip it and move on.
::close(fd);
continue;
}
if (err == 0) {
// Not the socket we're looking for.
::close(fd);
continue;
}
selected_fd = fd;
}
if (selected_fd == -1)
throw torrent::input_error("No listening stream socket found among systemd-provided fds.");
initialize_rpc_handlers();
rpc::SCgi* scgi = new rpc::SCgi;
scgi->open_fd(selected_fd);
scgi_thread::set_scgi(scgi);
return torrent::Object();
#else
throw torrent::input_error("Systemd SCGI endpoint is not supported.");
#endif
}
torrent::Object
apply_xmlrpc_dialect(const std::string& arg) {
int value;
if (arg == "i8")
value = rpc::XmlRpc::dialect_i8;
else if (arg == "apache")
value = rpc::XmlRpc::dialect_apache;
else if (arg == "generic")
value = rpc::XmlRpc::dialect_generic;
else
value = -1;
rpc::rpc.set_dialect(value);
return torrent::Object();
}
void
initialize_command_network() {
auto file_manager = torrent::file_manager();
auto http_stack = torrent::net_thread::http_stack();
auto nw_config = torrent::runtime::network_config();
CMD_ANY ("network.listen.port", [](auto, auto) { return torrent::runtime::network_manager()->listen_port(); });
CMD_ANY_VALUE_V ("network.listen.port.set", [](auto, auto& value) { return torrent::runtime::network_manager()->set_listen_port(value); });
CMD_ANY ("network.listen.port.random", [](auto, auto) { return torrent::runtime::client_config()->listen_port_random(); });
CMD_ANY_VALUE_V ("network.listen.port.random.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_listen_port_random(value); });
CMD_ANY ("network.listen.port.range", [](auto, auto) { return listen_port_range(); });
CMD_ANY_STRING_V("network.listen.port.range.set", [](auto, auto& value) { return set_listen_port_range(value); });
CMD_ANY ("network.listen.backlog", [](auto, auto) { return torrent::runtime::network_config()->listen_backlog(); });
CMD_ANY_VALUE_V ("network.listen.backlog.set", [](auto, auto& value) { return torrent::runtime::network_config()->set_listen_backlog(value); });
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); });
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(); });
CMD_VAR_STRING ("protocol.connection.leech", "leech");
CMD_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");
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(); });
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); });
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); });
CMD_ANY ("network.bind_address", [nw_config](auto, auto) { return nw_config->bind_address_best_match_str(); });
CMD_ANY_STRING_V("network.bind_address.set", [nw_config](auto, auto& str) { return nw_config->set_bind_address_str(str); });
CMD_ANY ("network.bind_address.ipv4", [nw_config](auto, auto) { return nw_config->bind_inet_address_str(); });
CMD_ANY_STRING_V("network.bind_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet_address_str(str); });
CMD_ANY ("network.bind_address.ipv6", [nw_config](auto, auto) { return nw_config->bind_inet6_address_str(); });
CMD_ANY_STRING_V("network.bind_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet6_address_str(str); });
CMD_ANY ("network.local_address", [nw_config](auto, auto) { return nw_config->local_address_best_match_str(); });
CMD_ANY_STRING_V("network.local_address.set", [nw_config](auto, auto& str) { return nw_config->set_local_address_str(str); });
CMD_ANY ("network.local_address.ipv4", [nw_config](auto, auto) { return nw_config->local_inet_address_str(); });
CMD_ANY_STRING_V("network.local_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet_address_str(str); });
CMD_ANY ("network.local_address.ipv6", [nw_config](auto, auto) { return nw_config->local_inet6_address_str(); });
CMD_ANY_STRING_V("network.local_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet6_address_str(str); });
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); });
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(); });
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(); });
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");
rpc::rpc.mark_safe("network.port_range");
rpc::rpc.mark_safe("network.listen.port");
rpc::rpc.mark_safe("network.listen.backlog");
rpc::rpc.mark_safe("network.http.current_open");
rpc::rpc.mark_safe("network.http.max_cache_connections");
rpc::rpc.mark_safe("network.http.max_host_connections");
rpc::rpc.mark_safe("network.http.max_total_connections");
rpc::rpc.mark_safe("network.total_handshakes");
rpc::rpc.mark_safe("network.open_files");
rpc::rpc.mark_safe("network.max_open_files");
rpc::rpc.mark_safe("network.send_buffer.size");
rpc::rpc.mark_safe("network.receive_buffer.size");
rpc::rpc.mark_safe("network.bind_address");
rpc::rpc.mark_safe("network.local_address");
rpc::rpc.mark_safe("network.xmlrpc.size_limit");
rpc::rpc.mark_safe("network.open_sockets");
rpc::rpc.mark_safe("network.http.cacert");
rpc::rpc.mark_safe("network.http.capath");
rpc::rpc.mark_safe("network.proxy.global");
rpc::rpc.mark_safe("network.proxy.http");
rpc::rpc.mark_safe("network.scgi.dont_route");
rpc::rpc.mark_safe("protocol.pex");
rpc::rpc.mark_safe("network.rpc.use_xmlrpc");
rpc::rpc.mark_safe("network.rpc.use_jsonrpc");
}
-109
View File
@@ -1,109 +0,0 @@
#include "config.h"
#include <torrent/bitfield.h>
#include <torrent/rate.h>
#include <torrent/net/socket_address.h>
#include <torrent/peer/connection_list.h>
#include <torrent/peer/peer.h>
#include <torrent/peer/peer_info.h>
#include <torrent/utils/string_manip.h>
#include "globals.h"
#include "command_helpers.h"
#include "control.h"
#include "core/manager.h"
#include "display/utils.h"
torrent::Object
retrieve_p_address(torrent::Peer* peer) {
auto sa = peer->peer_info()->socket_address();
auto addr_str = torrent::sa_addr_str(sa);
if (sa->sa_family == AF_INET6)
return "[" + addr_str + "]";
return addr_str;
}
torrent::Object
retrieve_p_client_version(torrent::Peer* peer) {
char buf[128];
display::print_client_version(buf, buf + 128, peer->peer_info()->client_info());
return std::string(buf);
}
torrent::Object
retrieve_p_options_str(torrent::Peer* peer) {
return torrent::utils::transform_to_hex_str(peer->peer_info()->options(), peer->peer_info()->options() + 8);
}
torrent::Object
retrieve_p_completed_percent(torrent::Peer* peer) {
if (peer->bitfield()->size_bits() == 0)
return int64_t(0);
return (100 * peer->bitfield()->size_set()) / peer->bitfield()->size_bits();
}
void
initialize_command_peer() {
CMD2_PEER("p.id", [](auto* peer, auto) { return torrent::utils::transform_to_hex_str(peer->id()); });
CMD2_PEER("p.id_html", [](auto* peer, auto) { return torrent::utils::copy_escape_html_str(peer->id()); });
CMD2_PEER("p.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));
CMD2_PEER("p.is_encrypted", std::bind(&torrent::Peer::is_encrypted, std::placeholders::_1));
CMD2_PEER("p.is_incoming", std::bind(&torrent::Peer::is_incoming, std::placeholders::_1));
CMD2_PEER("p.is_obfuscated", std::bind(&torrent::Peer::is_obfuscated, std::placeholders::_1));
CMD2_PEER("p.is_snubbed", std::bind(&torrent::Peer::is_snubbed, std::placeholders::_1));
CMD2_PEER("p.is_unwanted", std::bind(&torrent::PeerInfo::is_unwanted, std::bind(&torrent::Peer::peer_info, std::placeholders::_1)));
CMD2_PEER("p.is_preferred", std::bind(&torrent::PeerInfo::is_preferred, std::bind(&torrent::Peer::peer_info, std::placeholders::_1)));
CMD2_PEER("p.address", std::bind(&retrieve_p_address, std::placeholders::_1));
CMD2_PEER("p.port", [](auto* peer, auto) { return torrent::sa_port(peer->peer_info()->socket_address()); });
CMD2_PEER("p.completed_percent", std::bind(&retrieve_p_completed_percent, std::placeholders::_1));
CMD2_PEER("p.up_rate", std::bind(&torrent::Rate::rate, std::bind(&torrent::Peer::up_rate, std::placeholders::_1)));
CMD2_PEER("p.up_total", std::bind(&torrent::Rate::total, std::bind(&torrent::Peer::up_rate, std::placeholders::_1)));
CMD2_PEER("p.down_rate", std::bind(&torrent::Rate::rate, std::bind(&torrent::Peer::down_rate, std::placeholders::_1)));
CMD2_PEER("p.down_total", std::bind(&torrent::Rate::total, std::bind(&torrent::Peer::down_rate, std::placeholders::_1)));
CMD2_PEER("p.peer_rate", std::bind(&torrent::Rate::rate, std::bind(&torrent::Peer::peer_rate, std::placeholders::_1)));
CMD2_PEER("p.peer_total", std::bind(&torrent::Rate::total, std::bind(&torrent::Peer::peer_rate, std::placeholders::_1)));
CMD2_PEER ("p.snubbed", std::bind(&torrent::Peer::is_snubbed, std::placeholders::_1));
CMD2_PEER_VALUE_V("p.snubbed.set", std::bind(&torrent::Peer::set_snubbed, std::placeholders::_1, std::placeholders::_2));
CMD2_PEER ("p.banned", std::bind(&torrent::Peer::is_banned, std::placeholders::_1));
CMD2_PEER_VALUE_V("p.banned.set", std::bind(&torrent::Peer::set_banned, std::placeholders::_1, std::placeholders::_2));
CMD2_PEER_V("p.disconnect", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, 0));
CMD2_PEER_V("p.disconnect_delayed", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, torrent::ConnectionList::disconnect_delayed));
rpc::rpc.mark_safe("p.address");
rpc::rpc.mark_safe("p.port");
rpc::rpc.mark_safe("p.client_version");
rpc::rpc.mark_safe("p.options_str");
rpc::rpc.mark_safe("p.id");
rpc::rpc.mark_safe("p.id_html");
rpc::rpc.mark_safe("p.up_rate");
rpc::rpc.mark_safe("p.up_total");
rpc::rpc.mark_safe("p.down_rate");
rpc::rpc.mark_safe("p.down_total");
rpc::rpc.mark_safe("p.peer_rate");
rpc::rpc.mark_safe("p.peer_total");
rpc::rpc.mark_safe("p.is_encrypted");
rpc::rpc.mark_safe("p.is_incoming");
rpc::rpc.mark_safe("p.is_obfuscated");
rpc::rpc.mark_safe("p.is_snubbed");
rpc::rpc.mark_safe("p.is_unwanted");
rpc::rpc.mark_safe("p.is_preferred");
rpc::rpc.mark_safe("p.snubbed");
rpc::rpc.mark_safe("p.snubbed.set");
rpc::rpc.mark_safe("p.banned");
rpc::rpc.mark_safe("p.banned.set");
rpc::rpc.mark_safe("p.completed_percent");
rpc::rpc.mark_safe("p.disconnect");
rpc::rpc.mark_safe("p.disconnect_delayed");
}
-83
View File
@@ -1,83 +0,0 @@
#include "config.h"
#include <sys/types.h>
#include "core/manager.h"
#include "core/download.h"
#include "core/download_list.h"
#include "core/view.h"
#include "core/view_manager.h"
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
torrent::Object
cmd_scheduler_simple_added(core::Download* download) {
unsigned int numActive = (*control->view_manager()->find("active"))->size_visible();
int64_t maxActive = rpc::call_command("scheduler.max_active", torrent::Object()).as_value();
if (numActive < (uint64_t)maxActive)
control->core()->download_list()->resume(download);
return torrent::Object();
}
torrent::Object
cmd_scheduler_simple_removed(core::Download* download) {
control->core()->download_list()->pause(download);
core::View* viewActive = *control->view_manager()->find("active");
int64_t maxActive = rpc::call_command("scheduler.max_active", torrent::Object()).as_value();
if ((int64_t)viewActive->size_visible() >= maxActive)
return torrent::Object();
// The 'started' view contains all the views we may choose amongst.
core::View* viewStarted = *control->view_manager()->find("started");
for (core::View::iterator itr = viewStarted->begin_visible(), last = viewStarted->end_visible(); itr != last; itr++) {
if ((*itr)->is_active())
continue;
control->core()->download_list()->resume(*itr);
}
return torrent::Object();
}
torrent::Object
cmd_scheduler_simple_update([[maybe_unused]] core::Download* download) {
core::View* viewActive = *control->view_manager()->find("active");
core::View* viewStarted = *control->view_manager()->find("started");
unsigned int numActive = viewActive->size_visible();
uint64_t maxActive = rpc::call_command("scheduler.max_active", torrent::Object()).as_value();
if (viewActive->size_visible() < maxActive) {
for (core::View::iterator itr = viewStarted->begin_visible(), last = viewStarted->end_visible(); itr != last; itr++) {
if ((*itr)->is_active())
continue;
control->core()->download_list()->resume(*itr);
if (++numActive >= maxActive)
break;
}
} else {
while (viewActive->size_visible() > maxActive)
control->core()->download_list()->pause(*viewActive->begin_visible());
}
return torrent::Object();
}
void
initialize_command_scheduler() {
CMD2_VAR_VALUE("scheduler.max_active", int64_t(-1));
CMD2_DL("scheduler.simple.added", std::bind(&cmd_scheduler_simple_added, std::placeholders::_1));
CMD2_DL("scheduler.simple.removed", std::bind(&cmd_scheduler_simple_removed, std::placeholders::_1));
CMD2_DL("scheduler.simple.update", std::bind(&cmd_scheduler_simple_update, std::placeholders::_1));
}
-250
View File
@@ -1,250 +0,0 @@
#include "config.h"
#include <cstdio>
#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 "rpc/parse.h"
#include "rpc/parse_commands.h"
#include "globals.h"
#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];
torrent::sa_unique_ptr sa;
ret = std::sscanf(itr->as_string().c_str(), "%1023[^/]/%d%c", host, &prefixWidth, &dummy);
if (ret < 1)
throw torrent::input_error("Invalid address/prefix.");
try {
sa = torrent::sa_copy(torrent::sa_lookup_address(host, AF_INET).get());
} catch (torrent::input_error& e) {
throw torrent::input_error("Could not resolve host: " + std::string(e.what()));
}
uint32_t begin, end;
auto sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr);
begin = end = sa_addr;
if (ret == 2) {
if (++itr != args.end())
throw torrent::input_error("Cannot specify both network and range end.");
uint32_t netmask = std::numeric_limits<uint32_t>::max() << (32 - prefixWidth);
if (prefixWidth >= 32 || sa_addr & ~netmask)
throw torrent::input_error("Invalid address/prefix.");
end = sa_addr | ~netmask;
} else if (++itr != args.end()) {
try {
sa = torrent::sa_copy(torrent::sa_lookup_address(itr->as_string(), AF_INET).get());
} catch (torrent::input_error& e) {
throw torrent::input_error("Could not resolve host: " + std::string(e.what()));
}
sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr);
end = sa_addr;
}
// convert to [begin, end) making sure the end doesn't overflow
// (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) {
auto arg_itr = args.begin();
if (arg_itr == args.end())
throw torrent::input_error("Missing throttle name.");
const std::string& name = arg_itr->as_string();
if (name.empty() || name == "NULL")
throw torrent::input_error("Invalid throttle name '" + name + "'.");
if (++arg_itr == args.end() || arg_itr->as_string().empty())
throw torrent::input_error("Missing throttle rate for '" + name + "'.");
int64_t rate;
rpc::parse_whole_value_nothrow(arg_itr->as_string().c_str(), &rate);
if (rate < 0)
throw torrent::input_error("Throttle rate must be non-negative.");
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 != nullptr)
throttle->set_max_rate(rate * 1024);
return torrent::Object();
}
static const int throttle_info_up = (1 << 0);
static const int throttle_info_down = (1 << 1);
static const int throttle_info_max = (1 << 2);
static const int throttle_info_rate = (1 << 3);
torrent::Object
retrieve_throttle_info(const torrent::Object::string_type& name, int flags) {
auto itr = control->core()->throttles().find(name);
auto throttles = (itr == control->core()->throttles().end()) ? core::ThrottlePair(nullptr, nullptr) : itr->second;
auto* throttle = flags & throttle_info_down ? throttles.second : throttles.first;
auto* global = flags & throttle_info_down ? torrent::down_throttle_global() : torrent::up_throttle_global();
if (throttle == NULL && name.empty())
throttle = global;
if (throttle == NULL)
return flags & throttle_info_rate ? (int64_t)0 : (int64_t)-1;
else if (!throttle->is_throttled() || !global->is_throttled())
return (int64_t)0;
else if (flags & throttle_info_rate)
return (int64_t)throttle->rate()->rate();
else
return (int64_t)throttle->max_rate();
}
torrent::Object
throttle_update(const char* variable, int64_t value) {
rpc::commands.call_command(variable, value);
control->ui()->adjust_up_throttle(0);
control->ui()->adjust_down_throttle(0);
return torrent::Object();
}
void
initialize_command_throttle() {
CMD2_ANY ("throttle.unchoked_uploads", std::bind(&torrent::ResourceManager::currently_upload_unchoked, torrent::resource_manager()));
CMD2_ANY ("throttle.max_unchoked_uploads", std::bind(&torrent::ResourceManager::max_upload_unchoked, torrent::resource_manager()));
CMD2_ANY ("throttle.unchoked_downloads", std::bind(&torrent::ResourceManager::currently_download_unchoked, torrent::resource_manager()));
CMD2_ANY ("throttle.max_unchoked_downloads", std::bind(&torrent::ResourceManager::max_download_unchoked, torrent::resource_manager()));
CMD2_VAR_VALUE ("throttle.min_peers.normal", 100);
CMD2_VAR_VALUE ("throttle.max_peers.normal", 200);
CMD2_VAR_VALUE ("throttle.min_peers.seed", -1);
CMD2_VAR_VALUE ("throttle.max_peers.seed", -1);
CMD2_VAR_VALUE ("throttle.min_uploads", 0);
CMD2_VAR_VALUE ("throttle.max_uploads", 50);
CMD2_VAR_VALUE ("throttle.min_downloads", 0);
CMD2_VAR_VALUE ("throttle.max_downloads", 50);
CMD2_VAR_VALUE ("throttle.max_uploads.div._val", 1);
CMD2_VAR_VALUE ("throttle.max_uploads.global._val", 0);
CMD2_VAR_VALUE ("throttle.max_downloads.div._val", 1);
CMD2_VAR_VALUE ("throttle.max_downloads.global._val", 0);
CMD2_REDIRECT ("throttle.max_uploads.div", "throttle.max_uploads.div._val");
CMD2_REDIRECT ("throttle.max_uploads.global", "throttle.max_uploads.global._val");
CMD2_REDIRECT ("throttle.max_downloads.div", "throttle.max_downloads.div._val");
CMD2_REDIRECT ("throttle.max_downloads.global", "throttle.max_downloads.global._val");
CMD2_ANY_VALUE ("throttle.max_uploads.div.set", std::bind(&throttle_update, "throttle.max_uploads.div._val.set", std::placeholders::_2));
CMD2_ANY_VALUE ("throttle.max_uploads.global.set", std::bind(&throttle_update, "throttle.max_uploads.global._val.set", std::placeholders::_2));
CMD2_ANY_VALUE ("throttle.max_downloads.div.set", std::bind(&throttle_update, "throttle.max_downloads.div._val.set", std::placeholders::_2));
CMD2_ANY_VALUE ("throttle.max_downloads.global.set", std::bind(&throttle_update, "throttle.max_downloads.global._val.set", std::placeholders::_2));
// TODO: Move the logic into some libtorrent function.
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 ("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));
// 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_STRING ("throttle.up.max", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_up | throttle_info_max));
CMD2_ANY_STRING ("throttle.up.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_up | throttle_info_rate));
CMD2_ANY_STRING ("throttle.down.max", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_max));
CMD2_ANY_STRING ("throttle.down.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_rate));
rpc::rpc.mark_safe("throttle.unchoked_uploads");
rpc::rpc.mark_safe("throttle.max_unchoked_uploads");
rpc::rpc.mark_safe("throttle.unchoked_downloads");
rpc::rpc.mark_safe("throttle.max_unchoked_downloads");
rpc::rpc.mark_safe("throttle.min_peers.normal");
rpc::rpc.mark_safe("throttle.min_peers.normal.set");
rpc::rpc.mark_safe("throttle.max_peers.normal");
rpc::rpc.mark_safe("throttle.max_peers.normal.set");
rpc::rpc.mark_safe("throttle.min_peers.seed");
rpc::rpc.mark_safe("throttle.min_peers.seed.set");
rpc::rpc.mark_safe("throttle.max_peers.seed");
rpc::rpc.mark_safe("throttle.max_peers.seed.set");
rpc::rpc.mark_safe("throttle.min_uploads");
rpc::rpc.mark_safe("throttle.min_uploads.set");
rpc::rpc.mark_safe("throttle.max_uploads");
rpc::rpc.mark_safe("throttle.max_uploads.set");
rpc::rpc.mark_safe("throttle.min_downloads");
rpc::rpc.mark_safe("throttle.min_downloads.set");
rpc::rpc.mark_safe("throttle.max_downloads");
rpc::rpc.mark_safe("throttle.max_downloads.set");
rpc::rpc.mark_safe("throttle.max_uploads.div");
rpc::rpc.mark_safe("throttle.max_uploads.div.set");
rpc::rpc.mark_safe("throttle.max_uploads.div._val");
rpc::rpc.mark_safe("throttle.max_uploads.div._val.set");
rpc::rpc.mark_safe("throttle.max_uploads.global");
rpc::rpc.mark_safe("throttle.max_uploads.global.set");
rpc::rpc.mark_safe("throttle.max_uploads.global._val");
rpc::rpc.mark_safe("throttle.max_uploads.global._val.set");
rpc::rpc.mark_safe("throttle.max_downloads.div");
rpc::rpc.mark_safe("throttle.max_downloads.div.set");
rpc::rpc.mark_safe("throttle.max_downloads.div._val");
rpc::rpc.mark_safe("throttle.max_downloads.div._val.set");
rpc::rpc.mark_safe("throttle.max_downloads.global");
rpc::rpc.mark_safe("throttle.max_downloads.global.set");
rpc::rpc.mark_safe("throttle.max_downloads.global._val");
rpc::rpc.mark_safe("throttle.max_downloads.global._val.set");
rpc::rpc.mark_safe("throttle.global_up.rate");
rpc::rpc.mark_safe("throttle.global_up.total");
rpc::rpc.mark_safe("throttle.global_up.max_rate");
rpc::rpc.mark_safe("throttle.global_up.max_rate.set");
rpc::rpc.mark_safe("throttle.global_up.max_rate.set_kb");
rpc::rpc.mark_safe("throttle.global_down.rate");
rpc::rpc.mark_safe("throttle.global_down.total");
rpc::rpc.mark_safe("throttle.global_down.max_rate");
rpc::rpc.mark_safe("throttle.global_down.max_rate.set");
rpc::rpc.mark_safe("throttle.global_down.max_rate.set_kb");
rpc::rpc.mark_safe("throttle.up.max");
rpc::rpc.mark_safe("throttle.up.rate");
rpc::rpc.mark_safe("throttle.down.max");
rpc::rpc.mark_safe("throttle.down.rate");
}
-180
View File
@@ -1,180 +0,0 @@
#include "config.h"
#include <cassert>
#include <cstdio>
#include <netdb.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>
#include "core/download.h"
#include "core/manager.h"
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
#include "core/dht_manager.h"
void
tracker_set_enabled(torrent::tracker::Tracker* tracker, bool state) {
if (state)
tracker->enable();
else
tracker->disable();
}
torrent::Object
apply_dht_add_node(const std::string& arg) {
int port;
char dummy;
char host[1024];
int ret = std::sscanf(arg.c_str(), "%1023[^:]:%i%c", host, &port, &dummy);
if (ret == 1)
port = 6881;
else if (ret != 2)
throw torrent::input_error("Could not parse host.");
if (port < 1 || port > 65535)
throw torrent::input_error("Invalid port number.");
assert(std::this_thread::get_id() == torrent::main_thread::thread_id());
auto host_str = std::string(host);
// TODO: Move this lookup to DhtController.
auto callback_id = torrent::system::make_callback_id();
// Currently discarding SOCK_STREAM.
torrent::this_thread::resolver()->resolve_specific(callback_id, host_str, PF_INET, [host_str, port](torrent::c_sa_shared_ptr sa, int err) {
if (sa == nullptr) {
lt_log_print(torrent::LOG_DHT_ERROR, "dht.add_node : could not resolve host : %s (%s)", gai_strerror(err), host_str.c_str());
return;
}
lt_log_print(torrent::LOG_DHT_CONTROLLER, "dht.add_node : %s", host_str.c_str());
torrent::runtime::network_manager()->dht_add_bootstrap_node(host_str.c_str(), port);
});
return torrent::Object();
}
torrent::Object
apply_enable_trackers(int64_t arg) {
if (arg == 0) {
for (auto download : *control->core()->download_list())
download->tracker_controller().for_each([](auto& tracker) { tracker.disable(); });
} else {
for (auto download : *control->core()->download_list())
download->tracker_controller().for_each([](auto& tracker) { tracker.enable(); });
}
return torrent::Object();
}
void
initialize_command_tracker() {
CMD2_TRACKER ("t.is_busy", [](auto* tracker, auto) { return tracker->is_requesting(); });
CMD2_TRACKER ("t.is_enabled", [](auto* tracker, auto) { return tracker->is_enabled(); });
CMD2_TRACKER ("t.is_extra_tracker", [](auto* tracker, auto) { return tracker->is_extra_tracker(); });
CMD2_TRACKER ("t.is_open", [](auto* tracker, auto) { return tracker->is_requesting(); });
CMD2_TRACKER ("t.is_scrapable", [](auto* tracker, auto) { return tracker->is_scrapable(); });
CMD2_TRACKER ("t.is_usable", [](auto* tracker, auto) { return tracker->is_usable(); });
// TODO: Deprecate.
CMD2_TRACKER ("t.can_scrape", [](auto* tracker, auto) { return tracker->is_scrapable(); });
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", [](auto* tracker, auto& value) { return tracker_set_enabled(tracker, value); });
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", [](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", [](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", [](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", [](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", [](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", [](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", [](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", [](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_ANY ("trackers.use_udp", [](auto, auto) { return true; });
CMD2_ANY_VALUE_V ("trackers.use_udp.set", [](auto, auto) {
lt_log_print(torrent::LOG_ERROR, "trackers.use_udp.set is no longer supported", 0);
})
CMD2_ANY_STRING_V ("dht.mode.set", [](auto, auto& str) { return control->dht_manager()->set_mode_by_user(str); });
CMD2_ANY ("dht.port", [](auto, auto) { return torrent::runtime::network_manager()->dht_controller()->port(); });
CMD2_ANY_VALUE_V ("dht.port.set", [](auto, auto) {
lt_log_print(torrent::LOG_DHT_ERROR, "dht.port.set is no longer supported, use dht.override_port.set", 0);
});
CMD2_ANY ("dht.override_port", [](auto, auto) { return torrent::runtime::network_config()->override_dht_port(); });
CMD2_ANY_VALUE_V ("dht.override_port.set", [](auto, auto& value) { return torrent::runtime::network_manager()->set_dht_port(value); });
CMD2_ANY_STRING ("dht.add_node", [](auto, auto& str) { return apply_dht_add_node(str); });
CMD2_ANY ("dht.statistics", [](auto, auto) { return control->dht_manager()->dht_statistics(); });
rpc::rpc.mark_safe("t.url");
rpc::rpc.mark_safe("t.group");
rpc::rpc.mark_safe("t.id");
rpc::rpc.mark_safe("t.type");
rpc::rpc.mark_safe("t.is_usable");
rpc::rpc.mark_safe("t.is_busy");
rpc::rpc.mark_safe("t.is_enabled");
rpc::rpc.mark_safe("t.is_enabled.set");
rpc::rpc.mark_safe("t.is_extra_tracker");
rpc::rpc.mark_safe("t.is_open");
rpc::rpc.mark_safe("t.normal_interval");
rpc::rpc.mark_safe("t.scrape_time_last");
rpc::rpc.mark_safe("t.scrape_counter");
rpc::rpc.mark_safe("t.success_time_last");
rpc::rpc.mark_safe("t.success_counter");
rpc::rpc.mark_safe("t.failed_time_last");
rpc::rpc.mark_safe("t.failed_counter");
rpc::rpc.mark_safe("t.activity_time_last");
rpc::rpc.mark_safe("t.activity_time_next");
rpc::rpc.mark_safe("t.scrape_complete");
rpc::rpc.mark_safe("t.scrape_incomplete");
rpc::rpc.mark_safe("t.scrape_downloaded");
rpc::rpc.mark_safe("dht.mode.set");
rpc::rpc.mark_safe("dht.port");
rpc::rpc.mark_safe("dht.override_port");
rpc::rpc.mark_safe("dht.add_node");
rpc::rpc.mark_safe("dht.statistics");
rpc::rpc.mark_safe("trackers.numwant");
rpc::rpc.mark_safe("trackers.use_udp");
}
-940
View File
@@ -1,940 +0,0 @@
#include "config.h"
#include <sys/types.h>
#include <ctime>
#include <regex>
#include <torrent/utils/log.h>
#include "core/manager.h"
#include "core/view_manager.h"
#include "display/canvas.h"
#include "ui/root.h"
#include "ui/download_list.h"
#include "display/color_map.h"
#include "rpc/parse.h"
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
typedef void (core::ViewManager::*view_event_slot)(const std::string&, const torrent::Object&);
torrent::Object
apply_view_filter_on(const torrent::Object::list_type& args) {
if (args.size() < 1)
throw torrent::input_error("Too few arguments.");
const std::string& name = args.front().as_string();
if (name.empty())
throw torrent::input_error("First argument must be a string.");
core::ViewManager::filter_args filterArgs;
for (torrent::Object::list_const_iterator itr = ++args.begin(), last = args.end(); itr != last; itr++)
filterArgs.push_back(itr->as_string());
control->view_manager()->set_filter_on(name, filterArgs);
return torrent::Object();
}
torrent::Object
apply_view_event(view_event_slot viewFilterSlot, const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Wrong argument count.");
(control->view_manager()->*viewFilterSlot)(args.front().as_string(), args.back());
return torrent::Object();
}
torrent::Object
apply_view_sort(const torrent::Object::list_type& args) {
if (args.size() <= 0 || args.size() > 2)
throw torrent::input_error("Wrong argument count.");
const std::string& name = args.front().as_string();
if (name.empty())
throw torrent::input_error("First argument must be a string.");
int32_t value = 0;
if (args.size() == 2)
value = rpc::convert_to_value(args.back());
control->view_manager()->sort(name, value);
return torrent::Object();
}
torrent::Object
apply_view_list() {
torrent::Object rawResult = torrent::Object::create_list();
torrent::Object::list_type& result = rawResult.as_list();
for (auto itr : *control->view_manager())
result.push_back(itr->name());
return rawResult;
}
torrent::Object
apply_view_set(const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Wrong argument count.");
core::ViewManager::iterator itr = control->view_manager()->find(args.back().as_string());
if (itr == control->view_manager()->end())
throw torrent::input_error("Could not find view \"" + args.back().as_string() + "\".");
// if (args.front().as_string() == "main")
// control->ui()->download_list()->set_view(*itr);
// else
throw torrent::input_error("No such target.");
}
torrent::Object
apply_print([[maybe_unused]] rpc::target_type target, const torrent::Object& rawArgs) {
char buffer[1024];
rpc::print_object(buffer, buffer + 1024, &rawArgs, 0);
control->core()->push_log(buffer);
return torrent::Object();
}
torrent::Object
apply_cat([[maybe_unused]] rpc::target_type target, const torrent::Object& rawArgs) {
std::string result;
rpc::print_object_std(&result, &rawArgs, 0);
return result;
}
torrent::Object
apply_value([[maybe_unused]] rpc::target_type target, const torrent::Object::list_type& args) {
if (args.size() < 1)
throw torrent::input_error("'value' takes at least a number argument!");
if (args.size() > 2)
throw torrent::input_error("'value' takes at most two arguments!");
torrent::Object::value_type val = 0;
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;
char* endptr = 0;
val = strtoll(args.front().as_string().c_str(), &endptr, base);
while (*endptr == ' ' || *endptr == '\n') ++endptr;
if (*endptr)
throw torrent::input_error("Junk at end of number: " + args.front().as_string());
}
return val;
}
torrent::Object
apply_try(rpc::target_type target, const torrent::Object& args) {
try {
return rpc::call_object(args, target);
} catch (torrent::input_error& e) {
lt_log_print(torrent::LOG_RPC_EVENTS, "try command caught input_error: %s", e.what());
}
return torrent::Object();
}
// Move these boolean operators to a new file.
inline bool
as_boolean(const torrent::Object& rawArgs) {
switch (rawArgs.type()) {
case torrent::Object::TYPE_VALUE: return rawArgs.as_value();
case torrent::Object::TYPE_STRING: return !rawArgs.as_string().empty();
// We need to properly handle argument lists that are single-object
// at a higher level.
case torrent::Object::TYPE_LIST: return !rawArgs.as_list().empty() && as_boolean(rawArgs.as_list().front());
// case torrent::Object::TYPE_MAP: return !rawArgs.as_map().empty();
default: return false;
}
}
torrent::Object
apply_not(rpc::target_type target, const torrent::Object& rawArgs) {
bool result;
if (rawArgs.is_dict_key())
result = as_boolean(rpc::commands.call_command(rawArgs.as_dict_key().c_str(), rawArgs.as_dict_obj(),
target));
// TODO: Thus we should clean this up...
else if (rawArgs.is_list() && !rawArgs.as_list().empty())
return apply_not(target, rawArgs.as_list().front());
else
result = as_boolean(rawArgs);
return (int64_t)!result;
}
torrent::Object
apply_false([[maybe_unused]] rpc::target_type target, [[maybe_unused]] const torrent::Object& rawArgs) {
return (int64_t)0;
}
torrent::Object
apply_and(rpc::target_type target, const torrent::Object& rawArgs) {
if (rawArgs.type() != torrent::Object::TYPE_LIST)
return as_boolean(rawArgs);
for (const auto& itr : rawArgs.as_list())
if (itr.is_dict_key()) {
if (!as_boolean(rpc::commands.call_command(itr.as_dict_key().c_str(), itr.as_dict_obj(), target)))
return (int64_t)false;
} else if (itr.is_value()) {
if (!itr.as_value())
return (int64_t)false;
} else {
// TODO: Switch to new versions that only accept the new command syntax.
if (!as_boolean(rpc::parse_command_single(target, itr.as_string())))
return (int64_t)false;
}
return (int64_t)true;
}
torrent::Object
apply_or(rpc::target_type target, const torrent::Object& rawArgs) {
if (rawArgs.type() != torrent::Object::TYPE_LIST)
return as_boolean(rawArgs);
for (const auto& itr : rawArgs.as_list())
if (itr.is_dict_key()) {
if (as_boolean(rpc::commands.call_command(itr.as_dict_key().c_str(), itr.as_dict_obj(), target)))
return (int64_t)true;
} else if (itr.is_value()) {
if (itr.as_value())
return (int64_t)true;
} else {
if (as_boolean(rpc::parse_command_single(target, itr.as_string())))
return (int64_t)true;
}
return (int64_t)false;
}
torrent::Object
apply_cmp(rpc::target_type target, const torrent::Object::list_type& args) {
// We only need to check if empty() since if size() == 1 it calls
// the same command for both, or if size() == 2 then each side of
// the comparison has different commands.
if (args.empty())
throw torrent::input_error("Wrong argument count.");
// This really should be converted to using args flagged as
// commands, so that we can compare commands and statics values.
torrent::Object result1;
torrent::Object result2;
rpc::target_type target1 = rpc::is_target_pair(target) ? rpc::get_target_left(target) : target;
rpc::target_type target2 = rpc::is_target_pair(target) ? rpc::get_target_right(target) : target;
if (args.front().is_dict_key())
result1 = rpc::commands.call_command(args.front().as_dict_key().c_str(), args.front().as_dict_obj(), target1);
else
result1 = rpc::parse_command_single(target1, args.front().as_string());
if (args.back().is_dict_key())
result2 = rpc::commands.call_command(args.back().as_dict_key().c_str(), args.back().as_dict_obj(), target2);
else
result2 = rpc::parse_command_single(target2, args.back().as_string());
if (result1.type() != result2.type())
throw torrent::input_error("Type mismatch.");
switch (result1.type()) {
case torrent::Object::TYPE_VALUE: return result1.as_value() - result2.as_value();
case torrent::Object::TYPE_STRING: return result1.as_string().compare(result2.as_string());
default: return torrent::Object();
}
}
torrent::Object apply_less(rpc::target_type target, const torrent::Object::list_type& args) {
torrent::Object result = apply_cmp(target, args);
return result.is_value() ? result.as_value() < 0 : (int64_t)false;
}
torrent::Object apply_greater(rpc::target_type target, const torrent::Object::list_type& args) {
torrent::Object result = apply_cmp(target, args);
return result.is_value() ? result.as_value() > 0 : (int64_t)false;
}
torrent::Object apply_equal(rpc::target_type target, const torrent::Object::list_type& args) {
torrent::Object result = apply_cmp(target, args);
return result.is_value() ? result.as_value() == 0 : (int64_t)false;
}
torrent::Object
apply_compare(rpc::target_type target, const torrent::Object::list_type& args) {
if (!rpc::is_target_pair(target))
throw torrent::input_error("Can only compare a target pair.");
if (args.size() < 2)
throw torrent::input_error("Need at least order and one field.");
torrent::Object::list_const_iterator itr = args.begin();
std::string order = (itr++)->as_string();
const char* current = order.c_str();
torrent::Object result1;
torrent::Object result2;
for (torrent::Object::list_const_iterator last = args.end(); itr != last; itr++) {
std::string field = itr->as_string();
result1 = rpc::parse_command_single(rpc::get_target_left(target), field);
result2 = rpc::parse_command_single(rpc::get_target_right(target), field);
if (result1.type() != result2.type())
throw torrent::input_error(std::string("Type mismatch in compare of ") + field);
bool descending = *current == 'd' || *current == 'D' || *current == '-';
if (*current) {
if (!descending && !(*current == 'a' || *current == 'A' || *current == '+'))
throw torrent::input_error(std::string("Bad order '") + *current + "' in " + order);
++current;
}
switch (result1.type()) {
case torrent::Object::TYPE_VALUE:
if (result1.as_value() != result2.as_value())
return (int64_t) (descending ^ (result1.as_value() < result2.as_value()));
break;
case torrent::Object::TYPE_STRING:
if (result1.as_string() != result2.as_string())
return (int64_t) (descending ^ (result1.as_string() < result2.as_string()));
break;
default:
break; // treat unknown types as equal
}
}
// if all else is equal, ensure stable sort order based on memory location
return (int64_t) (target.second < target.third);
}
// Regexp based 'match' function.
// arg1: the text to match.
// arg2: the regexp pattern.
// eg: match{d.name=,.*linux.*iso}
torrent::Object apply_match(rpc::target_type target, const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Wrong argument count for 'match': 2 arguments needed.");
// This really should be converted to using args flagged as
// commands, so that we can compare commands and statics values.
torrent::Object result1;
torrent::Object result2;
rpc::target_type target1 = rpc::is_target_pair(target) ? rpc::get_target_left(target) : target;
rpc::target_type target2 = rpc::is_target_pair(target) ? rpc::get_target_right(target) : target;
if (args.front().is_dict_key())
result1 = rpc::commands.call_command(args.front().as_dict_key().c_str(), args.front().as_dict_obj(), target1);
else
result1 = rpc::parse_command_single(target1, args.front().as_string());
if (args.back().is_dict_key())
result2 = rpc::commands.call_command(args.back().as_dict_key().c_str(), args.back().as_dict_obj(), target2);
else
result2 = args.back().as_string();
if (result1.type() != result2.type())
throw torrent::input_error("Type mismatch for 'match' arguments.");
std::string text = result1.as_string();
std::string pattern = result2.as_string();
std::transform(text.begin(), text.end(), text.begin(), ::tolower);
std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower);
bool isAMatch = false;
try {
std::regex re(pattern);
isAMatch = std::regex_match(text, re);
} catch (const std::regex_error& exc) {
control->core()->push_log_std("regex_error: " + std::string(exc.what()));
}
return isAMatch ? (int64_t)true : (int64_t)false;
}
torrent::Object
apply_to_time(const torrent::Object& rawArgs, int flags) {
std::tm *u;
time_t t = (uint64_t)rawArgs.as_value();
if (flags & 0x1)
u = std::localtime(&t);
else
u = std::gmtime(&t);
if (u == NULL)
return torrent::Object();
char buffer[11];
if (flags & 0x2)
snprintf(buffer, 11, "%02u/%02u/%04u", u->tm_mday, (u->tm_mon + 1), (1900 + u->tm_year));
else
snprintf(buffer, 9, "%2d:%02d:%02d", u->tm_hour, u->tm_min, u->tm_sec);
return std::string(buffer);
}
torrent::Object
apply_to_elapsed_time(const torrent::Object& rawArgs) {
auto cached_seconds = torrent::this_thread::cached_seconds().count();
uint64_t arg = cached_seconds - rawArgs.as_value();
char buffer[48];
snprintf(buffer, 48, "%2d:%02d:%02d", (int)(arg / 3600), (int)((arg / 60) % 60), (int)(arg % 60));
return std::string(buffer);
}
torrent::Object
apply_to_kb(const torrent::Object& rawArgs) {
char buffer[32];
snprintf(buffer, 32, "%5.1f", (double)rawArgs.as_value() / (1 << 10));
return std::string(buffer);
}
torrent::Object
apply_to_mb(const torrent::Object& rawArgs) {
char buffer[32];
snprintf(buffer, 32, "%8.1f", (double)rawArgs.as_value() / (1 << 20));
return std::string(buffer);
}
torrent::Object
apply_to_xb(const torrent::Object& rawArgs) {
char buffer[48];
int64_t arg = rawArgs.as_value();
if (arg < (int64_t(1000) << 10))
snprintf(buffer, 48, "%5.1f KB", (double)arg / (int64_t(1) << 10));
else if (arg < (int64_t(1000) << 20))
snprintf(buffer, 48, "%5.1f MB", (double)arg / (int64_t(1) << 20));
else if (arg < (int64_t(1000) << 30))
snprintf(buffer, 48, "%5.1f GB", (double)arg / (int64_t(1) << 30));
else
snprintf(buffer, 48, "%5.1f TB", (double)arg / (int64_t(1) << 40));
return std::string(buffer);
}
torrent::Object
apply_to_throttle(const torrent::Object& rawArgs) {
int64_t arg = rawArgs.as_value();
if (arg < 0)
return "---";
else if (arg == 0)
return "off";
char buffer[32];
snprintf(buffer, 32, "%3d", (int)(arg / (1 << 10)));
return std::string(buffer);
}
// A series of if/else statements. Every even arguments are
// conditionals and odd arguments are branches to be executed, except
// the last one which is always a branch.
//
// if (cond1) { branch1 }
// <cond1>,<branch1>
//
// if (cond1) { branch1 } else if (cond2) { branch2 } else { branch3 }
// <cond1>,<branch1>,<cond2>,<branch2>,<branch3>
torrent::Object
apply_if(rpc::target_type target, const torrent::Object& raw_args, int flags) {
auto& args = raw_args.as_list();
auto itr = args.begin();
if (args.empty())
throw torrent::input_error("Empty argument list to " + std::string((flags & 0x1) ? "branch" : "if") + ".");
{
torrent::Object tmp;
const torrent::Object* conditional;
if (flags & 0x1 && itr->is_string())
conditional = &(tmp = 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())
conditional = &(tmp = rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target));
else
conditional = &*itr;
bool result;
switch (conditional->type()) {
case torrent::Object::TYPE_STRING:
result = !conditional->as_string().empty();
break;
case torrent::Object::TYPE_VALUE:
result = conditional->as_value();
break;
case torrent::Object::TYPE_NONE:
result = false;
break;
default:
throw torrent::input_error("Type not supported by " + std::string((flags & 0x1) ? "branch" : "if") + ".");
};
itr++;
if (!result && itr != args.end())
itr++;
}
if (itr == args.end())
return torrent::Object();
if (flags & 0x1) {
if (itr->is_string())
return rpc::parse_command(target, itr->as_string().c_str(), itr->as_string().c_str() + itr->as_string().size()).first;
if (itr->is_dict_key())
return rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target);
if (itr->is_list()) {
for (const auto& cmd_itr : itr->as_list()) {
if (cmd_itr.is_string())
rpc::parse_command(target, cmd_itr.as_string().c_str(), cmd_itr.as_string().c_str() + cmd_itr.as_string().size());
else if (cmd_itr.is_dict_key())
rpc::commands.call_command(cmd_itr.as_dict_key().c_str(), cmd_itr.as_dict_obj(), target);
else
throw torrent::input_error("Invalid command type in branch list.");
}
return torrent::Object();
}
throw torrent::input_error("Invalid command type in branch.");
}
return *itr;
}
torrent::Object
cmd_view_size(const torrent::Object::string_type& args) {
return (*control->view_manager()->find_throw(args))->size_visible();
}
torrent::Object
cmd_view_size_not_visible(const torrent::Object::string_type& args) {
return (*control->view_manager()->find_throw(args))->size_not_visible();
}
torrent::Object
cmd_view_persistent(const torrent::Object::string_type& args) {
core::View* view = *control->view_manager()->find_throw(args);
if (!view->get_filter().is_empty() || !view->event_added().is_empty() || !view->event_removed().is_empty())
throw torrent::input_error("Cannot set modified views as persitent.");
view->set_filter("d.views.has=" + args);
view->set_event_added("d.views.push_back_unique=" + args);
view->set_event_removed("d.views.remove=" + args);
return torrent::Object();
}
// TODO: These don't need wrapper functions anymore...
torrent::Object
cmd_ui_set_view(const torrent::Object::string_type& args) {
control->ui()->download_list()->set_current_view(args);
return torrent::Object();
}
torrent::Object
cmd_ui_current_view() {
return control->ui()->download_list()->current_view()->name();
}
torrent::Object
cmd_ui_unfocus_download(core::Download* download) {
control->ui()->download_list()->unfocus_download(download);
return torrent::Object();
}
torrent::Object
cmd_view_filter_download(core::Download* download, const torrent::Object::string_type& args) {
(*control->view_manager()->find_throw(args))->filter_download(download);
return torrent::Object();
}
torrent::Object
cmd_view_set_visible(core::Download* download, const torrent::Object::string_type& args) {
(*control->view_manager()->find_throw(args))->set_visible(download);
return torrent::Object();
}
torrent::Object
cmd_view_set_not_visible(core::Download* download, const torrent::Object::string_type& args) {
(*control->view_manager()->find_throw(args))->set_not_visible(download);
return torrent::Object();
}
torrent::Object
apply_elapsed_less(const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Wrong argument count.");
int64_t start_time = rpc::convert_to_value(args.front());
auto cached_seconds = torrent::this_thread::cached_seconds().count();
return (int64_t)(start_time != 0 && cached_seconds - start_time < rpc::convert_to_value(args.back()));
}
torrent::Object
apply_elapsed_greater(const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Wrong argument count.");
int64_t start_time = rpc::convert_to_value(args.front());
auto cached_seconds = torrent::this_thread::cached_seconds().count();
return (int64_t)(start_time != 0 && cached_seconds - start_time > rpc::convert_to_value(args.back()));
}
inline std::vector<int64_t>
as_vector(const torrent::Object::list_type& args) {
if (args.size() == 0)
throw torrent::input_error("Wrong argument count in as_vector.");
std::vector<int64_t> result;
for (const auto& arg : args) {
if (arg.is_value()) {
result.push_back(arg.as_value());
} else if (arg.is_string()) {
result.push_back(rpc::convert_to_value(arg.as_string()));
} else if (arg.is_list()) {
std::vector<int64_t> subResult = as_vector(arg.as_list());
result.insert(result.end(), subResult.begin(), subResult.end());
} else {
throw torrent::input_error("Wrong type supplied to as_vector.");
}
}
return result;
}
template <typename Comp>
int64_t
apply_math_basic(const char* name, Comp op, const torrent::Object::list_type& args) {
int64_t val = 0, rhs = 0;
bool divides = !strcmp(name, "math.div") || !strcmp(name, "math.mod");
if (args.size() == 0)
throw torrent::input_error(std::string(name) + ": No arguments provided!");
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
if (itr->is_value()) {
rhs = itr->as_value();
} else if (itr->is_string()) {
rhs = rpc::convert_to_value(itr->as_string());
} else if (itr->is_list()) {
rhs = apply_math_basic(name, op, itr->as_list());
} else {
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!");
val = itr == args.begin() ? rhs : op(val, rhs);
}
return val;
}
template <typename Comp>
int64_t
apply_arith_basic(Comp op, const torrent::Object::list_type& args) {
if (args.size() == 0)
throw torrent::input_error("Wrong argument count in apply_arith_basic.");
int64_t val = 0;
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
if (itr->is_value()) {
val = itr == args.begin() ? itr->as_value() : (op(val, itr->as_value()) ? val : itr->as_value());
} else if (itr->is_string()) {
int64_t cval = rpc::convert_to_value(itr->as_string());
val = itr == args.begin() ? cval : (op(val, cval) ? val : cval);
} else if (itr->is_list()) {
int64_t fval = apply_arith_basic(op, itr->as_list());
val = itr == args.begin() ? fval : (op(val, fval) ? val : fval);
} else {
throw torrent::input_error("Wrong type supplied to apply_arith_basic.");
}
}
return val;
}
int64_t
apply_arith_count(const torrent::Object::list_type& args) {
if (args.size() == 0)
throw torrent::input_error("Wrong argument count in apply_arith_count.");
int64_t val = 0;
for (const auto& arg : args) {
switch (arg.type()) {
case torrent::Object::TYPE_VALUE:
case torrent::Object::TYPE_STRING:
val++;
break;
case torrent::Object::TYPE_LIST:
val += apply_arith_count(arg.as_list());
break;
default:
throw torrent::input_error("Wrong type supplied to apply_arith_count.");
}
}
return val;
}
// Get the median of an unordered set of numbers of arbitrary
// type by modifing the underlying dataset
template <typename T = double, typename _InputIter>
T median(_InputIter __first, _InputIter __last) {
T __med;
unsigned int __size = __last - __first;
unsigned int __middle = __size / 2;
_InputIter __target1 = __first + __middle;
std::nth_element(__first, __target1, __last);
__med = *__target1;
if (__size % 2 == 0) {
_InputIter __target2 = std::max_element(__first, __target1);
__med = (__med + *__target2) / 2.0;
}
return __med;
}
int64_t
apply_arith_other(const char* op, const torrent::Object::list_type& args) {
if (args.size() == 0)
throw torrent::input_error("Wrong argument count in apply_arith_other.");
if (strcmp(op, "average") == 0) {
return (int64_t)(apply_math_basic(op, std::plus<int64_t>(), args) / apply_arith_count(args));
} else if (strcmp(op, "median") == 0) {
std::vector<int64_t> result = as_vector(args);
return (int64_t)median(result.begin(), result.end());
} else {
throw torrent::input_error("Wrong operation supplied to apply_arith_other.");
}
}
torrent::Object
cmd_status_throttle_names(bool up, const torrent::Object::list_type& args) {
if (args.size() == 0)
return torrent::Object();
std::vector<std::string> throttle_name_list;
for (const auto& arg : args) {
if (arg.is_string())
throttle_name_list.push_back(arg.as_string());
}
if (up)
control->ui()->set_status_throttle_up_names(throttle_name_list);
else
control->ui()->set_status_throttle_down_names(throttle_name_list);
return torrent::Object();
}
torrent::Object
apply_set_color(int color_id, const torrent::Object::string_type& color_str) {
control->object_storage()->set_str_string(display::color_vars[color_id], color_str);
display::Canvas::build_colors();
return torrent::Object();
}
void
initialize_command_ui() {
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_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.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_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));
// 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_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.focus.page_size", 0);
CMD2_ANY_LIST ("ui.status.throttle.up.set", std::bind(&cmd_status_throttle_names, true, std::placeholders::_2));
CMD2_ANY_LIST ("ui.status.throttle.down.set", std::bind(&cmd_status_throttle_names, false, std::placeholders::_2));
CMD2_ANY ("ui.keymap.style", std::bind(&ui::Root::keymap_style, control->ui()));
CMD2_ANY_STRING_V("ui.keymap.style.set", std::bind(&ui::Root::set_keymap_style, control->ui(), std::placeholders::_2));
// TODO: Add 'option_string' for rtorrent-specific options.
CMD2_VAR_STRING ("ui.torrent_list.layout", "full");
// Move.
CMD2_ANY("print", &apply_print);
CMD2_ANY("cat", &apply_cat);
CMD2_ANY_LIST("value", &apply_value);
CMD2_ANY("try", &apply_try);
CMD2_ANY("if", std::bind(&apply_if, std::placeholders::_1, std::placeholders::_2, 0));
CMD2_ANY("not", &apply_not);
CMD2_ANY("false", &apply_false);
CMD2_ANY("and", &apply_and);
CMD2_ANY("or", &apply_or);
// A temporary command for handling stuff until we get proper
// support for seperation of commands and literals.
CMD2_ANY("branch", std::bind(&apply_if, std::placeholders::_1, std::placeholders::_2, 1));
CMD2_ANY_LIST("less", &apply_less);
CMD2_ANY_LIST("greater", &apply_greater);
CMD2_ANY_LIST("equal", &apply_equal);
CMD2_ANY_LIST("compare", &apply_compare);
CMD2_ANY_LIST("match", &apply_match);
CMD2_ANY_VALUE("convert.gm_time", std::bind(&apply_to_time, std::placeholders::_2, 0));
CMD2_ANY_VALUE("convert.gm_date", std::bind(&apply_to_time, std::placeholders::_2, 0x2));
CMD2_ANY_VALUE("convert.time", std::bind(&apply_to_time, std::placeholders::_2, 0x1));
CMD2_ANY_VALUE("convert.date", std::bind(&apply_to_time, std::placeholders::_2, 0x1 | 0x2));
CMD2_ANY_VALUE("convert.elapsed_time", std::bind(&apply_to_elapsed_time, std::placeholders::_2));
CMD2_ANY_VALUE("convert.kb", std::bind(&apply_to_kb, std::placeholders::_2));
CMD2_ANY_VALUE("convert.mb", std::bind(&apply_to_mb, std::placeholders::_2));
CMD2_ANY_VALUE("convert.xb", std::bind(&apply_to_xb, std::placeholders::_2));
CMD2_ANY_VALUE("convert.throttle", std::bind(&apply_to_throttle, std::placeholders::_2));
CMD2_ANY_LIST("math.add", [](auto, const auto& args) { return apply_math_basic("math.add", std::plus(), args); });
CMD2_ANY_LIST("math.sub", [](auto, const auto& args) { return apply_math_basic("math.sub", std::minus(), args); });
CMD2_ANY_LIST("math.mul", [](auto, const auto& args) { return apply_math_basic("math.mul", std::multiplies(), args); });
CMD2_ANY_LIST("math.div", [](auto, const auto& args) { return apply_math_basic("math.div", std::divides(), args); });
CMD2_ANY_LIST("math.mod", [](auto, const auto& args) { return apply_math_basic("math.mod", std::modulus(), args); });
CMD2_ANY_LIST("math.min", [](auto, const auto& args) { return apply_arith_basic(std::less(), args); });
CMD2_ANY_LIST("math.max", [](auto, const auto& args) { return apply_arith_basic(std::greater(), args); });
CMD2_ANY_LIST("math.cnt", std::bind(&apply_arith_count, std::placeholders::_2));
CMD2_ANY_LIST("math.avg", std::bind(&apply_arith_other, "average", std::placeholders::_2));
CMD2_ANY_LIST("math.med", std::bind(&apply_arith_other, "median", std::placeholders::_2));
CMD2_ANY_LIST ("elapsed.less", std::bind(&apply_elapsed_less, std::placeholders::_2));
CMD2_ANY_LIST ("elapsed.greater", std::bind(&apply_elapsed_greater, std::placeholders::_2));
// Build set/get methods for all color definitions
for (int color_id = 1; color_id < display::RCOLOR_MAX; color_id++) {
control->object_storage()->insert_str(display::color_vars[color_id], "", rpc::object_storage::flag_string_type);
CMD2_ANY_STRING(std::string(display::color_vars[color_id]) + ".set", [color_id](const auto&, const auto& arg) {
return apply_set_color(color_id, arg);
});
CMD2_ANY(display::color_vars[color_id], [color_id](const auto&, const auto&) {
return control->object_storage()->get_str(display::color_vars[color_id]);
});
}
rpc::rpc.mark_safe("view.set_visible");
rpc::rpc.mark_safe("view.set_not_visible");
rpc::rpc.mark_safe("cat");
rpc::rpc.mark_safe("if");
rpc::rpc.mark_safe("branch");
rpc::rpc.mark_safe("and");
rpc::rpc.mark_safe("or");
rpc::rpc.mark_safe("not");
rpc::rpc.mark_safe("value");
rpc::rpc.mark_safe("compare");
rpc::rpc.mark_safe("elapsed.less");
rpc::rpc.mark_safe("elapsed.greater");
rpc::rpc.mark_safe("convert.gm_time");
rpc::rpc.mark_safe("convert.gm_date");
rpc::rpc.mark_safe("convert.time");
rpc::rpc.mark_safe("convert.date");
rpc::rpc.mark_safe("convert.elapsed_time");
rpc::rpc.mark_safe("convert.kb");
rpc::rpc.mark_safe("convert.mb");
rpc::rpc.mark_safe("convert.xb");
rpc::rpc.mark_safe("convert.throttle");
}
-171
View File
@@ -1,171 +0,0 @@
#include "config.h"
#include "control.h"
#include <unistd.h>
#include <sys/stat.h>
#include <torrent/net/http_stack.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/utils/directory_events.h>
#include "core/dht_manager.h"
#include "core/http_queue.h"
#include "core/manager.h"
#include "core/view_manager.h"
#include "display/canvas.h"
#include "display/window.h"
#include "display/window_http_queue.h"
#include "display/window_input.h"
#include "display/window_statusbar.h"
#include "display/window_title.h"
#include "display/manager.h"
#include "input/manager.h"
#include "input/input_event.h"
#include "rpc/command_scheduler.h"
#include "rpc/lua.h"
#include "rpc/parse_commands.h"
#include "rpc/object_storage.h"
#include "session/session_manager.h"
#include "ui/root.h"
#include "utils/watch_ready_queue.h"
Control::Control()
: m_ui(new ui::Root()),
m_display(new display::Manager()),
m_input(new input::Manager()),
m_inputStdin(new input::InputEvent(STDIN_FILENO)),
m_commandScheduler(new rpc::CommandScheduler()),
m_objectStorage(new rpc::object_storage()),
m_lua_engine(new rpc::LuaEngine()),
m_directory_events(new torrent::directory_events()),
m_watch_ready_queue(new utils::WatchReadyQueue()) {
m_core = std::make_unique<core::Manager>();
m_view_manager = std::make_unique<core::ViewManager>();
m_dht_manager = std::make_unique<core::DhtManager>();
m_inputStdin->slot_pressed(std::bind(&input::Manager::pressed, m_input.get(), std::placeholders::_1));
m_task_shutdown.slot() = [this] { handle_shutdown(); };
m_task_shutdown_clear_requests.slot() = [this] { handle_shutdown_clear_requests(); };
m_commandScheduler->set_slot_error_message([this](const std::string& msg) { m_core->push_log_std(msg); });
}
Control::~Control() {
m_view_manager.reset();
m_ui.reset();
m_display.reset();
}
void
Control::initialize() {
session_thread::thread()->start_thread();
scgi_thread::thread()->start_thread();
display::Canvas::initialize();
display::Window::slot_schedule([this](display::Window* w, std::chrono::microseconds t) { m_display->schedule(w, t); });
display::Window::slot_unschedule([this](display::Window* w) { m_display->unschedule(w); });
display::Window::slot_adjust([this]() { m_display->adjust_layout(); });
m_core->set_hashing_view(*m_view_manager->find_throw("hashing"));
m_ui->init(this);
if(!display::Canvas::daemon())
m_inputStdin->insert();
}
void
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();
m_directory_events->close();
if (scgi_thread::thread()->is_active())
scgi_thread::thread()->stop_thread_wait();
// Wait for all session files to be written.
session_thread::manager()->flush_all_pending_builds();
session_thread::thread()->stop_thread_wait();
m_ui->cleanup();
m_core->cleanup();
display::Canvas::erase_std();
display::Canvas::refresh_std();
display::Canvas::do_update();
display::Canvas::cleanup();
}
void
Control::cleanup_exception() {
display::Canvas::cleanup();
}
bool
Control::is_shutdown_completed() {
if (!m_shutdown_quick)
return false;
// Tracker requests can be disowned, so wait for these to
// finish. The edge case of torrent http downloads may delay
// shutdown.
// TODO: We keep http requests in the queue for a while after, so improve this check to ignore
// those.
if (torrent::net_thread::http_stack()->size() != 0 || !core()->http_queue()->empty())
return false;
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_shutdown_quick) {
torrent::runtime::network_manager()->listen_close();
torrent::runtime::shutdown();
m_directory_events->close();
m_core->shutdown(false);
if (!m_task_shutdown.is_scheduled())
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown, 5s);
} else {
torrent::runtime::quick_shutdown();
m_core->shutdown(true);
}
if (!m_task_shutdown_clear_requests.is_scheduled())
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown_clear_requests, 10s);
m_shutdown_quick = true;
m_shutdown_received = false;
}
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);
}
-121
View File
@@ -1,121 +0,0 @@
#ifndef RTORRENT_CONTROL_H
#define RTORRENT_CONTROL_H
#include <atomic>
#include <cinttypes>
#include <memory>
#include <sys/types.h>
#include <torrent/torrent.h>
#include <torrent/system/scheduler.h>
namespace ui {
class Root;
}
namespace core {
class Manager;
class ViewManager;
class DhtManager;
}
namespace display {
class Manager;
}
namespace input {
class InputEvent;
class Manager;
}
namespace rpc {
class CommandScheduler;
class XmlRpc;
class object_storage;
class LuaEngine;
}
namespace torrent {
class directory_events;
}
namespace utils {
class WatchReadyQueue;
}
class Control {
public:
Control();
~Control();
bool is_shutdown_completed();
bool is_shutdown_received() { return m_shutdown_received; }
bool is_shutdown_started() { return m_shutdown_quick; }
void initialize();
void cleanup();
void cleanup_exception();
void handle_shutdown();
void handle_shutdown_clear_requests();
void receive_normal_shutdown() { m_shutdown_received = true; }
void receive_quick_shutdown() { m_shutdown_received = true; m_shutdown_quick = true; }
core::Manager* core() { return m_core.get(); }
core::ViewManager* view_manager() { return m_view_manager.get(); }
core::DhtManager* dht_manager() { return m_dht_manager.get(); }
ui::Root* ui() { return m_ui.get(); }
display::Manager* display() { return m_display.get(); }
input::Manager* input() { return m_input.get(); }
input::InputEvent* input_stdin() { return m_inputStdin.get(); }
rpc::CommandScheduler* command_scheduler() { return m_commandScheduler.get(); }
rpc::object_storage* object_storage() { return m_objectStorage.get(); }
rpc::LuaEngine* lua_engine() { return m_lua_engine.get(); }
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++; }
const std::string& working_directory() const { return m_workingDirectory; }
void set_working_directory(const std::string& dir);
private:
Control(const Control&);
void operator = (const Control&);
std::unique_ptr<core::Manager> m_core;
std::unique_ptr<core::ViewManager> m_view_manager;
std::unique_ptr<core::DhtManager> m_dht_manager;
std::unique_ptr<ui::Root> m_ui;
std::unique_ptr<display::Manager> m_display;
std::unique_ptr<input::Manager> m_input;
std::unique_ptr<input::InputEvent> m_inputStdin;
std::unique_ptr<rpc::CommandScheduler> m_commandScheduler;
std::unique_ptr<rpc::object_storage> m_objectStorage;
std::unique_ptr<rpc::LuaEngine> m_lua_engine;
std::unique_ptr<torrent::directory_events> m_directory_events;
std::unique_ptr<utils::WatchReadyQueue> m_watch_ready_queue;
uint64_t m_tick{};
mode_t m_umask;
std::string m_workingDirectory;
torrent::system::SchedulerEntry m_task_shutdown;
torrent::system::SchedulerEntry m_task_shutdown_clear_requests;
int m_clear_requests_count{};
align_cacheline
std::atomic<bool> m_shutdown_received{};
std::atomic<bool> m_shutdown_quick{};
};
#endif
-319
View File
@@ -1,319 +0,0 @@
#include "config.h"
#include "dht_manager.h"
#include <fstream>
#include <sstream>
#include <torrent/object.h>
#include <torrent/object_stream.h>
#include <torrent/rate.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/tracker/dht_controller.h>
#include <torrent/utils/log.h>
#include "control.h"
#include "download.h"
#include "globals.h"
#include "manager.h"
#include "rpc/parse_commands.h"
#include "session/session_manager.h"
#define LT_LOG(log_fmt, ...) \
lt_log_print_subsystem(torrent::LOG_DHT_CONTROLLER, "dht_manager", log_fmt, __VA_ARGS__);
#define LT_LOG_ERROR(log_fmt, ...) \
lt_log_print_subsystem(torrent::LOG_DHT_ERROR, "dht_manager", log_fmt, __VA_ARGS__);
namespace core {
const char* DhtManager::dht_settings[dht_settings_num] = { "disable", "off", "auto", "on" };
DhtManager::~DhtManager() {
torrent::this_thread::scheduler()->erase(&m_update_timeout);
torrent::this_thread::scheduler()->erase(&m_stop_timeout);
}
void
DhtManager::load_dht_cache() {
if (m_start == dht_disable || !session_thread::manager()->is_used()) {
LT_LOG("ignoring cache file", 0);
return;
}
auto cache_filename = session_thread::manager()->path() + "rtorrent.dht_cache";
auto cache_stream = std::fstream(cache_filename.c_str(), std::ios::in | std::ios::binary);
torrent::Object cache = torrent::Object::create_map();
if (cache_stream.is_open()) {
cache_stream >> cache;
// If the cache file is corrupted we will just discard it with an
// error message.
if (cache_stream.fail()) {
LT_LOG_ERROR("cache file corrupted, discarding (path:%s)", cache_filename.c_str());
cache = torrent::Object::create_map();
} else {
LT_LOG("cache file read (path:%s)", cache_filename.c_str());
}
} else {
LT_LOG("could not open cache file (path:%s)", cache_filename.c_str());
}
torrent::runtime::network_manager()->dht_controller()->initialize(cache);
if (m_start == dht_on)
start_dht();
}
void
DhtManager::start_dht() {
torrent::this_thread::scheduler()->erase(&m_stop_timeout);
if (!torrent::runtime::network_manager()->is_dht_valid()) {
LT_LOG("server start skipped, manager is uninitialized", 0);
return;
}
if (torrent::runtime::network_manager()->is_dht_active()) {
LT_LOG("server start skipped, already active", 0);
return;
}
if (!torrent::runtime::network_manager()->dht_controller()->start()) {
m_start = dht_off;
return;
}
torrent::runtime::network_manager()->dht_controller()->reset_statistics();
m_update_timeout.slot() = std::bind(&DhtManager::update, this);
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_update_timeout, 60s);
m_dhtPrevCycle = 0;
m_dhtPrevQueriesSent = 0;
m_dhtPrevRepliesReceived = 0;
m_dhtPrevQueriesReceived = 0;
m_dhtPrevBytesUp = 0;
m_dhtPrevBytesDown = 0;
}
void
DhtManager::stop_dht() {
torrent::this_thread::scheduler()->erase(&m_update_timeout);
torrent::this_thread::scheduler()->erase(&m_stop_timeout);
if (torrent::runtime::network_manager()->is_dht_active()) {
LT_LOG("stopping server", 0);
log_statistics(true);
torrent::runtime::network_manager()->dht_controller()->stop();
}
}
void
DhtManager::save_dht_cache() {
if (!session_thread::manager()->is_used())
return;
if (!torrent::runtime::network_manager()->is_dht_valid())
return;
auto filename = session_thread::manager()->path() + "rtorrent.dht_cache";
auto filename_tmp = filename + ".new";
auto cache_file = std::fstream(filename_tmp.c_str(), std::ios::out | std::ios::trunc);
if (!cache_file.is_open())
return;
torrent::Object cache = torrent::Object::create_map();
cache_file << *torrent::runtime::network_manager()->dht_controller()->store_cache(&cache);
if (!cache_file.good())
return;
cache_file.close();
::rename(filename_tmp.c_str(), filename.c_str());
}
void
DhtManager::set_mode_by_user(const std::string& arg) {
unsigned int mode = [arg]() {
for (int i = 0; i < dht_settings_num; i++) {
if (arg == dht_settings[i])
return i;
}
throw torrent::input_error("Invalid dht mode: " + arg);
}();
m_set_by_user = true;
if (!torrent::runtime::is_network_initialized()) {
m_start = mode;
return;
}
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();
else if (m_start == dht_on)
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())
throw torrent::internal_error("DhtManager::update called with DHT inactive.");
if (m_start == dht_auto && !m_stop_timeout.is_scheduled()) {
DownloadList::const_iterator itr, end;
for (itr = control->core()->download_list()->begin(), end = control->core()->download_list()->end(); itr != end; ++itr)
if ((*itr)->download()->info()->is_active() && !(*itr)->download()->info()->is_private())
break;
if (itr == end) {
m_stop_timeout.slot() = std::bind(&DhtManager::stop_dht, this);
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_stop_timeout, 15min);
}
}
// While bootstrapping (log_statistics returns true), check every minute if it completed, otherwise update every 15 minutes.
if (log_statistics(false))
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_update_timeout, 1min);
else
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_update_timeout, 15min);
}
bool
DhtManager::log_statistics(bool force) {
auto stats = torrent::runtime::network_manager()->dht_controller()->get_statistics();
// Check for firewall problems.
if (stats.cycle > 2 && stats.queries_sent - m_dhtPrevQueriesSent > 100 && stats.queries_received == m_dhtPrevQueriesReceived) {
// We should have had clients ping us at least but have received
// nothing, that means the UDP port is probably unreachable.
if (torrent::runtime::network_manager()->is_dht_active_and_receiving_requests())
LT_LOG("listening port appears to be unreachable, no queries received", 0);
torrent::runtime::network_manager()->dht_controller()->set_receive_requests(false);
}
if (stats.queries_sent - m_dhtPrevQueriesSent > stats.num_nodes * 2 + 20 && stats.replies_received == m_dhtPrevRepliesReceived) {
// No replies to over 20 queries plus two per node we have. Probably firewalled.
if (!m_warned)
LT_LOG("listening port appears to be firewalled, no replies received", 0);
m_warned = true;
return false;
}
m_warned = false;
if (stats.queries_received > m_dhtPrevQueriesReceived)
torrent::runtime::network_manager()->dht_controller()->set_receive_requests(true);
// Nothing to log while bootstrapping, but check again every minute.
if (stats.cycle <= 1) {
m_dhtPrevCycle = stats.cycle;
return true;
}
// If bootstrap completed between now and the previous check, notify user.
if (m_dhtPrevCycle == 1) {
char buffer[128];
snprintf(buffer, sizeof(buffer), "DHT bootstrap complete, have %d nodes in %d buckets.", stats.num_nodes, stats.num_buckets);
control->core()->push_log_complete(buffer);
m_dhtPrevCycle = stats.cycle;
return false;
};
// Standard DHT statistics on first real cycle, and every 8th cycle
// afterwards (i.e. every 2 hours), or when forced.
if ((force && stats.cycle != m_dhtPrevCycle) || stats.cycle == 3 || stats.cycle > m_dhtPrevCycle + 7) {
char buffer[256];
snprintf(buffer, sizeof(buffer),
"DHT statistics: %d queries in, %d queries out, %d replies received, "
"%d known nodes in %d buckets, %d peers (highest: %d) tracked in %d torrents.",
stats.queries_received - m_dhtPrevQueriesReceived,
stats.queries_sent - m_dhtPrevQueriesSent,
stats.replies_received - m_dhtPrevRepliesReceived,
stats.num_nodes,
stats.num_buckets,
stats.num_peers,
stats.max_peers,
stats.num_trackers);
control->core()->push_log_complete(buffer);
m_dhtPrevCycle = stats.cycle;
m_dhtPrevQueriesSent = stats.queries_sent;
m_dhtPrevRepliesReceived = stats.replies_received;
m_dhtPrevQueriesReceived = stats.queries_received;
}
return false;
}
torrent::Object
DhtManager::dht_statistics() {
torrent::Object dhtStats = torrent::Object::create_map();
dhtStats.insert_key("dht", dht_settings[m_start]);
dhtStats.insert_key("active", torrent::runtime::network_manager()->is_dht_active());
dhtStats.insert_key("throttle", "");
if (torrent::runtime::network_manager()->is_dht_active()) {
auto stats = torrent::runtime::network_manager()->dht_controller()->get_statistics();
dhtStats.insert_key("cycle", stats.cycle);
dhtStats.insert_key("queries_received", stats.queries_received);
dhtStats.insert_key("queries_sent", stats.queries_sent);
dhtStats.insert_key("replies_received", stats.replies_received);
dhtStats.insert_key("errors_received", stats.errors_received);
dhtStats.insert_key("errors_caught", stats.errors_caught);
dhtStats.insert_key("bytes_read", int64_t());
dhtStats.insert_key("bytes_written", int64_t());
dhtStats.insert_key("nodes", stats.num_nodes);
dhtStats.insert_key("buckets", stats.num_buckets);
dhtStats.insert_key("peers", stats.num_peers);
dhtStats.insert_key("peers_max", stats.max_peers);
dhtStats.insert_key("torrents", stats.num_trackers);
}
return dhtStats;
}
}
-55
View File
@@ -1,55 +0,0 @@
#ifndef RTORRENT_CORE_DHT_MANAGER_H
#define RTORRENT_CORE_DHT_MANAGER_H
#include <torrent/object.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();
void save_dht_cache();
torrent::Object dht_statistics();
void start_dht();
void stop_dht();
void auto_start() { if (m_start == dht_auto) start_dht(); }
void set_mode_by_user(const std::string& arg);
void set_mode_directly(unsigned int mode);
void set_auto_if_untouched_and_has_session();
private:
static constexpr int dht_settings_num = 4;
static const char* dht_settings[dht_settings_num];
void update();
bool log_statistics(bool force);
unsigned int m_dhtPrevCycle;
unsigned int m_dhtPrevQueriesSent;
unsigned int m_dhtPrevRepliesReceived;
unsigned int m_dhtPrevQueriesReceived;
uint64_t m_dhtPrevBytesUp;
uint64_t m_dhtPrevBytesDown;
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};
};
}
#endif
-135
View File
@@ -1,135 +0,0 @@
#include "config.h"
#include "core/download.h"
#include <list>
#include <torrent/exceptions.h>
#include <torrent/rate.h>
#include <torrent/torrent.h>
#include <torrent/tracker/tracker.h>
#include <torrent/data/file.h>
#include <torrent/data/file_list.h>
#include <torrent/utils/file_stat.h>
#include "rpc/parse_commands.h"
#include "control.h"
#include "core/manager.h"
namespace core {
Download::Download(download_type d)
: m_download(d) {
m_download.info()->signal_tracker_success().push_back(std::bind(&Download::receive_tracker_msg, this, ""));
m_download.info()->signal_tracker_failed().push_back(std::bind(&Download::receive_tracker_msg, this, std::placeholders::_1));
}
Download::~Download() {
if (!m_download.is_valid())
return;
m_download = download_type();
}
uint32_t
Download::priority() {
return bencode()->get_key("rtorrent").get_key_value("priority");
}
void
Download::set_priority(uint32_t p) {
p %= 4;
// Seeding torrents get half the priority of unfinished torrents.
if (!is_done())
torrent::download_set_priority(m_download, p * p * 2);
else
torrent::download_set_priority(m_download, p * p);
bencode()->get_key("rtorrent").insert_key("priority", (int64_t)p);
}
uint32_t
Download::connection_list_size() const {
return m_download.connection_list()->size();
}
void
Download::receive_tracker_msg(std::string msg) {
if (msg.empty())
m_message = "";
else
m_message = "Tracker: [" + msg + "]";
}
float
Download::distributed_copies() const {
const uint8_t* avail = m_download.chunks_seen();
const torrent::Bitfield* bitfield = m_download.file_list()->bitfield();
if (avail == NULL)
return 0;
int minAvail = std::numeric_limits<uint8_t>::max();
int num = 0;
for (uint32_t i = m_download.file_list()->size_chunks(); i-- > 0; ) {
int totAvail = (int)avail[i] + bitfield->get(i);
if (totAvail == minAvail) {
num++;
} else if (totAvail < minAvail) {
minAvail = totAvail;
num = 1;
}
}
return minAvail + 1 - bitfield->is_all_set() - (float)num / m_download.file_list()->size_chunks();
}
void
Download::set_throttle_name(const std::string& name) {
if (m_download.info()->is_active())
throw torrent::input_error("Cannot set throttle on active download.");
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", name);
}
void
Download::set_root_directory(const std::string& path) {
// If the download is open, hashed and has completed chunks make
// sure to verify that the download files are still present.
//
// This should ensure that no one tries to set the destination
// directory 'after' moving files. In cases where the user wants to
// override this behavior the download must first be closed or
// 'd.directory_base.set' may be used.
torrent::utils::FileStat file_stat;
torrent::FileList* file_list = m_download.file_list();
if (is_hash_checked() && file_list->completed_chunks() != 0 &&
(file_list->is_multi_file() ?
!file_list->is_root_dir_created() :
!file_stat.update(file_list->front()->frozen_path().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);
file_list->set_root_dir(expand_path(path));
bencode()->get_key("rtorrent").insert_key("directory", path);
}
}
-116
View File
@@ -1,116 +0,0 @@
#ifndef RTORRENT_CORE_DOWNLOAD_H
#define RTORRENT_CORE_DOWNLOAD_H
#include <torrent/common.h>
#include <torrent/download.h>
#include <torrent/download_info.h>
#include <torrent/hash_string.h>
#include <torrent/data/file_list.h>
#include <torrent/peer/connection_list.h>
#include <torrent/tracker/wrappers.h>
#include "globals.h"
namespace core {
class Download {
public:
typedef torrent::Download download_type;
typedef torrent::FileList file_list_type;
typedef torrent::PeerList peer_list_type;
typedef torrent::TrackerList tracker_list_type;
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;
static const int variable_hashing_rehash = 3;
Download(download_type d);
~Download();
auto info() const { return m_download.info(); }
auto data() const { return m_download.data(); }
auto main() { return m_download.main(); }
bool is_open() const { return m_download.info()->is_open(); }
bool is_active() const { return m_download.info()->is_active(); }
bool is_done() const { return m_download.file_list()->is_done(); }
bool is_downloading() const { return is_active() && !is_done(); }
bool is_seeding() const { return is_active() && is_done(); }
// FIXME: Fixed a bug in libtorrent that caused is_hash_checked to
// return true when the torrent is closed. Remove this redundant
// test in the next milestone.
bool is_hash_checked() const { return is_open() && m_download.is_hash_checked(); }
bool is_hash_checking() const { return m_download.is_hash_checking(); }
bool is_hash_failed() const { return m_hashFailed; }
void set_hash_failed(bool v) { m_hashFailed = v; }
download_type* download() { return &m_download; }
const download_type* c_download() const { return &m_download; }
file_list_type* file_list() { return m_download.file_list(); }
const file_list_type* c_file_list() const { return m_download.file_list(); }
peer_list_type* peer_list() { return m_download.peer_list(); }
const peer_list_type* c_peer_list() const { return m_download.peer_list(); }
torrent::Object* bencode() { return m_download.bencode(); }
auto tracker_controller() { return m_download.tracker_controller(); }
uint32_t tracker_list_size() const { return m_download.c_tracker_controller().size(); }
auto connection_list() { return m_download.connection_list(); }
uint32_t connection_list_size() const;
const std::string& message() const { return m_message; }
void set_message(const std::string& msg) { m_message = msg; }
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_throttle_name(const std::string& name);
bool operator == (const std::string& str) const;
float distributed_copies() const;
// HACK: Choke group setting.
unsigned int group() const { return m_group; }
void set_group(unsigned int g) { m_group = g; }
private:
Download(const Download&);
void operator () (const Download&);
void receive_tracker_msg(std::string msg);
void receive_chunk_failed(uint32_t idx);
// Store the FileList instance so we can use slots etc on it.
download_type m_download;
bool m_hashFailed{};
std::string m_message;
uint32_t m_resumeFlags{default_resume_flags};
unsigned int m_group{};
};
inline bool
Download::operator == (const std::string& str) const {
return str.size() == torrent::HashString::size_data && *torrent::HashString::cast_from(str) == m_download.info()->hash();
}
}
#endif
-417
View File
@@ -1,417 +0,0 @@
#include "config.h"
#include "download_factory.h"
#include <cstdlib>
#include <fstream>
#include <functional>
#include <sstream>
#include <stdexcept>
#include <torrent/utils/log.h>
#include <torrent/utils/resume.h>
#include <torrent/object.h>
#include <torrent/object_stream.h>
#include <torrent/exceptions.h>
#include <torrent/rate.h>
#include <torrent/data/file_utils.h>
#include <torrent/net/http_stack.h>
#include <torrent/runtime/client_config.h>
#include <torrent/utils/string_manip.h>
#include "control.h"
#include "globals.h"
#include "core/download.h"
#include "core/http_queue.h"
#include "core/manager.h"
#include "rpc/parse_commands.h"
namespace core {
bool
is_network_uri(const std::string& uri) {
return
std::strncmp(uri.c_str(), "http://", 7) == 0 ||
std::strncmp(uri.c_str(), "https://", 8) == 0 ||
std::strncmp(uri.c_str(), "ftp://", 6) == 0;
}
static std::unique_ptr<torrent::Object>
download_factory_load_stream(const char* filename) {
std::fstream stream(filename, std::ios::in | std::ios::binary);
if (!stream.is_open())
return std::unique_ptr<torrent::Object>();
auto obj = std::make_unique<torrent::Object>();
stream >> *obj;
if (!stream.good())
return std::unique_ptr<torrent::Object>();
return obj;
}
bool
is_magnet_uri(const std::string& uri) {
return
std::strncmp(uri.c_str(), "magnet:?", 8) == 0;
}
DownloadFactory::DownloadFactory(Manager* m) :
m_manager(m) {
m_task_load.slot() = std::bind(&DownloadFactory::receive_load, this);
m_task_commit.slot() = std::bind(&DownloadFactory::receive_commit, this);
// m_variables["connection_leech"] = rpc::call_command("protocol.connection.leech");
// m_variables["connection_seed"] = rpc::call_command("protocol.connection.seed");
m_variables["connection_leech"] = std::string();
m_variables["connection_seed"] = std::string();
m_variables["directory"] = rpc::call_command("directory.default");
m_variables["tied_to_file"] = torrent::Object((int64_t)false);
}
DownloadFactory::~DownloadFactory() {
torrent::this_thread::scheduler()->erase(&m_task_load);
torrent::this_thread::scheduler()->erase(&m_task_commit);
delete m_object;
}
void
DownloadFactory::load(const std::string& uri) {
m_uri = uri;
torrent::this_thread::scheduler()->wait_for(&m_task_load, 0ms);
}
// This function must be called before DownloadFactory::commit().
void
DownloadFactory::load_raw_data(const std::string& input) {
if (m_stream)
throw torrent::internal_error("DownloadFactory::load*() called on an object with m_stream != NULL");
m_stream.reset(new std::stringstream(input));
m_loaded = true;
}
void
DownloadFactory::commit() {
torrent::this_thread::scheduler()->wait_for(&m_task_commit, 0ms);
}
void
DownloadFactory::receive_load() {
if (m_stream)
throw torrent::internal_error("DownloadFactory::load*() called on an object with m_stream != NULL");
if (is_network_uri(m_uri)) {
m_stream.reset(new std::stringstream);
auto done_fn = [this]() { receive_loaded(); };
auto failed_fn = [this](const std::string& error) { receive_failed(error); };
m_manager->http_queue()->insert(m_uri, m_stream, done_fn, failed_fn);
m_variables["tied_to_file"] = (int64_t)false;
return;
}
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();
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
DownloadFactory::receive_loaded() {
m_loaded = true;
if (m_commited)
receive_success();
}
void
DownloadFactory::receive_commit() {
m_commited = true;
if (m_loaded)
receive_success();
}
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());
uint32_t tracker_key;
if (rtorrent_object && rtorrent_object->has_key_value("key"))
tracker_key = rtorrent_object->get_key_value("key");
else
tracker_key = random() % (std::numeric_limits<uint32_t>::max() - 1) + 1;
Download* download = m_stream != nullptr ?
m_manager->download_list()->create(m_stream.get(), tracker_key, m_printLog) :
m_manager->download_list()->create(m_object, tracker_key, m_printLog);
m_object = NULL;
if (download == NULL) {
// core::Manager should already have added the error message to
// the log.
m_slot_finished();
return;
}
torrent::Object* root = download->bencode();
if (download->download()->info()->is_meta_download()) {
torrent::Object& meta = root->insert_key("rtorrent_meta_download", torrent::Object::create_map());
meta.insert_key("start", m_start);
meta.insert_key("print_log", m_printLog);
torrent::Object::list_type& commands = meta.insert_key("commands", torrent::Object::create_list()).as_list();
for (auto& m_command : m_commands)
commands.push_back(m_command);
}
if (m_session) {
if (rtorrent_object)
root->insert_key_move("rtorrent", *rtorrent_object);
if (libtorrent_resume_object)
root->insert_key_move("libtorrent_resume", *libtorrent_resume_object);
} else {
// We only allow session torrents to keep their
// 'rtorrent/libtorrent' sections. The "fast_resume" section
// should be safe to keep.
root->erase_key("rtorrent");
}
torrent::Object* rtorrent = &root->insert_preserve_copy("rtorrent", torrent::Object::create_map()).first->second;
torrent::Object& resumeObject = root->insert_preserve_copy("libtorrent_resume", torrent::Object::create_map()).first->second;
rtorrent->insert_key("key", download->tracker_controller().key());
initialize_rtorrent(download, rtorrent);
if (!rtorrent->has_key_string("custom1")) rtorrent->insert_key("custom1", std::string());
if (!rtorrent->has_key_string("custom2")) rtorrent->insert_key("custom2", std::string());
if (!rtorrent->has_key_string("custom3")) rtorrent->insert_key("custom3", std::string());
if (!rtorrent->has_key_string("custom4")) rtorrent->insert_key("custom4", std::string());
if (!rtorrent->has_key_string("custom5")) rtorrent->insert_key("custom5", std::string());
rpc::call_command("d.uploads_min.set", rpc::call_command("throttle.min_uploads"), rpc::make_target(download));
rpc::call_command("d.uploads_max.set", rpc::call_command("throttle.max_uploads"), rpc::make_target(download));
rpc::call_command("d.downloads_min.set", rpc::call_command("throttle.min_downloads"), rpc::make_target(download));
rpc::call_command("d.downloads_max.set", rpc::call_command("throttle.max_downloads"), rpc::make_target(download));
rpc::call_command("d.peers_min.set", rpc::call_command("throttle.min_peers.normal"), rpc::make_target(download));
rpc::call_command("d.peers_max.set", rpc::call_command("throttle.max_peers.normal"), rpc::make_target(download));
rpc::call_command("d.tracker_numwant.set", rpc::call_command("trackers.numwant"), rpc::make_target(download));
rpc::call_command("d.max_file_size.set", rpc::call_command("system.file.max_size"), rpc::make_target(download));
if (rpc::call_command_value("d.complete", rpc::make_target(download)) != 0) {
if (rpc::call_command_value("throttle.min_peers.seed") >= 0)
rpc::call_command("d.peers_min.set", rpc::call_command("throttle.min_peers.seed"), rpc::make_target(download));
if (rpc::call_command_value("throttle.max_peers.seed") >= 0)
rpc::call_command("d.peers_max.set", rpc::call_command("throttle.max_peers.seed"), rpc::make_target(download));
}
// 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);
// Check first if we already have these values set in the session
// torrent, so that it is safe to change the values.
//
// Need to also catch the exceptions.
if (rpc::call_command_value("system.file.split_size") >= 0)
torrent::file_split_all(download->download()->file_list(),
rpc::call_command_value("system.file.split_size"),
rpc::call_command_string("system.file.split_suffix"));
if (rtorrent->has_key_string("directory")) {
rpc::call_command("d.directory_base.set", rtorrent->get_key("directory"), rpc::make_target(download));
} else if (download->download()->info()->is_meta_download()) {
auto& metadata_path = control->core()->magnet_path();
if (!metadata_path.empty())
rpc::call_command("d.directory.set", metadata_path, rpc::make_target(download));
else
rpc::call_command("d.directory.set", session_thread::session_path(), rpc::make_target(download));
} else {
rpc::call_command("d.directory.set", m_variables["directory"], rpc::make_target(download));
}
if (!m_session && m_variables["tied_to_file"].as_value())
rpc::call_command("d.tied_to_file.set", m_uri.empty() ? m_variables["tied_file"] : m_uri, rpc::make_target(download));
rpc::call_command("d.peer_exchange.set", 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);
// The action of inserting might cause the torrent to be
// opened/started or such. Figure out a nicer way of handling this.
if (m_manager->download_list()->insert(download) == m_manager->download_list()->end()) {
// ATM doesn't really ever get here.
delete download;
m_slot_finished();
return;
}
// Save the info-hash just in case the commands decide to delete it.
torrent::HashString infohash = download->info()->hash();
try {
if (torrent::log_groups[torrent::LOG_TORRENT_DEBUG].valid())
log_created(download, rtorrent);
for (const auto& command : m_commands)
rpc::parse_command_multiple_std(command, rpc::make_target(download));
if (m_manager->download_list()->find(infohash) == m_manager->download_list()->end())
throw torrent::input_error("The newly created download was removed.");
if (!m_session)
rpc::call_command("d.state.set", (int64_t)m_start, rpc::make_target(download));
rpc::commands.call_catch(m_session ? "event.download.inserted_session" : "event.download.inserted_new",
rpc::make_target(download), torrent::Object(), "Download event action failed: ");
} catch (torrent::input_error& e) {
std::string msg = "Command on torrent creation failed: " + std::string(e.what());
if (m_printLog)
m_manager->push_log_std(msg);
if (m_manager->download_list()->find(infohash) != m_manager->download_list()->end()) {
// Should stop it, mark it bad. Perhaps even delete it?
download->set_hash_failed(true);
download->set_message(msg);
// m_manager->download_list()->erase(m_manager->download_list()->find(infohash.data()));
}
}
m_slot_finished();
}
void
DownloadFactory::log_created(Download* download, torrent::Object* rtorrent) {
std::stringstream dump;
dump << "info_hash = " << torrent::utils::transform_to_hex_str(download->info()->hash()) << std::endl;
dump << "session = " << (m_session ? "true" : "false") << std::endl;
if (download->download()->info()->is_meta_download())
dump << "magnet = true" << std::endl;
if (!rtorrent->has_key_string("directory"))
dump << "directory = \"" << (m_variables["directory"].is_string() ? m_variables["directory"].as_string() : std::string()) << '"' << std::endl;
else
dump << "directory_base = \"" << (rtorrent->get_key("directory").is_string() ? rtorrent->get_key("directory").as_string() : std::string()) << '"' << std::endl;
dump << "---COMMANDS---" << std::endl;
for (const auto& m_command : m_commands) {
dump << m_command << std::endl;
}
std::string dump_str = dump.str();
lt_log_print_dump(torrent::LOG_TORRENT_DEBUG, dump_str.c_str(), dump_str.size(), "Creating new download:");
}
void
DownloadFactory::receive_failed(const std::string& msg) {
// Add message to log.
if (m_printLog)
m_manager->push_log_std(msg + ": \"" + m_uri + "\"");
m_slot_finished();
}
void
DownloadFactory::initialize_rtorrent(Download* download, torrent::Object* rtorrent) {
auto cached_seconds = torrent::this_thread::cached_seconds().count();
if (!rtorrent->has_key_value("state") || rtorrent->get_key_value("state") > 1) {
rtorrent->insert_key("state", (int64_t)m_start);
rtorrent->insert_key("state_changed", cached_seconds);
rtorrent->insert_key("state_counter", int64_t());
} else if (!rtorrent->has_key_value("state_changed") ||
rtorrent->get_key_value("state_changed") > cached_seconds || rtorrent->get_key_value("state_changed") == 0 ||
!rtorrent->has_key_value("state_counter") || (uint64_t)rtorrent->get_key_value("state_counter") > (1 << 20)) {
rtorrent->insert_key("state_changed", cached_seconds);
rtorrent->insert_key("state_counter", int64_t());
}
rtorrent->insert_preserve_copy("complete", (int64_t)0);
rtorrent->insert_preserve_copy("hashing", (int64_t)Download::variable_hashing_stopped);
rtorrent->insert_preserve_copy("timestamp.started", (int64_t)0);
rtorrent->insert_preserve_copy("timestamp.finished", (int64_t)0);
rtorrent->insert_preserve_copy("tied_to_file", "");
rtorrent->insert_key("loaded_file", m_isFile ? m_uri : std::string());
if (rtorrent->has_key_value("priority"))
rpc::call_command("d.priority.set", rtorrent->get_key_value("priority") % 4, rpc::make_target(download));
else
rpc::call_command("d.priority.set", (int64_t)2, rpc::make_target(download));
if (rtorrent->has_key_value("total_uploaded"))
download->info()->mutable_up_rate()->set_total(rtorrent->get_key_value("total_uploaded"));
if (rtorrent->has_key_value("total_downloaded"))
download->info()->mutable_down_rate()->set_total(rtorrent->get_key_value("total_downloaded"));
if (rtorrent->has_key_value("chunks_done") && rtorrent->has_key_value("chunks_wanted"))
download->download()->set_chunks_done(rtorrent->get_key_value("chunks_done"), rtorrent->get_key_value("chunks_wanted"));
download->set_throttle_name(rtorrent->has_key_string("throttle_name")
? rtorrent->get_key_string("throttle_name")
: std::string());
rtorrent->insert_preserve_copy("ignore_commands", (int64_t)0);
rtorrent->insert_preserve_copy("views", torrent::Object::create_list());
rtorrent->insert_preserve_type("connection_leech", m_variables["connection_leech"]);
rtorrent->insert_preserve_type("connection_seed", m_variables["connection_seed"]);
rtorrent->insert_preserve_copy("choke_heuristics.up.leech", std::string());
rtorrent->insert_preserve_copy("choke_heuristics.up.seed", std::string());
rtorrent->insert_preserve_copy("choke_heuristics.down.leech", std::string());
rtorrent->insert_preserve_copy("choke_heuristics.down.seed", std::string());
}
}
-92
View File
@@ -1,92 +0,0 @@
// The DownloadFactory class assures that loading torrents can be done
// anywhere in the code by queueing the task. The user may change
// settings while, or even after, the torrent is loading.
#ifndef RTORRENT_CORE_DOWNLOAD_FACTORY_H
#define RTORRENT_CORE_DOWNLOAD_FACTORY_H
#include <functional>
#include <iosfwd>
#include <torrent/object.h>
#include <torrent/system/scheduler.h>
#include "http_queue.h"
namespace core {
class Download;
class Manager;
class DownloadFactory {
public:
typedef std::function<void ()> slot_void;
typedef std::vector<std::string> command_list_type;
// Do not destroy this object while it is in a HttpQueue.
DownloadFactory(Manager* m);
~DownloadFactory();
// Calling of receive_load() is delayed so you can change whatever
// you want without fear of the slots being triggered as you call
// load() or commit().
void load(const std::string& uri);
void load_raw_data(const std::string& input);
void commit();
command_list_type& commands() { return m_commands; }
torrent::Object::map_type& variables() { return m_variables; }
bool get_session() const { return m_session; }
void set_session(bool v) { m_session = v; }
bool get_start() const { return m_start; }
void set_start(bool v) { m_start = v; }
bool get_init_load() const { return m_initLoad; }
void set_init_load(bool v) { m_initLoad = v; }
bool print_log() const { return m_printLog; }
void set_print_log(bool v) { m_printLog = v; }
void slot_finished(slot_void s) { m_slot_finished = s; }
private:
void receive_load();
void receive_loaded();
void receive_commit();
void receive_success();
void receive_failed(const std::string& msg);
void log_created(Download* download, torrent::Object* rtorrent);
void initialize_rtorrent(Download* download, torrent::Object* rtorrent);
Manager* m_manager;
std::shared_ptr<std::iostream> m_stream;
torrent::Object* m_object{};
bool m_commited{};
bool m_loaded{};
std::string m_uri;
bool m_session{};
bool m_start{};
bool m_printLog{true};
bool m_isFile{};
bool m_initLoad{};
command_list_type m_commands;
torrent::Object::map_type m_variables;
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);
bool is_magnet_uri(const std::string& uri);
}
#endif
-722
View File
@@ -1,722 +0,0 @@
#include "config.h"
#include <algorithm>
#include <cstring>
#include <fstream>
#include <iostream>
#include <torrent/data/file.h>
#include <torrent/utils/resume.h>
#include <torrent/exceptions.h>
#include <torrent/download.h>
#include <torrent/hash_string.h>
#include <torrent/object.h>
#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"
#include "control.h"
#include "globals.h"
#include "manager.h"
#include "view.h"
#include "view_manager.h"
#include "core/dht_manager.h"
#include "core/download.h"
#include "core/download_list.h"
#include "session/session_manager.h"
#include "ui/root.h"
#define DL_TRIGGER_EVENT(download, event_name) \
rpc::commands.call_catch(event_name, rpc::make_target(download), torrent::Object(), "Event '" event_name "' failed: ");
namespace core {
inline void
DownloadList::check_contains([[maybe_unused]] Download* d) {
#ifdef USE_EXTRA_DEBUG
if (std::find(begin(), end(), d) == end())
throw torrent::internal_error("DownloadList::check_contains(...) failed.");
#endif
}
void
DownloadList::clear() {
int error_count = 0;
while (!empty()) {
auto download = back();
try {
close(download);
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());
error_count++;
continue;
}
}
if (error_count > 0)
throw torrent::internal_error("DownloadList::clear() failed to close or remove " + std::to_string(error_count) + " downloads.");
}
void
DownloadList::session_save() {
for (auto& download : *this)
session_thread::manager()->save_resume_download(download);
control->dht_manager()->save_dht_cache();
control->ui()->save_input_history();
}
DownloadList::iterator
DownloadList::find(const torrent::HashString& hash) {
return std::find_if(begin(), end(), [hash](Download* d) { return hash == d->info()->hash(); });
}
DownloadList::iterator
DownloadList::find_hex(const char* hash) {
if (strlen(hash) < 40)
return end();
torrent::HashString key;
if (torrent::utils::transform_from_hex(hash, hash + 40, key) != key.end())
return end();
return std::find_if(begin(), end(), [key](Download* d) { return key == d->info()->hash(); });
}
Download*
DownloadList::find_hex_ptr(const char* hash) {
iterator itr = find_hex(hash);
return itr != end() ? *itr : NULL;
}
Download*
DownloadList::create(torrent::Object* obj, uint32_t tracker_key, bool printLog) {
torrent::Download download;
try {
download = torrent::download_add(obj, tracker_key);
} catch (torrent::local_error& e) {
if (printLog)
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download: %s", e.what());
delete obj;
return NULL;
}
// There's no non-critical exceptions that should be throwable by
// the ctor, so don't catch.
return new Download(download);
}
Download*
DownloadList::create(std::istream* str, uint32_t tracker_key, bool printLog) {
torrent::Object* object = new torrent::Object;
torrent::Download download;
try {
*str >> *object;
// Don't throw input_error from here as gcc-3.3.5 produces bad
// code.
if (str->fail()) {
delete object;
if (printLog)
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download, the input is not a valid torrent.");
return NULL;
}
download = torrent::download_add(object, tracker_key);
} catch (torrent::local_error& e) {
delete object;
if (printLog)
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download: %s", e.what());
return NULL;
}
// There's no non-critical exceptions that should be throwable by
// the ctor, so don't catch.
return new Download(download);
}
DownloadList::iterator
DownloadList::insert(Download* download) {
iterator itr = base_type::insert(end(), download);
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Inserting download.");
try {
(*itr)->data()->slot_initial_hash() = std::bind(&DownloadList::hash_done, this, download);
(*itr)->data()->slot_download_done() = std::bind(&DownloadList::received_finished, this, download);
// This needs to be separated into two different calls to ensure
// the download remains in the view.
for (auto v : *control->view_manager())
v->insert(download);
for (auto v : *control->view_manager())
v->filter_download(download);
DL_TRIGGER_EVENT(*itr, "event.download.inserted");
} catch (torrent::local_error& e) {
// Should perhaps relax this, just print an error and remove the
// downloads?
throw torrent::internal_error("Caught during DownloadList::insert(...): " + std::string(e.what()));
}
return itr;
}
void
DownloadList::erase_ptr(Download* download) {
erase(std::find(begin(), end(), download));
}
DownloadList::iterator
DownloadList::erase(iterator itr) {
if (itr == end())
throw torrent::internal_error("DownloadList::erase(...) could not find download.");
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);
DL_TRIGGER_EVENT(*itr, "event.download.erased");
for (auto v : *control->view_manager())
v->erase(*itr);
torrent::download_remove(*(*itr)->download());
delete *itr;
return base_type::erase(itr);
}
bool
DownloadList::open(Download* download) {
try {
open_throw(download);
return true;
} catch (torrent::local_error& e) {
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not open download: %s", e.what());
return false;
}
}
void
DownloadList::open_throw(Download* download) {
check_contains(download);
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Opening download.");
if (download->download()->info()->is_open())
return;
int openFlags = download->resume_flags();
if (rpc::call_command_value("system.file.allocate"))
openFlags |= torrent::Download::open_enable_fallocate;
download->download()->open(openFlags);
DL_TRIGGER_EVENT(download, "event.download.opened");
}
void
DownloadList::close(Download* download) {
try {
close_throw(download);
} catch (torrent::local_error& e) {
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not close download: %s", e.what());
}
}
void
DownloadList::close_directly(Download* download) {
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download directly.");
if (download->download()->info()->is_active()) {
download->download()->stop(torrent::Download::stop_skip_tracker);
if (torrent::resume_check_target_files(*download->download(), download->download()->bencode()->get_key("libtorrent_resume")))
torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
}
if (download->download()->info()->is_open())
download->download()->close();
}
void
DownloadList::close_quick(Download* download) {
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download quickly.");
close(download);
// Make sure we cancel any tracker requests. This should rather be
// handled by some parameter to the close function, or some other
// way of giving the client more control of when STOPPED requests
// are sent.
download->download()->manual_cancel();
}
void
DownloadList::close_throw(Download* download) {
check_contains(download);
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download with throw.");
// 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.
pause(download);
// Check for is_open after pause due to hashing.
if (!download->is_open())
return;
// Save the torrent on close, this covers shutdown and if a torrent
// is manually closed which would clear the progress data. For
// better crash protection, save regulary in addition to this.
//
// Used to be in pause, but this was wrong for rehashing etc.
//
// Reconsider this save. Should be done explicitly when shutting down.
//control->core()->download_store()->save(download);
download->download()->close();
if (!download->is_hash_failed() && rpc::call_command_value("d.hashing", rpc::make_target(download)) != Download::variable_hashing_stopped)
throw torrent::internal_error("DownloadList::close_throw(...) called but we're going into a hashing loop.");
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
DL_TRIGGER_EVENT(download, "event.download.closed");
}
void
DownloadList::resume(Download* download, int flags) {
check_contains(download);
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Resuming download: flags:%0x.", flags);
try {
if (download->download()->info()->is_active())
return;
rpc::parse_command_single(rpc::make_target(download), "view.set_visible=active");
// We need to make sure the flags aren't reset if someone decideds
// to call resume() while it is hashing, etc.
if (download->resume_flags() == Download::default_resume_flags)
download->set_resume_flags(flags);
// Manual or end-of-download rehashing clears the resume data so
// we can just start the hashing again without clearing it again.
//
// It is also assumed the is_hash_checked flag gets cleared when
// 'hashing' was set.
if (!download->is_hash_checked()) {
// If the hash failed flag wasn't cleared then hashing won't be
// initiated.
if (download->is_hash_failed())
return;
if (rpc::call_command_value("d.hashing", rpc::make_target(download)) == Download::variable_hashing_stopped)
rpc::call_command("d.hashing.set", Download::variable_hashing_initial, rpc::make_target(download));
DL_TRIGGER_EVENT(download, "event.download.hash_queued");
return;
}
// This will never actually do anything due to the above hash check.
// open_throw(download);
auto cached_seconds = torrent::this_thread::cached_seconds().count();
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)) + 1, rpc::make_target(download));
if (download->is_done()) {
torrent::Object conn_current = rpc::call_command("d.connection_seed", torrent::Object(), rpc::make_target(download));
torrent::Object choke_up = rpc::call_command("d.up.choke_heuristics.seed", torrent::Object(), rpc::make_target(download));
torrent::Object choke_down = rpc::call_command("d.down.choke_heuristics.seed", torrent::Object(), rpc::make_target(download));
if (conn_current.is_string_empty()) conn_current = rpc::call_command("protocol.connection.seed", torrent::Object(), rpc::make_target(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));
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));
} else {
torrent::Object conn_current = rpc::call_command("d.connection_leech", torrent::Object(), rpc::make_target(download));
torrent::Object choke_up = rpc::call_command("d.up.choke_heuristics.leech", torrent::Object(), rpc::make_target(download));
torrent::Object choke_down = rpc::call_command("d.down.choke_heuristics.leech", torrent::Object(), rpc::make_target(download));
if (conn_current.is_string_empty()) conn_current = rpc::call_command("protocol.connection.leech", torrent::Object(), rpc::make_target(download));
if (choke_up.is_string_empty()) choke_up = rpc::call_command("protocol.choke_heuristics.up.leech", torrent::Object(), rpc::make_target(download));
if (choke_down.is_string_empty()) choke_down = rpc::call_command("protocol.choke_heuristics.down.leech", torrent::Object(), rpc::make_target(download));
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));
// For the moment, clear the resume data so we force hash-check
// on non-complete downloads after a crash. This shouldn't be
// needed, but for some reason linux 2.6 is very lazy about
// updating mtime.
//
// Disabling this due to the new resume code.
// torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"), true);
}
// If the DHT server is set to auto, start it now.
if (!download->download()->info()->is_private())
control->dht_manager()->auto_start();
// Update the priority to ensure it has the correct
// seeding/unfinished modifiers.
download->set_priority(download->priority());
download->download()->start(download->resume_flags());
download->set_resume_flags(Download::default_resume_flags);
DL_TRIGGER_EVENT(download, "event.download.resumed");
} catch (torrent::local_error& e) {
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not resume download: %s", e.what());
}
}
void
DownloadList::pause(Download* download, int flags) {
check_contains(download);
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Pausing download: flags:%0x.", flags);
try {
download->set_resume_flags(Download::default_resume_flags);
rpc::parse_command_single(rpc::make_target(download), "view.set_not_visible=active");
// Always clear hashing on pause. When a hashing request is added,
// it should have cleared the hash resume data.
if (rpc::call_command_value("d.hashing", rpc::make_target(download)) != Download::variable_hashing_stopped) {
download->download()->hash_stop();
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 (!download->download()->info()->is_active())
return;
download->download()->stop(flags);
torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
// TODO: This is actually for pause, not stop... And doesn't get
// called when the download isn't active, but was in the 'started'
// view.
DL_TRIGGER_EVENT(download, "event.download.paused");
auto cached_seconds = torrent::this_thread::cached_seconds().count();
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));
// Save the state after all the slots, etc have been called so we
// include the modifications they may make.
//control->core()->download_store()->save(download);
} catch (torrent::local_error& e) {
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not pause download: %s", e.what());
}
}
void
DownloadList::check_hash(Download* download) {
check_contains(download);
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Checking hash.");
try {
if (rpc::call_command_value("d.hashing", rpc::make_target(download)) != Download::variable_hashing_stopped)
return;
hash_queue(download, Download::variable_hashing_rehash);
} catch (torrent::local_error& e) {
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not check hash: %s", e.what());
}
}
void
DownloadList::hash_done(Download* download) {
check_contains(download);
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Hash done.");
if (download->is_hash_checking() || download->is_active())
throw torrent::internal_error("DownloadList::hash_done(...) download in invalid state.");
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;
}
// Need to find some sane conditional here. Can we check the total
// downloaded to ensure something was transferred, thus we didn't
// just hash an already completed torrent with lacking session data?
//
// Perhaps we should use a seperate variable or state, and check
// that. Thus we can bork the download if the hash check doesn't
// confirm all the data, avoiding large BW usage on f.ex. the
// ReiserFS bug with >4GB files.
int64_t hashing = rpc::call_command_value("d.hashing", rpc::make_target(download));
rpc::call_command_set_value("d.hashing.set", Download::variable_hashing_stopped, rpc::make_target(download));
if (download->is_done() && download->download()->info()->is_meta_download())
return process_meta_download(download);
switch (hashing) {
case Download::variable_hashing_initial:
case Download::variable_hashing_rehash:
// Normal re/hashing.
// 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));
download->set_message("Download registered as completed, but hash check returned unfinished chunks.");
}
// Save resume data so we update time-stamps and priorities if
// they were invalid/changed while loading/hashing.
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)
resume(download, download->resume_flags());
break;
case Download::variable_hashing_last:
if (download->is_done()) {
confirm_finished(download);
} else {
download->set_message("Hash check on download completion found bad chunks.");
lt_log_print(torrent::LOG_TORRENT_ERROR, "Hash check on download completion found bad chunks.");
DL_TRIGGER_EVENT(download, "event.download.hash_final_failed");
}
// TODO: Should we skip the 'hash_done' event here?
return;
case Download::variable_hashing_stopped:
default:
// Either an error or someone wrote to the hashing variable...
download->set_message("Hash check completed but the \"hashing\" variable is in an invalid state.");
return;
}
DL_TRIGGER_EVENT(download, "event.download.hash_done");
}
void
DownloadList::hash_queue(Download* download, int type) {
check_contains(download);
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Hash queue.");
if (rpc::call_command_value("d.hashing", rpc::make_target(download)) != Download::variable_hashing_stopped)
throw torrent::internal_error("DownloadList::hash_queue(...) hashing already queued.");
// HACK
if (download->is_open()) {
pause(download, torrent::Download::stop_skip_tracker);
download->download()->close();
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
DL_TRIGGER_EVENT(download, "event.download.closed");
}
torrent::resume_clear_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
download->set_hash_failed(false);
rpc::call_command_set_value("d.hashing.set", type, rpc::make_target(download));
if (download->is_open())
throw torrent::internal_error("DownloadList::hash_clear(...) download still open.");
// If any more stuff is added here, make sure resume etc are still
// correct.
DL_TRIGGER_EVENT(download, "event.download.hash_queued");
}
void
DownloadList::received_finished(Download* download) {
check_contains(download);
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Received finished.");
if (rpc::call_command_value("pieces.hash.on_completion"))
// Set some 'checking_finished_thingie' variable to make hash_done
// trigger correctly, also so it can bork on missing data.
hash_queue(download, Download::variable_hashing_last);
else
confirm_finished(download);
}
// The download must be open when we call this function.
void
DownloadList::confirm_finished(Download* download) {
check_contains(download);
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Confirming finished.");
if (download->download()->info()->is_meta_download())
return process_meta_download(download);
rpc::call_command("d.complete.set", (int64_t)1, rpc::make_target(download));
// Clean up these settings:
torrent::Object conn_current = rpc::call_command("d.connection_seed", torrent::Object(), rpc::make_target(download));
torrent::Object choke_up = rpc::call_command("d.up.choke_heuristics.seed", torrent::Object(), rpc::make_target(download));
torrent::Object choke_down = rpc::call_command("d.down.choke_heuristics.seed", torrent::Object(), rpc::make_target(download));
if (conn_current.is_string_empty()) conn_current = rpc::call_command("protocol.connection.seed", torrent::Object(), rpc::make_target(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));
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));
download->set_priority(download->priority());
if (rpc::call_command_value("d.peers_min", rpc::make_target(download)) == rpc::call_command_value("throttle.min_peers.normal") &&
rpc::call_command_value("throttle.min_peers.seed") >= 0)
rpc::call_command("d.peers_min.set", rpc::call_command("throttle.min_peers.seed"), rpc::make_target(download));
if (rpc::call_command_value("d.peers_max", rpc::make_target(download)) == rpc::call_command_value("throttle.max_peers.normal") &&
rpc::call_command_value("throttle.max_peers.seed") >= 0)
rpc::call_command("d.peers_max.set", rpc::call_command("throttle.max_peers.seed"), rpc::make_target(download));
// Do this before the slots are called in case one of them closes
// the download.
//
// Obsolete.
if (!download->is_active() && rpc::call_command_value("session.on_completion") != 0)
session_thread::manager()->save_resume_download(download);
// Send the completed request before resuming so we don't reset the
// up/downloaded baseline.
download->download()->send_completed();
// Save the hash in case the finished event erases it.
torrent::HashString infohash = download->info()->hash();
DL_TRIGGER_EVENT(download, "event.download.finished");
if (find(infohash) == end())
return;
// if (download->resume_flags() != Download::default_resume_flags)
// throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != Download::default_resume_flags.");
// See #1292.
//
// Just reset the value for the moment. If a torrent finishes while
// others are hashing, or some other situtation that causes resume
// flag to change could cause the state to be invalid.
//
// TODO: Add a check when setting the flags to see if the torrent is
// being hashed.
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,
torrent::Download::start_no_create |
torrent::Download::start_skip_tracker |
torrent::Download::start_keep_baseline);
}
void
DownloadList::process_meta_download(Download* download) {
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Processing meta download.");
rpc::call_command("d.stop", torrent::Object(), rpc::make_target(download));
rpc::call_command("d.close", torrent::Object(), rpc::make_target(download));
std::string metafile = (*download->file_list()->begin())->frozen_path().str();
std::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());
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.
bencode->insert_key("rtorrent_meta_download", torrent::Object()).swap(download->bencode()->get_key("rtorrent_meta_download"));
if (download->bencode()->has_key("announce"))
bencode->insert_key("announce", torrent::Object()).swap(download->bencode()->get_key("announce"));
if (download->bencode()->has_key("announce-list"))
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);
}
}
-136
View File
@@ -1,136 +0,0 @@
#ifndef RTORRENT_CORE_DOWNLOAD_LIST_H
#define RTORRENT_CORE_DOWNLOAD_LIST_H
#include <iosfwd>
#include <list>
#include <string>
namespace torrent {
class HashString;
class Object;
}
namespace core {
class Download;
// Container for all downloads. Add slots to the slot maps to cause
// some action to be taken when the torrent changes states. Don't
// change the states from outside of core.
//
// Fix apply_on_ratio if the base_type is changed.
class DownloadList : private std::list<Download*> {
public:
typedef std::list<Download*> base_type;
using base_type::iterator;
using base_type::const_iterator;
using base_type::reverse_iterator;
using base_type::const_reverse_iterator;
using base_type::value_type;
using base_type::pointer;
using base_type::begin;
using base_type::end;
using base_type::rbegin;
using base_type::rend;
using base_type::empty;
using base_type::size;
DownloadList() = default;
void clear();
void session_save();
iterator find(const torrent::HashString& hash);
iterator find_hex(const char* hash);
Download* find_hex_ptr(const char* hash);
// Might move this to DownloadFactory.
Download* create(std::istream* str, uint32_t tracker_key, bool printLog);
Download* create(torrent::Object* obj, uint32_t tracker_key, bool printLog);
iterator insert(Download* d);
void erase_ptr(Download* d);
iterator erase(iterator itr);
//void save(Download* d);
bool open(Download* d);
void open_throw(Download* d);
void close(Download* d);
void close_directly(Download* d);
void close_quick(Download* d);
void close_throw(Download* d);
void resume(Download* d, int flags = 0);
void pause(Download* d, int flags = 0);
void resume_default(Download* d) { resume(d); }
void pause_default(Download* d) { pause(d); }
void check_hash(Download* d);
enum {
D_SLOTS_INSERT,
D_SLOTS_ERASE,
D_SLOTS_OPEN,
D_SLOTS_CLOSE,
D_SLOTS_START,
D_SLOTS_STOP,
D_SLOTS_HASH_QUEUED,
D_SLOTS_HASH_REMOVED,
D_SLOTS_HASH_DONE,
D_SLOTS_FINISHED,
SLOTS_MAX_SIZE
};
static const char* slot_name(int m) {
switch(m) {
case D_SLOTS_INSERT: return "event.download.inserted";
case D_SLOTS_ERASE: return "event.download.erased";
case D_SLOTS_OPEN: return "event.download.opened";
case D_SLOTS_CLOSE: return "event.download.closed";
case D_SLOTS_START: return "event.download.resumed";
case D_SLOTS_STOP: return "event.download.paused";
case D_SLOTS_HASH_QUEUED: return "event.download.hash_queued";
case D_SLOTS_HASH_REMOVED: return "event.download.hash_removed";
case D_SLOTS_HASH_DONE: return "event.download.hash_done";
case D_SLOTS_FINISHED: return "event.download.finished";
default: return "BORK";
}
}
// The finished slots will be called when an active download with
// "finished" == 0 performs a hash check which returns a done
// torrent.
//
// But how to avoid sending 'completed' messages to the tracker?
// Also we need to handle cases when a hashing torrent starts up
// after a shutdown.
private:
DownloadList(const DownloadList&);
void operator = (const DownloadList&);
void hash_done(Download* d);
void hash_queue(Download* d, int type);
inline void check_contains(Download* d);
void received_finished(Download* d);
void confirm_finished(Download* d);
void process_meta_download(Download* d);
};
}
#endif
-48
View File
@@ -1,48 +0,0 @@
#include "config.h"
#include "http_queue.h"
#include <torrent/common.h>
#include <torrent/net/http_get.h>
#include <torrent/net/http_stack.h>
namespace core {
HttpQueue::iterator
HttpQueue::insert(const std::string& url, std::shared_ptr<std::ostream> stream,
std::function<void()> done_fn, std::function<void(const std::string&)> failed_fn) {
auto itr = base_type::insert(end(), torrent::net::HttpGet(url, stream));
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(torrent::this_thread::thread(), std::move(done_fn));
itr->add_done_slot(torrent::this_thread::thread(), [this, itr]() { erase(itr); });
itr->add_failed_slot(torrent::this_thread::thread(), std::move(failed_fn));
itr->add_failed_slot(torrent::this_thread::thread(), [this, itr](auto) { erase(itr); });
torrent::net_thread::http_stack()->start_get(*itr);
return itr;
}
void
HttpQueue::erase(iterator signal_itr) {
for (const auto& slot : m_signal_erase)
slot(*signal_itr);
signal_itr->close_and_keep_callbacks();
base_type::erase(signal_itr);
}
void
HttpQueue::clear() {
while (!empty())
erase(begin());
}
}
-58
View File
@@ -1,58 +0,0 @@
#ifndef RTORRENT_CORE_HTTP_QUEUE_H
#define RTORRENT_CORE_HTTP_QUEUE_H
#include <functional>
#include <iosfwd>
#include <memory>
#include <list>
#include <string>
#include <torrent/net/http_get.h>
namespace core {
// TODO: Remove this.
class HttpQueue : private std::list<torrent::net::HttpGet> {
public:
using base_type = std::list<torrent::net::HttpGet>;
using slot_curl_get = std::function<void (torrent::net::HttpGet)>;
using signal_curl_get = std::list<slot_curl_get>;
using base_type::iterator;
using base_type::const_iterator;
using base_type::reverse_iterator;
using base_type::const_reverse_iterator;
using base_type::begin;
using base_type::end;
using base_type::rbegin;
using base_type::rend;
using base_type::empty;
using base_type::size;
HttpQueue() = default;
~HttpQueue() { clear(); }
// Note that any slots connected to the CurlGet signals must be
// pushed in front of the erase slot added by HttpQueue::insert.
//
// Consider adding a flag to indicate whetever HttpQueue should
// delete the stream.
iterator insert(const std::string& url, std::shared_ptr<std::ostream> stream,
std::function<void()> done_fn, std::function<void(const std::string&)> failed_fn);
void erase(iterator itr);
void clear();
signal_curl_get& signal_insert() { return m_signal_insert; }
signal_curl_get& signal_erase() { return m_signal_erase; }
private:
signal_curl_get m_signal_insert;
signal_curl_get m_signal_erase;
};
}
#endif
-446
View File
@@ -1,446 +0,0 @@
#include "config.h"
#include "core/manager.h"
#include <cstdio>
#include <cstring>
#include <fstream>
#include <sstream>
#include <unistd.h>
#include <sys/select.h>
#include <fnmatch.h>
#include <torrent/utils/resume.h>
#include <torrent/object.h>
#include <torrent/exceptions.h>
#include <torrent/object_stream.h>
#include <torrent/throttle.h>
#include <torrent/net/http_stack.h>
#include <torrent/net/socket_address.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/utils/log.h>
#include <torrent/utils/string_manip.h>
#include "rpc/parse_commands.h"
#include "utils/directory.h"
#include "utils/base64.h"
#include "utils/file_status_cache.h"
#include "globals.h"
#include "control.h"
#include "core/download.h"
#include "core/download_factory.h"
#include "core/http_queue.h"
#include "core/view.h"
#include <torrent/runtime/client_config.h>
namespace core {
const int Manager::create_start;
const int Manager::create_tied;
const int Manager::create_quiet;
const int Manager::create_raw_data;
void
Manager::push_log(const char* msg) {
m_log_important->lock_and_push_log(msg, strlen(msg), 0);
m_log_complete->lock_and_push_log(msg, strlen(msg), 0);
}
Manager::Manager()
: m_log_important(torrent::log_open_log_buffer("important")),
m_log_complete(torrent::log_open_log_buffer("complete")) {
m_download_list = std::make_unique<DownloadList>();
m_file_status_cache = std::make_unique<FileStatusCache>();
m_http_queue = std::make_unique<HttpQueue>();
torrent::Throttle* unthrottled = torrent::Throttle::create_throttle();
unthrottled->set_max_rate(0);
m_throttles["NULL"] = std::make_pair(unthrottled, unthrottled);
}
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)
throw torrent::internal_error("Manager::set_hashing_view(...) received nullptr or is already set.");
m_hashingView = v;
m_hashingView->signal_changed().push_back(std::bind(&Manager::receive_hashing_changed, this));
}
ThrottlePair
Manager::get_throttle(const std::string& name) {
auto itr = m_throttles.find(name);
auto throttles = (itr == m_throttles.end() ? ThrottlePair(nullptr, nullptr) : itr->second);
if (throttles.first == nullptr)
throttles.first = torrent::up_throttle_global();
if (throttles.second == nullptr)
throttles.second = torrent::down_throttle_global();
return throttles;
}
int64_t
Manager::retrieve_throttle_value(const torrent::Object::string_type& name, bool rate, bool up) {
ThrottleMap::iterator itr = throttles().find(name);
if (itr == throttles().end()) {
return (int64_t)-1;
} else {
torrent::Throttle* throttle = up ? itr->second.first : itr->second.second;
// check whether the actual up/down throttle exist (one of the pair can be missing)
if (throttle == nullptr)
return (int64_t)-1;
int64_t throttle_max = (int64_t)throttle->max_rate();
if (rate) {
if (throttle_max > 0)
return (int64_t)throttle->rate()->rate();
else
return (int64_t)-1;
} else {
return throttle_max;
}
}
}
void
Manager::set_magnet_path(const std::string& path) {
if (path.empty())
m_magnet_path.clear();
else if (path.back() == '/')
m_magnet_path = path;
else
m_magnet_path = path + '/';
}
void
Manager::cleanup() {
// Need to disconnect log signals? Not really since we won't receive
// any more.
m_download_list->clear();
torrent::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::receive_http_failed(std::string msg) {
push_log_std("Http download error: \"" + msg + "\"");
}
bool
is_data_uri(const std::string& uri) {
return std::strncmp(uri.c_str(), "data:", 5) == 0;
}
std::string
decode_data_uri(const std::string& uri) {
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));
}
void
Manager::try_create_download(const std::string& uri, int flags, const command_list_type& commands) {
// If the path was attempted loaded before, skip it.
if ((flags & create_tied) &&
!(flags & create_raw_data) &&
!is_network_uri(uri) &&
!is_magnet_uri(uri) &&
!is_data_uri(uri) &&
!file_status_cache()->insert(uri))
return;
// Adding download.
DownloadFactory* f = new 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; });
if (is_data_uri(uri)) {
// Allow the use of data URIs, primarily for JSON-RPC which
// doesn't have a defined mechanism for binary data
f->load_raw_data(decode_data_uri(uri));
f->variables()["tied_to_file"] = (int64_t)false;
} else if (flags & create_raw_data) {
f->load_raw_data(uri);
} else {
f->load(uri);
}
f->commit();
}
void
Manager::try_create_download_from_meta_download(torrent::Object* bencode, const std::string& metafile) {
DownloadFactory* f = new DownloadFactory(this);
f->variables()["tied_to_file"] = (int64_t)true;
f->variables()["tied_file"] = metafile;
torrent::Object& meta = bencode->get_key("rtorrent_meta_download");
torrent::Object::list_type& commands = meta.get_key_list("commands");
for (const auto& command : commands)
f->commands().insert(f->commands().end(), command.as_string());
f->set_start(meta.get_key_value("start"));
f->set_print_log(meta.get_key_value("print_log"));
f->slot_finished([f]() { delete f; });
// Bit of a waste to create the bencode repesentation here
// only to have the DownloadFactory decode it.
std::stringstream s;
s.imbue(std::locale::classic());
s << *bencode;
f->load_raw_data(s.str());
f->commit();
}
utils::Directory
path_expand_transform(std::string path, const utils::directory_entry& entry) {
return path + entry.s_name;
}
namespace {
// Consider rewritting such that m_seq is replaced by first/last.
template <typename Sequence>
class split_iterator_t {
public:
typedef typename Sequence::const_iterator const_iterator;
typedef typename Sequence::value_type value_type;
split_iterator_t() {}
split_iterator_t(const Sequence& seq, value_type delim) :
m_seq(&seq),
m_delim(delim),
m_pos(seq.begin()),
m_next(std::find(seq.begin(), seq.end(), delim)) {
}
Sequence operator * () { return Sequence(m_pos, m_next); }
split_iterator_t& operator ++ () {
m_pos = m_next;
if (m_pos == m_seq->end())
return *this;
m_pos++;
m_next = std::find(m_pos, m_seq->end(), m_delim);
return *this;
}
bool operator == (const split_iterator_t&) const { return m_pos == m_seq->end(); }
bool operator != (const split_iterator_t&) const { return m_pos != m_seq->end(); }
private:
const Sequence* m_seq;
value_type m_delim;
const_iterator m_pos;
const_iterator m_next;
};
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence& seq, typename Sequence::value_type delim) {
return split_iterator_t<Sequence>(seq, delim);
}
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence&) {
return split_iterator_t<Sequence>();
}
}
// Move this somewhere better.
void
path_expand(std::vector<std::string>* paths, const std::string& pattern) {
std::vector<utils::Directory> currentCache;
std::vector<utils::Directory> nextCache;
auto first = split_iterator(pattern, '/');
auto last = split_iterator(pattern);
if (first == last)
return;
// Check for initial '/' that indicates the root.
if ((*first).empty()) {
currentCache.push_back(utils::Directory("/"));
++first;
} else if (torrent::utils::trim_spaces_str(*first) == "~") {
currentCache.push_back(utils::Directory("~"));
++first;
} else {
currentCache.push_back(utils::Directory("."));
}
// Might be an idea to use depth-first search instead.
for (; first != last; ++first) {
const std::string& pattern = *first;
if (pattern.empty())
continue;
// Special case for ".."?
for (auto& itr : currentCache) {
// Only include filenames starting with '.' if the pattern
// starts with the same.
itr.update((pattern[0] != '.') ? utils::Directory::update_hide_dot : 0);
itr.erase(std::remove_if(itr.begin(), itr.end(), [&pattern](const utils::directory_entry& entry) { return fnmatch(pattern.c_str(), entry.s_name.c_str(), 0) != 0; }), itr.end());
for (const auto& cache : itr)
nextCache.push_back(path_expand_transform(itr.path() + (itr.path() == "/" ? "" : "/"), cache));
}
currentCache.clear();
currentCache.swap(nextCache);
}
for (const auto& cache : currentCache)
paths->push_back(cache.path());
}
bool
manager_equal_tied(const std::string& path, Download* download) {
return path == rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
}
void
Manager::try_create_download_expand(const std::string& uri, int flags, command_list_type commands) {
if (flags & create_raw_data) {
try_create_download(uri, flags, commands);
return;
}
std::vector<std::string> paths;
paths.reserve(256);
path_expand(&paths, uri);
if (!paths.empty())
for (auto& path : paths)
try_create_download(path, flags, commands);
else
try_create_download(uri, flags, commands);
}
// DownloadList's hashing related functions don't actually start the
// hashing, it only reacts to events. This functions checks the
// hashing view and starts hashing if nessesary.
void
Manager::receive_hashing_changed() {
bool foundHashing = std::any_of(m_hashingView->begin_visible(), m_hashingView->end_visible(),
std::mem_fn(&Download::is_hash_checking));
// Try quick hashing all those with hashing == initial, set them to
// something else when failed.
for (View::iterator itr = m_hashingView->begin_visible(), last = m_hashingView->end_visible(); itr != last; ++itr) {
if ((*itr)->is_hash_checked())
throw torrent::internal_error("core::Manager::receive_hashing_changed() (*itr)->is_hash_checked().");
if ((*itr)->is_hash_checking() || (*itr)->is_hash_failed())
continue;
bool tryQuick =
rpc::call_command_value("d.hashing", rpc::make_target(*itr)) == Download::variable_hashing_initial &&
(*itr)->download()->file_list()->bitfield()->empty();
if (!tryQuick && foundHashing)
continue;
try {
m_download_list->open_throw(*itr);
// Since the bitfield is allocated on loading of resume load or
// hash start, and unallocated on close, we know that if it it
// not empty then we have already loaded any existing resume
// data.
if ((*itr)->download()->file_list()->bitfield()->empty())
torrent::resume_load_progress(*(*itr)->download(), (*itr)->download()->bencode()->get_key("libtorrent_resume"));
if (tryQuick) {
if ((*itr)->download()->hash_check(true))
continue;
(*itr)->download()->hash_stop();
if (foundHashing) {
rpc::call_command_set_value("d.hashing.set", Download::variable_hashing_rehash, rpc::make_target(*itr));
continue;
}
}
(*itr)->download()->hash_check(false);
foundHashing = true;
} catch (torrent::local_error& e) {
if (tryQuick) {
// Make sure we don't repeat the quick hashing.
rpc::call_command_set_value("d.hashing.set", Download::variable_hashing_rehash, rpc::make_target(*itr));
} 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());
}
}
}
}
}
-111
View File
@@ -1,111 +0,0 @@
#ifndef RTORRENT_CORE_MANAGER_H
#define RTORRENT_CORE_MANAGER_H
#include <iosfwd>
#include <memory>
#include <vector>
#include <torrent/utils/log_buffer.h>
#include <torrent/object.h>
#include "download_list.h"
#include "range_map.h"
namespace torrent {
class Bencode;
}
namespace utils {
class FileStatusCache;
}
namespace core {
class HttpQueue;
using ThrottlePair = std::pair<torrent::Throttle*, torrent::Throttle*>;
using ThrottleMap = std::map<std::string, ThrottlePair>;
class View;
class Manager {
public:
typedef DownloadList::iterator DListItr;
typedef utils::FileStatusCache FileStatusCache;
Manager();
~Manager();
bool is_download_shutdown_completed();
DownloadList* download_list() { return m_download_list.get(); }
FileStatusCache* file_status_cache() { return m_file_status_cache.get(); }
HttpQueue* http_queue() { return m_http_queue.get(); }
View* hashing_view() { return m_hashingView; }
void set_hashing_view(View* v);
auto* log_important() { return m_log_important.get(); }
auto* log_complete() { return m_log_complete.get(); }
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);
void cleanup();
const std::string& magnet_path();
void set_magnet_path(const std::string& path);
void shutdown(bool force);
void push_log(const char* msg);
void push_log_std(const std::string& msg) { m_log_important->lock_and_push_log(msg.c_str(), msg.size(), 0); m_log_complete->lock_and_push_log(msg.c_str(), msg.size(), 0); }
void push_log_complete(const std::string& msg) { m_log_complete->lock_and_push_log(msg.c_str(), msg.size(), 0); }
void handshake_log(const sockaddr* sa, int msg, int err, const torrent::HashString* hash);
static const int create_start = 0x1;
static const int create_tied = 0x2;
static const int create_quiet = 0x4;
static const int create_raw_data = 0x8;
typedef std::vector<std::string> command_list_type;
// 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);
private:
void create_http(const std::string& uri);
void create_final(std::istream* s);
void initialize_bencode(Download* d);
void receive_http_failed(std::string msg);
void receive_hashing_changed();
std::unique_ptr<DownloadList> m_download_list;
std::unique_ptr<FileStatusCache> m_file_status_cache;
std::unique_ptr<HttpQueue> m_http_queue;
View* m_hashingView{};
ThrottleMap m_throttles;
torrent::log_buffer_ptr m_log_important;
torrent::log_buffer_ptr m_log_complete;
std::string m_magnet_path;
};
// Meh, cleanup.
extern void receive_tracker_dump(const std::string& url, const char* data, size_t size);
inline const std::string& Manager::magnet_path() { return m_magnet_path; }
}
#endif
-199
View File
@@ -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

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