mirror of
https://github.com/LnL7/nix-darwin.git
synced 2024-12-15 17:51:01 +00:00
etc: check for existing files during checks stage
This ensures that activation fails early if there are any `/etc` files with unexpected state, rather than leaving the system half-activated.
This commit is contained in:
parent
c91c351943
commit
4eb1c549a9
3 changed files with 83 additions and 44 deletions
|
@ -3,8 +3,6 @@
|
|||
with lib;
|
||||
|
||||
let
|
||||
inherit (pkgs) stdenv;
|
||||
|
||||
cfg = config.services.activate-system;
|
||||
in
|
||||
|
||||
|
@ -36,6 +34,7 @@ in
|
|||
# Prevent the current configuration from being garbage-collected.
|
||||
ln -sfn /run/current-system /nix/var/nix/gcroots/current-system
|
||||
|
||||
${config.system.activationScripts.etcChecks.text}
|
||||
${config.system.activationScripts.etc.text}
|
||||
${config.system.activationScripts.keyboard.text}
|
||||
'';
|
||||
|
|
|
@ -52,6 +52,9 @@ in
|
|||
|
||||
${cfg.activationScripts.preActivation.text}
|
||||
|
||||
# We run `etcChecks` again just in case someone runs `activate`
|
||||
# directly without `activate-user`.
|
||||
${cfg.activationScripts.etcChecks.text}
|
||||
${cfg.activationScripts.extraActivation.text}
|
||||
${cfg.activationScripts.groups.text}
|
||||
${cfg.activationScripts.users.text}
|
||||
|
@ -99,6 +102,7 @@ in
|
|||
|
||||
${cfg.activationScripts.createRun.text}
|
||||
${cfg.activationScripts.checks.text}
|
||||
${cfg.activationScripts.etcChecks.text}
|
||||
${cfg.activationScripts.extraUserActivation.text}
|
||||
${cfg.activationScripts.userDefaults.text}
|
||||
${cfg.activationScripts.userLaunchd.text}
|
||||
|
|
|
@ -9,11 +9,8 @@ let
|
|||
mkTextDerivation = name: text: pkgs.writeText "etc-${name}" text;
|
||||
};
|
||||
|
||||
hasDir = path: length (splitString "/" path) > 1;
|
||||
|
||||
etc = filter (f: f.enable) (attrValues config.environment.etc);
|
||||
etcCopy = filter (f: f.copy) (attrValues config.environment.etc);
|
||||
etcDirs = filter (attr: hasDir attr.target) (attrValues config.environment.etc);
|
||||
|
||||
in
|
||||
|
||||
|
@ -42,64 +39,103 @@ in
|
|||
${concatMapStringsSep "\n" (attr: "touch '${attr.target}'.copy") etcCopy}
|
||||
'';
|
||||
|
||||
system.activationScripts.etcChecks.text = ''
|
||||
declare -A etcSha256Hashes=(
|
||||
${concatMapStringsSep "\n "
|
||||
(attr:
|
||||
"[${escapeShellArg attr.target}]=" +
|
||||
escapeShellArg (concatStringsSep " " attr.knownSha256Hashes))
|
||||
etc}
|
||||
)
|
||||
|
||||
declare -a etcProblems
|
||||
|
||||
while IFS= read -r -d "" configFile; do
|
||||
subPath=''${configFile#"$systemConfig"/etc/}
|
||||
etcStaticFile=/etc/static/$subPath
|
||||
etcFile=/etc/$subPath
|
||||
|
||||
if [[ -e $configFile.copy ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# We need to check files that exist and aren't already links to
|
||||
# $etcStaticFile for known hashes.
|
||||
if [[
|
||||
-e $etcFile
|
||||
&& $(readlink "$etcFile") != "$etcStaticFile"
|
||||
]]; then
|
||||
# Only check hashes of paths that resolve to regular files;
|
||||
# everything else (e.g. directories) we complain about
|
||||
# unconditionally.
|
||||
if [[ -f $(readlink -f "$etcFile") ]]; then
|
||||
etcFileSha256Output=$(shasum -a 256 "$etcFile")
|
||||
etcFileSha256Hash=''${etcFileSha256Output%% *}
|
||||
for knownSha256Hash in ''${etcSha256Hashes[$subPath]}; do
|
||||
if [[ $etcFileSha256Hash == "$knownSha256Hash" ]]; then
|
||||
# Hash matches, OK to overwrite; go to the next file.
|
||||
continue 2
|
||||
fi
|
||||
done
|
||||
fi
|
||||
etcProblems+=("$etcFile")
|
||||
fi
|
||||
done < <(find -H "$systemConfig/etc" -type l -print0)
|
||||
|
||||
if (( ''${#etcProblems[@]} )); then
|
||||
printf >&2 '\x1B[1;31merror: Unexpected files in /etc, aborting '
|
||||
printf >&2 'activation\x1B[0m\n'
|
||||
printf >&2 'The following files have unrecognized content and would be '
|
||||
printf >&2 'overwritten:\n\n'
|
||||
printf >&2 ' %s\n' "''${etcProblems[@]}"
|
||||
printf >&2 '\nPlease check there is nothing critical in these files, '
|
||||
printf >&2 'rename them by adding .before-nix-darwin to the end, and '
|
||||
printf >&2 'then try again.\n'
|
||||
exit 2
|
||||
fi
|
||||
'';
|
||||
|
||||
system.activationScripts.etc.text = ''
|
||||
# Set up the statically computed bits of /etc.
|
||||
echo "setting up /etc..." >&2
|
||||
printf >&2 'setting up /etc...\n'
|
||||
|
||||
declare -A etcSha256Hashes
|
||||
${concatMapStringsSep "\n" (attr: "etcSha256Hashes['/etc/${attr.target}']='${concatStringsSep " " attr.knownSha256Hashes}'") etc}
|
||||
ln -sfn "$(readlink -f "$systemConfig/etc")" /etc/static
|
||||
|
||||
ln -sfn "$(readlink -f $systemConfig/etc)" /etc/static
|
||||
|
||||
errorOccurred=false
|
||||
for etcStaticFile in $(find /etc/static/* -type l); do
|
||||
while IFS= read -r -d "" etcStaticFile; do
|
||||
etcFile=/etc/''${etcStaticFile#/etc/static/}
|
||||
etcDir=''${etcFile%/*}
|
||||
if [ ! -e "$etcDir" ]; then
|
||||
|
||||
if [[ ! -d $etcDir ]]; then
|
||||
mkdir -p "$etcDir"
|
||||
fi
|
||||
if [ -e "$etcStaticFile".copy ]; then
|
||||
|
||||
if [[ -e $etcStaticFile.copy ]]; then
|
||||
cp "$etcStaticFile" "$etcFile"
|
||||
continue
|
||||
fi
|
||||
if [ -e "$etcFile" ]; then
|
||||
if [ "$(readlink "$etcFile")" != "$etcStaticFile" ]; then
|
||||
if ! grep -q /etc/static "$etcFile"; then
|
||||
etcFileSha256=''$(shasum -a256 "$etcFile")
|
||||
etcFileSha256=''${etcFileSha256%% *}
|
||||
for knownSha256Hash in ''${etcSha256Hashes["$etcFile"]}; do
|
||||
if [ "$etcFileSha256" = "$knownSha256Hash" ]; then
|
||||
mv "$etcFile" "$etcFile.before-nix-darwin"
|
||||
ln -s "$etcStaticFile" "$etcFile"
|
||||
break
|
||||
else
|
||||
knownSha256Hash=
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$knownSha256Hash" ]; then
|
||||
echo "[1;31merror: not linking environment.etc.\"''${etcFile#/etc/}\" because $etcFile already exists, skipping...[0m" >&2
|
||||
echo "[1;31mexisting file has unknown content $etcFileSha256, move and activate again to apply[0m" >&2
|
||||
errorOccurred=true
|
||||
fi
|
||||
fi
|
||||
if [[ -e $etcFile ]]; then
|
||||
if [[ $(readlink -- "$etcFile") == "$etcStaticFile" ]]; then
|
||||
continue
|
||||
else
|
||||
mv "$etcFile" "$etcFile.before-nix-darwin"
|
||||
fi
|
||||
else
|
||||
ln -s "$etcStaticFile" "$etcFile"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$errorOccurred" != "false" ]; then
|
||||
exit 1
|
||||
fi
|
||||
ln -s "$etcStaticFile" "$etcFile"
|
||||
done < <(find -H /etc/static -type l -print0)
|
||||
|
||||
for etcFile in $(find /etc/* -type l 2> /dev/null); do
|
||||
etcStaticFile="$(echo $etcFile | sed 's,/etc/,/etc/static/,')"
|
||||
while IFS= read -r -d "" etcFile; do
|
||||
etcStaticFile=/etc/static/''${etcFile#/etc/}
|
||||
if [ "$(readlink "$etcFile")" = "$etcStaticFile" -a ! -e "$(readlink -f "$etcFile")" ]; then
|
||||
|
||||
# Delete stale links into /etc/static.
|
||||
if [[
|
||||
$(readlink "$etcFile") == "$etcStaticFile"
|
||||
&& ! -e $etcStaticFile
|
||||
]]; then
|
||||
rm "$etcFile"
|
||||
fi
|
||||
done
|
||||
done < <(find -H /etc -type l -print0)
|
||||
'';
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue