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