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
75 Once done, your computer should suspend shortly thereafter. One key press and
76 it should wake back up. Here's the issue though.
77
78
79 Permission denied?
80 ------------------
81
82 If you aren't root when you run that command though, you'll see...
83
84 .. code-block:: sh
85
86 echo mem > /sys/power/state
87 -bash: /sys/power/state: Permission denied
88
89 This is because nearly every path in /sys is owned by root:root with
90 permissions of 644 by default. I found that the easiest fix for this, so users
91 could control system state without being granted root privs through sudo (or
92 worse, su), was to write an init script that set permissions on the state file
93 on service start. Here's the one I wrote.
94
95 .. code-block:: sh
96
97 #!/usr/bin/env bash
98
99 start() {
100 chmod 664 /sys/power/state
101 chgrp power /sys/power/state
102 }
103
104 stop() {
105 chmod 644 /sys/power/state
106 chgrp root /sys/power/state
107 }
108
109 status() {
110 if [ "$(stat -c '%a:%G' /sys/power/state)" = '664:power' ]; then
111 printf 'running\n'
112 else
113 printf 'stopped\n'
114 fi
115 }
116
117
118 if [ -z "$(getent group power)" ]; then
119 printf "No 'power' group found. Cannot proceed\n"
120 exit 2
121 fi
122
123 if [ -z "${1:-}" ]; then
124 printf "usage: %s [start|stop|status]\n" "${0}"
125 exit 1
126 elif [ $1 = 'start' ] || [ $1 = 'stop' ] || [ $1 = 'status' ]; then
127 ${1}
128 else
129 printf "Unknown action '%s'\n" "${1}"
130 exit 1
131 fi
132
133
134 Note that this init script requires that a 'power' group exist. We only want
135 trusted users to be able to suspend the system. To achieve this, the init
136 script changes ownership of the state file to **root:power** and sets
137 permissions to 664 (allow group write). To allow a user to suspend the system
138 without elevated privileges, simply add them to the 'power' group.
139
140 Admittedly, this restriction is a bit overkill since system suspension is
141 really only useful for user-facing devices (eg: laptops and desktops), which
142 often have only one user. Regardless, if you want to have more than one user,
143 this init script will do the trick for you! If you find you don't care, I've
144 written you a less safe variant of this script!
145
146 .. code-block:: sh
147
148 #!/usr/bin/env bash
149
150 start() { chmod 666 /sys/power/state; }
151
152 stop() { chmod 644 /sys/power/state; }
153
154 status() {
155 if [ "$(stat -c '%a' /sys/power/state)" = '666' ]; then
156 printf 'running\n'
157 else
158 printf 'stopped\n'
159 fi
160 }
161
162 if [ -z "${1:-}" ]; then
163 printf "usage: %s [start|stop|status]\n" "${0}"
164 exit 1
165 elif [ $1 = 'start' ] || [ $1 = 'stop' ] || [ $1 = 'status' ]; then
166 ${1}
167 else
168 printf "Unknown action %s\n" "${1}"
169 exit 1
170 fi
171
172 Autobots! ... Suspend! ........
|