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