Since recent versions, rtorrent
can be run as a daemon without having to rely on terminal multiplexers such as tmux
to background the process and to simultaneously be able to access the instance.
First, create a directory to store state data by rtorrent
such as /opt/rtorrent
and place a configuration file within the directory. Perhaps the following should do well:
directory.default.set = DEFAULT_DIRECTORY session.path.set = /opt/rtorrent network.bind_address.set = 0.0.0.0 network.port_range.set = 62296-62296 network.port_random.set = no network.send_buffer.size.set = 128M pieces.hash.on_completion.set = no pieces.preload.type.set = 1 pieces.preload.min_size.set = 262144 pieces.preload.min_rate.set = 5120 pieces.memory.max.set = 1024M trackers.use_udp.set = yes protocol.encryption.set = allow_incoming,try_outgoing,enable_retry protocol.pex.set = yes dht.mode.set = auto dht.port.set = 6881 system.daemon.set = true network.scgi.open_port = "0.0.0.0:5000"
where:
DEFAULT_DIRECTORY
should be set to the default torrent saving and sharing directory
and then place a SystemD service file at /etc/systemd/system/rtorrent.service
with the following contents:
[Unit] Description=rTorrent After=network.target [Service] ExecStartPre=/usr/bin/rm -rf /opt/rtorrent/rtorrent.lock ExecStopPost=/usr/bin/rm -rf /opt/rtorrent/rtorrent.lock ExecStart=/usr/bin/rtorrent -n -o import=/opt/rtorrent/rtorrent.rc Restart=always RestartSec=10 StandardOutput=syslog StandardError=syslog SyslogIdentifier=rtorrent User=root Group=root Environment=PATH=/usr/bin/:/usr/local/bin/ WorkingDirectory=/opt/rtorrent [Install] WantedBy=multi-user.target
Unfortunately, rtorrent is slow to remove the lock file and typically will not start unless the lock file has been removed such that the SystemD script uses ExecStartPre
and ExecStopPost
to clean up the lock file before and after starting, respectively stopping rtorrent
.
rtorrent now exposes port 5000
on all interfaces (0.0.0.0
) and can be controlled via SCGI with interfaces such as Flood.
Using Unix tools, Simple Common Gateway Interface (SCGI) commands can be sent to the exposed SCGI rTorrent port. First, an SCGI payload has to be created that is defined within RFC 2616 compliant-header combining with some parameters that represent an SCGI header. For example, the following is an SCGI payload:
63: CONTENT_LENGTH⌴62⌴ SCGI⌴1⌴ REQUEST_METHOD⌴POST⌴ REQUEST_URI⌴/RPC2⌴ , <methodCall> <methodName>session.name</methodName> </methodCall>
where the spaces have been marked with the under-caret "⌴". The under-caret "⌴" has to be converted to null (ASCI 0) and any newlines or spaces suppressed for the final output payload that is sent.
The payload consists in header, comprised of the parameters CONTENT_LENGTH
, SCGI
, REQUEST_METHOD
and REQUEST_URI
along with their respective options, followed by a comma and the actual data to be sent to service (in this case, the methodCall
tag is indicative of an XML-RPC call).
The parameters CONTENT_LENGTH
has to be set to the length of the payload being sent - in this case, CONTENT_LENGTH
is set to representing the length of the XML after the comma without counting spaces (the final payload will have the spaces stripped).
That being said, using the following tools:
sed
,tr
,netcat
,grep
,xmlstarlet
a one-liner command can be built that will retrieve the session name from rTorrent consisting in the hostname followed by semicolon and the port that rTorrent is listening on.
cat <<EOF | \ sed -E 's/[ ]{2,}//g' | \ tr ' ' \\0 | \ tr -d '\n' | \ netcat localhost 5000 | \ grep -Pzo "(?s)<methodResponse>.+?<\/methodResponse>" | \ xmlstarlet select -t -v /methodResponse/params/param[1]/value/string 63: CONTENT_LENGTH 62 SCGI 1 REQUEST_METHOD POST REQUEST_URI /RPC2 , <methodCall> <methodName>session.name</methodName> </methodCall> EOF
Again, note that there are spaces around the parameter options 62
, 1
, POST
and /RPC2
that the command transforms to nulls.
The representation of the payload in hexadecimal, would have the following aspect:
00000000: 3633 3a43 4f4e 5445 4e54 5f4c 454e 4754 63:CONTENT_LENGT 00000010: 4800 3632 0053 4347 4900 3100 5245 5155 H.62.SCGI.1.REQU 00000020: 4553 545f 4d45 5448 4f44 0050 4f53 5400 EST_METHOD.POST. 00000030: 5245 5155 4553 545f 5552 4900 2f52 5043 REQUEST_URI./RPC 00000040: 3200 2c3c 6d65 7468 6f64 4361 6c6c 3e3c 2.,<methodCall>< 00000050: 6d65 7468 6f64 4e61 6d65 3e73 6573 7369 methodName>sessi 00000060: 6f6e 2e6e 616d 653c 2f6d 6574 686f 644e on.name</methodN 00000070: 616d 653e 3c2f 6d65 7468 6f64 4361 6c6c ame></methodCall 00000080: 3e >
The response for SCGI is a simple HTTP response comprised of headers such as the HTTP status, the content length, etc, followed by the body of the response. Typically, the response body to a session.name
method call would be:
<methodResponse> <params> <param><value><string>HOST:PORT</string></value></param> </params> </methodResponse>
where HOST
and PORT
are instantiated values returned by rTorrent.
The command however uses xmlstarlet
to retrieve just the host and port tuple from the response for conveniently using the response in scripts.
Based on the former, a simple bash script can be written:
#!/bin/bash ########################################################################### ## Copyright (C) Wizardry and Steamworks 2023 - License: GNU GPLv3 ## ########################################################################### # This script connects to rTorrent via SCGI, checks the hostname and the # # current PID of the rTorrent process and then returns 0 if the hostname # # and PID match the results from the system tools or 1 otherwise. # # # # Requirements: # # * sed # # * tr # # * netcat # # * grep # # * xmlstarlet # # # ########################################################################### ########################################################################### # CONFIGURATION # ########################################################################### # These settings correspond to the rTorrent setting: # network.scgi.open_port = "0.0.0.0:5000" # that enables the SCGI remote access functionality. RTORRENT_HOST="localhost" RTORRENT_PORT=5000 ########################################################################### # INTERNALS # ########################################################################### SESSION=$(cat <<EOF | \ sed -E 's/[ ]{2,}//g' | \ tr ' ' \\0 | \ tr -d '\n' | \ netcat "$RTORRENT_HOST" "$RTORRENT_PORT" | \ grep -Pzo "(?s)<methodResponse>.+?<\/methodResponse>" | \ xmlstarlet select -t -v /methodResponse/params/param[1]/value/string 63: CONTENT_LENGTH 62 SCGI 1 REQUEST_METHOD POST REQUEST_URI /RPC2 , <methodCall> <methodName>session.name</methodName> </methodCall> EOF ) HOST=`echo $SESSION | awk -F':' '{ print $1 }'` PID=`echo $SESSION | awk -F':' '{ print $2 }'` [ "$HOST" == `hostname` ] || exit 1 [ "$PID" == `pgrep -f /usr/bin/rtorrent` ] || exit 1 exit 0
that will check the session.name
parameter from rTorrent and correlate the hostname and process identifier with the hostname
command and the process identifier of the rTorrent process as retrieved from the process table.
Then, if the results match, the script will set a return status of 0
and 1
otherwise such that the script can be combined with software packages such as monit to monitor rtorrent more reliably than checking whether the process is to be found in the process table. Namely, in case rTorrent has crashed or has become a zombie process, then the SCGI port will not answer but rTorrent will still appear in the process table thereby giving the wrong impression that rTorrent is running properly.