chiark / gitweb /
logind: split up HandleSleepKey= into HandleSuspendKey= and HandleHibernateKey=
[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
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         free(i->who);
67         free(i->why);
68
69         hashmap_remove(i->manager->inhibitors, i->id);
70         inhibitor_remove_fifo(i);
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         char *temp_path, *cc;
82         int r;
83         FILE *f;
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=%lu\n"
102                 "PID=%lu\n",
103                 inhibit_what_to_string(i->what),
104                 inhibit_mode_to_string(i->mode),
105                 (unsigned long) i->uid,
106                 (unsigned long) i->pid);
107
108         if (i->who) {
109                 cc = cescape(i->who);
110                 if (!cc)
111                         r = -ENOMEM;
112                 else {
113                         fprintf(f, "WHO=%s\n", cc);
114                         free(cc);
115                 }
116         }
117
118         if (i->why) {
119                 cc = cescape(i->why);
120                 if (!cc)
121                         r = -ENOMEM;
122                 else {
123                         fprintf(f, "WHY=%s\n", cc);
124                         free(cc);
125                 }
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         fclose(f);
140         free(temp_path);
141
142 finish:
143         if (r < 0)
144                 log_error("Failed to save inhibit data for %s: %s", i->id, strerror(-r));
145
146         return r;
147 }
148
149 int inhibitor_start(Inhibitor *i) {
150         assert(i);
151
152         if (i->started)
153                 return 0;
154
155         dual_timestamp_get(&i->since);
156
157         log_debug("Inhibitor %s (%s) pid=%lu uid=%lu mode=%s started.",
158                   strna(i->who), strna(i->why),
159                   (unsigned long) i->pid, (unsigned long) i->uid,
160                   inhibit_mode_to_string(i->mode));
161
162         inhibitor_save(i);
163
164         i->started = true;
165
166         manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited\0" : "DelayInhibited\0");
167
168         return 0;
169 }
170
171 int inhibitor_stop(Inhibitor *i) {
172         assert(i);
173
174         if (i->started)
175                 log_debug("Inhibitor %s (%s) pid=%lu uid=%lu mode=%s stopped.",
176                           strna(i->who), strna(i->why),
177                           (unsigned long) i->pid, (unsigned long) i->uid,
178                           inhibit_mode_to_string(i->mode));
179
180         if (i->state_file)
181                 unlink(i->state_file);
182
183         i->started = false;
184
185         manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited\0" : "DelayInhibited\0");
186
187         return 0;
188 }
189
190 int inhibitor_load(Inhibitor *i) {
191         InhibitWhat w;
192         InhibitMode mm;
193         int r;
194         char *cc,
195                 *what = NULL,
196                 *uid = NULL,
197                 *pid = NULL,
198                 *who = NULL,
199                 *why = NULL,
200                 *mode = NULL;
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                 goto finish;
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                         goto finish;
226         }
227
228         if (pid) {
229                 r = parse_pid(pid, &i->pid);
230                 if (r < 0)
231                         goto finish;
232         }
233
234         if (who) {
235                 cc = cunescape(who);
236                 if (!cc) {
237                         r = -ENOMEM;
238                         goto finish;
239                 }
240
241                 free(i->who);
242                 i->who = cc;
243         }
244
245         if (why) {
246                 cc = cunescape(why);
247                 if (!cc) {
248                         r = -ENOMEM;
249                         goto finish;
250                 }
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                 if (fd >= 0)
261                         close_nointr_nofail(fd);
262         }
263
264 finish:
265         free(what);
266         free(uid);
267         free(pid);
268         free(who);
269         free(why);
270
271         return r;
272 }
273
274 int inhibitor_create_fifo(Inhibitor *i) {
275         int r;
276
277         assert(i);
278
279         /* Create FIFO */
280         if (!i->fifo_path) {
281                 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0);
282                 if (r < 0)
283                         return r;
284
285                 if (asprintf(&i->fifo_path, "/run/systemd/inhibit/%s.ref", i->id) < 0)
286                         return -ENOMEM;
287
288                 if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST)
289                         return -errno;
290         }
291
292         /* Open reading side */
293         if (i->fifo_fd < 0) {
294                 struct epoll_event ev;
295
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                 r = hashmap_put(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1), i);
301                 if (r < 0)
302                         return r;
303
304                 zero(ev);
305                 ev.events = 0;
306                 ev.data.u32 = FD_OTHER_BASE + i->fifo_fd;
307
308                 if (epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_ADD, i->fifo_fd, &ev) < 0)
309                         return -errno;
310         }
311
312         /* Open writing side */
313         r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
314         if (r < 0)
315                 return -errno;
316
317         return r;
318 }
319
320 void inhibitor_remove_fifo(Inhibitor *i) {
321         assert(i);
322
323         if (i->fifo_fd >= 0) {
324                 assert_se(hashmap_remove(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1)) == i);
325                 assert_se(epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_DEL, i->fifo_fd, NULL) == 0);
326                 close_nointr_nofail(i->fifo_fd);
327                 i->fifo_fd = -1;
328         }
329
330         if (i->fifo_path) {
331                 unlink(i->fifo_path);
332                 free(i->fifo_path);
333                 i->fifo_path = NULL;
334         }
335 }
336
337 InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
338         Inhibitor *i;
339         Iterator j;
340         InhibitWhat what = 0;
341
342         assert(m);
343
344         HASHMAP_FOREACH(i, m->inhibitor_fds, j)
345                 if (i->mode == mm)
346                         what |= i->what;
347
348         return what;
349 }
350
351 static int pid_is_active(Manager *m, pid_t pid) {
352         Session *s;
353         int r;
354
355         r = manager_get_session_by_pid(m, pid, &s);
356         if (r <= 0)
357                 return r;
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
371         Inhibitor *i;
372         Iterator j;
373         struct dual_timestamp ts = { 0, 0 };
374         bool inhibited = false;
375
376         assert(m);
377         assert(w > 0 && w < _INHIBIT_WHAT_MAX);
378
379         HASHMAP_FOREACH(i, m->inhibitor_fds, j) {
380                 if (!(i->what & w))
381                         continue;
382
383                 if (i->mode != mm)
384                         continue;
385
386                 if (ignore_inactive && pid_is_active(m, i->pid) <= 0)
387                         continue;
388
389                 if (ignore_uid && i->uid == uid)
390                         continue;
391
392                 if (!inhibited ||
393                     i->since.monotonic < ts.monotonic)
394                         ts = i->since;
395
396                 inhibited = true;
397         }
398
399         if (since)
400                 *since = ts;
401
402         return inhibited;
403 }
404
405 const char *inhibit_what_to_string(InhibitWhat w) {
406         static __thread char buffer[97];
407         char *p;
408
409         if (w < 0 || w >= _INHIBIT_WHAT_MAX)
410                 return NULL;
411
412         p = buffer;
413         if (w & INHIBIT_SHUTDOWN)
414                 p = stpcpy(p, "shutdown:");
415         if (w & INHIBIT_SLEEP)
416                 p = stpcpy(p, "sleep:");
417         if (w & INHIBIT_IDLE)
418                 p = stpcpy(p, "idle:");
419         if (w & INHIBIT_HANDLE_POWER_KEY)
420                 p = stpcpy(p, "handle-power-key:");
421         if (w & INHIBIT_HANDLE_SUSPEND_KEY)
422                 p = stpcpy(p, "handle-suspend-key:");
423         if (w & INHIBIT_HANDLE_HIBERNATE_KEY)
424                 p = stpcpy(p, "handle-hibernate-key:");
425         if (w & INHIBIT_HANDLE_LID_SWITCH)
426                 p = stpcpy(p, "handle-lid-switch:");
427
428         if (p > buffer)
429                 *(p-1) = 0;
430         else
431                 *p = 0;
432
433         return buffer;
434 }
435
436 InhibitWhat inhibit_what_from_string(const char *s) {
437         InhibitWhat what = 0;
438         char *w, *state;
439         size_t l;
440
441         FOREACH_WORD_SEPARATOR(w, l, s, ":", state) {
442                 if (l == 8 && strncmp(w, "shutdown", l) == 0)
443                         what |= INHIBIT_SHUTDOWN;
444                 else if (l == 5 && strncmp(w, "sleep", l) == 0)
445                         what |= INHIBIT_SLEEP;
446                 else if (l == 4 && strncmp(w, "idle", l) == 0)
447                         what |= INHIBIT_IDLE;
448                 else if (l == 16 && strncmp(w, "handle-power-key", l) == 0)
449                         what |= INHIBIT_HANDLE_POWER_KEY;
450                 else if (l == 18 && strncmp(w, "handle-suspend-key", l) == 0)
451                         what |= INHIBIT_HANDLE_SUSPEND_KEY;
452                 else if (l == 20 && strncmp(w, "handle-hibernate-key", l) == 0)
453                         what |= INHIBIT_HANDLE_HIBERNATE_KEY;
454                 else if (l == 17 && strncmp(w, "handle-lid-switch", l) == 0)
455                         what |= INHIBIT_HANDLE_LID_SWITCH;
456                 else
457                         return _INHIBIT_WHAT_INVALID;
458         }
459
460         return what;
461 }
462
463 static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
464         [INHIBIT_BLOCK] = "block",
465         [INHIBIT_DELAY] = "delay"
466 };
467
468 DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);