]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-inhibit.c
tree-wide: use mfree more
[thirdparty/systemd.git] / src / login / logind-inhibit.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2012 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <unistd.h>
24
25 #include "alloc-util.h"
26 #include "escape.h"
27 #include "fd-util.h"
28 #include "fileio.h"
29 #include "formats-util.h"
30 #include "logind-inhibit.h"
31 #include "mkdir.h"
32 #include "parse-util.h"
33 #include "string-table.h"
34 #include "string-util.h"
35 #include "user-util.h"
36 #include "util.h"
37
38 Inhibitor* inhibitor_new(Manager *m, const char* id) {
39 Inhibitor *i;
40
41 assert(m);
42
43 i = new0(Inhibitor, 1);
44 if (!i)
45 return NULL;
46
47 i->state_file = strappend("/run/systemd/inhibit/", id);
48 if (!i->state_file)
49 return mfree(i);
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 return mfree(i);
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-10);
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);