Running as a Daemon

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:

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.

Sending SCGI Commands from the Command Line Interface

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 $62$ 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:

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.