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