]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-inhibit.c
dbb58e4ac3e2264cb28e7d217b297548e657585e
[thirdparty/systemd.git] / src / login / logind-inhibit.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8
9 #include "alloc-util.h"
10 #include "env-file.h"
11 #include "errno-list.h"
12 #include "errno-util.h"
13 #include "escape.h"
14 #include "fd-util.h"
15 #include "fileio.h"
16 #include "format-util.h"
17 #include "io-util.h"
18 #include "logind-dbus.h"
19 #include "logind-inhibit.h"
20 #include "mkdir-label.h"
21 #include "parse-util.h"
22 #include "path-util.h"
23 #include "string-table.h"
24 #include "string-util.h"
25 #include "tmpfile-util.h"
26 #include "user-util.h"
27 #include "util.h"
28
29 static void inhibitor_remove_fifo(Inhibitor *i);
30
31 int inhibitor_new(Inhibitor **ret, Manager *m, const char* id) {
32 _cleanup_(inhibitor_freep) Inhibitor *i = NULL;
33 int r;
34
35 assert(ret);
36 assert(m);
37 assert(id);
38
39 i = new(Inhibitor, 1);
40 if (!i)
41 return -ENOMEM;
42
43 *i = (Inhibitor) {
44 .manager = m,
45 .what = _INHIBIT_WHAT_INVALID,
46 .mode = _INHIBIT_MODE_INVALID,
47 .uid = UID_INVALID,
48 .fifo_fd = -1,
49 };
50
51 i->state_file = path_join("/run/systemd/inhibit", id);
52 if (!i->state_file)
53 return -ENOMEM;
54
55 i->id = basename(i->state_file);
56
57 r = hashmap_put(m->inhibitors, i->id, i);
58 if (r < 0)
59 return r;
60
61 *ret = TAKE_PTR(i);
62 return 0;
63 }
64
65 Inhibitor* inhibitor_free(Inhibitor *i) {
66
67 if (!i)
68 return NULL;
69
70 free(i->who);
71 free(i->why);
72
73 sd_event_source_unref(i->event_source);
74 safe_close(i->fifo_fd);
75
76 hashmap_remove(i->manager->inhibitors, i->id);
77
78 /* Note that we don't remove neither the state file nor the fifo path here, since we want both to
79 * survive daemon restarts */
80 free(i->state_file);
81 free(i->fifo_path);
82
83 return mfree(i);
84 }
85
86 static int inhibitor_save(Inhibitor *i) {
87 _cleanup_free_ char *temp_path = NULL;
88 _cleanup_fclose_ FILE *f = NULL;
89 int r;
90
91 assert(i);
92
93 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0, MKDIR_WARN_MODE);
94 if (r < 0)
95 goto fail;
96
97 r = fopen_temporary(i->state_file, &f, &temp_path);
98 if (r < 0)
99 goto fail;
100
101 (void) fchmod(fileno(f), 0644);
102
103 fprintf(f,
104 "# This is private data. Do not parse.\n"
105 "WHAT=%s\n"
106 "MODE=%s\n"
107 "UID="UID_FMT"\n"
108 "PID="PID_FMT"\n",
109 inhibit_what_to_string(i->what),
110 inhibit_mode_to_string(i->mode),
111 i->uid,
112 i->pid);
113
114 if (i->who) {
115 _cleanup_free_ char *cc = NULL;
116
117 cc = cescape(i->who);
118 if (!cc) {
119 r = -ENOMEM;
120 goto fail;
121 }
122
123 fprintf(f, "WHO=%s\n", cc);
124 }
125
126 if (i->why) {
127 _cleanup_free_ char *cc = NULL;
128
129 cc = cescape(i->why);
130 if (!cc) {
131 r = -ENOMEM;
132 goto fail;
133 }
134
135 fprintf(f, "WHY=%s\n", cc);
136 }
137
138 if (i->fifo_path)
139 fprintf(f, "FIFO=%s\n", i->fifo_path);
140
141 r = fflush_and_check(f);
142 if (r < 0)
143 goto fail;
144
145 if (rename(temp_path, i->state_file) < 0) {
146 r = -errno;
147 goto fail;
148 }
149
150 return 0;
151
152 fail:
153 (void) unlink(i->state_file);
154
155 if (temp_path)
156 (void) unlink(temp_path);
157
158 return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file);
159 }
160
161 static int bus_manager_send_inhibited_change(Inhibitor *i) {
162 const char *property;
163
164 assert(i);
165
166 property = i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited";
167
168 return manager_send_changed(i->manager, property, NULL);
169 }
170
171 int inhibitor_start(Inhibitor *i) {
172 assert(i);
173
174 if (i->started)
175 return 0;
176
177 dual_timestamp_get(&i->since);
178
179 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.",
180 strna(i->who), strna(i->why),
181 i->pid, i->uid,
182 inhibit_mode_to_string(i->mode));
183
184 i->started = true;
185
186 inhibitor_save(i);
187
188 bus_manager_send_inhibited_change(i);
189
190 return 0;
191 }
192
193 void inhibitor_stop(Inhibitor *i) {
194 assert(i);
195
196 if (i->started)
197 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.",
198 strna(i->who), strna(i->why),
199 i->pid, i->uid,
200 inhibit_mode_to_string(i->mode));
201
202 inhibitor_remove_fifo(i);
203
204 if (i->state_file)
205 (void) unlink(i->state_file);
206
207 i->started = false;
208
209 bus_manager_send_inhibited_change(i);
210 }
211
212 int inhibitor_load(Inhibitor *i) {
213 _cleanup_free_ char *what = NULL, *uid = NULL, *pid = NULL, *who = NULL, *why = NULL, *mode = NULL;
214 InhibitWhat w;
215 InhibitMode mm;
216 char *cc;
217 ssize_t l;
218 int r;
219
220 r = parse_env_file(NULL, i->state_file,
221 "WHAT", &what,
222 "UID", &uid,
223 "PID", &pid,
224 "WHO", &who,
225 "WHY", &why,
226 "MODE", &mode,
227 "FIFO", &i->fifo_path);
228 if (r < 0)
229 return log_error_errno(r, "Failed to read %s: %m", i->state_file);
230
231 w = what ? inhibit_what_from_string(what) : 0;
232 if (w >= 0)
233 i->what = w;
234
235 mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK;
236 if (mm >= 0)
237 i->mode = mm;
238
239 if (uid) {
240 r = parse_uid(uid, &i->uid);
241 if (r < 0)
242 log_debug_errno(r, "Failed to parse UID of inhibitor: %s", uid);
243 }
244
245 if (pid) {
246 r = parse_pid(pid, &i->pid);
247 if (r < 0)
248 log_debug_errno(r, "Failed to parse PID of inhibitor: %s", pid);
249 }
250
251 if (who) {
252 l = cunescape(who, 0, &cc);
253 if (l < 0)
254 return log_debug_errno(l, "Failed to unescape \"who\" of inhibitor: %m");
255
256 free_and_replace(i->who, cc);
257 }
258
259 if (why) {
260 l = cunescape(why, 0, &cc);
261 if (l < 0)
262 return log_debug_errno(l, "Failed to unescape \"why\" of inhibitor: %m");
263
264 free_and_replace(i->why, cc);
265 }
266
267 if (i->fifo_path) {
268 _cleanup_close_ int fd = -1;
269
270 /* Let's re-open the FIFO on both sides, and close the writing side right away */
271 fd = inhibitor_create_fifo(i);
272 if (fd < 0)
273 return log_error_errno(fd, "Failed to reopen FIFO: %m");
274 }
275
276 return 0;
277 }
278
279 static int inhibitor_dispatch_fifo(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
280 Inhibitor *i = userdata;
281
282 assert(s);
283 assert(fd == i->fifo_fd);
284 assert(i);
285
286 inhibitor_stop(i);
287 inhibitor_free(i);
288
289 return 0;
290 }
291
292 int inhibitor_create_fifo(Inhibitor *i) {
293 int r;
294
295 assert(i);
296
297 /* Create FIFO */
298 if (!i->fifo_path) {
299 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0, MKDIR_WARN_MODE);
300 if (r < 0)
301 return r;
302
303 i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref");
304 if (!i->fifo_path)
305 return -ENOMEM;
306
307 if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST)
308 return -errno;
309 }
310
311 /* Open reading side */
312 if (i->fifo_fd < 0) {
313 i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
314 if (i->fifo_fd < 0)
315 return -errno;
316 }
317
318 if (!i->event_source) {
319 r = sd_event_add_io(i->manager->event, &i->event_source, i->fifo_fd, 0, inhibitor_dispatch_fifo, i);
320 if (r < 0)
321 return r;
322
323 r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10);
324 if (r < 0)
325 return r;
326
327 (void) sd_event_source_set_description(i->event_source, "inhibitor-ref");
328 }
329
330 /* Open writing side */
331 return RET_NERRNO(open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NONBLOCK));
332 }
333
334 static void inhibitor_remove_fifo(Inhibitor *i) {
335 assert(i);
336
337 i->event_source = sd_event_source_unref(i->event_source);
338 i->fifo_fd = safe_close(i->fifo_fd);
339
340 if (i->fifo_path) {
341 (void) unlink(i->fifo_path);
342 i->fifo_path = mfree(i->fifo_path);
343 }
344 }
345
346 bool inhibitor_is_orphan(Inhibitor *i) {
347 assert(i);
348
349 if (!i->started)
350 return true;
351
352 if (!i->fifo_path)
353 return true;
354
355 if (i->fifo_fd < 0)
356 return true;
357
358 if (pipe_eof(i->fifo_fd) != 0)
359 return true;
360
361 return false;
362 }
363
364 InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
365 Inhibitor *i;
366 InhibitWhat what = 0;
367
368 assert(m);
369
370 HASHMAP_FOREACH(i, m->inhibitors)
371 if (i->mode == mm && i->started)
372 what |= i->what;
373
374 return what;
375 }
376
377 static int pid_is_active(Manager *m, pid_t pid) {
378 Session *s;
379 int r;
380
381 /* Get client session. This is not what you are looking for these days.
382 * FIXME #6852 */
383 r = manager_get_session_by_pid(m, pid, &s);
384 if (r < 0)
385 return r;
386
387 /* If there's no session assigned to it, then it's globally
388 * active on all ttys */
389 if (r == 0)
390 return 1;
391
392 return session_is_active(s);
393 }
394
395 bool manager_is_inhibited(
396 Manager *m,
397 InhibitWhat w,
398 InhibitMode mm,
399 dual_timestamp *since,
400 bool ignore_inactive,
401 bool ignore_uid,
402 uid_t uid,
403 Inhibitor **offending) {
404
405 Inhibitor *i;
406 struct dual_timestamp ts = DUAL_TIMESTAMP_NULL;
407 bool inhibited = false;
408
409 assert(m);
410 assert(w > 0 && w < _INHIBIT_WHAT_MAX);
411
412 HASHMAP_FOREACH(i, m->inhibitors) {
413 if (!i->started)
414 continue;
415
416 if (!(i->what & w))
417 continue;
418
419 if (i->mode != mm)
420 continue;
421
422 if (ignore_inactive && pid_is_active(m, i->pid) <= 0)
423 continue;
424
425 if (ignore_uid && i->uid == uid)
426 continue;
427
428 if (!inhibited ||
429 i->since.monotonic < ts.monotonic)
430 ts = i->since;
431
432 inhibited = true;
433
434 if (offending)
435 *offending = i;
436 }
437
438 if (since)
439 *since = ts;
440
441 return inhibited;
442 }
443
444 const char *inhibit_what_to_string(InhibitWhat w) {
445 static thread_local char buffer[STRLEN(
446 "shutdown:"
447 "sleep:"
448 "idle:"
449 "handle-power-key:"
450 "handle-suspend-key:"
451 "handle-hibernate-key:"
452 "handle-lid-switch:"
453 "handle-reboot-key")+1];
454 char *p;
455
456 if (w < 0 || w >= _INHIBIT_WHAT_MAX)
457 return NULL;
458
459 p = buffer;
460 if (w & INHIBIT_SHUTDOWN)
461 p = stpcpy(p, "shutdown:");
462 if (w & INHIBIT_SLEEP)
463 p = stpcpy(p, "sleep:");
464 if (w & INHIBIT_IDLE)
465 p = stpcpy(p, "idle:");
466 if (w & INHIBIT_HANDLE_POWER_KEY)
467 p = stpcpy(p, "handle-power-key:");
468 if (w & INHIBIT_HANDLE_SUSPEND_KEY)
469 p = stpcpy(p, "handle-suspend-key:");
470 if (w & INHIBIT_HANDLE_HIBERNATE_KEY)
471 p = stpcpy(p, "handle-hibernate-key:");
472 if (w & INHIBIT_HANDLE_LID_SWITCH)
473 p = stpcpy(p, "handle-lid-switch:");
474 if (w & INHIBIT_HANDLE_REBOOT_KEY)
475 p = stpcpy(p, "handle-reboot-key:");
476
477 if (p > buffer)
478 *(p-1) = 0;
479 else
480 *p = 0;
481
482 return buffer;
483 }
484
485 int inhibit_what_from_string(const char *s) {
486 InhibitWhat what = 0;
487
488 for (const char *p = s;;) {
489 _cleanup_free_ char *word = NULL;
490 int r;
491
492 /* A sanity check that our return values fit in an int */
493 assert_cc((int) _INHIBIT_WHAT_MAX == _INHIBIT_WHAT_MAX);
494
495 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
496 if (r < 0)
497 return r;
498 if (r == 0)
499 return what;
500
501 if (streq(word, "shutdown"))
502 what |= INHIBIT_SHUTDOWN;
503 else if (streq(word, "sleep"))
504 what |= INHIBIT_SLEEP;
505 else if (streq(word, "idle"))
506 what |= INHIBIT_IDLE;
507 else if (streq(word, "handle-power-key"))
508 what |= INHIBIT_HANDLE_POWER_KEY;
509 else if (streq(word, "handle-suspend-key"))
510 what |= INHIBIT_HANDLE_SUSPEND_KEY;
511 else if (streq(word, "handle-hibernate-key"))
512 what |= INHIBIT_HANDLE_HIBERNATE_KEY;
513 else if (streq(word, "handle-lid-switch"))
514 what |= INHIBIT_HANDLE_LID_SWITCH;
515 else if (streq(word, "handle-reboot-key"))
516 what |= INHIBIT_HANDLE_REBOOT_KEY;
517 else
518 return _INHIBIT_WHAT_INVALID;
519 }
520 }
521
522 static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
523 [INHIBIT_BLOCK] = "block",
524 [INHIBIT_DELAY] = "delay"
525 };
526
527 DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);