chiark / gitweb /
5d81693e6c0bdbd4e2b03f66ca3ac05f06553fec
[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 "logind-inhibit.h"
30 #include "fileio.h"
31
32 Inhibitor* inhibitor_new(Manager *m, const char* id) {
33         Inhibitor *i;
34
35         assert(m);
36
37         i = new0(Inhibitor, 1);
38         if (!i)
39                 return NULL;
40
41         i->state_file = strappend("/run/systemd/inhibit/", id);
42         if (!i->state_file) {
43                 free(i);
44                 return NULL;
45         }
46
47         i->id = basename(i->state_file);
48
49         if (hashmap_put(m->inhibitors, i->id, i) < 0) {
50                 free(i->state_file);
51                 free(i);
52                 return NULL;
53         }
54
55         i->manager = m;
56         i->fifo_fd = -1;
57
58         return i;
59 }
60
61 void inhibitor_free(Inhibitor *i) {
62         assert(i);
63
64         hashmap_remove(i->manager->inhibitors, i->id);
65
66         inhibitor_remove_fifo(i);
67
68         free(i->who);
69         free(i->why);
70
71         if (i->state_file) {
72                 unlink(i->state_file);
73                 free(i->state_file);
74         }
75
76         free(i);
77 }
78
79 int inhibitor_save(Inhibitor *i) {
80         _cleanup_free_ char *temp_path = NULL;
81         _cleanup_fclose_ FILE *f = NULL;
82         int r;
83
84         assert(i);
85
86         r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0);
87         if (r < 0)
88                 goto finish;
89
90         r = fopen_temporary(i->state_file, &f, &temp_path);
91         if (r < 0)
92                 goto finish;
93
94         fchmod(fileno(f), 0644);
95
96         fprintf(f,
97                 "# This is private data. Do not parse.\n"
98                 "WHAT=%s\n"
99                 "MODE=%s\n"
100                 "UID="UID_FMT"\n"
101                 "PID="PID_FMT"\n",
102                 inhibit_what_to_string(i->what),
103                 inhibit_mode_to_string(i->mode),
104                 i->uid,
105                 i->pid);
106
107         if (i->who) {
108                 _cleanup_free_ char *cc = NULL;
109
110                 cc = cescape(i->who);
111                 if (!cc)
112                         r = -ENOMEM;
113                 else
114                         fprintf(f, "WHO=%s\n", cc);
115         }
116
117         if (i->why) {
118                 _cleanup_free_ char *cc = NULL;
119
120                 cc = cescape(i->why);
121                 if (!cc)
122                         r = -ENOMEM;
123                 else
124                         fprintf(f, "WHY=%s\n", cc);
125         }
126
127         if (i->fifo_path)
128                 fprintf(f, "FIFO=%s\n", i->fifo_path);
129
130         fflush(f);
131
132         if (ferror(f) || rename(temp_path, i->state_file) < 0) {
133                 r = -errno;
134                 unlink(i->state_file);
135                 unlink(temp_path);
136         }
137
138 finish:
139         if (r < 0)
140                 log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file);
141
142         return r;
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(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                 cc = cunescape(who);
235                 if (!cc)
236                         return -ENOMEM;
237
238                 free(i->who);
239                 i->who = cc;
240         }
241
242         if (why) {
243                 cc = cunescape(why);
244                 if (!cc)
245                         return -ENOMEM;
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);
282                 if (r < 0)
283                         return r;
284
285                 i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref", NULL);
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_NDELAY);
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);
306                 if (r < 0)
307                         return r;
308         }
309
310         /* Open writing side */
311         r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
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                 free(i->fifo_path);
327                 i->fifo_path = NULL;
328         }
329 }
330
331 InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
332         Inhibitor *i;
333         Iterator j;
334         InhibitWhat what = 0;
335
336         assert(m);
337
338         HASHMAP_FOREACH(i, m->inhibitors, j)
339                 if (i->mode == mm)
340                         what |= i->what;
341
342         return what;
343 }
344
345 static int pid_is_active(Manager *m, pid_t pid) {
346         Session *s;
347         int r;
348
349         r = manager_get_session_by_pid(m, pid, &s);
350         if (r < 0)
351                 return r;
352
353         /* If there's no session assigned to it, then it's globally
354          * active on all ttys */
355         if (r == 0)
356                 return 1;
357
358         return session_is_active(s);
359 }
360
361 bool manager_is_inhibited(
362                 Manager *m,
363                 InhibitWhat w,
364                 InhibitMode mm,
365                 dual_timestamp *since,
366                 bool ignore_inactive,
367                 bool ignore_uid,
368                 uid_t uid,
369                 Inhibitor **offending) {
370
371         Inhibitor *i;
372         Iterator j;
373         struct dual_timestamp ts = { 0, 0 };
374         bool inhibited = false;
375
376         assert(m);
377         assert(w > 0 && w < _INHIBIT_WHAT_MAX);
378
379         HASHMAP_FOREACH(i, m->inhibitors, j) {
380                 if (!(i->what & w))
381                         continue;
382
383                 if (i->mode != mm)
384                         continue;
385
386                 if (ignore_inactive && pid_is_active(m, i->pid) <= 0)
387                         continue;
388
389                 if (ignore_uid && i->uid == uid)
390                         continue;
391
392                 if (!inhibited ||
393                     i->since.monotonic < ts.monotonic)
394                         ts = i->since;
395
396                 inhibited = true;
397
398                 if (offending)
399                         *offending = i;
400         }
401
402         if (since)
403                 *since = ts;
404
405         return inhibited;
406 }
407
408 const char *inhibit_what_to_string(InhibitWhat w) {
409         static thread_local char buffer[97];
410         char *p;
411
412         if (w < 0 || w >= _INHIBIT_WHAT_MAX)
413                 return NULL;
414
415         p = buffer;
416         if (w & INHIBIT_SHUTDOWN)
417                 p = stpcpy(p, "shutdown:");
418         if (w & INHIBIT_SLEEP)
419                 p = stpcpy(p, "sleep:");
420         if (w & INHIBIT_IDLE)
421                 p = stpcpy(p, "idle:");
422         if (w & INHIBIT_HANDLE_POWER_KEY)
423                 p = stpcpy(p, "handle-power-key:");
424         if (w & INHIBIT_HANDLE_SUSPEND_KEY)
425                 p = stpcpy(p, "handle-suspend-key:");
426         if (w & INHIBIT_HANDLE_HIBERNATE_KEY)
427                 p = stpcpy(p, "handle-hibernate-key:");
428         if (w & INHIBIT_HANDLE_LID_SWITCH)
429                 p = stpcpy(p, "handle-lid-switch:");
430
431         if (p > buffer)
432                 *(p-1) = 0;
433         else
434                 *p = 0;
435
436         return buffer;
437 }
438
439 InhibitWhat inhibit_what_from_string(const char *s) {
440         InhibitWhat what = 0;
441         const char *word, *state;
442         size_t l;
443
444         FOREACH_WORD_SEPARATOR(word, l, s, ":", state) {
445                 if (l == 8 && strneq(word, "shutdown", l))
446                         what |= INHIBIT_SHUTDOWN;
447                 else if (l == 5 && strneq(word, "sleep", l))
448                         what |= INHIBIT_SLEEP;
449                 else if (l == 4 && strneq(word, "idle", l))
450                         what |= INHIBIT_IDLE;
451                 else if (l == 16 && strneq(word, "handle-power-key", l))
452                         what |= INHIBIT_HANDLE_POWER_KEY;
453                 else if (l == 18 && strneq(word, "handle-suspend-key", l))
454                         what |= INHIBIT_HANDLE_SUSPEND_KEY;
455                 else if (l == 20 && strneq(word, "handle-hibernate-key", l))
456                         what |= INHIBIT_HANDLE_HIBERNATE_KEY;
457                 else if (l == 17 && strneq(word, "handle-lid-switch", l))
458                         what |= INHIBIT_HANDLE_LID_SWITCH;
459                 else
460                         return _INHIBIT_WHAT_INVALID;
461         }
462
463         return what;
464 }
465
466 static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
467         [INHIBIT_BLOCK] = "block",
468         [INHIBIT_DELAY] = "delay"
469 };
470
471 DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);