]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-inhibit.c
Merge pull request #12753 from jrouleau/fix/hibernate-resume-timeout
[thirdparty/systemd.git] / src / login / logind-inhibit.c
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 "logind-dbus.h"
17 #include "logind-inhibit.h"
18 #include "mkdir.h"
19 #include "parse-util.h"
20 #include "string-table.h"
21 #include "string-util.h"
22 #include "tmpfile-util.h"
23 #include "user-util.h"
24 #include "util.h"
25
26 Inhibitor* inhibitor_new(Manager *m, const char* id) {
27 Inhibitor *i;
28
29 assert(m);
30
31 i = new0(Inhibitor, 1);
32 if (!i)
33 return NULL;
34
35 i->state_file = strappend("/run/systemd/inhibit/", id);
36 if (!i->state_file)
37 return mfree(i);
38
39 i->id = basename(i->state_file);
40
41 if (hashmap_put(m->inhibitors, i->id, i) < 0) {
42 free(i->state_file);
43 return mfree(i);
44 }
45
46 i->manager = m;
47 i->fifo_fd = -1;
48
49 return i;
50 }
51
52 void inhibitor_free(Inhibitor *i) {
53 assert(i);
54
55 hashmap_remove(i->manager->inhibitors, i->id);
56
57 inhibitor_remove_fifo(i);
58
59 free(i->who);
60 free(i->why);
61
62 if (i->state_file) {
63 (void) unlink(i->state_file);
64 free(i->state_file);
65 }
66
67 free(i);
68 }
69
70 int inhibitor_save(Inhibitor *i) {
71 _cleanup_free_ char *temp_path = NULL;
72 _cleanup_fclose_ FILE *f = NULL;
73 int r;
74
75 assert(i);
76
77 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0, MKDIR_WARN_MODE);
78 if (r < 0)
79 goto fail;
80
81 r = fopen_temporary(i->state_file, &f, &temp_path);
82 if (r < 0)
83 goto fail;
84
85 fchmod(fileno(f), 0644);
86
87 fprintf(f,
88 "# This is private data. Do not parse.\n"
89 "WHAT=%s\n"
90 "MODE=%s\n"
91 "UID="UID_FMT"\n"
92 "PID="PID_FMT"\n",
93 inhibit_what_to_string(i->what),
94 inhibit_mode_to_string(i->mode),
95 i->uid,
96 i->pid);
97
98 if (i->who) {
99 _cleanup_free_ char *cc = NULL;
100
101 cc = cescape(i->who);
102 if (!cc) {
103 r = -ENOMEM;
104 goto fail;
105 }
106
107 fprintf(f, "WHO=%s\n", cc);
108 }
109
110 if (i->why) {
111 _cleanup_free_ char *cc = NULL;
112
113 cc = cescape(i->why);
114 if (!cc) {
115 r = -ENOMEM;
116 goto fail;
117 }
118
119 fprintf(f, "WHY=%s\n", cc);
120 }
121
122 if (i->fifo_path)
123 fprintf(f, "FIFO=%s\n", i->fifo_path);
124
125 r = fflush_and_check(f);
126 if (r < 0)
127 goto fail;
128
129 if (rename(temp_path, i->state_file) < 0) {
130 r = -errno;
131 goto fail;
132 }
133
134 return 0;
135
136 fail:
137 (void) unlink(i->state_file);
138
139 if (temp_path)
140 (void) unlink(temp_path);
141
142 return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file);
143 }
144
145 int inhibitor_start(Inhibitor *i) {
146 assert(i);
147
148 if (i->started)
149 return 0;
150
151 dual_timestamp_get(&i->since);
152
153 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.",
154 strna(i->who), strna(i->why),
155 i->pid, i->uid,
156 inhibit_mode_to_string(i->mode));
157
158 inhibitor_save(i);
159
160 i->started = true;
161
162 manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
163
164 return 0;
165 }
166
167 int inhibitor_stop(Inhibitor *i) {
168 assert(i);
169
170 if (i->started)
171 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.",
172 strna(i->who), strna(i->why),
173 i->pid, i->uid,
174 inhibit_mode_to_string(i->mode));
175
176 if (i->state_file)
177 (void) unlink(i->state_file);
178
179 i->started = false;
180
181 manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
182
183 return 0;
184 }
185
186 int inhibitor_load(Inhibitor *i) {
187
188 _cleanup_free_ char
189 *what = NULL,
190 *uid = NULL,
191 *pid = NULL,
192 *who = NULL,
193 *why = NULL,
194 *mode = NULL;
195
196 InhibitWhat w;
197 InhibitMode mm;
198 char *cc;
199 int r;
200
201 r = parse_env_file(NULL, i->state_file,
202 "WHAT", &what,
203 "UID", &uid,
204 "PID", &pid,
205 "WHO", &who,
206 "WHY", &why,
207 "MODE", &mode,
208 "FIFO", &i->fifo_path);
209 if (r < 0)
210 return r;
211
212 w = what ? inhibit_what_from_string(what) : 0;
213 if (w >= 0)
214 i->what = w;
215
216 mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK;
217 if (mm >= 0)
218 i->mode = mm;
219
220 if (uid) {
221 r = parse_uid(uid, &i->uid);
222 if (r < 0)
223 return r;
224 }
225
226 if (pid) {
227 r = parse_pid(pid, &i->pid);
228 if (r < 0)
229 return r;
230 }
231
232 if (who) {
233 r = cunescape(who, 0, &cc);
234 if (r < 0)
235 return r;
236
237 free(i->who);
238 i->who = cc;
239 }
240
241 if (why) {
242 r = cunescape(why, 0, &cc);
243 if (r < 0)
244 return r;
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 safe_close(fd);
255 }
256
257 return 0;
258 }
259
260 static int inhibitor_dispatch_fifo(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
261 Inhibitor *i = userdata;
262
263 assert(s);
264 assert(fd == i->fifo_fd);
265 assert(i);
266
267 inhibitor_stop(i);
268 inhibitor_free(i);
269
270 return 0;
271 }
272
273 int inhibitor_create_fifo(Inhibitor *i) {
274 int r;
275
276 assert(i);
277
278 /* Create FIFO */
279 if (!i->fifo_path) {
280 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0, MKDIR_WARN_MODE);
281 if (r < 0)
282 return r;
283
284 i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref");
285 if (!i->fifo_path)
286 return -ENOMEM;
287
288 if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST)
289 return -errno;
290 }
291
292 /* Open reading side */
293 if (i->fifo_fd < 0) {
294 i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
295 if (i->fifo_fd < 0)
296 return -errno;
297 }
298
299 if (!i->event_source) {
300 r = sd_event_add_io(i->manager->event, &i->event_source, i->fifo_fd, 0, inhibitor_dispatch_fifo, i);
301 if (r < 0)
302 return r;
303
304 r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10);
305 if (r < 0)
306 return r;
307 }
308
309 /* Open writing side */
310 r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NONBLOCK);
311 if (r < 0)
312 return -errno;
313
314 return r;
315 }
316
317 void inhibitor_remove_fifo(Inhibitor *i) {
318 assert(i);
319
320 i->event_source = sd_event_source_unref(i->event_source);
321 i->fifo_fd = safe_close(i->fifo_fd);
322
323 if (i->fifo_path) {
324 (void) unlink(i->fifo_path);
325 i->fifo_path = mfree(i->fifo_path);
326 }
327 }
328
329 InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
330 Inhibitor *i;
331 Iterator j;
332 InhibitWhat what = 0;
333
334 assert(m);
335
336 HASHMAP_FOREACH(i, m->inhibitors, j)
337 if (i->mode == mm && i->started)
338 what |= i->what;
339
340 return what;
341 }
342
343 static int pid_is_active(Manager *m, pid_t pid) {
344 Session *s;
345 int r;
346
347 /* Get client session. This is not what you are looking for these days.
348 * FIXME #6852 */
349 r = manager_get_session_by_pid(m, pid, &s);
350 if (r < 0)
351 return r;
352
353 /* If there's no session assigned to it, then it's globally
354 * active on all ttys */
355 if (r == 0)
356 return 1;
357
358 return session_is_active(s);
359 }
360
361 bool manager_is_inhibited(
362 Manager *m,
363 InhibitWhat w,
364 InhibitMode mm,
365 dual_timestamp *since,
366 bool ignore_inactive,
367 bool ignore_uid,
368 uid_t uid,
369 Inhibitor **offending) {
370
371 Inhibitor *i;
372 Iterator j;
373 struct dual_timestamp ts = DUAL_TIMESTAMP_NULL;
374 bool inhibited = false;
375
376 assert(m);
377 assert(w > 0 && w < _INHIBIT_WHAT_MAX);
378
379 HASHMAP_FOREACH(i, m->inhibitors, j) {
380 if (!i->started)
381 continue;
382
383 if (!(i->what & w))
384 continue;
385
386 if (i->mode != mm)
387 continue;
388
389 if (ignore_inactive && pid_is_active(m, i->pid) <= 0)
390 continue;
391
392 if (ignore_uid && i->uid == uid)
393 continue;
394
395 if (!inhibited ||
396 i->since.monotonic < ts.monotonic)
397 ts = i->since;
398
399 inhibited = true;
400
401 if (offending)
402 *offending = i;
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_local 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 const char *word, *state;
445 size_t l;
446
447 FOREACH_WORD_SEPARATOR(word, l, s, ":", state) {
448 if (l == 8 && strneq(word, "shutdown", l))
449 what |= INHIBIT_SHUTDOWN;
450 else if (l == 5 && strneq(word, "sleep", l))
451 what |= INHIBIT_SLEEP;
452 else if (l == 4 && strneq(word, "idle", l))
453 what |= INHIBIT_IDLE;
454 else if (l == 16 && strneq(word, "handle-power-key", l))
455 what |= INHIBIT_HANDLE_POWER_KEY;
456 else if (l == 18 && strneq(word, "handle-suspend-key", l))
457 what |= INHIBIT_HANDLE_SUSPEND_KEY;
458 else if (l == 20 && strneq(word, "handle-hibernate-key", l))
459 what |= INHIBIT_HANDLE_HIBERNATE_KEY;
460 else if (l == 17 && strneq(word, "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);