{ config, lib, utils, pkgs, ... }: with utils; with lib; let cfg = config.services.lvm-snapshots; snapshotMount = name: "${cfg.mountPoint}/${if isNull cfg.snapshots."${name}".mountName then name else cfg.snapshots."${name}".mountName}"; snapshotName = name: "${name}-snapshot"; snapshotConfig = { options = { LV = mkOption { type = types.str; }; VG = mkOption { type = types.str; }; mountName = mkOption { type = types.nullOr types.str; default = null; }; cowSize = mkOption { type = types.str; default = "-l20%ORIGIN"; }; readOnly = mkOption { type = types.bool; default = true; }; persist = mkOption { type = types.bool; default = false; }; }; }; in { options = { services.lvm-snapshots = { snapshots = mkOption { type = types.attrsOf (types.submodule snapshotConfig); default = {}; }; mountPoint = mkOption { type = types.path; default = "/mnt"; }; }; }; config = mkIf (cfg != {}) { boot.kernelModules = [ "dm_snapshot" ]; system.activationScripts = mapAttrs' (name: scfg: nameValuePair ("lvm-mountpoint" + name) '' mkdir -p ${snapshotMount name} '') cfg.snapshots; systemd.services = mapAttrs' (name: scfg: nameValuePair ("lvm-snapshot@" + escapeSystemdPath name) { enable = true; description = "LVM-snapshot of ${scfg.VG}/${scfg.LV}"; bindsTo = ["${escapeSystemdPath "/dev/${scfg.VG}/${scfg.LV}"}.device"]; after = ["${escapeSystemdPath "/dev/${scfg.VG}/${scfg.LV}"}.device"]; unitConfig = { StopWhenUnneeded = !scfg.persist; AssertPathIsDirectory = "/var/lock"; }; path = with pkgs; [ devicemapper utillinux ]; script = '' ( flock -xn -E 4 9 if [[ "$?" -ne 0 ]]; then exit $? fi lvcreate -s ${scfg.cowSize} --name ${snapshotName name} ${scfg.VG}/${scfg.LV} sleep infinity & ) 9>/var/lock/lvm-snapshot.${scfg.VG} ''; preStop = '' lvremove -f ${scfg.VG}/${snapshotName name} ''; serviceConfig = with pkgs; { Type = "forking"; RestartForceExitStatus = [ "4" ]; RestartSec = "5min"; }; }) cfg.snapshots; systemd.mounts = mapAttrsToList (name: scfg: { enable = true; unitConfig = { AssertPathIsDirectory = snapshotMount name; StopWhenUnneeded = !scfg.persist; }; bindsTo = [ ("lvm-snapshot@" + escapeSystemdPath name + ".service") ]; after = [ ("lvm-snapshot@" + escapeSystemdPath name + ".service") ]; options = concatStringsSep "," ([ "noauto" ] ++ optional scfg.readOnly "ro"); where = snapshotMount name; what = "/dev/" + scfg.VG + "/" + snapshotName name; }) cfg.snapshots; }; }