RogerBW's Blog

Interfacing with rtorrent over Unix sockets 20 October 2021

The torrent client rtorrent is the best I've found that runs on Unix systems, but its built-in logic is somewhat lacking. Fortunately one can extend this.

For example, it can say "seed until you've uploaded twice as much as you downloaded, then stop seeding", which is fair enough. But I compromise between kindness and disc space; if I've downloaded something very unpopular (such as an old Linux distro that nobody else wants any more) I'd like to reduce the ratio at which I'll stop seeding, and after a couple of months I probably don't want to keep it around at all. Meanwhile anything that has reached its ratio can be deleted from the server. (I've already set up logic within rtorrent that hard-links completed downloads to somewhere else, for downloading from the remote torrenting machine to archive storage.)

But that's OK, rtorrent has a remote control interface. There's some documentation on this at the rtorrent wiki, but it assumes that you want to link from a Unix domain socket to a web server endpoint, which you then have to secure, and then plug in something to that that talks HTTP/TCP. Other documentation I've found makes the same assumption. I wanted a simpler approach, just talking to the socket directly from a program running under the same userid on the same machine.

First you need to tell rtorrent to create a listening socket. This needs an explicit path; obviously change this to somewhere that exists on your filesystem. (^x gets you a command prompt within rtorrent, and/or add this to your ~/.rtorrent.rc.)

network.scgi.open_local = /home/roger/rtorrent/rpc.socket

Now you need to talk to it. This is where other documentation goes off into web servers and mod_scgi and other such stuff. No need for any of that! I did it in Perl, but basically anything that can talk to a Unix socket will work, so rather than go into details of my code I'll just talk about the algorithm.

So in your language of choice: connect to the socket (you may need to specify that it's a SOCK_STREAM).

The commands you'll be sending are in XML-RPC, wrapped up in SCGI. In Perl at least, while there are modules to deal with all these things, they tend to assume that you'll be using an HTTP transport and a web server rather than a socket, so I ended up doing it the straightforward way.

So start with your command, as it might be "download_list" "" (using a blank parameter to specify the "main" view). Turn that into a lump of XML like:

<?xml version="1.0"?>
<methodCall>
  <methodName>download_list</methodName>
  <params>
    <param>
        <value><string></string></value>
    </param>
  </params>
</methodCall>

(XML-RPC has other parameter data types, but rtorrent appears to accept everything as strings.)

That's the request body; let's say it's 179 characters long. Now that gets formatted as an SCGI request, which starts as a series of key-value pairs. The minimal version might look like:

CONTENT_LENGTH 179
SCGI 1
REQUEST_METHOD POST
REQUEST_URI /RPC

Turn these into a single string, with a NUL after each key and value.

CONTENT_LENGTH<00>179<00>SCGI<00>1<00>.../RPC<00>

Now netstring-encode it: prepend it with its total length (just this header block, ignoring the request body) in decimal digits, and a colon separator.

63:CONTENT_LENGTH<00>.../RPC<00>

Append to that string a comma, then the request body. Send all that in one lump to the socket.

You'll get back a series of lines (terminated with CR LF); the first blank line indicates the end of the headers, and everything after that should be XML. You can parse this properly if you have a convenient XMLRPC decoder, but for these purposes you can just use a generic XML parser and snag the contents of every string or i8 node (it may emit other types which I haven't yet seen). In the case of this download_list command, you'll get a list of strings with hashes of the torrents that rtorrent knows about.

I found that sending further commands to the socket left it unresponsive, so I open and close a new socket connection for each command. There is probably a better way of doing this.

Sending system.listMethods will list every call that the system supports. There's very little documentation beyond the source code, but ones that apply to individual torrent downloads start with d. and take the hash as (first?) parameter; some useful ones are:

  • d.name - the torrent's name
  • d.base_path - full path to download
  • d.complete - 1 if it's complete, 0 if not
  • d.ratio - 1000× the upload ratio
  • d.state - 1 if running, 0 if not
  • d.timestamp-started - unixtime when the download began
  • d.timestamp-finished - unixtime when the download completed
  • d.erase - remove the torrent from the client (not deleting its files, you can do that in your own code)
  • d.close - make the torrent inactive in the client
Tags: computing

Comments on this post are now closed. If you have particular grounds for adding a late comment, comment on a more recent post quoting the URL of this one.

Search
Archive
Tags 1920s 1930s 1940s 1950s 1960s 1970s 1980s 1990s 2000s 2010s 2300ad 3d printing action advent of code aeronautics aikakirja anecdote animation anime army astronomy audio audio tech base commerce battletech bayern beer boardgaming book of the week bookmonth chain of command children chris chronicle church of no redeeming virtues cold war comedy computing contemporary cornish smuggler cosmic encounter coup covid-19 crime crystal cthulhu eternal cycling dead of winter doctor who documentary drama driving drone ecchi economics en garde espionage essen 2015 essen 2016 essen 2017 essen 2018 essen 2019 essen 2022 essen 2023 essen 2024 existential risk falklands war fandom fanfic fantasy feminism film firefly first world war flash point flight simulation food garmin drive gazebo genesys geocaching geodata gin gkp gurps gurps 101 gus harpoon historical history horror hugo 2014 hugo 2015 hugo 2016 hugo 2017 hugo 2018 hugo 2019 hugo 2020 hugo 2021 hugo 2022 hugo 2023 hugo 2024 hugo-nebula reread in brief avoid instrumented life javascript julian simpson julie enfield kickstarter kotlin learn to play leaving earth linux liquor lovecraftiana lua mecha men with beards mpd museum music mystery naval noir non-fiction one for the brow opera parody paul temple perl perl weekly challenge photography podcast politics postscript powers prediction privacy project woolsack pyracantha python quantum rail raku ranting raspberry pi reading reading boardgames social real life restaurant reviews romance rpg a day rpgs ruby rust scala science fiction scythe second world war security shipwreck simutrans smartphone south atlantic war squaddies stationery steampunk stuarts suburbia superheroes suspense television the resistance the weekly challenge thirsty meeples thriller tin soldier torg toys trailers travel type 26 type 31 type 45 vietnam war war wargaming weather wives and sweethearts writing about writing x-wing young adult
Special All book reviews, All film reviews
Produced by aikakirja v0.1