1{ config, lib, pkgs, utils, ... }:
2
3with utils;
4with lib;
5with import ./systemd-unit-options.nix { inherit config lib; };
6with import ./systemd-lib.nix { inherit config lib pkgs; };
7
8let
9
10 cfg = config.systemd;
11
12 systemd = cfg.package;
13
14 upstreamSystemUnits =
15 [ # Targets.
16 "basic.target"
17 "busnames.target"
18 "sysinit.target"
19 "sockets.target"
20 "exit.target"
21 "graphical.target"
22 "multi-user.target"
23 "network.target"
24 "network-pre.target"
25 "network-online.target"
26 "nss-lookup.target"
27 "nss-user-lookup.target"
28 "time-sync.target"
29 #"cryptsetup.target"
30 "sigpwr.target"
31 "timers.target"
32 "paths.target"
33 "rpcbind.target"
34
35 # Rescue mode.
36 "rescue.target"
37 "rescue.service"
38
39 # Udev.
40 "systemd-udevd-control.socket"
41 "systemd-udevd-kernel.socket"
42 "systemd-udevd.service"
43 "systemd-udev-settle.service"
44 "systemd-udev-trigger.service"
45 # hwdb.bin is managed by NixOS
46 # "systemd-hwdb-update.service"
47
48 # Consoles.
49 "getty.target"
50 "getty@.service"
51 "serial-getty@.service"
52 "console-getty.service"
53 "container-getty@.service"
54 "systemd-vconsole-setup.service"
55
56 # Hardware (started by udev when a relevant device is plugged in).
57 "sound.target"
58 "bluetooth.target"
59 "printer.target"
60 "smartcard.target"
61
62 # Login stuff.
63 "systemd-logind.service"
64 "autovt@.service"
65 "systemd-user-sessions.service"
66 "dbus-org.freedesktop.login1.service"
67 "dbus-org.freedesktop.machine1.service"
68 "org.freedesktop.login1.busname"
69 "org.freedesktop.machine1.busname"
70 "user@.service"
71
72 # Journal.
73 "systemd-journald.socket"
74 "systemd-journald.service"
75 "systemd-journal-flush.service"
76 "systemd-journal-gatewayd.socket"
77 "systemd-journal-gatewayd.service"
78 "systemd-journal-catalog-update.service"
79 "systemd-journald-audit.socket"
80 "systemd-journald-dev-log.socket"
81 "syslog.socket"
82
83 # SysV init compatibility.
84 "systemd-initctl.socket"
85 "systemd-initctl.service"
86
87 # Kernel module loading.
88 "systemd-modules-load.service"
89 "kmod-static-nodes.service"
90
91 # Filesystems.
92 "systemd-fsck@.service"
93 "systemd-fsck-root.service"
94 "systemd-remount-fs.service"
95 "local-fs.target"
96 "local-fs-pre.target"
97 "remote-fs.target"
98 "remote-fs-pre.target"
99 "swap.target"
100 "dev-hugepages.mount"
101 "dev-mqueue.mount"
102 "proc-sys-fs-binfmt_misc.mount"
103 "sys-fs-fuse-connections.mount"
104 "sys-kernel-config.mount"
105 "sys-kernel-debug.mount"
106
107 # Maintaining state across reboots.
108 "systemd-random-seed.service"
109 "systemd-backlight@.service"
110 "systemd-rfkill.service"
111 "systemd-rfkill.socket"
112
113 # Hibernate / suspend.
114 "hibernate.target"
115 "suspend.target"
116 "sleep.target"
117 "hybrid-sleep.target"
118 "systemd-hibernate.service"
119 "systemd-hybrid-sleep.service"
120 "systemd-suspend.service"
121
122 # Reboot stuff.
123 "reboot.target"
124 "systemd-reboot.service"
125 "poweroff.target"
126 "systemd-poweroff.service"
127 "halt.target"
128 "systemd-halt.service"
129 "shutdown.target"
130 "umount.target"
131 "final.target"
132 "kexec.target"
133 "systemd-kexec.service"
134 "systemd-update-utmp.service"
135
136 # Password entry.
137 "systemd-ask-password-console.path"
138 "systemd-ask-password-console.service"
139 "systemd-ask-password-wall.path"
140 "systemd-ask-password-wall.service"
141
142 # Slices / containers.
143 "slices.target"
144 "system.slice"
145 "user.slice"
146 "machine.slice"
147 "machines.target"
148 "systemd-machined.service"
149 "systemd-nspawn@.service"
150
151 # Temporary file creation / cleanup.
152 "systemd-tmpfiles-clean.service"
153 "systemd-tmpfiles-clean.timer"
154 "systemd-tmpfiles-setup.service"
155 "systemd-tmpfiles-setup-dev.service"
156
157 # Misc.
158 "org.freedesktop.systemd1.busname"
159 "systemd-sysctl.service"
160 "dbus-org.freedesktop.timedate1.service"
161 "dbus-org.freedesktop.locale1.service"
162 "dbus-org.freedesktop.hostname1.service"
163 "org.freedesktop.timedate1.busname"
164 "org.freedesktop.locale1.busname"
165 "org.freedesktop.hostname1.busname"
166 "systemd-timedated.service"
167 "systemd-localed.service"
168 "systemd-hostnamed.service"
169 "systemd-binfmt.service"
170 "systemd-exit.service"
171 ]
172 ++ cfg.additionalUpstreamSystemUnits;
173
174 upstreamSystemWants =
175 [ "sysinit.target.wants"
176 "sockets.target.wants"
177 "local-fs.target.wants"
178 "multi-user.target.wants"
179 "timers.target.wants"
180 ];
181
182 upstreamUserUnits =
183 [ "basic.target"
184 "bluetooth.target"
185 "busnames.target"
186 "default.target"
187 "exit.target"
188 "graphical-session-pre.target"
189 "graphical-session.target"
190 "paths.target"
191 "printer.target"
192 "shutdown.target"
193 "smartcard.target"
194 "sockets.target"
195 "sound.target"
196 "systemd-exit.service"
197 "timers.target"
198 ];
199
200 boolToString = value: if value then "yes" else "no";
201
202 makeJobScript = name: text:
203 let mkScriptName = s: (replaceChars [ "\\" ] [ "-" ] (shellEscape s) );
204 x = pkgs.writeTextFile { name = "unit-script"; executable = true; destination = "/bin/${mkScriptName name}"; inherit text; };
205 in "${x}/bin/${mkScriptName name}";
206
207 unitConfig = { name, config, ... }: {
208 config = {
209 unitConfig =
210 optionalAttrs (config.requires != [])
211 { Requires = toString config.requires; }
212 // optionalAttrs (config.wants != [])
213 { Wants = toString config.wants; }
214 // optionalAttrs (config.after != [])
215 { After = toString config.after; }
216 // optionalAttrs (config.before != [])
217 { Before = toString config.before; }
218 // optionalAttrs (config.bindsTo != [])
219 { BindsTo = toString config.bindsTo; }
220 // optionalAttrs (config.partOf != [])
221 { PartOf = toString config.partOf; }
222 // optionalAttrs (config.conflicts != [])
223 { Conflicts = toString config.conflicts; }
224 // optionalAttrs (config.requisite != [])
225 { Requisite = toString config.requisite; }
226 // optionalAttrs (config.restartTriggers != [])
227 { X-Restart-Triggers = toString config.restartTriggers; }
228 // optionalAttrs (config.description != "") {
229 Description = config.description; }
230 // optionalAttrs (config.documentation != []) {
231 Documentation = toString config.documentation; }
232 // optionalAttrs (config.onFailure != []) {
233 OnFailure = toString config.onFailure;
234 };
235 };
236 };
237
238 serviceConfig = { name, config, ... }: {
239 config = mkMerge
240 [ { # Default path for systemd services. Should be quite minimal.
241 path =
242 [ pkgs.coreutils
243 pkgs.findutils
244 pkgs.gnugrep
245 pkgs.gnused
246 systemd
247 ];
248 environment.PATH = config.path;
249 }
250 (mkIf (config.preStart != "")
251 { serviceConfig.ExecStartPre = makeJobScript "${name}-pre-start" ''
252 #! ${pkgs.stdenv.shell} -e
253 ${config.preStart}
254 '';
255 })
256 (mkIf (config.script != "")
257 { serviceConfig.ExecStart = makeJobScript "${name}-start" ''
258 #! ${pkgs.stdenv.shell} -e
259 ${config.script}
260 '' + " " + config.scriptArgs;
261 })
262 (mkIf (config.postStart != "")
263 { serviceConfig.ExecStartPost = makeJobScript "${name}-post-start" ''
264 #! ${pkgs.stdenv.shell} -e
265 ${config.postStart}
266 '';
267 })
268 (mkIf (config.reload != "")
269 { serviceConfig.ExecReload = makeJobScript "${name}-reload" ''
270 #! ${pkgs.stdenv.shell} -e
271 ${config.reload}
272 '';
273 })
274 (mkIf (config.preStop != "")
275 { serviceConfig.ExecStop = makeJobScript "${name}-pre-stop" ''
276 #! ${pkgs.stdenv.shell} -e
277 ${config.preStop}
278 '';
279 })
280 (mkIf (config.postStop != "")
281 { serviceConfig.ExecStopPost = makeJobScript "${name}-post-stop" ''
282 #! ${pkgs.stdenv.shell} -e
283 ${config.postStop}
284 '';
285 })
286 ];
287 };
288
289 mountConfig = { name, config, ... }: {
290 config = {
291 mountConfig =
292 { What = config.what;
293 Where = config.where;
294 } // optionalAttrs (config.type != "") {
295 Type = config.type;
296 } // optionalAttrs (config.options != "") {
297 Options = config.options;
298 };
299 };
300 };
301
302 automountConfig = { name, config, ... }: {
303 config = {
304 automountConfig =
305 { Where = config.where;
306 };
307 };
308 };
309
310 commonUnitText = def: ''
311 [Unit]
312 ${attrsToSection def.unitConfig}
313 '';
314
315 targetToUnit = name: def:
316 { inherit (def) aliases wantedBy requiredBy enable;
317 text =
318 ''
319 [Unit]
320 ${attrsToSection def.unitConfig}
321 '';
322 };
323
324 serviceToUnit = name: def:
325 { inherit (def) aliases wantedBy requiredBy enable;
326 text = commonUnitText def +
327 ''
328 [Service]
329 ${let env = cfg.globalEnvironment // def.environment;
330 in concatMapStrings (n:
331 let s = optionalString (env."${n}" != null)
332 "Environment=${builtins.toJSON "${n}=${env.${n}}"}\n";
333 in if stringLength s >= 2048 then throw "The value of the environment variable ‘${n}’ in systemd service ‘${name}.service’ is too long." else s) (attrNames env)}
334 ${if def.reloadIfChanged then ''
335 X-ReloadIfChanged=true
336 '' else if !def.restartIfChanged then ''
337 X-RestartIfChanged=false
338 '' else ""}
339 ${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"}
340 ${attrsToSection def.serviceConfig}
341 '';
342 };
343
344 socketToUnit = name: def:
345 { inherit (def) aliases wantedBy requiredBy enable;
346 text = commonUnitText def +
347 ''
348 [Socket]
349 ${attrsToSection def.socketConfig}
350 ${concatStringsSep "\n" (map (s: "ListenStream=${s}") def.listenStreams)}
351 '';
352 };
353
354 timerToUnit = name: def:
355 { inherit (def) aliases wantedBy requiredBy enable;
356 text = commonUnitText def +
357 ''
358 [Timer]
359 ${attrsToSection def.timerConfig}
360 '';
361 };
362
363 pathToUnit = name: def:
364 { inherit (def) aliases wantedBy requiredBy enable;
365 text = commonUnitText def +
366 ''
367 [Path]
368 ${attrsToSection def.pathConfig}
369 '';
370 };
371
372 mountToUnit = name: def:
373 { inherit (def) aliases wantedBy requiredBy enable;
374 text = commonUnitText def +
375 ''
376 [Mount]
377 ${attrsToSection def.mountConfig}
378 '';
379 };
380
381 automountToUnit = name: def:
382 { inherit (def) aliases wantedBy requiredBy enable;
383 text = commonUnitText def +
384 ''
385 [Automount]
386 ${attrsToSection def.automountConfig}
387 '';
388 };
389
390 sliceToUnit = name: def:
391 { inherit (def) aliases wantedBy requiredBy enable;
392 text = commonUnitText def +
393 ''
394 [Slice]
395 ${attrsToSection def.sliceConfig}
396 '';
397 };
398
399 logindHandlerType = types.enum [
400 "ignore" "poweroff" "reboot" "halt" "kexec" "suspend"
401 "hibernate" "hybrid-sleep" "lock"
402 ];
403
404in
405
406{
407
408 ###### interface
409
410 options = {
411
412 systemd.package = mkOption {
413 default = pkgs.systemd;
414 defaultText = "pkgs.systemd";
415 type = types.package;
416 description = "The systemd package.";
417 };
418
419 systemd.units = mkOption {
420 description = "Definition of systemd units.";
421 default = {};
422 type = with types; attrsOf (submodule (
423 { name, config, ... }:
424 { options = concreteUnitOptions;
425 config = {
426 unit = mkDefault (makeUnit name config);
427 };
428 }));
429 };
430
431 systemd.packages = mkOption {
432 default = [];
433 type = types.listOf types.package;
434 description = "Packages providing systemd units.";
435 };
436
437 systemd.targets = mkOption {
438 default = {};
439 type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
440 description = "Definition of systemd target units.";
441 };
442
443 systemd.services = mkOption {
444 default = {};
445 type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ]);
446 description = "Definition of systemd service units.";
447 };
448
449 systemd.sockets = mkOption {
450 default = {};
451 type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ]);
452 description = "Definition of systemd socket units.";
453 };
454
455 systemd.timers = mkOption {
456 default = {};
457 type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ]);
458 description = "Definition of systemd timer units.";
459 };
460
461 systemd.paths = mkOption {
462 default = {};
463 type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
464 description = "Definition of systemd path units.";
465 };
466
467 systemd.mounts = mkOption {
468 default = [];
469 type = with types; listOf (submodule [ { options = mountOptions; } unitConfig mountConfig ]);
470 description = ''
471 Definition of systemd mount units.
472 This is a list instead of an attrSet, because systemd mandates the names to be derived from
473 the 'where' attribute.
474 '';
475 };
476
477 systemd.automounts = mkOption {
478 default = [];
479 type = with types; listOf (submodule [ { options = automountOptions; } unitConfig automountConfig ]);
480 description = ''
481 Definition of systemd automount units.
482 This is a list instead of an attrSet, because systemd mandates the names to be derived from
483 the 'where' attribute.
484 '';
485 };
486
487 systemd.slices = mkOption {
488 default = {};
489 type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig] );
490 description = "Definition of slice configurations.";
491 };
492
493 systemd.generators = mkOption {
494 type = types.attrsOf types.path;
495 default = {};
496 example = { "systemd-gpt-auto-generator" = "/dev/null"; };
497 description = ''
498 Definition of systemd generators.
499 For each <literal>NAME = VALUE</literal> pair of the attrSet, a link is generated from
500 <literal>/etc/systemd/system-generators/NAME</literal> to <literal>VALUE</literal>.
501 '';
502 };
503
504 systemd.generator-packages = mkOption {
505 default = [];
506 type = types.listOf types.package;
507 example = literalExample "[ pkgs.systemd-cryptsetup-generator ]";
508 description = "Packages providing systemd generators.";
509 };
510
511 systemd.defaultUnit = mkOption {
512 default = "multi-user.target";
513 type = types.str;
514 description = "Default unit started when the system boots.";
515 };
516
517 systemd.ctrlAltDelUnit = mkOption {
518 default = "reboot.target";
519 type = types.str;
520 example = "poweroff.target";
521 description = ''
522 Target that should be started when Ctrl-Alt-Delete is pressed.
523 '';
524 };
525
526 systemd.globalEnvironment = mkOption {
527 type = types.attrs;
528 default = {};
529 example = { TZ = "CET"; };
530 description = ''
531 Environment variables passed to <emphasis>all</emphasis> systemd units.
532 '';
533 };
534
535 systemd.extraConfig = mkOption {
536 default = "";
537 type = types.lines;
538 example = "DefaultLimitCORE=infinity";
539 description = ''
540 Extra config options for systemd. See man systemd-system.conf for
541 available options.
542 '';
543 };
544
545 services.journald.console = mkOption {
546 default = "";
547 type = types.str;
548 description = "If non-empty, write log messages to the specified TTY device.";
549 };
550
551 services.journald.rateLimitInterval = mkOption {
552 default = "10s";
553 type = types.str;
554 description = ''
555 Configures the rate limiting interval that is applied to all
556 messages generated on the system. This rate limiting is applied
557 per-service, so that two services which log do not interfere with
558 each other's limit. The value may be specified in the following
559 units: s, min, h, ms, us. To turn off any kind of rate limiting,
560 set either value to 0.
561 '';
562 };
563
564 services.journald.rateLimitBurst = mkOption {
565 default = 100;
566 type = types.int;
567 description = ''
568 Configures the rate limiting burst limit (number of messages per
569 interval) that is applied to all messages generated on the system.
570 This rate limiting is applied per-service, so that two services
571 which log do not interfere with each other's limit.
572 '';
573 };
574
575 services.journald.extraConfig = mkOption {
576 default = "";
577 type = types.lines;
578 example = "Storage=volatile";
579 description = ''
580 Extra config options for systemd-journald. See man journald.conf
581 for available options.
582 '';
583 };
584
585 services.journald.enableHttpGateway = mkOption {
586 default = false;
587 type = types.bool;
588 description = ''
589 Whether to enable the HTTP gateway to the journal.
590 '';
591 };
592
593 services.logind.extraConfig = mkOption {
594 default = "";
595 type = types.lines;
596 example = "IdleAction=lock";
597 description = ''
598 Extra config options for systemd-logind. See man logind.conf for
599 available options.
600 '';
601 };
602
603 services.logind.lidSwitch = mkOption {
604 default = "suspend";
605 example = "ignore";
606 type = logindHandlerType;
607
608 description = ''
609 Specifies what to be done when the laptop lid is closed.
610 '';
611 };
612
613 services.logind.lidSwitchDocked = mkOption {
614 default = "ignore";
615 example = "suspend";
616 type = logindHandlerType;
617
618 description = ''
619 Specifies what to be done when the laptop lid is closed
620 and another screen is added.
621 '';
622 };
623
624 systemd.user.extraConfig = mkOption {
625 default = "";
626 type = types.lines;
627 example = "DefaultCPUAccounting=yes";
628 description = ''
629 Extra config options for systemd user instances. See man systemd-user.conf for
630 available options.
631 '';
632 };
633
634 systemd.tmpfiles.rules = mkOption {
635 type = types.listOf types.str;
636 default = [];
637 example = [ "d /tmp 1777 root root 10d" ];
638 description = ''
639 Rules for creating and cleaning up temporary files
640 automatically. See
641 <citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
642 for the exact format. You should not use this option to create
643 files required by systemd services, since there is no
644 guarantee that <command>systemd-tmpfiles</command> runs when
645 the system is reconfigured using
646 <command>nixos-rebuild</command>.
647 '';
648 };
649
650 systemd.user.units = mkOption {
651 description = "Definition of systemd per-user units.";
652 default = {};
653 type = with types; attrsOf (submodule (
654 { name, config, ... }:
655 { options = concreteUnitOptions;
656 config = {
657 unit = mkDefault (makeUnit name config);
658 };
659 }));
660 };
661
662 systemd.user.paths = mkOption {
663 default = {};
664 type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
665 description = "Definition of systemd per-user path units.";
666 };
667
668 systemd.user.services = mkOption {
669 default = {};
670 type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ] );
671 description = "Definition of systemd per-user service units.";
672 };
673
674 systemd.user.slices = mkOption {
675 default = {};
676 type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig ] );
677 description = "Definition of systemd per-user slice units.";
678 };
679
680 systemd.user.sockets = mkOption {
681 default = {};
682 type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ] );
683 description = "Definition of systemd per-user socket units.";
684 };
685
686 systemd.user.targets = mkOption {
687 default = {};
688 type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
689 description = "Definition of systemd per-user target units.";
690 };
691
692 systemd.user.timers = mkOption {
693 default = {};
694 type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ] );
695 description = "Definition of systemd per-user timer units.";
696 };
697
698 systemd.additionalUpstreamSystemUnits = mkOption {
699 default = [ ];
700 type = types.listOf types.str;
701 example = [ "debug-shell.service" "systemd-quotacheck.service" ];
702 description = ''
703 Additional units shipped with systemd that shall be enabled.
704 '';
705 };
706
707 };
708
709
710 ###### implementation
711
712 config = {
713
714 warnings = concatLists (mapAttrsToList (name: service:
715 optional (service.serviceConfig.Type or "" == "oneshot" && service.serviceConfig.Restart or "no" != "no")
716 "Service ‘${name}.service’ with ‘Type=oneshot’ must have ‘Restart=no’") cfg.services);
717
718 system.build.units = cfg.units;
719
720 environment.systemPackages = [ systemd ];
721
722 environment.etc = let
723 # generate contents for /etc/systemd/system-generators from
724 # systemd.generators and systemd.generator-packages
725 generators = pkgs.runCommand "system-generators" { packages = cfg.generator-packages; } ''
726 mkdir -p $out
727 for package in $packages
728 do
729 ln -s $package/lib/systemd/system-generators/* $out/
730 done;
731 ${concatStrings (mapAttrsToList (generator: target: "ln -s ${target} $out/${generator};\n") cfg.generators)}
732 '';
733 in ({
734 "systemd/system".source = generateUnits "system" cfg.units upstreamSystemUnits upstreamSystemWants;
735
736 "systemd/user".source = generateUnits "user" cfg.user.units upstreamUserUnits [];
737
738 "systemd/system.conf".text = ''
739 [Manager]
740 ${config.systemd.extraConfig}
741 '';
742
743 "systemd/user.conf".text = ''
744 [Manager]
745 ${config.systemd.user.extraConfig}
746 '';
747
748 "systemd/journald.conf".text = ''
749 [Journal]
750 RateLimitInterval=${config.services.journald.rateLimitInterval}
751 RateLimitBurst=${toString config.services.journald.rateLimitBurst}
752 ${optionalString (config.services.journald.console != "") ''
753 ForwardToConsole=yes
754 TTYPath=${config.services.journald.console}
755 ''}
756 ${config.services.journald.extraConfig}
757 '';
758
759 "systemd/logind.conf".text = ''
760 [Login]
761 KillUserProcesses=no
762 HandleLidSwitch=${config.services.logind.lidSwitch}
763 HandleLidSwitchDocked=${config.services.logind.lidSwitchDocked}
764 ${config.services.logind.extraConfig}
765 '';
766
767 "systemd/sleep.conf".text = ''
768 [Sleep]
769 '';
770
771 "tmpfiles.d/systemd.conf".source = "${systemd}/example/tmpfiles.d/systemd.conf";
772 "tmpfiles.d/x11.conf".source = "${systemd}/example/tmpfiles.d/x11.conf";
773
774 "tmpfiles.d/nixos.conf".text = ''
775 # This file is created automatically and should not be modified.
776 # Please change the option ‘systemd.tmpfiles.rules’ instead.
777
778 ${concatStringsSep "\n" cfg.tmpfiles.rules}
779 '';
780
781 "systemd/system-generators" = { source = generators; };
782 });
783
784 services.dbus.enable = true;
785
786 system.activationScripts.systemd = stringAfter [ "groups" ]
787 ''
788 mkdir -m 0755 -p /var/lib/udev
789
790 if ! [ -e /etc/machine-id ]; then
791 ${systemd}/bin/systemd-machine-id-setup
792 fi
793
794 # Keep a persistent journal. Note that systemd-tmpfiles will
795 # set proper ownership/permissions.
796 # FIXME: revert to 0700 with systemd v233.
797 mkdir -m 0750 -p /var/log/journal
798 '';
799
800 users.extraUsers.systemd-network.uid = config.ids.uids.systemd-network;
801 users.extraGroups.systemd-network.gid = config.ids.gids.systemd-network;
802 users.extraUsers.systemd-resolve.uid = config.ids.uids.systemd-resolve;
803 users.extraGroups.systemd-resolve.gid = config.ids.gids.systemd-resolve;
804
805 # Target for ‘charon send-keys’ to hook into.
806 users.extraGroups.keys.gid = config.ids.gids.keys;
807
808 systemd.targets.keys =
809 { description = "Security Keys";
810 unitConfig.X-StopOnReconfiguration = true;
811 };
812
813 systemd.units =
814 mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths
815 // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
816 // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.slices
817 // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets
818 // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets
819 // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers
820 // listToAttrs (map
821 (v: let n = escapeSystemdPath v.where;
822 in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts)
823 // listToAttrs (map
824 (v: let n = escapeSystemdPath v.where;
825 in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
826
827 systemd.user.units =
828 mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.user.paths
829 // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services
830 // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.user.slices
831 // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.user.sockets
832 // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.user.targets
833 // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.user.timers;
834
835 system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled
836 [ "DEVTMPFS" "CGROUPS" "INOTIFY_USER" "SIGNALFD" "TIMERFD" "EPOLL" "NET"
837 "SYSFS" "PROC_FS" "FHANDLE" "DMIID" "AUTOFS4_FS" "TMPFS_POSIX_ACL"
838 "TMPFS_XATTR" "SECCOMP"
839 ];
840
841 users.extraGroups.systemd-journal.gid = config.ids.gids.systemd-journal;
842 users.extraUsers.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway;
843 users.extraGroups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway;
844
845 # Generate timer units for all services that have a ‘startAt’ value.
846 systemd.timers =
847 mapAttrs (name: service:
848 { wantedBy = [ "timers.target" ];
849 timerConfig.OnCalendar = service.startAt;
850 })
851 (filterAttrs (name: service: service.enable && service.startAt != []) cfg.services);
852
853 # Generate timer units for all services that have a ‘startAt’ value.
854 systemd.user.timers =
855 mapAttrs (name: service:
856 { wantedBy = [ "timers.target" ];
857 timerConfig.OnCalendar = service.startAt;
858 })
859 (filterAttrs (name: service: service.startAt != []) cfg.user.services);
860
861 systemd.sockets.systemd-journal-gatewayd.wantedBy =
862 optional config.services.journald.enableHttpGateway "sockets.target";
863
864 # Provide the systemd-user PAM service, required to run systemd
865 # user instances.
866 security.pam.services.systemd-user =
867 { # Ensure that pam_systemd gets included. This is special-cased
868 # in systemd to provide XDG_RUNTIME_DIR.
869 startSession = true;
870 };
871
872 # Some overrides to upstream units.
873 systemd.services."systemd-backlight@".restartIfChanged = false;
874 systemd.services."systemd-fsck@".restartIfChanged = false;
875 systemd.services."systemd-fsck@".path = [ config.system.path ];
876 systemd.services."user@".restartIfChanged = false;
877 systemd.services.systemd-journal-flush.restartIfChanged = false;
878 systemd.services.systemd-random-seed.restartIfChanged = false;
879 systemd.services.systemd-remount-fs.restartIfChanged = false;
880 systemd.services.systemd-update-utmp.restartIfChanged = false;
881 systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions.
882 systemd.services.systemd-logind.restartTriggers = [ config.environment.etc."systemd/logind.conf".source ];
883 systemd.services.systemd-logind.stopIfChanged = false;
884 systemd.services.systemd-journald.restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
885 systemd.services.systemd-journald.stopIfChanged = false;
886 systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true;
887 systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true;
888 systemd.targets.network-online.wantedBy = [ "multi-user.target" ];
889 systemd.services.systemd-binfmt.wants = [ "proc-sys-fs-binfmt_misc.automount" ];
890
891 # Don't bother with certain units in containers.
892 systemd.services.systemd-remount-fs.unitConfig.ConditionVirtualization = "!container";
893 systemd.services.systemd-random-seed.unitConfig.ConditionVirtualization = "!container";
894
895 };
896
897 # FIXME: Remove these eventually.
898 imports =
899 [ (mkRenamedOptionModule [ "boot" "systemd" "sockets" ] [ "systemd" "sockets" ])
900 (mkRenamedOptionModule [ "boot" "systemd" "targets" ] [ "systemd" "targets" ])
901 (mkRenamedOptionModule [ "boot" "systemd" "services" ] [ "systemd" "services" ])
902 ];
903
904}