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.users.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 setuid = true;
132 };
133 fcrondyn = {
134 source = "${pkgs.fcron}/bin/fcrondyn";
135 owner = "fcron";
136 group = "fcron";
137 setgid = true;
138 };
139 fcronsighup = {
140 source = "${pkgs.fcron}/bin/fcronsighup";
141 group = "fcron";
142 };
143 };
144 systemd.services.fcron = {
145 description = "fcron daemon";
146 after = [ "local-fs.target" ];
147 wantedBy = [ "multi-user.target" ];
148
149 path = [ pkgs.fcron ];
150
151 preStart = ''
152 install \
153 --mode 0770 \
154 --owner fcron \
155 --group fcron \
156 --directory /var/spool/fcron
157 # load system crontab file
158 /run/wrappers/bin/fcrontab -u systab - < ${pkgs.writeText "systab" cfg.systab}
159 '';
160
161 serviceConfig = {
162 Type = "forking";
163 ExecStart = "${pkgs.fcron}/sbin/fcron -m ${toString cfg.maxSerialJobs} ${queuelen}";
164 };
165 };
166 };
167}