chiark / gitweb /
Unifiy free() usage
[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 <string.h>
25 #include <unistd.h>
26
27 #include "util.h"
28 #include "mkdir.h"
29 #include "logind-inhibit.h"
30 #include "fileio.h"
31 #include "formats-util.h"
32
33 Inhibitor* inhibitor_new(Manager *m, const char* id) {
34         Inhibitor *i;
35
36         assert(m);
37
38         i = new0(Inhibitor, 1);
39         if (!i)
40                 return NULL;
41
42         i->state_file = strappend("/run/systemd/inhibit/", id);
43         if (!i->state_file) {
44                 free(i);
45                 return NULL;
46         }
47
48         i->id = basename(i->state_file);
49
50         if (hashmap_put(m->inhibitors, i->id, i) < 0) {
51                 free(i->state_file);
52                 free(i);
53                 return NULL;
54         }
55
56         i->manager = m;
57         i->fifo_fd = -1;
58
59         return i;
60 }
61
62 void inhibitor_free(Inhibitor *i) {
63         assert(i);
64
65         hashmap_remove(i->manager->inhibitors, i->id);
66
67         inhibitor_remove_fifo(i);
68
69         free(i->who);
70         free(i->why);
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         _cleanup_free_ char *temp_path = NULL;
82         _cleanup_fclose_ FILE *f = NULL;
83         int r;
84
85         assert(i);
86
87         r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0);
88         if (r < 0)
89                 goto fail;
90
91         r = fopen_temporary(i->state_file, &f, &temp_path);
92         if (r < 0)
93                 goto fail;
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="UID_FMT"\n"
102                 "PID="PID_FMT"\n",
103                 inhibit_what_to_string(i->what),
104                 inhibit_mode_to_string(i->mode),
105                 i->uid,
106                 i->pid);
107
108         if (i->who) {
109                 _cleanup_free_ char *cc = NULL;
110
111                 cc = cescape(i->who);
112                 if (!cc) {
113                         r = -ENOMEM;
114                         goto fail;
115                 }
116
117                 fprintf(f, "WHO=%s\n", cc);
118         }
119
120         if (i->why) {
121                 _cleanup_free_ char *cc = NULL;
122
123                 cc = cescape(i->why);
124                 if (!cc) {
125                         r = -ENOMEM;
126                         goto fail;
127                 }
128
129                 fprintf(f, "WHY=%s\n", cc);
130         }
131
132         if (i->fifo_path)
133                 fprintf(f, "FIFO=%s\n", i->fifo_path);
134
135         r = fflush_and_check(f);
136         if (r < 0)
137                 goto fail;
138
139         if (rename(temp_path, i->state_file) < 0) {
140                 r = -errno;
141                 goto fail;
142         }
143
144         return 0;
145
146 fail:
147         (void) unlink(i->state_file);
148
149         if (temp_path)
150                 (void) unlink(temp_path);
151
152         return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file);
153 }
154
155 int inhibitor_start(Inhibitor *i) {
156         assert(i);
157
158         if (i->started)
159                 return 0;
160
161         dual_timestamp_get(&i->since);
162
163         log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.",
164                   strna(i->who), strna(i->why),
165                   i->pid, i->uid,
166                   inhibit_mode_to_string(i->mode));
167
168         inhibitor_save(i);
169
170         i->started = true;
171
172         manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
173
174         return 0;
175 }
176
177 int inhibitor_stop(Inhibitor *i) {
178         assert(i);
179
180         if (i->started)
181                 log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.",
182                           strna(i->who), strna(i->why),
183                           i->pid, i->uid,
184                           inhibit_mode_to_string(i->mode));
185
186         if (i->state_file)
187                 unlink(i->state_file);
188
189         i->started = false;
190
191         manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL);
192
193         return 0;
194 }
195
196 int inhibitor_load(Inhibitor *i) {
197
198         _cleanup_free_ char
199                 *what = NULL,
200                 *uid = NULL,
201                 *pid = NULL,
202                 *who = NULL,
203                 *why = NULL,
204                 *mode = NULL;
205
206         InhibitWhat w;
207         InhibitMode mm;
208         char *cc;
209         int r;
210
211         r = parse_env_file(i->state_file, NEWLINE,
212                            "WHAT", &what,
213                            "UID", &uid,
214                            "PID", &pid,
215                            "WHO", &who,
216                            "WHY", &why,
217                            "MODE", &mode,
218                            "FIFO", &i->fifo_path,
219                            NULL);
220         if (r < 0)
221                 return r;
222
223         w = what ? inhibit_what_from_string(what) : 0;
224         if (w >= 0)
225                 i->what = w;
226
227         mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK;
228         if  (mm >= 0)
229                 i->mode = mm;
230
231         if (uid) {
232                 r = parse_uid(uid, &i->uid);
233                 if (r < 0)
234                         return r;
235         }
236
237         if (pid) {
238                 r = parse_pid(pid, &i->pid);
239                 if (r < 0)
240                         return r;
241         }
242
243         if (who) {
244                 r = cunescape(who, 0, &cc);
245                 if (r < 0)
246                         return r;
247
248                 free(i->who);
249                 i->who = cc;
250         }
251
252         if (why) {
253                 r = cunescape(why, 0, &cc);
254                 if (r < 0)
255                         return r;
256
257                 free(i->why);
258                 i->why = cc;
259         }
260
261         if (i->fifo_path) {
262                 int fd;
263
264                 fd = inhibitor_create_fifo(i);
265                 safe_close(fd);
266         }
267
268         return 0;
269 }
270
271 static int inhibitor_dispatch_fifo(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
272         Inhibitor *i = userdata;
273
274         assert(s);
275         assert(fd == i->fifo_fd);
276         assert(i);
277
278         inhibitor_stop(i);
279         inhibitor_free(i);
280
281         return 0;
282 }
283
284 int inhibitor_create_fifo(Inhibitor *i) {
285         int r;
286
287         assert(i);
288
289         /* Create FIFO */
290         if (!i->fifo_path) {
291                 r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0);
292                 if (r < 0)
293                         return r;
294
295                 i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref", NULL);
296                 if (!i->fifo_path)
297                         return -ENOMEM;
298
299                 if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST)
300                         return -errno;
301         }
302
303         /* Open reading side */
304         if (i->fifo_fd < 0) {
305                 i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
306                 if (i->fifo_fd < 0)
307                         return -errno;
308         }
309
310         if (!i->event_source) {
311                 r = sd_event_add_io(i->manager->event, &i->event_source, i->fifo_fd, 0, inhibitor_dispatch_fifo, i);
312                 if (r < 0)
313                         return r;
314
315                 r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE);
316                 if (r < 0)
317                         return r;
318         }
319
320         /* Open writing side */
321         r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
322         if (r < 0)
323                 return -errno;
324
325         return r;
326 }
327
328 void inhibitor_remove_fifo(Inhibitor *i) {
329         assert(i);
330
331         i->event_source = sd_event_source_unref(i->event_source);
332         i->fifo_fd = safe_close(i->fifo_fd);
333
334         if (i->fifo_path) {
335                 unlink(i->fifo_path);
336                 i->fifo_path = mfree(i->fifo_path);
337         }
338 }
339
340 InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
341         Inhibitor *i;
342         Iterator j;
343         InhibitWhat what = 0;
344
345         assert(m);
346
347         HASHMAP_FOREACH(i, m->inhibitors, j)
348                 if (i->mode == mm)
349                         what |= i->what;
350
351         return what;
352 }
353
354 static int pid_is_active(Manager *m, pid_t pid) {
355         Session *s;
356         int r;
357
358         r = manager_get_session_by_pid(m, pid, &s);
359         if (r < 0)
360                 return r;
361
362         /* If there's no session assigned to it, then it's globally
363          * active on all ttys */
364         if (r == 0)
365                 return 1;
366
367         return session_is_active(s);
368 }
369
370 bool manager_is_inhibited(
371                 Manager *m,
372                 InhibitWhat w,
373                 InhibitMode mm,
374                 dual_timestamp *since,
375                 bool ignore_inactive,
376                 bool ignore_uid,
377                 uid_t uid,
378                 Inhibitor **offending) {
379
380         Inhibitor *i;
381         Iterator j;
382         struct dual_timestamp ts = DUAL_TIMESTAMP_NULL;
383         bool inhibited = false;
384
385         assert(m);
386         assert(w > 0 && w < _INHIBIT_WHAT_MAX);
387
388         HASHMAP_FOREACH(i, m->inhibitors, j) {
389                 if (!(i->what & w))
390                         continue;
391
392                 if (i->mode != mm)
393                         continue;
394
395                 if (ignore_inactive && pid_is_active(m, i->pid) <= 0)
396                         continue;
397
398                 if (ignore_uid && i->uid == uid)
399                         continue;
400
401                 if (!inhibited ||
402                     i->since.monotonic < ts.monotonic)
403                         ts = i->since;
404
405                 inhibited = true;
406
407                 if (offending)
408                         *offending = i;
409         }
410
411         if (since)
412                 *since = ts;
413
414         return inhibited;
415 }
416
417 const char *inhibit_what_to_string(InhibitWhat w) {
418         static thread_local char buffer[97];
419         char *p;
420
421         if (w < 0 || w >= _INHIBIT_WHAT_MAX)
422                 return NULL;
423
424         p = buffer;
425         if (w & INHIBIT_SHUTDOWN)
426                 p = stpcpy(p, "shutdown:");
427         if (w & INHIBIT_SLEEP)
428                 p = stpcpy(p, "sleep:");
429         if (w & INHIBIT_IDLE)
430                 p = stpcpy(p, "idle:");
431         if (w & INHIBIT_HANDLE_POWER_KEY)
432                 p = stpcpy(p, "handle-power-key:");
433         if (w & INHIBIT_HANDLE_SUSPEND_KEY)
434                 p = stpcpy(p, "handle-suspend-key:");
435         if (w & INHIBIT_HANDLE_HIBERNATE_KEY)
436                 p = stpcpy(p, "handle-hibernate-key:");
437         if (w & INHIBIT_HANDLE_LID_SWITCH)
438                 p = stpcpy(p, "handle-lid-switch:");
439
440         if (p > buffer)
441                 *(p-1) = 0;
442         else
443                 *p = 0;
444
445         return buffer;
446 }
447
448 InhibitWhat inhibit_what_from_string(const char *s) {
449         InhibitWhat what = 0;
450         const char *word, *state;
451         size_t l;
452
453         FOREACH_WORD_SEPARATOR(word, l, s, ":", state) {
454                 if (l == 8 && strneq(word, "shutdown", l))
455                         what |= INHIBIT_SHUTDOWN;
456                 else if (l == 5 && strneq(word, "sleep", l))
457                         what |= INHIBIT_SLEEP;
458                 else if (l == 4 && strneq(word, "idle", l))
459                         what |= INHIBIT_IDLE;
460                 else if (l == 16 && strneq(word, "handle-power-key", l))
461                         what |= INHIBIT_HANDLE_POWER_KEY;
462                 else if (l == 18 && strneq(word, "handle-suspend-key", l))
463                         what |= INHIBIT_HANDLE_SUSPEND_KEY;
464                 else if (l == 20 && strneq(word, "handle-hibernate-key", l))
465                         what |= INHIBIT_HANDLE_HIBERNATE_KEY;
466                 else if (l == 17 && strneq(word, "handle-lid-switch", l))
467                         what |= INHIBIT_HANDLE_LID_SWITCH;
468                 else
469                         return _INHIBIT_WHAT_INVALID;
470         }
471
472         return what;
473 }
474
475 static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
476         [INHIBIT_BLOCK] = "block",
477         [INHIBIT_DELAY] = "delay"
478 };
479
480 DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);