chiark / gitweb /
Do no isolate in case of emergency or severe problems
[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 #include <sys/epoll.h>
30
31 #include "conf-parser.h"
32 #include "util.h"
33 #include "logind-button.h"
34 #include "special.h"
35 #include "dbus-common.h"
36 #include "sd-messages.h"
37
38 Button* button_new(Manager *m, const char *name) {
39         Button *b;
40
41         assert(m);
42         assert(name);
43
44         b = new0(Button, 1);
45         if (!b)
46                 return NULL;
47
48         b->name = strdup(name);
49         if (!b->name) {
50                 free(b);
51                 return NULL;
52         }
53
54         if (hashmap_put(m->buttons, b->name, b) < 0) {
55                 free(b->name);
56                 free(b);
57                 return NULL;
58         }
59
60         b->manager = m;
61         b->fd = -1;
62
63         return b;
64 }
65
66 void button_free(Button *b) {
67         assert(b);
68
69         hashmap_remove(b->manager->buttons, b->name);
70
71         if (b->fd >= 0) {
72                 hashmap_remove(b->manager->button_fds, INT_TO_PTR(b->fd + 1));
73                 assert_se(epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_DEL, b->fd, NULL) == 0);
74
75                 /* If the device has been unplugged close() returns
76                  * ENODEV, let's ignore this, hence we don't use
77                  * close_nointr_nofail() */
78                 close(b->fd);
79         }
80
81         free(b->name);
82         free(b->seat);
83         free(b);
84 }
85
86 int button_set_seat(Button *b, const char *sn) {
87         char *s;
88
89         assert(b);
90         assert(sn);
91
92         s = strdup(sn);
93         if (!s)
94                 return -ENOMEM;
95
96         free(b->seat);
97         b->seat = s;
98
99         return 0;
100 }
101
102 int button_open(Button *b) {
103         char name[256], *p;
104         struct epoll_event ev;
105         int r;
106
107         assert(b);
108
109         if (b->fd >= 0) {
110                 close(b->fd);
111                 b->fd = -1;
112         }
113
114         p = strappend("/dev/input/", b->name);
115         if (!p)
116                 return log_oom();
117
118         b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
119         free(p);
120         if (b->fd < 0) {
121                 log_warning("Failed to open %s: %m", b->name);
122                 return -errno;
123         }
124
125         if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
126                 log_error("Failed to get input name: %m");
127                 r = -errno;
128                 goto fail;
129         }
130
131         zero(ev);
132         ev.events = EPOLLIN;
133         ev.data.u32 = FD_OTHER_BASE + b->fd;
134
135         if (epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_ADD, b->fd, &ev) < 0) {
136                 log_error("Failed to add to epoll: %m");
137                 r = -errno;
138                 goto fail;
139         }
140
141         r = hashmap_put(b->manager->button_fds, INT_TO_PTR(b->fd + 1), b);
142         if (r < 0) {
143                 log_error("Failed to add to hash map: %s", strerror(-r));
144                 assert_se(epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_DEL, b->fd, NULL) == 0);
145                 goto fail;
146         }
147
148         log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
149
150         return 0;
151
152 fail:
153         close(b->fd);
154         b->fd = -1;
155         return r;
156 }
157
158 static int button_handle(
159                 Button *b,
160                 InhibitWhat inhibit_key,
161                 HandleAction handle,
162                 bool ignore_inhibited,
163                 bool is_edge) {
164
165         int r;
166
167         assert(b);
168
169         r = manager_handle_action(b->manager, inhibit_key, handle, ignore_inhibited, is_edge);
170         if (r > 0)
171                 /* We are executing the operation, so make sure we don't
172                  * execute another one until the lid is opened/closed again */
173                 b->lid_close_queued = false;
174
175         return r;
176 }
177
178 int button_process(Button *b) {
179         struct input_event ev;
180         ssize_t l;
181
182         assert(b);
183
184         l = read(b->fd, &ev, sizeof(ev));
185         if (l < 0)
186                 return errno != EAGAIN ? -errno : 0;
187         if ((size_t) l < sizeof(ev))
188                 return -EIO;
189
190         if (ev.type == EV_KEY && ev.value > 0) {
191
192                 switch (ev.code) {
193
194                 case KEY_POWER:
195                 case KEY_POWER2:
196                         log_struct(LOG_INFO,
197                                    "MESSAGE=Power key pressed.",
198                                    MESSAGE_ID(SD_MESSAGE_POWER_KEY),
199                                    NULL);
200                         return button_handle(b, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
201
202                 /* The kernel is a bit confused here:
203
204                    KEY_SLEEP   = suspend-to-ram, which everybody else calls "suspend"
205                    KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
206                 */
207
208                 case KEY_SLEEP:
209                         log_struct(LOG_INFO,
210                                    "MESSAGE=Suspend key pressed.",
211                                    MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
212                                    NULL);
213                         return button_handle(b, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
214
215                 case KEY_SUSPEND:
216                         log_struct(LOG_INFO,
217                                    "MESSAGE=Hibernate key pressed.",
218                                    MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
219                                    NULL);
220                         return button_handle(b, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
221                 }
222
223         } else if (ev.type == EV_SW && ev.value > 0) {
224
225                 switch (ev.code) {
226
227                 case SW_LID:
228                         log_struct(LOG_INFO,
229                                    "MESSAGE=Lid closed.",
230                                    MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
231                                    NULL);
232                         b->lid_close_queued = true;
233
234                         return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, true);
235                 }
236
237         } else if (ev.type == EV_SW && ev.value == 0) {
238
239                 switch (ev.code) {
240
241                 case SW_LID:
242                         log_struct(LOG_INFO,
243                                    "MESSAGE=Lid opened.",
244                                    MESSAGE_ID(SD_MESSAGE_LID_OPENED),
245                                    NULL);
246                         b->lid_close_queued = false;
247                         break;
248                 }
249         }
250
251         return 0;
252 }
253
254 int button_recheck(Button *b) {
255         assert(b);
256
257         if (!b->lid_close_queued)
258                 return 0;
259
260         return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, false);
261 }