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