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