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