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