summary refs log tree commit diff
path: root/nixos/modules/services/scheduling
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/scheduling')
-rw-r--r--nixos/modules/services/scheduling/atd.nix106
-rw-r--r--nixos/modules/services/scheduling/cron.nix138
-rw-r--r--nixos/modules/services/scheduling/fcron.nix170
3 files changed, 414 insertions, 0 deletions
diff --git a/nixos/modules/services/scheduling/atd.nix b/nixos/modules/services/scheduling/atd.nix
new file mode 100644
index 00000000000..9bb0191ee46
--- /dev/null
+++ b/nixos/modules/services/scheduling/atd.nix
@@ -0,0 +1,106 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.atd;
+
+  inherit (pkgs) at;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.atd.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable the <command>at</command> daemon, a command scheduler.
+      '';
+    };
+
+    services.atd.allowEveryone = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to make <filename>/var/spool/at{jobs,spool}</filename>
+        writeable by everyone (and sticky).  This is normally not
+        needed since the <command>at</command> commands are
+        setuid/setgid <literal>atd</literal>.
+     '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    # Not wrapping "batch" because it's a shell script (kernel drops perms
+    # anyway) and it's patched to invoke the "at" setuid wrapper.
+    security.wrappers = builtins.listToAttrs (
+      map (program: { name = "${program}"; value = {
+      source = "${at}/bin/${program}";
+      owner = "atd";
+      group = "atd";
+      setuid = true;
+      setgid = true;
+    };}) [ "at" "atq" "atrm" ]);
+
+    environment.systemPackages = [ at ];
+
+    security.pam.services.atd = {};
+
+    users.users.atd =
+      {
+        uid = config.ids.uids.atd;
+        group = "atd";
+        description = "atd user";
+        home = "/var/empty";
+      };
+
+    users.groups.atd.gid = config.ids.gids.atd;
+
+    systemd.services.atd = {
+      description = "Job Execution Daemon (atd)";
+      wantedBy = [ "multi-user.target" ];
+
+      path = [ at ];
+
+      preStart = ''
+        # Snippets taken and adapted from the original `install' rule of
+        # the makefile.
+
+        # We assume these values are those actually used in Nixpkgs for
+        # `at'.
+        spooldir=/var/spool/atspool
+        jobdir=/var/spool/atjobs
+        etcdir=/etc/at
+
+        install -dm755 -o atd -g atd "$etcdir"
+        spool_and_job_dir_perms=${if cfg.allowEveryone then "1777" else "1770"}
+        install -dm"$spool_and_job_dir_perms" -o atd -g atd "$spooldir" "$jobdir"
+        if [ ! -f "$etcdir"/at.deny ]; then
+            touch "$etcdir"/at.deny
+            chown root:atd "$etcdir"/at.deny
+            chmod 640 "$etcdir"/at.deny
+        fi
+        if [ ! -f "$jobdir"/.SEQ ]; then
+            touch "$jobdir"/.SEQ
+            chown atd:atd "$jobdir"/.SEQ
+            chmod 600 "$jobdir"/.SEQ
+        fi
+      '';
+
+      script = "atd";
+
+      serviceConfig.Type = "forking";
+    };
+  };
+}
diff --git a/nixos/modules/services/scheduling/cron.nix b/nixos/modules/services/scheduling/cron.nix
new file mode 100644
index 00000000000..1fac54003cb
--- /dev/null
+++ b/nixos/modules/services/scheduling/cron.nix
@@ -0,0 +1,138 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  # Put all the system cronjobs together.
+  systemCronJobsFile = pkgs.writeText "system-crontab"
+    ''
+      SHELL=${pkgs.bash}/bin/bash
+      PATH=${config.system.path}/bin:${config.system.path}/sbin
+      ${optionalString (config.services.cron.mailto != null) ''
+        MAILTO="${config.services.cron.mailto}"
+      ''}
+      NIX_CONF_DIR=/etc/nix
+      ${lib.concatStrings (map (job: job + "\n") config.services.cron.systemCronJobs)}
+    '';
+
+  # Vixie cron requires build-time configuration for the sendmail path.
+  cronNixosPkg = pkgs.cron.override {
+    # The mail.nix nixos module, if there is any local mail system enabled,
+    # should have sendmail in this path.
+    sendmailPath = "/run/wrappers/bin/sendmail";
+  };
+
+  allFiles =
+    optional (config.services.cron.systemCronJobs != []) systemCronJobsFile
+    ++ config.services.cron.cronFiles;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.cron = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the Vixie cron daemon.";
+      };
+
+      mailto = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = "Email address to which job output will be mailed.";
+      };
+
+      systemCronJobs = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = literalExpression ''
+          [ "* * * * *  test   ls -l / > /tmp/cronout 2>&1"
+            "* * * * *  eelco  echo Hello World > /home/eelco/cronout"
+          ]
+        '';
+        description = ''
+          A list of Cron jobs to be appended to the system-wide
+          crontab.  See the manual page for crontab for the expected
+          format. If you want to get the results mailed you must setuid
+          sendmail. See <option>security.wrappers</option>
+
+          If neither /var/cron/cron.deny nor /var/cron/cron.allow exist only root
+          is allowed to have its own crontab file. The /var/cron/cron.deny file
+          is created automatically for you, so every user can use a crontab.
+
+          Many nixos modules set systemCronJobs, so if you decide to disable vixie cron
+          and enable another cron daemon, you may want it to get its system crontab
+          based on systemCronJobs.
+        '';
+      };
+
+      cronFiles = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = ''
+          A list of extra crontab files that will be read and appended to the main
+          crontab file when the cron service starts.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkMerge [
+
+    { services.cron.enable = mkDefault (allFiles != []); }
+    (mkIf (config.services.cron.enable) {
+      security.wrappers.crontab =
+        { setuid = true;
+          owner = "root";
+          group = "root";
+          source = "${cronNixosPkg}/bin/crontab";
+        };
+      environment.systemPackages = [ cronNixosPkg ];
+      environment.etc.crontab =
+        { source = pkgs.runCommand "crontabs" { inherit allFiles; preferLocalBuild = true; }
+            ''
+              touch $out
+              for i in $allFiles; do
+                cat "$i" >> $out
+              done
+            '';
+          mode = "0600"; # Cron requires this.
+        };
+
+      systemd.services.cron =
+        { description = "Cron Daemon";
+
+          wantedBy = [ "multi-user.target" ];
+
+          preStart =
+            ''
+              mkdir -m 710 -p /var/cron
+
+              # By default, allow all users to create a crontab.  This
+              # is denoted by the existence of an empty cron.deny file.
+              if ! test -e /var/cron/cron.allow -o -e /var/cron/cron.deny; then
+                  touch /var/cron/cron.deny
+              fi
+            '';
+
+          restartTriggers = [ config.time.timeZone ];
+          serviceConfig.ExecStart = "${cronNixosPkg}/bin/cron -n";
+        };
+
+    })
+
+  ];
+
+}
diff --git a/nixos/modules/services/scheduling/fcron.nix b/nixos/modules/services/scheduling/fcron.nix
new file mode 100644
index 00000000000..acaa995f739
--- /dev/null
+++ b/nixos/modules/services/scheduling/fcron.nix
@@ -0,0 +1,170 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.fcron;
+
+  queuelen = if cfg.queuelen == null then "" else "-q ${toString cfg.queuelen}";
+
+  # Duplicate code, also found in cron.nix. Needs deduplication.
+  systemCronJobs =
+    ''
+      SHELL=${pkgs.bash}/bin/bash
+      PATH=${config.system.path}/bin:${config.system.path}/sbin
+      ${optionalString (config.services.cron.mailto != null) ''
+        MAILTO="${config.services.cron.mailto}"
+      ''}
+      NIX_CONF_DIR=/etc/nix
+      ${lib.concatStrings (map (job: job + "\n") config.services.cron.systemCronJobs)}
+    '';
+
+  allowdeny = target: users:
+    { source = pkgs.writeText "fcron.${target}" (concatStringsSep "\n" users);
+      target = "fcron.${target}";
+      mode = "644";
+      gid = config.ids.gids.fcron;
+    };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.fcron = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the <command>fcron</command> daemon.";
+      };
+
+      allow = mkOption {
+        type = types.listOf types.str;
+        default = [ "all" ];
+        description = ''
+          Users allowed to use fcrontab and fcrondyn (one name per
+          line, <literal>all</literal> for everyone).
+        '';
+      };
+
+      deny = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = "Users forbidden from using fcron.";
+      };
+
+      maxSerialJobs = mkOption {
+        type = types.int;
+        default = 1;
+        description = "Maximum number of serial jobs which can run simultaneously.";
+      };
+
+      queuelen = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = "Number of jobs the serial queue and the lavg queue can contain.";
+      };
+
+      systab = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''The "system" crontab contents.'';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.fcron.systab = systemCronJobs;
+
+    environment.etc = listToAttrs
+      (map (x: { name = x.target; value = x; })
+      [ (allowdeny "allow" (cfg.allow))
+        (allowdeny "deny" cfg.deny)
+        # see man 5 fcron.conf
+        { source =
+            let
+              isSendmailWrapped =
+                lib.hasAttr "sendmail" config.security.wrappers;
+              sendmailPath =
+                if isSendmailWrapped then "/run/wrappers/bin/sendmail"
+                else "${config.system.path}/bin/sendmail";
+            in
+            pkgs.writeText "fcron.conf" ''
+              fcrontabs   =       /var/spool/fcron
+              pidfile     =       /run/fcron.pid
+              fifofile    =       /run/fcron.fifo
+              fcronallow  =       /etc/fcron.allow
+              fcrondeny   =       /etc/fcron.deny
+              shell       =       /bin/sh
+              sendmail    =       ${sendmailPath}
+              editor      =       ${pkgs.vim}/bin/vim
+            '';
+          target = "fcron.conf";
+          gid = config.ids.gids.fcron;
+          mode = "0644";
+        }
+      ]);
+
+    environment.systemPackages = [ pkgs.fcron ];
+    users.users.fcron = {
+      uid = config.ids.uids.fcron;
+      home = "/var/spool/fcron";
+      group = "fcron";
+    };
+    users.groups.fcron.gid = config.ids.gids.fcron;
+
+    security.wrappers = {
+      fcrontab = {
+        source = "${pkgs.fcron}/bin/fcrontab";
+        owner = "fcron";
+        group = "fcron";
+        setgid = true;
+        setuid = true;
+      };
+      fcrondyn = {
+        source = "${pkgs.fcron}/bin/fcrondyn";
+        owner = "fcron";
+        group = "fcron";
+        setgid = true;
+        setuid = false;
+      };
+      fcronsighup = {
+        source = "${pkgs.fcron}/bin/fcronsighup";
+        owner = "root";
+        group = "fcron";
+        setuid = true;
+      };
+    };
+    systemd.services.fcron = {
+      description = "fcron daemon";
+      wantedBy = [ "multi-user.target" ];
+
+      path = [ pkgs.fcron ];
+
+      preStart = ''
+        install \
+          --mode 0770 \
+          --owner fcron \
+          --group fcron \
+          --directory /var/spool/fcron
+        # load system crontab file
+        /run/wrappers/bin/fcrontab -u systab - < ${pkgs.writeText "systab" cfg.systab}
+      '';
+
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "${pkgs.fcron}/sbin/fcron -m ${toString cfg.maxSerialJobs} ${queuelen}";
+      };
+    };
+  };
+}