chiark / gitweb /
tree-wide: remove Lennart's copyright lines
[elogind.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);