chiark / gitweb /
doc: disable "make check" for gtk-doc
[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
37 Button* button_new(Manager *m, const char *name) {
38         Button *b;
39
40         assert(m);
41         assert(name);
42
43         b = new0(Button, 1);
44         if (!b)
45                 return NULL;
46
47         b->name = strdup(name);
48         if (!b->name) {
49                 free(b);
50                 return NULL;
51         }
52
53         if (hashmap_put(m->buttons, b->name, b) < 0) {
54                 free(b->name);
55                 free(b);
56                 return NULL;
57         }
58
59         b->manager = m;
60         b->fd = -1;
61
62         return b;
63 }
64
65 void button_free(Button *b) {
66         assert(b);
67
68         hashmap_remove(b->manager->buttons, b->name);
69
70         if (b->fd >= 0) {
71                 hashmap_remove(b->manager->button_fds, INT_TO_PTR(b->fd + 1));
72                 assert_se(epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_DEL, b->fd, NULL) == 0);
73                 close_nointr_nofail(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 int button_open(Button *b) {
98         char name[256], *p;
99         struct epoll_event ev;
100         int r;
101
102         assert(b);
103
104         if (b->fd >= 0) {
105                 close_nointr_nofail(b->fd);
106                 b->fd = -1;
107         }
108
109         p = strappend("/dev/input/", b->name);
110         if (!p)
111                 return log_oom();
112
113         b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
114         free(p);
115         if (b->fd < 0) {
116                 log_warning("Failed to open %s: %m", b->name);
117                 return -errno;
118         }
119
120         if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
121                 log_error("Failed to get input name: %m");
122                 r = -errno;
123                 goto fail;
124         }
125
126         zero(ev);
127         ev.events = EPOLLIN;
128         ev.data.u32 = FD_OTHER_BASE + b->fd;
129
130         if (epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_ADD, b->fd, &ev) < 0) {
131                 log_error("Failed to add to epoll: %m");
132                 r = -errno;
133                 goto fail;
134         }
135
136         r = hashmap_put(b->manager->button_fds, INT_TO_PTR(b->fd + 1), b);
137         if (r < 0) {
138                 log_error("Failed to add to hash map: %s", strerror(-r));
139                 assert_se(epoll_ctl(b->manager->epoll_fd, EPOLL_CTL_DEL, b->fd, NULL) == 0);
140                 goto fail;
141         }
142
143         log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
144
145         return 0;
146
147 fail:
148         close_nointr_nofail(b->fd);
149         b->fd = -1;
150         return r;
151 }
152
153 static int button_handle(
154                 Button *b,
155                 InhibitWhat inhibit_key,
156                 HandleAction handle,
157                 bool ignore_inhibited,
158                 bool is_edge) {
159
160         int r;
161
162         assert(b);
163
164         r = manager_handle_action(b->manager, inhibit_key, handle, ignore_inhibited, is_edge);
165         if (r > 0)
166                 /* We are executing the operation, so make sure we don't
167                  * execute another one until the lid is opened/closed again */
168                 b->lid_close_queued = false;
169
170         return r;
171 }
172
173 int button_process(Button *b) {
174         struct input_event ev;
175         ssize_t l;
176
177         assert(b);
178
179         l = read(b->fd, &ev, sizeof(ev));
180         if (l < 0)
181                 return errno != EAGAIN ? -errno : 0;
182         if ((size_t) l < sizeof(ev))
183                 return -EIO;
184
185         if (ev.type == EV_KEY && ev.value > 0) {
186
187                 switch (ev.code) {
188
189                 case KEY_POWER:
190                 case KEY_POWER2:
191                         log_info("Power key pressed.");
192                         return button_handle(b, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
193
194                 /* The kernel is a bit confused here:
195
196                    KEY_SLEEP   = suspend-to-ram, which everybody else calls "suspend"
197                    KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
198                 */
199
200                 case KEY_SLEEP:
201                         log_info("Suspend key pressed.");
202                         return button_handle(b, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
203
204                 case KEY_SUSPEND:
205                         log_info("Hibernate key pressed.");
206                         return button_handle(b, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
207                 }
208
209         } else if (ev.type == EV_SW && ev.value > 0) {
210
211                 switch (ev.code) {
212
213                 case SW_LID:
214                         log_info("Lid closed.");
215                         b->lid_close_queued = true;
216
217                         return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, true);
218                 }
219
220         } else if (ev.type == EV_SW && ev.value == 0) {
221
222                 switch (ev.code) {
223
224                 case SW_LID:
225                         log_info("Lid opened.");
226                         b->lid_close_queued = false;
227                         break;
228                 }
229         }
230
231         return 0;
232 }
233
234 int button_recheck(Button *b) {
235         assert(b);
236
237         if (!b->lid_close_queued)
238                 return 0;
239
240         return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, false);
241 }