Unverified Commit 9258f576 authored by r-vdp's avatar r-vdp
Browse files

systemd: add a name option to all systemd units

This allows us to set things like dependencies in a way that we can
catch typos at eval time.
So instead of
```nix
systemd.services.foo.wants = [ "bar.service" ];
```
we can write
```nix
systemd.services.foo.wants = [ config.systemd.services.bar.name ];
```
which will throw an error if no such service has been defined.

Not all cases can be done like this (eg template services), but in a lot
of cases this will allow to avoid typos.

There is a matching option on the unit option
(`systemd.units."foo.service".name`) as well.
parent b432281d
Loading
Loading
Loading
Loading
+55 −20
Original line number Diff line number Diff line
{ 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}
+20 −12
Original line number Diff line number Diff line
@@ -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 ]);
+8 −0
Original line number Diff line number Diff line
@@ -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" ];
+5 −3
Original line number Diff line number Diff line
@@ -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
+11 −12
Original line number Diff line number Diff line
@@ -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 = {
Loading