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