at 23.11-pre 22 kB view raw
1{ config, lib, pkgs, utils, ... }: 2 3with utils; 4with systemdUtils.unitOptions; 5with lib; 6 7let 8 9 cfg = config.systemd; 10 11 inherit (systemdUtils.lib) 12 generateUnits 13 targetToUnit 14 serviceToUnit 15 socketToUnit 16 timerToUnit 17 pathToUnit 18 mountToUnit 19 automountToUnit 20 sliceToUnit; 21 22 upstreamSystemUnits = 23 [ # Targets. 24 "basic.target" 25 "sysinit.target" 26 "sockets.target" 27 "exit.target" 28 "graphical.target" 29 "multi-user.target" 30 "network.target" 31 "network-pre.target" 32 "network-online.target" 33 "nss-lookup.target" 34 "nss-user-lookup.target" 35 "time-sync.target" 36 ] ++ optionals cfg.package.withCryptsetup [ 37 "cryptsetup.target" 38 "cryptsetup-pre.target" 39 "remote-cryptsetup.target" 40 ] ++ [ 41 "sigpwr.target" 42 "timers.target" 43 "paths.target" 44 "rpcbind.target" 45 46 # Rescue mode. 47 "rescue.target" 48 "rescue.service" 49 50 # Udev. 51 "systemd-udevd-control.socket" 52 "systemd-udevd-kernel.socket" 53 "systemd-udevd.service" 54 "systemd-udev-settle.service" 55 ] ++ (optional (!config.boot.isContainer) "systemd-udev-trigger.service") ++ [ 56 # hwdb.bin is managed by NixOS 57 # "systemd-hwdb-update.service" 58 59 # Consoles. 60 "getty.target" 61 "getty-pre.target" 62 "getty@.service" 63 "serial-getty@.service" 64 "console-getty.service" 65 "container-getty@.service" 66 "systemd-vconsole-setup.service" 67 68 # Hardware (started by udev when a relevant device is plugged in). 69 "sound.target" 70 "bluetooth.target" 71 "printer.target" 72 "smartcard.target" 73 74 # Kernel module loading. 75 "systemd-modules-load.service" 76 "kmod-static-nodes.service" 77 "modprobe@.service" 78 79 # Filesystems. 80 "systemd-fsck@.service" 81 "systemd-fsck-root.service" 82 "systemd-growfs@.service" 83 "systemd-growfs-root.service" 84 "systemd-remount-fs.service" 85 "systemd-pstore.service" 86 "local-fs.target" 87 "local-fs-pre.target" 88 "remote-fs.target" 89 "remote-fs-pre.target" 90 "swap.target" 91 "dev-hugepages.mount" 92 "dev-mqueue.mount" 93 "sys-fs-fuse-connections.mount" 94 ] ++ (optional (!config.boot.isContainer) "sys-kernel-config.mount") ++ [ 95 "sys-kernel-debug.mount" 96 97 # Maintaining state across reboots. 98 "systemd-random-seed.service" 99 "systemd-backlight@.service" 100 "systemd-rfkill.service" 101 "systemd-rfkill.socket" 102 103 # Hibernate / suspend. 104 "hibernate.target" 105 "suspend.target" 106 "suspend-then-hibernate.target" 107 "sleep.target" 108 "hybrid-sleep.target" 109 "systemd-hibernate.service" 110 "systemd-hybrid-sleep.service" 111 "systemd-suspend.service" 112 "systemd-suspend-then-hibernate.service" 113 114 # Reboot stuff. 115 "reboot.target" 116 "systemd-reboot.service" 117 "poweroff.target" 118 "systemd-poweroff.service" 119 "halt.target" 120 "systemd-halt.service" 121 "shutdown.target" 122 "umount.target" 123 "final.target" 124 "kexec.target" 125 "systemd-kexec.service" 126 ] ++ lib.optional cfg.package.withUtmp "systemd-update-utmp.service" ++ [ 127 128 # Password entry. 129 "systemd-ask-password-console.path" 130 "systemd-ask-password-console.service" 131 "systemd-ask-password-wall.path" 132 "systemd-ask-password-wall.service" 133 134 # Slices / containers. 135 "slices.target" 136 ] ++ optionals cfg.package.withImportd [ 137 "systemd-importd.service" 138 ] ++ optionals cfg.package.withMachined [ 139 "machine.slice" 140 "machines.target" 141 "systemd-machined.service" 142 ] ++ [ 143 "systemd-nspawn@.service" 144 145 # Misc. 146 "systemd-sysctl.service" 147 ] ++ optionals cfg.package.withTimedated [ 148 "dbus-org.freedesktop.timedate1.service" 149 "systemd-timedated.service" 150 ] ++ optionals cfg.package.withLocaled [ 151 "dbus-org.freedesktop.locale1.service" 152 "systemd-localed.service" 153 ] ++ optionals cfg.package.withHostnamed [ 154 "dbus-org.freedesktop.hostname1.service" 155 "systemd-hostnamed.service" 156 ] ++ optionals cfg.package.withPortabled [ 157 "dbus-org.freedesktop.portable1.service" 158 "systemd-portabled.service" 159 ] ++ [ 160 "systemd-exit.service" 161 "systemd-update-done.service" 162 ] ++ cfg.additionalUpstreamSystemUnits; 163 164 upstreamSystemWants = 165 [ "sysinit.target.wants" 166 "sockets.target.wants" 167 "local-fs.target.wants" 168 "multi-user.target.wants" 169 "timers.target.wants" 170 ]; 171 172 proxy_env = config.networking.proxy.envVars; 173 174in 175 176{ 177 ###### interface 178 179 options = { 180 181 systemd.package = mkOption { 182 default = pkgs.systemd; 183 defaultText = literalExpression "pkgs.systemd"; 184 type = types.package; 185 description = lib.mdDoc "The systemd package."; 186 }; 187 188 systemd.units = mkOption { 189 description = lib.mdDoc "Definition of systemd units."; 190 default = {}; 191 type = systemdUtils.types.units; 192 }; 193 194 systemd.packages = mkOption { 195 default = []; 196 type = types.listOf types.package; 197 example = literalExpression "[ pkgs.systemd-cryptsetup-generator ]"; 198 description = lib.mdDoc "Packages providing systemd units and hooks."; 199 }; 200 201 systemd.targets = mkOption { 202 default = {}; 203 type = systemdUtils.types.targets; 204 description = lib.mdDoc "Definition of systemd target units."; 205 }; 206 207 systemd.services = mkOption { 208 default = {}; 209 type = systemdUtils.types.services; 210 description = lib.mdDoc "Definition of systemd service units."; 211 }; 212 213 systemd.sockets = mkOption { 214 default = {}; 215 type = systemdUtils.types.sockets; 216 description = lib.mdDoc "Definition of systemd socket units."; 217 }; 218 219 systemd.timers = mkOption { 220 default = {}; 221 type = systemdUtils.types.timers; 222 description = lib.mdDoc "Definition of systemd timer units."; 223 }; 224 225 systemd.paths = mkOption { 226 default = {}; 227 type = systemdUtils.types.paths; 228 description = lib.mdDoc "Definition of systemd path units."; 229 }; 230 231 systemd.mounts = mkOption { 232 default = []; 233 type = systemdUtils.types.mounts; 234 description = lib.mdDoc '' 235 Definition of systemd mount units. 236 This is a list instead of an attrSet, because systemd mandates the names to be derived from 237 the 'where' attribute. 238 ''; 239 }; 240 241 systemd.automounts = mkOption { 242 default = []; 243 type = systemdUtils.types.automounts; 244 description = lib.mdDoc '' 245 Definition of systemd automount units. 246 This is a list instead of an attrSet, because systemd mandates the names to be derived from 247 the 'where' attribute. 248 ''; 249 }; 250 251 systemd.slices = mkOption { 252 default = {}; 253 type = systemdUtils.types.slices; 254 description = lib.mdDoc "Definition of slice configurations."; 255 }; 256 257 systemd.generators = mkOption { 258 type = types.attrsOf types.path; 259 default = {}; 260 example = { systemd-gpt-auto-generator = "/dev/null"; }; 261 description = lib.mdDoc '' 262 Definition of systemd generators. 263 For each `NAME = VALUE` pair of the attrSet, a link is generated from 264 `/etc/systemd/system-generators/NAME` to `VALUE`. 265 ''; 266 }; 267 268 systemd.shutdown = mkOption { 269 type = types.attrsOf types.path; 270 default = {}; 271 description = lib.mdDoc '' 272 Definition of systemd shutdown executables. 273 For each `NAME = VALUE` pair of the attrSet, a link is generated from 274 `/etc/systemd/system-shutdown/NAME` to `VALUE`. 275 ''; 276 }; 277 278 systemd.defaultUnit = mkOption { 279 default = "multi-user.target"; 280 type = types.str; 281 description = lib.mdDoc "Default unit started when the system boots."; 282 }; 283 284 systemd.ctrlAltDelUnit = mkOption { 285 default = "reboot.target"; 286 type = types.str; 287 example = "poweroff.target"; 288 description = lib.mdDoc '' 289 Target that should be started when Ctrl-Alt-Delete is pressed. 290 ''; 291 }; 292 293 systemd.globalEnvironment = mkOption { 294 type = with types; attrsOf (nullOr (oneOf [ str path package ])); 295 default = {}; 296 example = { TZ = "CET"; }; 297 description = lib.mdDoc '' 298 Environment variables passed to *all* systemd units. 299 ''; 300 }; 301 302 systemd.managerEnvironment = mkOption { 303 type = with types; attrsOf (nullOr (oneOf [ str path package ])); 304 default = {}; 305 example = { SYSTEMD_LOG_LEVEL = "debug"; }; 306 description = lib.mdDoc '' 307 Environment variables of PID 1. These variables are 308 *not* passed to started units. 309 ''; 310 }; 311 312 systemd.enableCgroupAccounting = mkOption { 313 default = true; 314 type = types.bool; 315 description = lib.mdDoc '' 316 Whether to enable cgroup accounting. 317 ''; 318 }; 319 320 systemd.enableUnifiedCgroupHierarchy = mkOption { 321 default = true; 322 type = types.bool; 323 description = lib.mdDoc '' 324 Whether to enable the unified cgroup hierarchy (cgroupsv2). 325 ''; 326 }; 327 328 systemd.extraConfig = mkOption { 329 default = ""; 330 type = types.lines; 331 example = "DefaultLimitCORE=infinity"; 332 description = lib.mdDoc '' 333 Extra config options for systemd. See systemd-system.conf(5) man page 334 for available options. 335 ''; 336 }; 337 338 systemd.sleep.extraConfig = mkOption { 339 default = ""; 340 type = types.lines; 341 example = "HibernateDelaySec=1h"; 342 description = lib.mdDoc '' 343 Extra config options for systemd sleep state logic. 344 See sleep.conf.d(5) man page for available options. 345 ''; 346 }; 347 348 systemd.additionalUpstreamSystemUnits = mkOption { 349 default = [ ]; 350 type = types.listOf types.str; 351 example = [ "debug-shell.service" "systemd-quotacheck.service" ]; 352 description = lib.mdDoc '' 353 Additional units shipped with systemd that shall be enabled. 354 ''; 355 }; 356 357 systemd.suppressedSystemUnits = mkOption { 358 default = [ ]; 359 type = types.listOf types.str; 360 example = [ "systemd-backlight@.service" ]; 361 description = lib.mdDoc '' 362 A list of units to skip when generating system systemd configuration directory. This has 363 priority over upstream units, {option}`systemd.units`, and 364 {option}`systemd.additionalUpstreamSystemUnits`. The main purpose of this is to 365 prevent a upstream systemd unit from being added to the initrd with any modifications made to it 366 by other NixOS modules. 367 ''; 368 }; 369 370 systemd.watchdog.device = mkOption { 371 type = types.nullOr types.path; 372 default = null; 373 example = "/dev/watchdog"; 374 description = lib.mdDoc '' 375 The path to a hardware watchdog device which will be managed by systemd. 376 If not specified, systemd will default to /dev/watchdog. 377 ''; 378 }; 379 380 systemd.watchdog.runtimeTime = mkOption { 381 type = types.nullOr types.str; 382 default = null; 383 example = "30s"; 384 description = lib.mdDoc '' 385 The amount of time which can elapse before a watchdog hardware device 386 will automatically reboot the system. Valid time units include "ms", 387 "s", "min", "h", "d", and "w". 388 ''; 389 }; 390 391 systemd.watchdog.rebootTime = mkOption { 392 type = types.nullOr types.str; 393 default = null; 394 example = "10m"; 395 description = lib.mdDoc '' 396 The amount of time which can elapse after a reboot has been triggered 397 before a watchdog hardware device will automatically reboot the system. 398 Valid time units include "ms", "s", "min", "h", "d", and "w". 399 ''; 400 }; 401 402 systemd.watchdog.kexecTime = mkOption { 403 type = types.nullOr types.str; 404 default = null; 405 example = "10m"; 406 description = lib.mdDoc '' 407 The amount of time which can elapse when kexec is being executed before 408 a watchdog hardware device will automatically reboot the system. This 409 option should only be enabled if reloadTime is also enabled. Valid 410 time units include "ms", "s", "min", "h", "d", and "w". 411 ''; 412 }; 413 }; 414 415 416 ###### implementation 417 418 config = { 419 420 warnings = concatLists ( 421 mapAttrsToList 422 (name: service: 423 let 424 type = service.serviceConfig.Type or ""; 425 restart = service.serviceConfig.Restart or "no"; 426 hasDeprecated = builtins.hasAttr "StartLimitInterval" service.serviceConfig; 427 in 428 concatLists [ 429 (optional (type == "oneshot" && (restart == "always" || restart == "on-success")) 430 "Service '${name}.service' with 'Type=oneshot' cannot have 'Restart=always' or 'Restart=on-success'" 431 ) 432 (optional hasDeprecated 433 "Service '${name}.service' uses the attribute 'StartLimitInterval' in the Service section, which is deprecated. See https://github.com/NixOS/nixpkgs/issues/45786." 434 ) 435 (optional (service.reloadIfChanged && service.reloadTriggers != []) 436 "Service '${name}.service' has both 'reloadIfChanged' and 'reloadTriggers' set. This is probably not what you want, because 'reloadTriggers' behave the same whay as 'restartTriggers' if 'reloadIfChanged' is set." 437 ) 438 ] 439 ) 440 cfg.services 441 ); 442 443 system.build.units = cfg.units; 444 445 system.nssModules = [ cfg.package.out ]; 446 system.nssDatabases = { 447 hosts = (mkMerge [ 448 (mkOrder 400 ["mymachines"]) # 400 to ensure it comes before resolve (which is mkBefore'd) 449 (mkOrder 999 ["myhostname"]) # after files (which is 998), but before regular nss modules 450 ]); 451 passwd = (mkMerge [ 452 (mkAfter [ "systemd" ]) 453 ]); 454 group = (mkMerge [ 455 (mkAfter [ "[success=merge] systemd" ]) # need merge so that NSS won't stop at file-based groups 456 ]); 457 }; 458 459 environment.systemPackages = [ cfg.package ]; 460 461 environment.etc = let 462 # generate contents for /etc/systemd/system-${type} from attrset of links and packages 463 hooks = type: links: pkgs.runCommand "system-${type}" { 464 preferLocalBuild = true; 465 packages = cfg.packages; 466 } '' 467 set -e 468 mkdir -p $out 469 for package in $packages 470 do 471 for hook in $package/lib/systemd/system-${type}/* 472 do 473 ln -s $hook $out/ 474 done 475 done 476 ${concatStrings (mapAttrsToList (exec: target: "ln -s ${target} $out/${exec};\n") links)} 477 ''; 478 479 enabledUpstreamSystemUnits = filter (n: ! elem n cfg.suppressedSystemUnits) upstreamSystemUnits; 480 enabledUnits = filterAttrs (n: v: ! elem n cfg.suppressedSystemUnits) cfg.units; 481 482 in ({ 483 "systemd/system".source = generateUnits { 484 type = "system"; 485 units = enabledUnits; 486 upstreamUnits = enabledUpstreamSystemUnits; 487 upstreamWants = upstreamSystemWants; 488 }; 489 490 "systemd/system.conf".text = '' 491 [Manager] 492 ManagerEnvironment=${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "${n}=${lib.escapeShellArg v}") cfg.managerEnvironment)} 493 ${optionalString config.systemd.enableCgroupAccounting '' 494 DefaultCPUAccounting=yes 495 DefaultIOAccounting=yes 496 DefaultBlockIOAccounting=yes 497 DefaultIPAccounting=yes 498 ''} 499 DefaultLimitCORE=infinity 500 ${optionalString (config.systemd.watchdog.device != null) '' 501 WatchdogDevice=${config.systemd.watchdog.device} 502 ''} 503 ${optionalString (config.systemd.watchdog.runtimeTime != null) '' 504 RuntimeWatchdogSec=${config.systemd.watchdog.runtimeTime} 505 ''} 506 ${optionalString (config.systemd.watchdog.rebootTime != null) '' 507 RebootWatchdogSec=${config.systemd.watchdog.rebootTime} 508 ''} 509 ${optionalString (config.systemd.watchdog.kexecTime != null) '' 510 KExecWatchdogSec=${config.systemd.watchdog.kexecTime} 511 ''} 512 513 ${config.systemd.extraConfig} 514 ''; 515 516 "systemd/sleep.conf".text = '' 517 [Sleep] 518 ${config.systemd.sleep.extraConfig} 519 ''; 520 521 "systemd/system-generators" = { source = hooks "generators" cfg.generators; }; 522 "systemd/system-shutdown" = { source = hooks "shutdown" cfg.shutdown; }; 523 }); 524 525 services.dbus.enable = true; 526 527 users.users.systemd-network = { 528 uid = config.ids.uids.systemd-network; 529 group = "systemd-network"; 530 }; 531 users.groups.systemd-network.gid = config.ids.gids.systemd-network; 532 users.users.systemd-resolve = { 533 uid = config.ids.uids.systemd-resolve; 534 group = "systemd-resolve"; 535 }; 536 users.groups.systemd-resolve.gid = config.ids.gids.systemd-resolve; 537 538 # Target for ‘charon send-keys’ to hook into. 539 users.groups.keys.gid = config.ids.gids.keys; 540 541 systemd.targets.keys = 542 { description = "Security Keys"; 543 unitConfig.X-StopOnReconfiguration = true; 544 }; 545 546 systemd.units = 547 mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths 548 // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services 549 // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.slices 550 // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets 551 // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets 552 // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers 553 // listToAttrs (map 554 (v: let n = escapeSystemdPath v.where; 555 in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts) 556 // listToAttrs (map 557 (v: let n = escapeSystemdPath v.where; 558 in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts); 559 560 # Environment of PID 1 561 systemd.managerEnvironment = { 562 # Doesn't contain systemd itself - everything works so it seems to use the compiled-in value for its tools 563 # util-linux is needed for the main fsck utility wrapping the fs-specific ones 564 PATH = lib.makeBinPath (config.system.fsPackages ++ [cfg.package.util-linux]); 565 LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive"; 566 TZDIR = "/etc/zoneinfo"; 567 # If SYSTEMD_UNIT_PATH ends with an empty component (":"), the usual unit load path will be appended to the contents of the variable 568 SYSTEMD_UNIT_PATH = lib.mkIf (config.boot.extraSystemdUnitPaths != []) "${builtins.concatStringsSep ":" config.boot.extraSystemdUnitPaths}:"; 569 }; 570 571 572 system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled 573 [ "DEVTMPFS" "CGROUPS" "INOTIFY_USER" "SIGNALFD" "TIMERFD" "EPOLL" "NET" 574 "SYSFS" "PROC_FS" "FHANDLE" "CRYPTO_USER_API_HASH" "CRYPTO_HMAC" 575 "CRYPTO_SHA256" "DMIID" "AUTOFS4_FS" "TMPFS_POSIX_ACL" 576 "TMPFS_XATTR" "SECCOMP" 577 ]; 578 579 # Generate timer units for all services that have a ‘startAt’ value. 580 systemd.timers = 581 mapAttrs (name: service: 582 { wantedBy = [ "timers.target" ]; 583 timerConfig.OnCalendar = service.startAt; 584 }) 585 (filterAttrs (name: service: service.enable && service.startAt != []) cfg.services); 586 587 # Some overrides to upstream units. 588 systemd.services."systemd-backlight@".restartIfChanged = false; 589 systemd.services."systemd-fsck@".restartIfChanged = false; 590 systemd.services."systemd-fsck@".path = [ config.system.path ]; 591 systemd.services.systemd-random-seed.restartIfChanged = false; 592 systemd.services.systemd-remount-fs.restartIfChanged = false; 593 systemd.services.systemd-update-utmp.restartIfChanged = false; 594 systemd.services.systemd-udev-settle.restartIfChanged = false; # Causes long delays in nixos-rebuild 595 systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true; 596 systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true; 597 systemd.targets.network-online.wantedBy = [ "multi-user.target" ]; 598 systemd.services.systemd-importd.environment = proxy_env; 599 systemd.services.systemd-pstore.wantedBy = [ "sysinit.target" ]; # see #81138 600 601 # NixOS has kernel modules in a different location, so override that here. 602 systemd.services.kmod-static-nodes.unitConfig.ConditionFileNotEmpty = [ 603 "" # required to unset the previous value! 604 "/run/booted-system/kernel-modules/lib/modules/%v/modules.devname" 605 ]; 606 607 # Don't bother with certain units in containers. 608 systemd.services.systemd-remount-fs.unitConfig.ConditionVirtualization = "!container"; 609 systemd.services.systemd-random-seed.unitConfig.ConditionVirtualization = "!container"; 610 611 # Increase numeric PID range (set directly instead of copying a one-line file from systemd) 612 # https://github.com/systemd/systemd/pull/12226 613 boot.kernel.sysctl."kernel.pid_max" = mkIf pkgs.stdenv.is64bit (lib.mkDefault 4194304); 614 615 boot.kernelParams = optional (!cfg.enableUnifiedCgroupHierarchy) "systemd.unified_cgroup_hierarchy=0"; 616 617 # Avoid potentially degraded system state due to 618 # "Userspace Out-Of-Memory (OOM) Killer was skipped because of a failed condition check (ConditionControlGroupController=v2)." 619 systemd.oomd.enable = mkIf (!cfg.enableUnifiedCgroupHierarchy) false; 620 621 services.logrotate.settings = { 622 "/var/log/btmp" = mapAttrs (_: mkDefault) { 623 frequency = "monthly"; 624 rotate = 1; 625 create = "0660 root ${config.users.groups.utmp.name}"; 626 minsize = "1M"; 627 }; 628 "/var/log/wtmp" = mapAttrs (_: mkDefault) { 629 frequency = "monthly"; 630 rotate = 1; 631 create = "0664 root ${config.users.groups.utmp.name}"; 632 minsize = "1M"; 633 }; 634 }; 635 }; 636 637 # FIXME: Remove these eventually. 638 imports = 639 [ (mkRenamedOptionModule [ "boot" "systemd" "sockets" ] [ "systemd" "sockets" ]) 640 (mkRenamedOptionModule [ "boot" "systemd" "targets" ] [ "systemd" "targets" ]) 641 (mkRenamedOptionModule [ "boot" "systemd" "services" ] [ "systemd" "services" ]) 642 (mkRenamedOptionModule [ "jobs" ] [ "systemd" "services" ]) 643 (mkRemovedOptionModule [ "systemd" "generator-packages" ] "Use systemd.packages instead.") 644 ]; 645}