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 Session *button_get_session(Button *b) {
160 seat = hashmap_get(b->manager->seats, b->seat);
167 static int button_power_off(Button *b, HandleButton handle) {
173 if (handle == HANDLE_OFF)
176 if (handle == HANDLE_NO_SESSION) {
177 if (hashmap_size(b->manager->sessions) > 0) {
178 log_error("Refusing power-off, user is logged in.");
183 } else if (handle == HANDLE_TTY_SESSION ||
184 handle == HANDLE_ANY_SESSION) {
188 n = hashmap_size(b->manager->sessions);
189 s = button_get_session(b);
191 /* Silently ignore events of graphical sessions */
192 if (handle == HANDLE_TTY_SESSION &&
193 s && s->type == SESSION_X11)
196 if (n > 1 || (n == 1 && !s)) {
197 log_error("Refusing power-off, other user is logged in.");
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.");
212 log_info("Powering off...");
214 dbus_error_init(&error);
215 r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, SPECIAL_POWEROFF_TARGET, INHIBIT_SHUTDOWN, &error);
217 log_error("Failed to power off: %s", bus_error_message(&error));
218 dbus_error_free(&error);
224 static int button_suspend(Button *b, HandleButton handle) {
230 if (handle == HANDLE_OFF)
233 if (handle == HANDLE_NO_SESSION) {
234 if (hashmap_size(b->manager->sessions) > 0) {
235 log_error("Refusing suspend, user is logged in.");
240 } else if (handle == HANDLE_TTY_SESSION ||
241 handle == HANDLE_ANY_SESSION) {
245 n = hashmap_size(b->manager->sessions);
246 s = button_get_session(b);
248 /* Silently ignore events of graphical sessions */
249 if (handle == HANDLE_TTY_SESSION &&
250 s && s->type == SESSION_X11)
253 if (n > 1 || (n == 1 && !s)) {
254 log_error("Refusing suspend, other user is logged in.");
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.");
268 log_info("Suspending...");
270 dbus_error_init(&error);
271 r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, SPECIAL_SUSPEND_TARGET, INHIBIT_SLEEP, &error);
273 log_error("Failed to suspend: %s", bus_error_message(&error));
274 dbus_error_free(&error);
280 int button_process(Button *b) {
281 struct input_event ev;
286 l = read(b->fd, &ev, sizeof(ev));
288 return errno != EAGAIN ? -errno : 0;
289 if ((size_t) l < sizeof(ev))
292 if (ev.type == EV_KEY && ev.value > 0) {
298 log_info("Power key pressed.");
299 return button_power_off(b, b->manager->handle_power_key);
303 log_info("Sleep key pressed.");
304 return button_suspend(b, b->manager->handle_sleep_key);
307 } else if (ev.type == EV_SW && ev.value > 0) {
312 log_info("Lid closed.");
313 return button_suspend(b, b->manager->handle_lid_switch);
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"
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");