chiark / gitweb /
logind: add new loginctl lock-sessions command
[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                 return log_oom();
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
147 fail:
148         close_nointr_nofail(b->fd);
149         b->fd = -1;
150         return r;
151 }
152
153 static Session *button_get_session(Button *b) {
154         Seat *seat;
155         assert(b);
156
157         if (!b->seat)
158                 return NULL;
159
160         seat = hashmap_get(b->manager->seats, b->seat);
161         if (!seat)
162                 return NULL;
163
164         return seat->active;
165 }
166
167 static int button_power_off(Button *b, HandleButton handle) {
168         DBusError error;
169         int r;
170
171         assert(b);
172
173         if (handle == HANDLE_OFF)
174                 return 0;
175
176         if (handle == HANDLE_NO_SESSION) {
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
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) {
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
224 static int button_suspend(Button *b, HandleButton handle) {
225         DBusError error;
226         int r;
227
228         assert(b);
229
230         if (handle == HANDLE_OFF)
231                 return 0;
232
233         if (handle == HANDLE_NO_SESSION) {
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
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) {
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
280 int 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
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
320 static const char* const handle_button_table[_HANDLE_BUTTON_MAX] = {
321         [HANDLE_OFF] = "off",
322         [HANDLE_NO_SESSION] = "no-session",
323         [HANDLE_TTY_SESSION] = "tty-session",
324         [HANDLE_ANY_SESSION] = "any-session",
325         [HANDLE_ALWAYS] = "always"
326 };
327 DEFINE_STRING_TABLE_LOOKUP(handle_button, HandleButton);
328 DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_button, handle_button, HandleButton, "Failed to parse handle button setting");