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