chiark / gitweb /
c43ae23acf6adf8d76b4ccf4bbe1b0510c9352ee
[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 <sys/epoll.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28
29 #include "util.h"
30 #include "mkdir.h"
31 #include "path-util.h"
32 #include "logind-inhibit.h"
33 #include "fileio.h"
34
35 Inhibitor* inhibitor_new(Manager *m, const char* id) {
36         Inhibitor *i;
37
38         assert(m);
39
40         i = new0(Inhibitor, 1);
41         if (!i)
42                 return NULL;
43
44         i->state_file = strappend("/run/systemd/inhibit/", id);
45         if (!i->state_file) {
46                 free(i);
47                 return NULL;
48         }
49
50         i->id = path_get_file_name(i->state_file);
51
52         if (hashmap_put(m->inhibitors, i->id, i) < 0) {
53                 free(i->state_file);
54                 free(i);
55                 return NULL;
56         }
57
58         i->manager = m;
59         i->fifo_fd = -1;
60
61         return i;
62 }
63
64 void inhibitor_free(Inhibitor *i) {
65         assert(i);
66
67         free(i->who);
68         free(i->why);
69
70         hashmap_remove(i->manager->inhibitors, i->id);
71         inhibitor_remove_fifo(i);
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         char *temp_path, *cc;
83         int r;
84         FILE *f;
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                 cc = cescape(i->who);
111                 if (!cc)
112                         r = -ENOMEM;
113                 else {
114                         fprintf(f, "WHO=%s\n", cc);
115                         free(cc);
116                 }
117         }
118
119         if (i->why) {
120                 cc = cescape(i->why);
121                 if (!cc)
122                         r = -ENOMEM;
123                 else {
124                         fprintf(f, "WHY=%s\n", cc);
125                         free(cc);
126                 }
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         fclose(f);
141         free(temp_path);
142
143 finish:
144         if (r < 0)
145                 log_error("Failed to save inhibit data for %s: %s", i->id, strerror(-r));
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=%lu uid=%lu mode=%s started.",
159                   strna(i->who), strna(i->why),
160                   (unsigned long) i->pid, (unsigned long) 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\0" : "DelayInhibited\0");
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=%lu uid=%lu mode=%s stopped.",
177                           strna(i->who), strna(i->why),
178                           (unsigned long) i->pid, (unsigned long) 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\0" : "DelayInhibited\0");
187
188         return 0;
189 }
190
191 int inhibitor_load(Inhibitor *i) {
192         InhibitWhat w;
193         InhibitMode mm;
194         int r;
195         char *cc,
196                 *what = NULL,
197                 *uid = NULL,
198                 *pid = NULL,
199                 *who = NULL,
200                 *why = NULL,
201                 *mode = NULL;
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                 goto finish;
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                         goto finish;
227         }
228
229         if (pid) {
230                 r = parse_pid(pid, &i->pid);
231                 if (r < 0)
232                         goto finish;
233         }
234
235         if (who) {
236                 cc = cunescape(who);
237                 if (!cc) {
238                         r = -ENOMEM;
239                         goto finish;
240                 }
241
242                 free(i->who);
243                 i->who = cc;
244         }
245
246         if (why) {
247                 cc = cunescape(why);
248                 if (!cc) {
249                         r = -ENOMEM;
250                         goto finish;
251                 }
252
253                 free(i->why);
254                 i->why = cc;
255         }
256
257         if (i->fifo_path) {
258                 int fd;
259
260                 fd = inhibitor_create_fifo(i);
261                 if (fd >= 0)
262                         close_nointr_nofail(fd);
263         }
264
265 finish:
266         free(what);
267         free(uid);
268         free(pid);
269         free(who);
270         free(why);
271
272         return r;
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                 if (asprintf(&i->fifo_path, "/run/systemd/inhibit/%s.ref", i->id) < 0)
287                         return -ENOMEM;
288
289                 if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST)
290                         return -errno;
291         }
292
293         /* Open reading side */
294         if (i->fifo_fd < 0) {
295                 struct epoll_event ev;
296
297                 i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
298                 if (i->fifo_fd < 0)
299                         return -errno;
300
301                 r = hashmap_put(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1), i);
302                 if (r < 0)
303                         return r;
304
305                 zero(ev);
306                 ev.events = 0;
307                 ev.data.u32 = FD_OTHER_BASE + i->fifo_fd;
308
309                 if (epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_ADD, i->fifo_fd, &ev) < 0)
310                         return -errno;
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->fifo_fd >= 0) {
325                 assert_se(hashmap_remove(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1)) == i);
326                 assert_se(epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_DEL, i->fifo_fd, NULL) == 0);
327                 close_nointr_nofail(i->fifo_fd);
328                 i->fifo_fd = -1;
329         }
330
331         if (i->fifo_path) {
332                 unlink(i->fifo_path);
333                 free(i->fifo_path);
334                 i->fifo_path = NULL;
335         }
336 }
337
338 InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
339         Inhibitor *i;
340         Iterator j;
341         InhibitWhat what = 0;
342
343         assert(m);
344
345         HASHMAP_FOREACH(i, m->inhibitor_fds, j)
346                 if (i->mode == mm)
347                         what |= i->what;
348
349         return what;
350 }
351
352 static int pid_is_active(Manager *m, pid_t pid) {
353         Session *s;
354         int r;
355
356         r = manager_get_session_by_pid(m, pid, &s);
357         if (r < 0)
358                 return r;
359
360         /* If there's no session assigned to it, then it's globally
361          * active on all ttys */
362         if (r == 0)
363                 return 1;
364
365         return session_is_active(s);
366 }
367
368 bool manager_is_inhibited(
369                 Manager *m,
370                 InhibitWhat w,
371                 InhibitMode mm,
372                 dual_timestamp *since,
373                 bool ignore_inactive,
374                 bool ignore_uid,
375                 uid_t uid) {
376
377         Inhibitor *i;
378         Iterator j;
379         struct dual_timestamp ts = { 0, 0 };
380         bool inhibited = false;
381
382         assert(m);
383         assert(w > 0 && w < _INHIBIT_WHAT_MAX);
384
385         HASHMAP_FOREACH(i, m->inhibitor_fds, j) {
386                 if (!(i->what & w))
387                         continue;
388
389                 if (i->mode != mm)
390                         continue;
391
392                 if (ignore_inactive && pid_is_active(m, i->pid) <= 0)
393                         continue;
394
395                 if (ignore_uid && i->uid == uid)
396                         continue;
397
398                 if (!inhibited ||
399                     i->since.monotonic < ts.monotonic)
400                         ts = i->since;
401
402                 inhibited = true;
403         }
404
405         if (since)
406                 *since = ts;
407
408         return inhibited;
409 }
410
411 const char *inhibit_what_to_string(InhibitWhat w) {
412         static __thread char buffer[97];
413         char *p;
414
415         if (w < 0 || w >= _INHIBIT_WHAT_MAX)
416                 return NULL;
417
418         p = buffer;
419         if (w & INHIBIT_SHUTDOWN)
420                 p = stpcpy(p, "shutdown:");
421         if (w & INHIBIT_SLEEP)
422                 p = stpcpy(p, "sleep:");
423         if (w & INHIBIT_IDLE)
424                 p = stpcpy(p, "idle:");
425         if (w & INHIBIT_HANDLE_POWER_KEY)
426                 p = stpcpy(p, "handle-power-key:");
427         if (w & INHIBIT_HANDLE_SUSPEND_KEY)
428                 p = stpcpy(p, "handle-suspend-key:");
429         if (w & INHIBIT_HANDLE_HIBERNATE_KEY)
430                 p = stpcpy(p, "handle-hibernate-key:");
431         if (w & INHIBIT_HANDLE_LID_SWITCH)
432                 p = stpcpy(p, "handle-lid-switch:");
433
434         if (p > buffer)
435                 *(p-1) = 0;
436         else
437                 *p = 0;
438
439         return buffer;
440 }
441
442 InhibitWhat inhibit_what_from_string(const char *s) {
443         InhibitWhat what = 0;
444         char *w, *state;
445         size_t l;
446
447         FOREACH_WORD_SEPARATOR(w, l, s, ":", state) {
448                 if (l == 8 && strneq(w, "shutdown", l))
449                         what |= INHIBIT_SHUTDOWN;
450                 else if (l == 5 && strneq(w, "sleep", l))
451                         what |= INHIBIT_SLEEP;
452                 else if (l == 4 && strneq(w, "idle", l))
453                         what |= INHIBIT_IDLE;
454                 else if (l == 16 && strneq(w, "handle-power-key", l))
455                         what |= INHIBIT_HANDLE_POWER_KEY;
456                 else if (l == 18 && strneq(w, "handle-suspend-key", l))
457                         what |= INHIBIT_HANDLE_SUSPEND_KEY;
458                 else if (l == 20 && strneq(w, "handle-hibernate-key", l))
459                         what |= INHIBIT_HANDLE_HIBERNATE_KEY;
460                 else if (l == 17 && strneq(w, "handle-lid-switch", l))
461                         what |= INHIBIT_HANDLE_LID_SWITCH;
462                 else
463                         return _INHIBIT_WHAT_INVALID;
464         }
465
466         return what;
467 }
468
469 static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
470         [INHIBIT_BLOCK] = "block",
471         [INHIBIT_DELAY] = "delay"
472 };
473
474 DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);