chiark / gitweb /
treewide: another round of simplifications
[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
30 #include "sd-messages.h"
31 #include "conf-parser.h"
32 #include "util.h"
33 #include "special.h"
34 #include "logind-button.h"
35
36 Button* button_new(Manager *m, const char *name) {
37         Button *b;
38
39         assert(m);
40         assert(name);
41
42         b = new0(Button, 1);
43         if (!b)
44                 return NULL;
45
46         b->name = strdup(name);
47         if (!b->name) {
48                 free(b);
49                 return NULL;
50         }
51
52         if (hashmap_put(m->buttons, b->name, b) < 0) {
53                 free(b->name);
54                 free(b);
55                 return NULL;
56         }
57
58         b->manager = m;
59         b->fd = -1;
60
61         return b;
62 }
63
64 void button_free(Button *b) {
65         assert(b);
66
67         hashmap_remove(b->manager->buttons, b->name);
68
69         sd_event_source_unref(b->io_event_source);
70         sd_event_source_unref(b->check_event_source);
71
72         if (b->fd >= 0) {
73                 /* If the device has been unplugged close() returns
74                  * ENODEV, let's ignore this, hence we don't use
75                  * safe_close() */
76                 close(b->fd);
77         }
78
79         free(b->name);
80         free(b->seat);
81         free(b);
82 }
83
84 int button_set_seat(Button *b, const char *sn) {
85         char *s;
86
87         assert(b);
88         assert(sn);
89
90         s = strdup(sn);
91         if (!s)
92                 return -ENOMEM;
93
94         free(b->seat);
95         b->seat = s;
96
97         return 0;
98 }
99
100 static void button_lid_switch_handle_action(Manager *manager, bool is_edge) {
101         HandleAction handle_action;
102
103         assert(manager);
104
105         /* If we are docked, handle the lid switch differently */
106         if (manager_is_docked_or_multiple_displays(manager))
107                 handle_action = manager->handle_lid_switch_docked;
108         else
109                 handle_action = manager->handle_lid_switch;
110
111         manager_handle_action(manager, INHIBIT_HANDLE_LID_SWITCH, handle_action, manager->lid_switch_ignore_inhibited, is_edge);
112 }
113
114 static int button_recheck(sd_event_source *e, void *userdata) {
115         Button *b = userdata;
116
117         assert(b);
118         assert(b->lid_closed);
119
120         button_lid_switch_handle_action(b->manager, false);
121         return 1;
122 }
123
124 static int button_install_check_event_source(Button *b) {
125         int r;
126         assert(b);
127
128         /* Install a post handler, so that we keep rechecking as long as the lid is closed. */
129
130         if (b->check_event_source)
131                 return 0;
132
133         r = sd_event_add_post(b->manager->event, &b->check_event_source, button_recheck, b);
134         if (r < 0)
135                 return r;
136
137         return sd_event_source_set_priority(b->check_event_source, SD_EVENT_PRIORITY_IDLE+1);
138 }
139
140 static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
141         Button *b = userdata;
142         struct input_event ev;
143         ssize_t l;
144
145         assert(s);
146         assert(fd == b->fd);
147         assert(b);
148
149         l = read(b->fd, &ev, sizeof(ev));
150         if (l < 0)
151                 return errno != EAGAIN ? -errno : 0;
152         if ((size_t) l < sizeof(ev))
153                 return -EIO;
154
155         if (ev.type == EV_KEY && ev.value > 0) {
156
157                 switch (ev.code) {
158
159                 case KEY_POWER:
160                 case KEY_POWER2:
161                         log_struct(LOG_INFO,
162                                    LOG_MESSAGE("Power key pressed."),
163                                    LOG_MESSAGE_ID(SD_MESSAGE_POWER_KEY),
164                                    NULL);
165
166                         manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
167                         break;
168
169                 /* The kernel is a bit confused here:
170
171                    KEY_SLEEP   = suspend-to-ram, which everybody else calls "suspend"
172                    KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
173                 */
174
175                 case KEY_SLEEP:
176                         log_struct(LOG_INFO,
177                                    LOG_MESSAGE("Suspend key pressed."),
178                                    LOG_MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
179                                    NULL);
180
181                         manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
182                         break;
183
184                 case KEY_SUSPEND:
185                         log_struct(LOG_INFO,
186                                    LOG_MESSAGE("Hibernate key pressed."),
187                                    LOG_MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
188                                    NULL);
189
190                         manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
191                         break;
192                 }
193
194         } else if (ev.type == EV_SW && ev.value > 0) {
195
196                 if (ev.code == SW_LID) {
197                         log_struct(LOG_INFO,
198                                    LOG_MESSAGE("Lid closed."),
199                                    LOG_MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
200                                    NULL);
201
202                         b->lid_closed = true;
203                         button_lid_switch_handle_action(b->manager, true);
204                         button_install_check_event_source(b);
205
206                 } else if (ev.code == SW_DOCK) {
207                         log_struct(LOG_INFO,
208                                    LOG_MESSAGE("System docked."),
209                                    LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED),
210                                    NULL);
211
212                         b->docked = true;
213                 }
214
215         } else if (ev.type == EV_SW && ev.value == 0) {
216
217                 if (ev.code == SW_LID) {
218                         log_struct(LOG_INFO,
219                                    LOG_MESSAGE("Lid opened."),
220                                    LOG_MESSAGE_ID(SD_MESSAGE_LID_OPENED),
221                                    NULL);
222
223                         b->lid_closed = false;
224                         b->check_event_source = sd_event_source_unref(b->check_event_source);
225
226                 } else if (ev.code == SW_DOCK) {
227                         log_struct(LOG_INFO,
228                                    LOG_MESSAGE("System undocked."),
229                                    LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED),
230                                    NULL);
231
232                         b->docked = false;
233                 }
234         }
235
236         return 0;
237 }
238
239 int button_open(Button *b) {
240         char *p, name[256];
241         int r;
242
243         assert(b);
244
245         if (b->fd >= 0) {
246                 close(b->fd);
247                 b->fd = -1;
248         }
249
250         p = strappenda("/dev/input/", b->name);
251
252         b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
253         if (b->fd < 0)
254                 return log_warning_errno(errno, "Failed to open %s: %m", b->name);
255
256         if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
257                 log_error_errno(errno, "Failed to get input name: %m");
258                 r = -errno;
259                 goto fail;
260         }
261
262         r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b);
263         if (r < 0) {
264                 log_error_errno(r, "Failed to add button event: %m");
265                 goto fail;
266         }
267
268         log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
269
270         return 0;
271
272 fail:
273         close(b->fd);
274         b->fd = -1;
275         return r;
276 }
277
278 int button_check_switches(Button *b) {
279         uint8_t switches[SW_MAX/8+1] = {};
280         assert(b);
281
282         if (b->fd < 0)
283                 return -EINVAL;
284
285         if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0)
286                 return -errno;
287
288         b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1;
289         b->docked = (switches[SW_DOCK/8] >> (SW_DOCK % 8)) & 1;
290
291         if (b->lid_closed)
292                 button_install_check_event_source(b);
293
294         return 0;
295 }