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