nixos/systemd: Switch to ManagerEnvironment=

This accomplishes multiple things:
- Allows us to start systemd without stage-2-init.sh. This was not
  possible before because the environment would have been wrong
- `systemctl daemon-reexec` also changes the environment, giving us
  newer tools for the fs packages
- Starts systemd in a fully clean environment, making everything more
  consistent and pure
This commit is contained in:
Janne Heß 2022-03-23 15:56:23 +01:00
parent d56076aa39
commit a3e0698bf6
No known key found for this signature in database
GPG Key ID: 69165158F05265DF
4 changed files with 31 additions and 15 deletions

View File

@ -167,10 +167,6 @@ exec 1>&$logOutFd 2>&$logErrFd
exec {logOutFd}>&- {logErrFd}>&-
# Start systemd.
# Start systemd in a clean environment.
echo "starting systemd..."
PATH=/run/current-system/systemd/lib/systemd:@fsPackagesPath@ \
LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive @systemdUnitPathEnvVar@ \
TZDIR=/etc/zoneinfo \
exec @systemdExecutable@
exec env - @systemdExecutable@ "$@"

View File

@ -19,11 +19,6 @@ let
pkgs.coreutils
pkgs.util-linux
] ++ lib.optional useHostResolvConf pkgs.openresolv);
fsPackagesPath = lib.makeBinPath config.system.fsPackages;
systemdUnitPathEnvVar = lib.optionalString (config.boot.extraSystemdUnitPaths != [])
("SYSTEMD_UNIT_PATH="
+ builtins.concatStringsSep ":" config.boot.extraSystemdUnitPaths
+ ":"); # If SYSTEMD_UNIT_PATH ends with an empty component (":"), the usual unit load path will be appended to the contents of the variable
postBootCommands = pkgs.writeText "local-cmds"
''
${config.boot.postBootCommands}
@ -78,12 +73,10 @@ in
};
systemdExecutable = mkOption {
default = "systemd";
default = "/run/current-system/systemd/lib/systemd/systemd";
type = types.str;
description = ''
The program to execute to start systemd. Typically
<literal>systemd</literal>, which will find systemd in the
PATH.
The program to execute to start systemd.
'';
};

View File

@ -302,6 +302,16 @@ in
'';
};
systemd.managerEnvironment = mkOption {
type = with types; attrsOf (nullOr (oneOf [ str path package ]));
default = {};
example = { SYSTEMD_LOG_LEVEL = "debug"; };
description = ''
Environment variables of PID 1. These variables are
<emphasis>not</emphasis> passed to started units.
'';
};
systemd.enableCgroupAccounting = mkOption {
default = true;
type = types.bool;
@ -470,11 +480,13 @@ in
enabledUpstreamSystemUnits = filter (n: ! elem n cfg.suppressedSystemUnits) upstreamSystemUnits;
enabledUnits = filterAttrs (n: v: ! elem n cfg.suppressedSystemUnits) cfg.units;
in ({
"systemd/system".source = generateUnits "system" enabledUnits enabledUpstreamSystemUnits upstreamSystemWants;
"systemd/system.conf".text = ''
[Manager]
ManagerEnvironment=${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "${n}=${lib.escapeShellArg v}") cfg.managerEnvironment)}
${optionalString config.systemd.enableCgroupAccounting ''
DefaultCPUAccounting=yes
DefaultIOAccounting=yes
@ -542,6 +554,17 @@ in
(v: let n = escapeSystemdPath v.where;
in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
# Environment of PID 1
systemd.managerEnvironment = {
# Doesn't contain systemd itself - everything works so it seems to use the compiled-in value for its tools
PATH = lib.makeBinPath config.system.fsPackages;
LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
TZDIR = "/etc/zoneinfo";
# If SYSTEMD_UNIT_PATH ends with an empty component (":"), the usual unit load path will be appended to the contents of the variable
SYSTEMD_UNIT_PATH = lib.mkIf (config.boot.extraSystemdUnitPaths != []) "${builtins.concatStringsSep ":" config.boot.extraSystemdUnitPaths}:";
};
system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled
[ "DEVTMPFS" "CGROUPS" "INOTIFY_USER" "SIGNALFD" "TIMERFD" "EPOLL" "NET"
"SYSFS" "PROC_FS" "FHANDLE" "CRYPTO_USER_API_HASH" "CRYPTO_HMAC"

View File

@ -192,5 +192,9 @@ import ./make-test-python.nix ({ pkgs, ... }: {
with subtest("systemd per-unit accounting works"):
assert "IP traffic received: 84B" in output_ping
assert "IP traffic sent: 84B" in output_ping
with subtest("systemd environment is properly set"):
machine.systemctl("daemon-reexec") # Rewrites /proc/1/environ
machine.succeed("grep -q TZDIR=/etc/zoneinfo /proc/1/environ")
'';
})