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