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