diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh index 936077b9df1e..50ee0b8841e5 100644 --- a/nixos/modules/system/boot/stage-2-init.sh +++ b/nixos/modules/system/boot/stage-2-init.sh @@ -167,6 +167,7 @@ exec {logOutFd}>&- {logErrFd}>&- # Start systemd. echo "starting systemd..." + PATH=/run/current-system/systemd/lib/systemd:@fsPackagesPath@ \ - LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive \ + LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive @systemdUnitPathEnvVar@ \ exec @systemdExecutable@ diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix index 94bc34fea0db..f6b6a8e4b0b4 100644 --- a/nixos/modules/system/boot/stage-2.nix +++ b/nixos/modules/system/boot/stage-2.nix @@ -10,7 +10,7 @@ let src = ./stage-2-init.sh; shellDebug = "${pkgs.bashInteractive}/bin/bash"; shell = "${pkgs.bash}/bin/bash"; - inherit (config.boot) systemdExecutable; + inherit (config.boot) systemdExecutable extraSystemdUnitPaths; isExecutable = true; inherit (config.nix) readOnlyStore; inherit useHostResolvConf; @@ -20,6 +20,10 @@ let 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} @@ -82,6 +86,15 @@ in PATH. ''; }; + + extraSystemdUnitPaths = mkOption { + default = []; + type = types.listOf types.str; + description = '' + Additional paths that get appended to the SYSTEMD_UNIT_PATH environment variable + that can contain mutable unit files. + ''; + }; }; }; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 3ce71b0abe6d..4c5a2b39bd98 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -393,6 +393,7 @@ in systemd-networkd-vrf = handleTest ./systemd-networkd-vrf.nix {}; systemd-nspawn = handleTest ./systemd-nspawn.nix {}; systemd-timesyncd = handleTest ./systemd-timesyncd.nix {}; + systemd-unit-path = handleTest ./systemd-unit-path.nix {}; taskserver = handleTest ./taskserver.nix {}; telegraf = handleTest ./telegraf.nix {}; tiddlywiki = handleTest ./tiddlywiki.nix {}; diff --git a/nixos/tests/systemd-unit-path.nix b/nixos/tests/systemd-unit-path.nix new file mode 100644 index 000000000000..5998a187188a --- /dev/null +++ b/nixos/tests/systemd-unit-path.nix @@ -0,0 +1,47 @@ +import ./make-test-python.nix ({ pkgs, ... }: + +let + exampleScript = pkgs.writeTextFile { + name = "example.sh"; + text = '' + #! ${pkgs.runtimeShell} -e + + while true; do + echo "Example script running" >&2 + ${pkgs.coreutils}/bin/sleep 1 + done + ''; + executable = true; + }; + + unitFile = pkgs.writeTextFile { + name = "example.service"; + text = '' + [Unit] + Description=Example systemd service unit file + + [Service] + ExecStart=${exampleScript} + + [Install] + WantedBy=multi-user.target + ''; + }; +in +{ + name = "systemd-unit-path"; + + machine = { pkgs, lib, ... }: { + boot.extraSystemdUnitPaths = [ "/etc/systemd-rw/system" ]; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + machine.succeed("mkdir -p /etc/systemd-rw/system") + machine.succeed( + "cp ${unitFile} /etc/systemd-rw/system/example.service" + ) + machine.succeed("systemctl start example.service") + machine.succeed("systemctl status example.service | grep 'Active: active'") + ''; +})