diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix index b67495609ff5..b6bd9de59fd3 100644 --- a/nixos/lib/systemd-lib.nix +++ b/nixos/lib/systemd-lib.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs }: +{ config, lib, pkgs, utils }: let inherit (lib) @@ -381,8 +381,41 @@ in rec { }; }; - serviceConfig = { config, ... }: { - config.environment.PATH = mkIf (config.path != []) "${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}"; + serviceConfig = { name, config, ... }: { + config = { + name = "${name}.service"; + environment.PATH = mkIf (config.path != []) "${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}"; + }; + }; + + pathConfig = { name, config, ... }: { + config = { + name = "${name}.path"; + }; + }; + + socketConfig = { name, config, ... }: { + config = { + name = "${name}.socket"; + }; + }; + + sliceConfig = { name, config, ... }: { + config = { + name = "${name}.slice"; + }; + }; + + targetConfig = { name, config, ... }: { + config = { + name = "${name}.target"; + }; + }; + + timerConfig = { name, config, ... }: { + config = { + name = "${name}.timer"; + }; }; stage2ServiceConfig = { @@ -401,6 +434,7 @@ in rec { mountConfig = { config, ... }: { config = { + name = "${utils.escapeSystemdPath config.where}.mount"; mountConfig = { What = config.what; Where = config.where; @@ -414,6 +448,7 @@ in rec { automountConfig = { config, ... }: { config = { + name = "${utils.escapeSystemdPath config.where}.automount"; automountConfig = { Where = config.where; }; @@ -429,8 +464,8 @@ in rec { WantedBy=${concatStringsSep " " def.wantedBy} ''; - targetToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; + targetToUnit = def: + { inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = '' [Unit] @@ -438,8 +473,8 @@ in rec { ''; }; - serviceToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; + serviceToUnit = def: + { inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def ('' [Service] '' + (let env = cfg.globalEnvironment // def.environment; @@ -448,7 +483,7 @@ in rec { "Environment=${toJSON "${n}=${env.${n}}"}\n"; # systemd max line length is now 1MiB # https://github.com/systemd/systemd/commit/e6dde451a51dc5aaa7f4d98d39b8fe735f73d2af - in if stringLength s >= 1048576 then throw "The value of the environment variable ‘${n}’ in systemd service ‘${name}.service’ is too long." else s) (attrNames env)) + in if stringLength s >= 1048576 then throw "The value of the environment variable ‘${n}’ in systemd service ‘${def.name}.service’ is too long." else s) (attrNames env)) + (if def ? reloadIfChanged && def.reloadIfChanged then '' X-ReloadIfChanged=true '' else if (def ? restartIfChanged && !def.restartIfChanged) then '' @@ -459,8 +494,8 @@ in rec { '' + attrsToSection def.serviceConfig); }; - socketToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; + socketToUnit = def: + { inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def '' [Socket] ${attrsToSection def.socketConfig} @@ -469,40 +504,40 @@ in rec { ''; }; - timerToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; + timerToUnit = def: + { inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def '' [Timer] ${attrsToSection def.timerConfig} ''; }; - pathToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; + pathToUnit = def: + { inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def '' [Path] ${attrsToSection def.pathConfig} ''; }; - mountToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; + mountToUnit = def: + { inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def '' [Mount] ${attrsToSection def.mountConfig} ''; }; - automountToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; + automountToUnit = def: + { inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def '' [Automount] ${attrsToSection def.automountConfig} ''; }; - sliceToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; + sliceToUnit = def: + { inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def '' [Slice] ${attrsToSection def.sliceConfig} diff --git a/nixos/lib/systemd-types.nix b/nixos/lib/systemd-types.nix index c4c5771cff82..f3bc8e06d9cb 100644 --- a/nixos/lib/systemd-types.nix +++ b/nixos/lib/systemd-types.nix @@ -5,8 +5,13 @@ let automountConfig makeUnit mountConfig + pathConfig + sliceConfig + socketConfig stage1ServiceConfig stage2ServiceConfig + targetConfig + timerConfig unitConfig ; @@ -48,29 +53,32 @@ let ; in -rec { +{ units = attrsOf (submodule ({ name, config, ... }: { options = concreteUnitOptions; - config = { unit = mkDefault (makeUnit name config); }; + config = { + name = mkDefault name; + unit = mkDefault (makeUnit name config); + }; })); services = attrsOf (submodule [ stage2ServiceOptions unitConfig stage2ServiceConfig ]); initrdServices = attrsOf (submodule [ stage1ServiceOptions unitConfig stage1ServiceConfig ]); - targets = attrsOf (submodule [ stage2CommonUnitOptions unitConfig ]); - initrdTargets = attrsOf (submodule [ stage1CommonUnitOptions unitConfig ]); + targets = attrsOf (submodule [ stage2CommonUnitOptions unitConfig targetConfig ]); + initrdTargets = attrsOf (submodule [ stage1CommonUnitOptions unitConfig targetConfig ]); - sockets = attrsOf (submodule [ stage2SocketOptions unitConfig ]); - initrdSockets = attrsOf (submodule [ stage1SocketOptions unitConfig ]); + sockets = attrsOf (submodule [ stage2SocketOptions unitConfig socketConfig]); + initrdSockets = attrsOf (submodule [ stage1SocketOptions unitConfig socketConfig ]); - timers = attrsOf (submodule [ stage2TimerOptions unitConfig ]); - initrdTimers = attrsOf (submodule [ stage1TimerOptions unitConfig ]); + timers = attrsOf (submodule [ stage2TimerOptions unitConfig timerConfig ]); + initrdTimers = attrsOf (submodule [ stage1TimerOptions unitConfig timerConfig ]); - paths = attrsOf (submodule [ stage2PathOptions unitConfig ]); - initrdPaths = attrsOf (submodule [ stage1PathOptions unitConfig ]); + paths = attrsOf (submodule [ stage2PathOptions unitConfig pathConfig ]); + initrdPaths = attrsOf (submodule [ stage1PathOptions unitConfig pathConfig ]); - slices = attrsOf (submodule [ stage2SliceOptions unitConfig ]); - initrdSlices = attrsOf (submodule [ stage1SliceOptions unitConfig ]); + slices = attrsOf (submodule [ stage2SliceOptions unitConfig sliceConfig ]); + initrdSlices = attrsOf (submodule [ stage1SliceOptions unitConfig sliceConfig ]); mounts = listOf (submodule [ stage2MountOptions unitConfig mountConfig ]); initrdMounts = listOf (submodule [ stage1MountOptions unitConfig mountConfig ]); diff --git a/nixos/lib/systemd-unit-options.nix b/nixos/lib/systemd-unit-options.nix index fc990a87f0c2..160f2bf9483a 100644 --- a/nixos/lib/systemd-unit-options.nix +++ b/nixos/lib/systemd-unit-options.nix @@ -65,6 +65,14 @@ in rec { ''; }; + name = lib.mkOption { + type = lib.types.str; + description = '' + The name of this systemd unit, including its extension. + This can be used to refer to this unit from other systemd units. + ''; + }; + overrideStrategy = mkOption { default = "asDropinIfExists"; type = types.enum [ "asDropinIfExists" "asDropin" ]; diff --git a/nixos/lib/utils.nix b/nixos/lib/utils.nix index 4992113bdbd2..c1c1828a2c12 100644 --- a/nixos/lib/utils.nix +++ b/nixos/lib/utils.nix @@ -35,7 +35,8 @@ let inherit (lib.strings) toJSON normalizePath escapeC; in -rec { +let +utils = rec { # Copy configuration files to avoid having the entire sources in the system closure copyFile = filePath: pkgs.runCommand (builtins.unsafeDiscardStringContext (baseNameOf filePath)) {} '' @@ -262,11 +263,12 @@ rec { filter (x: !(elem (getName x) namesToRemove)) packages; systemdUtils = { - lib = import ./systemd-lib.nix { inherit lib config pkgs; }; + lib = import ./systemd-lib.nix { inherit lib config pkgs utils; }; unitOptions = import ./systemd-unit-options.nix { inherit lib systemdUtils; }; types = import ./systemd-types.nix { inherit lib systemdUtils pkgs; }; network = { units = import ./systemd-network-units.nix { inherit lib systemdUtils; }; }; }; -} +}; +in utils diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index aea6855f91c5..c82924763d5e 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -595,18 +595,17 @@ in }; systemd.units = - mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths - // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services - // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.slices - // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets - // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets - // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers - // listToAttrs (map - (v: let n = escapeSystemdPath v.where; - in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts) - // listToAttrs (map - (v: let n = escapeSystemdPath v.where; - in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts); + let + withName = cfgToUnit: cfg: lib.nameValuePair cfg.name (cfgToUnit cfg); + in + mapAttrs' (_: withName pathToUnit) cfg.paths + // mapAttrs' (_: withName serviceToUnit) cfg.services + // mapAttrs' (_: withName sliceToUnit) cfg.slices + // mapAttrs' (_: withName socketToUnit) cfg.sockets + // mapAttrs' (_: withName targetToUnit) cfg.targets + // mapAttrs' (_: withName timerToUnit) cfg.timers + // listToAttrs (map (withName mountToUnit) cfg.mounts) + // listToAttrs (map (withName automountToUnit) cfg.automounts); # Environment of PID 1 systemd.managerEnvironment = { diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index 00441b693d67..cc32b2a15e7c 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -490,18 +490,18 @@ in { targets.initrd.aliases = ["default.target"]; units = - mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths - // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services - // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.slices - // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets - // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets - // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers + mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit v)) cfg.paths + // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit v)) cfg.services + // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit v)) cfg.slices + // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit v)) cfg.sockets + // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit v)) cfg.targets + // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit v)) cfg.timers // listToAttrs (map (v: let n = escapeSystemdPath v.where; - in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts) + in nameValuePair "${n}.mount" (mountToUnit v)) cfg.mounts) // listToAttrs (map (v: let n = escapeSystemdPath v.where; - in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts); + in nameValuePair "${n}.automount" (automountToUnit v)) cfg.automounts); # make sure all the /dev nodes are set up services.systemd-tmpfiles-setup-dev.wantedBy = ["sysinit.target"]; diff --git a/nixos/modules/system/boot/systemd/user.nix b/nixos/modules/system/boot/systemd/user.nix index 4c7b51ee22b7..2685cf7e283a 100644 --- a/nixos/modules/system/boot/systemd/user.nix +++ b/nixos/modules/system/boot/systemd/user.nix @@ -175,12 +175,12 @@ in { }; systemd.user.units = - mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths - // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services - // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.slices - // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets - // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets - // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers; + mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit v)) cfg.paths + // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit v)) cfg.services + // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit v)) cfg.slices + // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit v)) cfg.sockets + // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit v)) cfg.targets + // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit v)) cfg.timers; # Generate timer units for all services that have a ‘startAt’ value. systemd.user.timers = diff --git a/nixos/tests/systemd.nix b/nixos/tests/systemd.nix index 1a39cc73c886..4b087d403f37 100644 --- a/nixos/tests/systemd.nix +++ b/nixos/tests/systemd.nix @@ -1,7 +1,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { name = "systemd"; - nodes.machine = { lib, ... }: { + nodes.machine = { config, lib, ... }: { imports = [ common/user-account.nix common/x11.nix ]; virtualisation.emptyDiskImages = [ 512 512 ]; @@ -38,9 +38,18 @@ import ./make-test-python.nix ({ pkgs, ... }: { script = "true"; }; + systemd.services.testDependency1 = { + description = "Test Dependency 1"; + wantedBy = [ config.systemd.services."testservice1".name ]; + serviceConfig.Type = "oneshot"; + script = '' + true + ''; + }; + systemd.services.testservice1 = { description = "Test Service 1"; - wantedBy = [ "multi-user.target" ]; + wantedBy = [ config.systemd.targets.multi-user.name ]; serviceConfig.Type = "oneshot"; script = '' if [ "$XXX_SYSTEM" = foo ]; then