Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/rtorrent: add rtorrent tmux service (user and system) #30850

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
beb325b
nzbget systemd unit updates nzbget.conf to installed WebDir, ConfigTe…
csingley Oct 5, 2017
362c2d1
nzbget systemd unit - fix variable escaping in sed expression
csingley Oct 6, 2017
e3a6212
nzbget service: override broken WebDir/ConfigTemplate in nzbget.conf
csingley Oct 23, 2017
837c930
rtorrent service - rtorrent user gets bash shell
csingley Oct 25, 2017
eec11ce
rtorrent service - cosmetics to align closer with Arch's wiki
csingley Oct 25, 2017
4a13a17
Fixed systemd.services.rtorrent.path; removed PermissionsStartOnly.
csingley Oct 25, 2017
d649599
rtorrent system service & user service
csingley Oct 26, 2017
3f8ab2b
Moved rtorrent uid/gid to misc/ids.nix
csingley Oct 26, 2017
05802c1
Revert accidental changes to nzbget.nix
csingley Oct 26, 2017
ca2b6f3
Merge remote-tracking branch 'upstream/master'
csingley Oct 26, 2017
c2f6eb6
Merge branch 'master' into rtorrent
csingley Oct 27, 2017
d410579
rtorrent service - rtorrent user gets bash shell
csingley Oct 25, 2017
fccf076
rtorrent service - cosmetics to align closer with Arch's wiki
csingley Oct 25, 2017
07ec7d0
Fixed systemd.services.rtorrent.path; removed PermissionsStartOnly.
csingley Oct 25, 2017
33a0cc4
rtorrent system service & user service
csingley Oct 26, 2017
acd98d7
Renumber rtorrent uid/gid
csingley Oct 27, 2017
80a4569
Revert accidental changes to nzbget.nix
csingley Oct 26, 2017
86d5553
nixos/rtorrent: init rtorrent tmux service
csingley Oct 26, 2017
61a74f6
nixos/rtorrent: add rtorrent tmux service
csingley Oct 27, 2017
19a5c41
Merge branch 'rtorrent' of https://github.com/csingley/nixpkgs into r…
csingley Oct 27, 2017
11e4b5b
nixos/rtorrent: add rtorrent tmux service (system and user)
csingley Oct 27, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions nixos/modules/misc/ids.nix
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@
kanboard = 281;
pykms = 282;
kodi = 283;
rtorrent = 284;

# When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!

Expand Down Expand Up @@ -567,6 +568,7 @@
kanboard = 281;
pykms = 282;
kodi = 283;
rtorrent = 284;

# When adding a gid, make sure it doesn't match an existing
# uid. Users and groups with the same name should have equal
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@
./services/torrent/flexget.nix
./services/torrent/opentracker.nix
./services/torrent/peerflix.nix
./services/torrent/rtorrent.nix
./services/torrent/transmission.nix
./services/ttys/agetty.nix
./services/ttys/gpm.nix
Expand Down
161 changes: 161 additions & 0 deletions nixos/modules/services/torrent/rtorrent.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
{ config, pkgs, lib, ... }:

with lib;

let
cfg = config.services.rtorrent;
in
{
options.services.rtorrent = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable a system service for rtorrent (via tmux).
Use `sudo -u rtorrent tmux attach` to manage rtorrent,
and `Ctrl-B d` to detach.

If true, services.rtorrent.install is considered true, whatever its value.
'';
};
install = mkOption {
type = types.bool;
default = false;
description = ''
Whether to install a user service for rtorrent (via tmux).
The service must be manually started for each user with
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate text.

``.

The service must be manually started for each user with
`systemctl --user start rtorrent`. To start rtorrent on boot as a
system service, instead set services.rtorrent.enable = true.

Once the service has started, use `tmux -L rtorrent attach` to manage rtorrent.
'';
};

dataDir = mkOption {
type = types.str;
default = "/var/lib/rtorrent";
description = ''
If enabled as a system service, the directory where rtorrent stores its data files.
If installed as a user service, this value is ignored.
'';
};

configFile = mkOption {
type = types.str;
default = "/var/lib/rtorrent/rtorrent.rc";
description = ''
If enabled as a system service, the location of rtorrent's config file.
If installed as a user service, this value is ignored.
'';
};

userDataDir = mkOption {
type = types.str;
default = "rtorrent";
description = ''
If installed as a user service, the directory where rtorrent stores its data files relative to $HOME.
If enabled as a system service, this value is ignored.
'';
};

userConfigFile = mkOption {
type = types.str;
default = ".rtorrent.rc";
description = ''
If installed as a user service, the location of rtorrent's config file relative to $HOME.
If enabled as a system service, this value is ignored.
'';
};

user = mkOption {
type = types.str;
default = "rtorrent";
description = ''
User account under which rtorrent runs, if enabled as a system service.
Ignored if installed as a user service.
'';
};

group = mkOption {
type = types.str;
default = "rtorrent";
description = ''
Group under which rtorrent runs, if enabled as a system service.
Ignored if installed as a user service.
'';
};

