Linux:Non-root Suspend

Several years ago I switched to a significantly less user-friendly distro. Since this distro did so little for the end user, I had to start learning how to do some of the things often taken for granted by those using more ready-to-use distros (a group I used to be a part of). One of these seemingly very simple operations was putting my laptop into a suspended state without requiring root access.

Note that this post is a moot point if you are using acpid, as its daemon is started on boot and runs as root, sidestepping the point of this post. I later configured my acpid daemon, but I still wanted to learn how to do this as a regular user.

Linux System Power Management

First, let's cover how you can manage system-wide power in Linux. Keep in mind that this isn't entirely accurate anymore with so many distros using systemd for their init systems. Systemd provides an alternate means than the standard linux sysfs method we will discuss here.

Linux provides a few pseudo-filesystems to make interracting with kernel data structures much easier. Pertinent to this post, the kernel offers the sysfs filesystem. If you would like to know more about the details of this filesystem, its purpose, and other uses, take a quick peek at its man page. I promise, it makes for good bathroom reading.

man 5 sysfs

For today's use, we'll be looking at /sys/power/state. This interface is used for, you guessed it, controlling kernel power states. What fun.

First, let's take a look at what power states are available to us.

$ cat /sys/power/state
freeze mem disk

Without getting into the details of acpi states, here's what those three do.

  • freeze: Lightweight software-only lower power state. Not quite as good as

    suspending, but with a faster resume time. Leaves devices like the cpu running, but in an idle state.

  • mem: Suspend to memory (what we want to do), store cpu state in memory,

    and effectively power down all devices except RAM (acpi state S3).

  • disk: Effectively hibernate. Depending on the system configuration, this

    just powers off the system after writing memory contents to disk. Beware: If you don't have swap set up, you won't be able to recover successfully from this state.

If you want to learn more about Linux kernel power states, have a look at the kernel documentation over at kernel.org.

Suspending to Memory

Suspending to memory is actually very easy. You just need to execute the following command.

echo mem > /sys/power/state

Once done, your computer should suspend shortly thereafter. One key press and it should wake back up. Here's the issue though.

Permission denied?

If you aren't root when you run that command though, you'll see...

echo mem > /sys/power/state
-bash: /sys/power/state: Permission denied

This is because nearly every path in /sys is owned by root:root with permissions of 644 by default. I found that the easiest fix for this, so users could control system state without being granted root privs through sudo (or worse, su), was to write an init script that set permissions on the state file on service start. Here's the one I wrote.

#!/usr/bin/env bash

start() {
  chmod 664 /sys/power/state
  chgrp power /sys/power/state
}

stop() {
  chmod 644 /sys/power/state
  chgrp root /sys/power/state
}

status() {
  if [ "$(stat -c '%a:%G' /sys/power/state)" = '664:power' ]; then
    printf 'running\n'
  else
    printf 'stopped\n'
  fi
}


if [ -z "$(getent group power)" ]; then
  printf "No 'power' group found. Cannot proceed\n"
  exit 2
fi

if [ -z "${1:-}" ]; then
  printf "usage: %s [start|stop|status]\n" "${0}"
  exit 1
elif [ $1 = 'start' ] || [ $1 = 'stop' ] || [ $1 = 'status' ]; then
  ${1}
else
  printf "Unknown action '%s'\n" "${1}"
  exit 1
fi

Note that this init script requires that a 'power' group exist. We only want trusted users to be able to suspend the system. To achieve this, the init script changes ownership of the state file to root:power and sets permissions to 664 (allow group write). To allow a user to suspend the system without elevated privileges, simply add them to the 'power' group.

Admittedly, this restriction is a bit overkill since system suspension is really only useful for user-facing devices (eg: laptops and desktops), which often have only one user. Regardless, if you want to have more than one user, this init script will do the trick for you! If you find you don't care, I've written you a less safe variant of this script!

#!/usr/bin/env bash

start() { chmod 666 /sys/power/state; }

stop() { chmod 644 /sys/power/state; }

status() {
  if [ "$(stat -c '%a' /sys/power/state)" = '666' ]; then
    printf 'running\n'
  else
    printf 'stopped\n'
  fi
}

if [ -z "${1:-}" ]; then
  printf "usage: %s [start|stop|status]\n" "${0}"
  exit 1
elif [ $1 = 'start' ] || [ $1 = 'stop' ] || [ $1 = 'status' ]; then
  ${1}
else
  printf "Unknown action %s\n" "${1}"
  exit 1
fi

Autobots! ... Suspend! ........

Last edited: 2021-01-18 04:00:07 UTC