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