]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-button.c
logind: add missing files
[thirdparty/systemd.git] / src / login / logind-button.c
CommitLineData
e9d21f24
LP
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
37Button* 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
65void 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
81int 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
97int 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 log_error("Out of memory");
112 return -ENOMEM;
113 }
114
115 b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
116 free(p);
117 if (b->fd < 0) {
118 log_warning("Failed to open %s: %m", b->name);
119 return -errno;
120 }
121
122 if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
123 log_error("Failed to get input name: %m");
124 r = -errno;
125 goto fail;
126 }
127
128 zero(ev);
129 ev.events = EPOLLIN;
130 ev.data.u32 = FD_OTHER_BASE + b->fd;
131
132 if (epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_ADD, b->fd, &ev) < 0) {
133 log_error("Failed to add to epoll: %m");
134 r = -errno;
135 goto fail;
136 }
137
138 r = hashmap_put(b->manager->button_fds, INT_TO_PTR(b->fd + 1), b);
139 if (r < 0) {
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);
142 goto fail;
143 }
144
145 log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
146
147 return 0;
148
149fail:
150 close_nointr_nofail(b->fd);
151 b->fd = -1;
152 return r;
153}
154
155static bool has_graphical_session(Manager *m, const char *seat) {
156 Seat *s;
157
158 assert(m);
159 assert(seat);
160
161 s = hashmap_get(m->seats, seat);
162 if (!s)
163 return false;
164
165 if (!s->active)
166 return false;
167
168 return s->active->type == SESSION_X11;
169}
170
171static int button_power_off(Button *b, HandleButton handle) {
172 DBusError error;
173 int r;
174
175 assert(b);
176
177 if (handle == HANDLE_NO)
178 return 0;
179
180 if (handle != HANDLE_ALWAYS) {
181
182 if (hashmap_size(b->manager->sessions) > 0) {
183 log_error("Refusing power-off, user is logged in.");
184 warn_melody();
185 return -EPERM;
186 }
187
188 if (manager_is_inhibited(b->manager, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL)) {
189 log_error("Refusing power-off, shutdown is inhibited.");
190 warn_melody();
191 return -EPERM;
192 }
193 }
194
195 log_info("Powering off...");
196
197 dbus_error_init(&error);
198 r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, SPECIAL_POWEROFF_TARGET, INHIBIT_SHUTDOWN, &error);
199 if (r < 0) {
200 log_error("Failed to power off: %s", bus_error_message(&error));
201 dbus_error_free(&error);
202 }
203
204 return r;
205}
206
207static int button_suspend(Button *b, HandleButton handle) {
208 DBusError error;
209 int r;
210
211 assert(b);
212
213 if (handle == HANDLE_NO)
214 return 0;
215
216 if (handle != HANDLE_ALWAYS) {
217
218 if (hashmap_size(b->manager->sessions) > 0) {
219 log_error("Refusing suspend, user is logged in.");
220 warn_melody();
221 return -EPERM;
222 }
223
224 if (manager_is_inhibited(b->manager, INHIBIT_SLEEP, INHIBIT_BLOCK, NULL)) {
225 log_error("Refusing suspend, sleeping is inhibited.");
226 warn_melody();
227 return -EPERM;
228 }
229 }
230
231 log_info("Suspending...");
232
233 dbus_error_init(&error);
234 r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, SPECIAL_SUSPEND_TARGET, INHIBIT_SLEEP, &error);
235 if (r < 0) {
236 log_error("Failed to suspend: %s", bus_error_message(&error));
237 dbus_error_free(&error);
238 }
239
240 return r;
241}
242
243int button_process(Button *b) {
244 struct input_event ev;
245 ssize_t l;
246
247 assert(b);
248
249 l = read(b->fd, &ev, sizeof(ev));
250 if (l < 0)
251 return errno != EAGAIN ? -errno : 0;
252 if ((size_t) l < sizeof(ev))
253 return -EIO;
254
255 /* If there's a graphical session on the seat this device
256 * belongs to we ignore events, it is job of the graphical
257 * session to handle the event. */
258 if (has_graphical_session(b->manager, b->seat))
259 return 0;
260
261 if (ev.type == EV_KEY && ev.value > 0) {
262
263 switch (ev.code) {
264
265 case KEY_POWER:
266 case KEY_POWER2:
267 log_info("Power key pressed.");
268 return button_power_off(b, b->manager->handle_power_key);
269
270 case KEY_SLEEP:
271 case KEY_SUSPEND:
272 log_info("Sleep key pressed.");
273 return button_suspend(b, b->manager->handle_sleep_key);
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 return button_suspend(b, b->manager->handle_lid_switch);
283 }
284 }
285
286 return 0;
287}
288
289static const char* const handle_button_table[_HANDLE_BUTTON_MAX] = {
290 [HANDLE_YES] = "yes",
291 [HANDLE_NO] = "no",
292 [HANDLE_ALWAYS] = "always"
293};
294DEFINE_STRING_TABLE_LOOKUP(handle_button, HandleButton);
295DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_button, handle_button, HandleButton, "Failed to parse handle button setting");