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