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