]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-button.c
journal: when comparing two entries from separate files make sure we reposition the...
[thirdparty/systemd.git] / src / login / logind-button.c
CommitLineData
e9d21f24
LP
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
37Button* 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
65void 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
81int 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
97int 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);
0d0f0c50
SL
110 if (!p)
111 return log_oom();
e9d21f24
LP
112
113 b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
114 free(p);
115 if (b->fd < 0) {
116 log_warning("Failed to open %s: %m", b->name);
117 return -errno;
118 }
119
120 if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
121 log_error("Failed to get input name: %m");
122 r = -errno;
123 goto fail;
124 }
125
126 zero(ev);
127 ev.events = EPOLLIN;
128 ev.data.u32 = FD_OTHER_BASE + b->fd;
129
130 if (epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_ADD, b->fd, &ev) < 0) {
131 log_error("Failed to add to epoll: %m");
132 r = -errno;
133 goto fail;
134 }
135
136 r = hashmap_put(b->manager->button_fds, INT_TO_PTR(b->fd + 1), b);
137 if (r < 0) {
138 log_error("Failed to add to hash map: %s", strerror(-r));
139 assert_se(epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_DEL, b->fd, NULL) == 0);
140 goto fail;
141 }
142
143 log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
144
145 return 0;
146
147fail:
148 close_nointr_nofail(b->fd);
149 b->fd = -1;
150 return r;
151}
152
6de0e0e5
LP
153static Session *button_get_session(Button *b) {
154 Seat *seat;
155 assert(b);
e9d21f24 156
6de0e0e5
LP
157 if (!b->seat)
158 return NULL;
e9d21f24 159
6de0e0e5
LP
160 seat = hashmap_get(b->manager->seats, b->seat);
161 if (!seat)
162 return NULL;
e9d21f24 163
6de0e0e5 164 return seat->active;
e9d21f24
LP
165}
166
167static int button_power_off(Button *b, HandleButton handle) {
168 DBusError error;
169 int r;
170
171 assert(b);
172
6de0e0e5 173 if (handle == HANDLE_OFF)
e9d21f24
LP
174 return 0;
175
6de0e0e5 176 if (handle == HANDLE_NO_SESSION) {
e9d21f24
LP
177 if (hashmap_size(b->manager->sessions) > 0) {
178 log_error("Refusing power-off, user is logged in.");
179 warn_melody();
180 return -EPERM;
181 }
182
6de0e0e5
LP
183 } else if (handle == HANDLE_TTY_SESSION ||
184 handle == HANDLE_ANY_SESSION) {
185 unsigned n;
186 Session *s;
187
188 n = hashmap_size(b->manager->sessions);
189 s = button_get_session(b);
190
191 /* Silently ignore events of graphical sessions */
192 if (handle == HANDLE_TTY_SESSION &&
193 s && s->type == SESSION_X11)
194 return 0;
195
196 if (n > 1 || (n == 1 && !s)) {
197 log_error("Refusing power-off, other user is logged in.");
198 warn_melody();
199 return -EPERM;
200 }
201
202 }
203
204 if (handle != HANDLE_ALWAYS) {
e9d21f24
LP
205 if (manager_is_inhibited(b->manager, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL)) {
206 log_error("Refusing power-off, shutdown is inhibited.");
207 warn_melody();
208 return -EPERM;
209 }
210 }
211
212 log_info("Powering off...");
213
214 dbus_error_init(&error);
215 r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, SPECIAL_POWEROFF_TARGET, INHIBIT_SHUTDOWN, &error);
216 if (r < 0) {
217 log_error("Failed to power off: %s", bus_error_message(&error));
218 dbus_error_free(&error);
219 }
220
221 return r;
222}
223
224static int button_suspend(Button *b, HandleButton handle) {
225 DBusError error;
226 int r;
227
228 assert(b);
229
6de0e0e5 230 if (handle == HANDLE_OFF)
e9d21f24
LP
231 return 0;
232
6de0e0e5 233 if (handle == HANDLE_NO_SESSION) {
e9d21f24
LP
234 if (hashmap_size(b->manager->sessions) > 0) {
235 log_error("Refusing suspend, user is logged in.");
236 warn_melody();
237 return -EPERM;
238 }
239
6de0e0e5
LP
240 } else if (handle == HANDLE_TTY_SESSION ||
241 handle == HANDLE_ANY_SESSION) {
242 unsigned n;
243 Session *s;
244
245 n = hashmap_size(b->manager->sessions);
246 s = button_get_session(b);
247
248 /* Silently ignore events of graphical sessions */
249 if (handle == HANDLE_TTY_SESSION &&
250 s && s->type == SESSION_X11)
251 return 0;
252
253 if (n > 1 || (n == 1 && !s)) {
254 log_error("Refusing suspend, other user is logged in.");
255 warn_melody();
256 return -EPERM;
257 }
258 }
259
260 if (handle != HANDLE_ALWAYS) {
e9d21f24
LP
261 if (manager_is_inhibited(b->manager, INHIBIT_SLEEP, INHIBIT_BLOCK, NULL)) {
262 log_error("Refusing suspend, sleeping is inhibited.");
263 warn_melody();
264 return -EPERM;
265 }
266 }
267
268 log_info("Suspending...");
269
270 dbus_error_init(&error);
271 r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, SPECIAL_SUSPEND_TARGET, INHIBIT_SLEEP, &error);
272 if (r < 0) {
273 log_error("Failed to suspend: %s", bus_error_message(&error));
274 dbus_error_free(&error);
275 }
276
277 return r;
278}
279
280int button_process(Button *b) {
281 struct input_event ev;
282 ssize_t l;
283
284 assert(b);
285
286 l = read(b->fd, &ev, sizeof(ev));
287 if (l < 0)
288 return errno != EAGAIN ? -errno : 0;
289 if ((size_t) l < sizeof(ev))
290 return -EIO;
291
e9d21f24
LP
292 if (ev.type == EV_KEY && ev.value > 0) {
293
294 switch (ev.code) {
295
296 case KEY_POWER:
297 case KEY_POWER2:
298 log_info("Power key pressed.");
299 return button_power_off(b, b->manager->handle_power_key);
300
301 case KEY_SLEEP:
302 case KEY_SUSPEND:
303 log_info("Sleep key pressed.");
304 return button_suspend(b, b->manager->handle_sleep_key);
305
306 }
307 } else if (ev.type == EV_SW && ev.value > 0) {
308
309 switch (ev.code) {
310
311 case SW_LID:
312 log_info("Lid closed.");
313 return button_suspend(b, b->manager->handle_lid_switch);
314 }
315 }
316
317 return 0;
318}
319
320static const char* const handle_button_table[_HANDLE_BUTTON_MAX] = {
6de0e0e5
LP
321 [HANDLE_OFF] = "off",
322 [HANDLE_NO_SESSION] = "no-session",
323 [HANDLE_TTY_SESSION] = "tty-session",
324 [HANDLE_ANY_SESSION] = "any-session",
e9d21f24
LP
325 [HANDLE_ALWAYS] = "always"
326};
327DEFINE_STRING_TABLE_LOOKUP(handle_button, HandleButton);
328DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_button, handle_button, HandleButton, "Failed to parse handle button setting");