chiark / gitweb /
unit-name: style fix in unit_name_is_template()
[elogind.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");