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 lock_sessions(Manager *m) {
157 log_info("Locking sessions...");
159 HASHMAP_FOREACH(session, m->sessions, i)
160 session_send_lock(session, true);
165 static int button_handle(
167 InhibitWhat inhibit_key,
169 bool ignore_inhibited,
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..."
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
194 InhibitWhat inhibit_operation;
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.");
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));
210 /* Locking is handled differently from the rest. */
211 if (handle == HANDLE_LOCK)
212 return lock_sessions(b->manager);
214 inhibit_operation = handle == HANDLE_SUSPEND || handle == HANDLE_HIBERNATE || handle == HANDLE_HYBRID_SLEEP ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
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)) {
221 /* If this is just a recheck of the lid switch then don't warn about anything */
223 log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_operation));
227 log_error("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_operation));
232 log_info("%s", message_table[handle]);
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;
238 dbus_error_init(&error);
239 r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, target_table[handle], inhibit_operation, &error);
241 log_error("Failed to execute operation: %s", bus_error_message(&error));
242 dbus_error_free(&error);
249 int button_process(Button *b) {
250 struct input_event ev;
255 l = read(b->fd, &ev, sizeof(ev));
257 return errno != EAGAIN ? -errno : 0;
258 if ((size_t) l < sizeof(ev))
261 if (ev.type == EV_KEY && ev.value > 0) {
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);
270 /* The kernel is a bit confused here:
272 KEY_SLEEP = suspend-to-ram, which everybody else calls "suspend"
273 KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
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);
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);
285 } else if (ev.type == EV_SW && ev.value > 0) {
290 log_info("Lid closed.");
291 b->lid_close_queued = true;
293 return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, true);
296 } else if (ev.type == EV_SW && ev.value == 0) {
301 log_info("Lid opened.");
302 b->lid_close_queued = false;
310 int button_recheck(Button *b) {
313 if (!b->lid_close_queued)
316 return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, false);
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"
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");