IOTTMCO

Intuitively Obvious to the Most Casual Observer

Battery mode with custom systemd targets

Systemd’s targets are essentially glorified runlevels: they’re the chosen mechanism for easily grouping various services together, and succinctly describing the mode of operation of a system. Like all systemd units, they can have also dependencies, making management a bit easier. By creating a special “battery mode” target, we can facilitate easy or even automatic shutdown of unwanted processes when AC power is lost.

The default systemd setup has two targets of interest to us: multi-user.target and graphical.target. The latter depends on the former. On desktop and laptop systems, graphical.target will be the default; on servers, only multi-user.target will be loaded. For laptop systems, we want to add two more: battery.target to replace multi-user.target in battery mode, and graphical-battery.target to do the same for graphical.target.

Creating these new targets is just a matter of copying the appropriate file from /usr/lib/systemd/system to /etc/systemd/system and deleting or changing the obvious lines. The resulting battery.target is:

[Unit]
Description=Battery
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes

Now set up the dependencies you want by creating a directory battery.target.wants and creating the appropriate symlinks, and the battery target is available for use.

Systemd’s equivalent to changing runlevels is isolate, which shuts down all processes not needed for the specified unit, and then starts that unit. You can restore a system to a “fresh” boot state (provided all processes have been started by systemd services) with systemctl isolate graphical.target. The last line enables running isolate on battery.target. (Make sure to have the same line in graphical-battery.target if you’re using that.) Now you can switch into battery mode with

# systemctl isolate battery.target

and back out by isolating multi-user.target. These commands can be put in the relevant /etc/acpi scripts to have them be run automatically, provided you’re using acpid.