]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-inhibit.c
5912f6feea8cbf7f7d874e95fd234d125709a082
[thirdparty/systemd.git] / src / login / logind-inhibit.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright 2012 Lennart Poettering
4 ***/
5
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <string.h>
9 #include <unistd.h>
10
11 #include "alloc-util.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 "user-util.h"
22 #include "util.h"
23
24 Inhibitor* inhibitor_new(Manager *m, const char* id) {
25 Inhibitor *i;
26
27 assert(m);
28
29 i = new0(Inhibitor, 1);
30 if (!i)
31 return NULL;
32
33 i->state_file = strappend("/run/systemd/inhibit/", id);
34 if (!i->state_file)
35 return mfree(i);
36
37 i->id = basename(i->state_file);
38
39 if (hashmap_put(m->inhibitors, i->id, i) < 0) {
40 free(i->state_file);
41 return mfree(i);
42 }
43
44 i->manager = m;
45 i->fifo_fd = -1;
46
47 return i;
48 }
49
50 void inhibitor_free(Inhibitor *i) {
51 assert(i);
52
53 hashmap_remove(i->manager->inhibitors, i->id);
54
55 inhibitor_remove_fifo(i);
56
57 free(i->who);
58 free(i->why);
59
60 if (i->state_file) {
61 unlink(i->state_file);
62 free(i->state_file);
63 }
64
65 free(i);
66 }
67
68 int inhibitor_save(Inhibitor *i) {
69 _cleanup_free_ char *temp_path = NULL;
70 _cleanup_fclose_ FILE *f = NULL;
71 int r;
72
73 assert(i);
74
75 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0, MKDIR_WARN_MODE);
76 if (r < 0)
77 goto fail;
78
79 r = fopen_temporary(i->state_file, &f, &temp_path);
80 if (r < 0)
81 goto fail;
82
83 fchmod(fileno(f), 0644);
84
85 fprintf(f,
86 "# This is private data. Do not parse.\n"
87 "WHAT=%s\n"
88 "MODE=%s\n"
89 "UID="UID_FMT"\n"
90 "PID="PID_FMT"\n",
91 inhibit_what_to_string(i->what),
92 inhibit_mode_to_string(i->mode),
93 i->uid,
94 i->pid);
95
96 if (i->who) {
97 _cleanup_free_ char *cc = NULL;
98
99 cc = cescape(i->who);
100 if (!cc) {
101 r = -ENOMEM;
102 goto fail;
103 }
104
105 fprintf(f, "WHO=%s\n", cc);
106 }
107
108 if (i->why) {
109 _cleanup_free_ char *cc = NULL;
110
111 cc = cescape(i->why);
112 if (!cc) {
113 r = -ENOMEM;
114 goto fail;
115 }
116
117 fprintf(f, "WHY=%s\n", cc);
118 }
119
120 if (i->fifo_path)
121 fprintf(f, "FIFO=%s\n", i->fifo_path);
122
123 r = fflush_and_check(f);
124 if (r < 0)
125 goto fail;
126
127 if (rename(temp_path, i->state_file) < 0) {
128 r = -errno;
129 goto fail;
130 }
131
132 return 0;
133
134 fail:
135 (void) unlink(i->state_file);
136
137 if (temp_path)
138 (void) unlink(temp_path);
139
140 return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file);
141 }
142
143 int inhibitor_start(Inhibitor *i) {
144 assert(i);
145
146 if (i->started)
147 return 0;
148
149 dual_timestamp_get(&i->since);
150
151 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.",
152 strna(i->who), strna(i->why),
153 i->pid, i->uid,
154 inhibit_mode_to_string(i->mode));
155
156 inhibitor_save(i);
157
158 i->started = true;
159
160 manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
161
162 return 0;
163 }
164
165 int inhibitor_stop(Inhibitor *i) {
166 assert(i);
167
168 if (i->started)
169 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.",
170 strna(i->who), strna(i->why),
171 i->pid, i->uid,
172 inhibit_mode_to_string(i->mode));
173
174 if (i->state_file)
175 unlink(i->state_file);
176
177 i->started = false;
178
179 manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
180
181 return 0;
182 }
183
184 int inhibitor_load(Inhibitor *i) {
185
186 _cleanup_free_ char
187 *what = NULL,
188 *uid = NULL,
189 *pid = NULL,
190 *who = NULL,
191 *why = NULL,
192 *mode = NULL;
193
194 InhibitWhat w;
195 InhibitMode mm;
196 char *cc;
197 int r;
198
199 r = parse_env_file(NULL, i->state_file, NEWLINE,
200 "WHAT", &what,
201 "UID", &uid,
202 "PID", &pid,
203 "WHO", &who,
204 "WHY", &why,
205 "MODE", &mode,
206 "FIFO", &i->fifo_path,
207 NULL);
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 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);