at 22.05-pre 7.9 kB view raw
1{ config, lib, pkgs }: 2 3with lib; 4 5let 6 cfg = config.systemd; 7 lndir = "${pkgs.buildPackages.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}