···1+# Activation script {#sec-activation-script}
2+3+The activation script is a bash script called to activate the new
4+configuration which resides in a NixOS system in `$out/activate`. Since its
5+contents depend on your system configuration, the contents may differ.
6+This chapter explains how the script works in general and some common NixOS
7+snippets. Please be aware that the script is executed on every boot and system
8+switch, so tasks that can be performed in other places should be performed
9+there (for example letting a directory of a service be created by systemd using
10+mechanisms like `StateDirectory`, `CacheDirectory`, ... or if that's not
11+possible using `preStart` of the service).
12+13+Activation scripts are defined as snippets using
14+[](#opt-system.activationScripts). They can either be a simple multiline string
15+or an attribute set that can depend on other snippets. The builder for the
16+activation script will take these dependencies into account and order the
17+snippets accordingly. As a simple example:
18+19+```nix
20+system.activationScripts.my-activation-script = {
21+ deps = [ "etc" ];
22+ # supportsDryActivation = true;
23+ text = ''
24+ echo "Hallo i bims"
25+ '';
26+};
27+```
28+29+This example creates an activation script snippet that is run after the `etc`
30+snippet. The special variable `supportsDryActivation` can be set so the snippet
31+is also run when `nixos-rebuild dry-activate` is run. To differentiate between
32+real and dry activation, the `$NIXOS_ACTION` environment variable can be
33+read which is set to `dry-activate` when a dry activation is done.
34+35+An activation script can write to special files instructing
36+`switch-to-configuration` to restart/reload units. The script will take these
37+requests into account and will incorperate the unit configuration as described
38+above. This means that the activation script will "fake" a modified unit file
39+and `switch-to-configuration` will act accordingly. By doing so, configuration
40+like [systemd.services.\<name\>.restartIfChanged](#opt-systemd.services) is
41+respected. Since the activation script is run **after** services are already
42+stopped, [systemd.services.\<name\>.stopIfChanged](#opt-systemd.services)
43+cannot be taken into account anymore and the unit is always restarted instead
44+of being stopped and started afterwards.
45+46+The files that can be written to are `/run/nixos/activation-restart-list` and
47+`/run/nixos/activation-reload-list` with their respective counterparts for
48+dry activation being `/run/nixos/dry-activation-restart-list` and
49+`/run/nixos/dry-activation-reload-list`. Those files can contain
50+newline-separated lists of unit names where duplicates are being ignored. These
51+files are not create automatically and activation scripts must take the
52+possiblility into account that they have to create them first.
53+54+## NixOS snippets {#sec-activation-script-nixos-snippets}
55+56+There are some snippets NixOS enables by default because disabling them would
57+most likely break you system. This section lists a few of them and what they
58+do:
59+60+- `binsh` creates `/bin/sh` which points to the runtime shell
61+- `etc` sets up the contents of `/etc`, this includes systemd units and
62+ excludes `/etc/passwd`, `/etc/group`, and `/etc/shadow` (which are managed by
63+ the `users` snippet)
64+- `hostname` sets the system's hostname in the kernel (not in `/etc`)
65+- `modprobe` sets the path to the `modprobe` binary for module auto-loading
66+- `nix` prepares the nix store and adds a default initial channel
67+- `specialfs` is responsible for mounting filesystems like `/proc` and `sys`
68+- `users` creates and removes users and groups by managing `/etc/passwd`,
69+ `/etc/group` and `/etc/shadow`. This also creates home directories
70+- `usrbinenv` creates `/usr/bin/env`
71+- `var` creates some directories in `/var` that are not service-specific
72+- `wrappers` creates setuid wrappers like `ping` and `sudo`
···1+# Unit handling {#sec-unit-handling}
2+3+To figure out what units need to be started/stopped/restarted/reloaded, the
4+script first checks the current state of the system, similar to what `systemctl
5+list-units` shows. For each of the units, the script goes through the following
6+checks:
7+8+- Is the unit file still in the new system? If not, **stop** the service unless
9+ it sets `X-StopOnRemoval` in the `[Unit]` section to `false`.
10+11+- Is it a `.target` unit? If so, **start** it unless it sets
12+ `RefuseManualStart` in the `[Unit]` section to `true` or `X-OnlyManualStart`
13+ in the `[Unit]` section to `true`. Also **stop** the unit again unless it
14+ sets `X-StopOnReconfiguration` to `false`.
15+16+- Are the contents of the unit files different? They are compared by parsing
17+ them and comparing their contents. If they are different but only
18+ `X-Reload-Triggers` in the `[Unit]` section is changed, **reload** the unit.
19+ The NixOS module system allows setting these triggers with the option
20+ [systemd.services.\<name\>.reloadTriggers](#opt-systemd.services). If the
21+ unit files differ in any way, the following actions are performed:
22+23+ - `.path` and `.slice` units are ignored. There is no need to restart them
24+ since changes in their values are applied by systemd when systemd is
25+ reloaded.
26+27+ - `.mount` units are **reload**ed. These mostly come from the `/etc/fstab`
28+ parser.
29+30+ - `.socket` units are currently ignored. This is to be fixed at a later
31+ point.
32+33+ - The rest of the units (mostly `.service` units) are then **reload**ed if
34+ `X-ReloadIfChanged` in the `[Service]` section is set to `true` (exposed
35+ via [systemd.services.\<name\>.reloadIfChanged](#opt-systemd.services)).
36+37+ - If the reload flag is not set, some more flags decide if the unit is
38+ skipped. These flags are `X-RestartIfChanged` in the `[Service]` section
39+ (exposed via
40+ [systemd.services.\<name\>.restartIfChanged](#opt-systemd.services)),
41+ `RefuseManualStop` in the `[Unit]` section, and `X-OnlyManualStart` in the
42+ `[Unit]` section.
43+44+ - The rest of the behavior is decided whether the unit has `X-StopIfChanged`
45+ in the `[Service]` section set (exposed via
46+ [systemd.services.\<name\>.stopIfChanged](#opt-systemd.services)). This is
47+ set to `true` by default and must be explicitly turned off if not wanted.
48+ If the flag is enabled, the unit is **stop**ped and then **start**ed. If
49+ not, the unit is **restart**ed. The goal of the flag is to make sure that
50+ the new unit never runs in the old environment which is still in place
51+ before the activation script is run.
52+53+ - The last thing that is taken into account is whether the unit is a service
54+ and socket-activated. Due to a bug, this is currently only done when
55+ `X-StopIfChanged` is set. If the unit is socket-activated, the socket is
56+ stopped and started, and the service is stopped and to be started by socket
57+ activation.
···1+# What happens during a system switch? {#sec-switching-systems}
2+3+Running `nixos-rebuild switch` is one of the more common tasks under NixOS.
4+This chapter explains some of the internals of this command to make it simpler
5+for new module developers to configure their units correctly and to make it
6+easier to understand what is happening and why for curious administrators.
7+8+`nixos-rebuild`, like many deployment solutions, calls `switch-to-configuration`
9+which resides in a NixOS system at `$out/bin/switch-to-configuration`. The
10+script is called with the action that is to be performed like `switch`, `test`,
11+`boot`. There is also the `dry-activate` action which does not really perform
12+the actions but rather prints what it would do if you called it with `test`.
13+This feature can be used to check what service states would be changed if the
14+configuration was switched to.
15+16+If the action is `switch` or `boot`, the bootloader is updated first so the
17+configuration will be the next one to boot. Unless `NIXOS_NO_SYNC` is set to
18+`1`, `/nix/store` is synced to disk.
19+20+If the action is `switch` or `test`, the currently running system is inspected
21+and the actions to switch to the new system are calculated. This process takes
22+two data sources into account: `/etc/fstab` and the current systemd status.
23+Mounts and swaps are read from `/etc/fstab` and the corresponding actions are
24+generated. If a new mount is added, for example, the proper `.mount` unit is
25+marked to be started. The current systemd state is inspected, the difference
26+between the current system and the desired configuration is calculated and
27+actions are generated to get to this state. There are a lot of nuances that can
28+be controlled by the units which are explained here.
29+30+After calculating what should be done, the actions are carried out. The order
31+of actions is always the same:
32+- Stop units (`systemctl stop`)
33+- Run activation script (`$out/activate`)
34+- See if the activation script requested more units to restart
35+- Restart systemd if needed (`systemd daemon-reexec`)
36+- Forget about the failed state of units (`systemctl reset-failed`)
37+- Reload systemd (`systemctl daemon-reload`)
38+- Reload systemd user instances (`systemctl --user daemon-reload`)
39+- Set up tmpfiles (`systemd-tmpfiles --create`)
40+- Reload units (`systemctl reload`)
41+- Restart units (`systemctl restart`)
42+- Start units (`systemctl start`)
43+- Inspect what changed during these actions and print units that failed and
44+ that were newly started
45+46+Most of these actions are either self-explaining but some of them have to do
47+with our units or the activation script. For this reason, these topics are
48+explained in the next sections.
49+50+```{=docbook}
51+<xi:include href="unit-handling.section.xml" />
52+<xi:include href="activation-script.section.xml" />
53+```
···1+<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-activation-script">
2+ <title>Activation script</title>
3+ <para>
4+ The activation script is a bash script called to activate the new
5+ configuration which resides in a NixOS system in
6+ <literal>$out/activate</literal>. Since its contents depend on your
7+ system configuration, the contents may differ. This chapter explains
8+ how the script works in general and some common NixOS snippets.
9+ Please be aware that the script is executed on every boot and system
10+ switch, so tasks that can be performed in other places should be
11+ performed there (for example letting a directory of a service be
12+ created by systemd using mechanisms like
13+ <literal>StateDirectory</literal>,
14+ <literal>CacheDirectory</literal>, … or if that’s not possible using
15+ <literal>preStart</literal> of the service).
16+ </para>
17+ <para>
18+ Activation scripts are defined as snippets using
19+ <xref linkend="opt-system.activationScripts" />. They can either be
20+ a simple multiline string or an attribute set that can depend on
21+ other snippets. The builder for the activation script will take
22+ these dependencies into account and order the snippets accordingly.
23+ As a simple example:
24+ </para>
25+ <programlisting language="bash">
26+system.activationScripts.my-activation-script = {
27+ deps = [ "etc" ];
28+ # supportsDryActivation = true;
29+ text = ''
30+ echo "Hallo i bims"
31+ '';
32+};
33+</programlisting>
34+ <para>
35+ This example creates an activation script snippet that is run after
36+ the <literal>etc</literal> snippet. The special variable
37+ <literal>supportsDryActivation</literal> can be set so the snippet
38+ is also run when <literal>nixos-rebuild dry-activate</literal> is
39+ run. To differentiate between real and dry activation, the
40+ <literal>$NIXOS_ACTION</literal> environment variable can be read
41+ which is set to <literal>dry-activate</literal> when a dry
42+ activation is done.
43+ </para>
44+ <para>
45+ An activation script can write to special files instructing
46+ <literal>switch-to-configuration</literal> to restart/reload units.
47+ The script will take these requests into account and will
48+ incorperate the unit configuration as described above. This means
49+ that the activation script will <quote>fake</quote> a modified unit
50+ file and <literal>switch-to-configuration</literal> will act
51+ accordingly. By doing so, configuration like
52+ <link linkend="opt-systemd.services">systemd.services.<name>.restartIfChanged</link>
53+ is respected. Since the activation script is run
54+ <emphasis role="strong">after</emphasis> services are already
55+ stopped,
56+ <link linkend="opt-systemd.services">systemd.services.<name>.stopIfChanged</link>
57+ cannot be taken into account anymore and the unit is always
58+ restarted instead of being stopped and started afterwards.
59+ </para>
60+ <para>
61+ The files that can be written to are
62+ <literal>/run/nixos/activation-restart-list</literal> and
63+ <literal>/run/nixos/activation-reload-list</literal> with their
64+ respective counterparts for dry activation being
65+ <literal>/run/nixos/dry-activation-restart-list</literal> and
66+ <literal>/run/nixos/dry-activation-reload-list</literal>. Those
67+ files can contain newline-separated lists of unit names where
68+ duplicates are being ignored. These files are not create
69+ automatically and activation scripts must take the possiblility into
70+ account that they have to create them first.
71+ </para>
72+ <section xml:id="sec-activation-script-nixos-snippets">
73+ <title>NixOS snippets</title>
74+ <para>
75+ There are some snippets NixOS enables by default because disabling
76+ them would most likely break you system. This section lists a few
77+ of them and what they do:
78+ </para>
79+ <itemizedlist spacing="compact">
80+ <listitem>
81+ <para>
82+ <literal>binsh</literal> creates <literal>/bin/sh</literal>
83+ which points to the runtime shell
84+ </para>
85+ </listitem>
86+ <listitem>
87+ <para>
88+ <literal>etc</literal> sets up the contents of
89+ <literal>/etc</literal>, this includes systemd units and
90+ excludes <literal>/etc/passwd</literal>,
91+ <literal>/etc/group</literal>, and
92+ <literal>/etc/shadow</literal> (which are managed by the
93+ <literal>users</literal> snippet)
94+ </para>
95+ </listitem>
96+ <listitem>
97+ <para>
98+ <literal>hostname</literal> sets the system’s hostname in the
99+ kernel (not in <literal>/etc</literal>)
100+ </para>
101+ </listitem>
102+ <listitem>
103+ <para>
104+ <literal>modprobe</literal> sets the path to the
105+ <literal>modprobe</literal> binary for module auto-loading
106+ </para>
107+ </listitem>
108+ <listitem>
109+ <para>
110+ <literal>nix</literal> prepares the nix store and adds a
111+ default initial channel
112+ </para>
113+ </listitem>
114+ <listitem>
115+ <para>
116+ <literal>specialfs</literal> is responsible for mounting
117+ filesystems like <literal>/proc</literal> and
118+ <literal>sys</literal>
119+ </para>
120+ </listitem>
121+ <listitem>
122+ <para>
123+ <literal>users</literal> creates and removes users and groups
124+ by managing <literal>/etc/passwd</literal>,
125+ <literal>/etc/group</literal> and
126+ <literal>/etc/shadow</literal>. This also creates home
127+ directories
128+ </para>
129+ </listitem>
130+ <listitem>
131+ <para>
132+ <literal>usrbinenv</literal> creates
133+ <literal>/usr/bin/env</literal>
134+ </para>
135+ </listitem>
136+ <listitem>
137+ <para>
138+ <literal>var</literal> creates some directories in
139+ <literal>/var</literal> that are not service-specific
140+ </para>
141+ </listitem>
142+ <listitem>
143+ <para>
144+ <literal>wrappers</literal> creates setuid wrappers like
145+ <literal>ping</literal> and <literal>sudo</literal>
146+ </para>
147+ </listitem>
148+ </itemizedlist>
149+ </section>
150+</section>
···1+<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-unit-handling">
2+ <title>Unit handling</title>
3+ <para>
4+ To figure out what units need to be
5+ started/stopped/restarted/reloaded, the script first checks the
6+ current state of the system, similar to what
7+ <literal>systemctl list-units</literal> shows. For each of the
8+ units, the script goes through the following checks:
9+ </para>
10+ <itemizedlist>
11+ <listitem>
12+ <para>
13+ Is the unit file still in the new system? If not,
14+ <emphasis role="strong">stop</emphasis> the service unless it
15+ sets <literal>X-StopOnRemoval</literal> in the
16+ <literal>[Unit]</literal> section to <literal>false</literal>.
17+ </para>
18+ </listitem>
19+ <listitem>
20+ <para>
21+ Is it a <literal>.target</literal> unit? If so,
22+ <emphasis role="strong">start</emphasis> it unless it sets
23+ <literal>RefuseManualStart</literal> in the
24+ <literal>[Unit]</literal> section to <literal>true</literal> or
25+ <literal>X-OnlyManualStart</literal> in the
26+ <literal>[Unit]</literal> section to <literal>true</literal>.
27+ Also <emphasis role="strong">stop</emphasis> the unit again
28+ unless it sets <literal>X-StopOnReconfiguration</literal> to
29+ <literal>false</literal>.
30+ </para>
31+ </listitem>
32+ <listitem>
33+ <para>
34+ Are the contents of the unit files different? They are compared
35+ by parsing them and comparing their contents. If they are
36+ different but only <literal>X-Reload-Triggers</literal> in the
37+ <literal>[Unit]</literal> section is changed,
38+ <emphasis role="strong">reload</emphasis> the unit. The NixOS
39+ module system allows setting these triggers with the option
40+ <link linkend="opt-systemd.services">systemd.services.<name>.reloadTriggers</link>.
41+ If the unit files differ in any way, the following actions are
42+ performed:
43+ </para>
44+ <itemizedlist>
45+ <listitem>
46+ <para>
47+ <literal>.path</literal> and <literal>.slice</literal> units
48+ are ignored. There is no need to restart them since changes
49+ in their values are applied by systemd when systemd is
50+ reloaded.
51+ </para>
52+ </listitem>
53+ <listitem>
54+ <para>
55+ <literal>.mount</literal> units are
56+ <emphasis role="strong">reload</emphasis>ed. These mostly
57+ come from the <literal>/etc/fstab</literal> parser.
58+ </para>
59+ </listitem>
60+ <listitem>
61+ <para>
62+ <literal>.socket</literal> units are currently ignored. This
63+ is to be fixed at a later point.
64+ </para>
65+ </listitem>
66+ <listitem>
67+ <para>
68+ The rest of the units (mostly <literal>.service</literal>
69+ units) are then <emphasis role="strong">reload</emphasis>ed
70+ if <literal>X-ReloadIfChanged</literal> in the
71+ <literal>[Service]</literal> section is set to
72+ <literal>true</literal> (exposed via
73+ <link linkend="opt-systemd.services">systemd.services.<name>.reloadIfChanged</link>).
74+ </para>
75+ </listitem>
76+ <listitem>
77+ <para>
78+ If the reload flag is not set, some more flags decide if the
79+ unit is skipped. These flags are
80+ <literal>X-RestartIfChanged</literal> in the
81+ <literal>[Service]</literal> section (exposed via
82+ <link linkend="opt-systemd.services">systemd.services.<name>.restartIfChanged</link>),
83+ <literal>RefuseManualStop</literal> in the
84+ <literal>[Unit]</literal> section, and
85+ <literal>X-OnlyManualStart</literal> in the
86+ <literal>[Unit]</literal> section.
87+ </para>
88+ </listitem>
89+ <listitem>
90+ <para>
91+ The rest of the behavior is decided whether the unit has
92+ <literal>X-StopIfChanged</literal> in the
93+ <literal>[Service]</literal> section set (exposed via
94+ <link linkend="opt-systemd.services">systemd.services.<name>.stopIfChanged</link>).
95+ This is set to <literal>true</literal> by default and must
96+ be explicitly turned off if not wanted. If the flag is
97+ enabled, the unit is
98+ <emphasis role="strong">stop</emphasis>ped and then
99+ <emphasis role="strong">start</emphasis>ed. If not, the unit
100+ is <emphasis role="strong">restart</emphasis>ed. The goal of
101+ the flag is to make sure that the new unit never runs in the
102+ old environment which is still in place before the
103+ activation script is run.
104+ </para>
105+ </listitem>
106+ <listitem>
107+ <para>
108+ The last thing that is taken into account is whether the
109+ unit is a service and socket-activated. Due to a bug, this
110+ is currently only done when
111+ <literal>X-StopIfChanged</literal> is set. If the unit is
112+ socket-activated, the socket is stopped and started, and the
113+ service is stopped and to be started by socket activation.
114+ </para>
115+ </listitem>
116+ </itemizedlist>
117+ </listitem>
118+ </itemizedlist>
119+</section>
···1+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" xml:id="sec-switching-systems">
2+ <title>What happens during a system switch?</title>
3+ <para>
4+ Running <literal>nixos-rebuild switch</literal> is one of the more
5+ common tasks under NixOS. This chapter explains some of the
6+ internals of this command to make it simpler for new module
7+ developers to configure their units correctly and to make it easier
8+ to understand what is happening and why for curious administrators.
9+ </para>
10+ <para>
11+ <literal>nixos-rebuild</literal>, like many deployment solutions,
12+ calls <literal>switch-to-configuration</literal> which resides in a
13+ NixOS system at <literal>$out/bin/switch-to-configuration</literal>.
14+ The script is called with the action that is to be performed like
15+ <literal>switch</literal>, <literal>test</literal>,
16+ <literal>boot</literal>. There is also the
17+ <literal>dry-activate</literal> action which does not really perform
18+ the actions but rather prints what it would do if you called it with
19+ <literal>test</literal>. This feature can be used to check what
20+ service states would be changed if the configuration was switched
21+ to.
22+ </para>
23+ <para>
24+ If the action is <literal>switch</literal> or
25+ <literal>boot</literal>, the bootloader is updated first so the
26+ configuration will be the next one to boot. Unless
27+ <literal>NIXOS_NO_SYNC</literal> is set to <literal>1</literal>,
28+ <literal>/nix/store</literal> is synced to disk.
29+ </para>
30+ <para>
31+ If the action is <literal>switch</literal> or
32+ <literal>test</literal>, the currently running system is inspected
33+ and the actions to switch to the new system are calculated. This
34+ process takes two data sources into account:
35+ <literal>/etc/fstab</literal> and the current systemd status. Mounts
36+ and swaps are read from <literal>/etc/fstab</literal> and the
37+ corresponding actions are generated. If a new mount is added, for
38+ example, the proper <literal>.mount</literal> unit is marked to be
39+ started. The current systemd state is inspected, the difference
40+ between the current system and the desired configuration is
41+ calculated and actions are generated to get to this state. There are
42+ a lot of nuances that can be controlled by the units which are
43+ explained here.
44+ </para>
45+ <para>
46+ After calculating what should be done, the actions are carried out.
47+ The order of actions is always the same:
48+ </para>
49+ <itemizedlist spacing="compact">
50+ <listitem>
51+ <para>
52+ Stop units (<literal>systemctl stop</literal>)
53+ </para>
54+ </listitem>
55+ <listitem>
56+ <para>
57+ Run activation script (<literal>$out/activate</literal>)
58+ </para>
59+ </listitem>
60+ <listitem>
61+ <para>
62+ See if the activation script requested more units to restart
63+ </para>
64+ </listitem>
65+ <listitem>
66+ <para>
67+ Restart systemd if needed
68+ (<literal>systemd daemon-reexec</literal>)
69+ </para>
70+ </listitem>
71+ <listitem>
72+ <para>
73+ Forget about the failed state of units
74+ (<literal>systemctl reset-failed</literal>)
75+ </para>
76+ </listitem>
77+ <listitem>
78+ <para>
79+ Reload systemd (<literal>systemctl daemon-reload</literal>)
80+ </para>
81+ </listitem>
82+ <listitem>
83+ <para>
84+ Reload systemd user instances
85+ (<literal>systemctl --user daemon-reload</literal>)
86+ </para>
87+ </listitem>
88+ <listitem>
89+ <para>
90+ Set up tmpfiles (<literal>systemd-tmpfiles --create</literal>)
91+ </para>
92+ </listitem>
93+ <listitem>
94+ <para>
95+ Reload units (<literal>systemctl reload</literal>)
96+ </para>
97+ </listitem>
98+ <listitem>
99+ <para>
100+ Restart units (<literal>systemctl restart</literal>)
101+ </para>
102+ </listitem>
103+ <listitem>
104+ <para>
105+ Start units (<literal>systemctl start</literal>)
106+ </para>
107+ </listitem>
108+ <listitem>
109+ <para>
110+ Inspect what changed during these actions and print units that
111+ failed and that were newly started
112+ </para>
113+ </listitem>
114+ </itemizedlist>
115+ <para>
116+ Most of these actions are either self-explaining but some of them
117+ have to do with our units or the activation script. For this reason,
118+ these topics are explained in the next sections.
119+ </para>
120+ <xi:include href="unit-handling.section.xml" />
121+ <xi:include href="activation-script.section.xml" />
122+</chapter>
···42 upgrade notes</link>.
43 </para>
44 </listitem>
45+ <listitem>
46+ <para>
47+ systemd services can now set
48+ <link linkend="opt-systemd.services">systemd.services.<name>.reloadTriggers</link>
49+ instead of <literal>reloadIfChanged</literal> for a more
50+ granular distinction between reloads and restarts.
51+ </para>
52+ </listitem>
53 </itemizedlist>
54 </section>
55 <section xml:id="sec-release-22.05-new-services">
···558 honors <literal>restartIfChanged</literal> and
559 <literal>reloadIfChanged</literal> of the units.
560 </para>
561+ <itemizedlist spacing="compact">
562+ <listitem>
563+ <para>
564+ Preferring to reload instead of restarting can still
565+ be achieved using
566+ <literal>/run/nixos/activation-reload-list</literal>.
567+ </para>
568+ </listitem>
569+ </itemizedlist>
570 </listitem>
571 <listitem>
572 <para>
+3
nixos/doc/manual/release-notes/rl-2205.section.md
···17 Migrations may take a while, see the [changelog](https://docs.mattermost.com/install/self-managed-changelog.html#release-v6-3-extended-support-release)
18 and [important upgrade notes](https://docs.mattermost.com/upgrade/important-upgrade-notes.html).
190020## New Services {#sec-release-22.05-new-services}
2122- [aesmd](https://github.com/intel/linux-sgx#install-the-intelr-sgx-psw), the Intel SGX Architectural Enclave Service Manager. Available as [services.aesmd](#opt-services.aesmd.enable).
···179180- `switch-to-configuration` (the script that is run when running `nixos-rebuild switch` for example) has been reworked
181 * The interface that allows activation scripts to restart units has been streamlined. Restarting and reloading is now done by a single file `/run/nixos/activation-restart-list` that honors `restartIfChanged` and `reloadIfChanged` of the units.
0182 * The script now uses a proper ini-file parser to parse systemd units. Some values are now only searched in one section instead of in the entire unit. This is only relevant for units that don't use the NixOS systemd moule.
183 * `RefuseManualStop`, `X-OnlyManualStart`, `X-StopOnRemoval`, `X-StopOnReconfiguration` are only searched in the `[Unit]` section
184 * `X-ReloadIfChanged`, `X-RestartIfChanged`, `X-StopIfChanged` are only searched in the `[Service]` section
···17 Migrations may take a while, see the [changelog](https://docs.mattermost.com/install/self-managed-changelog.html#release-v6-3-extended-support-release)
18 and [important upgrade notes](https://docs.mattermost.com/upgrade/important-upgrade-notes.html).
1920+- systemd services can now set [systemd.services.\<name\>.reloadTriggers](#opt-systemd.services) instead of `reloadIfChanged` for a more granular distinction between reloads and restarts.
21+22## New Services {#sec-release-22.05-new-services}
2324- [aesmd](https://github.com/intel/linux-sgx#install-the-intelr-sgx-psw), the Intel SGX Architectural Enclave Service Manager. Available as [services.aesmd](#opt-services.aesmd.enable).
···181182- `switch-to-configuration` (the script that is run when running `nixos-rebuild switch` for example) has been reworked
183 * The interface that allows activation scripts to restart units has been streamlined. Restarting and reloading is now done by a single file `/run/nixos/activation-restart-list` that honors `restartIfChanged` and `reloadIfChanged` of the units.
184+ * Preferring to reload instead of restarting can still be achieved using `/run/nixos/activation-reload-list`.
185 * The script now uses a proper ini-file parser to parse systemd units. Some values are now only searched in one section instead of in the entire unit. This is only relevant for units that don't use the NixOS systemd moule.
186 * `RefuseManualStop`, `X-OnlyManualStart`, `X-StopOnRemoval`, `X-StopOnReconfiguration` are only searched in the `[Unit]` section
187 * `X-ReloadIfChanged`, `X-RestartIfChanged`, `X-StopIfChanged` are only searched in the `[Service]` section
+16
nixos/lib/systemd-unit-options.nix
···201 '';
202 };
20300000000000204 onFailure = mkOption {
205 default = [];
206 type = types.listOf unitNameType;
···338 configuration switch if its definition has changed. If
339 enabled, the value of <option>restartIfChanged</option> is
340 ignored.
00000341 '';
342 };
343
···201 '';
202 };
203204+ reloadTriggers = mkOption {
205+ default = [];
206+ type = types.listOf unitOption;
207+ description = ''
208+ An arbitrary list of items such as derivations. If any item
209+ in the list changes between reconfigurations, the service will
210+ be reloaded. If anything but a reload trigger changes in the
211+ unit file, the unit will be restarted instead.
212+ '';
213+ };
214+215 onFailure = mkOption {
216 default = [];
217 type = types.listOf unitNameType;
···349 configuration switch if its definition has changed. If
350 enabled, the value of <option>restartIfChanged</option> is
351 ignored.
352+353+ This option should not be used anymore in favor of
354+ <option>reloadTriggers</option> which allows more granular
355+ control of when a service is reloaded and when a service
356+ is restarted.
357 '';
358 };
359
···23use strict;
4use warnings;
05use Config::IniFiles;
6use File::Path qw(make_path);
7use File::Basename;
8-use File::Slurp;
9use Net::DBus;
10use Sys::Syslog qw(:standard :macros);
11use Cwd 'abs_path';
···20my $reloadListFile = "/run/nixos/reload-list";
2122# Parse restart/reload requests by the activation script.
23-# Activation scripts may write newline-separated units to this
24# file and switch-to-configuration will handle them. While
25# `stopIfChanged = true` is ignored, switch-to-configuration will
26# handle `restartIfChanged = false` and `reloadIfChanged = true`.
0000027my $restartByActivationFile = "/run/nixos/activation-restart-list";
028my $dryRestartByActivationFile = "/run/nixos/dry-activation-restart-list";
02930make_path("/run/nixos", { mode => oct(755) });
31···131132 # Copy over all sections
133 foreach my $sectionName (keys %fileContents) {
0000134 # Copy over all keys
135 foreach my $iniKey (keys %{$fileContents{$sectionName}}) {
136 # Ensure the value is an array so it's easier to work with
···192 write_file($fn, { append => 1 }, "$unit\n") if $action ne "dry-activate";
193}
194195-# As a fingerprint for determining whether a unit has changed, we use
196-# its absolute path. If it has an override file, we append *its*
197-# absolute path as well.
198-sub fingerprintUnit {
199- my ($s) = @_;
200- return abs_path($s) . (-f "${s}.d/overrides.conf" ? " " . abs_path "${s}.d/overrides.conf" : "");
0000000000000000000000000000000000000000000000000000000000000000000000000000201}
202203sub handleModifiedUnit {
204- my ($unit, $baseName, $newUnitFile, $activePrev, $unitsToStop, $unitsToStart, $unitsToReload, $unitsToRestart, $unitsToSkip) = @_;
205206 if ($unit eq "sysinit.target" || $unit eq "basic.target" || $unit eq "multi-user.target" || $unit eq "graphical.target" || $unit =~ /\.path$/ || $unit =~ /\.slice$/) {
207 # Do nothing. These cannot be restarted directly.
···219 # Revert of the attempt: https://github.com/NixOS/nixpkgs/pull/147609
220 # More details: https://github.com/NixOS/nixpkgs/issues/74899#issuecomment-981142430
221 } else {
222- my %unitInfo = parseUnit($newUnitFile);
223- if (parseSystemdBool(\%unitInfo, "Service", "X-ReloadIfChanged", 0)) {
224 $unitsToReload->{$unit} = 1;
225 recordUnit($reloadListFile, $unit);
226 }
···234 # stopped and started.
235 $unitsToRestart->{$unit} = 1;
236 recordUnit($restartListFile, $unit);
00000237 } else {
238 # If this unit is socket-activated, then stop the
239 # socket unit(s) as well, and restart the
···253 $unitsToStart->{$socket} = 1;
254 recordUnit($startListFile, $socket);
255 $socketActivated = 1;
00000256 }
257 }
258 }
···268 }
269270 $unitsToStop->{$unit} = 1;
00000271 }
272 }
273 }
···344 }
345 }
346347- elsif (fingerprintUnit($prevUnitFile) ne fingerprintUnit($newUnitFile)) {
348- handleModifiedUnit($unit, $baseName, $newUnitFile, $activePrev, \%unitsToStop, \%unitsToStart, \%unitsToReload, \%unitsToRestart, \%unitsToSkip);
00000000349 }
350 }
351}
···359 chomp $escaped;
360 close $cmd or die;
361 return $escaped;
362-}
363-364-sub unique {
365- my %seen;
366- my @res;
367- foreach my $name (@_) {
368- next if $seen{$name};
369- $seen{$name} = 1;
370- push @res, $name;
371- }
372- return @res;
373}
374375# Compare the previous and new fstab to figure out which filesystems
···407 # "systemctl stop" here because systemd has lots of alias
408 # units that prevent a stop from actually calling
409 # "swapoff".
410- print STDERR "stopping swap device: $device\n";
411- system("@utillinux@/sbin/swapoff", $device);
0000412 }
413 # FIXME: update swap options (i.e. its priority).
414}
···469 next;
470 }
471472- handleModifiedUnit($unit, $baseName, $newUnitFile, $activePrev, \%unitsToRestart, \%unitsToRestart, \%unitsToReload, \%unitsToRestart, \%unitsToSkip);
473 }
474 unlink($dryRestartByActivationFile);
4750000000000476 print STDERR "would restart systemd\n" if $restartSystemd;
477 print STDERR "would reload the following units: ", join(", ", sort(keys %unitsToReload)), "\n"
478 if scalar(keys %unitsToReload) > 0;
···525 next;
526 }
527528- handleModifiedUnit($unit, $baseName, $newUnitFile, $activePrev, \%unitsToRestart, \%unitsToRestart, \%unitsToReload, \%unitsToRestart, \%unitsToSkip);
529}
530# We can remove the file now because it has been propagated to the other restart/reload files
531unlink($restartByActivationFile);
00000000000532533# Restart systemd if necessary. Note that this is done using the
534# current version of systemd, just in case the new one has trouble
···23use strict;
4use warnings;
5+use Array::Compare;
6use Config::IniFiles;
7use File::Path qw(make_path);
8use File::Basename;
9+use File::Slurp qw(read_file write_file edit_file);
10use Net::DBus;
11use Sys::Syslog qw(:standard :macros);
12use Cwd 'abs_path';
···21my $reloadListFile = "/run/nixos/reload-list";
2223# Parse restart/reload requests by the activation script.
24+# Activation scripts may write newline-separated units to the restart
25# file and switch-to-configuration will handle them. While
26# `stopIfChanged = true` is ignored, switch-to-configuration will
27# handle `restartIfChanged = false` and `reloadIfChanged = true`.
28+# This is the same as specifying a restart trigger in the NixOS module.
29+#
30+# The reload file asks the script to reload a unit. This is the same as
31+# specifying a reload trigger in the NixOS module and can be ignored if
32+# the unit is restarted in this activation.
33my $restartByActivationFile = "/run/nixos/activation-restart-list";
34+my $reloadByActivationFile = "/run/nixos/activation-reload-list";
35my $dryRestartByActivationFile = "/run/nixos/dry-activation-restart-list";
36+my $dryReloadByActivationFile = "/run/nixos/dry-activation-reload-list";
3738make_path("/run/nixos", { mode => oct(755) });
39···139140 # Copy over all sections
141 foreach my $sectionName (keys %fileContents) {
142+ if ($sectionName eq "Install") {
143+ # Skip the [Install] section because it has no relevant keys for us
144+ next;
145+ }
146 # Copy over all keys
147 foreach my $iniKey (keys %{$fileContents{$sectionName}}) {
148 # Ensure the value is an array so it's easier to work with
···204 write_file($fn, { append => 1 }, "$unit\n") if $action ne "dry-activate";
205}
206207+# The opposite of recordUnit, removes a unit name from a file
208+sub unrecord_unit {
209+ my ($fn, $unit) = @_;
210+ edit_file { s/^$unit\n//msx } $fn if $action ne "dry-activate";
211+}
212+213+# Compare the contents of two unit files and return whether the unit
214+# needs to be restarted or reloaded. If the units differ, the service
215+# is restarted unless the only difference is `X-Reload-Triggers` in the
216+# `Unit` section. If this is the only modification, the unit is reloaded
217+# instead of restarted.
218+# Returns:
219+# - 0 if the units are equal
220+# - 1 if the units are different and a restart action is required
221+# - 2 if the units are different and a reload action is required
222+sub compare_units {
223+ my ($old_unit, $new_unit) = @_;
224+ my $comp = Array::Compare->new;
225+ my $ret = 0;
226+227+ # Comparison hash for the sections
228+ my %section_cmp = map { $_ => 1 } keys %{$new_unit};
229+ # Iterate over the sections
230+ foreach my $section_name (keys %{$old_unit}) {
231+ # Missing section in the new unit?
232+ if (not exists $section_cmp{$section_name}) {
233+ if ($section_name eq 'Unit' and %{$old_unit->{'Unit'}} == 1 and defined(%{$old_unit->{'Unit'}}{'X-Reload-Triggers'})) {
234+ # If a new [Unit] section was removed that only contained X-Reload-Triggers,
235+ # do nothing.
236+ next;
237+ } else {
238+ return 1;
239+ }
240+ }
241+ delete $section_cmp{$section_name};
242+ # Comparison hash for the section contents
243+ my %ini_cmp = map { $_ => 1 } keys %{$new_unit->{$section_name}};
244+ # Iterate over the keys of the section
245+ foreach my $ini_key (keys %{$old_unit->{$section_name}}) {
246+ delete $ini_cmp{$ini_key};
247+ my @old_value = @{$old_unit->{$section_name}{$ini_key}};
248+ # If the key is missing in the new unit, they are different...
249+ if (not $new_unit->{$section_name}{$ini_key}) {
250+ # ... unless the key that is now missing was the reload trigger
251+ if ($section_name eq 'Unit' and $ini_key eq 'X-Reload-Triggers') {
252+ next;
253+ }
254+ return 1;
255+ }
256+ my @new_value = @{$new_unit->{$section_name}{$ini_key}};
257+ # If the contents are different, the units are different
258+ if (not $comp->compare(\@old_value, \@new_value)) {
259+ # Check if only the reload triggers changed
260+ if ($section_name eq 'Unit' and $ini_key eq 'X-Reload-Triggers') {
261+ $ret = 2;
262+ } else {
263+ return 1;
264+ }
265+ }
266+ }
267+ # A key was introduced that was missing in the old unit
268+ if (%ini_cmp) {
269+ if ($section_name eq 'Unit' and %ini_cmp == 1 and defined($ini_cmp{'X-Reload-Triggers'})) {
270+ # If the newly introduced key was the reload triggers, reload the unit
271+ $ret = 2;
272+ } else {
273+ return 1;
274+ }
275+ };
276+ }
277+ # A section was introduced that was missing in the old unit
278+ if (%section_cmp) {
279+ if (%section_cmp == 1 and defined($section_cmp{'Unit'}) and %{$new_unit->{'Unit'}} == 1 and defined(%{$new_unit->{'Unit'}}{'X-Reload-Triggers'})) {
280+ # If a new [Unit] section was introduced that only contains X-Reload-Triggers,
281+ # reload instead of restarting
282+ $ret = 2;
283+ } else {
284+ return 1;
285+ }
286+ }
287+288+ return $ret;
289}
290291sub handleModifiedUnit {
292+ my ($unit, $baseName, $newUnitFile, $newUnitInfo, $activePrev, $unitsToStop, $unitsToStart, $unitsToReload, $unitsToRestart, $unitsToSkip) = @_;
293294 if ($unit eq "sysinit.target" || $unit eq "basic.target" || $unit eq "multi-user.target" || $unit eq "graphical.target" || $unit =~ /\.path$/ || $unit =~ /\.slice$/) {
295 # Do nothing. These cannot be restarted directly.
···307 # Revert of the attempt: https://github.com/NixOS/nixpkgs/pull/147609
308 # More details: https://github.com/NixOS/nixpkgs/issues/74899#issuecomment-981142430
309 } else {
310+ my %unitInfo = $newUnitInfo ? %{$newUnitInfo} : parseUnit($newUnitFile);
311+ if (parseSystemdBool(\%unitInfo, "Service", "X-ReloadIfChanged", 0) and not $unitsToRestart->{$unit} and not $unitsToStop->{$unit}) {
312 $unitsToReload->{$unit} = 1;
313 recordUnit($reloadListFile, $unit);
314 }
···322 # stopped and started.
323 $unitsToRestart->{$unit} = 1;
324 recordUnit($restartListFile, $unit);
325+ # Remove from units to reload so we don't restart and reload
326+ if ($unitsToReload->{$unit}) {
327+ delete $unitsToReload->{$unit};
328+ unrecord_unit($reloadListFile, $unit);
329+ }
330 } else {
331 # If this unit is socket-activated, then stop the
332 # socket unit(s) as well, and restart the
···346 $unitsToStart->{$socket} = 1;
347 recordUnit($startListFile, $socket);
348 $socketActivated = 1;
349+ }
350+ # Remove from units to reload so we don't restart and reload
351+ if ($unitsToReload->{$unit}) {
352+ delete $unitsToReload->{$unit};
353+ unrecord_unit($reloadListFile, $unit);
354 }
355 }
356 }
···366 }
367368 $unitsToStop->{$unit} = 1;
369+ # Remove from units to reload so we don't restart and reload
370+ if ($unitsToReload->{$unit}) {
371+ delete $unitsToReload->{$unit};
372+ unrecord_unit($reloadListFile, $unit);
373+ }
374 }
375 }
376 }
···447 }
448 }
449450+ else {
451+ my %old_unit_info = parseUnit($prevUnitFile);
452+ my %new_unit_info = parseUnit($newUnitFile);
453+ my $diff = compare_units(\%old_unit_info, \%new_unit_info);
454+ if ($diff eq 1) {
455+ handleModifiedUnit($unit, $baseName, $newUnitFile, \%new_unit_info, $activePrev, \%unitsToStop, \%unitsToStart, \%unitsToReload, \%unitsToRestart, \%unitsToSkip);
456+ } elsif ($diff eq 2 and not $unitsToRestart{$unit}) {
457+ $unitsToReload{$unit} = 1;
458+ recordUnit($reloadListFile, $unit);
459+ }
460 }
461 }
462}
···470 chomp $escaped;
471 close $cmd or die;
472 return $escaped;
00000000000473}
474475# Compare the previous and new fstab to figure out which filesystems
···507 # "systemctl stop" here because systemd has lots of alias
508 # units that prevent a stop from actually calling
509 # "swapoff".
510+ if ($action ne "dry-activate") {
511+ print STDERR "would stop swap device: $device\n";
512+ } else {
513+ print STDERR "stopping swap device: $device\n";
514+ system("@utillinux@/sbin/swapoff", $device);
515+ }
516 }
517 # FIXME: update swap options (i.e. its priority).
518}
···573 next;
574 }
575576+ handleModifiedUnit($unit, $baseName, $newUnitFile, undef, $activePrev, \%unitsToRestart, \%unitsToRestart, \%unitsToReload, \%unitsToRestart, \%unitsToSkip);
577 }
578 unlink($dryRestartByActivationFile);
579580+ foreach (split('\n', read_file($dryReloadByActivationFile, err_mode => 'quiet') // "")) {
581+ my $unit = $_;
582+583+ if (defined($activePrev->{$unit}) and not $unitsToRestart{$unit} and not $unitsToStop{$unit}) {
584+ $unitsToReload{$unit} = 1;
585+ recordUnit($reloadListFile, $unit);
586+ }
587+ }
588+ unlink($dryReloadByActivationFile);
589+590 print STDERR "would restart systemd\n" if $restartSystemd;
591 print STDERR "would reload the following units: ", join(", ", sort(keys %unitsToReload)), "\n"
592 if scalar(keys %unitsToReload) > 0;
···639 next;
640 }
641642+ handleModifiedUnit($unit, $baseName, $newUnitFile, undef, $activePrev, \%unitsToRestart, \%unitsToRestart, \%unitsToReload, \%unitsToRestart, \%unitsToSkip);
643}
644# We can remove the file now because it has been propagated to the other restart/reload files
645unlink($restartByActivationFile);
646+647+foreach (split('\n', read_file($reloadByActivationFile, err_mode => 'quiet') // "")) {
648+ my $unit = $_;
649+650+ if (defined($activePrev->{$unit}) and not $unitsToRestart{$unit} and not $unitsToStop{$unit}) {
651+ $unitsToReload{$unit} = 1;
652+ recordUnit($reloadListFile, $unit);
653+ }
654+}
655+# We can remove the file now because it has been propagated to the other reload file
656+unlink($reloadByActivationFile);
657658# Restart systemd if necessary. Note that this is done using the
659# current version of systemd, just in case the new one has trouble
···117 configurationName = config.boot.loader.grub.configurationName;
118119 # Needed by switch-to-configuration.
120+ perl = pkgs.perl.withPackages (p: with p; [ ArrayCompare ConfigIniFiles FileSlurp NetDBus ]);
121 };
122123 # Handle assertions and warnings
+5
nixos/modules/system/boot/systemd.nix
···243 { Requisite = toString config.requisite; }
244 // optionalAttrs (config.restartTriggers != [])
245 { X-Restart-Triggers = toString config.restartTriggers; }
00246 // optionalAttrs (config.description != "") {
247 Description = config.description; }
248 // optionalAttrs (config.documentation != []) {
···916 )
917 (optional hasDeprecated
918 "Service '${name}.service' uses the attribute 'StartLimitInterval' in the Service section, which is deprecated. See https://github.com/NixOS/nixpkgs/issues/45786."
000919 )
920 ]
921 )
···243 { Requisite = toString config.requisite; }
244 // optionalAttrs (config.restartTriggers != [])
245 { X-Restart-Triggers = toString config.restartTriggers; }
246+ // optionalAttrs (config.reloadTriggers != [])
247+ { X-Reload-Triggers = toString config.reloadTriggers; }
248 // optionalAttrs (config.description != "") {
249 Description = config.description; }
250 // optionalAttrs (config.documentation != []) {
···918 )
919 (optional hasDeprecated
920 "Service '${name}.service' uses the attribute 'StartLimitInterval' in the Service section, which is deprecated. See https://github.com/NixOS/nixpkgs/issues/45786."
921+ )
922+ (optional (service.reloadIfChanged && service.reloadTriggers != [])
923+ "Service '${name}.service' has both 'reloadIfChanged' and 'reloadTriggers' set. This is probably not what you want, because 'reloadTriggers' behave the same whay as 'restartTriggers' if 'reloadIfChanged' is set."
924 )
925 ]
926 )
+241-6
nixos/tests/switch-test.nix
···18 Type = "oneshot";
19 RemainAfterExit = true;
20 ExecStart = "${pkgs.coreutils}/bin/true";
021 };
22 };
23 };
···70 };
71 };
720000000000000000000000000000000000000000000000000000000000000000000000000073 restart-and-reload-by-activation-script.configuration = {
74 systemd.services = rec {
75 simple-service = {
···93 no-restart-service = simple-service // {
94 restartIfChanged = false;
95 };
0000000000096 };
9798 system.activationScripts.restart-and-reload-test = {
···101 text = ''
102 if [ "$NIXOS_ACTION" = dry-activate ]; then
103 f=/run/nixos/dry-activation-restart-list
0104 else
105 f=/run/nixos/activation-restart-list
0106 fi
107 cat <<EOF >> "$f"
108 simple-service.service
109 simple-restart-service.service
110 simple-reload-service.service
111 no-restart-service.service
0000000112 EOF
113 '';
114 };
00000115 };
116117 mount.configuration = {
···241 raise Exception(f"Unexpected string '{needle}' was found")
24224300244 machine.succeed(
245 "${stderrRunner} ${originalSystem}/bin/switch-to-configuration test"
246 )
···379 assert_contains(out, "Main PID:") # output of systemctl
380 assert_lacks(out, "as well:")
3810000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000382 with subtest("restart and reload by activation script"):
383 switch_to_specialisation("${machine}", "simpleServiceNorestart")
384 out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script")
···386 assert_lacks(out, "NOT restarting the following changed units:")
387 assert_lacks(out, "reloading the following units:")
388 assert_lacks(out, "restarting the following units:")
389- assert_contains(out, "\nstarting the following units: no-restart-service.service, simple-reload-service.service, simple-restart-service.service, simple-service.service\n")
390 assert_lacks(out, "as well:")
391 # Switch to the same system where the example services get restarted
392- # by the activation script
393 out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script")
394 assert_lacks(out, "stopping the following units:")
395 assert_lacks(out, "NOT restarting the following changed units:")
396- assert_contains(out, "reloading the following units: simple-reload-service.service\n")
397- assert_contains(out, "restarting the following units: simple-restart-service.service, simple-service.service\n")
000000000398 assert_lacks(out, "\nstarting the following units:")
399 assert_lacks(out, "as well:")
400 # The same, but in dry mode
401 out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script", action="dry-activate")
402 assert_lacks(out, "would stop the following units:")
403 assert_lacks(out, "would NOT stop the following changed units:")
404- assert_contains(out, "would reload the following units: simple-reload-service.service\n")
405- assert_contains(out, "would restart the following units: simple-restart-service.service, simple-service.service\n")
406 assert_lacks(out, "\nwould start the following units:")
407 assert_lacks(out, "as well:")
408
···18 Type = "oneshot";
19 RemainAfterExit = true;
20 ExecStart = "${pkgs.coreutils}/bin/true";
21+ ExecReload = "${pkgs.coreutils}/bin/true";
22 };
23 };
24 };
···71 };
72 };
7374+ simpleServiceWithExtraSection.configuration = {
75+ imports = [ simpleServiceNostop.configuration ];
76+ systemd.packages = [ (pkgs.writeTextFile {
77+ name = "systemd-extra-section";
78+ destination = "/etc/systemd/system/test.service";
79+ text = ''
80+ [X-Test]
81+ X-Test-Value=a
82+ '';
83+ }) ];
84+ };
85+86+ simpleServiceWithExtraSectionOtherName.configuration = {
87+ imports = [ simpleServiceNostop.configuration ];
88+ systemd.packages = [ (pkgs.writeTextFile {
89+ name = "systemd-extra-section";
90+ destination = "/etc/systemd/system/test.service";
91+ text = ''
92+ [X-Test2]
93+ X-Test-Value=a
94+ '';
95+ }) ];
96+ };
97+98+ simpleServiceWithInstallSection.configuration = {
99+ imports = [ simpleServiceNostop.configuration ];
100+ systemd.packages = [ (pkgs.writeTextFile {
101+ name = "systemd-extra-section";
102+ destination = "/etc/systemd/system/test.service";
103+ text = ''
104+ [Install]
105+ WantedBy=multi-user.target
106+ '';
107+ }) ];
108+ };
109+110+ simpleServiceWithExtraKey.configuration = {
111+ imports = [ simpleServiceNostop.configuration ];
112+ systemd.services.test.serviceConfig."X-Test" = "test";
113+ };
114+115+ simpleServiceWithExtraKeyOtherValue.configuration = {
116+ imports = [ simpleServiceNostop.configuration ];
117+ systemd.services.test.serviceConfig."X-Test" = "test2";
118+ };
119+120+ simpleServiceWithExtraKeyOtherName.configuration = {
121+ imports = [ simpleServiceNostop.configuration ];
122+ systemd.services.test.serviceConfig."X-Test2" = "test";
123+ };
124+125+ simpleServiceReloadTrigger.configuration = {
126+ imports = [ simpleServiceNostop.configuration ];
127+ systemd.services.test.reloadTriggers = [ "/dev/null" ];
128+ };
129+130+ simpleServiceReloadTriggerModified.configuration = {
131+ imports = [ simpleServiceNostop.configuration ];
132+ systemd.services.test.reloadTriggers = [ "/dev/zero" ];
133+ };
134+135+ simpleServiceReloadTriggerModifiedAndSomethingElse.configuration = {
136+ imports = [ simpleServiceNostop.configuration ];
137+ systemd.services.test = {
138+ reloadTriggers = [ "/dev/zero" ];
139+ serviceConfig."X-Test" = "test";
140+ };
141+ };
142+143+ simpleServiceReloadTriggerModifiedSomethingElse.configuration = {
144+ imports = [ simpleServiceNostop.configuration ];
145+ systemd.services.test.serviceConfig."X-Test" = "test";
146+ };
147+148 restart-and-reload-by-activation-script.configuration = {
149 systemd.services = rec {
150 simple-service = {
···168 no-restart-service = simple-service // {
169 restartIfChanged = false;
170 };
171+172+ reload-triggers = simple-service // {
173+ wantedBy = [ "multi-user.target" ];
174+ };
175+176+ reload-triggers-and-restart-by-as = simple-service;
177+178+ reload-triggers-and-restart = simple-service // {
179+ stopIfChanged = false; # easier to check for this
180+ wantedBy = [ "multi-user.target" ];
181+ };
182 };
183184 system.activationScripts.restart-and-reload-test = {
···187 text = ''
188 if [ "$NIXOS_ACTION" = dry-activate ]; then
189 f=/run/nixos/dry-activation-restart-list
190+ g=/run/nixos/dry-activation-reload-list
191 else
192 f=/run/nixos/activation-restart-list
193+ g=/run/nixos/activation-reload-list
194 fi
195 cat <<EOF >> "$f"
196 simple-service.service
197 simple-restart-service.service
198 simple-reload-service.service
199 no-restart-service.service
200+ reload-triggers-and-restart-by-as.service
201+ EOF
202+203+ cat <<EOF >> "$g"
204+ reload-triggers.service
205+ reload-triggers-and-restart-by-as.service
206+ reload-triggers-and-restart.service
207 EOF
208 '';
209 };
210+ };
211+212+ restart-and-reload-by-activation-script-modified.configuration = {
213+ imports = [ restart-and-reload-by-activation-script.configuration ];
214+ systemd.services.reload-triggers-and-restart.serviceConfig.X-Modified = "test";
215 };
216217 mount.configuration = {
···341 raise Exception(f"Unexpected string '{needle}' was found")
342343344+ machine.wait_for_unit("multi-user.target")
345+346 machine.succeed(
347 "${stderrRunner} ${originalSystem}/bin/switch-to-configuration test"
348 )
···481 assert_contains(out, "Main PID:") # output of systemctl
482 assert_lacks(out, "as well:")
483484+ with subtest("unit file parser"):
485+ # Switch to a well-known state
486+ switch_to_specialisation("${machine}", "simpleServiceNostop")
487+488+ # Add a section
489+ out = switch_to_specialisation("${machine}", "simpleServiceWithExtraSection")
490+ assert_lacks(out, "stopping the following units:")
491+ assert_lacks(out, "NOT restarting the following changed units:")
492+ assert_lacks(out, "reloading the following units:")
493+ assert_contains(out, "\nrestarting the following units: test.service\n")
494+ assert_lacks(out, "\nstarting the following units:")
495+ assert_lacks(out, "the following new units were started:")
496+ assert_lacks(out, "as well:")
497+498+ # Rename it
499+ out = switch_to_specialisation("${machine}", "simpleServiceWithExtraSectionOtherName")
500+ assert_lacks(out, "stopping the following units:")
501+ assert_lacks(out, "NOT restarting the following changed units:")
502+ assert_lacks(out, "reloading the following units:")
503+ assert_contains(out, "\nrestarting the following units: test.service\n")
504+ assert_lacks(out, "\nstarting the following units:")
505+ assert_lacks(out, "the following new units were started:")
506+ assert_lacks(out, "as well:")
507+508+ # Remove it
509+ out = switch_to_specialisation("${machine}", "simpleServiceNostop")
510+ assert_lacks(out, "stopping the following units:")
511+ assert_lacks(out, "NOT restarting the following changed units:")
512+ assert_lacks(out, "reloading the following units:")
513+ assert_contains(out, "\nrestarting the following units: test.service\n")
514+ assert_lacks(out, "\nstarting the following units:")
515+ assert_lacks(out, "the following new units were started:")
516+ assert_lacks(out, "as well:")
517+518+ # [Install] section is ignored
519+ out = switch_to_specialisation("${machine}", "simpleServiceWithInstallSection")
520+ assert_lacks(out, "stopping the following units:")
521+ assert_lacks(out, "NOT restarting the following changed units:")
522+ assert_lacks(out, "reloading the following units:")
523+ assert_lacks(out, "\nrestarting the following units:")
524+ assert_lacks(out, "\nstarting the following units:")
525+ assert_lacks(out, "the following new units were started:")
526+ assert_lacks(out, "as well:")
527+528+ # Add a key
529+ out = switch_to_specialisation("${machine}", "simpleServiceWithExtraKey")
530+ assert_lacks(out, "stopping the following units:")
531+ assert_lacks(out, "NOT restarting the following changed units:")
532+ assert_lacks(out, "reloading the following units:")
533+ assert_contains(out, "\nrestarting the following units: test.service\n")
534+ assert_lacks(out, "\nstarting the following units:")
535+ assert_lacks(out, "the following new units were started:")
536+ assert_lacks(out, "as well:")
537+538+ # Change its value
539+ out = switch_to_specialisation("${machine}", "simpleServiceWithExtraKeyOtherValue")
540+ assert_lacks(out, "stopping the following units:")
541+ assert_lacks(out, "NOT restarting the following changed units:")
542+ assert_lacks(out, "reloading the following units:")
543+ assert_contains(out, "\nrestarting the following units: test.service\n")
544+ assert_lacks(out, "\nstarting the following units:")
545+ assert_lacks(out, "the following new units were started:")
546+ assert_lacks(out, "as well:")
547+548+ # Rename it
549+ out = switch_to_specialisation("${machine}", "simpleServiceWithExtraKeyOtherName")
550+ assert_lacks(out, "stopping the following units:")
551+ assert_lacks(out, "NOT restarting the following changed units:")
552+ assert_lacks(out, "reloading the following units:")
553+ assert_contains(out, "\nrestarting the following units: test.service\n")
554+ assert_lacks(out, "\nstarting the following units:")
555+ assert_lacks(out, "the following new units were started:")
556+ assert_lacks(out, "as well:")
557+558+ # Remove it
559+ out = switch_to_specialisation("${machine}", "simpleServiceNostop")
560+ assert_lacks(out, "stopping the following units:")
561+ assert_lacks(out, "NOT restarting the following changed units:")
562+ assert_lacks(out, "reloading the following units:")
563+ assert_contains(out, "\nrestarting the following units: test.service\n")
564+ assert_lacks(out, "\nstarting the following units:")
565+ assert_lacks(out, "the following new units were started:")
566+ assert_lacks(out, "as well:")
567+568+ # Add a reload trigger
569+ out = switch_to_specialisation("${machine}", "simpleServiceReloadTrigger")
570+ assert_lacks(out, "stopping the following units:")
571+ assert_lacks(out, "NOT restarting the following changed units:")
572+ assert_contains(out, "reloading the following units: test.service\n")
573+ assert_lacks(out, "\nrestarting the following units:")
574+ assert_lacks(out, "\nstarting the following units:")
575+ assert_lacks(out, "the following new units were started:")
576+ assert_lacks(out, "as well:")
577+578+ # Modify the reload trigger
579+ out = switch_to_specialisation("${machine}", "simpleServiceReloadTriggerModified")
580+ assert_lacks(out, "stopping the following units:")
581+ assert_lacks(out, "NOT restarting the following changed units:")
582+ assert_contains(out, "reloading the following units: test.service\n")
583+ assert_lacks(out, "\nrestarting the following units:")
584+ assert_lacks(out, "\nstarting the following units:")
585+ assert_lacks(out, "the following new units were started:")
586+ assert_lacks(out, "as well:")
587+588+ # Modify the reload trigger and something else
589+ out = switch_to_specialisation("${machine}", "simpleServiceReloadTriggerModifiedAndSomethingElse")
590+ assert_lacks(out, "stopping the following units:")
591+ assert_lacks(out, "NOT restarting the following changed units:")
592+ assert_lacks(out, "reloading the following units:")
593+ assert_contains(out, "\nrestarting the following units: test.service\n")
594+ assert_lacks(out, "\nstarting the following units:")
595+ assert_lacks(out, "the following new units were started:")
596+ assert_lacks(out, "as well:")
597+598+ # Remove the reload trigger
599+ out = switch_to_specialisation("${machine}", "simpleServiceReloadTriggerModifiedSomethingElse")
600+ assert_lacks(out, "stopping the following units:")
601+ assert_lacks(out, "NOT restarting the following changed units:")
602+ assert_lacks(out, "reloading the following units:")
603+ assert_lacks(out, "\nrestarting the following units:")
604+ assert_lacks(out, "\nstarting the following units:")
605+ assert_lacks(out, "the following new units were started:")
606+ assert_lacks(out, "as well:")
607+608 with subtest("restart and reload by activation script"):
609 switch_to_specialisation("${machine}", "simpleServiceNorestart")
610 out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script")
···612 assert_lacks(out, "NOT restarting the following changed units:")
613 assert_lacks(out, "reloading the following units:")
614 assert_lacks(out, "restarting the following units:")
615+ assert_contains(out, "\nstarting the following units: no-restart-service.service, reload-triggers-and-restart-by-as.service, simple-reload-service.service, simple-restart-service.service, simple-service.service\n")
616 assert_lacks(out, "as well:")
617 # Switch to the same system where the example services get restarted
618+ # and reloaded by the activation script
619 out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script")
620 assert_lacks(out, "stopping the following units:")
621 assert_lacks(out, "NOT restarting the following changed units:")
622+ assert_contains(out, "reloading the following units: reload-triggers-and-restart.service, reload-triggers.service, simple-reload-service.service\n")
623+ assert_contains(out, "restarting the following units: reload-triggers-and-restart-by-as.service, simple-restart-service.service, simple-service.service\n")
624+ assert_lacks(out, "\nstarting the following units:")
625+ assert_lacks(out, "as well:")
626+ # Switch to the same system and see if the service gets restarted when it's modified
627+ # while the fact that it's supposed to be reloaded by the activation script is ignored.
628+ out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script-modified")
629+ assert_lacks(out, "stopping the following units:")
630+ assert_lacks(out, "NOT restarting the following changed units:")
631+ assert_contains(out, "reloading the following units: reload-triggers.service, simple-reload-service.service\n")
632+ assert_contains(out, "restarting the following units: reload-triggers-and-restart-by-as.service, reload-triggers-and-restart.service, simple-restart-service.service, simple-service.service\n")
633 assert_lacks(out, "\nstarting the following units:")
634 assert_lacks(out, "as well:")
635 # The same, but in dry mode
636 out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script", action="dry-activate")
637 assert_lacks(out, "would stop the following units:")
638 assert_lacks(out, "would NOT stop the following changed units:")
639+ assert_contains(out, "would reload the following units: reload-triggers.service, simple-reload-service.service\n")
640+ assert_contains(out, "would restart the following units: reload-triggers-and-restart-by-as.service, reload-triggers-and-restart.service, simple-restart-service.service, simple-service.service\n")
641 assert_lacks(out, "\nwould start the following units:")
642 assert_lacks(out, "as well:")
643
+10-14
pkgs/applications/misc/eureka-editor/default.nix
···1-{ lib, stdenv, fetchzip, fltk, zlib, xdg-utils, xorg, libjpeg, libGL }:
23stdenv.mkDerivation rec {
4 pname = "eureka-editor";
5- version = "1.21";
6- shortver = "121";
78 src = fetchzip {
9- url = "mirror://sourceforge/eureka-editor/Eureka/${version}/eureka-${shortver}-source.tar.gz";
10- sha256 = "0fpj13aq4wh3f7473cdc5jkf1c71jiiqmjc0ihqa0nm3hic1d4yv";
11 };
1213- buildInputs = [ fltk zlib xdg-utils libjpeg xorg.libXinerama libGL ];
1415 enableParallelBuilding = true;
1617- preBuild = ''
18- substituteInPlace src/main.cc \
19- --replace /usr/local $out
20- substituteInPlace Makefile \
21- --replace /usr/local $out \
22- --replace "-o root " ""
23 '';
2425 preInstall = ''
···32 meta = with lib; {
33 homepage = "http://eureka-editor.sourceforge.net";
34 description = "A map editor for the classic DOOM games, and a few related games such as Heretic and Hexen";
35- license = licenses.gpl2;
36 platforms = platforms.all;
37- broken = stdenv.isDarwin;
38 maintainers = with maintainers; [ neonfuz ];
39 };
40}
···1+{ lib, stdenv, fetchzip, fltk, zlib, xdg-utils, xorg, libjpeg, libGLU }:
23stdenv.mkDerivation rec {
4 pname = "eureka-editor";
5+ version = "1.27b";
067 src = fetchzip {
8+ url = "mirror://sourceforge/eureka-editor/Eureka/${lib.versions.majorMinor version}/eureka-${version}-source.tar.gz";
9+ sha256 = "075w7xxsgbgh6dhndc1pfxb2h1s5fhsw28yl1c025gmx9bb4v3bf";
10 };
1112+ buildInputs = [ fltk zlib xdg-utils libjpeg xorg.libXinerama libGLU ];
1314 enableParallelBuilding = true;
1516+ postPatch = ''
17+ substituteInPlace src/main.cc --replace /usr/local $out
18+ substituteInPlace Makefile --replace /usr/local $out
00019 '';
2021 preInstall = ''
···28 meta = with lib; {
29 homepage = "http://eureka-editor.sourceforge.net";
30 description = "A map editor for the classic DOOM games, and a few related games such as Heretic and Hexen";
31+ license = licenses.gpl2Plus;
32 platforms = platforms.all;
33+ badPlatforms = platforms.darwin;
34 maintainers = with maintainers; [ neonfuz ];
35 };
36}
···88 ];
89 # Slow tests
90 disabledTests = [
91- "test_clifford" # fails on cvxpy >= 1.1.15. https://github.com/Qiskit/qiskit-aer/pull/1318. Remove in future.
92 "test_snapshot" # TODO: these ~30 tests fail on setup due to pytest fixture issues?
93 "test_initialize_2" # TODO: simulations appear incorrect, off by >10%.
0009495 # these fail for some builds. Haven't been able to reproduce error locally.
96 "test_kraus_gate_noise"
···88 ];
89 # Slow tests
90 disabledTests = [
091 "test_snapshot" # TODO: these ~30 tests fail on setup due to pytest fixture issues?
92 "test_initialize_2" # TODO: simulations appear incorrect, off by >10%.
93+ # These tests fail on cvxpy >= 1.1.15
94+ "test_clifford"
95+ "test_approx_random"
9697 # these fail for some builds. Haven't been able to reproduce error locally.
98 "test_kraus_gate_noise"
···97 pytest_xdist = pytest-xdist; # added 2021-01-04
98 python_simple_hipchat = python-simple-hipchat; # added 2021-07-21
99 qasm2image = throw "qasm2image is no longer maintained (since November 2018), and is not compatible with the latest pythonPackages.qiskit versions."; # added 2020-12-09
0100 rdflib-jsonld = throw "rdflib-jsonld is not compatible with rdflib 6"; # added 2021-11-05
101 repeated_test = throw "repeated_test is no longer maintained"; # added 2022-01-11
102 requests_toolbelt = requests-toolbelt; # added 2017-09-26
···97 pytest_xdist = pytest-xdist; # added 2021-01-04
98 python_simple_hipchat = python-simple-hipchat; # added 2021-07-21
99 qasm2image = throw "qasm2image is no longer maintained (since November 2018), and is not compatible with the latest pythonPackages.qiskit versions."; # added 2020-12-09
100+ qiskit-aqua = throw "qiskit-aqua has been removed due to deprecation, with its functionality moved to different qiskit packages";
101 rdflib-jsonld = throw "rdflib-jsonld is not compatible with rdflib 6"; # added 2021-11-05
102 repeated_test = throw "repeated_test is no longer maintained"; # added 2022-01-11
103 requests_toolbelt = requests-toolbelt; # added 2017-09-26