]> git.proxmox.com Git - systemd.git/blame - src/login/logind-inhibit.c
New upstream version 236
[systemd.git] / src / login / logind-inhibit.c
CommitLineData
52ad194e 1/* SPDX-License-Identifier: LGPL-2.1+ */
663996b3
MS
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>
663996b3 23#include <string.h>
663996b3
MS
24#include <unistd.h>
25
db2df898
MP
26#include "alloc-util.h"
27#include "escape.h"
28#include "fd-util.h"
663996b3 29#include "fileio.h"
2897b343 30#include "format-util.h"
db2df898
MP
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"
663996b3
MS
38
39Inhibitor* 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);
8a584da2
MP
49 if (!i->state_file)
50 return mfree(i);
663996b3 51
60f067b4 52 i->id = basename(i->state_file);
663996b3
MS
53
54 if (hashmap_put(m->inhibitors, i->id, i) < 0) {
55 free(i->state_file);
8a584da2 56 return mfree(i);
663996b3
MS
57 }
58
59 i->manager = m;
60 i->fifo_fd = -1;
61
62 return i;
63}
64
65void inhibitor_free(Inhibitor *i) {
66 assert(i);
67
663996b3 68 hashmap_remove(i->manager->inhibitors, i->id);
60f067b4 69
663996b3
MS
70 inhibitor_remove_fifo(i);
71
60f067b4
JS
72 free(i->who);
73 free(i->why);
74
663996b3
MS
75 if (i->state_file) {
76 unlink(i->state_file);
77 free(i->state_file);
78 }
79
80 free(i);
81}
82
83int inhibitor_save(Inhibitor *i) {
60f067b4
JS
84 _cleanup_free_ char *temp_path = NULL;
85 _cleanup_fclose_ FILE *f = NULL;
663996b3 86 int r;
663996b3
MS
87
88 assert(i);
89
52ad194e 90 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0, false);
663996b3 91 if (r < 0)
5fd56512 92 goto fail;
663996b3
MS
93
94 r = fopen_temporary(i->state_file, &f, &temp_path);
95 if (r < 0)
5fd56512 96 goto fail;
663996b3
MS
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"
60f067b4
JS
104 "UID="UID_FMT"\n"
105 "PID="PID_FMT"\n",
663996b3
MS
106 inhibit_what_to_string(i->what),
107 inhibit_mode_to_string(i->mode),
60f067b4
JS
108 i->uid,
109 i->pid);
663996b3
MS
110
111 if (i->who) {
60f067b4
JS
112 _cleanup_free_ char *cc = NULL;
113
663996b3 114 cc = cescape(i->who);
13d276d0 115 if (!cc) {
663996b3 116 r = -ENOMEM;
13d276d0
MP
117 goto fail;
118 }
119
120 fprintf(f, "WHO=%s\n", cc);
663996b3
MS
121 }
122
123 if (i->why) {
60f067b4
JS
124 _cleanup_free_ char *cc = NULL;
125
663996b3 126 cc = cescape(i->why);
13d276d0 127 if (!cc) {
663996b3 128 r = -ENOMEM;
13d276d0
MP
129 goto fail;
130 }
131
132 fprintf(f, "WHY=%s\n", cc);
663996b3
MS
133 }
134
135 if (i->fifo_path)
136 fprintf(f, "FIFO=%s\n", i->fifo_path);
137
5fd56512
MP
138 r = fflush_and_check(f);
139 if (r < 0)
140 goto fail;
663996b3 141
5fd56512 142 if (rename(temp_path, i->state_file) < 0) {
663996b3 143 r = -errno;
5fd56512 144 goto fail;
663996b3
MS
145 }
146
5fd56512
MP
147 return 0;
148
149fail:
150 (void) unlink(i->state_file);
151
152 if (temp_path)
153 (void) unlink(temp_path);
663996b3 154
5fd56512 155 return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file);
663996b3
MS
156}
157
158int inhibitor_start(Inhibitor *i) {
159 assert(i);
160
161 if (i->started)
162 return 0;
163
164 dual_timestamp_get(&i->since);
165
60f067b4 166 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.",
663996b3 167 strna(i->who), strna(i->why),
60f067b4 168 i->pid, i->uid,
663996b3
MS
169 inhibit_mode_to_string(i->mode));
170
171 inhibitor_save(i);
172
173 i->started = true;
174
60f067b4 175 manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
663996b3
MS
176
177 return 0;
178}
179
180int inhibitor_stop(Inhibitor *i) {
181 assert(i);
182
183 if (i->started)
60f067b4 184 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.",
663996b3 185 strna(i->who), strna(i->why),
60f067b4 186 i->pid, i->uid,
663996b3
MS
187 inhibit_mode_to_string(i->mode));
188
189 if (i->state_file)
190 unlink(i->state_file);
191
192 i->started = false;
193
60f067b4 194 manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
663996b3
MS
195
196 return 0;
197}
198
199int inhibitor_load(Inhibitor *i) {
60f067b4
JS
200
201 _cleanup_free_ char
663996b3
MS
202 *what = NULL,
203 *uid = NULL,
204 *pid = NULL,
205 *who = NULL,
206 *why = NULL,
207 *mode = NULL;
208
60f067b4
JS
209 InhibitWhat w;
210 InhibitMode mm;
211 char *cc;
212 int r;
213
663996b3
MS
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)
60f067b4 224 return r;
663996b3
MS
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)
60f067b4 237 return r;
663996b3
MS
238 }
239
240 if (pid) {
241 r = parse_pid(pid, &i->pid);
242 if (r < 0)
60f067b4 243 return r;
663996b3
MS
244 }
245
246 if (who) {
e3bff60a
MP
247 r = cunescape(who, 0, &cc);
248 if (r < 0)
249 return r;
663996b3
MS
250
251 free(i->who);
252 i->who = cc;
253 }
254
255 if (why) {
e3bff60a
MP
256 r = cunescape(why, 0, &cc);
257 if (r < 0)
258 return r;
663996b3
MS
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);
60f067b4 268 safe_close(fd);
663996b3
MS
269 }
270
60f067b4
JS
271 return 0;
272}
663996b3 273
60f067b4
JS
274static 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;
663996b3
MS
285}
286
287int inhibitor_create_fifo(Inhibitor *i) {
288 int r;
289
290 assert(i);
291
292 /* Create FIFO */
293 if (!i->fifo_path) {
52ad194e 294 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0, false);
663996b3
MS
295 if (r < 0)
296 return r;
297
2897b343 298 i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref");
60f067b4 299 if (!i->fifo_path)
663996b3
MS
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) {
663996b3
MS
308 i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
309 if (i->fifo_fd < 0)
310 return -errno;
60f067b4 311 }
663996b3 312
60f067b4
JS
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);
663996b3
MS
315 if (r < 0)
316 return r;
317
aa27b158 318 r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10);
60f067b4
JS
319 if (r < 0)
320 return r;
663996b3
MS
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
331void inhibitor_remove_fifo(Inhibitor *i) {
332 assert(i);
333
60f067b4
JS
334 i->event_source = sd_event_source_unref(i->event_source);
335 i->fifo_fd = safe_close(i->fifo_fd);
663996b3
MS
336
337 if (i->fifo_path) {
338 unlink(i->fifo_path);
6300502b 339 i->fifo_path = mfree(i->fifo_path);
663996b3
MS
340 }
341}
342
343InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
344 Inhibitor *i;
345 Iterator j;
346 InhibitWhat what = 0;
347
348 assert(m);
349
60f067b4 350 HASHMAP_FOREACH(i, m->inhibitors, j)
81c58355 351 if (i->mode == mm && i->started)
663996b3
MS
352 what |= i->what;
353
354 return what;
355}
356
357static int pid_is_active(Manager *m, pid_t pid) {
358 Session *s;
359 int r;
360
52ad194e
MB
361 /* Get client session. This is not what you are looking for these days.
362 * FIXME #6852 */
663996b3
MS
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
375bool manager_is_inhibited(
376 Manager *m,
377 InhibitWhat w,
378 InhibitMode mm,
379 dual_timestamp *since,
380 bool ignore_inactive,
381 bool ignore_uid,
60f067b4
JS
382 uid_t uid,
383 Inhibitor **offending) {
663996b3
MS
384
385 Inhibitor *i;
386 Iterator j;
86f210e9 387 struct dual_timestamp ts = DUAL_TIMESTAMP_NULL;
663996b3
MS
388 bool inhibited = false;
389
390 assert(m);
391 assert(w > 0 && w < _INHIBIT_WHAT_MAX);
392
60f067b4 393 HASHMAP_FOREACH(i, m->inhibitors, j) {
81c58355
MB
394 if (!i->started)
395 continue;
396
663996b3
MS
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;
60f067b4
JS
414
415 if (offending)
416 *offending = i;
663996b3
MS
417 }
418
419 if (since)
420 *since = ts;
421
422 return inhibited;
423}
424
425const char *inhibit_what_to_string(InhibitWhat w) {
60f067b4 426 static thread_local char buffer[97];
663996b3
MS
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
456InhibitWhat inhibit_what_from_string(const char *s) {
457 InhibitWhat what = 0;
5eef597e 458 const char *word, *state;
663996b3
MS
459 size_t l;
460
5eef597e
MP
461 FOREACH_WORD_SEPARATOR(word, l, s, ":", state) {
462 if (l == 8 && strneq(word, "shutdown", l))
663996b3 463 what |= INHIBIT_SHUTDOWN;
5eef597e 464 else if (l == 5 && strneq(word, "sleep", l))
663996b3 465 what |= INHIBIT_SLEEP;
5eef597e 466 else if (l == 4 && strneq(word, "idle", l))
663996b3 467 what |= INHIBIT_IDLE;
5eef597e 468 else if (l == 16 && strneq(word, "handle-power-key", l))
663996b3 469 what |= INHIBIT_HANDLE_POWER_KEY;
5eef597e 470 else if (l == 18 && strneq(word, "handle-suspend-key", l))
663996b3 471 what |= INHIBIT_HANDLE_SUSPEND_KEY;
5eef597e 472 else if (l == 20 && strneq(word, "handle-hibernate-key", l))
663996b3 473 what |= INHIBIT_HANDLE_HIBERNATE_KEY;
5eef597e 474 else if (l == 17 && strneq(word, "handle-lid-switch", l))
663996b3
MS
475 what |= INHIBIT_HANDLE_LID_SWITCH;
476 else
477 return _INHIBIT_WHAT_INVALID;
478 }
479
480 return what;
481}
482
483static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
484 [INHIBIT_BLOCK] = "block",
485 [INHIBIT_DELAY] = "delay"
486};
487
488DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);