forked from NixOS/nixpkgs
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsysfs.nix
More file actions
265 lines (233 loc) · 7.06 KB
/
Copy pathsysfs.nix
File metadata and controls
265 lines (233 loc) · 7.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
{
lib,
config,
utils,
pkgs,
...
}:
let
inherit (lib)
all
any
concatLines
concatStringsSep
escapeShellArg
flatten
floatToString
foldl'
head
isAttrs
isDerivation
isFloat
isList
length
listToAttrs
match
mapAttrsToList
nameValuePair
removePrefix
tail
throwIf
;
inherit (lib.options)
showDefs
showOption
;
inherit (lib.strings)
escapeC
isConvertibleWithToString
;
inherit (lib.path.subpath) join;
inherit (utils) escapeSystemdPath;
cfg = config.boot.kernel.sysfs;
sysfsAttrs = with lib.types; nullOr (either sysfsValue (attrsOf sysfsAttrs));
sysfsValue = lib.mkOptionType {
name = "sysfs value";
description = "sysfs attribute value";
descriptionClass = "noun";
check = v: isConvertibleWithToString v;
merge =
loc: defs:
if length defs == 1 then
(head defs).value
else
(foldl' (
first: def:
# merge definitions if they produce the same value string
throwIf (mkValueString first.value != mkValueString def.value)
"The option \"${showOption loc}\" has conflicting definition values:${
showDefs [
first
def
]
}"
first
) (head defs) (tail defs)).value;
};
mapAttrsToListRecursive =
fn: set:
let
recurse =
p: v:
if isAttrs v && !isDerivation v then mapAttrsToList (n: v: recurse (p ++ [ n ]) v) v else fn p v;
in
flatten (recurse [ ] set);
mkPath = p: "/sys" + removePrefix "." (join p);
hasGlob = p: any (n: match ''(.*[^\\])?[*?[].*'' n != null) p;
mkValueString =
v:
# true will be converted to "1" by toString, saving one branch
if v == false then
"0"
else if isFloat v then
floatToString v # warn about loss of precision
else if isList v then
concatStringsSep "," (map mkValueString v)
else
toString v;
# escape whitespace and linebreaks, as well as the escape character itself,
# to ensure that field boundaries are always preserved
escapeTmpfiles = escapeC [
"\t"
"\n"
"\r"
" "
"\\"
];
tmpfiles = pkgs.runCommand "nixos-sysfs-tmpfiles.d" { } (
''
mkdir "$out"
''
+ concatLines (
mapAttrsToListRecursive (
p: v:
let
path = mkPath p;
in
if v == null then
[ ]
else
''
printf 'w %s - - - - %s\n' \
${escapeShellArg (escapeTmpfiles path)} \
${escapeShellArg (escapeTmpfiles (mkValueString v))} \
>"$out"/${escapeShellArg (escapeSystemdPath path)}.conf
''
) cfg
)
);
in
{
options = {
boot.kernel.sysfs = lib.mkOption {
type = lib.types.submodule {
freeformType = lib.types.attrsOf sysfsAttrs // {
description = "nested attribute set of null or sysfs attribute values";
};
};
description = ''
sysfs attributes to be set as soon as they become available.
Attribute names represent path components in the sysfs filesystem and
cannot be `.` or `..` nor contain any slash character (`/`).
Names may contain shell‐style glob patterns (`*`, `?` and `[…]`)
matching a single path component, these should however be used with
caution, as they may produce unexpected results if attribute paths
overlap.
Values will be converted to strings, with list elements concatenated
with commata and booleans converted to numeric values (`0` or `1`).
`null` values are ignored, allowing removal of values defined in other
modules, as are empty attribute sets.
List values defined in different modules will _not_ be concatenated.
This option may only be used for attributes which can be set
idempotently, as the configured values might be written more than once.
'';
default = { };
example = lib.literalExpression ''
{
# enable transparent hugepages with deferred defragmentaion
kernel.mm.transparent_hugepage = {
enabled = "always";
defrag = "defer";
shmem_enabled = "within_size";
};
devices.system.cpu = {
# configure powesave frequency governor for all CPUs
# the [0-9]* glob pattern ensures that other paths
# like cpufreq or cpuidle are not matched
"cpu[0-9]*" = {
scaling_governor = "powersave";
energy_performance_preference = 8;
};
# disable frequency boost
intel_pstate.no_turbo = true;
};
}
'';
};
};
config = lib.mkIf (cfg != { }) {
systemd = {
paths = {
"nixos-sysfs@" = {
description = "/%I attribute watcher";
pathConfig.PathExistsGlob = "/%I";
unitConfig.DefaultDependencies = false;
};
}
// listToAttrs (
mapAttrsToListRecursive (
p: v:
if v == null then
[ ]
else
nameValuePair "nixos-sysfs@${escapeSystemdPath (mkPath p)}" {
overrideStrategy = "asDropin";
wantedBy = [ "sysinit.target" ];
before = [ "sysinit.target" ];
}
) cfg
);
services."nixos-sysfs@" = {
description = "/%I attribute setter";
unitConfig = {
DefaultDependencies = false;
AssertPathIsMountPoint = "/sys";
AssertPathExistsGlob = "/%I";
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
# while we could be tempted to use simple shell script to set the
# sysfs attributes specified by the path or glob pattern, it is
# almost impossible to properly escape a glob pattern so that it
# can be used safely in a shell script
ExecStart = "${lib.getExe' config.systemd.package "systemd-tmpfiles"} --prefix=/sys --create ${tmpfiles}/%i.conf";
# hardening may be overkill for such a simple and short‐lived
# service, the following settings would however be suitable to deny
# access to anything but /sys
#ProtectProc = "noaccess";
#ProcSubset = "pid";
#ProtectSystem = "strict";
#PrivateDevices = true;
#SystemCallErrorNumber = "EPERM";
#SystemCallFilter = [
# "@basic-io"
# "@file-system"
#];
};
};
};
warnings = mapAttrsToListRecursive (
p: v:
if hasGlob p then
"Attribute path \"${concatStringsSep "." p}\" contains glob patterns. Please ensure that it does not overlap with other paths."
else
[ ]
) cfg;
assertions = mapAttrsToListRecursive (p: v: {
assertion = all (n: match ''(\.\.?|.*/.*)'' n == null) p;
message = "Attribute path \"${concatStringsSep "." p}\" has invalid components.";
}) cfg;
};
meta.maintainers = with lib.maintainers; [ mvs ];
}