From 12cb82af912c3aa1c664c018c8f08cf17bc34c08 Mon Sep 17 00:00:00 2001 From: Robert Helgesson Date: Wed, 10 Apr 2019 00:40:45 +0200 Subject: [PATCH] systemd: make the unit option type more robust This should allow more sensible merging behavior. In particular, with this change it is possible to use, for example, `mkForce` for greater control of merging. Fixes #543 --- modules/misc/news.nix | 30 ++++++++++++++++++++++++++++++ modules/systemd.nix | 22 ++++++++++++++-------- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/modules/misc/news.nix b/modules/misc/news.nix index 29523c92..b2ce5296 100644 --- a/modules/misc/news.nix +++ b/modules/misc/news.nix @@ -1019,6 +1019,36 @@ in A new module is available: 'services.xcape'. ''; } + + { + time = "2019-04-11T22:50:10+00:00"; + condition = hostPlatform.isLinux; + message = '' + The type used for the systemd unit options under + + systemd.user.services, systemd.user.sockets, etc. + + has been changed to offer more robust merging of configurations. + + If you don't override values within systemd units then you are not + affected by this change. Unfortunately, if you do override unit values + you may encounter errors due to this change. + + In particular, if you get an error saying that a "unique option" is + "defined multiple times" then you need to use 'lib.mkForce'. For + example, + + systemd.user.services.foo.Service.ExecStart = "/foo/bar"; + + becomes + + systemd.user.services.foo.Service.ExecStart = lib.mkForce "/foo/bar"; + + We had to make this change because the old merging was causing too + many confusing situations for people. Apologies for potentially + breaking your configuration! + ''; + } ]; }; } diff --git a/modules/systemd.nix b/modules/systemd.nix index 051aee94..2a67bb31 100644 --- a/modules/systemd.nix +++ b/modules/systemd.nix @@ -58,9 +58,14 @@ let servicesStartTimeoutMs = builtins.toString cfg.servicesStartTimeoutMs; - attrsRecursivelyMerged = types.attrs // { - merge = loc: foldl' (res: def: recursiveUpdate res def.value) {}; - }; + unitType = unitKind: with types; + let + primitive = either bool (either int str); + in + attrsOf (attrsOf (attrsOf (either primitive (listOf primitive)))) + // { + description = "systemd ${unitKind} unit configuration"; + }; unitDescription = type: '' Definition of systemd per-user ${type} units. Attributes are @@ -78,6 +83,7 @@ let { Unit = { Description = "Example description"; + Documentation = [ "man:example(1)" "man:example(5)" ]; }; ${type} = { @@ -113,35 +119,35 @@ in services = mkOption { default = {}; - type = attrsRecursivelyMerged; + type = unitType "service"; description = unitDescription "service"; example = unitExample "Service"; }; sockets = mkOption { default = {}; - type = attrsRecursivelyMerged; + type = unitType "socket"; description = unitDescription "socket"; example = unitExample "Socket"; }; targets = mkOption { default = {}; - type = attrsRecursivelyMerged; + type = unitType "target"; description = unitDescription "target"; example = unitExample "Target"; }; timers = mkOption { default = {}; - type = attrsRecursivelyMerged; + type = unitType "timer"; description = unitDescription "timer"; example = unitExample "Timer"; }; paths = mkOption { default = {}; - type = attrsRecursivelyMerged; + type = unitType "path"; description = unitDescription "path"; example = unitExample "Path"; };