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