From eb7120dc79966d5ed168321fd213de38de13a2b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janne=20He=C3=9F?= Date: Thu, 22 Jul 2021 17:38:31 +0200 Subject: [PATCH] nixos/etc: Replace make-etc.sh with nix and bash The main goal of this commit is to replace the rather fragile passing of multiple arrays which could break in cases like #130935. While I could have just added proper shell escaping to the variables being passed, I opted for the more painful approach of replacing the fragile and somewhat strange construct with the 5 bash lists. While there are currently no more problems present with the current approach (at least none that I know of), the new approach seems more solid and might get around problems that could arise in the future stemming from either the multiple-lists situation or from the absence of proper shell quoting all over the script. --- nixos/modules/system/boot/systemd.nix | 2 +- nixos/modules/system/etc/etc.nix | 64 ++++++++++++++++++++++----- nixos/modules/system/etc/make-etc.sh | 45 ------------------- 3 files changed, 53 insertions(+), 58 deletions(-) delete mode 100644 nixos/modules/system/etc/make-etc.sh diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index abd8ab29caef..e19097759267 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -1045,7 +1045,7 @@ in done '' + concatMapStrings (name: optionalString (hasPrefix "tmpfiles.d/" name) '' rm -f $out/${removePrefix "tmpfiles.d/" name} - '') config.system.build.etc.targets; + '') config.system.build.etc.passthru.targets; }) + "/*"; "systemd/system-generators" = { source = hooks "generators" cfg.generators; }; diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix index a450f303572e..183de7292101 100644 --- a/nixos/modules/system/etc/etc.nix +++ b/nixos/modules/system/etc/etc.nix @@ -8,21 +8,61 @@ let etc' = filter (f: f.enable) (attrValues config.environment.etc); - etc = pkgs.stdenvNoCC.mkDerivation { - name = "etc"; - - builder = ./make-etc.sh; - + etc = pkgs.runCommand "etc" { preferLocalBuild = true; allowSubstitutes = false; - /* !!! Use toXML. */ - sources = map (x: x.source) etc'; - targets = map (x: x.target) etc'; - modes = map (x: x.mode) etc'; - users = map (x: x.user) etc'; - groups = map (x: x.group) etc'; - }; + # This is needed for the systemd module + passthru.targets = map (x: x.target) etc'; + } /* sh */ '' + set -euo pipefail + + makeEtcEntry() { + src="$1" + target="$2" + mode="$3" + user="$4" + group="$5" + + if [[ "$src" = *'*'* ]]; then + # If the source name contains '*', perform globbing. + mkdir -p "$out/etc/$target" + for fn in $src; do + ln -s "$fn" "$out/etc/$target/" + done + else + + mkdir -p "$out/etc/$(dirname "$target")" + if ! [ -e "$out/etc/$target" ]; then + ln -s "$src" "$out/etc/$target" + else + echo "duplicate entry $target -> $src" + if [ "$(readlink "$out/etc/$target")" != "$src" ]; then + echo "mismatched duplicate entry $(readlink "$out/etc/$target") <-> $src" + ret=1 + + continue + fi + fi + + if [ "$mode" != symlink ]; then + echo "$mode" > "$out/etc/$target.mode" + echo "$user" > "$out/etc/$target.uid" + echo "$group" > "$out/etc/$target.gid" + fi + fi + } + + mkdir -p "$out/etc" + ${concatMapStringsSep "\n" (etcEntry: escapeShellArgs [ + "makeEtcEntry" + etcEntry.source + etcEntry.target + etcEntry.mode + etcEntry.user + etcEntry.group + ]) etc'} + ''; in diff --git a/nixos/modules/system/etc/make-etc.sh b/nixos/modules/system/etc/make-etc.sh deleted file mode 100644 index aabfb5e88a65..000000000000 --- a/nixos/modules/system/etc/make-etc.sh +++ /dev/null @@ -1,45 +0,0 @@ -source $stdenv/setup - -mkdir -p $out/etc - -set -f -sources_=($sources) -targets_=($targets) -modes_=($modes) -users_=($users) -groups_=($groups) -set +f - -for ((i = 0; i < ${#targets_[@]}; i++)); do - source="${sources_[$i]}" - target="${targets_[$i]}" - - if [[ "$source" =~ '*' ]]; then - - # If the source name contains '*', perform globbing. - mkdir -p $out/etc/$target - for fn in $source; do - ln -s "$fn" $out/etc/$target/ - done - - else - - mkdir -p $out/etc/$(dirname $target) - if ! [ -e $out/etc/$target ]; then - ln -s $source $out/etc/$target - else - echo "duplicate entry $target -> $source" - if test "$(readlink $out/etc/$target)" != "$source"; then - echo "mismatched duplicate entry $(readlink $out/etc/$target) <-> $source" - exit 1 - fi - fi - - if test "${modes_[$i]}" != symlink; then - echo "${modes_[$i]}" > $out/etc/$target.mode - echo "${users_[$i]}" > $out/etc/$target.uid - echo "${groups_[$i]}" > $out/etc/$target.gid - fi - - fi -done