]> Git — Sourcephile - sourcephile-nix.git/blob - nixpkgs/patches/transmission.diff
bash: add utilities
[sourcephile-nix.git] / nixpkgs / patches / transmission.diff
1 diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
2 index 34a5219c959..779924a65a8 100644
3 --- a/nixos/modules/services/torrent/transmission.nix
4 +++ b/nixos/modules/services/torrent/transmission.nix
5 @@ -7,15 +7,20 @@ let
6 inherit (config.environment) etc;
7 apparmor = config.security.apparmor;
8 rootDir = "/run/transmission";
9 - homeDir = "/var/lib/transmission";
10 settingsDir = ".config/transmission-daemon";
11 downloadsDir = "Downloads";
12 incompleteDir = ".incomplete";
13 watchDir = "watchdir";
14 - # TODO: switch to configGen.json once RFC0042 is implemented
15 - settingsFile = pkgs.writeText "settings.json" (builtins.toJSON cfg.settings);
16 + settingsFormat = pkgs.formats.json {};
17 + settingsFile = settingsFormat.generate "settings.json" cfg.settings;
18 in
19 {
20 + imports = [
21 + (mkRenamedOptionModule ["services" "transmission" "port"]
22 + ["services" "transmission" "settings" "rpc-port"])
23 + (mkAliasOptionModule ["services" "transmission" "openFirewall"]
24 + ["services" "transmission" "openPeerPorts"])
25 + ];
26 options = {
27 services.transmission = {
28 enable = mkEnableOption ''the headless Transmission BitTorrent daemon.
29 @@ -24,48 +29,141 @@ in
30 transmission-remote, the WebUI (http://127.0.0.1:9091/ by default),
31 or other clients like stig or tremc.
32
33 - Torrents are downloaded to ${homeDir}/${downloadsDir} by default and are
34 + Torrents are downloaded to <xref linkend="opt-services.transmission.home"/>/${downloadsDir} by default and are
35 accessible to users in the "transmission" group'';
36
37 - settings = mkOption rec {
38 - # TODO: switch to types.config.json as prescribed by RFC0042 once it's implemented
39 - type = types.attrs;
40 - apply = recursiveUpdate default;
41 - default =
42 - {
43 - download-dir = "${cfg.home}/${downloadsDir}";
44 - incomplete-dir = "${cfg.home}/${incompleteDir}";
45 - incomplete-dir-enabled = true;
46 - watch-dir = "${cfg.home}/${watchDir}";
47 - watch-dir-enabled = false;
48 - message-level = 1;
49 - peer-port = 51413;
50 - peer-port-random-high = 65535;
51 - peer-port-random-low = 49152;
52 - peer-port-random-on-start = false;
53 - rpc-bind-address = "127.0.0.1";
54 - rpc-port = 9091;
55 - script-torrent-done-enabled = false;
56 - script-torrent-done-filename = "";
57 - umask = 2; # 0o002 in decimal as expected by Transmission
58 - utp-enabled = true;
59 - };
60 - example =
61 - {
62 - download-dir = "/srv/torrents/";
63 - incomplete-dir = "/srv/torrents/.incomplete/";
64 - incomplete-dir-enabled = true;
65 - rpc-whitelist = "127.0.0.1,192.168.*.*";
66 - };
67 + settings = mkOption {
68 description = ''
69 - Attribute set whose fields overwrites fields in
70 + Settings whose options overwrite fields in
71 <literal>.config/transmission-daemon/settings.json</literal>
72 - (each time the service starts). String values must be quoted, integer and
73 - boolean values must not.
74 + (each time the service starts).
75
76 See <link xlink:href="https://github.com/transmission/transmission/wiki/Editing-Configuration-Files">Transmission's Wiki</link>
77 - for documentation.
78 + for documentation of settings not explicitely covered by this module.
79 '';
80 + default = {};
81 + type = types.submodule {
82 + freeformType = settingsFormat.type;
83 + options.download-dir = mkOption {
84 + type = types.path;
85 + default = "${cfg.home}/${downloadsDir}";
86 + description = "Directory where to download torrents.";
87 + };
88 + options.incomplete-dir = mkOption {
89 + type = types.path;
90 + default = "${cfg.home}/${incompleteDir}";
91 + description = ''
92 + When enabled with
93 + services.transmission.home
94 + <xref linkend="opt-services.transmission.settings.incomplete-dir-enabled"/>,
95 + new torrents will download the files to this directory.
96 + When complete, the files will be moved to download-dir
97 + <xref linkend="opt-services.transmission.settings.download-dir"/>.
98 + '';
99 + };
100 + options.incomplete-dir-enabled = mkOption {
101 + type = types.bool;
102 + default = true;
103 + description = "";
104 + };
105 + options.message-level = mkOption {
106 + type = types.ints.between 0 2;
107 + default = 2;
108 + description = "Set verbosity of transmission messages.";
109 + };
110 + options.peer-port = mkOption {
111 + type = types.port;
112 + default = 51413;
113 + description = "The peer port to listen for incoming connections.";
114 + };
115 + options.peer-port-random-high = mkOption {
116 + type = types.port;
117 + default = 65535;
118 + description = ''
119 + The maximum peer port to listen to for incoming connections
120 + when <xref linkend="opt-services.transmission.settings.peer-port-random-on-start"/> is enabled.
121 + '';
122 + };
123 + options.peer-port-random-low = mkOption {
124 + type = types.port;
125 + default = 65535;
126 + description = ''
127 + The minimal peer port to listen to for incoming connections
128 + when <xref linkend="opt-services.transmission.settings.peer-port-random-on-start"/> is enabled.
129 + '';
130 + };
131 + options.peer-port-random-on-start = mkOption {
132 + type = types.bool;
133 + default = false;
134 + description = "Randomize the peer port.";
135 + };
136 + options.rpc-bind-address = mkOption {
137 + type = types.str;
138 + default = "127.0.0.1";
139 + example = "0.0.0.0";
140 + description = ''
141 + Where to listen for RPC connections.
142 + Use \"0.0.0.0\" to listen on all interfaces.
143 + '';
144 + };
145 + options.rpc-port = mkOption {
146 + type = types.port;
147 + default = 9091;
148 + description = "The RPC port to listen to.";
149 + };
150 + options.script-torrent-done-enabled = mkOption {
151 + type = types.bool;
152 + default = false;
153 + description = ''
154 + Whether to run
155 + <xref linkend="opt-services.transmission.settings.script-torrent-done-filename"/>
156 + at torrent completion.
157 + '';
158 + };
159 + options.script-torrent-done-filename = mkOption {
160 + type = types.nullOr types.path;
161 + default = null;
162 + description = "Executable to be run at torrent completion.";
163 + };
164 + options.umask = mkOption {
165 + type = types.int;
166 + default = 2;
167 + description = ''
168 + Sets transmission's file mode creation mask.
169 + See the umask(2) manpage for more information.
170 + Users who want their saved torrents to be world-writable
171 + may want to set this value to 0.
172 + Bear in mind that the json markup language only accepts numbers in base 10,
173 + so the standard umask(2) octal notation "022" is written in settings.json as 18.
174 + '';
175 + };
176 + options.utp-enabled = mkOption {
177 + type = types.bool;
178 + default = true;
179 + description = ''
180 + Whether to enable <link xlink:href="http://en.wikipedia.org/wiki/Micro_Transport_Protocol">Micro Transport Protocol (µTP)</link>.
181 + '';
182 + };
183 + options.watch-dir = mkOption {
184 + type = types.path;
185 + default = "${cfg.home}/${watchDir}";
186 + description = "Watch a directory for torrent files and add them to transmission.";
187 + };
188 + options.watch-dir-enabled = mkOption {
189 + type = types.bool;
190 + default = false;
191 + description = ''Whether to enable the
192 + <xref linkend="opt-services.transmission.settings.watch-dir"/>.
193 + '';
194 + };
195 + options.trash-original-torrent-files = mkOption {
196 + type = types.bool;
197 + default = false;
198 + description = ''Whether to delete torrents added from the
199 + <xref linkend="opt-services.transmission.settings.watch-dir"/>.
200 + '';
201 + };
202 + };
203 };
204
205 downloadDirPermissions = mkOption {
206 @@ -74,31 +172,22 @@ in
207 example = "775";
208 description = ''
209 The permissions set by <literal>systemd.activationScripts.transmission-daemon</literal>
210 - on the directories <link linkend="opt-services.transmission.settings">settings.download-dir</link>
211 - and <link linkend="opt-services.transmission.settings">settings.incomplete-dir</link>.
212 + on the directories <xref linkend="opt-services.transmission.settings.download-dir"/>
213 + and <xref linkend="opt-services.transmission.settings.incomplete-dir"/>.
214 Note that you may also want to change
215 - <link linkend="opt-services.transmission.settings">settings.umask</link>.
216 - '';
217 - };
218 -
219 - port = mkOption {
220 - type = types.port;
221 - description = ''
222 - TCP port number to run the RPC/web interface.
223 -
224 - If instead you want to change the peer port,
225 - use <link linkend="opt-services.transmission.settings">settings.peer-port</link>
226 - or <link linkend="opt-services.transmission.settings">settings.peer-port-random-on-start</link>.
227 + <xref linkend="opt-services.transmission.settings.umask"/>.
228 '';
229 };
230
231 home = mkOption {
232 type = types.path;
233 - default = homeDir;
234 + default = "/var/lib/transmission";
235 description = ''
236 The directory where Transmission will create <literal>${settingsDir}</literal>.
237 - as well as <literal>${downloadsDir}/</literal> unless <link linkend="opt-services.transmission.settings">settings.download-dir</link> is changed,
238 - and <literal>${incompleteDir}/</literal> unless <link linkend="opt-services.transmission.settings">settings.incomplete-dir</link> is changed.
239 + as well as <literal>${downloadsDir}/</literal> unless
240 + <xref linkend="opt-services.transmission.settings.download-dir"/> is changed,
241 + and <literal>${incompleteDir}/</literal> unless
242 + <xref linkend="opt-services.transmission.settings.incomplete-dir"/> is changed.
243 '';
244 };
245
246 @@ -119,19 +208,22 @@ in
247 description = ''
248 Path to a JSON file to be merged with the settings.
249 Useful to merge a file which is better kept out of the Nix store
250 - because it contains sensible data like <link linkend="opt-services.transmission.settings">settings.rpc-password</link>.
251 + because it contains sensible data like
252 + <xref linkend="opt-services.transmission.settings.rpc-password"/>.
253 '';
254 default = "/dev/null";
255 example = "/var/lib/secrets/transmission/settings.json";
256 };
257
258 - openFirewall = mkEnableOption "opening of the peer port(s) in the firewall";
259 + openPeerPorts = mkEnableOption "opening of the peer port(s) in the firewall";
260 +
261 + openRPCPort = mkEnableOption "opening of the RPC port in the firewall";
262
263 performanceNetParameters = mkEnableOption ''tweaking of kernel parameters
264 to open many more connections at the same time.
265
266 Note that you may also want to increase
267 - <link linkend="opt-services.transmission.settings">settings.peer-limit-global</link>.
268 + <xref linkend="opt-services.transmission.settings.peer-limit-global"/>.
269 And be aware that these settings are quite aggressive
270 and might not suite your regular desktop use.
271 For instance, SSH sessions may time out more easily'';
272 @@ -152,36 +244,10 @@ in
273 install -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.download-dir}'
274 '' + optionalString cfg.settings.incomplete-dir-enabled ''
275 install -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.incomplete-dir}'
276 + '' + optionalString cfg.settings.watch-dir-enabled ''
277 + install -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.watch-dir}'
278 '';
279
280 - assertions = [
281 - { assertion = builtins.match "^/.*" cfg.home != null;
282 - message = "`services.transmission.home' must be an absolute path.";
283 - }
284 - { assertion = types.path.check cfg.settings.download-dir;
285 - message = "`services.transmission.settings.download-dir' must be an absolute path.";
286 - }
287 - { assertion = types.path.check cfg.settings.incomplete-dir;
288 - message = "`services.transmission.settings.incomplete-dir' must be an absolute path.";
289 - }
290 - { assertion = types.path.check cfg.settings.watch-dir;
291 - message = "`services.transmission.settings.watch-dir' must be an absolute path.";
292 - }
293 - { assertion = cfg.settings.script-torrent-done-filename == "" || types.path.check cfg.settings.script-torrent-done-filename;
294 - message = "`services.transmission.settings.script-torrent-done-filename' must be an absolute path.";
295 - }
296 - { assertion = types.port.check cfg.settings.rpc-port;
297 - message = "${toString cfg.settings.rpc-port} is not a valid port number for `services.transmission.settings.rpc-port`.";
298 - }
299 - # In case both port and settings.rpc-port are explicitely defined: they must be the same.
300 - { assertion = !options.services.transmission.port.isDefined || cfg.port == cfg.settings.rpc-port;
301 - message = "`services.transmission.port' is not equal to `services.transmission.settings.rpc-port'";
302 - }
303 - ];
304 -
305 - services.transmission.settings =
306 - optionalAttrs options.services.transmission.port.isDefined { rpc-port = cfg.port; };
307 -
308 systemd.services.transmission = {
309 description = "Transmission BitTorrent Service";
310 after = [ "network.target" ] ++ optional apparmor.enable "apparmor.service";
311 @@ -226,11 +292,9 @@ in
312 cfg.settings.download-dir
313 ] ++
314 optional cfg.settings.incomplete-dir-enabled
315 - cfg.settings.incomplete-dir
316 - ++
317 - optional cfg.settings.watch-dir-enabled
318 - cfg.settings.watch-dir
319 - ;
320 + cfg.settings.incomplete-dir ++
321 + optional (cfg.settings.watch-dir-enabled && cfg.settings.trash-original-torrent-files)
322 + cfg.settings.watch-dir;
323 BindReadOnlyPaths = [
324 # No confinement done of /nix/store here like in systemd-confinement.nix,
325 # an AppArmor profile is provided to get a confinement based upon paths and rights.
326 @@ -239,8 +303,10 @@ in
327 "/run"
328 ] ++
329 optional (cfg.settings.script-torrent-done-enabled &&
330 - cfg.settings.script-torrent-done-filename != "")
331 - cfg.settings.script-torrent-done-filename;
332 + cfg.settings.script-torrent-done-filename != null)
333 + cfg.settings.script-torrent-done-filename ++
334 + optional (cfg.settings.watch-dir-enabled && !cfg.settings.trash-original-torrent-files)
335 + cfg.settings.watch-dir;
336 # The following options are only for optimizing:
337 # systemd-analyze security transmission
338 AmbientCapabilities = "";
339 @@ -307,25 +373,28 @@ in
340 };
341 });
342
343 - networking.firewall = mkIf cfg.openFirewall (
344 - if cfg.settings.peer-port-random-on-start
345 - then
346 - { allowedTCPPortRanges =
347 - [ { from = cfg.settings.peer-port-random-low;
348 - to = cfg.settings.peer-port-random-high;
349 - }
350 - ];
351 - allowedUDPPortRanges =
352 - [ { from = cfg.settings.peer-port-random-low;
353 - to = cfg.settings.peer-port-random-high;
354 - }
355 - ];
356 - }
357 - else
358 - { allowedTCPPorts = [ cfg.settings.peer-port ];
359 - allowedUDPPorts = [ cfg.settings.peer-port ];
360 - }
361 - );
362 + networking.firewall = mkMerge [
363 + (mkIf cfg.openPeerPorts (
364 + if cfg.settings.peer-port-random-on-start
365 + then
366 + { allowedTCPPortRanges =
367 + [ { from = cfg.settings.peer-port-random-low;
368 + to = cfg.settings.peer-port-random-high;
369 + }
370 + ];
371 + allowedUDPPortRanges =
372 + [ { from = cfg.settings.peer-port-random-low;
373 + to = cfg.settings.peer-port-random-high;
374 + }
375 + ];
376 + }
377 + else
378 + { allowedTCPPorts = [ cfg.settings.peer-port ];
379 + allowedUDPPorts = [ cfg.settings.peer-port ];
380 + }
381 + ))
382 + (mkIf cfg.openRPCPort { allowedTCPPorts = [ cfg.settings.rpc-port ]; })
383 + ];
384
385 boot.kernel.sysctl = mkMerge [
386 # Transmission uses a single UDP socket in order to implement multiple uTP sockets,
387 @@ -340,21 +409,21 @@ in
388 # Increase the number of available source (local) TCP and UDP ports to 49151.
389 # Usual default is 32768 60999, ie. 28231 ports.
390 # Find out your current usage with: ss -s
391 - "net.ipv4.ip_local_port_range" = "16384 65535";
392 + "net.ipv4.ip_local_port_range" = mkDefault "16384 65535";
393 # Timeout faster generic TCP states.
394 # Usual default is 600.
395 # Find out your current usage with: watch -n 1 netstat -nptuo
396 - "net.netfilter.nf_conntrack_generic_timeout" = 60;
397 + "net.netfilter.nf_conntrack_generic_timeout" = mkDefault 60;
398 # Timeout faster established but inactive connections.
399 # Usual default is 432000.
400 - "net.netfilter.nf_conntrack_tcp_timeout_established" = 600;
401 + "net.netfilter.nf_conntrack_tcp_timeout_established" = mkDefault 600;
402 # Clear immediately TCP states after timeout.
403 # Usual default is 120.
404 - "net.netfilter.nf_conntrack_tcp_timeout_time_wait" = 1;
405 + "net.netfilter.nf_conntrack_tcp_timeout_time_wait" = mkDefault 1;
406 # Increase the number of trackable connections.
407 # Usual default is 262144.
408 # Find out your current usage with: conntrack -C
409 - "net.netfilter.nf_conntrack_max" = 1048576;
410 + "net.netfilter.nf_conntrack_max" = mkDefault 1048576;
411 })
412 ];
413
414 @@ -370,7 +439,7 @@ in
415 rw ${cfg.settings.incomplete-dir}/**,
416 ''}
417 ${optionalString cfg.settings.watch-dir-enabled ''
418 - rw ${cfg.settings.watch-dir}/**,
419 + r${optionalString cfg.settings.trash-original-torrent-files "w"} ${cfg.settings.watch-dir}/**,
420 ''}
421 profile dirs {
422 rw ${cfg.settings.download-dir}/**,
423 @@ -378,12 +447,12 @@ in
424 rw ${cfg.settings.incomplete-dir}/**,
425 ''}
426 ${optionalString cfg.settings.watch-dir-enabled ''
427 - rw ${cfg.settings.watch-dir}/**,
428 + r${optionalString cfg.settings.trash-original-torrent-files "w"} ${cfg.settings.watch-dir}/**,
429 ''}
430 }
431
432 ${optionalString (cfg.settings.script-torrent-done-enabled &&
433 - cfg.settings.script-torrent-done-filename != "") ''
434 + cfg.settings.script-torrent-done-filename != null) ''
435 # Stack transmission_directories profile on top of
436 # any existing profile for script-torrent-done-filename
437 # FIXME: to be tested as I'm not sure it works well with NoNewPrivileges=