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