]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-button.c
util: replace close_nointr_nofail() by a more useful safe_close()
[thirdparty/systemd.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 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 }