nixpkgs/nixos/modules/system/etc/etc.nix
Matthias Treydte 9ce8df127d nixos/etc: make sure local "source" files are imported to the store
The treatment of the "source" parameter changed
with eb7120dc79, breaking stuff.

Before that commit, the source parameter was converted to a
string by implicit coercion, which would copy the file to the
store and yield an string containing the store path. Now, by
the virtue of escapeShellArg, toString is called explicitly on
that path, which will yield an string containing the absolute
path of the file.

This commit restores the old behavior.
2021-09-02 15:50:44 +02:00

201 lines
5.4 KiB
Nix

# Management of static files in /etc.
{ config, lib, pkgs, ... }:
with lib;
let
# if the source is a local file, it should be imported to the store
localToStore = mapAttrs (name: value: if name == "source" then "${value}" else value);
etc' = map localToStore (filter (f: f.enable) (attrValues config.environment.etc));
etc = pkgs.runCommandLocal "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
{
###### interface
options = {
environment.etc = mkOption {
default = {};
example = literalExample ''
{ example-configuration-file =
{ source = "/nix/store/.../etc/dir/file.conf.example";
mode = "0440";
};
"default/useradd".text = "GROUP=100 ...";
}
'';
description = ''
Set of files that have to be linked in <filename>/etc</filename>.
'';
type = with types; attrsOf (submodule (
{ name, config, ... }:
{ options = {
enable = mkOption {
type = types.bool;
default = true;
description = ''
Whether this /etc file should be generated. This
option allows specific /etc files to be disabled.
'';
};
target = mkOption {
type = types.str;
description = ''
Name of symlink (relative to
<filename>/etc</filename>). Defaults to the attribute
name.
'';
};
text = mkOption {
default = null;
type = types.nullOr types.lines;
description = "Text of the file.";
};
source = mkOption {
type = types.path;
description = "Path of the source file.";
};
mode = mkOption {
type = types.str;
default = "symlink";
example = "0600";
description = ''
If set to something else than <literal>symlink</literal>,
the file is copied instead of symlinked, with the given
file mode.
'';
};
uid = mkOption {
default = 0;
type = types.int;
description = ''
UID of created file. Only takes effect when the file is
copied (that is, the mode is not 'symlink').
'';
};
gid = mkOption {
default = 0;
type = types.int;
description = ''
GID of created file. Only takes effect when the file is
copied (that is, the mode is not 'symlink').
'';
};
user = mkOption {
default = "+${toString config.uid}";
type = types.str;
description = ''
User name of created file.
Only takes effect when the file is copied (that is, the mode is not 'symlink').
Changing this option takes precedence over <literal>uid</literal>.
'';
};
group = mkOption {
default = "+${toString config.gid}";
type = types.str;
description = ''
Group name of created file.
Only takes effect when the file is copied (that is, the mode is not 'symlink').
Changing this option takes precedence over <literal>gid</literal>.
'';
};
};
config = {
target = mkDefault name;
source = mkIf (config.text != null) (
let name' = "etc-" + baseNameOf name;
in mkDefault (pkgs.writeText name' config.text));
};
}));
};
};
###### implementation
config = {
system.build.etc = etc;
system.activationScripts.etc = stringAfter [ "users" "groups" ]
''
# Set up the statically computed bits of /etc.
echo "setting up /etc..."
${pkgs.perl.withPackages (p: [ p.FileSlurp ])}/bin/perl ${./setup-etc.pl} ${etc}/etc
'';
};
}