diff --git a/nixos/modules/system/permown.nix b/nixos/modules/system/permown.nix index 5eaadc8..ab63ef5 100644 --- a/nixos/modules/system/permown.nix +++ b/nixos/modules/system/permown.nix @@ -3,8 +3,9 @@ with lib; let + cfg = config.system.permown; - nameGenerator = path: "permown.${replaceStrings [ "/" ] [ "_" ] path}"; + in { @@ -27,6 +28,15 @@ in default = null; type = types.nullOr types.str; }; + keepGoing = mkOption { + default = false; + type = types.bool; + description = '' + Whether to keep going when chowning or chmodding fails. + If set to false, then errors will cause the service to restart + instead. + ''; + }; owner = mkOption { type = types.str; }; path = mkOption { default = config._module.args.name; @@ -47,52 +57,91 @@ in }; config = - let plans = lib.attrValues cfg; - - in mkIf (plans != [ ]) { - + let + plans = attrValues cfg; + in + mkIf (plans != [ ]) { system.activationScripts.permown = let mkdir = { path, ... }: '' - ${pkgs.coreutils}/bin/mkdir -p ${path} + ${pkgs.coreutils}/bin/mkdir -p "${path}" ''; in concatMapStrings mkdir plans; - systemd.services = listToAttrs (flip map plans - ({ path, directory-mode, file-mode, owner, group, umask, ... }: { - name = nameGenerator path; - value = { - environment = { - DIR_MODE = directory-mode; - FILE_MODE = file-mode; - OWNER_GROUP = "${owner}:${group}"; - ROOT_PATH = path; - }; - path = [ pkgs.coreutils pkgs.findutils pkgs.inotifyTools ]; - serviceConfig = { - ExecStart = pkgs.writers.writeDash "permown" '' - set -efu - find "$ROOT_PATH" -exec chown -h "$OWNER_GROUP" {} + - find "$ROOT_PATH" -type d -exec chmod "$DIR_MODE" {} + - find "$ROOT_PATH" -type f -exec chmod "$FILE_MODE" {} + - ''; - PrivateTmp = true; - #Restart = "always"; - #RestartSec = 10; - UMask = umask; - }; - wantedBy = [ "multi-user.target" ]; - }; - })); + systemd.services = + let + nameGenerator = { path, ... }: + "permown.${replaceStrings [ "/" ] [ "_" ] path}"; + serviceDefinition = + { path, directory-mode, file-mode, owner, group, umask, keepGoing, ... }: + { + environment = { + DIR_MODE = directory-mode; + FILE_MODE = file-mode; + OWNER_GROUP = "${owner}:${group}"; + ROOT_PATH = path; + }; + path = [ + pkgs.coreutils + pkgs.findutils + pkgs.inotifyTools + ]; + serviceConfig = { + ExecStart = + let + continuable = command: + if keepGoing + then "{ ${command}; } || :" + else command; + in + pkgs.writers.writeDash "permown" '' + set -efu + + find "$ROOT_PATH" -exec chown -h "$OWNER_GROUP" {} + + find "$ROOT_PATH" -type d -exec chmod "$DIR_MODE" {} + + find "$ROOT_PATH" -type f -exec chmod "$FILE_MODE" {} + + + paths=/tmp/paths + rm -f "$paths" + mkfifo "$paths" + + inotifywait -mrq -e CREATE --format %w%f "$ROOT_PATH" > "$paths" & + inotifywaitpid=$! + + trap cleanup EXIT + cleanup() { + kill "$inotifywaitpid" + } + + while read -r path + do + if test -d "$path"; then + cleanup + exec "$0" "$@" + fi + ${continuable ''chown -h "$OWNER_GROUP" "$path"''} + if test -f "$path"; then + ${continuable ''chmod "$FILE_MODE" "$path"''} + fi + done < "$paths" + ''; + PrivateTmp = true; + Restart = "always"; + RestartSec = 10; + UMask = umask; + }; + wantedBy = [ "multi-user.target" ]; + }; + in + listToAttrs (map + (plan: + { + name = nameGenerator plan; + value = serviceDefinition plan; + }) + plans); - systemd.timers = listToAttrs (flip map plans ({ path, timer, ... }: { - name = nameGenerator path; - value = { - wantedBy = [ "multi-user.target" ]; - timerConfig.OnCalendar = timer; - }; - })); };