chiark / gitweb /
login: fix pos-array allocation
[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                 } else if (ev.code == SW_DOCK) {
193                         log_struct(LOG_INFO,
194                                    "MESSAGE=System docked.",
195                                    MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED),
196                                    NULL);
197
198                         b->docked = true;
199                 }
200
201         } else if (ev.type == EV_SW && ev.value == 0) {
202
203                 if (ev.code == SW_LID) {
204                         log_struct(LOG_INFO,
205                                    "MESSAGE=Lid opened.",
206                                    MESSAGE_ID(SD_MESSAGE_LID_OPENED),
207                                    NULL);
208
209                         b->lid_closed = false;
210                         b->check_event_source = sd_event_source_unref(b->check_event_source);
211
212                 } else if (ev.code == SW_DOCK) {
213                         log_struct(LOG_INFO,
214                                    "MESSAGE=System undocked.",
215                                    MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED),
216                                    NULL);
217
218                         b->docked = false;
219                 }
220         }
221
222         return 0;
223 }
224
225 int button_open(Button *b) {
226         char *p, name[256];
227         int r;
228
229         assert(b);
230
231         if (b->fd >= 0) {
232                 close(b->fd);
233                 b->fd = -1;
234         }
235
236         p = strappenda("/dev/input/", b->name);
237
238         b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
239         if (b->fd < 0) {
240                 log_warning("Failed to open %s: %m", b->name);
241                 return -errno;
242         }
243
244         if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
245                 log_error("Failed to get input name: %m");
246                 r = -errno;
247                 goto fail;
248         }
249
250         r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b);
251         if (r < 0) {
252                 log_error("Failed to add button event: %s", strerror(-r));
253                 goto fail;
254         }
255
256         log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
257
258         return 0;
259
260 fail:
261         close(b->fd);
262         b->fd = -1;
263         return r;
264 }
265
266 int button_check_switches(Button *b) {
267         uint8_t switches[SW_MAX/8+1] = {};
268         assert(b);
269
270         if (b->fd < 0)
271                 return -EINVAL;
272
273         if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0)
274                 return -errno;
275
276         b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1;
277         b->docked = (switches[SW_DOCK/8] >> (SW_DOCK % 8)) & 1;
278
279         if (b->lid_closed)
280                 button_install_check_event_source(b);
281
282         return 0;
283 }