1pkgs: with pkgs.lib;
2
3rec {
4
5 # Copy configuration files to avoid having the entire sources in the system closure
6 copyFile = filePath: pkgs.runCommandNoCC (builtins.unsafeDiscardStringContext (builtins.baseNameOf filePath)) {} ''
7 cp ${filePath} $out
8 '';
9
10 # Check whenever fileSystem is needed for boot. NOTE: Make sure
11 # pathsNeededForBoot is closed under the parent relationship, i.e. if /a/b/c
12 # is in the list, put /a and /a/b in as well.
13 pathsNeededForBoot = [ "/" "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/etc" ];
14 fsNeededForBoot = fs: fs.neededForBoot || elem fs.mountPoint pathsNeededForBoot;
15
16 # Check whenever `b` depends on `a` as a fileSystem
17 fsBefore = a: b: a.mountPoint == b.device
18 || hasPrefix "${a.mountPoint}${optionalString (!(hasSuffix "/" a.mountPoint)) "/"}" b.mountPoint;
19
20 # Escape a path according to the systemd rules, e.g. /dev/xyzzy
21 # becomes dev-xyzzy. FIXME: slow.
22 escapeSystemdPath = s:
23 replaceChars ["/" "-" " "] ["-" "\\x2d" "\\x20"]
24 (removePrefix "/" s);
25
26 # Returns a system path for a given shell package
27 toShellPath = shell:
28 if types.shellPackage.check shell then
29 "/run/current-system/sw${shell.shellPath}"
30 else if types.package.check shell then
31 throw "${shell} is not a shell package"
32 else
33 shell;
34
35 /* Recurse into a list or an attrset, searching for attrs named like
36 the value of the "attr" parameter, and return an attrset where the
37 names are the corresponding jq path where the attrs were found and
38 the values are the values of the attrs.
39
40 Example:
41 recursiveGetAttrWithJqPrefix {
42 example = [
43 {
44 irrelevant = "not interesting";
45 }
46 {
47 ignored = "ignored attr";
48 relevant = {
49 secret = {
50 _secret = "/path/to/secret";
51 };
52 };
53 }
54 ];
55 } "_secret" -> { ".example[1].relevant.secret" = "/path/to/secret"; }
56 */
57 recursiveGetAttrWithJqPrefix = item: attr:
58 let
59 recurse = prefix: item:
60 if item ? ${attr} then
61 nameValuePair prefix item.${attr}
62 else if isAttrs item then
63 map (name: recurse (prefix + "." + name) item.${name}) (attrNames item)
64 else if isList item then
65 imap0 (index: item: recurse (prefix + "[${toString index}]") item) item
66 else
67 [];
68 in listToAttrs (flatten (recurse "" item));
69
70 /* Takes an attrset and a file path and generates a bash snippet that
71 outputs a JSON file at the file path with all instances of
72
73 { _secret = "/path/to/secret" }
74
75 in the attrset replaced with the contents of the file
76 "/path/to/secret" in the output JSON.
77
78 When a configuration option accepts an attrset that is finally
79 converted to JSON, this makes it possible to let the user define
80 arbitrary secret values.
81
82 Example:
83 If the file "/path/to/secret" contains the string
84 "topsecretpassword1234",
85
86 genJqSecretsReplacementSnippet {
87 example = [
88 {
89 irrelevant = "not interesting";
90 }
91 {
92 ignored = "ignored attr";
93 relevant = {
94 secret = {
95 _secret = "/path/to/secret";
96 };
97 };
98 }
99 ];
100 } "/path/to/output.json"
101
102 would generate a snippet that, when run, outputs the following
103 JSON file at "/path/to/output.json":
104
105 {
106 "example": [
107 {
108 "irrelevant": "not interesting"
109 },
110 {
111 "ignored": "ignored attr",
112 "relevant": {
113 "secret": "topsecretpassword1234"
114 }
115 }
116 ]
117 }
118 */
119 genJqSecretsReplacementSnippet = genJqSecretsReplacementSnippet' "_secret";
120
121 # Like genJqSecretsReplacementSnippet, but allows the name of the
122 # attr which identifies the secret to be changed.
123 genJqSecretsReplacementSnippet' = attr: set: output:
124 let
125 secrets = recursiveGetAttrWithJqPrefix set attr;
126 in ''
127 if [[ -h '${output}' ]]; then
128 rm '${output}'
129 fi
130 ''
131 + concatStringsSep
132 "\n"
133 (imap1 (index: name: "export secret${toString index}=$(<'${secrets.${name}}')")
134 (attrNames secrets))
135 + "\n"
136 + "${pkgs.jq}/bin/jq >'${output}' '"
137 + concatStringsSep
138 " | "
139 (imap1 (index: name: ''${name} = $ENV.secret${toString index}'')
140 (attrNames secrets))
141 + ''
142 ' <<'EOF'
143 ${builtins.toJSON set}
144 EOF
145 '';
146}