package = mkOption {
type = types.package;
default = pkgs.rtorrent;
defaultText = "pkgs.rtorrent";
description = "The rtorrent package to use.";
};
};

config = mkIf (cfg.enable || cfg.install) {
users.groups = mkIf (cfg.enable && cfg.group == "rtorrent") {
rtorrent.gid = config.ids.gids.rtorrent;
};
users.extraUsers = mkIf (cfg.enable && cfg.user == "rtorrent") {
rtorrent = {
uid = config.ids.uids.rtorrent;
group = cfg.group;
shell = pkgs.bashInteractive;
home = cfg.dataDir;
createHome = true;
};
};
systemd.services.rtorrent = mkIf cfg.enable {
description = "rTorrent system service (via tmux)";
after = [ "network.target" ];
# Default rtorrent.rc (in preStart) uses bash, which needs to be on the $PATH
path = [ cfg.package pkgs.tmux pkgs.bash pkgs.procps ];
preStart = ''
test -f "${cfg.configFile}" || {
echo "creating default rtorrent config file at ${cfg.configFile}."
cat > "${cfg.configFile}" << EOF
'' + replaceStrings [ "/home/USERNAME/rtorrent" ] [ cfg.dataDir ] (readFile ./rtorrent.rc) + "\nEOF\n}";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "forking";
KillMode = "none";
User = cfg.user;
Group = cfg.group;
ExecStart = "${pkgs.tmux}/bin/tmux new-session -c ${cfg.dataDir} -s rtorrent -n rtorrent -d rtorrent -n -o import=${cfg.configFile}";
ExecStop = "${pkgs.bash}/bin/bash -c \"tmux send-keys -t rtorrent C-q && while pidof rtorrent > /dev/null; do sleep 0.5; done\"";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very hacky. Not sure if there is a better way to solve this together with tmux but it doesn't feel right to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's a little reminiscent of creaky init scripts.
On the plus side, it's at least a fairly well tested hack; lots of people have been using it on Arch for a few years:

https://wiki.archlinux.org/index.php/RTorrent#Systemd_services_using_tmux_or_screen

WorkingDirectory = "${cfg.dataDir}";
Restart = "on-failure";
};
};
systemd.user.services.rtorrent = mkIf (!cfg.enable) {
description = "rTorrent user service (via tmux)";
after = [ "network.target" ];
# Default rtorrent.rc (in preStart) uses bash, which needs to be on the $PATH
path = [ cfg.package pkgs.tmux pkgs.bash pkgs.procps ];
preStart = ''
test -d $HOME/${cfg.userDataDir} || {
echo "Creating rtorrent data directory at $HOME/${cfg.userDataDir}."
mkdir $HOME/${cfg.userDataDir}
}
if test -e $HOME/${cfg.userDataDir}/.session/rtorrent.lock && test -z `pidof rtorrent`; then
rm -f $HOME/${cfg.userDataDir}/.session/rtorrent.lock
fi

test -f $HOME/${cfg.userConfigFile} || {
echo "creating default rtorrent config file at $HOME/${cfg.userConfigFile}."
cat > $HOME/${cfg.userConfigFile} << EOF
'' + replaceStrings [ "/home/USERNAME/rtorrent" ] [ "$HOME/${cfg.userDataDir}" ] (readFile ./rtorrent.rc) + "\nEOF\n}";
environment = { HOME = "%h"; };
serviceConfig = {
Type = "forking";
ExecStart = "${pkgs.tmux}/bin/tmux -L rtorrent new-session -s rt -n rtorrent -d rtorrent -n -o import=%h/${cfg.userConfigFile}";
ExecStop = "${pkgs.bash}/bin/bash -c \"tmux -L rtorrent send-keys -t rt:rtorrent.0 C-q; while pidof rtorrent > /dev/null; do echo stopping rtorrent...; sleep 1; done\"";
Restart = "on-failure";
};
};
};
}
94 changes: 94 additions & 0 deletions nixos/modules/services/torrent/rtorrent.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#############################################################################
# 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.watch, private|const|string, (cat,(cfg.basedir),"watch/")
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")

# Create instance directories
execute.throw = bash, -c, (cat,\
"builtin cd \"", (cfg.basedir), "\" ",\
"&& mkdir -p .session download log watch/{load,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.basedir), ".session/")
directory.default.set = (cat, (cfg.basedir), "download/")
log.execute = (cat, (cfg.logs), "execute.log")
##log.xmlrpc = (cat, (cfg.logs), "xmlrpc.log")
execute.nothrow = bash, -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
##network.http.capath.set = "/etc/ssl/certs"
##network.http.ssl_verify_peer.set = 0
##network.http.ssl_verify_host.set = 0
##pieces.hash.on_completion.set = no
##keys.layout.set = qwerty

##view.sort_current = seeding, greater=d.ratio=
schedule2 = monitor_diskspace, 15, 60, ((close_low_diskspace, 1000M))

# 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, (cat, (cfg.watch), "start/*.torrent")))
schedule2 = watch_load, 11, 10, ((load.normal, (cat, (cfg.watch), "load/*.torrent")))

# 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_debug", "log"