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