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