From 3a2161ef205c432db0053e9a82893069b54e55ed Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sun, 17 Oct 2021 20:50:47 +0200 Subject: yggdrasil-wg: ... --- modules/networkd/systemd-lib.nix | 237 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 modules/networkd/systemd-lib.nix (limited to 'modules/networkd/systemd-lib.nix') 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 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.systemd; + lndir = "${pkgs.xorg.lndir}/bin/lndir"; +in rec { + + shellEscape = s: (replaceChars [ "\\" ] [ "\\\\" ] s); + + mkPathSafeName = lib.replaceChars ["@" ":" "\\" "[" "]"] ["-" "-" "-" "" ""]; + + makeUnit = name: unit: + if unit.enable then + pkgs.runCommand "unit-${mkPathSafeName name}" + { preferLocalBuild = true; + allowSubstitutes = false; + inherit (unit) text; + } + '' + mkdir -p $out + echo -n "$text" > $out/${shellEscape name} + '' + else + pkgs.runCommand "unit-${mkPathSafeName name}-disabled" + { preferLocalBuild = true; + allowSubstitutes = false; + } + '' + mkdir -p $out + ln -s /dev/null $out/${shellEscape name} + ''; + + boolValues = [true false "yes" "no"]; + + digits = map toString (range 0 9); + + isByteFormat = s: + let + l = reverseList (stringToCharacters s); + suffix = head l; + nums = tail l; + in elem suffix (["K" "M" "G" "T"] ++ digits) + && all (num: elem num digits) nums; + + assertByteFormat = name: group: attr: + optional (attr ? ${name} && ! isByteFormat attr.${name}) + "Systemd ${group} field `${name}' must be in byte format [0-9]+[KMGT]."; + + hexChars = stringToCharacters "0123456789abcdefABCDEF"; + + isMacAddress = s: stringLength s == 17 + && flip all (splitString ":" s) (bytes: + all (byte: elem byte hexChars) (stringToCharacters bytes) + ); + + assertMacAddress = name: group: attr: + optional (attr ? ${name} && ! isMacAddress attr.${name}) + "Systemd ${group} field `${name}' must be a valid mac address."; + + isPort = i: i >= 0 && i <= 65535; + + assertPort = name: group: attr: + optional (attr ? ${name} && ! isPort attr.${name}) + "Error on the systemd ${group} field `${name}': ${attr.name} is not a valid port number."; + + assertValueOneOf = name: values: group: attr: + optional (attr ? ${name} && !elem attr.${name} values) + "Systemd ${group} field `${name}' cannot have value `${toString attr.${name}}'."; + + assertHasField = name: group: attr: + optional (!(attr ? ${name})) + "Systemd ${group} field `${name}' must exist."; + + assertRange = name: min: max: group: attr: + optional (attr ? ${name} && !(min <= attr.${name} && max >= attr.${name})) + "Systemd ${group} field `${name}' is outside the range [${toString min},${toString max}]"; + + assertMinimum = name: min: group: attr: + optional (attr ? ${name} && attr.${name} < min) + "Systemd ${group} field `${name}' must be greater than or equal to ${toString min}"; + + assertOnlyFields = fields: group: attr: + let badFields = filter (name: ! elem name fields) (attrNames attr); in + optional (badFields != [ ]) + "Systemd ${group} has extra fields [${concatStringsSep " " badFields}]."; + + assertInt = name: group: attr: + optional (attr ? ${name} && !isInt attr.${name}) + "Systemd ${group} field `${name}' is not an integer"; + + checkUnitConfig = group: checks: attrs: let + # We're applied at the top-level type (attrsOf unitOption), so the actual + # unit options might contain attributes from mkOverride and mkIf that we need to + # convert into single values before checking them. + defs = mapAttrs (const (v: + if v._type or "" == "override" then v.content + else if v._type or "" == "if" then v.content + else v + )) attrs; + errors = concatMap (c: c group defs) checks; + in if errors == [] then true + else builtins.trace (concatStringsSep "\n" errors) false; + + toOption = x: + if x == true then "true" + else if x == false then "false" + else toString x; + + attrsToSection = as: + concatStrings (concatLists (mapAttrsToList (name: value: + map (x: '' + ${name}=${toOption x} + '') + (if isList value then value else [value])) + as)); + + generateUnits = generateUnits' true; + + generateUnits' = allowCollisions: type: units: upstreamUnits: upstreamWants: + pkgs.runCommand "${type}-units" + { preferLocalBuild = true; + allowSubstitutes = false; + } '' + mkdir -p $out + + # Copy the upstream systemd units we're interested in. + for i in ${toString upstreamUnits}; do + fn=${cfg.package}/example/systemd/${type}/$i + if ! [ -e $fn ]; then echo "missing $fn"; false; fi + if [ -L $fn ]; then + target="$(readlink "$fn")" + if [ ''${target:0:3} = ../ ]; then + ln -s "$(readlink -f "$fn")" $out/ + else + cp -pd $fn $out/ + fi + else + ln -s $fn $out/ + fi + done + + # Copy .wants links, but only those that point to units that + # we're interested in. + for i in ${toString upstreamWants}; do + fn=${cfg.package}/example/systemd/${type}/$i + if ! [ -e $fn ]; then echo "missing $fn"; false; fi + x=$out/$(basename $fn) + mkdir $x + for i in $fn/*; do + y=$x/$(basename $i) + cp -pd $i $y + if ! [ -e $y ]; then rm $y; fi + done + done + + # Symlink all units provided listed in systemd.packages. + packages="${toString cfg.packages}" + + # Filter duplicate directories + declare -A unique_packages + for k in $packages ; do unique_packages[$k]=1 ; done + + for i in ''${!unique_packages[@]}; do + for fn in $i/etc/systemd/${type}/* $i/lib/systemd/${type}/*; do + if ! [[ "$fn" =~ .wants$ ]]; then + if [[ -d "$fn" ]]; then + targetDir="$out/$(basename "$fn")" + mkdir -p "$targetDir" + ${lndir} "$fn" "$targetDir" + else + ln -s $fn $out/ + fi + fi + done + done + + # Symlink all units defined by systemd.units. If these are also + # provided by systemd or systemd.packages, then add them as + # .d/overrides.conf, which makes them extend the + # upstream unit. + for i in ${toString (mapAttrsToList (n: v: v.unit) units)}; do + fn=$(basename $i/*) + if [ -e $out/$fn ]; then + if [ "$(readlink -f $i/$fn)" = /dev/null ]; then + ln -sfn /dev/null $out/$fn + else + ${if allowCollisions then '' + mkdir -p $out/$fn.d + ln -s $i/$fn $out/$fn.d/overrides.conf + '' else '' + echo "Found multiple derivations configuring $fn!" + exit 1 + ''} + fi + else + ln -fs $i/$fn $out/ + fi + done + + # Create service aliases from aliases option. + ${concatStrings (mapAttrsToList (name: unit: + concatMapStrings (name2: '' + ln -sfn '${name}' $out/'${name2}' + '') unit.aliases) units)} + + # Create .wants and .requires symlinks from the wantedBy and + # requiredBy options. + ${concatStrings (mapAttrsToList (name: unit: + concatMapStrings (name2: '' + mkdir -p $out/'${name2}.wants' + ln -sfn '../${name}' $out/'${name2}.wants'/ + '') unit.wantedBy) units)} + + ${concatStrings (mapAttrsToList (name: unit: + concatMapStrings (name2: '' + mkdir -p $out/'${name2}.requires' + ln -sfn '../${name}' $out/'${name2}.requires'/ + '') unit.requiredBy) units)} + + ${optionalString (type == "system") '' + # Stupid misc. symlinks. + ln -s ${cfg.defaultUnit} $out/default.target + ln -s ${cfg.ctrlAltDelUnit} $out/ctrl-alt-del.target + ln -s rescue.target $out/kbrequest.target + + mkdir -p $out/getty.target.wants/ + ln -s ../autovt@tty1.service $out/getty.target.wants/ + + ln -s ../local-fs.target ../remote-fs.target \ + ../nss-lookup.target ../nss-user-lookup.target ../swap.target \ + $out/multi-user.target.wants/ + ''} + ''; # */ + +} -- cgit v1.2.3