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