{ config, lib, pkgs, ... }: with lib; let escape = escapeShellArg; cfg = config.rbackup; start = name: plan: let login-name = "root"; identity = plan.sshKeyPath; ssh = "ssh -i ${escape identity}"; in pkgs.writers.writeBash "backup.${name}" '' set -efu rsync_src=${escape plan.src} rsync_dst=${escape plan.dst} echo >&2 "update snapshot current; $rsync_dst <- $rsync_src" rsync \ --rsh=${escape ssh} \ --append -avz \ ${optionalString plan.delete "--delete"} \ "$rsync_src/" \ "$rsync_dst" ''; in { options.rbackup = { plans = mkOption { default = { }; type = types.attrsOf (types.submodule ({ config, ... }: { options = { sshKeyPath = mkOption { type = types.str; }; src = mkOption { type = types.str; }; dst = mkOption { type = types.str; }; startAt = mkOption { default = "hourly"; type = with types; nullOr str; # TODO systemd.time(7)'s calendar event }; delete = mkOption { type = types.bool; default = true; description = "delete old files (adds the --delete argument to rsync)"; }; timerConfig = mkOption { type = with types; attrsOf str; default = optionalAttrs (config.startAt != null) { OnCalendar = config.startAt; }; }; }; })); }; }; config = { systemd.services = mapAttrs' (name: plan: nameValuePair "rbackup.${name}" { path = with pkgs; [ coreutils gnused openssh rsync util-linux ]; restartIfChanged = false; serviceConfig = rec { ExecStart = start name plan; SyslogIdentifier = ExecStart.name; Type = "oneshot"; }; }) cfg.plans; systemd.timers = mapAttrs' (name: plan: nameValuePair "rbackup.${name}" { wantedBy = [ "timers.target" ]; timerConfig = plan.timerConfig; }) cfg.plans; }; }