1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
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.
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.
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/>.
26 #include <sys/ioctl.h>
28 #include <linux/input.h>
29 #include <sys/epoll.h>
31 #include "conf-parser.h"
33 #include "logind-button.h"
35 #include "dbus-common.h"
37 Button* button_new(Manager *m, const char *name) {
47 b->name = strdup(name);
53 if (hashmap_put(m->buttons, b->name, b) < 0) {
65 void button_free(Button *b) {
68 hashmap_remove(b->manager->buttons, b->name);
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);
81 int button_set_seat(Button *b, const char *sn) {
97 int button_open(Button *b) {
99 struct epoll_event ev;
105 close_nointr_nofail(b->fd);
109 p = strappend("/dev/input/", b->name);
113 b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
116 log_warning("Failed to open %s: %m", b->name);
120 if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
121 log_error("Failed to get input name: %m");
128 ev.data.u32 = FD_OTHER_BASE + b->fd;
130 if (epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_ADD, b->fd, &ev) < 0) {
131 log_error("Failed to add to epoll: %m");
136 r = hashmap_put(b->manager->button_fds, INT_TO_PTR(b->fd + 1), b);
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);
143 log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
148 close_nointr_nofail(b->fd);
153 static int button_handle(
155 InhibitWhat inhibit_key,
157 bool ignore_inhibited,
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..."
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
182 InhibitWhat inhibit_operation;
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.");
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));
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);
205 inhibit_operation = handle == HANDLE_SUSPEND || handle == HANDLE_HIBERNATE || handle == HANDLE_HYBRID_SLEEP ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
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)) {
212 /* If this is just a recheck of the lid switch then don't warn about anything */
214 log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_operation));
218 log_error("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_operation));
223 log_info("%s", message_table[handle]);
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;
229 dbus_error_init(&error);
230 r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, target_table[handle], inhibit_operation, &error);
232 log_error("Failed to execute operation: %s", bus_error_message(&error));
233 dbus_error_free(&error);
240 int button_process(Button *b) {
241 struct input_event ev;
246 l = read(b->fd, &ev, sizeof(ev));
248 return errno != EAGAIN ? -errno : 0;
249 if ((size_t) l < sizeof(ev))
252 if (ev.type == EV_KEY && ev.value > 0) {
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);
261 /* The kernel is a bit confused here:
263 KEY_SLEEP = suspend-to-ram, which everybody else calls "suspend"
264 KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
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);
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);
276 } else if (ev.type == EV_SW && ev.value > 0) {
281 log_info("Lid closed.");
282 b->lid_close_queued = true;
284 return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, true);
287 } else if (ev.type == EV_SW && ev.value == 0) {
292 log_info("Lid opened.");
293 b->lid_close_queued = false;
301 int button_recheck(Button *b) {
304 if (!b->lid_close_queued)
307 return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, false);
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"
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");