at 23.11-pre 5.4 kB view raw
1# Management of static files in /etc. 2 3{ config, lib, pkgs, ... }: 4 5with lib; 6 7let 8 9 etc' = filter (f: f.enable) (attrValues config.environment.etc); 10 11 etc = pkgs.runCommandLocal "etc" { 12 # This is needed for the systemd module 13 passthru.targets = map (x: x.target) etc'; 14 } /* sh */ '' 15 set -euo pipefail 16 17 makeEtcEntry() { 18 src="$1" 19 target="$2" 20 mode="$3" 21 user="$4" 22 group="$5" 23 24 if [[ "$src" = *'*'* ]]; then 25 # If the source name contains '*', perform globbing. 26 mkdir -p "$out/etc/$target" 27 for fn in $src; do 28 ln -s "$fn" "$out/etc/$target/" 29 done 30 else 31 32 mkdir -p "$out/etc/$(dirname "$target")" 33 if ! [ -e "$out/etc/$target" ]; then 34 ln -s "$src" "$out/etc/$target" 35 else 36 echo "duplicate entry $target -> $src" 37 if [ "$(readlink "$out/etc/$target")" != "$src" ]; then 38 echo "mismatched duplicate entry $(readlink "$out/etc/$target") <-> $src" 39 ret=1 40 41 continue 42 fi 43 fi 44 45 if [ "$mode" != symlink ]; then 46 echo "$mode" > "$out/etc/$target.mode" 47 echo "$user" > "$out/etc/$target.uid" 48 echo "$group" > "$out/etc/$target.gid" 49 fi 50 fi 51 } 52 53 mkdir -p "$out/etc" 54 ${concatMapStringsSep "\n" (etcEntry: escapeShellArgs [ 55 "makeEtcEntry" 56 # Force local source paths to be added to the store 57 "${etcEntry.source}" 58 etcEntry.target 59 etcEntry.mode 60 etcEntry.user 61 etcEntry.group 62 ]) etc'} 63 ''; 64 65in 66 67{ 68 69 imports = [ ../build.nix ]; 70 71 ###### interface 72 73 options = { 74 75 environment.etc = mkOption { 76 default = {}; 77 example = literalExpression '' 78 { example-configuration-file = 79 { source = "/nix/store/.../etc/dir/file.conf.example"; 80 mode = "0440"; 81 }; 82 "default/useradd".text = "GROUP=100 ..."; 83 } 84 ''; 85 description = lib.mdDoc '' 86 Set of files that have to be linked in {file}`/etc`. 87 ''; 88 89 type = with types; attrsOf (submodule ( 90 { name, config, options, ... }: 91 { options = { 92 93 enable = mkOption { 94 type = types.bool; 95 default = true; 96 description = lib.mdDoc '' 97 Whether this /etc file should be generated. This 98 option allows specific /etc files to be disabled. 99 ''; 100 }; 101 102 target = mkOption { 103 type = types.str; 104 description = lib.mdDoc '' 105 Name of symlink (relative to 106 {file}`/etc`). Defaults to the attribute 107 name. 108 ''; 109 }; 110 111 text = mkOption { 112 default = null; 113 type = types.nullOr types.lines; 114 description = lib.mdDoc "Text of the file."; 115 }; 116 117 source = mkOption { 118 type = types.path; 119 description = lib.mdDoc "Path of the source file."; 120 }; 121 122 mode = mkOption { 123 type = types.str; 124 default = "symlink"; 125 example = "0600"; 126 description = lib.mdDoc '' 127 If set to something else than `symlink`, 128 the file is copied instead of symlinked, with the given 129 file mode. 130 ''; 131 }; 132 133 uid = mkOption { 134 default = 0; 135 type = types.int; 136 description = lib.mdDoc '' 137 UID of created file. Only takes effect when the file is 138 copied (that is, the mode is not 'symlink'). 139 ''; 140 }; 141 142 gid = mkOption { 143 default = 0; 144 type = types.int; 145 description = lib.mdDoc '' 146 GID of created file. Only takes effect when the file is 147 copied (that is, the mode is not 'symlink'). 148 ''; 149 }; 150 151 user = mkOption { 152 default = "+${toString config.uid}"; 153 type = types.str; 154 description = lib.mdDoc '' 155 User name of created file. 156 Only takes effect when the file is copied (that is, the mode is not 'symlink'). 157 Changing this option takes precedence over `uid`. 158 ''; 159 }; 160 161 group = mkOption { 162 default = "+${toString config.gid}"; 163 type = types.str; 164 description = lib.mdDoc '' 165 Group name of created file. 166 Only takes effect when the file is copied (that is, the mode is not 'symlink'). 167 Changing this option takes precedence over `gid`. 168 ''; 169 }; 170 171 }; 172 173 config = { 174 target = mkDefault name; 175 source = mkIf (config.text != null) ( 176 let name' = "etc-" + baseNameOf name; 177 in mkDerivedConfig options.text (pkgs.writeText name') 178 ); 179 }; 180 181 })); 182 183 }; 184 185 }; 186 187 188 ###### implementation 189 190 config = { 191 192 system.build.etc = etc; 193 system.build.etcActivationCommands = 194 '' 195 # Set up the statically computed bits of /etc. 196 echo "setting up /etc..." 197 ${pkgs.perl.withPackages (p: [ p.FileSlurp ])}/bin/perl ${./setup-etc.pl} ${etc}/etc 198 ''; 199 }; 200 201}