]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-inhibit.c
Rename formats-util.h to format-util.h
[thirdparty/systemd.git] / src / login / logind-inhibit.c
CommitLineData
f8e2fb7b
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2012 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
20#include <errno.h>
21#include <fcntl.h>
f8e2fb7b 22#include <string.h>
f8e2fb7b
LP
23#include <unistd.h>
24
b5efdb8a 25#include "alloc-util.h"
4f5dd394 26#include "escape.h"
3ffd4af2 27#include "fd-util.h"
a5c32cff 28#include "fileio.h"
f97b34a6 29#include "format-util.h"
3ffd4af2 30#include "logind-inhibit.h"
4f5dd394 31#include "mkdir.h"
6bedfcbb 32#include "parse-util.h"
8b43440b 33#include "string-table.h"
07630cea 34#include "string-util.h"
b1d4f8e1 35#include "user-util.h"
4f5dd394 36#include "util.h"
f8e2fb7b
LP
37
38Inhibitor* inhibitor_new(Manager *m, const char* id) {
39 Inhibitor *i;
40
41 assert(m);
42
43 i = new0(Inhibitor, 1);
44 if (!i)
45 return NULL;
46
47 i->state_file = strappend("/run/systemd/inhibit/", id);
6b430fdb
ZJS
48 if (!i->state_file)
49 return mfree(i);
f8e2fb7b 50
2b6bf07d 51 i->id = basename(i->state_file);
f8e2fb7b
LP
52
53 if (hashmap_put(m->inhibitors, i->id, i) < 0) {
54 free(i->state_file);
6b430fdb 55 return mfree(i);
f8e2fb7b
LP
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
605405c6 297 i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref");
cc377381 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
e11544a8 317 r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10);
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);