···
34
+
inherit (lib.options)
39
+
inherit (lib.strings)
41
+
isConvertibleWithToString
44
+
inherit (lib.path.subpath) join;
46
+
inherit (utils) escapeSystemdPath;
48
+
cfg = config.boot.kernel.sysfs;
50
+
sysfsAttrs = with lib.types; nullOr (either sysfsValue (attrsOf sysfsAttrs));
51
+
sysfsValue = lib.mkOptionType {
52
+
name = "sysfs value";
53
+
description = "sysfs attribute value";
54
+
descriptionClass = "noun";
55
+
check = v: isConvertibleWithToString v;
58
+
if length defs == 1 then
63
+
# merge definitions if they produce the same value string
64
+
throwIf (mkValueString first.value != mkValueString def.value)
65
+
"The option \"${showOption loc}\" has conflicting definition values:${
72
+
) (head defs) (tail defs)).value;
75
+
mapAttrsToListRecursive =
80
+
if isAttrs v && !isDerivation v then mapAttrsToList (n: v: recurse (p ++ [ n ]) v) v else fn p v;
82
+
flatten (recurse [ ] set);
84
+
mkPath = p: "/sys" + removePrefix "." (join p);
85
+
hasGlob = p: any (n: match ''(.*[^\\])?[*?[].*'' n != null) p;
89
+
# true will be converted to "1" by toString, saving one branch
92
+
else if isFloat v then
93
+
floatToString v # warn about loss of precision
94
+
else if isList v then
95
+
concatStringsSep "," (map mkValueString v)
99
+
# escape whitespace and linebreaks, as well as the escape character itself,
100
+
# to ensure that field boundaries are always preserved
101
+
escapeTmpfiles = escapeC [
109
+
tmpfiles = pkgs.runCommand "nixos-sysfs-tmpfiles.d" { } (
114
+
mapAttrsToListRecursive (
123
+
printf 'w %s - - - - %s\n' \
124
+
${escapeShellArg (escapeTmpfiles path)} \
125
+
${escapeShellArg (escapeTmpfiles (mkValueString v))} \
126
+
>"$out"/${escapeShellArg (escapeSystemdPath path)}.conf
134
+
boot.kernel.sysfs = lib.mkOption {
135
+
type = lib.types.submodule {
136
+
freeformType = lib.types.attrsOf sysfsAttrs // {
137
+
description = "nested attribute set of null or sysfs attribute values";
142
+
sysfs attributes to be set as soon as they become available.
144
+
Attribute names represent path components in the sysfs filesystem and
145
+
cannot be `.` or `..` nor contain any slash character (`/`).
147
+
Names may contain shell‐style glob patterns (`*`, `?` and `[…]`)
148
+
matching a single path component, these should however be used with
149
+
caution, as they may produce unexpected results if attribute paths
152
+
Values will be converted to strings, with list elements concatenated
153
+
with commata and booleans converted to numeric values (`0` or `1`).
155
+
`null` values are ignored, allowing removal of values defined in other
156
+
modules, as are empty attribute sets.
158
+
List values defined in different modules will _not_ be concatenated.
160
+
This option may only be used for attributes which can be set
161
+
idempotently, as the configured values might be written more than once.
166
+
example = lib.literalExpression ''
168
+
# enable transparent hugepages with deferred defragmentaion
169
+
kernel.mm.transparent_hugepage = {
170
+
enabled = "always";
172
+
shmem_enabled = "within_size";
175
+
devices.system.cpu = {
176
+
# configure powesave frequency governor for all CPUs
177
+
# the [0-9]* glob pattern ensures that other paths
178
+
# like cpufreq or cpuidle are not matched
180
+
scaling_governor = "powersave";
181
+
energy_performance_preference = 8;
184
+
# disable frequency boost
185
+
intel_pstate.no_turbo = true;
192
+
config = lib.mkIf (cfg != { }) {
197
+
description = "/%I attribute watcher";
198
+
pathConfig.PathExistsGlob = "/%I";
199
+
unitConfig.DefaultDependencies = false;
203
+
mapAttrsToListRecursive (
208
+
nameValuePair "nixos-sysfs@${escapeSystemdPath (mkPath p)}" {
209
+
overrideStrategy = "asDropin";
210
+
wantedBy = [ "sysinit.target" ];
211
+
before = [ "sysinit.target" ];
216
+
services."nixos-sysfs@" = {
217
+
description = "/%I attribute setter";
220
+
DefaultDependencies = false;
221
+
AssertPathIsMountPoint = "/sys";
222
+
AssertPathExistsGlob = "/%I";
227
+
RemainAfterExit = true;
229
+
# while we could be tempted to use simple shell script to set the
230
+
# sysfs attributes specified by the path or glob pattern, it is
231
+
# almost impossible to properly escape a glob pattern so that it
232
+
# can be used safely in a shell script
233
+
ExecStart = "${lib.getExe' config.systemd.package "systemd-tmpfiles"} --prefix=/sys --create ${tmpfiles}/%i.conf";
235
+
# hardening may be overkill for such a simple and short‐lived
236
+
# service, the following settings would however be suitable to deny
237
+
# access to anything but /sys
238
+
#ProtectProc = "noaccess";
239
+
#ProcSubset = "pid";
240
+
#ProtectSystem = "strict";
241
+
#PrivateDevices = true;
242
+
#SystemCallErrorNumber = "EPERM";
243
+
#SystemCallFilter = [
251
+
warnings = mapAttrsToListRecursive (
254
+
"Attribute path \"${concatStringsSep "." p}\" contains glob patterns. Please ensure that it does not overlap with other paths."
259
+
assertions = mapAttrsToListRecursive (p: v: {
260
+
assertion = all (n: match ''(\.\.?|.*/.*)'' n == null) p;
261
+
message = "Attribute path \"${concatStringsSep "." p}\" has invalid components.";
265
+
meta.maintainers = with lib.maintainers; [ mvs ];