chiark / gitweb /
logind: make sure there's always a getty available on TTY6
[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 bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since) {
352         Inhibitor *i;
353         Iterator j;
354         struct dual_timestamp ts = { 0, 0 };
355         bool inhibited = false;
356
357         assert(m);
358         assert(w > 0 && w < _INHIBIT_WHAT_MAX);
359
360         HASHMAP_FOREACH(i, m->inhibitor_fds, j) {
361                 if (!(i->what & w))
362                         continue;
363
364                 if (i->mode != mm)
365                         continue;
366
367                 if (!inhibited ||
368                     i->since.monotonic < ts.monotonic)
369                         ts = i->since;
370
371                 inhibited = true;
372         }
373
374         if (since)
375                 *since = ts;
376
377         return inhibited;
378 }
379
380 const char *inhibit_what_to_string(InhibitWhat w) {
381
382         static const char* const table[_INHIBIT_WHAT_MAX] = {
383                 [0] = "",
384                 [INHIBIT_SHUTDOWN] = "shutdown",
385                 [INHIBIT_SLEEP] = "sleep",
386                 [INHIBIT_IDLE] = "idle",
387                 [INHIBIT_SHUTDOWN|INHIBIT_SLEEP] = "shutdown:sleep",
388                 [INHIBIT_SHUTDOWN|INHIBIT_IDLE] = "shutdown:idle",
389                 [INHIBIT_SHUTDOWN|INHIBIT_SLEEP|INHIBIT_IDLE] = "shutdown:sleep:idle",
390                 [INHIBIT_SLEEP|INHIBIT_IDLE] = "sleep:idle"
391         };
392
393         if (w < 0 || w >= _INHIBIT_WHAT_MAX)
394                 return NULL;
395
396         return table[w];
397 }
398
399 InhibitWhat inhibit_what_from_string(const char *s) {
400         InhibitWhat what = 0;
401         char *w, *state;
402         size_t l;
403
404         FOREACH_WORD_SEPARATOR(w, l, s, ":", state) {
405                 if (l == 8 && strncmp(w, "shutdown", l) == 0)
406                         what |= INHIBIT_SHUTDOWN;
407                 else if (l == 5 && strncmp(w, "sleep", l) == 0)
408                         what |= INHIBIT_SLEEP;
409                 else if (l == 4 && strncmp(w, "idle", l) == 0)
410                         what |= INHIBIT_IDLE;
411                 else
412                         return _INHIBIT_WHAT_INVALID;
413         }
414
415         return what;
416
417 }
418
419 static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
420         [INHIBIT_BLOCK] = "block",
421         [INHIBIT_DELAY] = "delay"
422 };
423
424 DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);