python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / services / torrent / deluge.nix
blob70fad4d7d76c765bc1a81af2e9f60c3cb89914d6
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.services.deluge;
7   cfg_web = config.services.deluge.web;
8   isDeluge1 = versionOlder cfg.package.version "2.0.0";
10   openFilesLimit = 4096;
11   listenPortsDefault = [ 6881 6889 ];
13   listToRange = x: { from = elemAt x 0; to = elemAt x 1; };
15   configDir = "${cfg.dataDir}/.config/deluge";
16   configFile = pkgs.writeText "core.conf" (builtins.toJSON cfg.config);
17   declarativeLockFile = "${configDir}/.declarative";
19   preStart = if cfg.declarative then ''
20     if [ -e ${declarativeLockFile} ]; then
21       # Was declarative before, no need to back up anything
22       ${if isDeluge1 then "ln -sf" else "cp"} ${configFile} ${configDir}/core.conf
23       ln -sf ${cfg.authFile} ${configDir}/auth
24     else
25       # Declarative for the first time, backup stateful files
26       ${if isDeluge1 then "ln -s" else "cp"} -b --suffix=.stateful ${configFile} ${configDir}/core.conf
27       ln -sb --suffix=.stateful ${cfg.authFile} ${configDir}/auth
28       echo "Autogenerated file that signifies that this server configuration is managed declaratively by NixOS" \
29         > ${declarativeLockFile}
30     fi
31   '' else ''
32     if [ -e ${declarativeLockFile} ]; then
33       rm ${declarativeLockFile}
34     fi
35   '';
36 in {
37   options = {
38     services = {
39       deluge = {
40         enable = mkEnableOption (lib.mdDoc "Deluge daemon");
42         openFilesLimit = mkOption {
43           default = openFilesLimit;
44           type = types.either types.int types.str;
45           description = lib.mdDoc ''
46             Number of files to allow deluged to open.
47           '';
48         };
50         config = mkOption {
51           type = types.attrs;
52           default = {};
53           example = literalExpression ''
54             {
55               download_location = "/srv/torrents/";
56               max_upload_speed = "1000.0";
57               share_ratio_limit = "2.0";
58               allow_remote = true;
59               daemon_port = 58846;
60               listen_ports = [ ${toString listenPortsDefault} ];
61             }
62           '';
63           description = lib.mdDoc ''
64             Deluge core configuration for the core.conf file. Only has an effect
65             when {option}`services.deluge.declarative` is set to
66             `true`. String values must be quoted, integer and
67             boolean values must not. See
68             <https://git.deluge-torrent.org/deluge/tree/deluge/core/preferencesmanager.py#n41>
69             for the availaible options.
70           '';
71         };
73         declarative = mkOption {
74           type = types.bool;
75           default = false;
76           description = lib.mdDoc ''
77             Whether to use a declarative deluge configuration.
78             Only if set to `true`, the options
79             {option}`services.deluge.config`,
80             {option}`services.deluge.openFirewall` and
81             {option}`services.deluge.authFile` will be
82             applied.
83           '';
84         };
86         openFirewall = mkOption {
87           default = false;
88           type = types.bool;
89           description = lib.mdDoc ''
90             Whether to open the firewall for the ports in
91             {option}`services.deluge.config.listen_ports`. It only takes effet if
92             {option}`services.deluge.declarative` is set to
93             `true`.
95             It does NOT apply to the daemon port nor the web UI port. To access those
96             ports secuerly check the documentation
97             <https://dev.deluge-torrent.org/wiki/UserGuide/ThinClient#CreateSSHTunnel>
98             or use a VPN or configure certificates for deluge.
99           '';
100         };
102         dataDir = mkOption {
103           type = types.path;
104           default = "/var/lib/deluge";
105           description = lib.mdDoc ''
106             The directory where deluge will create files.
107           '';
108         };
110         authFile = mkOption {
111           type = types.path;
112           example = "/run/keys/deluge-auth";
113           description = lib.mdDoc ''
114             The file managing the authentication for deluge, the format of this
115             file is straightforward, each line contains a
116             username:password:level tuple in plaintext. It only has an effect
117             when {option}`services.deluge.declarative` is set to
118             `true`.
119             See <https://dev.deluge-torrent.org/wiki/UserGuide/Authentication> for
120             more informations.
121           '';
122         };
124         user = mkOption {
125           type = types.str;
126           default = "deluge";
127           description = lib.mdDoc ''
128             User account under which deluge runs.
129           '';
130         };
132         group = mkOption {
133           type = types.str;
134           default = "deluge";
135           description = lib.mdDoc ''
136             Group under which deluge runs.
137           '';
138         };
140         extraPackages = mkOption {
141           type = types.listOf types.package;
142           default = [];
143           description = lib.mdDoc ''
144             Extra packages available at runtime to enable Deluge's plugins. For example,
145             extraction utilities are required for the built-in "Extractor" plugin.
146             This always contains unzip, gnutar, xz and bzip2.
147           '';
148         };
150         package = mkOption {
151           type = types.package;
152           example = literalExpression "pkgs.deluge-2_x";
153           description = lib.mdDoc ''
154             Deluge package to use.
155           '';
156         };
157       };
159       deluge.web = {
160         enable = mkEnableOption (lib.mdDoc "Deluge Web daemon");
162         port = mkOption {
163           type = types.port;
164           default = 8112;
165           description = lib.mdDoc ''
166             Deluge web UI port.
167           '';
168         };
170         openFirewall = mkOption {
171           type = types.bool;
172           default = false;
173           description = lib.mdDoc ''
174             Open ports in the firewall for deluge web daemon
175           '';
176         };
177       };
178     };
179   };
181   config = mkIf cfg.enable {
183     services.deluge.package = mkDefault (
184       if versionAtLeast config.system.stateVersion "20.09" then
185         pkgs.deluge-2_x
186       else
187         # deluge-1_x is no longer packaged and this will resolve to an error
188         # thanks to the alias for this name.  This is left here so that anyone
189         # using NixOS older than 20.09 receives that error when they upgrade
190         # and is forced to make an intentional choice to switch to deluge-2_x.
191         # That might be slightly inconvenient but there is no path to
192         # downgrade from 2.x to 1.x so NixOS should not automatically perform
193         # this state migration.
194         pkgs.deluge-1_x
195     );
197     # Provide a default set of `extraPackages`.
198     services.deluge.extraPackages = with pkgs; [ unzip gnutar xz bzip2 ];
200     systemd.tmpfiles.rules = [
201       "d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group}"
202       "d '${cfg.dataDir}/.config' 0770 ${cfg.user} ${cfg.group}"
203       "d '${cfg.dataDir}/.config/deluge' 0770 ${cfg.user} ${cfg.group}"
204     ]
205     ++ optional (cfg.config ? download_location)
206       "d '${cfg.config.download_location}' 0770 ${cfg.user} ${cfg.group}"
207     ++ optional (cfg.config ? torrentfiles_location)
208       "d '${cfg.config.torrentfiles_location}' 0770 ${cfg.user} ${cfg.group}"
209     ++ optional (cfg.config ? move_completed_path)
210       "d '${cfg.config.move_completed_path}' 0770 ${cfg.user} ${cfg.group}";
212     systemd.services.deluged = {
213       after = [ "network.target" ];
214       description = "Deluge BitTorrent Daemon";
215       wantedBy = [ "multi-user.target" ];
216       path = [ cfg.package ] ++ cfg.extraPackages;
217       serviceConfig = {
218         ExecStart = ''
219           ${cfg.package}/bin/deluged \
220             --do-not-daemonize \
221             --config ${configDir}
222         '';
223         # To prevent "Quit & shutdown daemon" from working; we want systemd to
224         # manage it!
225         Restart = "on-success";
226         User = cfg.user;
227         Group = cfg.group;
228         UMask = "0002";
229         LimitNOFILE = cfg.openFilesLimit;
230       };
231       preStart = preStart;
232     };
234     systemd.services.delugeweb = mkIf cfg_web.enable {
235       after = [ "network.target" "deluged.service"];
236       requires = [ "deluged.service" ];
237       description = "Deluge BitTorrent WebUI";
238       wantedBy = [ "multi-user.target" ];
239       path = [ cfg.package ];
240       serviceConfig = {
241         ExecStart = ''
242           ${cfg.package}/bin/deluge-web \
243             ${optionalString (!isDeluge1) "--do-not-daemonize"} \
244             --config ${configDir} \
245             --port ${toString cfg.web.port}
246         '';
247         User = cfg.user;
248         Group = cfg.group;
249       };
250     };
252     networking.firewall = mkMerge [
253       (mkIf (cfg.declarative && cfg.openFirewall && !(cfg.config.random_port or true)) {
254         allowedTCPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
255         allowedUDPPortRanges = singleton (listToRange (cfg.config.listen_ports or listenPortsDefault));
256       })
257       (mkIf (cfg.web.openFirewall) {
258         allowedTCPPorts = [ cfg.web.port ];
259       })
260     ];
262     environment.systemPackages = [ cfg.package ];
264     users.users = mkIf (cfg.user == "deluge") {
265       deluge = {
266         group = cfg.group;
267         uid = config.ids.uids.deluge;
268         home = cfg.dataDir;
269         description = "Deluge Daemon user";
270       };
271     };
273     users.groups = mkIf (cfg.group == "deluge") {
274       deluge = {
275         gid = config.ids.gids.deluge;
276       };
277     };
278   };