]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-button.c
api: in constructor function calls, always put the returned object pointer first...
[thirdparty/systemd.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
145 button_handle(b, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
146 break;
147
148 /* The kernel is a bit confused here:
149
150 KEY_SLEEP = suspend-to-ram, which everybody else calls "suspend"
151 KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
152 */
153
154 case KEY_SLEEP:
155 log_struct(LOG_INFO,
156 "MESSAGE=Suspend key pressed.",
157 MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
158 NULL);
159
160 button_handle(b, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
161 break;
162
163 case KEY_SUSPEND:
164 log_struct(LOG_INFO,
165 "MESSAGE=Hibernate key pressed.",
166 MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
167 NULL);
168
169 button_handle(b, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
170 break;
171 }
172
173 } else if (ev.type == EV_SW && ev.value > 0) {
174
175 if (ev.code == SW_LID) {
176 log_struct(LOG_INFO,
177 "MESSAGE=Lid closed.",
178 MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
179 NULL);
180
181 b->lid_close_queued = true;
182 button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, true);
183 }
184
185 } else if (ev.type == EV_SW && ev.value == 0) {
186
187 if (ev.code == SW_LID) {
188 log_struct(LOG_INFO,
189 "MESSAGE=Lid opened.",
190 MESSAGE_ID(SD_MESSAGE_LID_OPENED),
191 NULL);
192
193 b->lid_close_queued = false;
194 }
195 }
196
197 return 0;
198 }
199
200 int button_open(Button *b) {
201 char *p, name[256];
202 int r;
203
204 assert(b);
205
206 if (b->fd >= 0) {
207 close(b->fd);
208 b->fd = -1;
209 }
210
211 p = strappenda("/dev/input/", b->name);
212
213 b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
214 if (b->fd < 0) {
215 log_warning("Failed to open %s: %m", b->name);
216 return -errno;
217 }
218
219 if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
220 log_error("Failed to get input name: %m");
221 r = -errno;
222 goto fail;
223 }
224
225 r = sd_event_add_io(b->manager->event, &b->event_source, b->fd, EPOLLIN, button_dispatch, b);
226 if (r < 0) {
227 log_error("Failed to add button event: %s", strerror(-r));
228 goto fail;
229 }
230
231 log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
232
233 return 0;
234
235 fail:
236 close(b->fd);
237 b->fd = -1;
238 return r;
239 }
240
241 int button_recheck(Button *b) {
242 assert(b);
243
244 if (!b->lid_close_queued)
245 return 0;
246
247 return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, false);
248 }