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