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