1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.fcron;
8
9 queuelen = if cfg.queuelen == null then "" else "-q ${toString cfg.queuelen}";
10
11 # Duplicate code, also found in cron.nix. Needs deduplication.
12 systemCronJobs =
13 ''
14 SHELL=${pkgs.bash}/bin/bash
15 PATH=${config.system.path}/bin:${config.system.path}/sbin
16 ${optionalString (config.services.cron.mailto != null) ''
17 MAILTO="${config.services.cron.mailto}"
18 ''}
19 NIX_CONF_DIR=/etc/nix
20 ${lib.concatStrings (map (job: job + "\n") config.services.cron.systemCronJobs)}
21 '';
22
23 allowdeny = target: users:
24 { source = pkgs.writeText "fcron.${target}" (concatStringsSep "\n" users);
25 target = "fcron.${target}";
26 mode = "644";
27 gid = config.ids.gids.fcron;
28 };
29
30in
31
32{
33
34 ###### interface
35
36 options = {
37
38 services.fcron = {
39
40 enable = mkOption {
41 type = types.bool;
42 default = false;
43 description = "Whether to enable the <command>fcron</command> daemon.";
44 };
45
46 allow = mkOption {
47 type = types.listOf types.str;
48 default = [ "all" ];
49 description = ''
50 Users allowed to use fcrontab and fcrondyn (one name per
51 line, <literal>all</literal> for everyone).
52 '';
53 };
54
55 deny = mkOption {
56 type = types.listOf types.str;
57 default = [];
58 description = "Users forbidden from using fcron.";
59 };
60
61 maxSerialJobs = mkOption {
62 type = types.int;
63 default = 1;
64 description = "Maximum number of serial jobs which can run simultaneously.";
65 };
66
67 queuelen = mkOption {
68 type = types.nullOr types.int;
69 default = null;
70 description = "Number of jobs the serial queue and the lavg queue can contain.";
71 };
72
73 systab = mkOption {
74 type = types.lines;
75 default = "";
76 description = ''The "system" crontab contents.'';
77 };
78 };
79
80 };
81
82
83 ###### implementation
84
85 config = mkIf cfg.enable {
86
87 services.fcron.systab = systemCronJobs;
88
89 environment.etc =
90 [ (allowdeny "allow" (cfg.allow))
91 (allowdeny "deny" cfg.deny)
92 # see man 5 fcron.conf
93 { source =
94 let
95 isSendmailWrapped =
96 lib.hasAttr "sendmail" config.security.wrappers;
97 sendmailPath =
98 if isSendmailWrapped then "/run/wrappers/bin/sendmail"
99 else "${config.system.path}/bin/sendmail";
100 in
101 pkgs.writeText "fcron.conf" ''
102 fcrontabs = /var/spool/fcron
103 pidfile = /var/run/fcron.pid
104 fifofile = /var/run/fcron.fifo
105 fcronallow = /etc/fcron.allow
106 fcrondeny = /etc/fcron.deny
107 shell = /bin/sh
108 sendmail = ${sendmailPath}
109 editor = ${pkgs.vim}/bin/vim
110 '';
111 target = "fcron.conf";
112 gid = config.ids.gids.fcron;
113 mode = "0644";
114 }
115 ];
116
117 environment.systemPackages = [ pkgs.fcron ];
118 users.extraUsers.fcron = {
119 uid = config.ids.uids.fcron;
120 home = "/var/spool/fcron";
121 group = "fcron";
122 };
123 users.groups.fcron.gid = config.ids.gids.fcron;
124
125 security.wrappers = {
126 fcrontab = {
127 source = "${pkgs.fcron}/bin/fcrontab";
128 owner = "fcron";
129 group = "fcron";
130 setgid = true;
131 };
132 fcrondyn = {
133 source = "${pkgs.fcron}/bin/fcrondyn";
134 owner = "fcron";
135 group = "fcron";
136 setgid = true;
137 };
138 fcronsighup = {
139 source = "${pkgs.fcron}/bin/fcronsighup";
140 group = "fcron";
141 };
142 };
143 systemd.services.fcron = {
144 description = "fcron daemon";
145 after = [ "local-fs.target" ];
146 wantedBy = [ "multi-user.target" ];
147
148 path = [ pkgs.fcron ];
149
150 preStart = ''
151 install \
152 --mode 0770 \
153 --owner fcron \
154 --group fcron \
155 --directory /var/spool/fcron
156 # load system crontab file
157 /run/wrappers/bin/fcrontab -u systab - < ${pkgs.writeText "systab" cfg.systab}
158 '';
159
160 serviceConfig = {
161 Type = "forking";
162 ExecStart = "${pkgs.fcron}/sbin/fcron -m ${toString cfg.maxSerialJobs} ${queuelen}";
163 };
164 };
165 };
166}