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