1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.aria2;
7
8 homeDir = "/var/lib/aria2";
9
10 settingsDir = "${homeDir}";
11 sessionFile = "${homeDir}/aria2.session";
12 downloadDir = "${homeDir}/Downloads";
13
14 rangesToStringList = map (x: builtins.toString x.from +"-"+ builtins.toString x.to);
15
16 settingsFile = pkgs.writeText "aria2.conf"
17 ''
18 dir=${cfg.downloadDir}
19 listen-port=${concatStringsSep "," (rangesToStringList cfg.listenPortRange)}
20 rpc-listen-port=${toString cfg.rpcListenPort}
21 '';
22
23in
24{
25 imports = [
26 (mkRemovedOptionModule [ "services" "aria2" "rpcSecret" ] "Use services.aria2.rpcSecretFile instead")
27 ];
28
29 options = {
30 services.aria2 = {
31 enable = mkOption {
32 type = types.bool;
33 default = false;
34 description = ''
35 Whether or not to enable the headless Aria2 daemon service.
36
37 Aria2 daemon can be controlled via the RPC interface using
38 one of many WebUI (http://localhost:6800/ by default).
39
40 Targets are downloaded to ${downloadDir} by default and are
41 accessible to users in the "aria2" group.
42 '';
43 };
44 openPorts = mkOption {
45 type = types.bool;
46 default = false;
47 description = ''
48 Open listen and RPC ports found in listenPortRange and rpcListenPort
49 options in the firewall.
50 '';
51 };
52 downloadDir = mkOption {
53 type = types.path;
54 default = downloadDir;
55 description = ''
56 Directory to store downloaded files.
57 '';
58 };
59 listenPortRange = mkOption {
60 type = types.listOf types.attrs;
61 default = [ { from = 6881; to = 6999; } ];
62 description = ''
63 Set UDP listening port range used by DHT(IPv4, IPv6) and UDP tracker.
64 '';
65 };
66 rpcListenPort = mkOption {
67 type = types.int;
68 default = 6800;
69 description = "Specify a port number for JSON-RPC/XML-RPC server to listen to. Possible Values: 1024-65535";
70 };
71 rpcSecretFile = mkOption {
72 type = types.path;
73 example = "/run/secrets/aria2-rpc-token.txt";
74 description = ''
75 A file containing the RPC secret authorization token.
76 Read https://aria2.github.io/manual/en/html/aria2c.html#rpc-auth to know how this option value is used.
77 '';
78 };
79 extraArguments = mkOption {
80 type = types.separatedString " ";
81 example = "--rpc-listen-all --remote-time=true";
82 default = "";
83 description = ''
84 Additional arguments to be passed to Aria2.
85 '';
86 };
87 };
88 };
89
90 config = mkIf cfg.enable {
91
92 # Need to open ports for proper functioning
93 networking.firewall = mkIf cfg.openPorts {
94 allowedUDPPortRanges = config.services.aria2.listenPortRange;
95 allowedTCPPorts = [ config.services.aria2.rpcListenPort ];
96 };
97
98 users.users.aria2 = {
99 group = "aria2";
100 uid = config.ids.uids.aria2;
101 description = "aria2 user";
102 home = homeDir;
103 createHome = false;
104 };
105
106 users.groups.aria2.gid = config.ids.gids.aria2;
107
108 systemd.tmpfiles.rules = [
109 "d '${homeDir}' 0770 aria2 aria2 - -"
110 "d '${config.services.aria2.downloadDir}' 0770 aria2 aria2 - -"
111 ];
112
113 systemd.services.aria2 = {
114 description = "aria2 Service";
115 after = [ "network.target" ];
116 wantedBy = [ "multi-user.target" ];
117 preStart = ''
118 if [[ ! -e "${sessionFile}" ]]
119 then
120 touch "${sessionFile}"
121 fi
122 cp -f "${settingsFile}" "${settingsDir}/aria2.conf"
123 echo "rpc-secret=$(cat "$CREDENTIALS_DIRECTORY/rpcSecretFile")" >> "${settingsDir}/aria2.conf"
124 '';
125
126 serviceConfig = {
127 Restart = "on-abort";
128 ExecStart = "${pkgs.aria2}/bin/aria2c --enable-rpc --conf-path=${settingsDir}/aria2.conf ${config.services.aria2.extraArguments} --save-session=${sessionFile}";
129 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
130 User = "aria2";
131 Group = "aria2";
132 LoadCredential="rpcSecretFile:${cfg.rpcSecretFile}";
133 };
134 };
135 };
136}