-
-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
nixos/acpid: refactor and add initrd support #277898
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -3,153 +3,189 @@ | |||
with lib; | ||||
|
||||
let | ||||
cfg = config.services.acpid; | ||||
|
||||
canonicalHandlers = { | ||||
powerEvent = { | ||||
event = "button/power.*"; | ||||
action = cfg.powerEventCommands; | ||||
}; | ||||
|
||||
lidEvent = { | ||||
event = "button/lid.*"; | ||||
action = cfg.lidEventCommands; | ||||
}; | ||||
|
||||
acEvent = { | ||||
event = "ac_adapter.*"; | ||||
action = cfg.acEventCommands; | ||||
}; | ||||
}; | ||||
|
||||
acpiConfDir = pkgs.runCommand "acpi-events" { preferLocalBuild = true; } | ||||
'' | ||||
mkdir -p $out | ||||
${ | ||||
# Generate a configuration file for each event. (You can't have | ||||
# multiple events in one config file...) | ||||
let f = name: handler: | ||||
'' | ||||
fn=$out/${name} | ||||
echo "event=${handler.event}" > $fn | ||||
echo "action=${pkgs.writeShellScriptBin "${name}.sh" handler.action }/bin/${name}.sh '%e'" >> $fn | ||||
''; | ||||
in concatStringsSep "\n" (mapAttrsToList f (canonicalHandlers // cfg.handlers)) | ||||
} | ||||
stage1Cfg = config.boot.initrd.services.acpid; | ||||
stage2Cfg = config.services.acpid; | ||||
|
||||
# Created as a single derivation for simpler use in initrd | ||||
mkAcpiHandlers = handlers: let | ||||
mkScript = name: handler: '' | ||||
echo ${lib.escapeShellArg handler.action} > "$out/scripts/${name}.sh" | ||||
chmod +x "$out/scripts/${name}.sh" | ||||
''; | ||||
|
||||
in | ||||
|
||||
{ | ||||
|
||||
###### interface | ||||
mkHandler = name: handler: '' | ||||
echo "event=${handler.event}" > "$out/handlers/${name}" | ||||
echo "action=$out/scripts/${name}.sh" >> "$out/handlers/${name}" | ||||
outfoxxed marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
''; | ||||
in pkgs.runCommand "acpi-events" { preferLocalBuild = true; } '' | ||||
outfoxxed marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
mkdir -p $out/scripts | ||||
mkdir -p $out/handlers | ||||
outfoxxed marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
${concatStringsSep "\n" (mapAttrsToList mkScript handlers)} | ||||
${concatStringsSep "\n" (mapAttrsToList mkHandler handlers)} | ||||
''; | ||||
|
||||
acpiConfFor = cfg: let | ||||
canonicalHandlers = { | ||||
powerEvent = { | ||||
event = "button/power.*"; | ||||
action = cfg.powerEventCommands; | ||||
}; | ||||
|
||||
options = { | ||||
lidEvent = { | ||||
event = "button/lid.*"; | ||||
action = cfg.lidEventCommands; | ||||
}; | ||||
|
||||
services.acpid = { | ||||
acEvent = { | ||||
event = "ac_adapter.*"; | ||||
action = cfg.acEventCommands; | ||||
}; | ||||
}; | ||||
in mkAcpiHandlers ((lib.filterAttrs (_: handler: handler.action != "") canonicalHandlers) // cfg.handlers); | ||||
|
||||
enable = mkEnableOption (lib.mdDoc "the ACPI daemon"); | ||||
options = stage2: { | ||||
enable = mkEnableOption (lib.mdDoc "the ACPI daemon"); | ||||
package = mkPackageOption pkgs "acpid" {}; | ||||
|
||||
logEvents = mkOption { | ||||
type = types.bool; | ||||
default = false; | ||||
description = lib.mdDoc "Log all event activity."; | ||||
}; | ||||
logEvents = mkOption { | ||||
type = types.bool; | ||||
default = false; | ||||
description = lib.mdDoc "Log all event activity"; | ||||
}; | ||||
|
||||
handlers = mkOption { | ||||
type = types.attrsOf (types.submodule { | ||||
options = { | ||||
event = mkOption { | ||||
type = types.str; | ||||
example = literalExpression ''"button/power.*" "button/lid.*" "ac_adapter.*" "button/mute.*" "button/volumedown.*" "cd/play.*" "cd/next.*"''; | ||||
description = lib.mdDoc "Event type."; | ||||
}; | ||||
|
||||
action = mkOption { | ||||
type = types.lines; | ||||
description = lib.mdDoc "Shell commands to execute when the event is triggered."; | ||||
}; | ||||
}; | ||||
}); | ||||
|
||||
description = lib.mdDoc '' | ||||
Event handlers. | ||||
|
||||
::: {.note} | ||||
Handler can be a single command. | ||||
::: | ||||
''; | ||||
default = {}; | ||||
example = { | ||||
ac-power = { | ||||
event = "ac_adapter/*"; | ||||
action = '' | ||||
vals=($1) # space separated string to array of multiple values | ||||
case ''${vals[3]} in | ||||
00000000) | ||||
echo unplugged >> /tmp/acpi.log | ||||
;; | ||||
00000001) | ||||
echo plugged in >> /tmp/acpi.log | ||||
;; | ||||
*) | ||||
echo unknown >> /tmp/acpi.log | ||||
;; | ||||
esac | ||||
handlers = mkOption { | ||||
type = types.attrsOf (types.submodule { | ||||
options = { | ||||
event = mkOption { | ||||
type = types.str; | ||||
description = lib.mdDoc ('' | ||||
Event type | ||||
'' + lib.optionalString (!stage2) '' | ||||
|
||||
::: {.note} | ||||
Many events will require kernel modules to be available via `boot.initrd.availableKernelModules`. | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there any other useful information we can give or link how to know which ones are required? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is what I found https://wiki.archlinux.org/title/ACPI_modules |
||||
::: | ||||
''); | ||||
|
||||
example = literalExpression '' | ||||
"button/power.*" "button/lid.*" "ac_adapter.*" "button/mute.*" "button/volumedown.*" "cd/play.*" "cd/next.*" | ||||
''; | ||||
}; | ||||
}; | ||||
}; | ||||
|
||||
powerEventCommands = mkOption { | ||||
type = types.lines; | ||||
default = ""; | ||||
description = lib.mdDoc "Shell commands to execute on a button/power.* event."; | ||||
}; | ||||
|
||||
lidEventCommands = mkOption { | ||||
type = types.lines; | ||||
default = ""; | ||||
description = lib.mdDoc "Shell commands to execute on a button/lid.* event."; | ||||
action = mkOption { | ||||
type = types.lines; | ||||
description = "Shell commands to execute when the event is triggered"; | ||||
}; | ||||
}; | ||||
}); | ||||
|
||||
description = lib.mdDoc '' | ||||
Event handlers | ||||
|
||||
::: {.note} | ||||
Handler can be a single command. | ||||
::: | ||||
''; | ||||
|
||||
default = {}; | ||||
example = { | ||||
ac-power = { | ||||
event = "ac_adapter/*"; | ||||
|
||||
action = '' | ||||
vals=($1) # space separated string to array of multiple values | ||||
case ''${vals[3]} in | ||||
00000000) | ||||
echo unplugged >> /tmp/acpi.log | ||||
;; | ||||
00000001) | ||||
echo plugged in >> /tmp/acpi.log | ||||
;; | ||||
*) | ||||
echo unknown >> /tmp/acpi.log | ||||
;; | ||||
esac | ||||
''; | ||||
}; | ||||
}; | ||||
}; | ||||
|
||||
acEventCommands = mkOption { | ||||
type = types.lines; | ||||
default = ""; | ||||
description = lib.mdDoc "Shell commands to execute on an ac_adapter.* event."; | ||||
}; | ||||
powerEventCommands = mkOption { | ||||
type = types.lines; | ||||
default = ""; | ||||
description = "Shell commands to execute on a button/power.* event"; | ||||
}; | ||||
|
||||
lidEventCommands = mkOption { | ||||
type = types.lines; | ||||
default = ""; | ||||
description = "Shell commands to execute on a button/lid.* event"; | ||||
}; | ||||
|
||||
acEventCommands = mkOption { | ||||
type = types.lines; | ||||
default = ""; | ||||
description = "Shell commands to execute on an ac_adapter.* event"; | ||||
}; | ||||
}; | ||||
|
||||
|
||||
###### implementation | ||||
|
||||
config = mkIf cfg.enable { | ||||
|
||||
mkConfig = cfg: mkIf cfg.enable { | ||||
systemd.services.acpid = { | ||||
description = "ACPI Daemon"; | ||||
documentation = [ "man:acpid(8)" ]; | ||||
|
||||
wantedBy = [ "multi-user.target" ]; | ||||
# Acpid requires /var/run to exist but dosen't create it, and no systemd service seems to create it either. | ||||
# Note this is only a problem for stage1. | ||||
Comment on lines
+138
to
+139
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should rather patch this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Patch what here? Making acpid create or link |
||||
script = '' | ||||
ln -s /run /var/run || true | ||||
|
||||
Comment on lines
+141
to
+142
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||
${cfg.package}/bin/acpid \ | ||||
outfoxxed marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
--foreground \ | ||||
--netlink \ | ||||
--confdir "${acpiConfFor cfg}/handlers" \ | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't the trailing \ causing problems when logEvents is not appended? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it behaves similarly to running it interactively > echo hi \
> \
> \
> \
>
hi
> |
||||
${lib.optionalString cfg.logEvents "--logevents"} | ||||
''; | ||||
|
||||
serviceConfig = { | ||||
ExecStart = escapeShellArgs | ||||
([ "${pkgs.acpid}/bin/acpid" | ||||
"--foreground" | ||||
"--netlink" | ||||
"--confdir" "${acpiConfDir}" | ||||
] ++ optional cfg.logEvents "--logevents" | ||||
); | ||||
}; | ||||
unitConfig = { | ||||
ConditionVirtualization = "!systemd-nspawn"; | ||||
ConditionPathExists = [ "/proc/acpi" ]; | ||||
}; | ||||
|
||||
}; | ||||
|
||||
}; | ||||
in { | ||||
options = { | ||||
services.acpid = options true; | ||||
boot.initrd.services.acpid = options false; | ||||
}; | ||||
|
||||
config = mkMerge [ | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shoudl avoid mkMerge on config. For me that often caused hard to debug infinite recusions but I am not sure if that was only on me. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've never experienced this issue and |
||||
(mkConfig stage2Cfg) | ||||
{ boot.initrd = mkConfig stage1Cfg; } | ||||
|
||||
(mkIf stage2Cfg.enable { | ||||
systemd.services.acpid.wantedBy = [ "multi-user.target" ]; | ||||
}) | ||||
|
||||
(mkIf stage1Cfg.enable { | ||||
assertions = [ { | ||||
assertion = config.boot.initrd.systemd.enable; | ||||
message = "the acpid module only works with systemd based initrd"; | ||||
} ]; | ||||
|
||||
boot.initrd = { | ||||
# Add the kernel modules required by each canonical command type if present | ||||
availableKernelModules = mkMerge [ | ||||
(mkIf (stage1Cfg.powerEventCommands != "" || stage1Cfg.lidEventCommands != "") [ "button" ]) | ||||
(mkIf (stage1Cfg.acEventCommands != "") [ "ac" ]) | ||||
]; | ||||
|
||||
systemd = { | ||||
initrdBin = [ stage1Cfg.package ]; | ||||
storePaths = [ (acpiConfFor stage1Cfg) ]; | ||||
|
||||
services.acpid.wantedBy = [ "initrd.target" ]; | ||||
}; | ||||
}; | ||||
}) | ||||
]; | ||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be confusing as initrd has stage 1 and 2, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so would
initrdCfg
/mainCfg
work? I'm not sure what to name the one underconfig.services
.