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