chiark / gitweb /
logind: add missing files
[elogind.git] / src / login / logind-button.c
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
37 Button* 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
65 void 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
81 int 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
97 int 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
149 fail:
150         close_nointr_nofail(b->fd);
151         b->fd = -1;
152         return r;
153 }
154
155 static 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
171 static 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
207 static 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
243 int 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
289 static const char* const handle_button_table[_HANDLE_BUTTON_MAX] = {
290         [HANDLE_YES] = "yes",
291         [HANDLE_NO] = "no",
292         [HANDLE_ALWAYS] = "always"
293 };
294 DEFINE_STRING_TABLE_LOOKUP(handle_button, HandleButton);
295 DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_button, handle_button, HandleButton, "Failed to parse handle button setting");