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