chiark / gitweb /
util: split-out path-util.[ch]
[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 = safe_mkdir("/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, "Inhibited\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, "Inhibited\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                 parse_uid(uid, &i->uid);
224
225         if (pid)
226                 parse_pid(pid, &i->pid);
227
228         if (who) {
229                 cc = cunescape(who);
230                 if (!cc) {
231                         r = -ENOMEM;
232                         goto finish;
233                 }
234
235                 free(i->who);
236                 i->who = cc;
237         }
238
239         if (why) {
240                 cc = cunescape(why);
241                 if (!cc) {
242                         r = -ENOMEM;
243                         goto finish;
244                 }
245
246                 free(i->why);
247                 i->why = cc;
248         }
249
250         if (i->fifo_path) {
251                 int fd;
252
253                 fd = inhibitor_create_fifo(i);
254                 if (fd >= 0)
255                         close_nointr_nofail(fd);
256         }
257
258 finish:
259         free(what);
260         free(uid);
261         free(pid);
262         free(who);
263         free(why);
264
265         return r;
266 }
267
268 int inhibitor_create_fifo(Inhibitor *i) {
269         int r;
270
271         assert(i);
272
273         /* Create FIFO */
274         if (!i->fifo_path) {
275                 r = safe_mkdir("/run/systemd/inhibit", 0755, 0, 0);
276                 if (r < 0)
277                         return r;
278
279                 if (asprintf(&i->fifo_path, "/run/systemd/inhibit/%s.ref", i->id) < 0)
280                         return -ENOMEM;
281
282                 if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST)
283                         return -errno;
284         }
285
286         /* Open reading side */
287         if (i->fifo_fd < 0) {
288                 struct epoll_event ev;
289
290                 i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
291                 if (i->fifo_fd < 0)
292                         return -errno;
293
294                 r = hashmap_put(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1), i);
295                 if (r < 0)
296                         return r;
297
298                 zero(ev);
299                 ev.events = 0;
300                 ev.data.u32 = FD_FIFO_BASE + i->fifo_fd;
301
302                 if (epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_ADD, i->fifo_fd, &ev) < 0)
303                         return -errno;
304         }
305
306         /* Open writing side */
307         r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
308         if (r < 0)
309                 return -errno;
310
311         return r;
312 }
313
314 void inhibitor_remove_fifo(Inhibitor *i) {
315         assert(i);
316
317         if (i->fifo_fd >= 0) {
318                 assert_se(hashmap_remove(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1)) == i);
319                 assert_se(epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_DEL, i->fifo_fd, NULL) == 0);
320                 close_nointr_nofail(i->fifo_fd);
321                 i->fifo_fd = -1;
322         }
323
324         if (i->fifo_path) {
325                 unlink(i->fifo_path);
326                 free(i->fifo_path);
327                 i->fifo_path = NULL;
328         }
329 }
330
331 InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
332         Inhibitor *i;
333         Iterator j;
334         InhibitWhat what = 0;
335
336         assert(m);
337
338         HASHMAP_FOREACH(i, m->inhibitor_fds, j)
339                 if (i->mode == mm)
340                         what |= i->what;
341
342         return what;
343 }
344
345 bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since) {
346         Inhibitor *i;
347         Iterator j;
348         struct dual_timestamp ts = { 0, 0 };
349         bool inhibited = false;
350
351         assert(m);
352         assert(w > 0 && w < _INHIBIT_WHAT_MAX);
353
354         HASHMAP_FOREACH(i, m->inhibitor_fds, j) {
355                 if (!(i->what & w))
356                         continue;
357
358                 if (i->mode != mm)
359                         continue;
360
361                 if (!inhibited ||
362                     i->since.monotonic < ts.monotonic)
363                         ts = i->since;
364
365                 inhibited = true;
366         }
367
368         if (since)
369                 *since = ts;
370
371         return inhibited;
372 }
373
374 const char *inhibit_what_to_string(InhibitWhat w) {
375
376         static const char* const table[_INHIBIT_WHAT_MAX] = {
377                 [0] = "",
378                 [INHIBIT_SHUTDOWN] = "shutdown",
379                 [INHIBIT_SUSPEND] = "suspend",
380                 [INHIBIT_IDLE] = "idle",
381                 [INHIBIT_SHUTDOWN|INHIBIT_SUSPEND] = "shutdown:suspend",
382                 [INHIBIT_SHUTDOWN|INHIBIT_IDLE] = "shutdown:idle",
383                 [INHIBIT_SHUTDOWN|INHIBIT_SUSPEND|INHIBIT_IDLE] = "shutdown:suspend:idle",
384                 [INHIBIT_SUSPEND|INHIBIT_IDLE] = "suspend:idle"
385         };
386
387         if (w < 0 || w >= _INHIBIT_WHAT_MAX)
388                 return NULL;
389
390         return table[w];
391 }
392
393 InhibitWhat inhibit_what_from_string(const char *s) {
394         InhibitWhat what = 0;
395         char *w, *state;
396         size_t l;
397
398         FOREACH_WORD_SEPARATOR(w, l, s, ":", state) {
399                 if (l == 8 && strncmp(w, "shutdown", l) == 0)
400                         what |= INHIBIT_SHUTDOWN;
401                 else if (l == 7 && strncmp(w, "suspend", l) == 0)
402                         what |= INHIBIT_SUSPEND;
403                 else if (l == 4 && strncmp(w, "idle", l) == 0)
404                         what |= INHIBIT_IDLE;
405                 else
406                         return _INHIBIT_WHAT_INVALID;
407         }
408
409         return what;
410
411 }
412
413 static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
414         [INHIBIT_BLOCK] = "block",
415         [INHIBIT_DELAY] = "delay"
416 };
417
418 DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);