1{ config, pkgs, lib, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.netatalk;
8
9 extmapFile = pkgs.writeText "extmap.conf" cfg.extmap;
10
11 afpToString = x: if builtins.typeOf x == "bool"
12 then (if x then "true" else "false")
13 else toString x;
14
15 volumeConfig = name:
16 let vol = getAttr name cfg.volumes; in
17 "[${name}]\n " + (toString (
18 map
19 (key: "${key} = ${afpToString (getAttr key vol)}\n")
20 (attrNames vol)
21 ));
22
23 afpConf = ''[Global]
24 extmap file = ${extmapFile}
25 afp port = ${toString cfg.port}
26
27 ${cfg.extraConfig}
28
29 ${if cfg.homes.enable then ''[Homes]
30 ${optionalString (cfg.homes.path != "") "path = ${cfg.homes.path}"}
31 basedir regex = ${cfg.homes.basedirRegex}
32 ${cfg.homes.extraConfig}
33 '' else ""}
34
35 ${toString (map volumeConfig (attrNames cfg.volumes))}
36 '';
37
38 afpConfFile = pkgs.writeText "afp.conf" afpConf;
39
40in
41
42{
43 options = {
44 services.netatalk = {
45
46 enable = mkOption {
47 default = false;
48 description = "Whether to enable the Netatalk AFP fileserver.";
49 };
50
51 port = mkOption {
52 default = 548;
53 description = "TCP port to be used for AFP.";
54 };
55
56 extraConfig = mkOption {
57 type = types.lines;
58 default = "";
59 example = "uam list = uams_guest.so";
60 description = ''
61 Lines of configuration to add to the <literal>[Global]</literal> section.
62 See <literal>man apf.conf</literal> for more information.
63 '';
64 };
65
66 homes = {
67 enable = mkOption {
68 default = false;
69 description = "Enable sharing of the UNIX server user home directories.";
70 };
71
72 path = mkOption {
73 default = "";
74 example = "afp-data";
75 description = "Share not the whole user home but this subdirectory path.";
76 };
77
78 basedirRegex = mkOption {
79 example = "/home";
80 description = "Regex which matches the parent directory of the user homes.";
81 };
82
83 extraConfig = mkOption {
84 type = types.lines;
85 default = "";
86 description = ''
87 Lines of configuration to add to the <literal>[Homes]</literal> section.
88 See <literal>man apf.conf</literal> for more information.
89 '';
90 };
91 };
92
93 volumes = mkOption {
94 default = { };
95 type = types.attrsOf (types.attrsOf types.unspecified);
96 description =
97 ''
98 Set of AFP volumes to export.
99 See <literal>man apf.conf</literal> for more information.
100 '';
101 example =
102 { srv =
103 { path = "/srv";
104 "read only" = true;
105 "hosts allow" = "10.1.0.0/16 10.2.1.100 2001:0db8:1234::/48";
106 };
107 };
108 };
109
110 extmap = mkOption {
111 type = types.lines;
112 default = "";
113 description = ''
114 File name extension mappings.
115 See <literal>man extmap.conf</literal> for more information.
116 '';
117 };
118
119 };
120 };
121
122 config = mkIf cfg.enable {
123
124 systemd.services.netatalk = {
125 description = "Netatalk AFP fileserver for Macintosh clients";
126 unitConfig.Documentation = "man:afp.conf(5) man:netatalk(8) man:afpd(8) man:cnid_metad(8) man:cnid_dbd(8)";
127 after = [ "network.target" "avahi-daemon.service" ];
128 wantedBy = [ "multi-user.target" ];
129
130 path = [ pkgs.netatalk ];
131
132 serviceConfig = {
133 Type = "forking";
134 GuessMainPID = "no";
135 PIDFile = "/run/lock/netatalk";
136 ExecStartPre = "${pkgs.coreutils}/bin/mkdir -m 0755 -p /var/lib/netatalk/CNID";
137 ExecStart = "${pkgs.netatalk}/sbin/netatalk -F ${afpConfFile}";
138 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
139 ExecStop = "${pkgs.coreutils}/bin/kill -TERM $MAINPID";
140 Restart = "always";
141 RestartSec = 1;
142 };
143
144 };
145
146 security.pam.services.netatalk.unixAuth = true;
147
148 };
149
150}