git.iko.soy/typeable/nixpkgs
on master help sign in
Merge pull request #167153 from helsinki-systems/feat/systemd-stage-1-jobscripts

nixos/stage-1-systemd: Implement job scripts

commit33a73886c4eaccbe407112e26d378383ba0f0171 authorLassulus <github@lassul.us> dateMon 4 Apr 2022, 14:15 (5 years ago) parentdd0d10bbeb0222
2 files changed+332−305
nixos/lib/systemd-unit-options.nix+330−304 nixos/modules/system/boot/systemd/initrd.nix+2−1
nixos/lib/systemd-unit-options.nix+330−304
@@ -94,131 +94,133 @@ in rec {
9494
9595 };
9696
97− commonUnitOptions = { options = (sharedOptions // {
97+ commonUnitOptions = {
98+ options = sharedOptions // {
9899
99− description = mkOption {
100− default = "";
101− type = types.singleLineStr;
102− description = "Description of this unit used in systemd messages and progress indicators.";
103− };
100+ description = mkOption {
101+ default = "";
102+ type = types.singleLineStr;
103+ description = "Description of this unit used in systemd messages and progress indicators.";
104+ };
104105
105− documentation = mkOption {
106− default = [];
107− type = types.listOf types.str;
108− description = "A list of URIs referencing documentation for this unit or its configuration.";
109− };
106+ documentation = mkOption {
107+ default = [];
108+ type = types.listOf types.str;
109+ description = "A list of URIs referencing documentation for this unit or its configuration.";
110+ };
110111
111− requires = mkOption {
112− default = [];
113− type = types.listOf unitNameType;
114− description = ''
115− Start the specified units when this unit is started, and stop
116− this unit when the specified units are stopped or fail.
117− '';
118− };
112+ requires = mkOption {
113+ default = [];
114+ type = types.listOf unitNameType;
115+ description = ''
116+ Start the specified units when this unit is started, and stop
117+ this unit when the specified units are stopped or fail.
118+ '';
119+ };
119120
120− wants = mkOption {
121− default = [];
122− type = types.listOf unitNameType;
123− description = ''
124− Start the specified units when this unit is started.
125− '';
126− };
121+ wants = mkOption {
122+ default = [];
123+ type = types.listOf unitNameType;
124+ description = ''
125+ Start the specified units when this unit is started.
126+ '';
127+ };
127128
128− after = mkOption {
129− default = [];
130− type = types.listOf unitNameType;
131− description = ''
132− If the specified units are started at the same time as
133− this unit, delay this unit until they have started.
134− '';
135− };
129+ after = mkOption {
130+ default = [];
131+ type = types.listOf unitNameType;
132+ description = ''
133+ If the specified units are started at the same time as
134+ this unit, delay this unit until they have started.
135+ '';
136+ };
136137
137− before = mkOption {
138− default = [];
139− type = types.listOf unitNameType;
140− description = ''
141− If the specified units are started at the same time as
142− this unit, delay them until this unit has started.
143− '';
144− };
138+ before = mkOption {
139+ default = [];
140+ type = types.listOf unitNameType;
141+ description = ''
142+ If the specified units are started at the same time as
143+ this unit, delay them until this unit has started.
144+ '';
145+ };
145146
146− bindsTo = mkOption {
147− default = [];
148− type = types.listOf unitNameType;
149− description = ''
150− Like ‘requires’, but in addition, if the specified units
151− unexpectedly disappear, this unit will be stopped as well.
152− '';
153− };
147+ bindsTo = mkOption {
148+ default = [];
149+ type = types.listOf unitNameType;
150+ description = ''
151+ Like ‘requires’, but in addition, if the specified units
152+ unexpectedly disappear, this unit will be stopped as well.
153+ '';
154+ };
154155
155− partOf = mkOption {
156− default = [];
157− type = types.listOf unitNameType;
158− description = ''
159− If the specified units are stopped or restarted, then this
160− unit is stopped or restarted as well.
161− '';
162− };
156+ partOf = mkOption {
157+ default = [];
158+ type = types.listOf unitNameType;
159+ description = ''
160+ If the specified units are stopped or restarted, then this
161+ unit is stopped or restarted as well.
162+ '';
163+ };
163164
164− conflicts = mkOption {
165− default = [];
166− type = types.listOf unitNameType;
167− description = ''
168− If the specified units are started, then this unit is stopped
169− and vice versa.
170− '';
171− };
165+ conflicts = mkOption {
166+ default = [];
167+ type = types.listOf unitNameType;
168+ description = ''
169+ If the specified units are started, then this unit is stopped
170+ and vice versa.
171+ '';
172+ };
172173
173− requisite = mkOption {
174− default = [];
175− type = types.listOf unitNameType;
176− description = ''
177− Similar to requires. However if the units listed are not started,
178− they will not be started and the transaction will fail.
179− '';
180− };
174+ requisite = mkOption {
175+ default = [];
176+ type = types.listOf unitNameType;
177+ description = ''
178+ Similar to requires. However if the units listed are not started,
179+ they will not be started and the transaction will fail.
180+ '';
181+ };
181182
182− unitConfig = mkOption {
183− default = {};
184− example = { RequiresMountsFor = "/data"; };
185− type = types.attrsOf unitOption;
186− description = ''
187− Each attribute in this set specifies an option in the
188− <literal>[Unit]</literal> section of the unit. See
189− <citerefentry><refentrytitle>systemd.unit</refentrytitle>
190− <manvolnum>5</manvolnum></citerefentry> for details.
191− '';
192− };
183+ unitConfig = mkOption {
184+ default = {};
185+ example = { RequiresMountsFor = "/data"; };
186+ type = types.attrsOf unitOption;
187+ description = ''
188+ Each attribute in this set specifies an option in the
189+ <literal>[Unit]</literal> section of the unit. See
190+ <citerefentry><refentrytitle>systemd.unit</refentrytitle>
191+ <manvolnum>5</manvolnum></citerefentry> for details.
192+ '';
193+ };
193194
194− onFailure = mkOption {
195− default = [];
196− type = types.listOf unitNameType;
197− description = ''
198− A list of one or more units that are activated when
199− this unit enters the "failed" state.
200− '';
201− };
195+ onFailure = mkOption {
196+ default = [];
197+ type = types.listOf unitNameType;
198+ description = ''
199+ A list of one or more units that are activated when
200+ this unit enters the "failed" state.
201+ '';
202+ };
202203
203− startLimitBurst = mkOption {
204− type = types.int;
205− description = ''
206− Configure unit start rate limiting. Units which are started
207− more than startLimitBurst times within an interval time
208− interval are not permitted to start any more.
209− '';
210− };
204+ startLimitBurst = mkOption {
205+ type = types.int;
206+ description = ''
207+ Configure unit start rate limiting. Units which are started
208+ more than startLimitBurst times within an interval time
209+ interval are not permitted to start any more.
210+ '';
211+ };
211212
212− startLimitIntervalSec = mkOption {
213− type = types.int;
214− description = ''
215− Configure unit start rate limiting. Units which are started
216− more than startLimitBurst times within an interval time
217− interval are not permitted to start any more.
218− '';
219− };
213+ startLimitIntervalSec = mkOption {
214+ type = types.int;
215+ description = ''
216+ Configure unit start rate limiting. Units which are started
217+ more than startLimitBurst times within an interval time
218+ interval are not permitted to start any more.
219+ '';
220+ };
220221
221− }); };
222+ };
223+ };
222224
223225 stage2CommonUnitOptions = {
224226 imports = [
@@ -250,49 +252,41 @@ in rec {
250252 };
251253 stage1CommonUnitOptions = commonUnitOptions;
252254
253− serviceOptions = { options = {
254−
255− environment = mkOption {
256− default = {};
257− type = with types; attrsOf (nullOr (oneOf [ str path package ]));
258− example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
259− description = "Environment variables passed to the service's processes.";
260− };
261−
262− path = mkOption {
263− default = [];
264− type = with types; listOf (oneOf [ package str ]);
265− description = ''
266− Packages added to the service's <envar>PATH</envar>
267− environment variable. Both the <filename>bin</filename>
268− and <filename>sbin</filename> subdirectories of each
269− package are added.
270− '';
271− };
255+ serviceOptions = { name, config, ... }: {
256+ options = {
272257
273− serviceConfig = mkOption {
274− default = {};
275− example =
276− { RestartSec = 5;
277− };
278− type = types.addCheck (types.attrsOf unitOption) checkService;
279− description = ''
280− Each attribute in this set specifies an option in the
281− <literal>[Service]</literal> section of the unit. See
282− <citerefentry><refentrytitle>systemd.service</refentrytitle>
283− <manvolnum>5</manvolnum></citerefentry> for details.
284− '';
285− };
258+ environment = mkOption {
259+ default = {};
260+ type = with types; attrsOf (nullOr (oneOf [ str path package ]));
261+ example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
262+ description = "Environment variables passed to the service's processes.";
263+ };
286264
287− }; };
265+ path = mkOption {
266+ default = [];
267+ type = with types; listOf (oneOf [ package str ]);
268+ description = ''
269+ Packages added to the service's <envar>PATH</envar>
270+ environment variable. Both the <filename>bin</filename>
271+ and <filename>sbin</filename> subdirectories of each
272+ package are added.
273+ '';
274+ };
288275
289− stage2ServiceOptions = { name, config, ... }: {
290− imports = [
291− stage2CommonUnitOptions
292− serviceOptions
293− ];
276+ serviceConfig = mkOption {
277+ default = {};
278+ example =
279+ { RestartSec = 5;
280+ };
281+ type = types.addCheck (types.attrsOf unitOption) checkService;
282+ description = ''
283+ Each attribute in this set specifies an option in the
284+ <literal>[Service]</literal> section of the unit. See
285+ <citerefentry><refentrytitle>systemd.service</refentrytitle>
286+ <manvolnum>5</manvolnum></citerefentry> for details.
287+ '';
288+ };
294289
295− options = {
296290 script = mkOption {
297291 type = types.lines;
298292 default = "";
@@ -349,6 +343,51 @@ in rec {
349343 '';
350344 };
351345
346+ jobScripts = mkOption {
347+ type = with types; coercedTo path singleton (listOf path);
348+ internal = true;
349+ description = "A list of all job script derivations of this unit.";
350+ default = [];
351+ };
352+
353+ };
354+
355+ config = mkMerge [
356+ (mkIf (config.preStart != "") rec {
357+ jobScripts = makeJobScript "${name}-pre-start" config.preStart;
358+ serviceConfig.ExecStartPre = [ jobScripts ];
359+ })
360+ (mkIf (config.script != "") rec {
361+ jobScripts = makeJobScript "${name}-start" config.script;
362+ serviceConfig.ExecStart = jobScripts + " " + config.scriptArgs;
363+ })
364+ (mkIf (config.postStart != "") rec {
365+ jobScripts = (makeJobScript "${name}-post-start" config.postStart);
366+ serviceConfig.ExecStartPost = [ jobScripts ];
367+ })
368+ (mkIf (config.reload != "") rec {
369+ jobScripts = makeJobScript "${name}-reload" config.reload;
370+ serviceConfig.ExecReload = jobScripts;
371+ })
372+ (mkIf (config.preStop != "") rec {
373+ jobScripts = makeJobScript "${name}-pre-stop" config.preStop;
374+ serviceConfig.ExecStop = jobScripts;
375+ })
376+ (mkIf (config.postStop != "") rec {
377+ jobScripts = makeJobScript "${name}-post-stop" config.postStop;
378+ serviceConfig.ExecStopPost = jobScripts;
379+ })
380+ ];
381+
382+ };
383+
384+ stage2ServiceOptions = {
385+ imports = [
386+ stage2CommonUnitOptions
387+ serviceOptions
388+ ];
389+
390+ options = {
352391 restartIfChanged = mkOption {
353392 type = types.bool;
354393 default = true;
@@ -404,33 +443,6 @@ in rec {
404443 apply = v: if isList v then v else [ v ];
405444 };
406445 };
407−
408− config = mkMerge
409− [ (mkIf (config.preStart != "")
410− { serviceConfig.ExecStartPre =
411− [ (makeJobScript "${name}-pre-start" config.preStart) ];
412− })
413− (mkIf (config.script != "")
414− { serviceConfig.ExecStart =
415− makeJobScript "${name}-start" config.script + " " + config.scriptArgs;
416− })
417− (mkIf (config.postStart != "")
418− { serviceConfig.ExecStartPost =
419− [ (makeJobScript "${name}-post-start" config.postStart) ];
420− })
421− (mkIf (config.reload != "")
422− { serviceConfig.ExecReload =
423− makeJobScript "${name}-reload" config.reload;
424− })
425− (mkIf (config.preStop != "")
426− { serviceConfig.ExecStop =
427− makeJobScript "${name}-pre-stop" config.preStop;
428− })
429− (mkIf (config.postStop != "")
430− { serviceConfig.ExecStopPost =
431− makeJobScript "${name}-post-stop" config.postStop;
432− })
433− ];
434446 };
435447
436448 stage1ServiceOptions = {
@@ -441,41 +453,43 @@ in rec {
441453 };
442454
443455
444− socketOptions = { options = {
456+ socketOptions = {
457+ options = {
445458
446− listenStreams = mkOption {
447− default = [];
448− type = types.listOf types.str;
449− example = [ "0.0.0.0:993" "/run/my-socket" ];
450− description = ''
451− For each item in this list, a <literal>ListenStream</literal>
452− option in the <literal>[Socket]</literal> section will be created.
453− '';
454− };
459+ listenStreams = mkOption {
460+ default = [];
461+ type = types.listOf types.str;
462+ example = [ "0.0.0.0:993" "/run/my-socket" ];
463+ description = ''
464+ For each item in this list, a <literal>ListenStream</literal>
465+ option in the <literal>[Socket]</literal> section will be created.
466+ '';
467+ };
455468
456− listenDatagrams = mkOption {
457− default = [];
458− type = types.listOf types.str;
459− example = [ "0.0.0.0:993" "/run/my-socket" ];
460− description = ''
461− For each item in this list, a <literal>ListenDatagram</literal>
462− option in the <literal>[Socket]</literal> section will be created.
463− '';
464− };
469+ listenDatagrams = mkOption {
470+ default = [];
471+ type = types.listOf types.str;
472+ example = [ "0.0.0.0:993" "/run/my-socket" ];
473+ description = ''
474+ For each item in this list, a <literal>ListenDatagram</literal>
475+ option in the <literal>[Socket]</literal> section will be created.
476+ '';
477+ };
465478
466− socketConfig = mkOption {
467− default = {};
468− example = { ListenStream = "/run/my-socket"; };
469− type = types.attrsOf unitOption;
470− description = ''
471− Each attribute in this set specifies an option in the
472− <literal>[Socket]</literal> section of the unit. See
473− <citerefentry><refentrytitle>systemd.socket</refentrytitle>
474− <manvolnum>5</manvolnum></citerefentry> for details.
475− '';
479+ socketConfig = mkOption {
480+ default = {};
481+ example = { ListenStream = "/run/my-socket"; };
482+ type = types.attrsOf unitOption;
483+ description = ''
484+ Each attribute in this set specifies an option in the
485+ <literal>[Socket]</literal> section of the unit. See
486+ <citerefentry><refentrytitle>systemd.socket</refentrytitle>
487+ <manvolnum>5</manvolnum></citerefentry> for details.
488+ '';
489+ };
476490 };
477491
478− }; };
492+ };
479493
480494 stage2SocketOptions = {
481495 imports = [
@@ -492,23 +506,25 @@ in rec {
492506 };
493507
494508
495− timerOptions = { options = {
509+ timerOptions = {
510+ options = {
496511
497− timerConfig = mkOption {
498− default = {};
499− example = { OnCalendar = "Sun 14:00:00"; Unit = "foo.service"; };
500− type = types.attrsOf unitOption;
501− description = ''
502− Each attribute in this set specifies an option in the
503− <literal>[Timer]</literal> section of the unit. See
504− <citerefentry><refentrytitle>systemd.timer</refentrytitle>
505− <manvolnum>5</manvolnum></citerefentry> and
506− <citerefentry><refentrytitle>systemd.time</refentrytitle>
507− <manvolnum>7</manvolnum></citerefentry> for details.
508− '';
509− };
512+ timerConfig = mkOption {
513+ default = {};
514+ example = { OnCalendar = "Sun 14:00:00"; Unit = "foo.service"; };
515+ type = types.attrsOf unitOption;
516+ description = ''
517+ Each attribute in this set specifies an option in the
518+ <literal>[Timer]</literal> section of the unit. See
519+ <citerefentry><refentrytitle>systemd.timer</refentrytitle>
520+ <manvolnum>5</manvolnum></citerefentry> and
521+ <citerefentry><refentrytitle>systemd.time</refentrytitle>
522+ <manvolnum>7</manvolnum></citerefentry> for details.
523+ '';
524+ };
510525
511− }; };
526+ };
527+ };
512528
513529 stage2TimerOptions = {
514530 imports = [
@@ -525,21 +541,23 @@ in rec {
525541 };
526542
527543
528− pathOptions = { options = {
544+ pathOptions = {
545+ options = {
529546
530− pathConfig = mkOption {
531− default = {};
532− example = { PathChanged = "/some/path"; Unit = "changedpath.service"; };
533− type = types.attrsOf unitOption;
534− description = ''
535− Each attribute in this set specifies an option in the
536− <literal>[Path]</literal> section of the unit. See
537− <citerefentry><refentrytitle>systemd.path</refentrytitle>
538− <manvolnum>5</manvolnum></citerefentry> for details.
539− '';
540− };
547+ pathConfig = mkOption {
548+ default = {};
549+ example = { PathChanged = "/some/path"; Unit = "changedpath.service"; };
550+ type = types.attrsOf unitOption;
551+ description = ''
552+ Each attribute in this set specifies an option in the
553+ <literal>[Path]</literal> section of the unit. See
554+ <citerefentry><refentrytitle>systemd.path</refentrytitle>
555+ <manvolnum>5</manvolnum></citerefentry> for details.
556+ '';
557+ };
541558
542− }; };
559+ };
560+ };
543561
544562 stage2PathOptions = {
545563 imports = [
@@ -556,49 +574,52 @@ in rec {
556574 };
557575
558576
559− mountOptions = { options = {
577+ mountOptions = {
578+ options = {
560579
561− what = mkOption {
562− example = "/dev/sda1";
563− type = types.str;
564− description = "Absolute path of device node, file or other resource. (Mandatory)";
565− };
580+ what = mkOption {
581+ example = "/dev/sda1";
582+ type = types.str;
583+ description = "Absolute path of device node, file or other resource. (Mandatory)";
584+ };
566585
567− where = mkOption {
568− example = "/mnt";
569− type = types.str;
570− description = ''
571− Absolute path of a directory of the mount point.
572− Will be created if it doesn't exist. (Mandatory)
573− '';
574− };
586+ where = mkOption {
587+ example = "/mnt";
588+ type = types.str;
589+ description = ''
590+ Absolute path of a directory of the mount point.
591+ Will be created if it doesn't exist. (Mandatory)
592+ '';
593+ };
575594
576− type = mkOption {
577− default = "";
578− example = "ext4";
579− type = types.str;
580− description = "File system type.";
581− };
595+ type = mkOption {
596+ default = "";
597+ example = "ext4";
598+ type = types.str;
599+ description = "File system type.";
600+ };
582601
583− options = mkOption {
584− default = "";
585− example = "noatime";
586− type = types.commas;
587− description = "Options used to mount the file system.";
588− };
602+ options = mkOption {
603+ default = "";
604+ example = "noatime";
605+ type = types.commas;
606+ description = "Options used to mount the file system.";
607+ };
608+
609+ mountConfig = mkOption {
610+ default = {};
611+ example = { DirectoryMode = "0775"; };
612+ type = types.attrsOf unitOption;
613+ description = ''
614+ Each attribute in this set specifies an option in the
615+ <literal>[Mount]</literal> section of the unit. See
616+ <citerefentry><refentrytitle>systemd.mount</refentrytitle>
617+ <manvolnum>5</manvolnum></citerefentry> for details.
618+ '';
619+ };
589620
590− mountConfig = mkOption {
591− default = {};
592− example = { DirectoryMode = "0775"; };
593− type = types.attrsOf unitOption;
594− description = ''
595− Each attribute in this set specifies an option in the
596− <literal>[Mount]</literal> section of the unit. See
597− <citerefentry><refentrytitle>systemd.mount</refentrytitle>
598− <manvolnum>5</manvolnum></citerefentry> for details.
599− '';
600621 };
601− }; };
622+ };
602623
603624 stage2MountOptions = {
604625 imports = [
@@ -614,29 +635,32 @@ in rec {
614635 ];
615636 };
616637
617− automountOptions = { options = {
638+ automountOptions = {
639+ options = {
618640
619− where = mkOption {
620− example = "/mnt";
621− type = types.str;
622− description = ''
623− Absolute path of a directory of the mount point.
624− Will be created if it doesn't exist. (Mandatory)
625− '';
626− };
641+ where = mkOption {
642+ example = "/mnt";
643+ type = types.str;
644+ description = ''
645+ Absolute path of a directory of the mount point.
646+ Will be created if it doesn't exist. (Mandatory)
647+ '';
648+ };
649+
650+ automountConfig = mkOption {
651+ default = {};
652+ example = { DirectoryMode = "0775"; };
653+ type = types.attrsOf unitOption;
654+ description = ''
655+ Each attribute in this set specifies an option in the
656+ <literal>[Automount]</literal> section of the unit. See
657+ <citerefentry><refentrytitle>systemd.automount</refentrytitle>
658+ <manvolnum>5</manvolnum></citerefentry> for details.
659+ '';
660+ };
627661
628− automountConfig = mkOption {
629− default = {};
630− example = { DirectoryMode = "0775"; };
631− type = types.attrsOf unitOption;
632− description = ''
633− Each attribute in this set specifies an option in the
634− <literal>[Automount]</literal> section of the unit. See
635− <citerefentry><refentrytitle>systemd.automount</refentrytitle>
636− <manvolnum>5</manvolnum></citerefentry> for details.
637− '';
638662 };
639− }; };
663+ };
640664
641665 stage2AutomountOptions = {
642666 imports = [
@@ -652,21 +676,23 @@ in rec {
652676 ];
653677 };
654678
655− sliceOptions = { options = {
679+ sliceOptions = {
680+ options = {
656681
657− sliceConfig = mkOption {
658− default = {};
659− example = { MemoryMax = "2G"; };
660− type = types.attrsOf unitOption;
661− description = ''
662− Each attribute in this set specifies an option in the
663− <literal>[Slice]</literal> section of the unit. See
664− <citerefentry><refentrytitle>systemd.slice</refentrytitle>
665− <manvolnum>5</manvolnum></citerefentry> for details.
666− '';
667− };
682+ sliceConfig = mkOption {
683+ default = {};
684+ example = { MemoryMax = "2G"; };
685+ type = types.attrsOf unitOption;
686+ description = ''
687+ Each attribute in this set specifies an option in the
688+ <literal>[Slice]</literal> section of the unit. See
689+ <citerefentry><refentrytitle>systemd.slice</refentrytitle>
690+ <manvolnum>5</manvolnum></citerefentry> for details.
691+ '';
692+ };
668693
669− }; };
694+ };
695+ };
670696
671697 stage2SliceOptions = {
672698 imports = [
nixos/modules/system/boot/systemd/initrd.nix+2−1
@@ -96,6 +96,7 @@ let
9696
9797 enabledUpstreamUnits = filter (n: ! elem n cfg.suppressedUnits) upstreamUnits;
9898 enabledUnits = filterAttrs (n: v: ! elem n cfg.suppressedUnits) cfg.units;
99+ jobScripts = concatLists (mapAttrsToList (_: unit: unit.jobScripts or []) (filterAttrs (_: v: v.enable) cfg.services));
99100
100101 stage1Units = generateUnits {
101102 type = "initrd";
@@ -378,7 +379,7 @@ in {
378379
379380 # so NSS can look up usernames
380381 "${pkgs.glibc}/lib/libnss_files.so"
381− ];
382+ ] ++ jobScripts;
382383
383384 targets.initrd.aliases = ["default.target"];
384385 units =