diff --git a/modules/system/boot/systemd-unit-options.nix b/modules/system/boot/systemd-unit-options.nix
index 239c837c6767..5707fb6f87e9 100644
--- a/modules/system/boot/systemd-unit-options.nix
+++ b/modules/system/boot/systemd-unit-options.nix
@@ -6,6 +6,17 @@ rec {
unitOptions = {
+ enable = mkOption {
+ default = true;
+ types = types.bool;
+ description = ''
+ If set to false, this unit will be a symlink to
+ /dev/null. This is primarily useful to prevent specific
+ template instances (e.g. serial-getty@ttyS0)
+ from being started.
+ '';
+ };
+
description = mkOption {
default = "";
types = types.uniq types.string;
diff --git a/modules/system/boot/systemd.nix b/modules/system/boot/systemd.nix
index dc0c2ba4504b..aa0b679ea2f5 100644
--- a/modules/system/boot/systemd.nix
+++ b/modules/system/boot/systemd.nix
@@ -10,7 +10,14 @@ let
systemd = pkgs.systemd;
makeUnit = name: unit:
- pkgs.writeTextFile { name = "unit"; inherit (unit) text; destination = "/${name}"; };
+ pkgs.runCommand "unit" { inherit (unit) text; }
+ (if unit.enable then ''
+ mkdir -p $out
+ echo -n "$text" > $out/${name}
+ '' else ''
+ mkdir -p $out
+ ln -s /dev/null $out/${name}
+ '');
upstreamUnits =
[ # Targets.
@@ -205,7 +212,7 @@ let
as));
targetToUnit = name: def:
- { inherit (def) wantedBy;
+ { inherit (def) wantedBy enable;
text =
''
[Unit]
@@ -214,7 +221,7 @@ let
};
serviceToUnit = name: def:
- { inherit (def) wantedBy;
+ { inherit (def) wantedBy enable;
text =
''
[Unit]
@@ -258,7 +265,7 @@ let
};
socketToUnit = name: def:
- { inherit (def) wantedBy;
+ { inherit (def) wantedBy enable;
text =
''
[Unit]
@@ -333,6 +340,16 @@ in
types = types.uniq types.string;
description = "Text of this systemd unit.";
};
+ enable = mkOption {
+ default = true;
+ types = types.bool;
+ description = ''
+ If set to false, this unit will be a symlink to
+ /dev/null. This is primarily useful to prevent specific
+ template instances (e.g. serial-getty@ttyS0)
+ from being started.
+ '';
+ };
wantedBy = mkOption {
default = [];
types = types.listOf types.string;
diff --git a/modules/testing/test-instrumentation.nix b/modules/testing/test-instrumentation.nix
index efce3153c88e..222a10a00435 100644
--- a/modules/testing/test-instrumentation.nix
+++ b/modules/testing/test-instrumentation.nix
@@ -13,9 +13,8 @@ let kernel = config.boot.kernelPackages.kernel; in
boot.systemd.services.backdoor =
{ wantedBy = [ "multi-user.target" ];
- requires = [ "dev-hvc0.device" ];
- after = [ "dev-hvc0.device" ];
-
+ requires = [ "dev-hvc0.device" "dev-ttyS0.device" ];
+ after = [ "dev-hvc0.device" "dev-ttyS0.device" ];
script =
''
export USER=root
@@ -27,10 +26,15 @@ let kernel = config.boot.kernelPackages.kernel; in
echo "connecting to host..." >&2
stty -F /dev/hvc0 raw -echo # prevent nl -> cr/nl conversion
echo
- PS1= /bin/sh
+ PS1= exec /bin/sh
'';
};
+ # Prevent agetty from being instantiated on ttyS0, since it
+ # interferes with the backdoor (writes to ttyS0 will randomly fail
+ # with EIO).
+ boot.systemd.services."serial-getty@ttyS0".enable = false;
+
boot.initrd.postDeviceCommands =
''
# Using acpi_pm as a clock source causes the guest clock to
diff --git a/tests/nat.nix b/tests/nat.nix
index e2f01e71b263..7b03739a9bc4 100644
--- a/tests/nat.nix
+++ b/tests/nat.nix
@@ -40,6 +40,7 @@
startAll;
# The router should have access to the server.
+ $server->waitForUnit("network.target");
$server->waitForUnit("httpd");
$router->waitForUnit("network.target");
$router->succeed("curl --fail http://server/ >&2");
@@ -68,7 +69,7 @@
$client->fail("ping -c 1 server >&2");
# And make sure that restarting the NAT job works.
- $router->succeed("start nat");
+ $router->succeed("systemctl start nat");
$client->succeed("curl --fail http://server/ >&2");
$client->succeed("ping -c 1 server >&2");
'';