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