chiark / gitweb /
logind: hook up inhibit logic with idle hint logic
[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         dual_timestamp_get(&i->since);
154
155         log_debug("Inhibitor %s (%s) pid=%lu uid=%lu started.",
156                   strna(i->who), strna(i->why),
157                   (unsigned long) i->pid, (unsigned long) i->uid);
158
159         inhibitor_save(i);
160
161         i->started = true;
162
163         manager_send_changed(i->manager, "Inhibited\0");
164
165         return 0;
166 }
167
168 int inhibitor_stop(Inhibitor *i) {
169         assert(i);
170
171         if (i->started)
172                 log_debug("Inhibitor %s (%s) pid=%lu uid=%lu stopped.",
173                           strna(i->who), strna(i->why),
174                           (unsigned long) i->pid, (unsigned long) i->uid);
175
176         if (i->state_file)
177                 unlink(i->state_file);
178
179         i->started = false;
180
181         manager_send_changed(i->manager, "Inhibited\0");
182
183         return 0;
184 }
185
186 int inhibitor_load(Inhibitor *i) {
187         InhibitWhat w;
188         int r;
189         char *cc,
190                 *what = NULL,
191                 *uid = NULL,
192                 *pid = NULL,
193                 *who = NULL,
194                 *why = NULL;
195
196         r = parse_env_file(i->state_file, NEWLINE,
197                            "WHAT", &what,
198                            "UID", &uid,
199                            "PID", &pid,
200                            "WHO", &who,
201                            "WHY", &why,
202                            "FIFO", &i->fifo_path,
203                            NULL);
204         if (r < 0)
205                 goto finish;
206
207         w = inhibit_what_from_string(what);
208         if (w >= 0)
209                 i->what = w;
210
211         parse_uid(uid, &i->uid);
212         parse_pid(pid, &i->pid);
213
214         if (who) {
215                 cc = cunescape(who);
216                 if (!cc) {
217                         r = -ENOMEM;
218                         goto finish;
219                 }
220
221                 free(i->who);
222                 i->who = cc;
223         }
224
225         if (why) {
226                 cc = cunescape(why);
227                 if (!cc) {
228                         r = -ENOMEM;
229                         goto finish;
230                 }
231
232                 free(i->why);
233                 i->why = cc;
234         }
235
236         if (i->fifo_path) {
237                 int fd;
238
239                 fd = inhibitor_create_fifo(i);
240                 if (fd >= 0)
241                         close_nointr_nofail(fd);
242         }
243
244 finish:
245         free(what);
246         free(uid);
247         free(pid);
248         free(who);
249         free(why);
250
251         return r;
252 }
253
254 int inhibitor_create_fifo(Inhibitor *i) {
255         int r;
256
257         assert(i);
258
259         /* Create FIFO */
260         if (!i->fifo_path) {
261                 r = safe_mkdir("/run/systemd/inhibit", 0755, 0, 0);
262                 if (r < 0)
263                         return r;
264
265                 if (asprintf(&i->fifo_path, "/run/systemd/inhibit/%s.ref", i->id) < 0)
266                         return -ENOMEM;
267
268                 if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST)
269                         return -errno;
270         }
271
272         /* Open reading side */
273         if (i->fifo_fd < 0) {
274                 struct epoll_event ev;
275
276                 i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
277                 if (i->fifo_fd < 0)
278                         return -errno;
279
280                 r = hashmap_put(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1), i);
281                 if (r < 0)
282                         return r;
283
284                 zero(ev);
285                 ev.events = 0;
286                 ev.data.u32 = FD_FIFO_BASE + i->fifo_fd;
287
288                 if (epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_ADD, i->fifo_fd, &ev) < 0)
289                         return -errno;
290         }
291
292         /* Open writing side */
293         r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
294         if (r < 0)
295                 return -errno;
296
297         return r;
298 }
299
300 void inhibitor_remove_fifo(Inhibitor *i) {
301         assert(i);
302
303         if (i->fifo_fd >= 0) {
304                 assert_se(hashmap_remove(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1)) == i);
305                 assert_se(epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_DEL, i->fifo_fd, NULL) == 0);
306                 close_nointr_nofail(i->fifo_fd);
307                 i->fifo_fd = -1;
308         }
309
310         if (i->fifo_path) {
311                 unlink(i->fifo_path);
312                 free(i->fifo_path);
313                 i->fifo_path = NULL;
314         }
315 }
316
317 InhibitWhat manager_inhibit_what(Manager *m) {
318         Inhibitor *i;
319         Iterator j;
320         InhibitWhat what = 0;
321
322         assert(m);
323
324         HASHMAP_FOREACH(i, m->inhibitor_fds, j)
325                 what |= i->what;
326
327         return what;
328 }
329
330 bool manager_is_inhibited(Manager *m, InhibitWhat w, dual_timestamp *since) {
331         Inhibitor *i;
332         Iterator j;
333         struct dual_timestamp ts = { 0, 0 };
334         bool inhibited = false;
335
336         assert(m);
337         assert(w > 0 && w < _INHIBIT_WHAT_MAX);
338
339         HASHMAP_FOREACH(i, m->inhibitor_fds, j) {
340                 if (!(i->what & w))
341                         continue;
342
343                 if (!inhibited ||
344                     i->since.monotonic < ts.monotonic)
345                         ts = i->since;
346
347                 inhibited = true;
348         }
349
350         if (since)
351                 *since = ts;
352
353         return inhibited;
354 }
355
356 const char *inhibit_what_to_string(InhibitWhat w) {
357
358         static const char* const table[_INHIBIT_WHAT_MAX] = {
359                 [0] = "",
360                 [INHIBIT_SHUTDOWN] = "shutdown",
361                 [INHIBIT_SUSPEND] = "suspend",
362                 [INHIBIT_IDLE] = "idle",
363                 [INHIBIT_SHUTDOWN|INHIBIT_SUSPEND] = "shutdown:suspend",
364                 [INHIBIT_SHUTDOWN|INHIBIT_IDLE] = "shutdown:idle",
365                 [INHIBIT_SHUTDOWN|INHIBIT_SUSPEND|INHIBIT_IDLE] = "shutdown:suspend:idle",
366                 [INHIBIT_SUSPEND|INHIBIT_IDLE] = "suspend:idle"
367         };
368
369         if (w < 0 || w >= _INHIBIT_WHAT_MAX)
370                 return NULL;
371
372         return table[w];
373 }
374
375 InhibitWhat inhibit_what_from_string(const char *s) {
376         InhibitWhat what = 0;
377         char *w, *state;
378         size_t l;
379
380         FOREACH_WORD_SEPARATOR(w, l, s, ":", state) {
381                 if (l == 8 && strncmp(w, "shutdown", l) == 0)
382                         what |= INHIBIT_SHUTDOWN;
383                 else if (l == 7 && strncmp(w, "suspend", l) == 0)
384                         what |= INHIBIT_SUSPEND;
385                 else if (l == 4 && strncmp(w, "idle", l) == 0)
386                         what |= INHIBIT_IDLE;
387                 else
388                         return _INHIBIT_WHAT_INVALID;
389         }
390
391         return what;
392
393 }