]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-inhibit.c
shared/install: use _cleanup_free_
[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"
6482f626 29#include "formats-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);
48 if (!i->state_file) {
49 free(i);
50 return NULL;
51 }
52
2b6bf07d 53 i->id = basename(i->state_file);
f8e2fb7b
LP
54
55 if (hashmap_put(m->inhibitors, i->id, i) < 0) {
56 free(i->state_file);
57 free(i);
58 return NULL;
59 }
60
61 i->manager = m;
62 i->fifo_fd = -1;
63
64 return i;
65}
66
67void inhibitor_free(Inhibitor *i) {
68 assert(i);
69
f8e2fb7b 70 hashmap_remove(i->manager->inhibitors, i->id);
cc377381 71
f8e2fb7b
LP
72 inhibitor_remove_fifo(i);
73
cc377381
LP
74 free(i->who);
75 free(i->why);
76
f8e2fb7b
LP
77 if (i->state_file) {
78 unlink(i->state_file);
79 free(i->state_file);
80 }
81
82 free(i);
83}
84
85int inhibitor_save(Inhibitor *i) {
cc377381
LP
86 _cleanup_free_ char *temp_path = NULL;
87 _cleanup_fclose_ FILE *f = NULL;
f8e2fb7b 88 int r;
f8e2fb7b
LP
89
90 assert(i);
91
d2e54fae 92 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0);
f8e2fb7b 93 if (r < 0)
dacd6cee 94 goto fail;
f8e2fb7b
LP
95
96 r = fopen_temporary(i->state_file, &f, &temp_path);
97 if (r < 0)
dacd6cee 98 goto fail;
f8e2fb7b
LP
99
100 fchmod(fileno(f), 0644);
101
102 fprintf(f,
103 "# This is private data. Do not parse.\n"
104 "WHAT=%s\n"
eecd1362 105 "MODE=%s\n"
90b2de37
ZJS
106 "UID="UID_FMT"\n"
107 "PID="PID_FMT"\n",
f8e2fb7b 108 inhibit_what_to_string(i->what),
eecd1362 109 inhibit_mode_to_string(i->mode),
90b2de37
ZJS
110 i->uid,
111 i->pid);
f8e2fb7b
LP
112
113 if (i->who) {
cc377381
LP
114 _cleanup_free_ char *cc = NULL;
115
f8e2fb7b 116 cc = cescape(i->who);
88231eb6 117 if (!cc) {
f8e2fb7b 118 r = -ENOMEM;
88231eb6
TA
119 goto fail;
120 }
121
122 fprintf(f, "WHO=%s\n", cc);
f8e2fb7b
LP
123 }
124
125 if (i->why) {
cc377381
LP
126 _cleanup_free_ char *cc = NULL;
127
f8e2fb7b 128 cc = cescape(i->why);
88231eb6 129 if (!cc) {
f8e2fb7b 130 r = -ENOMEM;
88231eb6
TA
131 goto fail;
132 }
133
134 fprintf(f, "WHY=%s\n", cc);
f8e2fb7b
LP
135 }
136
137 if (i->fifo_path)
138 fprintf(f, "FIFO=%s\n", i->fifo_path);
139
dacd6cee
LP
140 r = fflush_and_check(f);
141 if (r < 0)
142 goto fail;
f8e2fb7b 143
dacd6cee 144 if (rename(temp_path, i->state_file) < 0) {
f8e2fb7b 145 r = -errno;
dacd6cee 146 goto fail;
f8e2fb7b
LP
147 }
148
dacd6cee
LP
149 return 0;
150
151fail:
152 (void) unlink(i->state_file);
153
154 if (temp_path)
155 (void) unlink(temp_path);
f8e2fb7b 156
dacd6cee 157 return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file);
f8e2fb7b
LP
158}
159
160int inhibitor_start(Inhibitor *i) {
161 assert(i);
162
163 if (i->started)
164 return 0;
165
c7b5eb98
LP
166 dual_timestamp_get(&i->since);
167
de0671ee 168 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.",
f8e2fb7b 169 strna(i->who), strna(i->why),
de0671ee 170 i->pid, i->uid,
eecd1362 171 inhibit_mode_to_string(i->mode));
f8e2fb7b
LP
172
173 inhibitor_save(i);
174
175 i->started = true;
176
cc377381 177 manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
f8e2fb7b
LP
178
179 return 0;
180}
181
182int inhibitor_stop(Inhibitor *i) {
183 assert(i);
184
185 if (i->started)
de0671ee 186 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.",
f8e2fb7b 187 strna(i->who), strna(i->why),
de0671ee 188 i->pid, i->uid,
eecd1362 189 inhibit_mode_to_string(i->mode));
f8e2fb7b
LP
190
191 if (i->state_file)
192 unlink(i->state_file);
193
194 i->started = false;
195
cc377381 196 manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
f8e2fb7b
LP
197
198 return 0;
199}
200
201int inhibitor_load(Inhibitor *i) {
cc377381
LP
202
203 _cleanup_free_ char
f8e2fb7b
LP
204 *what = NULL,
205 *uid = NULL,
206 *pid = NULL,
207 *who = NULL,
eecd1362
LP
208 *why = NULL,
209 *mode = NULL;
f8e2fb7b 210
cc377381
LP
211 InhibitWhat w;
212 InhibitMode mm;
213 char *cc;
214 int r;
215
f8e2fb7b
LP
216 r = parse_env_file(i->state_file, NEWLINE,
217 "WHAT", &what,
218 "UID", &uid,
219 "PID", &pid,
220 "WHO", &who,
221 "WHY", &why,
eecd1362 222 "MODE", &mode,
f8e2fb7b
LP
223 "FIFO", &i->fifo_path,
224 NULL);
225 if (r < 0)
cc377381 226 return r;
f8e2fb7b 227
eecd1362 228 w = what ? inhibit_what_from_string(what) : 0;
f8e2fb7b
LP
229 if (w >= 0)
230 i->what = w;
231
eecd1362
LP
232 mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK;
233 if (mm >= 0)
234 i->mode = mm;
235
a34faf57
LN
236 if (uid) {
237 r = parse_uid(uid, &i->uid);
238 if (r < 0)
cc377381 239 return r;
a34faf57 240 }
eecd1362 241
a34faf57
LN
242 if (pid) {
243 r = parse_pid(pid, &i->pid);
244 if (r < 0)
cc377381 245 return r;
a34faf57 246 }
f8e2fb7b
LP
247
248 if (who) {
527b7a42
LP
249 r = cunescape(who, 0, &cc);
250 if (r < 0)
251 return r;
f8e2fb7b
LP
252
253 free(i->who);
254 i->who = cc;
255 }
256
257 if (why) {
527b7a42
LP
258 r = cunescape(why, 0, &cc);
259 if (r < 0)
260 return r;
f8e2fb7b
LP
261
262 free(i->why);
263 i->why = cc;
264 }
265
266 if (i->fifo_path) {
267 int fd;
268
269 fd = inhibitor_create_fifo(i);
03e334a1 270 safe_close(fd);
f8e2fb7b
LP
271 }
272
cc377381
LP
273 return 0;
274}
f8e2fb7b 275
cc377381
LP
276static int inhibitor_dispatch_fifo(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
277 Inhibitor *i = userdata;
278
279 assert(s);
280 assert(fd == i->fifo_fd);
281 assert(i);
282
283 inhibitor_stop(i);
284 inhibitor_free(i);
285
286 return 0;
f8e2fb7b
LP
287}
288
289int inhibitor_create_fifo(Inhibitor *i) {
290 int r;
291
292 assert(i);
293
294 /* Create FIFO */
295 if (!i->fifo_path) {
d2e54fae 296 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0);
f8e2fb7b
LP
297 if (r < 0)
298 return r;
299
cc377381
LP
300 i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref", NULL);
301 if (!i->fifo_path)
f8e2fb7b
LP
302 return -ENOMEM;
303
304 if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST)
305 return -errno;
306 }
307
308 /* Open reading side */
309 if (i->fifo_fd < 0) {
f8e2fb7b
LP
310 i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
311 if (i->fifo_fd < 0)
312 return -errno;
cc377381 313 }
f8e2fb7b 314
cc377381 315 if (!i->event_source) {
151b9b96 316 r = sd_event_add_io(i->manager->event, &i->event_source, i->fifo_fd, 0, inhibitor_dispatch_fifo, i);
f8e2fb7b
LP
317 if (r < 0)
318 return r;
319
e11544a8 320 r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10);
cc377381
LP
321 if (r < 0)
322 return r;
f8e2fb7b
LP
323 }
324
325 /* Open writing side */
326 r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
327 if (r < 0)
328 return -errno;
329
330 return r;
331}
332
333void inhibitor_remove_fifo(Inhibitor *i) {
334 assert(i);
335
03e334a1
LP
336 i->event_source = sd_event_source_unref(i->event_source);
337 i->fifo_fd = safe_close(i->fifo_fd);
f8e2fb7b
LP
338
339 if (i->fifo_path) {
340 unlink(i->fifo_path);
a1e58e8e 341 i->fifo_path = mfree(i->fifo_path);
f8e2fb7b
LP
342 }
343}
344
eecd1362 345InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
f8e2fb7b
LP
346 Inhibitor *i;
347 Iterator j;
348 InhibitWhat what = 0;
349
350 assert(m);
351
cc377381 352 HASHMAP_FOREACH(i, m->inhibitors, j)
eecd1362
LP
353 if (i->mode == mm)
354 what |= i->what;
f8e2fb7b
LP
355
356 return what;
357}
358
beaafb2e
LP
359static int pid_is_active(Manager *m, pid_t pid) {
360 Session *s;
361 int r;
362
363 r = manager_get_session_by_pid(m, pid, &s);
2c4f86c1 364 if (r < 0)
beaafb2e
LP
365 return r;
366
2c4f86c1
LP
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
beaafb2e
LP
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,
409133be
LP
380 bool ignore_inactive,
381 bool ignore_uid,
85a428c6
LP
382 uid_t uid,
383 Inhibitor **offending) {
beaafb2e 384
c7b5eb98
LP
385 Inhibitor *i;
386 Iterator j;
5cb14b37 387 struct dual_timestamp ts = DUAL_TIMESTAMP_NULL;
c7b5eb98
LP
388 bool inhibited = false;
389
390 assert(m);
391 assert(w > 0 && w < _INHIBIT_WHAT_MAX);
392
cc377381 393 HASHMAP_FOREACH(i, m->inhibitors, j) {
c7b5eb98
LP
394 if (!(i->what & w))
395 continue;
396
eecd1362
LP
397 if (i->mode != mm)
398 continue;
399
409133be
LP
400 if (ignore_inactive && pid_is_active(m, i->pid) <= 0)
401 continue;
402
403 if (ignore_uid && i->uid == uid)
beaafb2e
LP
404 continue;
405
c7b5eb98
LP
406 if (!inhibited ||
407 i->since.monotonic < ts.monotonic)
408 ts = i->since;
409
410 inhibited = true;
85a428c6
LP
411
412 if (offending)
413 *offending = i;
c7b5eb98
LP
414 }
415
416 if (since)
417 *since = ts;
418
419 return inhibited;
420}
421
f8e2fb7b 422const char *inhibit_what_to_string(InhibitWhat w) {
ec202eae 423 static thread_local char buffer[97];
beaafb2e 424 char *p;
f8e2fb7b
LP
425
426 if (w < 0 || w >= _INHIBIT_WHAT_MAX)
427 return NULL;
428
beaafb2e
LP
429 p = buffer;
430 if (w & INHIBIT_SHUTDOWN)
431 p = stpcpy(p, "shutdown:");
432 if (w & INHIBIT_SLEEP)
433 p = stpcpy(p, "sleep:");
434 if (w & INHIBIT_IDLE)
435 p = stpcpy(p, "idle:");
436 if (w & INHIBIT_HANDLE_POWER_KEY)
437 p = stpcpy(p, "handle-power-key:");
8e7fd6ad
LP
438 if (w & INHIBIT_HANDLE_SUSPEND_KEY)
439 p = stpcpy(p, "handle-suspend-key:");
440 if (w & INHIBIT_HANDLE_HIBERNATE_KEY)
441 p = stpcpy(p, "handle-hibernate-key:");
beaafb2e
LP
442 if (w & INHIBIT_HANDLE_LID_SWITCH)
443 p = stpcpy(p, "handle-lid-switch:");
444
445 if (p > buffer)
446 *(p-1) = 0;
447 else
448 *p = 0;
449
450 return buffer;
f8e2fb7b
LP
451}
452
453InhibitWhat inhibit_what_from_string(const char *s) {
454 InhibitWhat what = 0;
a2a5291b 455 const char *word, *state;
f8e2fb7b
LP
456 size_t l;
457
a2a5291b
ZJS
458 FOREACH_WORD_SEPARATOR(word, l, s, ":", state) {
459 if (l == 8 && strneq(word, "shutdown", l))
f8e2fb7b 460 what |= INHIBIT_SHUTDOWN;
a2a5291b 461 else if (l == 5 && strneq(word, "sleep", l))
4943c1c9 462 what |= INHIBIT_SLEEP;
a2a5291b 463 else if (l == 4 && strneq(word, "idle", l))
f8e2fb7b 464 what |= INHIBIT_IDLE;
a2a5291b 465 else if (l == 16 && strneq(word, "handle-power-key", l))
beaafb2e 466 what |= INHIBIT_HANDLE_POWER_KEY;
a2a5291b 467 else if (l == 18 && strneq(word, "handle-suspend-key", l))
8e7fd6ad 468 what |= INHIBIT_HANDLE_SUSPEND_KEY;
a2a5291b 469 else if (l == 20 && strneq(word, "handle-hibernate-key", l))
8e7fd6ad 470 what |= INHIBIT_HANDLE_HIBERNATE_KEY;
a2a5291b 471 else if (l == 17 && strneq(word, "handle-lid-switch", l))
beaafb2e 472 what |= INHIBIT_HANDLE_LID_SWITCH;
f8e2fb7b
LP
473 else
474 return _INHIBIT_WHAT_INVALID;
475 }
476
477 return what;
f8e2fb7b 478}
eecd1362
LP
479
480static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
481 [INHIBIT_BLOCK] = "block",
482 [INHIBIT_DELAY] = "delay"
483};
484
485DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);