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