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