]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-button.c
logind: rework button setting semantics
[thirdparty/systemd.git] / src / login / logind-button.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <assert.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <sys/ioctl.h>
27 #include <unistd.h>
28 #include <linux/input.h>
29 #include <sys/epoll.h>
30
31 #include "conf-parser.h"
32 #include "util.h"
33 #include "logind-button.h"
34 #include "special.h"
35 #include "dbus-common.h"
36
37 Button* button_new(Manager *m, const char *name) {
38 Button *b;
39
40 assert(m);
41 assert(name);
42
43 b = new0(Button, 1);
44 if (!b)
45 return NULL;
46
47 b->name = strdup(name);
48 if (!b->name) {
49 free(b);
50 return NULL;
51 }
52
53 if (hashmap_put(m->buttons, b->name, b) < 0) {
54 free(b->name);
55 free(b);
56 return NULL;
57 }
58
59 b->manager = m;
60 b->fd = -1;
61
62 return b;
63 }
64
65 void button_free(Button *b) {
66 assert(b);
67
68 hashmap_remove(b->manager->buttons, b->name);
69
70 if (b->fd >= 0) {
71 hashmap_remove(b->manager->button_fds, INT_TO_PTR(b->fd + 1));
72 assert_se(epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_DEL, b->fd, NULL) == 0);
73 close_nointr_nofail(b->fd);
74 }
75
76 free(b->name);
77 free(b->seat);
78 free(b);
79 }
80
81 int button_set_seat(Button *b, const char *sn) {
82 char *s;
83
84 assert(b);
85 assert(sn);
86
87 s = strdup(sn);
88 if (!s)
89 return -ENOMEM;
90
91 free(b->seat);
92 b->seat = s;
93
94 return 0;
95 }
96
97 int button_open(Button *b) {
98 char name[256], *p;
99 struct epoll_event ev;
100 int r;
101
102 assert(b);
103
104 if (b->fd >= 0) {
105 close_nointr_nofail(b->fd);
106 b->fd = -1;
107 }
108
109 p = strappend("/dev/input/", b->name);
110 if (!p) {
111 log_error("Out of memory");
112 return -ENOMEM;
113 }
114
115 b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
116 free(p);
117 if (b->fd < 0) {
118 log_warning("Failed to open %s: %m", b->name);
119 return -errno;
120 }
121
122 if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
123 log_error("Failed to get input name: %m");
124 r = -errno;
125 goto fail;
126 }
127
128 zero(ev);
129 ev.events = EPOLLIN;
130 ev.data.u32 = FD_OTHER_BASE + b->fd;
131
132 if (epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_ADD, b->fd, &ev) < 0) {
133 log_error("Failed to add to epoll: %m");
134 r = -errno;
135 goto fail;
136 }
137
138 r = hashmap_put(b->manager->button_fds, INT_TO_PTR(b->fd + 1), b);
139 if (r < 0) {
140 log_error("Failed to add to hash map: %s", strerror(-r));
141 assert_se(epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_DEL, b->fd, NULL) == 0);
142 goto fail;
143 }
144
145 log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
146
147 return 0;
148
149 fail:
150 close_nointr_nofail(b->fd);
151 b->fd = -1;
152 return r;
153 }
154
155 static Session *button_get_session(Button *b) {
156 Seat *seat;
157 assert(b);
158
159 if (!b->seat)
160 return NULL;
161
162 seat = hashmap_get(b->manager->seats, b->seat);
163 if (!seat)
164 return NULL;
165
166 return seat->active;
167 }
168
169 static int button_power_off(Button *b, HandleButton handle) {
170 DBusError error;
171 int r;
172
173 assert(b);
174
175 if (handle == HANDLE_OFF)
176 return 0;
177
178 if (handle == HANDLE_NO_SESSION) {
179 if (hashmap_size(b->manager->sessions) > 0) {
180 log_error("Refusing power-off, user is logged in.");
181 warn_melody();
182 return -EPERM;
183 }
184
185 } else if (handle == HANDLE_TTY_SESSION ||
186 handle == HANDLE_ANY_SESSION) {
187 unsigned n;
188 Session *s;
189
190 n = hashmap_size(b->manager->sessions);
191 s = button_get_session(b);
192
193 /* Silently ignore events of graphical sessions */
194 if (handle == HANDLE_TTY_SESSION &&
195 s && s->type == SESSION_X11)
196 return 0;
197
198 if (n > 1 || (n == 1 && !s)) {
199 log_error("Refusing power-off, other user is logged in.");
200 warn_melody();
201 return -EPERM;
202 }
203
204 }
205
206 if (handle != HANDLE_ALWAYS) {
207 if (manager_is_inhibited(b->manager, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL)) {
208 log_error("Refusing power-off, shutdown is inhibited.");
209 warn_melody();
210 return -EPERM;
211 }
212 }
213
214 log_info("Powering off...");
215
216 dbus_error_init(&error);
217 r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, SPECIAL_POWEROFF_TARGET, INHIBIT_SHUTDOWN, &error);
218 if (r < 0) {
219 log_error("Failed to power off: %s", bus_error_message(&error));
220 dbus_error_free(&error);
221 }
222
223 return r;
224 }
225
226 static int button_suspend(Button *b, HandleButton handle) {
227 DBusError error;
228 int r;
229
230 assert(b);
231
232 if (handle == HANDLE_OFF)
233 return 0;
234
235 if (handle == HANDLE_NO_SESSION) {
236 if (hashmap_size(b->manager->sessions) > 0) {
237 log_error("Refusing suspend, user is logged in.");
238 warn_melody();
239 return -EPERM;
240 }
241
242 } else if (handle == HANDLE_TTY_SESSION ||
243 handle == HANDLE_ANY_SESSION) {
244 unsigned n;
245 Session *s;
246
247 n = hashmap_size(b->manager->sessions);
248 s = button_get_session(b);
249
250 /* Silently ignore events of graphical sessions */
251 if (handle == HANDLE_TTY_SESSION &&
252 s && s->type == SESSION_X11)
253 return 0;
254
255 if (n > 1 || (n == 1 && !s)) {
256 log_error("Refusing suspend, other user is logged in.");
257 warn_melody();
258 return -EPERM;
259 }
260 }
261
262 if (handle != HANDLE_ALWAYS) {
263 if (manager_is_inhibited(b->manager, INHIBIT_SLEEP, INHIBIT_BLOCK, NULL)) {
264 log_error("Refusing suspend, sleeping is inhibited.");
265 warn_melody();
266 return -EPERM;
267 }
268 }
269
270 log_info("Suspending...");
271
272 dbus_error_init(&error);
273 r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, SPECIAL_SUSPEND_TARGET, INHIBIT_SLEEP, &error);
274 if (r < 0) {
275 log_error("Failed to suspend: %s", bus_error_message(&error));
276 dbus_error_free(&error);
277 }
278
279 return r;
280 }
281
282 int button_process(Button *b) {
283 struct input_event ev;
284 ssize_t l;
285
286 assert(b);
287
288 l = read(b->fd, &ev, sizeof(ev));
289 if (l < 0)
290 return errno != EAGAIN ? -errno : 0;
291 if ((size_t) l < sizeof(ev))
292 return -EIO;
293
294 if (ev.type == EV_KEY && ev.value > 0) {
295
296 switch (ev.code) {
297
298 case KEY_POWER:
299 case KEY_POWER2:
300 log_info("Power key pressed.");
301 return button_power_off(b, b->manager->handle_power_key);
302
303 case KEY_SLEEP:
304 case KEY_SUSPEND:
305 log_info("Sleep key pressed.");
306 return button_suspend(b, b->manager->handle_sleep_key);
307
308 }
309 } else if (ev.type == EV_SW && ev.value > 0) {
310
311 switch (ev.code) {
312
313 case SW_LID:
314 log_info("Lid closed.");
315 return button_suspend(b, b->manager->handle_lid_switch);
316 }
317 }
318
319 return 0;
320 }
321
322 static const char* const handle_button_table[_HANDLE_BUTTON_MAX] = {
323 [HANDLE_OFF] = "off",
324 [HANDLE_NO_SESSION] = "no-session",
325 [HANDLE_TTY_SESSION] = "tty-session",
326 [HANDLE_ANY_SESSION] = "any-session",
327 [HANDLE_ALWAYS] = "always"
328 };
329 DEFINE_STRING_TABLE_LOOKUP(handle_button, HandleButton);
330 DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_button, handle_button, HandleButton, "Failed to parse handle button setting");