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