chiark / gitweb /
event: be more conservative when returning errors from event handler callbacks
[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->event_source);
70
71         if (b->fd >= 0) {
72                 /* If the device has been unplugged close() returns
73                  * ENODEV, let's ignore this, hence we don't use
74                  * close_nointr_nofail() */
75                 close(b->fd);
76         }
77
78         free(b->name);
79         free(b->seat);
80         free(b);
81 }
82
83 int button_set_seat(Button *b, const char *sn) {
84         char *s;
85
86         assert(b);
87         assert(sn);
88
89         s = strdup(sn);
90         if (!s)
91                 return -ENOMEM;
92
93         free(b->seat);
94         b->seat = s;
95
96         return 0;
97 }
98
99 static int button_handle(
100                 Button *b,
101                 InhibitWhat inhibit_key,
102                 HandleAction handle,
103                 bool ignore_inhibited,
104                 bool is_edge) {
105
106         int r;
107
108         assert(b);
109
110         r = manager_handle_action(b->manager, inhibit_key, handle, ignore_inhibited, is_edge);
111         if (r > 0)
112                 /* We are executing the operation, so make sure we don't
113                  * execute another one until the lid is opened/closed again */
114                 b->lid_close_queued = false;
115
116         return 0;
117 }
118
119 static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
120         Button *b = userdata;
121         struct input_event ev;
122         ssize_t l;
123
124         assert(s);
125         assert(fd == b->fd);
126         assert(b);
127
128         l = read(b->fd, &ev, sizeof(ev));
129         if (l < 0)
130                 return errno != EAGAIN ? -errno : 0;
131         if ((size_t) l < sizeof(ev))
132                 return -EIO;
133
134         if (ev.type == EV_KEY && ev.value > 0) {
135
136                 switch (ev.code) {
137
138                 case KEY_POWER:
139                 case KEY_POWER2:
140                         log_struct(LOG_INFO,
141                                    "MESSAGE=Power key pressed.",
142                                    MESSAGE_ID(SD_MESSAGE_POWER_KEY),
143                                    NULL);
144
145                         button_handle(b, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
146                         break;
147
148                 /* The kernel is a bit confused here:
149
150                    KEY_SLEEP   = suspend-to-ram, which everybody else calls "suspend"
151                    KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
152                 */
153
154                 case KEY_SLEEP:
155                         log_struct(LOG_INFO,
156                                    "MESSAGE=Suspend key pressed.",
157                                    MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
158                                    NULL);
159
160                         button_handle(b, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
161                         break;
162
163                 case KEY_SUSPEND:
164                         log_struct(LOG_INFO,
165                                    "MESSAGE=Hibernate key pressed.",
166                                    MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
167                                    NULL);
168
169                         button_handle(b, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
170                         break;
171                 }
172
173         } else if (ev.type == EV_SW && ev.value > 0) {
174
175                 if (ev.code == SW_LID) {
176                         log_struct(LOG_INFO,
177                                    "MESSAGE=Lid closed.",
178                                    MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
179                                    NULL);
180
181                         b->lid_close_queued = true;
182                         button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, true);
183                 }
184
185         } else if (ev.type == EV_SW && ev.value == 0) {
186
187                 if (ev.code == SW_LID) {
188                         log_struct(LOG_INFO,
189                                    "MESSAGE=Lid opened.",
190                                    MESSAGE_ID(SD_MESSAGE_LID_OPENED),
191                                    NULL);
192
193                         b->lid_close_queued = false;
194                 }
195         }
196
197         return 0;
198 }
199
200 int button_open(Button *b) {
201         char *p, name[256];
202         int r;
203
204         assert(b);
205
206         if (b->fd >= 0) {
207                 close(b->fd);
208                 b->fd = -1;
209         }
210
211         p = strappenda("/dev/input/", b->name);
212
213         b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
214         if (b->fd < 0) {
215                 log_warning("Failed to open %s: %m", b->name);
216                 return -errno;
217         }
218
219         if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
220                 log_error("Failed to get input name: %m");
221                 r = -errno;
222                 goto fail;
223         }
224
225         r = sd_event_add_io(b->manager->event, b->fd, EPOLLIN, button_dispatch, b, &b->event_source);
226         if (r < 0) {
227                 log_error("Failed to add button event: %s", strerror(-r));
228                 goto fail;
229         }
230
231         log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
232
233         return 0;
234
235 fail:
236         close(b->fd);
237         b->fd = -1;
238         return r;
239 }
240
241 int button_recheck(Button *b) {
242         assert(b);
243
244         if (!b->lid_close_queued)
245                 return 0;
246
247         return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, false);
248 }