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