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