at 24.11-pre 21 kB view raw
1{ lib, systemdUtils }: 2 3let 4 inherit (systemdUtils.lib) 5 assertValueOneOf 6 automountConfig 7 checkUnitConfig 8 makeJobScript 9 mountConfig 10 serviceConfig 11 unitConfig 12 unitNameType 13 ; 14 15 inherit (lib) 16 any 17 concatMap 18 filterOverrides 19 isList 20 mergeEqualOption 21 mkIf 22 mkMerge 23 mkOption 24 mkOptionType 25 singleton 26 toList 27 types 28 ; 29 30 checkService = checkUnitConfig "Service" [ 31 (assertValueOneOf "Type" [ 32 "exec" "simple" "forking" "oneshot" "dbus" "notify" "notify-reload" "idle" 33 ]) 34 (assertValueOneOf "Restart" [ 35 "no" "on-success" "on-failure" "on-abnormal" "on-abort" "always" 36 ]) 37 ]; 38 39in rec { 40 41 unitOption = mkOptionType { 42 name = "systemd option"; 43 merge = loc: defs: 44 let 45 defs' = filterOverrides defs; 46 in 47 if any (def: isList def.value) defs' 48 then concatMap (def: toList def.value) defs' 49 else mergeEqualOption loc defs'; 50 }; 51 52 sharedOptions = { 53 54 enable = mkOption { 55 default = true; 56 type = types.bool; 57 description = '' 58 If set to false, this unit will be a symlink to 59 /dev/null. This is primarily useful to prevent specific 60 template instances 61 (e.g. `serial-getty@ttyS0`) from being 62 started. Note that `enable=true` does not 63 make a unit start by default at boot; if you want that, see 64 `wantedBy`. 65 ''; 66 }; 67 68 name = lib.mkOption { 69 type = lib.types.str; 70 description = '' 71 The name of this systemd unit, including its extension. 72 This can be used to refer to this unit from other systemd units. 73 ''; 74 }; 75 76 overrideStrategy = mkOption { 77 default = "asDropinIfExists"; 78 type = types.enum [ "asDropinIfExists" "asDropin" ]; 79 description = '' 80 Defines how unit configuration is provided for systemd: 81 82 `asDropinIfExists` creates a unit file when no unit file is provided by the package 83 otherwise a drop-in file name `overrides.conf`. 84 85 `asDropin` creates a drop-in file named `overrides.conf`. 86 Mainly needed to define instances for systemd template units (e.g. `systemd-nspawn@mycontainer.service`). 87 88 See also {manpage}`systemd.unit(5)`. 89 ''; 90 }; 91 92 requiredBy = mkOption { 93 default = []; 94 type = types.listOf unitNameType; 95 description = '' 96 Units that require (i.e. depend on and need to go down with) this unit. 97 As discussed in the `wantedBy` option description this also creates 98 `.requires` symlinks automatically. 99 ''; 100 }; 101 102 upheldBy = mkOption { 103 default = []; 104 type = types.listOf unitNameType; 105 description = '' 106 Keep this unit running as long as the listed units are running. This is a continuously 107 enforced version of wantedBy. 108 ''; 109 }; 110 111 wantedBy = mkOption { 112 default = []; 113 type = types.listOf unitNameType; 114 description = '' 115 Units that want (i.e. depend on) this unit. The default method for 116 starting a unit by default at boot time is to set this option to 117 `["multi-user.target"]` for system services. Likewise for user units 118 (`systemd.user.<name>.*`) set it to `["default.target"]` to make a unit 119 start by default when the user `<name>` logs on. 120 121 This option creates a `.wants` symlink in the given target that exists 122 statelessly without the need for running `systemctl enable`. 123 The `[Install]` section described in {manpage}`systemd.unit(5)` however is 124 not supported because it is a stateful process that does not fit well 125 into the NixOS design. 126 ''; 127 }; 128 129 aliases = mkOption { 130 default = []; 131 type = types.listOf unitNameType; 132 description = "Aliases of that unit."; 133 }; 134 135 }; 136 137 concreteUnitOptions = sharedOptions // { 138 139 text = mkOption { 140 type = types.nullOr types.str; 141 default = null; 142 description = "Text of this systemd unit."; 143 }; 144 145 unit = mkOption { 146 internal = true; 147 description = "The generated unit."; 148 }; 149 150 }; 151 152 commonUnitOptions = { 153 options = sharedOptions // { 154 155 description = mkOption { 156 default = ""; 157 type = types.singleLineStr; 158 description = "Description of this unit used in systemd messages and progress indicators."; 159 }; 160 161 documentation = mkOption { 162 default = []; 163 type = types.listOf types.str; 164 description = "A list of URIs referencing documentation for this unit or its configuration."; 165 }; 166 167 requires = mkOption { 168 default = []; 169 type = types.listOf unitNameType; 170 description = '' 171 Start the specified units when this unit is started, and stop 172 this unit when the specified units are stopped or fail. 173 ''; 174 }; 175 176 wants = mkOption { 177 default = []; 178 type = types.listOf unitNameType; 179 description = '' 180 Start the specified units when this unit is started. 181 ''; 182 }; 183 184 upholds = mkOption { 185 default = []; 186 type = types.listOf unitNameType; 187 description = '' 188 Keeps the specified running while this unit is running. A continuous version of `wants`. 189 ''; 190 }; 191 192 after = mkOption { 193 default = []; 194 type = types.listOf unitNameType; 195 description = '' 196 If the specified units are started at the same time as 197 this unit, delay this unit until they have started. 198 ''; 199 }; 200 201 before = mkOption { 202 default = []; 203 type = types.listOf unitNameType; 204 description = '' 205 If the specified units are started at the same time as 206 this unit, delay them until this unit has started. 207 ''; 208 }; 209 210 bindsTo = mkOption { 211 default = []; 212 type = types.listOf unitNameType; 213 description = '' 214 Like requires, but in addition, if the specified units 215 unexpectedly disappear, this unit will be stopped as well. 216 ''; 217 }; 218 219 partOf = mkOption { 220 default = []; 221 type = types.listOf unitNameType; 222 description = '' 223 If the specified units are stopped or restarted, then this 224 unit is stopped or restarted as well. 225 ''; 226 }; 227 228 conflicts = mkOption { 229 default = []; 230 type = types.listOf unitNameType; 231 description = '' 232 If the specified units are started, then this unit is stopped 233 and vice versa. 234 ''; 235 }; 236 237 requisite = mkOption { 238 default = []; 239 type = types.listOf unitNameType; 240 description = '' 241 Similar to requires. However if the units listed are not started, 242 they will not be started and the transaction will fail. 243 ''; 244 }; 245 246 unitConfig = mkOption { 247 default = {}; 248 example = { RequiresMountsFor = "/data"; }; 249 type = types.attrsOf unitOption; 250 description = '' 251 Each attribute in this set specifies an option in the 252 `[Unit]` section of the unit. See 253 {manpage}`systemd.unit(5)` for details. 254 ''; 255 }; 256 257 onFailure = mkOption { 258 default = []; 259 type = types.listOf unitNameType; 260 description = '' 261 A list of one or more units that are activated when 262 this unit enters the "failed" state. 263 ''; 264 }; 265 266 onSuccess = mkOption { 267 default = []; 268 type = types.listOf unitNameType; 269 description = '' 270 A list of one or more units that are activated when 271 this unit enters the "inactive" state. 272 ''; 273 }; 274 275 startLimitBurst = mkOption { 276 type = types.int; 277 description = '' 278 Configure unit start rate limiting. Units which are started 279 more than startLimitBurst times within an interval time 280 interval are not permitted to start any more. 281 ''; 282 }; 283 284 startLimitIntervalSec = mkOption { 285 type = types.int; 286 description = '' 287 Configure unit start rate limiting. Units which are started 288 more than startLimitBurst times within an interval time 289 interval are not permitted to start any more. 290 ''; 291 }; 292 293 }; 294 }; 295 296 stage2CommonUnitOptions = { 297 imports = [ 298 commonUnitOptions 299 ]; 300 301 options = { 302 restartTriggers = mkOption { 303 default = []; 304 type = types.listOf types.unspecified; 305 description = '' 306 An arbitrary list of items such as derivations. If any item 307 in the list changes between reconfigurations, the service will 308 be restarted. 309 ''; 310 }; 311 312 reloadTriggers = mkOption { 313 default = []; 314 type = types.listOf unitOption; 315 description = '' 316 An arbitrary list of items such as derivations. If any item 317 in the list changes between reconfigurations, the service will 318 be reloaded. If anything but a reload trigger changes in the 319 unit file, the unit will be restarted instead. 320 ''; 321 }; 322 }; 323 }; 324 stage1CommonUnitOptions = commonUnitOptions; 325 326 serviceOptions = { name, config, ... }: { 327 options = { 328 329 environment = mkOption { 330 default = {}; 331 type = with types; attrsOf (nullOr (oneOf [ str path package ])); 332 example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; }; 333 description = "Environment variables passed to the service's processes."; 334 }; 335 336 path = mkOption { 337 default = []; 338 type = with types; listOf (oneOf [ package str ]); 339 description = '' 340 Packages added to the service's {env}`PATH` 341 environment variable. Both the {file}`bin` 342 and {file}`sbin` subdirectories of each 343 package are added. 344 ''; 345 }; 346 347 serviceConfig = mkOption { 348 default = {}; 349 example = 350 { RestartSec = 5; 351 }; 352 type = types.addCheck (types.attrsOf unitOption) checkService; 353 description = '' 354 Each attribute in this set specifies an option in the 355 `[Service]` section of the unit. See 356 {manpage}`systemd.service(5)` for details. 357 ''; 358 }; 359 360 script = mkOption { 361 type = types.lines; 362 default = ""; 363 description = "Shell commands executed as the service's main process."; 364 }; 365 366 scriptArgs = mkOption { 367 type = types.str; 368 default = ""; 369 example = "%i"; 370 description = '' 371 Arguments passed to the main process script. 372 Can contain specifiers (`%` placeholders expanded by systemd, see {manpage}`systemd.unit(5)`). 373 ''; 374 }; 375 376 preStart = mkOption { 377 type = types.lines; 378 default = ""; 379 description = '' 380 Shell commands executed before the service's main process 381 is started. 382 ''; 383 }; 384 385 postStart = mkOption { 386 type = types.lines; 387 default = ""; 388 description = '' 389 Shell commands executed after the service's main process 390 is started. 391 ''; 392 }; 393 394 reload = mkOption { 395 type = types.lines; 396 default = ""; 397 description = '' 398 Shell commands executed when the service's main process 399 is reloaded. 400 ''; 401 }; 402 403 preStop = mkOption { 404 type = types.lines; 405 default = ""; 406 description = '' 407 Shell commands executed to stop the service. 408 ''; 409 }; 410 411 postStop = mkOption { 412 type = types.lines; 413 default = ""; 414 description = '' 415 Shell commands executed after the service's main process 416 has exited. 417 ''; 418 }; 419 420 jobScripts = mkOption { 421 type = with types; coercedTo path singleton (listOf path); 422 internal = true; 423 description = "A list of all job script derivations of this unit."; 424 default = []; 425 }; 426 427 }; 428 429 config = mkMerge [ 430 (mkIf (config.preStart != "") rec { 431 jobScripts = makeJobScript "${name}-pre-start" config.preStart; 432 serviceConfig.ExecStartPre = [ jobScripts ]; 433 }) 434 (mkIf (config.script != "") rec { 435 jobScripts = makeJobScript "${name}-start" config.script; 436 serviceConfig.ExecStart = jobScripts + " " + config.scriptArgs; 437 }) 438 (mkIf (config.postStart != "") rec { 439 jobScripts = (makeJobScript "${name}-post-start" config.postStart); 440 serviceConfig.ExecStartPost = [ jobScripts ]; 441 }) 442 (mkIf (config.reload != "") rec { 443 jobScripts = makeJobScript "${name}-reload" config.reload; 444 serviceConfig.ExecReload = jobScripts; 445 }) 446 (mkIf (config.preStop != "") rec { 447 jobScripts = makeJobScript "${name}-pre-stop" config.preStop; 448 serviceConfig.ExecStop = jobScripts; 449 }) 450 (mkIf (config.postStop != "") rec { 451 jobScripts = makeJobScript "${name}-post-stop" config.postStop; 452 serviceConfig.ExecStopPost = jobScripts; 453 }) 454 ]; 455 456 }; 457 458 stage2ServiceOptions = { 459 imports = [ 460 stage2CommonUnitOptions 461 serviceOptions 462 ]; 463 464 options = { 465 restartIfChanged = mkOption { 466 type = types.bool; 467 default = true; 468 description = '' 469 Whether the service should be restarted during a NixOS 470 configuration switch if its definition has changed. 471 ''; 472 }; 473 474 reloadIfChanged = mkOption { 475 type = types.bool; 476 default = false; 477 description = '' 478 Whether the service should be reloaded during a NixOS 479 configuration switch if its definition has changed. If 480 enabled, the value of {option}`restartIfChanged` is 481 ignored. 482 483 This option should not be used anymore in favor of 484 {option}`reloadTriggers` which allows more granular 485 control of when a service is reloaded and when a service 486 is restarted. 487 ''; 488 }; 489 490 stopIfChanged = mkOption { 491 type = types.bool; 492 default = true; 493 description = '' 494 If set, a changed unit is restarted by calling 495 {command}`systemctl stop` in the old configuration, 496 then {command}`systemctl start` in the new one. 497 Otherwise, it is restarted in a single step using 498 {command}`systemctl restart` in the new configuration. 499 The latter is less correct because it runs the 500 `ExecStop` commands from the new 501 configuration. 502 ''; 503 }; 504 505 startAt = mkOption { 506 type = with types; either str (listOf str); 507 default = []; 508 example = "Sun 14:00:00"; 509 description = '' 510 Automatically start this unit at the given date/time, which 511 must be in the format described in 512 {manpage}`systemd.time(7)`. This is equivalent 513 to adding a corresponding timer unit with 514 {option}`OnCalendar` set to the value given here. 515 ''; 516 apply = v: if isList v then v else [ v ]; 517 }; 518 }; 519 }; 520 521 stage1ServiceOptions = { 522 imports = [ 523 stage1CommonUnitOptions 524 serviceOptions 525 ]; 526 }; 527 528 529 socketOptions = { 530 options = { 531 532 listenStreams = mkOption { 533 default = []; 534 type = types.listOf types.str; 535 example = [ "0.0.0.0:993" "/run/my-socket" ]; 536 description = '' 537 For each item in this list, a `ListenStream` 538 option in the `[Socket]` section will be created. 539 ''; 540 }; 541 542 listenDatagrams = mkOption { 543 default = []; 544 type = types.listOf types.str; 545 example = [ "0.0.0.0:993" "/run/my-socket" ]; 546 description = '' 547 For each item in this list, a `ListenDatagram` 548 option in the `[Socket]` section will be created. 549 ''; 550 }; 551 552 socketConfig = mkOption { 553 default = {}; 554 example = { ListenStream = "/run/my-socket"; }; 555 type = types.attrsOf unitOption; 556 description = '' 557 Each attribute in this set specifies an option in the 558 `[Socket]` section of the unit. See 559 {manpage}`systemd.socket(5)` for details. 560 ''; 561 }; 562 }; 563 564 }; 565 566 stage2SocketOptions = { 567 imports = [ 568 stage2CommonUnitOptions 569 socketOptions 570 ]; 571 }; 572 573 stage1SocketOptions = { 574 imports = [ 575 stage1CommonUnitOptions 576 socketOptions 577 ]; 578 }; 579 580 581 timerOptions = { 582 options = { 583 584 timerConfig = mkOption { 585 default = {}; 586 example = { OnCalendar = "Sun 14:00:00"; Unit = "foo.service"; }; 587 type = types.attrsOf unitOption; 588 description = '' 589 Each attribute in this set specifies an option in the 590 `[Timer]` section of the unit. See 591 {manpage}`systemd.timer(5)` and 592 {manpage}`systemd.time(7)` for details. 593 ''; 594 }; 595 596 }; 597 }; 598 599 stage2TimerOptions = { 600 imports = [ 601 stage2CommonUnitOptions 602 timerOptions 603 ]; 604 }; 605 606 stage1TimerOptions = { 607 imports = [ 608 stage1CommonUnitOptions 609 timerOptions 610 ]; 611 }; 612 613 614 pathOptions = { 615 options = { 616 617 pathConfig = mkOption { 618 default = {}; 619 example = { PathChanged = "/some/path"; Unit = "changedpath.service"; }; 620 type = types.attrsOf unitOption; 621 description = '' 622 Each attribute in this set specifies an option in the 623 `[Path]` section of the unit. See 624 {manpage}`systemd.path(5)` for details. 625 ''; 626 }; 627 628 }; 629 }; 630 631 stage2PathOptions = { 632 imports = [ 633 stage2CommonUnitOptions 634 pathOptions 635 ]; 636 }; 637 638 stage1PathOptions = { 639 imports = [ 640 stage1CommonUnitOptions 641 pathOptions 642 ]; 643 }; 644 645 646 mountOptions = { 647 options = { 648 649 what = mkOption { 650 example = "/dev/sda1"; 651 type = types.str; 652 description = "Absolute path of device node, file or other resource. (Mandatory)"; 653 }; 654 655 where = mkOption { 656 example = "/mnt"; 657 type = types.str; 658 description = '' 659 Absolute path of a directory of the mount point. 660 Will be created if it doesn't exist. (Mandatory) 661 ''; 662 }; 663 664 type = mkOption { 665 default = ""; 666 example = "ext4"; 667 type = types.str; 668 description = "File system type."; 669 }; 670 671 options = mkOption { 672 default = ""; 673 example = "noatime"; 674 type = types.commas; 675 description = "Options used to mount the file system."; 676 }; 677 678 mountConfig = mkOption { 679 default = {}; 680 example = { DirectoryMode = "0775"; }; 681 type = types.attrsOf unitOption; 682 description = '' 683 Each attribute in this set specifies an option in the 684 `[Mount]` section of the unit. See 685 {manpage}`systemd.mount(5)` for details. 686 ''; 687 }; 688 689 }; 690 }; 691 692 stage2MountOptions = { 693 imports = [ 694 stage2CommonUnitOptions 695 mountOptions 696 ]; 697 }; 698 699 stage1MountOptions = { 700 imports = [ 701 stage1CommonUnitOptions 702 mountOptions 703 ]; 704 }; 705 706 automountOptions = { 707 options = { 708 709 where = mkOption { 710 example = "/mnt"; 711 type = types.str; 712 description = '' 713 Absolute path of a directory of the mount point. 714 Will be created if it doesn't exist. (Mandatory) 715 ''; 716 }; 717 718 automountConfig = mkOption { 719 default = {}; 720 example = { DirectoryMode = "0775"; }; 721 type = types.attrsOf unitOption; 722 description = '' 723 Each attribute in this set specifies an option in the 724 `[Automount]` section of the unit. See 725 {manpage}`systemd.automount(5)` for details. 726 ''; 727 }; 728 729 }; 730 }; 731 732 stage2AutomountOptions = { 733 imports = [ 734 stage2CommonUnitOptions 735 automountOptions 736 ]; 737 }; 738 739 stage1AutomountOptions = { 740 imports = [ 741 stage1CommonUnitOptions 742 automountOptions 743 ]; 744 }; 745 746 sliceOptions = { 747 options = { 748 749 sliceConfig = mkOption { 750 default = {}; 751 example = { MemoryMax = "2G"; }; 752 type = types.attrsOf unitOption; 753 description = '' 754 Each attribute in this set specifies an option in the 755 `[Slice]` section of the unit. See 756 {manpage}`systemd.slice(5)` for details. 757 ''; 758 }; 759 760 }; 761 }; 762 763 stage2SliceOptions = { 764 imports = [ 765 stage2CommonUnitOptions 766 sliceOptions 767 ]; 768 }; 769 770 stage1SliceOptions = { 771 imports = [ 772 stage1CommonUnitOptions 773 sliceOptions 774 ]; 775 }; 776 777}