chiark / gitweb /
socket: Set SO_REUSEPORT before bind()
[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 <string.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <sys/ioctl.h>
26 #include <unistd.h>
27 #include <linux/input.h>
28
29 #include "sd-messages.h"
30 #include "util.h"
31 #include "logind-button.h"
32
33 Button* 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
61 void button_free(Button *b) {
62         assert(b);
63
64         hashmap_remove(b->manager->buttons, b->name);
65
66         sd_event_source_unref(b->io_event_source);
67         sd_event_source_unref(b->check_event_source);
68
69         if (b->fd >= 0) {
70                 /* If the device has been unplugged close() returns
71                  * ENODEV, let's ignore this, hence we don't use
72                  * safe_close() */
73                 (void) close(b->fd);
74         }
75
76         free(b->name);
77         free(b->seat);
78         free(b);
79 }
80
81 int 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
97 static 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 */
103         if (manager_is_docked_or_external_displays(manager))
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
111 static int button_recheck(sd_event_source *e, void *userdata) {
112         Button *b = userdata;
113
114         assert(b);
115         assert(b->lid_closed);
116
117         button_lid_switch_handle_action(b->manager, false);
118         return 1;
119 }
120
121 static int button_install_check_event_source(Button *b) {
122         int r;
123         assert(b);
124
125         /* Install a post handler, so that we keep rechecking as long as the lid is closed. */
126
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);
135 }
136
137 static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
138         Button *b = userdata;
139         struct input_event ev;
140         ssize_t l;
141
142         assert(s);
143         assert(fd == b->fd);
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
152         if (ev.type == EV_KEY && ev.value > 0) {
153
154                 switch (ev.code) {
155
156                 case KEY_POWER:
157                 case KEY_POWER2:
158                         log_struct(LOG_INFO,
159                                    LOG_MESSAGE("Power key pressed."),
160                                    LOG_MESSAGE_ID(SD_MESSAGE_POWER_KEY),
161                                    NULL);
162
163                         manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
164                         break;
165
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
172                 case KEY_SLEEP:
173                         log_struct(LOG_INFO,
174                                    LOG_MESSAGE("Suspend key pressed."),
175                                    LOG_MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
176                                    NULL);
177
178                         manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
179                         break;
180
181                 case KEY_SUSPEND:
182                         log_struct(LOG_INFO,
183                                    LOG_MESSAGE("Hibernate key pressed."),
184                                    LOG_MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
185                                    NULL);
186
187                         manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
188                         break;
189                 }
190
191         } else if (ev.type == EV_SW && ev.value > 0) {
192
193                 if (ev.code == SW_LID) {
194                         log_struct(LOG_INFO,
195                                    LOG_MESSAGE("Lid closed."),
196                                    LOG_MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
197                                    NULL);
198
199                         b->lid_closed = true;
200                         button_lid_switch_handle_action(b->manager, true);
201                         button_install_check_event_source(b);
202
203                 } else if (ev.code == SW_DOCK) {
204                         log_struct(LOG_INFO,
205                                    LOG_MESSAGE("System docked."),
206                                    LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED),
207                                    NULL);
208
209                         b->docked = true;
210                 }
211
212         } else if (ev.type == EV_SW && ev.value == 0) {
213
214                 if (ev.code == SW_LID) {
215                         log_struct(LOG_INFO,
216                                    LOG_MESSAGE("Lid opened."),
217                                    LOG_MESSAGE_ID(SD_MESSAGE_LID_OPENED),
218                                    NULL);
219
220                         b->lid_closed = false;
221                         b->check_event_source = sd_event_source_unref(b->check_event_source);
222
223                 } else if (ev.code == SW_DOCK) {
224                         log_struct(LOG_INFO,
225                                    LOG_MESSAGE("System undocked."),
226                                    LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED),
227                                    NULL);
228
229                         b->docked = false;
230                 }
231         }
232
233         return 0;
234 }
235
236 int button_open(Button *b) {
237         char *p, name[256];
238         int r;
239
240         assert(b);
241
242         if (b->fd >= 0) {
243                 close(b->fd);
244                 b->fd = -1;
245         }
246
247         p = strjoina("/dev/input/", b->name);
248
249         b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
250         if (b->fd < 0)
251                 return log_warning_errno(errno, "Failed to open %s: %m", b->name);
252
253         if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
254                 log_error_errno(errno, "Failed to get input name: %m");
255                 r = -errno;
256                 goto fail;
257         }
258
259         r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b);
260         if (r < 0) {
261                 log_error_errno(r, "Failed to add button event: %m");
262                 goto fail;
263         }
264
265         log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
266
267         return 0;
268
269 fail:
270         close(b->fd);
271         b->fd = -1;
272         return r;
273 }
274
275 int button_check_switches(Button *b) {
276         uint8_t switches[SW_MAX/8+1] = {};
277         assert(b);
278
279         if (b->fd < 0)
280                 return -EINVAL;
281
282         if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0)
283                 return -errno;
284
285         b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1;
286         b->docked = (switches[SW_DOCK/8] >> (SW_DOCK % 8)) & 1;
287
288         if (b->lid_closed)
289                 button_install_check_event_source(b);
290
291         return 0;
292 }