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