]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-inhibit.c
hibernate-resume: add resumeflags= kernel option
[thirdparty/systemd.git] / src / login / logind-inhibit.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
f8e2fb7b
LP
2
3#include <errno.h>
4#include <fcntl.h>
f8e2fb7b 5#include <string.h>
ca78ad1d
ZJS
6#include <sys/stat.h>
7#include <sys/types.h>
f8e2fb7b
LP
8#include <unistd.h>
9
b5efdb8a 10#include "alloc-util.h"
686d13b9 11#include "env-file.h"
4f5dd394 12#include "escape.h"
3ffd4af2 13#include "fd-util.h"
a5c32cff 14#include "fileio.h"
f97b34a6 15#include "format-util.h"
3ffd4af2 16#include "logind-inhibit.h"
4f5dd394 17#include "mkdir.h"
6bedfcbb 18#include "parse-util.h"
8b43440b 19#include "string-table.h"
07630cea 20#include "string-util.h"
e4de7287 21#include "tmpfile-util.h"
b1d4f8e1 22#include "user-util.h"
4f5dd394 23#include "util.h"
f8e2fb7b
LP
24
25Inhibitor* 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);
6b430fdb
ZJS
35 if (!i->state_file)
36 return mfree(i);
f8e2fb7b 37
2b6bf07d 38 i->id = basename(i->state_file);
f8e2fb7b
LP
39
40 if (hashmap_put(m->inhibitors, i->id, i) < 0) {
41 free(i->state_file);
6b430fdb 42 return mfree(i);
f8e2fb7b
LP
43 }
44
45 i->manager = m;
46 i->fifo_fd = -1;
47
48 return i;
49}
50
51void inhibitor_free(Inhibitor *i) {
52 assert(i);
53
f8e2fb7b 54 hashmap_remove(i->manager->inhibitors, i->id);
cc377381 55
f8e2fb7b
LP
56 inhibitor_remove_fifo(i);
57
cc377381
LP
58 free(i->who);
59 free(i->why);
60
f8e2fb7b 61 if (i->state_file) {
6990fb6b 62 (void) unlink(i->state_file);
f8e2fb7b
LP
63 free(i->state_file);
64 }
65
66 free(i);
67}
68
69int inhibitor_save(Inhibitor *i) {
cc377381
LP
70 _cleanup_free_ char *temp_path = NULL;
71 _cleanup_fclose_ FILE *f = NULL;
f8e2fb7b 72 int r;
f8e2fb7b
LP
73
74 assert(i);
75
37c1d5e9 76 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0, MKDIR_WARN_MODE);
f8e2fb7b 77 if (r < 0)
dacd6cee 78 goto fail;
f8e2fb7b
LP
79
80 r = fopen_temporary(i->state_file, &f, &temp_path);
81 if (r < 0)
dacd6cee 82 goto fail;
f8e2fb7b
LP
83
84 fchmod(fileno(f), 0644);
85
86 fprintf(f,
87 "# This is private data. Do not parse.\n"
88 "WHAT=%s\n"
eecd1362 89 "MODE=%s\n"
90b2de37
ZJS
90 "UID="UID_FMT"\n"
91 "PID="PID_FMT"\n",
f8e2fb7b 92 inhibit_what_to_string(i->what),
eecd1362 93 inhibit_mode_to_string(i->mode),
90b2de37
ZJS
94 i->uid,
95 i->pid);
f8e2fb7b
LP
96
97 if (i->who) {
cc377381
LP
98 _cleanup_free_ char *cc = NULL;
99
f8e2fb7b 100 cc = cescape(i->who);
88231eb6 101 if (!cc) {
f8e2fb7b 102 r = -ENOMEM;
88231eb6
TA
103 goto fail;
104 }
105
106 fprintf(f, "WHO=%s\n", cc);
f8e2fb7b
LP
107 }
108
109 if (i->why) {
cc377381
LP
110 _cleanup_free_ char *cc = NULL;
111
f8e2fb7b 112 cc = cescape(i->why);
88231eb6 113 if (!cc) {
f8e2fb7b 114 r = -ENOMEM;
88231eb6
TA
115 goto fail;
116 }
117
118 fprintf(f, "WHY=%s\n", cc);
f8e2fb7b
LP
119 }
120
121 if (i->fifo_path)
122 fprintf(f, "FIFO=%s\n", i->fifo_path);
123
dacd6cee
LP
124 r = fflush_and_check(f);
125 if (r < 0)
126 goto fail;
f8e2fb7b 127
dacd6cee 128 if (rename(temp_path, i->state_file) < 0) {
f8e2fb7b 129 r = -errno;
dacd6cee 130 goto fail;
f8e2fb7b
LP
131 }
132
dacd6cee
LP
133 return 0;
134
135fail:
136 (void) unlink(i->state_file);
137
138 if (temp_path)
139 (void) unlink(temp_path);
f8e2fb7b 140
dacd6cee 141 return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file);
f8e2fb7b
LP
142}
143
144int inhibitor_start(Inhibitor *i) {
145 assert(i);
146
147 if (i->started)
148 return 0;
149
c7b5eb98
LP
150 dual_timestamp_get(&i->since);
151
de0671ee 152 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.",
f8e2fb7b 153 strna(i->who), strna(i->why),
de0671ee 154 i->pid, i->uid,
eecd1362 155 inhibit_mode_to_string(i->mode));
f8e2fb7b
LP
156
157 inhibitor_save(i);
158
159 i->started = true;
160
cc377381 161 manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
f8e2fb7b
LP
162
163 return 0;
164}
165
166int inhibitor_stop(Inhibitor *i) {
167 assert(i);
168
169 if (i->started)
de0671ee 170 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.",
f8e2fb7b 171 strna(i->who), strna(i->why),
de0671ee 172 i->pid, i->uid,
eecd1362 173 inhibit_mode_to_string(i->mode));
f8e2fb7b
LP
174
175 if (i->state_file)
6990fb6b 176 (void) unlink(i->state_file);
f8e2fb7b
LP
177
178 i->started = false;
179
cc377381 180 manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
f8e2fb7b
LP
181
182 return 0;
183}
184
185int inhibitor_load(Inhibitor *i) {
cc377381
LP
186
187 _cleanup_free_ char
f8e2fb7b
LP
188 *what = NULL,
189 *uid = NULL,
190 *pid = NULL,
191 *who = NULL,
eecd1362
LP
192 *why = NULL,
193 *mode = NULL;
f8e2fb7b 194
cc377381
LP
195 InhibitWhat w;
196 InhibitMode mm;
197 char *cc;
198 int r;
199
aa8fbc74 200 r = parse_env_file(NULL, i->state_file,
f8e2fb7b
LP
201 "WHAT", &what,
202 "UID", &uid,
203 "PID", &pid,
204 "WHO", &who,
205 "WHY", &why,
eecd1362 206 "MODE", &mode,
13df9c39 207 "FIFO", &i->fifo_path);
f8e2fb7b 208 if (r < 0)
cc377381 209 return r;
f8e2fb7b 210
eecd1362 211 w = what ? inhibit_what_from_string(what) : 0;
f8e2fb7b
LP
212 if (w >= 0)
213 i->what = w;
214
eecd1362
LP
215 mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK;
216 if (mm >= 0)
217 i->mode = mm;
218
a34faf57
LN
219 if (uid) {
220 r = parse_uid(uid, &i->uid);
221 if (r < 0)
cc377381 222 return r;
a34faf57 223 }
eecd1362 224
a34faf57
LN
225 if (pid) {
226 r = parse_pid(pid, &i->pid);
227 if (r < 0)
cc377381 228 return r;
a34faf57 229 }
f8e2fb7b
LP
230
231 if (who) {
527b7a42
LP
232 r = cunescape(who, 0, &cc);
233 if (r < 0)
234 return r;
f8e2fb7b
LP
235
236 free(i->who);
237 i->who = cc;
238 }
239
240 if (why) {
527b7a42
LP
241 r = cunescape(why, 0, &cc);
242 if (r < 0)
243 return r;
f8e2fb7b
LP
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);
03e334a1 253 safe_close(fd);
f8e2fb7b
LP
254 }
255
cc377381
LP
256 return 0;
257}
f8e2fb7b 258
cc377381
LP
259static 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;
f8e2fb7b
LP
270}
271
272int inhibitor_create_fifo(Inhibitor *i) {
273 int r;
274
275 assert(i);
276
277 /* Create FIFO */
278 if (!i->fifo_path) {
37c1d5e9 279 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0, MKDIR_WARN_MODE);
f8e2fb7b
LP
280 if (r < 0)
281 return r;
282
605405c6 283 i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref");
cc377381 284 if (!i->fifo_path)
f8e2fb7b
LP
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) {
db4a47e9 293 i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
f8e2fb7b
LP
294 if (i->fifo_fd < 0)
295 return -errno;
cc377381 296 }
f8e2fb7b 297
cc377381 298 if (!i->event_source) {
151b9b96 299 r = sd_event_add_io(i->manager->event, &i->event_source, i->fifo_fd, 0, inhibitor_dispatch_fifo, i);
f8e2fb7b
LP
300 if (r < 0)
301 return r;
302
e11544a8 303 r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10);
cc377381
LP
304 if (r < 0)
305 return r;
f8e2fb7b
LP
306 }
307
308 /* Open writing side */
db4a47e9 309 r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NONBLOCK);
f8e2fb7b
LP
310 if (r < 0)
311 return -errno;
312
313 return r;
314}
315
316void inhibitor_remove_fifo(Inhibitor *i) {
317 assert(i);
318
03e334a1
LP
319 i->event_source = sd_event_source_unref(i->event_source);
320 i->fifo_fd = safe_close(i->fifo_fd);
f8e2fb7b
LP
321
322 if (i->fifo_path) {
6990fb6b 323 (void) unlink(i->fifo_path);
a1e58e8e 324 i->fifo_path = mfree(i->fifo_path);
f8e2fb7b
LP
325 }
326}
327
eecd1362 328InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
f8e2fb7b
LP
329 Inhibitor *i;
330 Iterator j;
331 InhibitWhat what = 0;
332
333 assert(m);
334
cc377381 335 HASHMAP_FOREACH(i, m->inhibitors, j)
5e8273ac 336 if (i->mode == mm && i->started)
eecd1362 337 what |= i->what;
f8e2fb7b
LP
338
339 return what;
340}
341
beaafb2e
LP
342static int pid_is_active(Manager *m, pid_t pid) {
343 Session *s;
344 int r;
345
7b33c622
AJ
346 /* Get client session. This is not what you are looking for these days.
347 * FIXME #6852 */
beaafb2e 348 r = manager_get_session_by_pid(m, pid, &s);
2c4f86c1 349 if (r < 0)
beaafb2e
LP
350 return r;
351
2c4f86c1
LP
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
beaafb2e
LP
357 return session_is_active(s);
358}
359
360bool manager_is_inhibited(
361 Manager *m,
362 InhibitWhat w,
363 InhibitMode mm,
364 dual_timestamp *since,
409133be
LP
365 bool ignore_inactive,
366 bool ignore_uid,
85a428c6
LP
367 uid_t uid,
368 Inhibitor **offending) {
beaafb2e 369
c7b5eb98
LP
370 Inhibitor *i;
371 Iterator j;
5cb14b37 372 struct dual_timestamp ts = DUAL_TIMESTAMP_NULL;
c7b5eb98
LP
373 bool inhibited = false;
374
375 assert(m);
376 assert(w > 0 && w < _INHIBIT_WHAT_MAX);
377
cc377381 378 HASHMAP_FOREACH(i, m->inhibitors, j) {
5e8273ac
AF
379 if (!i->started)
380 continue;
381
c7b5eb98
LP
382 if (!(i->what & w))
383 continue;
384
eecd1362
LP
385 if (i->mode != mm)
386 continue;
387
409133be
LP
388 if (ignore_inactive && pid_is_active(m, i->pid) <= 0)
389 continue;
390
391 if (ignore_uid && i->uid == uid)
beaafb2e
LP
392 continue;
393
c7b5eb98
LP
394 if (!inhibited ||
395 i->since.monotonic < ts.monotonic)
396 ts = i->since;
397
398 inhibited = true;
85a428c6
LP
399
400 if (offending)
401 *offending = i;
c7b5eb98
LP
402 }
403
404 if (since)
405 *since = ts;
406
407 return inhibited;
408}
409
f8e2fb7b 410const char *inhibit_what_to_string(InhibitWhat w) {
ec202eae 411 static thread_local char buffer[97];
beaafb2e 412 char *p;
f8e2fb7b
LP
413
414 if (w < 0 || w >= _INHIBIT_WHAT_MAX)
415 return NULL;
416
beaafb2e
LP
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:");
8e7fd6ad
LP
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:");
beaafb2e
LP
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;
f8e2fb7b
LP
439}
440
441InhibitWhat inhibit_what_from_string(const char *s) {
442 InhibitWhat what = 0;
a2a5291b 443 const char *word, *state;
f8e2fb7b
LP
444 size_t l;
445
a2a5291b
ZJS
446 FOREACH_WORD_SEPARATOR(word, l, s, ":", state) {
447 if (l == 8 && strneq(word, "shutdown", l))
f8e2fb7b 448 what |= INHIBIT_SHUTDOWN;
a2a5291b 449 else if (l == 5 && strneq(word, "sleep", l))
4943c1c9 450 what |= INHIBIT_SLEEP;
a2a5291b 451 else if (l == 4 && strneq(word, "idle", l))
f8e2fb7b 452 what |= INHIBIT_IDLE;
a2a5291b 453 else if (l == 16 && strneq(word, "handle-power-key", l))
beaafb2e 454 what |= INHIBIT_HANDLE_POWER_KEY;
a2a5291b 455 else if (l == 18 && strneq(word, "handle-suspend-key", l))
8e7fd6ad 456 what |= INHIBIT_HANDLE_SUSPEND_KEY;
a2a5291b 457 else if (l == 20 && strneq(word, "handle-hibernate-key", l))
8e7fd6ad 458 what |= INHIBIT_HANDLE_HIBERNATE_KEY;
a2a5291b 459 else if (l == 17 && strneq(word, "handle-lid-switch", l))
beaafb2e 460 what |= INHIBIT_HANDLE_LID_SWITCH;
f8e2fb7b
LP
461 else
462 return _INHIBIT_WHAT_INVALID;
463 }
464
465 return what;
f8e2fb7b 466}
eecd1362
LP
467
468static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
469 [INHIBIT_BLOCK] = "block",
470 [INHIBIT_DELAY] = "delay"
471};
472
473DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);