]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-inhibit.c
logind: implement delay inhibitor locks in addition to block inhibitor locks
[thirdparty/systemd.git] / src / login / logind-inhibit.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 <errno.h>
23 #include <fcntl.h>
24 #include <sys/epoll.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28
29 #include "util.h"
30 #include "mkdir.h"
31
32 #include "logind-inhibit.h"
33
34 Inhibitor* inhibitor_new(Manager *m, const char* id) {
35 Inhibitor *i;
36
37 assert(m);
38
39 i = new0(Inhibitor, 1);
40 if (!i)
41 return NULL;
42
43 i->state_file = strappend("/run/systemd/inhibit/", id);
44 if (!i->state_file) {
45 free(i);
46 return NULL;
47 }
48
49 i->id = file_name_from_path(i->state_file);
50
51 if (hashmap_put(m->inhibitors, i->id, i) < 0) {
52 free(i->state_file);
53 free(i);
54 return NULL;
55 }
56
57 i->manager = m;
58 i->fifo_fd = -1;
59
60 return i;
61 }
62
63 void inhibitor_free(Inhibitor *i) {
64 assert(i);
65
66 free(i->who);
67 free(i->why);
68
69 hashmap_remove(i->manager->inhibitors, i->id);
70 inhibitor_remove_fifo(i);
71
72 if (i->state_file) {
73 unlink(i->state_file);
74 free(i->state_file);
75 }
76
77 free(i);
78 }
79
80 int inhibitor_save(Inhibitor *i) {
81 char *temp_path, *cc;
82 int r;
83 FILE *f;
84
85 assert(i);
86
87 r = safe_mkdir("/run/systemd/inhibit", 0755, 0, 0);
88 if (r < 0)
89 goto finish;
90
91 r = fopen_temporary(i->state_file, &f, &temp_path);
92 if (r < 0)
93 goto finish;
94
95 fchmod(fileno(f), 0644);
96
97 fprintf(f,
98 "# This is private data. Do not parse.\n"
99 "WHAT=%s\n"
100 "MODE=%s\n"
101 "UID=%lu\n"
102 "PID=%lu\n",
103 inhibit_what_to_string(i->what),
104 inhibit_mode_to_string(i->mode),
105 (unsigned long) i->uid,
106 (unsigned long) i->pid);
107
108 if (i->who) {
109 cc = cescape(i->who);
110 if (!cc)
111 r = -ENOMEM;
112 else {
113 fprintf(f, "WHO=%s\n", cc);
114 free(cc);
115 }
116 }
117
118 if (i->why) {
119 cc = cescape(i->why);
120 if (!cc)
121 r = -ENOMEM;
122 else {
123 fprintf(f, "WHY=%s\n", cc);
124 free(cc);
125 }
126 }
127
128 if (i->fifo_path)
129 fprintf(f, "FIFO=%s\n", i->fifo_path);
130
131 fflush(f);
132
133 if (ferror(f) || rename(temp_path, i->state_file) < 0) {
134 r = -errno;
135 unlink(i->state_file);
136 unlink(temp_path);
137 }
138
139 fclose(f);
140 free(temp_path);
141
142 finish:
143 if (r < 0)
144 log_error("Failed to save inhibit data for %s: %s", i->id, strerror(-r));
145
146 return r;
147 }
148
149 int inhibitor_start(Inhibitor *i) {
150 assert(i);
151
152 if (i->started)
153 return 0;
154
155 dual_timestamp_get(&i->since);
156
157 log_debug("Inhibitor %s (%s) pid=%lu uid=%lu mode=%s started.",
158 strna(i->who), strna(i->why),
159 (unsigned long) i->pid, (unsigned long) i->uid,
160 inhibit_mode_to_string(i->mode));
161
162 inhibitor_save(i);
163
164 i->started = true;
165
166 manager_send_changed(i->manager, "Inhibited\0");
167
168 return 0;
169 }
170
171 int inhibitor_stop(Inhibitor *i) {
172 assert(i);
173
174 if (i->started)
175 log_debug("Inhibitor %s (%s) pid=%lu uid=%lu mode=%s stopped.",
176 strna(i->who), strna(i->why),
177 (unsigned long) i->pid, (unsigned long) i->uid,
178 inhibit_mode_to_string(i->mode));
179
180 if (i->state_file)
181 unlink(i->state_file);
182
183 i->started = false;
184
185 manager_send_changed(i->manager, "Inhibited\0");
186
187 return 0;
188 }
189
190 int inhibitor_load(Inhibitor *i) {
191 InhibitWhat w;
192 InhibitMode mm;
193 int r;
194 char *cc,
195 *what = NULL,
196 *uid = NULL,
197 *pid = NULL,
198 *who = NULL,
199 *why = NULL,
200 *mode = NULL;
201
202 r = parse_env_file(i->state_file, NEWLINE,
203 "WHAT", &what,
204 "UID", &uid,
205 "PID", &pid,
206 "WHO", &who,
207 "WHY", &why,
208 "MODE", &mode,
209 "FIFO", &i->fifo_path,
210 NULL);
211 if (r < 0)
212 goto finish;
213
214 w = what ? inhibit_what_from_string(what) : 0;
215 if (w >= 0)
216 i->what = w;
217
218 mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK;
219 if (mm >= 0)
220 i->mode = mm;
221
222 if (uid)
223 parse_uid(uid, &i->uid);
224
225 if (pid)
226 parse_pid(pid, &i->pid);
227
228 if (who) {
229 cc = cunescape(who);
230 if (!cc) {
231 r = -ENOMEM;
232 goto finish;
233 }
234
235 free(i->who);
236 i->who = cc;
237 }
238
239 if (why) {
240 cc = cunescape(why);
241 if (!cc) {
242 r = -ENOMEM;
243 goto finish;
244 }
245
246 free(i->why);
247 i->why = cc;
248 }
249
250 if (i->fifo_path) {
251 int fd;
252
253 fd = inhibitor_create_fifo(i);
254 if (fd >= 0)
255 close_nointr_nofail(fd);
256 }
257
258 finish:
259 free(what);
260 free(uid);
261 free(pid);
262 free(who);
263 free(why);
264
265 return r;
266 }
267
268 int inhibitor_create_fifo(Inhibitor *i) {
269 int r;
270
271 assert(i);
272
273 /* Create FIFO */
274 if (!i->fifo_path) {
275 r = safe_mkdir("/run/systemd/inhibit", 0755, 0, 0);
276 if (r < 0)
277 return r;
278
279 if (asprintf(&i->fifo_path, "/run/systemd/inhibit/%s.ref", i->id) < 0)
280 return -ENOMEM;
281
282 if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST)
283 return -errno;
284 }
285
286 /* Open reading side */
287 if (i->fifo_fd < 0) {
288 struct epoll_event ev;
289
290 i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
291 if (i->fifo_fd < 0)
292 return -errno;
293
294 r = hashmap_put(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1), i);
295 if (r < 0)
296 return r;
297
298 zero(ev);
299 ev.events = 0;
300 ev.data.u32 = FD_FIFO_BASE + i->fifo_fd;
301
302 if (epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_ADD, i->fifo_fd, &ev) < 0)
303 return -errno;
304 }
305
306 /* Open writing side */
307 r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
308 if (r < 0)
309 return -errno;
310
311 return r;
312 }
313
314 void inhibitor_remove_fifo(Inhibitor *i) {
315 assert(i);
316
317 if (i->fifo_fd >= 0) {
318 assert_se(hashmap_remove(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1)) == i);
319 assert_se(epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_DEL, i->fifo_fd, NULL) == 0);
320 close_nointr_nofail(i->fifo_fd);
321 i->fifo_fd = -1;
322 }
323
324 if (i->fifo_path) {
325 unlink(i->fifo_path);
326 free(i->fifo_path);
327 i->fifo_path = NULL;
328 }
329 }
330
331 InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
332 Inhibitor *i;
333 Iterator j;
334 InhibitWhat what = 0;
335
336 assert(m);
337
338 HASHMAP_FOREACH(i, m->inhibitor_fds, j)
339 if (i->mode == mm)
340 what |= i->what;
341
342 return what;
343 }
344
345 bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since) {
346 Inhibitor *i;
347 Iterator j;
348 struct dual_timestamp ts = { 0, 0 };
349 bool inhibited = false;
350
351 assert(m);
352 assert(w > 0 && w < _INHIBIT_WHAT_MAX);
353
354 HASHMAP_FOREACH(i, m->inhibitor_fds, j) {
355 if (!(i->what & w))
356 continue;
357
358 if (i->mode != mm)
359 continue;
360
361 if (!inhibited ||
362 i->since.monotonic < ts.monotonic)
363 ts = i->since;
364
365 inhibited = true;
366 }
367
368 if (since)
369 *since = ts;
370
371 return inhibited;
372 }
373
374 const char *inhibit_what_to_string(InhibitWhat w) {
375
376 static const char* const table[_INHIBIT_WHAT_MAX] = {
377 [0] = "",
378 [INHIBIT_SHUTDOWN] = "shutdown",
379 [INHIBIT_SUSPEND] = "suspend",
380 [INHIBIT_IDLE] = "idle",
381 [INHIBIT_SHUTDOWN|INHIBIT_SUSPEND] = "shutdown:suspend",
382 [INHIBIT_SHUTDOWN|INHIBIT_IDLE] = "shutdown:idle",
383 [INHIBIT_SHUTDOWN|INHIBIT_SUSPEND|INHIBIT_IDLE] = "shutdown:suspend:idle",
384 [INHIBIT_SUSPEND|INHIBIT_IDLE] = "suspend:idle"
385 };
386
387 if (w < 0 || w >= _INHIBIT_WHAT_MAX)
388 return NULL;
389
390 return table[w];
391 }
392
393 InhibitWhat inhibit_what_from_string(const char *s) {
394 InhibitWhat what = 0;
395 char *w, *state;
396 size_t l;
397
398 FOREACH_WORD_SEPARATOR(w, l, s, ":", state) {
399 if (l == 8 && strncmp(w, "shutdown", l) == 0)
400 what |= INHIBIT_SHUTDOWN;
401 else if (l == 7 && strncmp(w, "suspend", l) == 0)
402 what |= INHIBIT_SUSPEND;
403 else if (l == 4 && strncmp(w, "idle", l) == 0)
404 what |= INHIBIT_IDLE;
405 else
406 return _INHIBIT_WHAT_INVALID;
407 }
408
409 return what;
410
411 }
412
413 static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
414 [INHIBIT_BLOCK] = "block",
415 [INHIBIT_DELAY] = "delay"
416 };
417
418 DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);