chiark / gitweb /
login: trivial grammar fix
[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 int lock_sessions(Manager *m) {
154         Iterator i;
155         Session *session;
156
157         log_info("Locking sessions...");
158
159         HASHMAP_FOREACH(session, m->sessions, i)
160                 session_send_lock(session, true);
161
162         return 1;
163 }
164
165 static int button_handle(
166                 Button *b,
167                 InhibitWhat inhibit_key,
168                 HandleButton handle,
169                 bool ignore_inhibited,
170                 bool is_edge) {
171
172         static const char * const message_table[_HANDLE_BUTTON_MAX] = {
173                 [HANDLE_POWEROFF] = "Powering Off...",
174                 [HANDLE_REBOOT] = "Rebooting...",
175                 [HANDLE_HALT] = "Halting...",
176                 [HANDLE_KEXEC] = "Rebooting via kexec...",
177                 [HANDLE_SUSPEND] = "Suspending...",
178                 [HANDLE_HIBERNATE] = "Hibernating...",
179                 [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending..."
180         };
181
182         static const char * const target_table[_HANDLE_BUTTON_MAX] = {
183                 [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET,
184                 [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET,
185                 [HANDLE_HALT] = SPECIAL_HALT_TARGET,
186                 [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET,
187                 [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET,
188                 [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
189                 [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET
190         };
191
192         DBusError error;
193         int r;
194         InhibitWhat inhibit_operation;
195
196         assert(b);
197
198         /* If the key handling is turned off, don't do anything */
199         if (handle == HANDLE_IGNORE) {
200                 log_debug("Refusing key handling, as it is turned off.");
201                 return 0;
202         }
203
204         /* If the key handling is inhibited, don't do anything */
205         if (manager_is_inhibited(b->manager, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0)) {
206                 log_debug("Refusing key handling, %s is inhibited.", inhibit_what_to_string(inhibit_key));
207                 return 0;
208         }
209
210         /* Locking is handled differently from the rest. */
211         if (handle == HANDLE_LOCK)
212                 return lock_sessions(b->manager);
213
214         inhibit_operation = handle == HANDLE_SUSPEND || handle == HANDLE_HIBERNATE || handle == HANDLE_HYBRID_SLEEP ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
215
216         /* If the actual operation is inhibited, warn and fail */
217         if (!ignore_inhibited &&
218             manager_is_inhibited(b->manager, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0)) {
219
220
221                 /* If this is just a recheck of the lid switch then don't warn about anything */
222                 if (!is_edge) {
223                         log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_operation));
224                         return 0;
225                 }
226
227                 log_error("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_operation));
228                 warn_melody();
229                 return -EPERM;
230         }
231
232         log_info("%s", message_table[handle]);
233
234         /* We are executing the operation, so make sure we don't
235          * execute another one until the lid is opened/closed again */
236         b->lid_close_queued = false;
237
238         dbus_error_init(&error);
239         r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, target_table[handle], inhibit_operation, &error);
240         if (r < 0) {
241                 log_error("Failed to execute operation: %s", bus_error_message(&error));
242                 dbus_error_free(&error);
243                 return r;
244         }
245
246         return 1;
247 }
248
249 int button_process(Button *b) {
250         struct input_event ev;
251         ssize_t l;
252
253         assert(b);
254
255         l = read(b->fd, &ev, sizeof(ev));
256         if (l < 0)
257                 return errno != EAGAIN ? -errno : 0;
258         if ((size_t) l < sizeof(ev))
259                 return -EIO;
260
261         if (ev.type == EV_KEY && ev.value > 0) {
262
263                 switch (ev.code) {
264
265                 case KEY_POWER:
266                 case KEY_POWER2:
267                         log_info("Power key pressed.");
268                         return button_handle(b, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
269
270                 /* The kernel is a bit confused here:
271
272                    KEY_SLEEP   = suspend-to-ram, which everybody else calls "suspend"
273                    KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
274                 */
275
276                 case KEY_SLEEP:
277                         log_info("Suspend key pressed.");
278                         return button_handle(b, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
279
280                 case KEY_SUSPEND:
281                         log_info("Hibernate key pressed.");
282                         return button_handle(b, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
283                 }
284
285         } else if (ev.type == EV_SW && ev.value > 0) {
286
287                 switch (ev.code) {
288
289                 case SW_LID:
290                         log_info("Lid closed.");
291                         b->lid_close_queued = true;
292
293                         return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, true);
294                 }
295
296         } else if (ev.type == EV_SW && ev.value == 0) {
297
298                 switch (ev.code) {
299
300                 case SW_LID:
301                         log_info("Lid opened.");
302                         b->lid_close_queued = false;
303                         break;
304                 }
305         }
306
307         return 0;
308 }
309
310 int button_recheck(Button *b) {
311         assert(b);
312
313         if (!b->lid_close_queued)
314                 return 0;
315
316         return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, false);
317 }
318
319 static const char* const handle_button_table[_HANDLE_BUTTON_MAX] = {
320         [HANDLE_IGNORE] = "ignore",
321         [HANDLE_POWEROFF] = "poweroff",
322         [HANDLE_REBOOT] = "reboot",
323         [HANDLE_HALT] = "halt",
324         [HANDLE_KEXEC] = "kexec",
325         [HANDLE_SUSPEND] = "suspend",
326         [HANDLE_HIBERNATE] = "hibernate",
327         [HANDLE_HYBRID_SLEEP] = "hybrid-sleep",
328         [HANDLE_LOCK] = "lock"
329 };
330 DEFINE_STRING_TABLE_LOOKUP(handle_button, HandleButton);
331 DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_button, handle_button, HandleButton, "Failed to parse handle button setting");