chiark / gitweb /
2f7a758e7cf2bba48efe87b6b500eee00f84b483
[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
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 = file_name_from_path(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                 "UID=%lu\n"
101                 "PID=%lu\n",
102                 inhibit_what_to_string(i->what),
103                 (unsigned long) i->uid,
104                 (unsigned long) i->pid);
105
106         if (i->who) {
107                 cc = cescape(i->who);
108                 if (!cc)
109                         r = -ENOMEM;
110                 else {
111                         fprintf(f, "WHO=%s\n", cc);
112                         free(cc);
113                 }
114         }
115
116         if (i->why) {
117                 cc = cescape(i->why);
118                 if (!cc)
119                         r = -ENOMEM;
120                 else {
121                         fprintf(f, "WHY=%s\n", cc);
122                         free(cc);
123                 }
124         }
125
126         if (i->fifo_path)
127                 fprintf(f, "FIFO=%s\n", i->fifo_path);
128
129         fflush(f);
130
131         if (ferror(f) || rename(temp_path, i->state_file) < 0) {
132                 r = -errno;
133                 unlink(i->state_file);
134                 unlink(temp_path);
135         }
136
137         fclose(f);
138         free(temp_path);
139
140 finish:
141         if (r < 0)
142                 log_error("Failed to save inhibit data for %s: %s", i->id, strerror(-r));
143
144         return r;
145 }
146
147 int inhibitor_start(Inhibitor *i) {
148         assert(i);
149
150         if (i->started)
151                 return 0;
152
153         log_debug("Inhibitor %s (%s) pid=%lu uid=%lu started.",
154                   strna(i->who), strna(i->why),
155                   (unsigned long) i->pid, (unsigned long) i->uid);
156
157         inhibitor_save(i);
158
159         i->started = true;
160
161         manager_send_changed(i->manager, "Inhibited\0");
162
163         return 0;
164 }
165
166 int inhibitor_stop(Inhibitor *i) {
167         assert(i);
168
169         if (i->started)
170                 log_debug("Inhibitor %s (%s) pid=%lu uid=%lu stopped.",
171                           strna(i->who), strna(i->why),
172                           (unsigned long) i->pid, (unsigned long) i->uid);
173
174         if (i->state_file)
175                 unlink(i->state_file);
176
177         i->started = false;
178
179         manager_send_changed(i->manager, "Inhibited\0");
180
181         return 0;
182 }
183
184 int inhibitor_load(Inhibitor *i) {
185         InhibitWhat w;
186         int r;
187         char *cc,
188                 *what = NULL,
189                 *uid = NULL,
190                 *pid = NULL,
191                 *who = NULL,
192                 *why = NULL;
193
194         r = parse_env_file(i->state_file, NEWLINE,
195                            "WHAT", &what,
196                            "UID", &uid,
197                            "PID", &pid,
198                            "WHO", &who,
199                            "WHY", &why,
200                            "FIFO", &i->fifo_path,
201                            NULL);
202         if (r < 0)
203                 goto finish;
204
205         w = inhibit_what_from_string(what);
206         if (w >= 0)
207                 i->what = w;
208
209         parse_uid(uid, &i->uid);
210         parse_pid(pid, &i->pid);
211
212         if (who) {
213                 cc = cunescape(who);
214                 if (!cc) {
215                         r = -ENOMEM;
216                         goto finish;
217                 }
218
219                 free(i->who);
220                 i->who = cc;
221         }
222
223         if (why) {
224                 cc = cunescape(why);
225                 if (!cc) {
226                         r = -ENOMEM;
227                         goto finish;
228                 }
229
230                 free(i->why);
231                 i->why = cc;
232         }
233
234         if (i->fifo_path) {
235                 int fd;
236
237                 fd = inhibitor_create_fifo(i);
238                 if (fd >= 0)
239                         close_nointr_nofail(fd);
240         }
241
242 finish:
243         free(what);
244         free(uid);
245         free(pid);
246         free(who);
247         free(why);
248
249         return r;
250 }
251
252 int inhibitor_create_fifo(Inhibitor *i) {
253         int r;
254
255         assert(i);
256
257         /* Create FIFO */
258         if (!i->fifo_path) {
259                 r = safe_mkdir("/run/systemd/inhibit", 0755, 0, 0);
260                 if (r < 0)
261                         return r;
262
263                 if (asprintf(&i->fifo_path, "/run/systemd/inhibit/%s.ref", i->id) < 0)
264                         return -ENOMEM;
265
266                 if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST)
267                         return -errno;
268         }
269
270         /* Open reading side */
271         if (i->fifo_fd < 0) {
272                 struct epoll_event ev;
273
274                 i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
275                 if (i->fifo_fd < 0)
276                         return -errno;
277
278                 r = hashmap_put(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1), i);
279                 if (r < 0)
280                         return r;
281
282                 zero(ev);
283                 ev.events = 0;
284                 ev.data.u32 = FD_FIFO_BASE + i->fifo_fd;
285
286                 if (epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_ADD, i->fifo_fd, &ev) < 0)
287                         return -errno;
288         }
289
290         /* Open writing side */
291         r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
292         if (r < 0)
293                 return -errno;
294
295         return r;
296 }
297
298 void inhibitor_remove_fifo(Inhibitor *i) {
299         assert(i);
300
301         if (i->fifo_fd >= 0) {
302                 assert_se(hashmap_remove(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1)) == i);
303                 assert_se(epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_DEL, i->fifo_fd, NULL) == 0);
304                 close_nointr_nofail(i->fifo_fd);
305                 i->fifo_fd = -1;
306         }
307
308         if (i->fifo_path) {
309                 unlink(i->fifo_path);
310                 free(i->fifo_path);
311                 i->fifo_path = NULL;
312         }
313 }
314
315 InhibitWhat manager_inhibit_what(Manager *m) {
316         Inhibitor *i;
317         Iterator j;
318         InhibitWhat what = 0;
319
320         assert(m);
321
322         HASHMAP_FOREACH(i, m->inhibitor_fds, j)
323                 what |= i->what;
324
325         return what;
326 }
327
328 const char *inhibit_what_to_string(InhibitWhat w) {
329
330         static const char* const table[_INHIBIT_WHAT_MAX] = {
331                 [0] = "",
332                 [INHIBIT_SHUTDOWN] = "shutdown",
333                 [INHIBIT_SUSPEND] = "suspend",
334                 [INHIBIT_IDLE] = "idle",
335                 [INHIBIT_SHUTDOWN|INHIBIT_SUSPEND] = "shutdown:suspend",
336                 [INHIBIT_SHUTDOWN|INHIBIT_IDLE] = "shutdown:idle",
337                 [INHIBIT_SHUTDOWN|INHIBIT_SUSPEND|INHIBIT_IDLE] = "shutdown:suspend:idle",
338                 [INHIBIT_SUSPEND|INHIBIT_IDLE] = "suspend:idle"
339         };
340
341         if (w < 0 || w >= _INHIBIT_WHAT_MAX)
342                 return NULL;
343
344         return table[w];
345 }
346
347 InhibitWhat inhibit_what_from_string(const char *s) {
348         InhibitWhat what = 0;
349         char *w, *state;
350         size_t l;
351
352         FOREACH_WORD_SEPARATOR(w, l, s, ":", state) {
353                 if (l == 8 && strncmp(w, "shutdown", l) == 0)
354                         what |= INHIBIT_SHUTDOWN;
355                 else if (l == 7 && strncmp(w, "suspend", l) == 0)
356                         what |= INHIBIT_SUSPEND;
357                 else if (l == 4 && strncmp(w, "idle", l) == 0)
358                         what |= INHIBIT_IDLE;
359                 else
360                         return _INHIBIT_WHAT_INVALID;
361         }
362
363         return what;
364
365 }