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