summaryrefslogtreecommitdiff
path: root/modules/networkd/systemd-lib.nix
diff options
context:
space:
mode:
Diffstat (limited to 'modules/networkd/systemd-lib.nix')
-rw-r--r--modules/networkd/systemd-lib.nix237
1 files changed, 237 insertions, 0 deletions
diff --git a/modules/networkd/systemd-lib.nix b/modules/networkd/systemd-lib.nix
new file mode 100644
index 00000000..2dbf1503
--- /dev/null
+++ b/modules/networkd/systemd-lib.nix
@@ -0,0 +1,237 @@
1{ config, lib, pkgs }:
2
3with lib;
4
5let
6 cfg = config.systemd;
7 lndir = "${pkgs.xorg.lndir}/bin/lndir";
8in rec {
9
10 shellEscape = s: (replaceChars [ "\\" ] [ "\\\\" ] s);
11
12 mkPathSafeName = lib.replaceChars ["@" ":" "\\" "[" "]"] ["-" "-" "-" "" ""];
13
14 makeUnit = name: unit:
15 if unit.enable then
16 pkgs.runCommand "unit-${mkPathSafeName name}"
17 { preferLocalBuild = true;
18 allowSubstitutes = false;
19 inherit (unit) text;
20 }
21 ''
22 mkdir -p $out
23 echo -n "$text" > $out/${shellEscape name}
24 ''
25 else
26 pkgs.runCommand "unit-${mkPathSafeName name}-disabled"
27 { preferLocalBuild = true;
28 allowSubstitutes = false;
29 }
30 ''
31 mkdir -p $out
32 ln -s /dev/null $out/${shellEscape name}
33 '';
34
35 boolValues = [true false "yes" "no"];
36
37 digits = map toString (range 0 9);
38
39 isByteFormat = s:
40 let
41 l = reverseList (stringToCharacters s);
42 suffix = head l;
43 nums = tail l;
44 in elem suffix (["K" "M" "G" "T"] ++ digits)
45 && all (num: elem num digits) nums;
46
47 assertByteFormat = name: group: attr:
48 optional (attr ? ${name} && ! isByteFormat attr.${name})
49 "Systemd ${group} field `${name}' must be in byte format [0-9]+[KMGT].";
50
51 hexChars = stringToCharacters "0123456789abcdefABCDEF";
52
53 isMacAddress = s: stringLength s == 17
54 && flip all (splitString ":" s) (bytes:
55 all (byte: elem byte hexChars) (stringToCharacters bytes)
56 );
57
58 assertMacAddress = name: group: attr:
59 optional (attr ? ${name} && ! isMacAddress attr.${name})
60 "Systemd ${group} field `${name}' must be a valid mac address.";
61
62 isPort = i: i >= 0 && i <= 65535;
63
64 assertPort = name: group: attr:
65 optional (attr ? ${name} && ! isPort attr.${name})
66 "Error on the systemd ${group} field `${name}': ${attr.name} is not a valid port number.";
67
68 assertValueOneOf = name: values: group: attr:
69 optional (attr ? ${name} && !elem attr.${name} values)
70 "Systemd ${group} field `${name}' cannot have value `${toString attr.${name}}'.";
71
72 assertHasField = name: group: attr:
73 optional (!(attr ? ${name}))
74 "Systemd ${group} field `${name}' must exist.";
75
76 assertRange = name: min: max: group: attr:
77 optional (attr ? ${name} && !(min <= attr.${name} && max >= attr.${name}))
78 "Systemd ${group} field `${name}' is outside the range [${toString min},${toString max}]";
79
80 assertMinimum = name: min: group: attr:
81 optional (attr ? ${name} && attr.${name} < min)
82 "Systemd ${group} field `${name}' must be greater than or equal to ${toString min}";
83
84 assertOnlyFields = fields: group: attr:
85 let badFields = filter (name: ! elem name fields) (attrNames attr); in
86 optional (badFields != [ ])
87 "Systemd ${group} has extra fields [${concatStringsSep " " badFields}].";
88
89 assertInt = name: group: attr:
90 optional (attr ? ${name} && !isInt attr.${name})
91 "Systemd ${group} field `${name}' is not an integer";
92
93 checkUnitConfig = group: checks: attrs: let
94 # We're applied at the top-level type (attrsOf unitOption), so the actual
95 # unit options might contain attributes from mkOverride and mkIf that we need to
96 # convert into single values before checking them.
97 defs = mapAttrs (const (v:
98 if v._type or "" == "override" then v.content
99 else if v._type or "" == "if" then v.content
100 else v
101 )) attrs;
102 errors = concatMap (c: c group defs) checks;
103 in if errors == [] then true
104 else builtins.trace (concatStringsSep "\n" errors) false;
105
106 toOption = x:
107 if x == true then "true"
108 else if x == false then "false"
109 else toString x;
110
111 attrsToSection = as:
112 concatStrings (concatLists (mapAttrsToList (name: value:
113 map (x: ''
114 ${name}=${toOption x}
115 '')
116 (if isList value then value else [value]))
117 as));
118
119 generateUnits = generateUnits' true;
120
121 generateUnits' = allowCollisions: type: units: upstreamUnits: upstreamWants:
122 pkgs.runCommand "${type}-units"
123 { preferLocalBuild = true;
124 allowSubstitutes = false;
125 } ''
126 mkdir -p $out
127
128 # Copy the upstream systemd units we're interested in.
129 for i in ${toString upstreamUnits}; do
130 fn=${cfg.package}/example/systemd/${type}/$i
131 if ! [ -e $fn ]; then echo "missing $fn"; false; fi
132 if [ -L $fn ]; then
133 target="$(readlink "$fn")"
134 if [ ''${target:0:3} = ../ ]; then
135 ln -s "$(readlink -f "$fn")" $out/
136 else
137 cp -pd $fn $out/
138 fi
139 else
140 ln -s $fn $out/
141 fi
142 done
143
144 # Copy .wants links, but only those that point to units that
145 # we're interested in.
146 for i in ${toString upstreamWants}; do
147 fn=${cfg.package}/example/systemd/${type}/$i
148 if ! [ -e $fn ]; then echo "missing $fn"; false; fi
149 x=$out/$(basename $fn)
150 mkdir $x
151 for i in $fn/*; do
152 y=$x/$(basename $i)
153 cp -pd $i $y
154 if ! [ -e $y ]; then rm $y; fi
155 done
156 done
157
158 # Symlink all units provided listed in systemd.packages.
159 packages="${toString cfg.packages}"
160
161 # Filter duplicate directories
162 declare -A unique_packages
163 for k in $packages ; do unique_packages[$k]=1 ; done
164
165 for i in ''${!unique_packages[@]}; do
166 for fn in $i/etc/systemd/${type}/* $i/lib/systemd/${type}/*; do
167 if ! [[ "$fn" =~ .wants$ ]]; then
168 if [[ -d "$fn" ]]; then
169 targetDir="$out/$(basename "$fn")"
170 mkdir -p "$targetDir"
171 ${lndir} "$fn" "$targetDir"
172 else
173 ln -s $fn $out/
174 fi
175 fi
176 done
177 done
178
179 # Symlink all units defined by systemd.units. If these are also
180 # provided by systemd or systemd.packages, then add them as
181 # <unit-name>.d/overrides.conf, which makes them extend the
182 # upstream unit.
183 for i in ${toString (mapAttrsToList (n: v: v.unit) units)}; do
184 fn=$(basename $i/*)
185 if [ -e $out/$fn ]; then
186 if [ "$(readlink -f $i/$fn)" = /dev/null ]; then
187 ln -sfn /dev/null $out/$fn
188 else
189 ${if allowCollisions then ''
190 mkdir -p $out/$fn.d
191 ln -s $i/$fn $out/$fn.d/overrides.conf
192 '' else ''
193 echo "Found multiple derivations configuring $fn!"
194 exit 1
195 ''}
196 fi
197 else
198 ln -fs $i/$fn $out/
199 fi
200 done
201
202 # Create service aliases from aliases option.
203 ${concatStrings (mapAttrsToList (name: unit:
204 concatMapStrings (name2: ''
205 ln -sfn '${name}' $out/'${name2}'
206 '') unit.aliases) units)}
207
208 # Create .wants and .requires symlinks from the wantedBy and
209 # requiredBy options.
210 ${concatStrings (mapAttrsToList (name: unit:
211 concatMapStrings (name2: ''
212 mkdir -p $out/'${name2}.wants'
213 ln -sfn '../${name}' $out/'${name2}.wants'/
214 '') unit.wantedBy) units)}
215
216 ${concatStrings (mapAttrsToList (name: unit:
217 concatMapStrings (name2: ''
218 mkdir -p $out/'${name2}.requires'
219 ln -sfn '../${name}' $out/'${name2}.requires'/
220 '') unit.requiredBy) units)}
221
222 ${optionalString (type == "system") ''
223 # Stupid misc. symlinks.
224 ln -s ${cfg.defaultUnit} $out/default.target
225 ln -s ${cfg.ctrlAltDelUnit} $out/ctrl-alt-del.target
226 ln -s rescue.target $out/kbrequest.target
227
228 mkdir -p $out/getty.target.wants/
229 ln -s ../autovt@tty1.service $out/getty.target.wants/
230
231 ln -s ../local-fs.target ../remote-fs.target \
232 ../nss-lookup.target ../nss-user-lookup.target ../swap.target \
233 $out/multi-user.target.wants/
234 ''}
235 ''; # */
236
237}