From 8d925cc8db5fcc0fe0e091d819d93f8580e62c53 Mon Sep 17 00:00:00 2001 From: Janne Heß Date: Sun, 30 Jan 2022 00:37:55 +0100 Subject: nixos/doc: Document the activation script This may be helpful to new module developers, curious users, and people who just need a reference without having to look at the implementation --- .../development/activation-script.section.md | 72 ++++++++++ nixos/doc/manual/development/development.xml | 1 + .../manual/development/unit-handling.section.md | 57 ++++++++ .../what-happens-during-a-system-switch.chapter.md | 53 ++++++++ .../development/activation-script.section.xml | 150 +++++++++++++++++++++ .../from_md/development/unit-handling.section.xml | 119 ++++++++++++++++ ...what-happens-during-a-system-switch.chapter.xml | 122 +++++++++++++++++ 7 files changed, 574 insertions(+) create mode 100644 nixos/doc/manual/development/activation-script.section.md create mode 100644 nixos/doc/manual/development/unit-handling.section.md create mode 100644 nixos/doc/manual/development/what-happens-during-a-system-switch.chapter.md create mode 100644 nixos/doc/manual/from_md/development/activation-script.section.xml create mode 100644 nixos/doc/manual/from_md/development/unit-handling.section.xml create mode 100644 nixos/doc/manual/from_md/development/what-happens-during-a-system-switch.chapter.xml diff --git a/nixos/doc/manual/development/activation-script.section.md b/nixos/doc/manual/development/activation-script.section.md new file mode 100644 index 00000000000..df683662404 --- /dev/null +++ b/nixos/doc/manual/development/activation-script.section.md @@ -0,0 +1,72 @@ +# Activation script {#sec-activation-script} + +The activation script is a bash script called to activate the new +configuration which resides in a NixOS system in `$out/activate`. Since its +contents depend on your system configuration, the contents may differ. +This chapter explains how the script works in general and some common NixOS +snippets. Please be aware that the script is executed on every boot and system +switch, so tasks that can be performed in other places should be performed +there (for example letting a directory of a service be created by systemd using +mechanisms like `StateDirectory`, `CacheDirectory`, ... or if that's not +possible using `preStart` of the service). + +Activation scripts are defined as snippets using +[](#opt-system.activationScripts). They can either be a simple multiline string +or an attribute set that can depend on other snippets. The builder for the +activation script will take these dependencies into account and order the +snippets accordingly. As a simple example: + +```nix +system.activationScripts.my-activation-script = { + deps = [ "etc" ]; + # supportsDryActivation = true; + text = '' + echo "Hallo i bims" + ''; +}; +``` + +This example creates an activation script snippet that is run after the `etc` +snippet. The special variable `supportsDryActivation` can be set so the snippet +is also run when `nixos-rebuild dry-activate` is run. To differentiate between +real and dry activation, the `$NIXOS_ACTION` environment variable can be +read which is set to `dry-activate` when a dry activation is done. + +An activation script can write to special files instructing +`switch-to-configuration` to restart/reload units. The script will take these +requests into account and will incorperate the unit configuration as described +above. This means that the activation script will "fake" a modified unit file +and `switch-to-configuration` will act accordingly. By doing so, configuration +like [systemd.services.\.restartIfChanged](#opt-systemd.services) is +respected. Since the activation script is run **after** services are already +stopped, [systemd.services.\.stopIfChanged](#opt-systemd.services) +cannot be taken into account anymore and the unit is always restarted instead +of being stopped and started afterwards. + +The files that can be written to are `/run/nixos/activation-restart-list` and +`/run/nixos/activation-reload-list` with their respective counterparts for +dry activation being `/run/nixos/dry-activation-restart-list` and +`/run/nixos/dry-activation-reload-list`. Those files can contain +newline-separated lists of unit names where duplicates are being ignored. These +files are not create automatically and activation scripts must take the +possiblility into account that they have to create them first. + +## NixOS snippets {#sec-activation-script-nixos-snippets} + +There are some snippets NixOS enables by default because disabling them would +most likely break you system. This section lists a few of them and what they +do: + +- `binsh` creates `/bin/sh` which points to the runtime shell +- `etc` sets up the contents of `/etc`, this includes systemd units and + excludes `/etc/passwd`, `/etc/group`, and `/etc/shadow` (which are managed by + the `users` snippet) +- `hostname` sets the system's hostname in the kernel (not in `/etc`) +- `modprobe` sets the path to the `modprobe` binary for module auto-loading +- `nix` prepares the nix store and adds a default initial channel +- `specialfs` is responsible for mounting filesystems like `/proc` and `sys` +- `users` creates and removes users and groups by managing `/etc/passwd`, + `/etc/group` and `/etc/shadow`. This also creates home directories +- `usrbinenv` creates `/usr/bin/env` +- `var` creates some directories in `/var` that are not service-specific +- `wrappers` creates setuid wrappers like `ping` and `sudo` diff --git a/nixos/doc/manual/development/development.xml b/nixos/doc/manual/development/development.xml index 0b2ad60a878..21286cdbd2b 100644 --- a/nixos/doc/manual/development/development.xml +++ b/nixos/doc/manual/development/development.xml @@ -12,6 +12,7 @@ + diff --git a/nixos/doc/manual/development/unit-handling.section.md b/nixos/doc/manual/development/unit-handling.section.md new file mode 100644 index 00000000000..d477f2c860f --- /dev/null +++ b/nixos/doc/manual/development/unit-handling.section.md @@ -0,0 +1,57 @@ +# Unit handling {#sec-unit-handling} + +To figure out what units need to be started/stopped/restarted/reloaded, the +script first checks the current state of the system, similar to what `systemctl +list-units` shows. For each of the units, the script goes through the following +checks: + +- Is the unit file still in the new system? If not, **stop** the service unless + it sets `X-StopOnRemoval` in the `[Unit]` section to `false`. + +- Is it a `.target` unit? If so, **start** it unless it sets + `RefuseManualStart` in the `[Unit]` section to `true` or `X-OnlyManualStart` + in the `[Unit]` section to `true`. Also **stop** the unit again unless it + sets `X-StopOnReconfiguration` to `false`. + +- Are the contents of the unit files different? They are compared by parsing + them and comparing their contents. If they are different but only + `X-Reload-Triggers` in the `[Unit]` section is changed, **reload** the unit. + The NixOS module system allows setting these triggers with the option + [systemd.services.\.reloadTriggers](#opt-systemd.services). If the + unit files differ in any way, the following actions are performed: + + - `.path` and `.slice` units are ignored. There is no need to restart them + since changes in their values are applied by systemd when systemd is + reloaded. + + - `.mount` units are **reload**ed. These mostly come from the `/etc/fstab` + parser. + + - `.socket` units are currently ignored. This is to be fixed at a later + point. + + - The rest of the units (mostly `.service` units) are then **reload**ed if + `X-ReloadIfChanged` in the `[Service]` section is set to `true` (exposed + via [systemd.services.\.reloadIfChanged](#opt-systemd.services)). + + - If the reload flag is not set, some more flags decide if the unit is + skipped. These flags are `X-RestartIfChanged` in the `[Service]` section + (exposed via + [systemd.services.\.restartIfChanged](#opt-systemd.services)), + `RefuseManualStop` in the `[Unit]` section, and `X-OnlyManualStart` in the + `[Unit]` section. + + - The rest of the behavior is decided whether the unit has `X-StopIfChanged` + in the `[Service]` section set (exposed via + [systemd.services.\.stopIfChanged](#opt-systemd.services)). This is + set to `true` by default and must be explicitly turned off if not wanted. + If the flag is enabled, the unit is **stop**ped and then **start**ed. If + not, the unit is **restart**ed. The goal of the flag is to make sure that + the new unit never runs in the old environment which is still in place + before the activation script is run. + + - The last thing that is taken into account is whether the unit is a service + and socket-activated. Due to a bug, this is currently only done when + `X-StopIfChanged` is set. If the unit is socket-activated, the socket is + stopped and started, and the service is stopped and to be started by socket + activation. diff --git a/nixos/doc/manual/development/what-happens-during-a-system-switch.chapter.md b/nixos/doc/manual/development/what-happens-during-a-system-switch.chapter.md new file mode 100644 index 00000000000..aad82831a3c --- /dev/null +++ b/nixos/doc/manual/development/what-happens-during-a-system-switch.chapter.md @@ -0,0 +1,53 @@ +# What happens during a system switch? {#sec-switching-systems} + +Running `nixos-rebuild switch` is one of the more common tasks under NixOS. +This chapter explains some of the internals of this command to make it simpler +for new module developers to configure their units correctly and to make it +easier to understand what is happening and why for curious administrators. + +`nixos-rebuild`, like many deployment solutions, calls `switch-to-configuration` +which resides in a NixOS system at `$out/bin/switch-to-configuration`. The +script is called with the action that is to be performed like `switch`, `test`, +`boot`. There is also the `dry-activate` action which does not really perform +the actions but rather prints what it would do if you called it with `test`. +This feature can be used to check what service states would be changed if the +configuration was switched to. + +If the action is `switch` or `boot`, the bootloader is updated first so the +configuration will be the next one to boot. Unless `NIXOS_NO_SYNC` is set to +`1`, `/nix/store` is synced to disk. + +If the action is `switch` or `test`, the currently running system is inspected +and the actions to switch to the new system are calculated. This process takes +two data sources into account: `/etc/fstab` and the current systemd status. +Mounts and swaps are read from `/etc/fstab` and the corresponding actions are +generated. If a new mount is added, for example, the proper `.mount` unit is +marked to be started. The current systemd state is inspected, the difference +between the current system and the desired configuration is calculated and +actions are generated to get to this state. There are a lot of nuances that can +be controlled by the units which are explained here. + +After calculating what should be done, the actions are carried out. The order +of actions is always the same: +- Stop units (`systemctl stop`) +- Run activation script (`$out/activate`) +- See if the activation script requested more units to restart +- Restart systemd if needed (`systemd daemon-reexec`) +- Forget about the failed state of units (`systemctl reset-failed`) +- Reload systemd (`systemctl daemon-reload`) +- Reload systemd user instances (`systemctl --user daemon-reload`) +- Set up tmpfiles (`systemd-tmpfiles --create`) +- Reload units (`systemctl reload`) +- Restart units (`systemctl restart`) +- Start units (`systemctl start`) +- Inspect what changed during these actions and print units that failed and + that were newly started + +Most of these actions are either self-explaining but some of them have to do +with our units or the activation script. For this reason, these topics are +explained in the next sections. + +```{=docbook} + + +``` diff --git a/nixos/doc/manual/from_md/development/activation-script.section.xml b/nixos/doc/manual/from_md/development/activation-script.section.xml new file mode 100644 index 00000000000..0d9e911216e --- /dev/null +++ b/nixos/doc/manual/from_md/development/activation-script.section.xml @@ -0,0 +1,150 @@ +
+ Activation script + + The activation script is a bash script called to activate the new + configuration which resides in a NixOS system in + $out/activate. Since its contents depend on your + system configuration, the contents may differ. This chapter explains + how the script works in general and some common NixOS snippets. + Please be aware that the script is executed on every boot and system + switch, so tasks that can be performed in other places should be + performed there (for example letting a directory of a service be + created by systemd using mechanisms like + StateDirectory, + CacheDirectory, … or if that’s not possible using + preStart of the service). + + + Activation scripts are defined as snippets using + . They can either be + a simple multiline string or an attribute set that can depend on + other snippets. The builder for the activation script will take + these dependencies into account and order the snippets accordingly. + As a simple example: + + +system.activationScripts.my-activation-script = { + deps = [ "etc" ]; + # supportsDryActivation = true; + text = '' + echo "Hallo i bims" + ''; +}; + + + This example creates an activation script snippet that is run after + the etc snippet. The special variable + supportsDryActivation can be set so the snippet + is also run when nixos-rebuild dry-activate is + run. To differentiate between real and dry activation, the + $NIXOS_ACTION environment variable can be read + which is set to dry-activate when a dry + activation is done. + + + An activation script can write to special files instructing + switch-to-configuration to restart/reload units. + The script will take these requests into account and will + incorperate the unit configuration as described above. This means + that the activation script will fake a modified unit + file and switch-to-configuration will act + accordingly. By doing so, configuration like + systemd.services.<name>.restartIfChanged + is respected. Since the activation script is run + after services are already + stopped, + systemd.services.<name>.stopIfChanged + cannot be taken into account anymore and the unit is always + restarted instead of being stopped and started afterwards. + + + The files that can be written to are + /run/nixos/activation-restart-list and + /run/nixos/activation-reload-list with their + respective counterparts for dry activation being + /run/nixos/dry-activation-restart-list and + /run/nixos/dry-activation-reload-list. Those + files can contain newline-separated lists of unit names where + duplicates are being ignored. These files are not create + automatically and activation scripts must take the possiblility into + account that they have to create them first. + +
+ NixOS snippets + + There are some snippets NixOS enables by default because disabling + them would most likely break you system. This section lists a few + of them and what they do: + + + + + binsh creates /bin/sh + which points to the runtime shell + + + + + etc sets up the contents of + /etc, this includes systemd units and + excludes /etc/passwd, + /etc/group, and + /etc/shadow (which are managed by the + users snippet) + + + + + hostname sets the system’s hostname in the + kernel (not in /etc) + + + + + modprobe sets the path to the + modprobe binary for module auto-loading + + + + + nix prepares the nix store and adds a + default initial channel + + + + + specialfs is responsible for mounting + filesystems like /proc and + sys + + + + + users creates and removes users and groups + by managing /etc/passwd, + /etc/group and + /etc/shadow. This also creates home + directories + + + + + usrbinenv creates + /usr/bin/env + + + + + var creates some directories in + /var that are not service-specific + + + + + wrappers creates setuid wrappers like + ping and sudo + + + +
+
diff --git a/nixos/doc/manual/from_md/development/unit-handling.section.xml b/nixos/doc/manual/from_md/development/unit-handling.section.xml new file mode 100644 index 00000000000..a6a654042f6 --- /dev/null +++ b/nixos/doc/manual/from_md/development/unit-handling.section.xml @@ -0,0 +1,119 @@ +
+ Unit handling + + To figure out what units need to be + started/stopped/restarted/reloaded, the script first checks the + current state of the system, similar to what + systemctl list-units shows. For each of the + units, the script goes through the following checks: + + + + + Is the unit file still in the new system? If not, + stop the service unless it + sets X-StopOnRemoval in the + [Unit] section to false. + + + + + Is it a .target unit? If so, + start it unless it sets + RefuseManualStart in the + [Unit] section to true or + X-OnlyManualStart in the + [Unit] section to true. + Also stop the unit again + unless it sets X-StopOnReconfiguration to + false. + + + + + Are the contents of the unit files different? They are compared + by parsing them and comparing their contents. If they are + different but only X-Reload-Triggers in the + [Unit] section is changed, + reload the unit. The NixOS + module system allows setting these triggers with the option + systemd.services.<name>.reloadTriggers. + If the unit files differ in any way, the following actions are + performed: + + + + + .path and .slice units + are ignored. There is no need to restart them since changes + in their values are applied by systemd when systemd is + reloaded. + + + + + .mount units are + reloaded. These mostly + come from the /etc/fstab parser. + + + + + .socket units are currently ignored. This + is to be fixed at a later point. + + + + + The rest of the units (mostly .service + units) are then reloaded + if X-ReloadIfChanged in the + [Service] section is set to + true (exposed via + systemd.services.<name>.reloadIfChanged). + + + + + If the reload flag is not set, some more flags decide if the + unit is skipped. These flags are + X-RestartIfChanged in the + [Service] section (exposed via + systemd.services.<name>.restartIfChanged), + RefuseManualStop in the + [Unit] section, and + X-OnlyManualStart in the + [Unit] section. + + + + + The rest of the behavior is decided whether the unit has + X-StopIfChanged in the + [Service] section set (exposed via + systemd.services.<name>.stopIfChanged). + This is set to true by default and must + be explicitly turned off if not wanted. If the flag is + enabled, the unit is + stopped and then + started. If not, the unit + is restarted. The goal of + the flag is to make sure that the new unit never runs in the + old environment which is still in place before the + activation script is run. + + + + + The last thing that is taken into account is whether the + unit is a service and socket-activated. Due to a bug, this + is currently only done when + X-StopIfChanged is set. If the unit is + socket-activated, the socket is stopped and started, and the + service is stopped and to be started by socket activation. + + + + + +
diff --git a/nixos/doc/manual/from_md/development/what-happens-during-a-system-switch.chapter.xml b/nixos/doc/manual/from_md/development/what-happens-during-a-system-switch.chapter.xml new file mode 100644 index 00000000000..66ba792ddac --- /dev/null +++ b/nixos/doc/manual/from_md/development/what-happens-during-a-system-switch.chapter.xml @@ -0,0 +1,122 @@ + + What happens during a system switch? + + Running nixos-rebuild switch is one of the more + common tasks under NixOS. This chapter explains some of the + internals of this command to make it simpler for new module + developers to configure their units correctly and to make it easier + to understand what is happening and why for curious administrators. + + + nixos-rebuild, like many deployment solutions, + calls switch-to-configuration which resides in a + NixOS system at $out/bin/switch-to-configuration. + The script is called with the action that is to be performed like + switch, test, + boot. There is also the + dry-activate action which does not really perform + the actions but rather prints what it would do if you called it with + test. This feature can be used to check what + service states would be changed if the configuration was switched + to. + + + If the action is switch or + boot, the bootloader is updated first so the + configuration will be the next one to boot. Unless + NIXOS_NO_SYNC is set to 1, + /nix/store is synced to disk. + + + If the action is switch or + test, the currently running system is inspected + and the actions to switch to the new system are calculated. This + process takes two data sources into account: + /etc/fstab and the current systemd status. Mounts + and swaps are read from /etc/fstab and the + corresponding actions are generated. If a new mount is added, for + example, the proper .mount unit is marked to be + started. The current systemd state is inspected, the difference + between the current system and the desired configuration is + calculated and actions are generated to get to this state. There are + a lot of nuances that can be controlled by the units which are + explained here. + + + After calculating what should be done, the actions are carried out. + The order of actions is always the same: + + + + + Stop units (systemctl stop) + + + + + Run activation script ($out/activate) + + + + + See if the activation script requested more units to restart + + + + + Restart systemd if needed + (systemd daemon-reexec) + + + + + Forget about the failed state of units + (systemctl reset-failed) + + + + + Reload systemd (systemctl daemon-reload) + + + + + Reload systemd user instances + (systemctl --user daemon-reload) + + + + + Set up tmpfiles (systemd-tmpfiles --create) + + + + + Reload units (systemctl reload) + + + + + Restart units (systemctl restart) + + + + + Start units (systemctl start) + + + + + Inspect what changed during these actions and print units that + failed and that were newly started + + + + + Most of these actions are either self-explaining but some of them + have to do with our units or the activation script. For this reason, + these topics are explained in the next sections. + + + + -- cgit 1.4.1