1# | Build a script to install and start a set of systemd units on any
2# systemd-based system.
3#
4# Creates a symlink at /etc/systemd-static/${namespace} for slightly
5# improved atomicity.
6{
7 writeScriptBin,
8 bash,
9 coreutils,
10 systemd,
11 runCommand,
12 lib,
13}:
14{
15 units,
16 # : AttrSet String (Either Path { path : Path, wanted-by : [ String ] })
17 # ^ A set whose names are unit names and values are
18 # either paths to the corresponding unit files or a set
19 # containing the path and the list of units this unit
20 # should be wanted-by (none by default).
21 #
22 # The names should include the unit suffix
23 # (e.g. ".service")
24 namespace,
25# : String
26# The namespace for the unit files, to allow for
27# multiple independent unit sets managed by
28# `setupSystemdUnits`.
29}:
30let
31 static = runCommand "systemd-static" { } ''
32 mkdir -p $out
33 ${lib.concatStringsSep "\n" (
34 lib.mapAttrsToList (nm: file: "ln -sv ${file.path or file} $out/${nm}") units
35 )}
36 '';
37 add-unit-snippet = name: file: ''
38 oldUnit=$(readlink -f "$unitDir/${name}" || echo "$unitDir/${name}")
39 if [ -f "$oldUnit" -a "$oldUnit" != "${file.path or file}" ]; then
40 unitsToStop+=("${name}")
41 fi
42 ln -sf "/etc/systemd-static/${namespace}/${name}" \
43 "$unitDir/.${name}.tmp"
44 mv -T "$unitDir/.${name}.tmp" "$unitDir/${name}"
45 ${lib.concatStringsSep "\n" (
46 map (unit: ''
47 mkdir -p "$unitDir/${unit}.wants"
48 ln -sf "../${name}" \
49 "$unitDir/${unit}.wants/.${name}.tmp"
50 mv -T "$unitDir/${unit}.wants/.${name}.tmp" \
51 "$unitDir/${unit}.wants/${name}"
52 '') file.wanted-by or [ ]
53 )}
54 unitsToStart+=("${name}")
55 '';
56in
57writeScriptBin "setup-systemd-units" ''
58 #!${bash}/bin/bash -e
59 export PATH=${coreutils}/bin:${systemd}/bin
60
61 unitDir=/etc/systemd/system
62 if [ ! -w "$unitDir" ]; then
63 unitDir=/nix/var/nix/profiles/default/lib/systemd/system
64 mkdir -p "$unitDir"
65 fi
66 declare -a unitsToStop unitsToStart
67
68 oldStatic=$(readlink -f /etc/systemd-static/${namespace} || true)
69 if [ "$oldStatic" != "${static}" ]; then
70 ${lib.concatStringsSep "\n" (lib.mapAttrsToList add-unit-snippet units)}
71 if [ ''${#unitsToStop[@]} -ne 0 ]; then
72 echo "Stopping unit(s) ''${unitsToStop[@]}" >&2
73 systemctl stop "''${unitsToStop[@]}"
74 fi
75 mkdir -p /etc/systemd-static
76 ln -sfT ${static} /etc/systemd-static/.${namespace}.tmp
77 mv -T /etc/systemd-static/.${namespace}.tmp /etc/systemd-static/${namespace}
78 systemctl daemon-reload
79 echo "Starting unit(s) ''${unitsToStart[@]}" >&2
80 systemctl start "''${unitsToStart[@]}"
81 else
82 echo "Units unchanged, doing nothing" >&2
83 fi
84''