Configure acpid From BusyBox in a Custom initrd

Published by Philipp Schuster on

acpid - Terminal Screenshot

I have a setup with a Linux Kernel and a custom initrd, hence, no full Linux distribution. The initrd is meant to be as minimal as it can be. As a consequence, even basic things such as power-off on power-button pressed must be configured manually. There is no init system available – instead, I have to provide the init system.

The initrd contains utilities from busybox. In this blog post, I show you how to configure acpid from busybox in a minimal initrd to power-off the system when the power-button-pressed event was recognized via ACPI.

A quick background information: busybox provides certain tools:

BusyBox combines tiny versions of many common UNIX utilities into a single
small executable. It provides minimalist replacements for most of the
utilities you usually find in bzip2, coreutils, dhcp, diffutils, e2fsprogs,
file, findutils, gawk, grep, inetutils, less, modutils, net-tools, procps,
sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim.

busybox’s README

It also contains acpid, an ACPI daemon. The ACPI daemon is used to define a policy in userland on how certain ACPI events, such as the power button or if the lid got closed, are handled. In order for this to work, the Linux kernel needs the ACPI_BUTTON kernel module. It’s description says:

This driver handles events on the power, sleep, and lid buttons.
A daemon reads events from input devices or via netlink and
performs user-defined actions such as shutting down the system.
This is necessary for software-controlled poweroff.

<linux>/drivers/acpi/Kconfig

Note that if you use the ACPI_TINY_POWER_BUTTON kernel module instead of ACPI_BUTTON, the mechanism to inform the userland over ACPI events does not exist.

It took me a few hours to figure out how all parts interact with each other and to configure acpid properly. In the end it is relatively easy, but there is not much good documentation – the code is mostly the documentation. Here is what I’ve found:

For better understanding, at first, I show the output of acpid -h:

BusyBox v1.35.0 () multi-call binary.

Usage: acpid [-df] [-c CONFDIR] [-l LOGFILE] [-a ACTIONFILE] [-M MAPFILE] [-e PROC_EVENT_FILE] [-p PIDFILE]

Listen to ACPI events and spawn specific helpers on event arrival

        -d      Log to stderr, not log file (implies -f)
        -f      Run in foreground
        -c DIR  Config directory [/etc/acpi]
        -e FILE /proc event file [/proc/acpi/event]
        -l FILE Log file [/var/log/acpid.log]
        -p FILE Pid file [/var/run/acpid.pid]
        -a FILE Action file [/etc/acpid.conf]
        -M FILE Map file [/etc/acpi.map]

Accept and ignore compatibility options -g -m -s -S -

acpid needs /etc/acpi as it’s working directory. When it receives an event from /dev/input/*, acpid executes the file in /etc/acpi/<action>. All supported events are either defined via the optional map file that is by default in /etc/acpi.map or via the built-in configuration given as:

static const struct acpi_event f_evt_tab[] = {
	{ "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRF 00000080" },
	{ "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRB 00000080" },
	{ "EV_SW", 0x05, "SW_LID", 0x00, 1, "button/lid LID0 00000080" },
};

Snippet taken from the source code. The source code can be found on GitHub.

This file maps the KEY_POWER event to the PWRF action. I’m not sure what the difference is between PWRF and PWRB. In my setup, the power button is recognized as PWRF action.

After acpid mapped the event to an action, it looks into the optional action file that is by default in /etc/acpi/ or uses its built-in action configuration:

static const struct acpi_action f_act_tab[] = {
	{ "PWRF", "PWRF/00000080" },
	{ "LID0", "LID/00000080" },
};

As a consequence, a power button event will instruct acpid to execute the file /etc/acpi/PWRF/00000080. The file 00000080 can either be an executable or link to one or contain a shell script with a shebang.

To test and prototype I suggest running acpid in foreground with logging enabled. This requires that you have terminal access to the initrd. Simply run acpid -d. If you run acpid in the background (just invoke acpid), make sure that /var/log/acpid.log exists and is writeable.

As /etc/acpi/PWRF/00000080 has large what-the-fuck potential, we can also configure acpid to use /etc/acpi/power. For this, we create a /etc/acpid.conf with the following content: PWRF power – that’s it! The action files lists pairs of action name to executable. The key and the value are separated by spaces. Each line defines one configuration.

Now, acpid will execute /etc/acpi/power if the power button is pressed, which is mapped to the PWRF action.

For completeness, I show you the content of my /etc/acpi/power script:

#!/bin/sh
# As this runs in the background, we need to write the message directly to the terminal device.
echo "acpid found the power-off event. Executing \"poweroff -f\" now" > /dev/ttyS0
poweroff -f

I hope I could help you to understand the usage of acpid from busybox. Please note that acpid from the https://sourceforge.net/projects/acpid2/ is configured different in an incompatible way.

acpid - Terminal Screenshot
The image shows all the steps from above in a terminal. It shows the file structure and the file contents. Last, it shows the acpid running in the foreground receiving a power-event.

More Resources


Philipp Schuster

Hi, I'm Philipp and interested in Computer Science. I especially like low level development, making ugly things nice, and de-mystify "low level magic".

0 Comments

Leave a Reply

Your email address will not be published.