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);
111 log_error("Out of memory");
115 b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
118 log_warning("Failed to open %s: %m", b->name);
122 if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
123 log_error("Failed to get input name: %m");
130 ev.data.u32 = FD_OTHER_BASE + b->fd;
132 if (epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_ADD, b->fd, &ev) < 0) {
133 log_error("Failed to add to epoll: %m");
138 r = hashmap_put(b->manager->button_fds, INT_TO_PTR(b->fd + 1), b);
140 log_error("Failed to add to hash map: %s", strerror(-r));
141 assert_se(epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_DEL, b->fd, NULL) == 0);
145 log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
150 close_nointr_nofail(b->fd);
155 static Session *button_get_session(Button *b) {
162 seat = hashmap_get(b->manager->seats, b->seat);
169 static int button_power_off(Button *b, HandleButton handle) {
175 if (handle == HANDLE_OFF)
178 if (handle == HANDLE_NO_SESSION) {
179 if (hashmap_size(b->manager->sessions) > 0) {
180 log_error("Refusing power-off, user is logged in.");
185 } else if (handle == HANDLE_TTY_SESSION ||
186 handle == HANDLE_ANY_SESSION) {
190 n = hashmap_size(b->manager->sessions);
191 s = button_get_session(b);
193 /* Silently ignore events of graphical sessions */
194 if (handle == HANDLE_TTY_SESSION &&
195 s && s->type == SESSION_X11)
198 if (n > 1 || (n == 1 && !s)) {
199 log_error("Refusing power-off, other user is logged in.");
206 if (handle != HANDLE_ALWAYS) {
207 if (manager_is_inhibited(b->manager, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL)) {
208 log_error("Refusing power-off, shutdown is inhibited.");
214 log_info("Powering off...");
216 dbus_error_init(&error);
217 r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, SPECIAL_POWEROFF_TARGET, INHIBIT_SHUTDOWN, &error);
219 log_error("Failed to power off: %s", bus_error_message(&error));
220 dbus_error_free(&error);
226 static int button_suspend(Button *b, HandleButton handle) {
232 if (handle == HANDLE_OFF)
235 if (handle == HANDLE_NO_SESSION) {
236 if (hashmap_size(b->manager->sessions) > 0) {
237 log_error("Refusing suspend, user is logged in.");
242 } else if (handle == HANDLE_TTY_SESSION ||
243 handle == HANDLE_ANY_SESSION) {
247 n = hashmap_size(b->manager->sessions);
248 s = button_get_session(b);
250 /* Silently ignore events of graphical sessions */
251 if (handle == HANDLE_TTY_SESSION &&
252 s && s->type == SESSION_X11)
255 if (n > 1 || (n == 1 && !s)) {
256 log_error("Refusing suspend, other user is logged in.");
262 if (handle != HANDLE_ALWAYS) {
263 if (manager_is_inhibited(b->manager, INHIBIT_SLEEP, INHIBIT_BLOCK, NULL)) {
264 log_error("Refusing suspend, sleeping is inhibited.");
270 log_info("Suspending...");
272 dbus_error_init(&error);
273 r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, SPECIAL_SUSPEND_TARGET, INHIBIT_SLEEP, &error);
275 log_error("Failed to suspend: %s", bus_error_message(&error));
276 dbus_error_free(&error);
282 int button_process(Button *b) {
283 struct input_event ev;
288 l = read(b->fd, &ev, sizeof(ev));
290 return errno != EAGAIN ? -errno : 0;
291 if ((size_t) l < sizeof(ev))
294 if (ev.type == EV_KEY && ev.value > 0) {
300 log_info("Power key pressed.");
301 return button_power_off(b, b->manager->handle_power_key);
305 log_info("Sleep key pressed.");
306 return button_suspend(b, b->manager->handle_sleep_key);
309 } else if (ev.type == EV_SW && ev.value > 0) {
314 log_info("Lid closed.");
315 return button_suspend(b, b->manager->handle_lid_switch);
322 static const char* const handle_button_table[_HANDLE_BUTTON_MAX] = {
323 [HANDLE_OFF] = "off",
324 [HANDLE_NO_SESSION] = "no-session",
325 [HANDLE_TTY_SESSION] = "tty-session",
326 [HANDLE_ANY_SESSION] = "any-session",
327 [HANDLE_ALWAYS] = "always"
329 DEFINE_STRING_TABLE_LOOKUP(handle_button, HandleButton);
330 DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_button, handle_button, HandleButton, "Failed to parse handle button setting");