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