1 Linux:Non-root Suspend
2 ======================
3
4 Several years ago I switched to a significantly less user-friendly distro.
5 Since this distro did so little for the end user, I had to start learning how
6 to do some of the things often taken for granted by those using more
7 ready-to-use distros (a group I used to be a part of). One of these seemingly
8 very simple operations was putting my laptop into a suspended state without
9 requiring root access.
10
11 Note that this post is a moot point if you are using acpid, as its daemon is
12 started on boot and runs as root, sidestepping the point of this post. I later
13 configured my acpid daemon, but I still wanted to learn how to do this as a
14 regular user.
15
16
17 Linux System Power Management
18 -----------------------------
19
20 First, let's cover how you can manage system-wide power in Linux. Keep in mind
21 that this isn't entirely accurate anymore with so many distros using systemd
22 for their init systems. Systemd provides an alternate means than the standard
23 linux sysfs method we will discuss here.
24
25 Linux provides a few pseudo-filesystems to make interracting with kernel data
26 structures much easier. Pertinent to this post, the kernel offers the sysfs
27 filesystem. If you would like to know more about the details of this
28 filesystem, its purpose, and other uses, take a quick peek at its man page. I
29 promise, it makes for good bathroom reading.
30
31
32 .. code-block:: sh
33
34 man 5 sysfs
35
36
37 For today's use, we'll be looking at **/sys/power/state**. This interface is
38 used for, you guessed it, controlling kernel power states. What fun.
39
40 First, let's take a look at what power states are available to us.
41
42 .. code-block:: sh
43
44 $ cat /sys/power/state
45 freeze mem disk
46
47 Without getting into the details of acpi states, here's what those three do.
48
49 * **freeze**: Lightweight software-only lower power state. Not quite as good as
50 suspending, but with a faster resume time. Leaves devices like
51 the cpu running, but in an idle state.
52 * **mem**: Suspend to memory (what we want to do), store cpu state in memory,
53 and effectively power down all devices except RAM (acpi state S3).
54 * **disk**: Effectively hibernate. Depending on the system configuration, this
55 just powers off the system after writing memory contents to disk.
56 **Beware**: If you don't have swap set up, you won't be able to
57 recover successfully from this state.
58
59 If you want to learn more about Linux kernel power states, have a look at the
60 kernel documentation over at
61 `kernel.org <https://www.kernel.org/doc/html/latest/admin-guide/pm/sleep-states.html>`_.
62
63
64 Suspending to Memory
65 --------------------
66
67 Suspending to memory is actually very easy. You just need to execute the
68 following command.
69
70 .. code-block:: sh
71
72 echo mem > /sys/power/state
73
74 Once done, your computer should suspend shortly thereafter. One key press and
75 it should wake back up. Here's the issue though.
76
77
78 Permission denied?
79 ------------------
80
81 If you aren't root when you run that command though, you'll see...
82
83 .. code-block:: sh
84
85 echo mem > /sys/power/state
86 -bash: /sys/power/state: Permission denied
87
88 This is because nearly every path in /sys is owned by root:root with
89 permissions of 644 by default. I found that the easiest fix for this, so users
90 could control system state without being granted root privs through sudo (or
91 worse, su), was to write an init script that set permissions on the state file
92 on service start. Here's the one I wrote.
93
94 .. code-block:: sh
95
96 #!/usr/bin/env bash
97
98 start() {
99 chmod 664 /sys/power/state
100 chgrp power /sys/power/state
101 }
102
103 stop() {
104 chmod 644 /sys/power/state
105 chgrp root /sys/power/state
106 }
107
108 status() {
109 if [ "$(stat -c '%a:%G' /sys/power/state)" = '664:power' ]; then
110 printf 'running\n'
111 else
112 printf 'stopped\n'
113 fi
114 }
115
116
117 if [ -z "$(getent group power)" ]; then
118 printf "No 'power' group found. Cannot proceed\n"
119 exit 2
120 fi
121
122 if [ -z "${1:-}" ]; then
123 printf "usage: %s [start|stop|status]\n" "${0}"
124 exit 1
125 elif [ $1 = 'start' ] || [ $1 = 'stop' ] || [ $1 = 'status' ]; then
126 ${1}
127 else
128 printf "Unknown action '%s'\n" "${1}"
129 exit 1
130 fi
131
132
133 Note that this init script requires that a 'power' group exist. We only want
134 trusted users to be able to suspend the system. To achieve this, the init
135 script changes ownership of the state file to **root:power** and sets
136 permissions to 664 (allow group write). To allow a user to suspend the system
137 without elevated privileges, simply add them to the 'power' group.
138
139 Admittedly, this restriction is a bit overkill since system suspension is
140 really only useful for user-facing devices (eg: laptops and desktops), which
141 often have only one user. Regardless, if you want to have more than one user,
142 this init script will do the trick for you! If you find you don't care, I've
143 written you a less safe variant of this script!
144
145 .. code-block:: sh
146
147 #!/usr/bin/env bash
148
149 start() { chmod 666 /sys/power/state; }
150
151 stop() { chmod 644 /sys/power/state; }
152
153 status() {
154 if [ "$(stat -c '%a' /sys/power/state)" = '666' ]; then
155 printf 'running\n'
156 else
157 printf 'stopped\n'
158 fi
159 }
160
161 if [ -z "${1:-}" ]; then
162 printf "usage: %s [start|stop|status]\n" "${0}"
163 exit 1
164 elif [ $1 = 'start' ] || [ $1 = 'stop' ] || [ $1 = 'status' ]; then
165 ${1}
166 else
167 printf "Unknown action %s\n" "${1}"
168 exit 1
169 fi
170
171 Autobots! ... Suspend! ........
|