{ config, lib, pkgs, ... }: with lib; let text = import ../lib/write-text.nix { inherit lib; mkTextDerivation = name: text: pkgs.writeText "etc-${name}" text; }; etc = filter (f: f.enable) (attrValues config.environment.etc); in { options = { environment.etc = mkOption { type = types.attrsOf (types.submodule text); default = { }; description = '' Set of files that have to be linked in {file}`/etc`. ''; }; }; config = { system.build.etc = pkgs.runCommand "etc" { preferLocalBuild = true; } '' mkdir -p $out/etc cd $out/etc ${concatMapStringsSep "\n" (attr: '' mkdir -p "$(dirname ${escapeShellArg attr.target})" ln -s ${escapeShellArgs [ attr.source attr.target ]} '') etc} ''; 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 # 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. printf >&2 'setting up /etc...\n' ln -sfn "$(readlink -f "$systemConfig/etc")" /etc/static while IFS= read -r -d "" etcStaticFile; do etcFile=/etc/''${etcStaticFile#/etc/static/} etcDir=''${etcFile%/*} if [[ ! -d $etcDir ]]; then mkdir -p "$etcDir" fi if [[ -e $etcFile ]]; then if [[ $(readlink -- "$etcFile") == "$etcStaticFile" ]]; then continue else mv "$etcFile" "$etcFile.before-nix-darwin" fi fi ln -s "$etcStaticFile" "$etcFile" done < <(find -H /etc/static -type l -print0) while IFS= read -r -d "" etcFile; do etcStaticFile=/etc/static/''${etcFile#/etc/} # Delete stale links into /etc/static. if [[ $(readlink -- "$etcFile") == "$etcStaticFile" && ! -e $etcStaticFile ]]; then rm "$etcFile" fi done < <(find -H /etc -type l -print0) ''; }; }