trilium-next-{desktop,server}: init at 0.90.12 (#356930)
[NixPkgs.git] / nixos / modules / services / networking / hylafax / systemd.nix
blob8850d961e63d4b727fdc4d2877c8eafd5d1d4b4a
2   config,
3   lib,
4   pkgs,
5   ...
6 }:
8 let
10   inherit (lib) mkIf mkMerge;
11   inherit (lib) concatStringsSep optionalString;
13   cfg = config.services.hylafax;
14   mapModems = lib.forEach (lib.attrValues cfg.modems);
16   mkConfigFile =
17     name: conf:
18     # creates hylafax config file,
19     # makes sure "Include" is listed *first*
20     let
21       mkLines = lib.flip lib.pipe [
22         (lib.mapAttrsToList (key: map (val: "${key}: ${val}")))
23         lib.concatLists
24       ];
25       include = mkLines { Include = conf.Include or [ ]; };
26       other = mkLines (conf // { Include = [ ]; });
27     in
28     pkgs.writeText "hylafax-config${name}" (concatStringsSep "\n" (include ++ other));
30   globalConfigPath = mkConfigFile "" cfg.faxqConfig;
32   modemConfigPath =
33     let
34       mkModemConfigFile =
35         { config, name, ... }: mkConfigFile ".${name}" (cfg.commonModemConfig // config);
36       mkLine =
37         { name, type, ... }@modem:
38         ''
39           # check if modem config file exists:
40           test -f "${pkgs.hylafaxplus}/spool/config/${type}"
41           ln \
42             --symbolic \
43             --no-target-directory \
44             "${mkModemConfigFile modem}" \
45             "$out/config.${name}"
46         '';
47     in
48     pkgs.runCommand "hylafax-config-modems" {
49       preferLocalBuild = true;
50     } ''mkdir --parents "$out/" ${concatStringsSep "\n" (mapModems mkLine)}'';
52   setupSpoolScript = pkgs.substituteAll {
53     name = "hylafax-setup-spool.sh";
54     src = ./spool.sh;
55     isExecutable = true;
56     faxuser = "uucp";
57     faxgroup = "uucp";
58     lockPath = "/var/lock";
59     inherit globalConfigPath modemConfigPath;
60     inherit (cfg) sendmailPath spoolAreaPath userAccessFile;
61     inherit (pkgs) hylafaxplus runtimeShell;
62   };
64   waitFaxqScript = pkgs.substituteAll {
65     # This script checks the modems status files
66     # and waits until all modems report readiness.
67     name = "hylafax-faxq-wait-start.sh";
68     src = ./faxq-wait.sh;
69     isExecutable = true;
70     timeoutSec = toString 10;
71     inherit (cfg) spoolAreaPath;
72     inherit (pkgs) runtimeShell;
73   };
75   sockets.hylafax-hfaxd = {
76     description = "HylaFAX server socket";
77     documentation = [ "man:hfaxd(8)" ];
78     wantedBy = [ "multi-user.target" ];
79     listenStreams = [ "127.0.0.1:4559" ];
80     socketConfig.FreeBind = true;
81     socketConfig.Accept = true;
82   };
84   paths.hylafax-faxq = {
85     description = "HylaFAX queue manager sendq watch";
86     documentation = [
87       "man:faxq(8)"
88       "man:sendq(5)"
89     ];
90     wantedBy = [ "multi-user.target" ];
91     pathConfig.PathExistsGlob = [ "${cfg.spoolAreaPath}/sendq/q*" ];
92   };
94   timers = mkMerge [
95     (mkIf (cfg.faxcron.enable.frequency != null) { hylafax-faxcron.timerConfig.Persistent = true; })
96     (mkIf (cfg.faxqclean.enable.frequency != null) { hylafax-faxqclean.timerConfig.Persistent = true; })
97   ];
99   hardenService =
100     # Add some common systemd service hardening settings,
101     # but allow each service (here) to override
102     # settings by explicitly setting those to `null`.
103     # More hardening would be nice but makes
104     # customizing hylafax setups very difficult.
105     # If at all, it should only be added along
106     # with some options to customize it.
107     let
108       hardening = {
109         PrivateDevices = true; # breaks /dev/tty...
110         PrivateNetwork = true;
111         PrivateTmp = true;
112         #ProtectClock = true;  # breaks /dev/tty... (why?)
113         ProtectControlGroups = true;
114         #ProtectHome = true;  # breaks custom spool dirs
115         ProtectKernelLogs = true;
116         ProtectKernelModules = true;
117         ProtectKernelTunables = true;
118         #ProtectSystem = "strict";  # breaks custom spool dirs
119         RestrictNamespaces = true;
120         RestrictRealtime = true;
121       };
122       filter = key: value: (value != null) || !(lib.hasAttr key hardening);
123       apply = service: lib.filterAttrs filter (hardening // (service.serviceConfig or { }));
124     in
125     service: service // { serviceConfig = apply service; };
127   services.hylafax-spool = {
128     description = "HylaFAX spool area preparation";
129     documentation = [ "man:hylafax-server(4)" ];
130     script = ''
131       ${setupSpoolScript}
132       cd "${cfg.spoolAreaPath}"
133       ${cfg.spoolExtraInit}
134       if ! test -f "${cfg.spoolAreaPath}/etc/hosts.hfaxd"
135       then
136         echo hosts.hfaxd is missing
137         exit 1
138       fi
139     '';
140     serviceConfig.ExecStop = "${setupSpoolScript}";
141     serviceConfig.RemainAfterExit = true;
142     serviceConfig.Type = "oneshot";
143     unitConfig.RequiresMountsFor = [ cfg.spoolAreaPath ];
144   };
146   services.hylafax-faxq = {
147     description = "HylaFAX queue manager";
148     documentation = [ "man:faxq(8)" ];
149     requires = [ "hylafax-spool.service" ];
150     after = [ "hylafax-spool.service" ];
151     wants = mapModems ({ name, ... }: "hylafax-faxgetty@${name}.service");
152     wantedBy = mkIf cfg.autostart [ "multi-user.target" ];
153     serviceConfig.Type = "forking";
154     serviceConfig.ExecStart = ''${pkgs.hylafaxplus}/spool/bin/faxq -q "${cfg.spoolAreaPath}"'';
155     # This delays the "readiness" of this service until
156     # all modems are initialized (or a timeout is reached).
157     # Otherwise, sending a fax with the fax service
158     # stopped will always yield a failed send attempt:
159     # The fax service is started when the job is created with
160     # `sendfax`, but modems need some time to initialize.
161     serviceConfig.ExecStartPost = [ "${waitFaxqScript}" ];
162     # faxquit fails if the pipe is already gone
163     # (e.g. the service is already stopping)
164     serviceConfig.ExecStop = ''-${pkgs.hylafaxplus}/spool/bin/faxquit -q "${cfg.spoolAreaPath}"'';
165     # disable some systemd hardening settings
166     serviceConfig.PrivateDevices = null;
167     serviceConfig.RestrictRealtime = null;
168   };
170   services."hylafax-hfaxd@" = {
171     description = "HylaFAX server";
172     documentation = [ "man:hfaxd(8)" ];
173     after = [ "hylafax-faxq.service" ];
174     requires = [ "hylafax-faxq.service" ];
175     serviceConfig.StandardInput = "socket";
176     serviceConfig.StandardOutput = "socket";
177     serviceConfig.ExecStart = ''${pkgs.hylafaxplus}/spool/bin/hfaxd -q "${cfg.spoolAreaPath}" -d -I'';
178     unitConfig.RequiresMountsFor = [ cfg.userAccessFile ];
179     # disable some systemd hardening settings
180     serviceConfig.PrivateDevices = null;
181     serviceConfig.PrivateNetwork = null;
182   };
184   services.hylafax-faxcron = rec {
185     description = "HylaFAX spool area maintenance";
186     documentation = [ "man:faxcron(8)" ];
187     after = [ "hylafax-spool.service" ];
188     requires = [ "hylafax-spool.service" ];
189     wantedBy = mkIf cfg.faxcron.enable.spoolInit requires;
190     startAt = mkIf (cfg.faxcron.enable.frequency != null) cfg.faxcron.enable.frequency;
191     serviceConfig.ExecStart = concatStringsSep " " [
192       "${pkgs.hylafaxplus}/spool/bin/faxcron"
193       ''-q "${cfg.spoolAreaPath}"''
194       ''-info ${toString cfg.faxcron.infoDays}''
195       ''-log  ${toString cfg.faxcron.logDays}''
196       ''-rcv  ${toString cfg.faxcron.rcvDays}''
197     ];
198   };
200   services.hylafax-faxqclean = rec {
201     description = "HylaFAX spool area queue cleaner";
202     documentation = [ "man:faxqclean(8)" ];
203     after = [ "hylafax-spool.service" ];
204     requires = [ "hylafax-spool.service" ];
205     wantedBy = mkIf cfg.faxqclean.enable.spoolInit requires;
206     startAt = mkIf (cfg.faxqclean.enable.frequency != null) cfg.faxqclean.enable.frequency;
207     serviceConfig.ExecStart = concatStringsSep " " [
208       "${pkgs.hylafaxplus}/spool/bin/faxqclean"
209       ''-q "${cfg.spoolAreaPath}"''
210       "-v"
211       (optionalString (cfg.faxqclean.archiving != "never") "-a")
212       (optionalString (cfg.faxqclean.archiving == "always") "-A")
213       ''-j ${toString (cfg.faxqclean.doneqMinutes * 60)}''
214       ''-d ${toString (cfg.faxqclean.docqMinutes * 60)}''
215     ];
216   };
218   mkFaxgettyService =
219     { name, ... }:
220     lib.nameValuePair "hylafax-faxgetty@${name}" rec {
221       description = "HylaFAX faxgetty for %I";
222       documentation = [ "man:faxgetty(8)" ];
223       bindsTo = [ "dev-%i.device" ];
224       requires = [ "hylafax-spool.service" ];
225       after = bindsTo ++ requires;
226       before = [
227         "hylafax-faxq.service"
228         "getty.target"
229       ];
230       unitConfig.StopWhenUnneeded = true;
231       unitConfig.AssertFileNotEmpty = "${cfg.spoolAreaPath}/etc/config.%I";
232       serviceConfig.UtmpIdentifier = "%I";
233       serviceConfig.TTYPath = "/dev/%I";
234       serviceConfig.Restart = "always";
235       serviceConfig.KillMode = "process";
236       serviceConfig.IgnoreSIGPIPE = false;
237       serviceConfig.ExecStart = ''-${pkgs.hylafaxplus}/spool/bin/faxgetty -q "${cfg.spoolAreaPath}" /dev/%I'';
238       # faxquit fails if the pipe is already gone
239       # (e.g. the service is already stopping)
240       serviceConfig.ExecStop = ''-${pkgs.hylafaxplus}/spool/bin/faxquit -q "${cfg.spoolAreaPath}" %I'';
241       # disable some systemd hardening settings
242       serviceConfig.PrivateDevices = null;
243       serviceConfig.RestrictRealtime = null;
244     };
246   modemServices = lib.listToAttrs (mapModems mkFaxgettyService);
251   config.systemd = mkIf cfg.enable {
252     inherit sockets timers paths;
253     services = lib.mapAttrs (lib.const hardenService) (services // modemServices);
254   };