chiark / gitweb /
720071a2a846faeb8cd9b0b9e51ba0c32bdc8a62
[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                  * close_nointr_nofail() */
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 int button_recheck(sd_event_source *e, void *userdata) {
101         Button *b = userdata;
102
103         assert(b);
104         assert(b->lid_closed);
105
106         manager_handle_action(b->manager, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, false);
107         return 1;
108 }
109
110 static int button_install_check_event_source(Button *b) {
111         int r;
112         assert(b);
113
114         /* Install a post handler, so that we keep rechecking as long as the lid is closed. */
115
116         if (b->check_event_source)
117                 return 0;
118
119         r = sd_event_add_post(b->manager->event, &b->check_event_source, button_recheck, b);
120         if (r < 0)
121                 return r;
122
123         return sd_event_source_set_priority(b->check_event_source, SD_EVENT_PRIORITY_IDLE+1);
124 }
125
126 static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
127         Button *b = userdata;
128         struct input_event ev;
129         ssize_t l;
130
131         assert(s);
132         assert(fd == b->fd);
133         assert(b);
134
135         l = read(b->fd, &ev, sizeof(ev));
136         if (l < 0)
137                 return errno != EAGAIN ? -errno : 0;
138         if ((size_t) l < sizeof(ev))
139                 return -EIO;
140
141         if (ev.type == EV_KEY && ev.value > 0) {
142
143                 switch (ev.code) {
144
145                 case KEY_POWER:
146                 case KEY_POWER2:
147                         log_struct(LOG_INFO,
148                                    "MESSAGE=Power key pressed.",
149                                    MESSAGE_ID(SD_MESSAGE_POWER_KEY),
150                                    NULL);
151
152                         manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
153                         break;
154
155                 /* The kernel is a bit confused here:
156
157                    KEY_SLEEP   = suspend-to-ram, which everybody else calls "suspend"
158                    KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
159                 */
160
161                 case KEY_SLEEP:
162                         log_struct(LOG_INFO,
163                                    "MESSAGE=Suspend key pressed.",
164                                    MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
165                                    NULL);
166
167                         manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
168                         break;
169
170                 case KEY_SUSPEND:
171                         log_struct(LOG_INFO,
172                                    "MESSAGE=Hibernate key pressed.",
173                                    MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
174                                    NULL);
175
176                         manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
177                         break;
178                 }
179
180         } else if (ev.type == EV_SW && ev.value > 0) {
181
182                 if (ev.code == SW_LID) {
183                         log_struct(LOG_INFO,
184                                    "MESSAGE=Lid closed.",
185                                    MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
186                                    NULL);
187
188                         b->lid_closed = true;
189                         manager_handle_action(b->manager, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, true);
190                         button_install_check_event_source(b);
191                 }
192
193         } else if (ev.type == EV_SW && ev.value == 0) {
194
195                 if (ev.code == SW_LID) {
196                         log_struct(LOG_INFO,
197                                    "MESSAGE=Lid opened.",
198                                    MESSAGE_ID(SD_MESSAGE_LID_OPENED),
199                                    NULL);
200
201                         b->lid_closed = false;
202                         b->check_event_source = sd_event_source_unref(b->check_event_source);
203                 }
204         }
205
206         return 0;
207 }
208
209 int button_open(Button *b) {
210         char *p, name[256];
211         int r;
212
213         assert(b);
214
215         if (b->fd >= 0) {
216                 close(b->fd);
217                 b->fd = -1;
218         }
219
220         p = strappenda("/dev/input/", b->name);
221
222         b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
223         if (b->fd < 0) {
224                 log_warning("Failed to open %s: %m", b->name);
225                 return -errno;
226         }
227
228         if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
229                 log_error("Failed to get input name: %m");
230                 r = -errno;
231                 goto fail;
232         }
233
234         r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b);
235         if (r < 0) {
236                 log_error("Failed to add button event: %s", strerror(-r));
237                 goto fail;
238         }
239
240         log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
241
242         return 0;
243
244 fail:
245         close(b->fd);
246         b->fd = -1;
247         return r;
248 }
249
250 int button_check_lid(Button *b) {
251         uint8_t switches[SW_MAX/8+1] = {};
252         assert(b);
253
254         if (b->fd < 0)
255                 return -EINVAL;
256
257         if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0)
258                 return -errno;
259
260         b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1;
261
262         if (b->lid_closed) {
263                 manager_handle_action(b->manager, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, true);
264                 button_install_check_event_source(b);
265         }
266
267         return 0;
268 }