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}