mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-07-19 00:20:32 +03:00

After final improvements to the official formatter implementation,
this commit now performs the first treewide reformat of Nix files using it.
This is part of the implementation of RFC 166.
Only "inactive" files are reformatted, meaning only files that
aren't being touched by any PR with activity in the past 2 months.
This is to avoid conflicts for PRs that might soon be merged.
Later we can do a full treewide reformat to get the rest,
which should not cause as many conflicts.
A CI check has already been running for some time to ensure that new and
already-formatted files are formatted, so the files being reformatted here
should also stay formatted.
This commit was automatically created and can be verified using
nix-build https://github.com/infinisil/treewide-nixpkgs-reformat-script/archive/a08b3a4d199c6124ac5b36a889d9099b4383463f.tar.gz \
--argstr baseRev 0128fbb0a5
result/bin/apply-formatting $NIXPKGS_PATH
381 lines
12 KiB
Nix
381 lines
12 KiB
Nix
# Management of static files in /etc.
|
|
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
|
|
etc' = lib.filter (f: f.enable) (lib.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"
|
|
${lib.concatMapStringsSep "\n" (
|
|
etcEntry:
|
|
lib.escapeShellArgs [
|
|
"makeEtcEntry"
|
|
# Force local source paths to be added to the store
|
|
"${etcEntry.source}"
|
|
etcEntry.target
|
|
etcEntry.mode
|
|
etcEntry.user
|
|
etcEntry.group
|
|
]
|
|
) etc'}
|
|
'';
|
|
|
|
etcHardlinks = lib.filter (f: f.mode != "symlink" && f.mode != "direct-symlink") etc';
|
|
|
|
in
|
|
|
|
{
|
|
|
|
imports = [ ../build.nix ];
|
|
|
|
###### interface
|
|
|
|
options = {
|
|
|
|
system.etc.overlay = {
|
|
enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = ''
|
|
Mount `/etc` as an overlayfs instead of generating it via a perl script.
|
|
|
|
Note: This is currently experimental. Only enable this option if you're
|
|
confident that you can recover your system if it breaks.
|
|
'';
|
|
};
|
|
|
|
mutable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = ''
|
|
Whether to mount `/etc` mutably (i.e. read-write) or immutably (i.e. read-only).
|
|
|
|
If this is false, only the immutable lowerdir is mounted. If it is
|
|
true, a writable upperdir is mounted on top.
|
|
'';
|
|
};
|
|
};
|
|
|
|
environment.etc = lib.mkOption {
|
|
default = { };
|
|
example = lib.literalExpression ''
|
|
{ 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 {file}`/etc`.
|
|
'';
|
|
|
|
type =
|
|
with lib.types;
|
|
attrsOf (
|
|
submodule (
|
|
{
|
|
name,
|
|
config,
|
|
options,
|
|
...
|
|
}:
|
|
{
|
|
options = {
|
|
|
|
enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = ''
|
|
Whether this /etc file should be generated. This
|
|
option allows specific /etc files to be disabled.
|
|
'';
|
|
};
|
|
|
|
target = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = ''
|
|
Name of symlink (relative to
|
|
{file}`/etc`). Defaults to the attribute
|
|
name.
|
|
'';
|
|
};
|
|
|
|
text = lib.mkOption {
|
|
default = null;
|
|
type = lib.types.nullOr lib.types.lines;
|
|
description = "Text of the file.";
|
|
};
|
|
|
|
source = lib.mkOption {
|
|
type = lib.types.path;
|
|
description = "Path of the source file.";
|
|
};
|
|
|
|
mode = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "symlink";
|
|
example = "0600";
|
|
description = ''
|
|
If set to something else than `symlink`,
|
|
the file is copied instead of symlinked, with the given
|
|
file mode.
|
|
'';
|
|
};
|
|
|
|
uid = lib.mkOption {
|
|
default = 0;
|
|
type = lib.types.int;
|
|
description = ''
|
|
UID of created file. Only takes effect when the file is
|
|
copied (that is, the mode is not 'symlink').
|
|
'';
|
|
};
|
|
|
|
gid = lib.mkOption {
|
|
default = 0;
|
|
type = lib.types.int;
|
|
description = ''
|
|
GID of created file. Only takes effect when the file is
|
|
copied (that is, the mode is not 'symlink').
|
|
'';
|
|
};
|
|
|
|
user = lib.mkOption {
|
|
default = "+${toString config.uid}";
|
|
type = lib.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 `uid`.
|
|
'';
|
|
};
|
|
|
|
group = lib.mkOption {
|
|
default = "+${toString config.gid}";
|
|
type = lib.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 `gid`.
|
|
'';
|
|
};
|
|
|
|
};
|
|
|
|
config = {
|
|
target = lib.mkDefault name;
|
|
source = lib.mkIf (config.text != null) (
|
|
let
|
|
name' = "etc-" + lib.replaceStrings [ "/" ] [ "-" ] name;
|
|
in
|
|
lib.mkDerivedConfig options.text (pkgs.writeText name')
|
|
);
|
|
};
|
|
|
|
}
|
|
)
|
|
);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
###### implementation
|
|
|
|
config = {
|
|
|
|
system.build.etc = etc;
|
|
system.build.etcActivationCommands =
|
|
let
|
|
etcOverlayOptions = lib.concatStringsSep "," (
|
|
[
|
|
"relatime"
|
|
"redirect_dir=on"
|
|
"metacopy=on"
|
|
]
|
|
++ lib.optionals config.system.etc.overlay.mutable [
|
|
"upperdir=/.rw-etc/upper"
|
|
"workdir=/.rw-etc/work"
|
|
]
|
|
);
|
|
in
|
|
if config.system.etc.overlay.enable then
|
|
''
|
|
# This script atomically remounts /etc when switching configuration. On a (re-)boot
|
|
# this should not run because /etc is mounted via a systemd mount unit
|
|
# instead. To a large extent this mimics what composefs does. Because
|
|
# it's relatively simple, however, we avoid the composefs dependency.
|
|
# Since this script is not idempotent, it should not run when etc hasn't
|
|
# changed.
|
|
if [[ ! $IN_NIXOS_SYSTEMD_STAGE1 ]] && [[ "${config.system.build.etc}/etc" != "$(readlink -f /run/current-system/etc)" ]]; then
|
|
echo "remounting /etc..."
|
|
|
|
tmpMetadataMount=$(mktemp --directory -t nixos-etc-metadata.XXXXXXXXXX)
|
|
mount --type erofs ${config.system.build.etcMetadataImage} $tmpMetadataMount
|
|
|
|
# Mount the new /etc overlay to a temporary private mount.
|
|
# This needs the indirection via a private bind mount because you
|
|
# cannot move shared mounts.
|
|
tmpEtcMount=$(mktemp --directory -t nixos-etc.XXXXXXXXXX)
|
|
mount --bind --make-private $tmpEtcMount $tmpEtcMount
|
|
mount --type overlay overlay \
|
|
--options lowerdir=$tmpMetadataMount::${config.system.build.etcBasedir},${etcOverlayOptions} \
|
|
$tmpEtcMount
|
|
|
|
# Before moving the new /etc overlay under the old /etc, we have to
|
|
# move mounts on top of /etc to the new /etc mountpoint.
|
|
findmnt /etc --submounts --list --noheading --kernel --output TARGET | while read -r mountPoint; do
|
|
if [[ "$mountPoint" = "/etc" ]]; then
|
|
continue
|
|
fi
|
|
|
|
tmpMountPoint="$tmpEtcMount/''${mountPoint:5}"
|
|
${
|
|
if config.system.etc.overlay.mutable then
|
|
''
|
|
if [[ -f "$mountPoint" ]]; then
|
|
touch "$tmpMountPoint"
|
|
elif [[ -d "$mountPoint" ]]; then
|
|
mkdir -p "$tmpMountPoint"
|
|
fi
|
|
''
|
|
else
|
|
''
|
|
if [[ ! -e "$tmpMountPoint" ]]; then
|
|
echo "Skipping undeclared mountpoint in environment.etc: $mountPoint"
|
|
continue
|
|
fi
|
|
''
|
|
}
|
|
mount --bind "$mountPoint" "$tmpMountPoint"
|
|
done
|
|
|
|
# Move the new temporary /etc mount underneath the current /etc mount.
|
|
#
|
|
# This should eventually use util-linux to perform this move beneath,
|
|
# however, this functionality is not yet in util-linux. See this
|
|
# tracking issue: https://github.com/util-linux/util-linux/issues/2604
|
|
${pkgs.move-mount-beneath}/bin/move-mount --move --beneath $tmpEtcMount /etc
|
|
|
|
# Unmount the top /etc mount to atomically reveal the new mount.
|
|
umount --lazy --recursive /etc
|
|
|
|
# Unmount the temporary mount
|
|
umount --lazy "$tmpEtcMount"
|
|
rmdir "$tmpEtcMount"
|
|
|
|
# Unmount old metadata mounts
|
|
# For some reason, `findmnt /tmp --submounts` does not show the nested
|
|
# mounts. So we'll just find all mounts of type erofs and filter on the
|
|
# name of the mountpoint.
|
|
findmnt --type erofs --list --kernel --output TARGET | while read -r mountPoint; do
|
|
if [[ "$mountPoint" =~ ^/tmp/nixos-etc-metadata\..{10}$ &&
|
|
"$mountPoint" != "$tmpMetadataMount" ]]; then
|
|
umount --lazy $mountPoint
|
|
rmdir "$mountPoint"
|
|
fi
|
|
done
|
|
fi
|
|
''
|
|
else
|
|
''
|
|
# 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
|
|
'';
|
|
|
|
system.build.etcBasedir = pkgs.runCommandLocal "etc-lowerdir" { } ''
|
|
set -euo pipefail
|
|
|
|
makeEtcEntry() {
|
|
src="$1"
|
|
target="$2"
|
|
|
|
mkdir -p "$out/$(dirname "$target")"
|
|
cp "$src" "$out/$target"
|
|
}
|
|
|
|
mkdir -p "$out"
|
|
${lib.concatMapStringsSep "\n" (
|
|
etcEntry:
|
|
lib.escapeShellArgs [
|
|
"makeEtcEntry"
|
|
# Force local source paths to be added to the store
|
|
"${etcEntry.source}"
|
|
etcEntry.target
|
|
]
|
|
) etcHardlinks}
|
|
'';
|
|
|
|
system.build.etcMetadataImage =
|
|
let
|
|
etcJson = pkgs.writeText "etc-json" (builtins.toJSON etc');
|
|
etcDump = pkgs.runCommand "etc-dump" { } ''
|
|
${lib.getExe pkgs.buildPackages.python3} ${./build-composefs-dump.py} ${etcJson} > $out
|
|
'';
|
|
in
|
|
pkgs.runCommand "etc-metadata.erofs"
|
|
{
|
|
nativeBuildInputs = with pkgs.buildPackages; [
|
|
composefs
|
|
erofs-utils
|
|
];
|
|
}
|
|
''
|
|
mkcomposefs --from-file ${etcDump} $out
|
|
fsck.erofs $out
|
|
'';
|
|
|
|
};
|
|
|
|
}
|