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 = lib.mdDoc "Whether to enable the {command}`fcron` daemon.";
44 };
45
46 allow = mkOption {
47 type = types.listOf types.str;
48 default = [ "all" ];
49 description = lib.mdDoc ''
50 Users allowed to use fcrontab and fcrondyn (one name per
51 line, `all` for everyone).
52 '';
53 };
54
55 deny = mkOption {
56 type = types.listOf types.str;
57 default = [];
58 description = lib.mdDoc "Users forbidden from using fcron.";
59 };
60
61 maxSerialJobs = mkOption {
62 type = types.int;
63 default = 1;
64 description = lib.mdDoc "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 = lib.mdDoc "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 = lib.mdDoc ''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 = listToAttrs
90 (map (x: { name = x.target; value = x; })
91 [ (allowdeny "allow" (cfg.allow))
92 (allowdeny "deny" cfg.deny)
93 # see man 5 fcron.conf
94 { source =
95 let
96 isSendmailWrapped =
97 lib.hasAttr "sendmail" config.security.wrappers;
98 sendmailPath =
99 if isSendmailWrapped then "/run/wrappers/bin/sendmail"
100 else "${config.system.path}/bin/sendmail";
101 in
102 pkgs.writeText "fcron.conf" ''
103 fcrontabs = /var/spool/fcron
104 pidfile = /run/fcron.pid
105 fifofile = /run/fcron.fifo
106 fcronallow = /etc/fcron.allow
107 fcrondeny = /etc/fcron.deny
108 shell = /bin/sh
109 sendmail = ${sendmailPath}
110 editor = ${pkgs.vim}/bin/vim
111 '';
112 target = "fcron.conf";
113 gid = config.ids.gids.fcron;
114 mode = "0644";
115 }
116 ]);
117
118 environment.systemPackages = [ pkgs.fcron ];
119 users.users.fcron = {
120 uid = config.ids.uids.fcron;
121 home = "/var/spool/fcron";
122 group = "fcron";
123 };
124 users.groups.fcron.gid = config.ids.gids.fcron;
125
126 security.wrappers = {
127 fcrontab = {
128 source = "${pkgs.fcron}/bin/fcrontab";
129 owner = "fcron";
130 group = "fcron";
131 setgid = true;
132 setuid = true;
133 };
134 fcrondyn = {
135 source = "${pkgs.fcron}/bin/fcrondyn";
136 owner = "fcron";
137 group = "fcron";
138 setgid = true;
139 setuid = false;
140 };
141 fcronsighup = {
142 source = "${pkgs.fcron}/bin/fcronsighup";
143 owner = "root";
144 group = "fcron";
145 setuid = true;
146 };
147 };
148 systemd.services.fcron = {
149 description = "fcron daemon";
150 wantedBy = [ "multi-user.target" ];
151
152 path = [ pkgs.fcron ];
153
154 preStart = ''
155 install \
156 --mode 0770 \
157 --owner fcron \
158 --group fcron \
159 --directory /var/spool/fcron
160 # load system crontab file
161 /run/wrappers/bin/fcrontab -u systab - < ${pkgs.writeText "systab" cfg.systab}
162 '';
163
164 serviceConfig = {
165 Type = "forking";
166 ExecStart = "${pkgs.fcron}/sbin/fcron -m ${toString cfg.maxSerialJobs} ${queuelen}";
167 };
168 };
169 };
170}