at 23.05-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 systemd.unit(1). 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 in systemd.unit(5) manpage described `[Install]` section 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 description = lib.mdDoc "Arguments passed to the main process script."; 328 }; 329 330 preStart = mkOption { 331 type = types.lines; 332 default = ""; 333 description = lib.mdDoc '' 334 Shell commands executed before the service's main process 335 is started. 336 ''; 337 }; 338 339 postStart = mkOption { 340 type = types.lines; 341 default = ""; 342 description = lib.mdDoc '' 343 Shell commands executed after the service's main process 344 is started. 345 ''; 346 }; 347 348 reload = mkOption { 349 type = types.lines; 350 default = ""; 351 description = lib.mdDoc '' 352 Shell commands executed when the service's main process 353 is reloaded. 354 ''; 355 }; 356 357 preStop = mkOption { 358 type = types.lines; 359 default = ""; 360 description = lib.mdDoc '' 361 Shell commands executed to stop the service. 362 ''; 363 }; 364 365 postStop = mkOption { 366 type = types.lines; 367 default = ""; 368 description = lib.mdDoc '' 369 Shell commands executed after the service's main process 370 has exited. 371 ''; 372 }; 373 374 jobScripts = mkOption { 375 type = with types; coercedTo path singleton (listOf path); 376 internal = true; 377 description = lib.mdDoc "A list of all job script derivations of this unit."; 378 default = []; 379 }; 380 381 }; 382 383 config = mkMerge [ 384 (mkIf (config.preStart != "") rec { 385 jobScripts = makeJobScript "${name}-pre-start" config.preStart; 386 serviceConfig.ExecStartPre = [ jobScripts ]; 387 }) 388 (mkIf (config.script != "") rec { 389 jobScripts = makeJobScript "${name}-start" config.script; 390 serviceConfig.ExecStart = jobScripts + " " + config.scriptArgs; 391 }) 392 (mkIf (config.postStart != "") rec { 393 jobScripts = (makeJobScript "${name}-post-start" config.postStart); 394 serviceConfig.ExecStartPost = [ jobScripts ]; 395 }) 396 (mkIf (config.reload != "") rec { 397 jobScripts = makeJobScript "${name}-reload" config.reload; 398 serviceConfig.ExecReload = jobScripts; 399 }) 400 (mkIf (config.preStop != "") rec { 401 jobScripts = makeJobScript "${name}-pre-stop" config.preStop; 402 serviceConfig.ExecStop = jobScripts; 403 }) 404 (mkIf (config.postStop != "") rec { 405 jobScripts = makeJobScript "${name}-post-stop" config.postStop; 406 serviceConfig.ExecStopPost = jobScripts; 407 }) 408 ]; 409 410 }; 411 412 stage2ServiceOptions = { 413 imports = [ 414 stage2CommonUnitOptions 415 serviceOptions 416 ]; 417 418 options = { 419 restartIfChanged = mkOption { 420 type = types.bool; 421 default = true; 422 description = lib.mdDoc '' 423 Whether the service should be restarted during a NixOS 424 configuration switch if its definition has changed. 425 ''; 426 }; 427 428 reloadIfChanged = mkOption { 429 type = types.bool; 430 default = false; 431 description = lib.mdDoc '' 432 Whether the service should be reloaded during a NixOS 433 configuration switch if its definition has changed. If 434 enabled, the value of {option}`restartIfChanged` is 435 ignored. 436 437 This option should not be used anymore in favor of 438 {option}`reloadTriggers` which allows more granular 439 control of when a service is reloaded and when a service 440 is restarted. 441 ''; 442 }; 443 444 stopIfChanged = mkOption { 445 type = types.bool; 446 default = true; 447 description = lib.mdDoc '' 448 If set, a changed unit is restarted by calling 449 {command}`systemctl stop` in the old configuration, 450 then {command}`systemctl start` in the new one. 451 Otherwise, it is restarted in a single step using 452 {command}`systemctl restart` in the new configuration. 453 The latter is less correct because it runs the 454 `ExecStop` commands from the new 455 configuration. 456 ''; 457 }; 458 459 startAt = mkOption { 460 type = with types; either str (listOf str); 461 default = []; 462 example = "Sun 14:00:00"; 463 description = lib.mdDoc '' 464 Automatically start this unit at the given date/time, which 465 must be in the format described in 466 {manpage}`systemd.time(7)`. This is equivalent 467 to adding a corresponding timer unit with 468 {option}`OnCalendar` set to the value given here. 469 ''; 470 apply = v: if isList v then v else [ v ]; 471 }; 472 }; 473 }; 474 475 stage1ServiceOptions = { 476 imports = [ 477 stage1CommonUnitOptions 478 serviceOptions 479 ]; 480 }; 481 482 483 socketOptions = { 484 options = { 485 486 listenStreams = mkOption { 487 default = []; 488 type = types.listOf types.str; 489 example = [ "0.0.0.0:993" "/run/my-socket" ]; 490 description = lib.mdDoc '' 491 For each item in this list, a `ListenStream` 492 option in the `[Socket]` section will be created. 493 ''; 494 }; 495 496 listenDatagrams = mkOption { 497 default = []; 498 type = types.listOf types.str; 499 example = [ "0.0.0.0:993" "/run/my-socket" ]; 500 description = lib.mdDoc '' 501 For each item in this list, a `ListenDatagram` 502 option in the `[Socket]` section will be created. 503 ''; 504 }; 505 506 socketConfig = mkOption { 507 default = {}; 508 example = { ListenStream = "/run/my-socket"; }; 509 type = types.attrsOf unitOption; 510 description = lib.mdDoc '' 511 Each attribute in this set specifies an option in the 512 `[Socket]` section of the unit. See 513 {manpage}`systemd.socket(5)` for details. 514 ''; 515 }; 516 }; 517 518 }; 519 520 stage2SocketOptions = { 521 imports = [ 522 stage2CommonUnitOptions 523 socketOptions 524 ]; 525 }; 526 527 stage1SocketOptions = { 528 imports = [ 529 stage1CommonUnitOptions 530 socketOptions 531 ]; 532 }; 533 534 535 timerOptions = { 536 options = { 537 538 timerConfig = mkOption { 539 default = {}; 540 example = { OnCalendar = "Sun 14:00:00"; Unit = "foo.service"; }; 541 type = types.attrsOf unitOption; 542 description = lib.mdDoc '' 543 Each attribute in this set specifies an option in the 544 `[Timer]` section of the unit. See 545 {manpage}`systemd.timer(5)` and 546 {manpage}`systemd.time(7)` for details. 547 ''; 548 }; 549 550 }; 551 }; 552 553 stage2TimerOptions = { 554 imports = [ 555 stage2CommonUnitOptions 556 timerOptions 557 ]; 558 }; 559 560 stage1TimerOptions = { 561 imports = [ 562 stage1CommonUnitOptions 563 timerOptions 564 ]; 565 }; 566 567 568 pathOptions = { 569 options = { 570 571 pathConfig = mkOption { 572 default = {}; 573 example = { PathChanged = "/some/path"; Unit = "changedpath.service"; }; 574 type = types.attrsOf unitOption; 575 description = lib.mdDoc '' 576 Each attribute in this set specifies an option in the 577 `[Path]` section of the unit. See 578 {manpage}`systemd.path(5)` for details. 579 ''; 580 }; 581 582 }; 583 }; 584 585 stage2PathOptions = { 586 imports = [ 587 stage2CommonUnitOptions 588 pathOptions 589 ]; 590 }; 591 592 stage1PathOptions = { 593 imports = [ 594 stage1CommonUnitOptions 595 pathOptions 596 ]; 597 }; 598 599 600 mountOptions = { 601 options = { 602 603 what = mkOption { 604 example = "/dev/sda1"; 605 type = types.str; 606 description = lib.mdDoc "Absolute path of device node, file or other resource. (Mandatory)"; 607 }; 608 609 where = mkOption { 610 example = "/mnt"; 611 type = types.str; 612 description = lib.mdDoc '' 613 Absolute path of a directory of the mount point. 614 Will be created if it doesn't exist. (Mandatory) 615 ''; 616 }; 617 618 type = mkOption { 619 default = ""; 620 example = "ext4"; 621 type = types.str; 622 description = lib.mdDoc "File system type."; 623 }; 624 625 options = mkOption { 626 default = ""; 627 example = "noatime"; 628 type = types.commas; 629 description = lib.mdDoc "Options used to mount the file system."; 630 }; 631 632 mountConfig = mkOption { 633 default = {}; 634 example = { DirectoryMode = "0775"; }; 635 type = types.attrsOf unitOption; 636 description = lib.mdDoc '' 637 Each attribute in this set specifies an option in the 638 `[Mount]` section of the unit. See 639 {manpage}`systemd.mount(5)` for details. 640 ''; 641 }; 642 643 }; 644 }; 645 646 stage2MountOptions = { 647 imports = [ 648 stage2CommonUnitOptions 649 mountOptions 650 ]; 651 }; 652 653 stage1MountOptions = { 654 imports = [ 655 stage1CommonUnitOptions 656 mountOptions 657 ]; 658 }; 659 660 automountOptions = { 661 options = { 662 663 where = mkOption { 664 example = "/mnt"; 665 type = types.str; 666 description = lib.mdDoc '' 667 Absolute path of a directory of the mount point. 668 Will be created if it doesn't exist. (Mandatory) 669 ''; 670 }; 671 672 automountConfig = mkOption { 673 default = {}; 674 example = { DirectoryMode = "0775"; }; 675 type = types.attrsOf unitOption; 676 description = lib.mdDoc '' 677 Each attribute in this set specifies an option in the 678 `[Automount]` section of the unit. See 679 {manpage}`systemd.automount(5)` for details. 680 ''; 681 }; 682 683 }; 684 }; 685 686 stage2AutomountOptions = { 687 imports = [ 688 stage2CommonUnitOptions 689 automountOptions 690 ]; 691 }; 692 693 stage1AutomountOptions = { 694 imports = [ 695 stage1CommonUnitOptions 696 automountOptions 697 ]; 698 }; 699 700 sliceOptions = { 701 options = { 702 703 sliceConfig = mkOption { 704 default = {}; 705 example = { MemoryMax = "2G"; }; 706 type = types.attrsOf unitOption; 707 description = lib.mdDoc '' 708 Each attribute in this set specifies an option in the 709 `[Slice]` section of the unit. See 710 {manpage}`systemd.slice(5)` for details. 711 ''; 712 }; 713 714 }; 715 }; 716 717 stage2SliceOptions = { 718 imports = [ 719 stage2CommonUnitOptions 720 sliceOptions 721 ]; 722 }; 723 724 stage1SliceOptions = { 725 imports = [ 726 stage1CommonUnitOptions 727 sliceOptions 728 ]; 729 }; 730 731}