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