]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/tmpfiles/tmpfiles.c
util: make time formatting a bit smarter
[thirdparty/systemd.git] / src / tmpfiles / tmpfiles.c
CommitLineData
5008d581
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
3b63d2d3 6 Copyright 2010 Lennart Poettering, Kay Sievers
5008d581
LP
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
5008d581
LP
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
5430f7f2 16 Lesser General Public License for more details.
5008d581 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
5008d581
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <unistd.h>
23#include <fcntl.h>
24#include <errno.h>
25#include <string.h>
26#include <sys/stat.h>
27#include <limits.h>
28#include <dirent.h>
29#include <grp.h>
30#include <pwd.h>
3b63d2d3
LP
31#include <stdio.h>
32#include <stdlib.h>
33#include <stddef.h>
34#include <getopt.h>
35#include <stdbool.h>
36#include <time.h>
37#include <sys/types.h>
38#include <sys/param.h>
b8bb3e8f
LP
39#include <glob.h>
40#include <fnmatch.h>
cb7ed9df 41#include <sys/capability.h>
5008d581
LP
42
43#include "log.h"
44#include "util.h"
54693d9b 45#include "macro.h"
d39efe74 46#include "missing.h"
49e942b2 47#include "mkdir.h"
9eb977db 48#include "path-util.h"
5008d581
LP
49#include "strv.h"
50#include "label.h"
3b63d2d3 51#include "set.h"
2c21044f 52#include "conf-files.h"
cb7ed9df 53#include "capability.h"
5008d581 54
01000479 55/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
5008d581 56 * them in the file system. This is intended to be used to create
db019b8d
KS
57 * properly owned directories beneath /tmp, /var/tmp, /run, which are
58 * volatile and hence need to be recreated on bootup. */
5008d581 59
66ccd038 60typedef enum ItemType {
b8bb3e8f 61 /* These ones take file names */
3b63d2d3
LP
62 CREATE_FILE = 'f',
63 TRUNCATE_FILE = 'F',
31ed59c5 64 WRITE_FILE = 'w',
3b63d2d3
LP
65 CREATE_DIRECTORY = 'd',
66 TRUNCATE_DIRECTORY = 'D',
ee17ee7c 67 CREATE_FIFO = 'p',
468d726b
LP
68 CREATE_SYMLINK = 'L',
69 CREATE_CHAR_DEVICE = 'c',
70 CREATE_BLOCK_DEVICE = 'b',
b8bb3e8f
LP
71
72 /* These ones take globs */
3b63d2d3 73 IGNORE_PATH = 'x',
78a92a5a 74 IGNORE_DIRECTORY_PATH = 'X',
3b63d2d3 75 REMOVE_PATH = 'r',
a8d88783 76 RECURSIVE_REMOVE_PATH = 'R',
777b87e7 77 RELABEL_PATH = 'z',
a8d88783 78 RECURSIVE_RELABEL_PATH = 'Z'
66ccd038 79} ItemType;
3b63d2d3
LP
80
81typedef struct Item {
66ccd038 82 ItemType type;
3b63d2d3
LP
83
84 char *path;
468d726b 85 char *argument;
5008d581
LP
86 uid_t uid;
87 gid_t gid;
3b63d2d3
LP
88 mode_t mode;
89 usec_t age;
90
468d726b
LP
91 dev_t major_minor;
92
3b63d2d3
LP
93 bool uid_set:1;
94 bool gid_set:1;
95 bool mode_set:1;
96 bool age_set:1;
24f3a374
LP
97
98 bool keep_first_level:1;
3b63d2d3
LP
99} Item;
100
b8bb3e8f 101static Hashmap *items = NULL, *globs = NULL;
17b90525 102static Set *unix_sockets = NULL;
3b63d2d3
LP
103
104static bool arg_create = false;
105static bool arg_clean = false;
106static bool arg_remove = false;
107
fba6e687
LP
108static const char *arg_prefix = NULL;
109
fabe5c0e
LP
110static const char conf_file_dirs[] =
111 "/etc/tmpfiles.d\0"
112 "/run/tmpfiles.d\0"
113 "/usr/local/lib/tmpfiles.d\0"
114 "/usr/lib/tmpfiles.d\0"
3f2afb29 115#ifdef HAVE_SPLIT_USR
fabe5c0e 116 "/lib/tmpfiles.d\0"
3f2afb29 117#endif
fabe5c0e 118 ;
9125670f 119
3b63d2d3
LP
120#define MAX_DEPTH 256
121
66ccd038 122static bool needs_glob(ItemType t) {
78a92a5a 123 return t == IGNORE_PATH || t == IGNORE_DIRECTORY_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH;
b8bb3e8f
LP
124}
125
126static struct Item* find_glob(Hashmap *h, const char *match) {
127 Item *j;
128 Iterator i;
129
130 HASHMAP_FOREACH(j, h, i)
131 if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
132 return j;
133
134 return NULL;
135}
136
17b90525 137static void load_unix_sockets(void) {
19fbec19 138 FILE _cleanup_fclose_ *f = NULL;
17b90525
LP
139 char line[LINE_MAX];
140
141 if (unix_sockets)
142 return;
143
144 /* We maintain a cache of the sockets we found in
145 * /proc/net/unix to speed things up a little. */
146
fdcad0c2
LP
147 unix_sockets = set_new(string_hash_func, string_compare_func);
148 if (!unix_sockets)
17b90525
LP
149 return;
150
fdcad0c2
LP
151 f = fopen("/proc/net/unix", "re");
152 if (!f)
17b90525
LP
153 return;
154
fdcad0c2
LP
155 /* Skip header */
156 if (!fgets(line, sizeof(line), f))
17b90525
LP
157 goto fail;
158
159 for (;;) {
160 char *p, *s;
161 int k;
162
fdcad0c2 163 if (!fgets(line, sizeof(line), f))
17b90525
LP
164 break;
165
166 truncate_nl(line);
167
fdcad0c2
LP
168 p = strchr(line, ':');
169 if (!p)
170 continue;
171
172 if (strlen(p) < 37)
17b90525
LP
173 continue;
174
fdcad0c2 175 p += 37;
17b90525 176 p += strspn(p, WHITESPACE);
fdcad0c2 177 p += strcspn(p, WHITESPACE); /* skip one more word */
17b90525
LP
178 p += strspn(p, WHITESPACE);
179
180 if (*p != '/')
181 continue;
182
fdcad0c2
LP
183 s = strdup(p);
184 if (!s)
17b90525
LP
185 goto fail;
186
4ff21d85
LP
187 path_kill_slashes(s);
188
fdcad0c2
LP
189 k = set_put(unix_sockets, s);
190 if (k < 0) {
17b90525
LP
191 free(s);
192
193 if (k != -EEXIST)
194 goto fail;
195 }
196 }
197
198 return;
199
200fail:
201 set_free_free(unix_sockets);
202 unix_sockets = NULL;
17b90525
LP
203}
204
205static bool unix_socket_alive(const char *fn) {
206 assert(fn);
207
208 load_unix_sockets();
209
210 if (unix_sockets)
211 return !!set_get(unix_sockets, (char*) fn);
212
213 /* We don't know, so assume yes */
214 return true;
215}
216
99d680ac
KS
217static int dir_is_mount_point(DIR *d, const char *subdir) {
218 struct file_handle *h;
219 int mount_id_parent, mount_id;
220 int r_p, r;
221
222 h = alloca(MAX_HANDLE_SZ);
223
224 h->handle_bytes = MAX_HANDLE_SZ;
225 r_p = name_to_handle_at(dirfd(d), ".", h, &mount_id_parent, 0);
226 if (r_p < 0)
227 r_p = -errno;
228
229 h->handle_bytes = MAX_HANDLE_SZ;
230 r = name_to_handle_at(dirfd(d), subdir, h, &mount_id, 0);
231 if (r < 0)
232 r = -errno;
233
234 /* got no handle; make no assumptions, return error */
235 if (r_p < 0 && r < 0)
236 return r_p;
237
238 /* got both handles; if they differ, it is a mount point */
239 if (r_p >= 0 && r >= 0)
240 return mount_id_parent != mount_id;
241
242 /* got only one handle; assume different mount points if one
243 * of both queries was not supported by the filesystem */
244 if (r_p == -ENOSYS || r_p == -ENOTSUP || r == -ENOSYS || r == -ENOTSUP)
245 return true;
246
247 /* return error */
248 if (r_p < 0)
249 return r_p;
250 return r;
251}
252
3b63d2d3 253static int dir_cleanup(
78a92a5a 254 Item *i,
3b63d2d3
LP
255 const char *p,
256 DIR *d,
257 const struct stat *ds,
258 usec_t cutoff,
259 dev_t rootdev,
260 bool mountpoint,
24f3a374
LP
261 int maxdepth,
262 bool keep_this_level)
3b63d2d3
LP
263{
264 struct dirent *dent;
265 struct timespec times[2];
266 bool deleted = false;
3b63d2d3
LP
267 int r = 0;
268
269 while ((dent = readdir(d))) {
270 struct stat s;
271 usec_t age;
19fbec19 272 char _cleanup_free_ *sub_path = NULL;
3b63d2d3
LP
273
274 if (streq(dent->d_name, ".") ||
275 streq(dent->d_name, ".."))
276 continue;
5008d581 277
3b63d2d3 278 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
5008d581 279
3b63d2d3
LP
280 if (errno != ENOENT) {
281 log_error("stat(%s/%s) failed: %m", p, dent->d_name);
282 r = -errno;
283 }
284
285 continue;
286 }
287
288 /* Stay on the same filesystem */
289 if (s.st_dev != rootdev)
290 continue;
291
99d680ac
KS
292 /* Try to detect bind mounts of the same filesystem instance; they
293 * do not differ in device major/minors. This type of query is not
294 * supported on all kernels or filesystem types though. */
295 if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0)
296 continue;
297
3b63d2d3
LP
298 /* Do not delete read-only files owned by root */
299 if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
300 continue;
301
3b63d2d3 302 if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
0d0f0c50 303 r = log_oom();
3b63d2d3
LP
304 goto finish;
305 }
306
307 /* Is there an item configured for this path? */
308 if (hashmap_get(items, sub_path))
309 continue;
310
b8bb3e8f
LP
311 if (find_glob(globs, sub_path))
312 continue;
313
3b63d2d3
LP
314 if (S_ISDIR(s.st_mode)) {
315
316 if (mountpoint &&
317 streq(dent->d_name, "lost+found") &&
318 s.st_uid == 0)
319 continue;
320
321 if (maxdepth <= 0)
322 log_warning("Reached max depth on %s.", sub_path);
323 else {
19fbec19 324 DIR _cleanup_closedir_ *sub_dir;
3b63d2d3
LP
325 int q;
326
e5f3d1ba 327 sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
3b63d2d3
LP
328 if (sub_dir == NULL) {
329 if (errno != ENOENT) {
330 log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
331 r = -errno;
332 }
333
334 continue;
335 }
336
78a92a5a 337 q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
3b63d2d3
LP
338
339 if (q < 0)
340 r = q;
341 }
342
24f3a374
LP
343 /* Note: if you are wondering why we don't
344 * support the sticky bit for excluding
345 * directories from cleaning like we do it for
346 * other file system objects: well, the sticky
347 * bit already has a meaning for directories,
348 * so we don't want to overload that. */
349
350 if (keep_this_level)
351 continue;
352
3b63d2d3
LP
353 /* Ignore ctime, we change it when deleting */
354 age = MAX(timespec_load(&s.st_mtim),
355 timespec_load(&s.st_atim));
356 if (age >= cutoff)
357 continue;
358
a6187d4c 359 if (i->type != IGNORE_DIRECTORY_PATH || !streq(dent->d_name, p)) {
78a92a5a 360 log_debug("rmdir '%s'\n", sub_path);
3b63d2d3 361
78a92a5a
MS
362 if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
363 if (errno != ENOENT && errno != ENOTEMPTY) {
364 log_error("rmdir(%s): %m", sub_path);
365 r = -errno;
366 }
3b63d2d3
LP
367 }
368 }
369
370 } else {
9c73736d
LP
371 /* Skip files for which the sticky bit is
372 * set. These are semantics we define, and are
373 * unknown elsewhere. See XDG_RUNTIME_DIR
374 * specification for details. */
375 if (s.st_mode & S_ISVTX)
376 continue;
377
17b90525 378 if (mountpoint && S_ISREG(s.st_mode)) {
3b63d2d3
LP
379 if (streq(dent->d_name, ".journal") &&
380 s.st_uid == 0)
381 continue;
382
383 if (streq(dent->d_name, "aquota.user") ||
384 streq(dent->d_name, "aquota.group"))
385 continue;
386 }
387
17b90525
LP
388 /* Ignore sockets that are listed in /proc/net/unix */
389 if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
390 continue;
391
78ab08eb
LP
392 /* Ignore device nodes */
393 if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
394 continue;
395
24f3a374
LP
396 /* Keep files on this level around if this is
397 * requested */
398 if (keep_this_level)
399 continue;
400
3b63d2d3
LP
401 age = MAX3(timespec_load(&s.st_mtim),
402 timespec_load(&s.st_atim),
403 timespec_load(&s.st_ctim));
404
405 if (age >= cutoff)
406 continue;
407
408 log_debug("unlink '%s'\n", sub_path);
409
410 if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
411 if (errno != ENOENT) {
412 log_error("unlink(%s): %m", sub_path);
413 r = -errno;
414 }
415 }
416
417 deleted = true;
418 }
419 }
420
421finish:
422 if (deleted) {
423 /* Restore original directory timestamps */
424 times[0] = ds->st_atim;
425 times[1] = ds->st_mtim;
426
427 if (futimens(dirfd(d), times) < 0)
428 log_error("utimensat(%s): %m", p);
429 }
430
3b63d2d3
LP
431 return r;
432}
433
062e01bb
MS
434static int item_set_perms(Item *i, const char *path) {
435 /* not using i->path directly because it may be a glob */
436 if (i->mode_set)
437 if (chmod(path, i->mode) < 0) {
438 log_error("chmod(%s) failed: %m", path);
439 return -errno;
440 }
441
442 if (i->uid_set || i->gid_set)
443 if (chown(path,
444 i->uid_set ? i->uid : (uid_t) -1,
445 i->gid_set ? i->gid : (gid_t) -1) < 0) {
446
447 log_error("chown(%s) failed: %m", path);
448 return -errno;
449 }
450
c9bc0764 451 return label_fix(path, false, false);
062e01bb
MS
452}
453
d4e9eb91
DR
454static int write_one_file(Item *i, const char *path) {
455 int r, e, fd, flags;
456 struct stat st;
457 mode_t u;
458
459 flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
460 i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
461
462 u = umask(0);
463 label_context_set(path, S_IFREG);
464 fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
465 e = errno;
466 label_context_clear();
467 umask(u);
468 errno = e;
469
470 if (fd < 0) {
471 if (i->type == WRITE_FILE && errno == ENOENT)
472 return 0;
473
474 log_error("Failed to create file %s: %m", path);
475 return -errno;
476 }
477
478 if (i->argument) {
479 ssize_t n;
480 size_t l;
54693d9b 481 _cleanup_free_ char *unescaped;
d4e9eb91 482
54693d9b 483 unescaped = cunescape(i->argument);
3785ba69
TJ
484 if (unescaped == NULL) {
485 close_nointr_nofail(fd);
54693d9b 486 return log_oom();
3785ba69 487 }
d4e9eb91 488
54693d9b
DR
489 l = strlen(unescaped);
490 n = write(fd, unescaped, l);
d4e9eb91 491
d4e9eb91
DR
492 if (n < 0 || (size_t) n < l) {
493 log_error("Failed to write file %s: %s", path, n < 0 ? strerror(-n) : "Short write");
494 close_nointr_nofail(fd);
495 return n < 0 ? n : -EIO;
496 }
497 }
498
3612fbc1
DR
499 close_nointr_nofail(fd);
500
d4e9eb91
DR
501 if (stat(path, &st) < 0) {
502 log_error("stat(%s) failed: %m", path);
503 return -errno;
504 }
505
506 if (!S_ISREG(st.st_mode)) {
507 log_error("%s is not a file.", path);
508 return -EEXIST;
509 }
510
511 r = item_set_perms(i, path);
512 if (r < 0)
513 return r;
514
515 return 0;
516}
517
062e01bb 518static int recursive_relabel_children(Item *i, const char *path) {
19fbec19 519 DIR _cleanup_closedir_ *d;
a8d88783
MS
520 int ret = 0;
521
522 /* This returns the first error we run into, but nevertheless
523 * tries to go on */
524
525 d = opendir(path);
526 if (!d)
527 return errno == ENOENT ? 0 : -errno;
528
529 for (;;) {
7d5e9c0f
LP
530 struct dirent *de;
531 union dirent_storage buf;
a8d88783
MS
532 bool is_dir;
533 int r;
19fbec19 534 char _cleanup_free_ *entry_path = NULL;
a8d88783 535
7d5e9c0f 536 r = readdir_r(d, &buf.de, &de);
a8d88783
MS
537 if (r != 0) {
538 if (ret == 0)
539 ret = -r;
540 break;
541 }
542
543 if (!de)
544 break;
545
546 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
547 continue;
548
549 if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) {
550 if (ret == 0)
551 ret = -ENOMEM;
552 continue;
553 }
554
555 if (de->d_type == DT_UNKNOWN) {
556 struct stat st;
557
558 if (lstat(entry_path, &st) < 0) {
559 if (ret == 0 && errno != ENOENT)
560 ret = -errno;
a8d88783
MS
561 continue;
562 }
563
564 is_dir = S_ISDIR(st.st_mode);
565
566 } else
567 is_dir = de->d_type == DT_DIR;
568
062e01bb 569 r = item_set_perms(i, entry_path);
a8d88783
MS
570 if (r < 0) {
571 if (ret == 0 && r != -ENOENT)
572 ret = r;
a8d88783
MS
573 continue;
574 }
575
576 if (is_dir) {
062e01bb 577 r = recursive_relabel_children(i, entry_path);
a8d88783
MS
578 if (r < 0 && ret == 0)
579 ret = r;
580 }
a8d88783
MS
581 }
582
a8d88783
MS
583 return ret;
584}
585
586static int recursive_relabel(Item *i, const char *path) {
587 int r;
588 struct stat st;
589
062e01bb 590 r = item_set_perms(i, path);
a8d88783
MS
591 if (r < 0)
592 return r;
593
594 if (lstat(path, &st) < 0)
595 return -errno;
596
597 if (S_ISDIR(st.st_mode))
062e01bb 598 r = recursive_relabel_children(i, path);
a8d88783
MS
599
600 return r;
601}
602
99e68c0b
MS
603static int glob_item(Item *i, int (*action)(Item *, const char *)) {
604 int r = 0, k;
605 glob_t g;
606 char **fn;
607
608 zero(g);
609
610 errno = 0;
611 if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) {
612
613 if (k != GLOB_NOMATCH) {
8333c77e 614 if (errno > 0)
99e68c0b
MS
615 errno = EIO;
616
617 log_error("glob(%s) failed: %m", i->path);
618 return -errno;
619 }
620 }
621
622 STRV_FOREACH(fn, g.gl_pathv)
623 if ((k = action(i, *fn)) < 0)
624 r = k;
625
626 globfree(&g);
627 return r;
628}
629
3b63d2d3 630static int create_item(Item *i) {
e9a5ef7c 631 int r, e;
3b63d2d3
LP
632 mode_t u;
633 struct stat st;
5008d581 634
3b63d2d3 635 assert(i);
5008d581 636
3b63d2d3
LP
637 switch (i->type) {
638
639 case IGNORE_PATH:
78a92a5a 640 case IGNORE_DIRECTORY_PATH:
3b63d2d3
LP
641 case REMOVE_PATH:
642 case RECURSIVE_REMOVE_PATH:
643 return 0;
5008d581 644
3b63d2d3 645 case CREATE_FILE:
31ed59c5 646 case TRUNCATE_FILE:
1845fdd9
DR
647 r = write_one_file(i, i->path);
648 if (r < 0)
649 return r;
650 break;
d4e9eb91
DR
651 case WRITE_FILE:
652 r = glob_item(i, write_one_file);
f05bc3f7
MS
653 if (r < 0)
654 return r;
5008d581 655
3b63d2d3
LP
656 break;
657
658 case TRUNCATE_DIRECTORY:
659 case CREATE_DIRECTORY:
5008d581
LP
660
661 u = umask(0);
d2e54fae 662 mkdir_parents_label(i->path, 0755);
3b63d2d3 663 r = mkdir(i->path, i->mode);
5008d581
LP
664 umask(u);
665
666 if (r < 0 && errno != EEXIST) {
3b63d2d3 667 log_error("Failed to create directory %s: %m", i->path);
f05bc3f7 668 return -errno;
5008d581
LP
669 }
670
3b63d2d3
LP
671 if (stat(i->path, &st) < 0) {
672 log_error("stat(%s) failed: %m", i->path);
f05bc3f7 673 return -errno;
5008d581
LP
674 }
675
676 if (!S_ISDIR(st.st_mode)) {
3b63d2d3 677 log_error("%s is not a directory.", i->path);
f05bc3f7 678 return -EEXIST;
5008d581
LP
679 }
680
062e01bb 681 r = item_set_perms(i, i->path);
f05bc3f7
MS
682 if (r < 0)
683 return r;
3b63d2d3
LP
684
685 break;
ee17ee7c
LP
686
687 case CREATE_FIFO:
688
689 u = umask(0);
690 r = mkfifo(i->path, i->mode);
691 umask(u);
692
693 if (r < 0 && errno != EEXIST) {
694 log_error("Failed to create fifo %s: %m", i->path);
f05bc3f7 695 return -errno;
ee17ee7c
LP
696 }
697
698 if (stat(i->path, &st) < 0) {
699 log_error("stat(%s) failed: %m", i->path);
f05bc3f7 700 return -errno;
ee17ee7c
LP
701 }
702
703 if (!S_ISFIFO(st.st_mode)) {
704 log_error("%s is not a fifo.", i->path);
f05bc3f7 705 return -EEXIST;
ee17ee7c
LP
706 }
707
062e01bb 708 r = item_set_perms(i, i->path);
f05bc3f7
MS
709 if (r < 0)
710 return r;
ee17ee7c
LP
711
712 break;
a8d88783 713
468d726b
LP
714 case CREATE_SYMLINK: {
715 char *x;
716
e9a5ef7c 717 label_context_set(i->path, S_IFLNK);
468d726b 718 r = symlink(i->argument, i->path);
e9a5ef7c
KS
719 e = errno;
720 label_context_clear();
721 errno = e;
722
468d726b
LP
723 if (r < 0 && errno != EEXIST) {
724 log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
725 return -errno;
726 }
727
728 r = readlink_malloc(i->path, &x);
729 if (r < 0) {
730 log_error("readlink(%s) failed: %s", i->path, strerror(-r));
731 return -errno;
732 }
733
734 if (!streq(i->argument, x)) {
735 free(x);
736 log_error("%s is not the right symlinks.", i->path);
737 return -EEXIST;
738 }
739
740 free(x);
741 break;
742 }
743
744 case CREATE_BLOCK_DEVICE:
745 case CREATE_CHAR_DEVICE: {
cb7ed9df
LP
746 mode_t file_type;
747
748 if (have_effective_cap(CAP_MKNOD) == 0) {
749 /* In a container we lack CAP_MKNOD. We
750 shouldnt attempt to create the device node in
751 that case to avoid noise, and we don't support
752 virtualized devices in containers anyway. */
753
754 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
755 return 0;
756 }
757
758 file_type = (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
468d726b
LP
759
760 u = umask(0);
e7aee759
MS
761 label_context_set(i->path, file_type);
762 r = mknod(i->path, i->mode | file_type, i->major_minor);
e9a5ef7c
KS
763 e = errno;
764 label_context_clear();
468d726b 765 umask(u);
e9a5ef7c 766 errno = e;
468d726b
LP
767
768 if (r < 0 && errno != EEXIST) {
769 log_error("Failed to create device node %s: %m", i->path);
770 return -errno;
771 }
772
773 if (stat(i->path, &st) < 0) {
774 log_error("stat(%s) failed: %m", i->path);
775 return -errno;
776 }
777
e7aee759 778 if ((st.st_mode & S_IFMT) != file_type) {
468d726b
LP
779 log_error("%s is not a device node.", i->path);
780 return -EEXIST;
781 }
782
783 r = item_set_perms(i, i->path);
784 if (r < 0)
785 return r;
786
787 break;
788 }
789
777b87e7
MS
790 case RELABEL_PATH:
791
792 r = glob_item(i, item_set_perms);
793 if (r < 0)
794 return 0;
795 break;
796
a8d88783
MS
797 case RECURSIVE_RELABEL_PATH:
798
799 r = glob_item(i, recursive_relabel);
800 if (r < 0)
801 return r;
3b63d2d3
LP
802 }
803
3b63d2d3
LP
804 log_debug("%s created successfully.", i->path);
805
f05bc3f7 806 return 0;
3b63d2d3
LP
807}
808
a0896123 809static int remove_item_instance(Item *i, const char *instance) {
3b63d2d3
LP
810 int r;
811
812 assert(i);
813
814 switch (i->type) {
815
816 case CREATE_FILE:
817 case TRUNCATE_FILE:
818 case CREATE_DIRECTORY:
ee17ee7c 819 case CREATE_FIFO:
468d726b
LP
820 case CREATE_SYMLINK:
821 case CREATE_BLOCK_DEVICE:
822 case CREATE_CHAR_DEVICE:
3b63d2d3 823 case IGNORE_PATH:
78a92a5a 824 case IGNORE_DIRECTORY_PATH:
777b87e7 825 case RELABEL_PATH:
a8d88783 826 case RECURSIVE_RELABEL_PATH:
31ed59c5 827 case WRITE_FILE:
3b63d2d3
LP
828 break;
829
830 case REMOVE_PATH:
b8bb3e8f
LP
831 if (remove(instance) < 0 && errno != ENOENT) {
832 log_error("remove(%s): %m", instance);
3b63d2d3 833 return -errno;
5008d581 834 }
3b63d2d3
LP
835
836 break;
837
838 case TRUNCATE_DIRECTORY:
839 case RECURSIVE_REMOVE_PATH:
d139b24a
LP
840 /* FIXME: we probably should use dir_cleanup() here
841 * instead of rm_rf() so that 'x' is honoured. */
f56d5db9 842 r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
468d726b 843 if (r < 0 && r != -ENOENT) {
b8bb3e8f 844 log_error("rm_rf(%s): %s", instance, strerror(-r));
3b63d2d3
LP
845 return r;
846 }
847
848 break;
849 }
850
851 return 0;
852}
853
a0896123 854static int remove_item(Item *i) {
99e68c0b
MS
855 int r = 0;
856
b8bb3e8f
LP
857 assert(i);
858
859 switch (i->type) {
860
861 case CREATE_FILE:
862 case TRUNCATE_FILE:
863 case CREATE_DIRECTORY:
ee17ee7c 864 case CREATE_FIFO:
468d726b
LP
865 case CREATE_SYMLINK:
866 case CREATE_CHAR_DEVICE:
867 case CREATE_BLOCK_DEVICE:
b8bb3e8f 868 case IGNORE_PATH:
78a92a5a 869 case IGNORE_DIRECTORY_PATH:
777b87e7 870 case RELABEL_PATH:
a8d88783 871 case RECURSIVE_RELABEL_PATH:
31ed59c5 872 case WRITE_FILE:
b8bb3e8f
LP
873 break;
874
875 case REMOVE_PATH:
876 case TRUNCATE_DIRECTORY:
99e68c0b
MS
877 case RECURSIVE_REMOVE_PATH:
878 r = glob_item(i, remove_item_instance);
879 break;
b8bb3e8f
LP
880 }
881
99e68c0b 882 return r;
b8bb3e8f
LP
883}
884
78a92a5a 885static int clean_item_instance(Item *i, const char* instance) {
19fbec19 886 DIR _cleanup_closedir_ *d = NULL;
78a92a5a
MS
887 struct stat s, ps;
888 bool mountpoint;
889 int r;
890 usec_t cutoff, n;
891
892 assert(i);
893
894 if (!i->age_set)
895 return 0;
896
897 n = now(CLOCK_REALTIME);
898 if (n < i->age)
899 return 0;
900
901 cutoff = n - i->age;
902
903 d = opendir(instance);
904 if (!d) {
905 if (errno == ENOENT || errno == ENOTDIR)
906 return 0;
907
908 log_error("Failed to open directory %s: %m", i->path);
909 return -errno;
910 }
911
912 if (fstat(dirfd(d), &s) < 0) {
913 log_error("stat(%s) failed: %m", i->path);
19fbec19 914 return -errno;
78a92a5a
MS
915 }
916
917 if (!S_ISDIR(s.st_mode)) {
918 log_error("%s is not a directory.", i->path);
19fbec19 919 return -ENOTDIR;
78a92a5a
MS
920 }
921
922 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
923 log_error("stat(%s/..) failed: %m", i->path);
19fbec19 924 return -errno;
78a92a5a
MS
925 }
926
927 mountpoint = s.st_dev != ps.st_dev ||
928 (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
929
19fbec19
ZJS
930 r = dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
931 MAX_DEPTH, i->keep_first_level);
78a92a5a
MS
932 return r;
933}
934
935static int clean_item(Item *i) {
936 int r = 0;
937
938 assert(i);
939
940 switch (i->type) {
941 case CREATE_DIRECTORY:
942 case TRUNCATE_DIRECTORY:
943 case IGNORE_PATH:
944 clean_item_instance(i, i->path);
945 break;
946 case IGNORE_DIRECTORY_PATH:
947 r = glob_item(i, clean_item_instance);
948 break;
949 default:
950 break;
951 }
952
953 return r;
954}
955
3b63d2d3
LP
956static int process_item(Item *i) {
957 int r, q, p;
958
959 assert(i);
960
961 r = arg_create ? create_item(i) : 0;
a0896123 962 q = arg_remove ? remove_item(i) : 0;
3b63d2d3
LP
963 p = arg_clean ? clean_item(i) : 0;
964
965 if (r < 0)
966 return r;
967
968 if (q < 0)
969 return q;
970
971 return p;
972}
973
974static void item_free(Item *i) {
975 assert(i);
976
977 free(i->path);
468d726b 978 free(i->argument);
3b63d2d3
LP
979 free(i);
980}
981
bfe95f35
LP
982static bool item_equal(Item *a, Item *b) {
983 assert(a);
984 assert(b);
985
986 if (!streq_ptr(a->path, b->path))
987 return false;
988
989 if (a->type != b->type)
990 return false;
991
992 if (a->uid_set != b->uid_set ||
993 (a->uid_set && a->uid != b->uid))
994 return false;
995
996 if (a->gid_set != b->gid_set ||
997 (a->gid_set && a->gid != b->gid))
998 return false;
999
1000 if (a->mode_set != b->mode_set ||
1001 (a->mode_set && a->mode != b->mode))
1002 return false;
1003
1004 if (a->age_set != b->age_set ||
1005 (a->age_set && a->age != b->age))
1006 return false;
1007
31ed59c5
LP
1008 if ((a->type == CREATE_FILE ||
1009 a->type == TRUNCATE_FILE ||
1010 a->type == WRITE_FILE ||
1011 a->type == CREATE_SYMLINK) &&
1733ca54 1012 !streq_ptr(a->argument, b->argument))
468d726b
LP
1013 return false;
1014
1015 if ((a->type == CREATE_CHAR_DEVICE ||
1016 a->type == CREATE_BLOCK_DEVICE) &&
1017 a->major_minor != b->major_minor)
1018 return false;
1019
bfe95f35
LP
1020 return true;
1021}
1022
fba6e687 1023static int parse_line(const char *fname, unsigned line, const char *buffer) {
7f2c1f4d
ZJS
1024 Item _cleanup_free_ *i = NULL;
1025 Item *existing;
19fbec19
ZJS
1026 char _cleanup_free_
1027 *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
66ccd038 1028 char type;
bfe95f35 1029 Hashmap *h;
31ed59c5 1030 int r, n = -1;
3b63d2d3
LP
1031
1032 assert(fname);
1033 assert(line >= 1);
1034 assert(buffer);
1035
468d726b 1036 i = new0(Item, 1);
0d0f0c50
SL
1037 if (!i)
1038 return log_oom();
3b63d2d3 1039
19fbec19
ZJS
1040 r = sscanf(buffer,
1041 "%c %ms %ms %ms %ms %ms %n",
66ccd038 1042 &type,
bd40a2d8
LP
1043 &i->path,
1044 &mode,
1045 &user,
1046 &group,
468d726b 1047 &age,
19fbec19
ZJS
1048 &n);
1049 if (r < 2) {
3b63d2d3 1050 log_error("[%s:%u] Syntax error.", fname, line);
7f2c1f4d 1051 return -EIO;
5008d581
LP
1052 }
1053
31ed59c5
LP
1054 if (n >= 0) {
1055 n += strspn(buffer+n, WHITESPACE);
1056 if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
1057 i->argument = unquote(buffer+n, "\"");
0d0f0c50
SL
1058 if (!i->argument)
1059 return log_oom();
31ed59c5
LP
1060 }
1061 }
1062
777b87e7 1063 switch(type) {
468d726b 1064
777b87e7
MS
1065 case CREATE_FILE:
1066 case TRUNCATE_FILE:
1067 case CREATE_DIRECTORY:
1068 case TRUNCATE_DIRECTORY:
1069 case CREATE_FIFO:
1070 case IGNORE_PATH:
78a92a5a 1071 case IGNORE_DIRECTORY_PATH:
777b87e7
MS
1072 case REMOVE_PATH:
1073 case RECURSIVE_REMOVE_PATH:
1074 case RELABEL_PATH:
1075 case RECURSIVE_RELABEL_PATH:
1076 break;
468d726b
LP
1077
1078 case CREATE_SYMLINK:
1079 if (!i->argument) {
1080 log_error("[%s:%u] Symlink file requires argument.", fname, line);
7f2c1f4d 1081 return -EBADMSG;
468d726b
LP
1082 }
1083 break;
1084
31ed59c5
LP
1085 case WRITE_FILE:
1086 if (!i->argument) {
1087 log_error("[%s:%u] Write file requires argument.", fname, line);
7f2c1f4d 1088 return -EBADMSG;
31ed59c5
LP
1089 }
1090 break;
1091
468d726b
LP
1092 case CREATE_CHAR_DEVICE:
1093 case CREATE_BLOCK_DEVICE: {
1094 unsigned major, minor;
1095
1096 if (!i->argument) {
1097 log_error("[%s:%u] Device file requires argument.", fname, line);
7f2c1f4d 1098 return -EBADMSG;
468d726b
LP
1099 }
1100
1101 if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) {
1102 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument);
7f2c1f4d 1103 return -EBADMSG;
468d726b
LP
1104 }
1105
1106 i->major_minor = makedev(major, minor);
1107 break;
1108 }
1109
777b87e7 1110 default:
a8d88783 1111 log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
7f2c1f4d 1112 return -EBADMSG;
3b63d2d3 1113 }
468d726b 1114
a8d88783 1115 i->type = type;
3b63d2d3
LP
1116
1117 if (!path_is_absolute(i->path)) {
1118 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
7f2c1f4d 1119 return -EBADMSG;
3b63d2d3
LP
1120 }
1121
1122 path_kill_slashes(i->path);
1123
7f2c1f4d
ZJS
1124 if (arg_prefix && !path_startswith(i->path, arg_prefix))
1125 return 0;
5008d581 1126
3b63d2d3 1127 if (user && !streq(user, "-")) {
4b67834e
LP
1128 const char *u = user;
1129
d05c5031 1130 r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
4b67834e 1131 if (r < 0) {
3b63d2d3 1132 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
7f2c1f4d 1133 return r;
3b63d2d3
LP
1134 }
1135
1136 i->uid_set = true;
1137 }
1138
1139 if (group && !streq(group, "-")) {
4b67834e
LP
1140 const char *g = group;
1141
1142 r = get_group_creds(&g, &i->gid);
1143 if (r < 0) {
3b63d2d3 1144 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
7f2c1f4d 1145 return r;
3b63d2d3
LP
1146 }
1147
1148 i->gid_set = true;
1149 }
1150
1151 if (mode && !streq(mode, "-")) {
1152 unsigned m;
1153
1154 if (sscanf(mode, "%o", &m) != 1) {
1155 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
7f2c1f4d 1156 return -ENOENT;
3b63d2d3
LP
1157 }
1158
1159 i->mode = m;
1160 i->mode_set = true;
1161 } else
468d726b
LP
1162 i->mode =
1163 i->type == CREATE_DIRECTORY ||
1164 i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
3b63d2d3
LP
1165
1166 if (age && !streq(age, "-")) {
24f3a374
LP
1167 const char *a = age;
1168
1169 if (*a == '~') {
1170 i->keep_first_level = true;
1171 a++;
1172 }
1173
7f602784 1174 if (parse_sec(a, &i->age) < 0) {
3b63d2d3 1175 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
7f2c1f4d 1176 return -EBADMSG;
3b63d2d3
LP
1177 }
1178
1179 i->age_set = true;
1180 }
1181
bfe95f35
LP
1182 h = needs_glob(i->type) ? globs : items;
1183
468d726b
LP
1184 existing = hashmap_get(h, i->path);
1185 if (existing) {
bfe95f35
LP
1186
1187 /* Two identical items are fine */
1188 if (!item_equal(existing, i))
022707d9 1189 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
022707d9 1190
7f2c1f4d 1191 return 0;
bfe95f35
LP
1192 }
1193
468d726b
LP
1194 r = hashmap_put(h, i->path, i);
1195 if (r < 0) {
3b63d2d3 1196 log_error("Failed to insert item %s: %s", i->path, strerror(-r));
7f2c1f4d 1197 return r;
3b63d2d3
LP
1198 }
1199
7f2c1f4d 1200 i = NULL; /* avoid cleanup */
5008d581 1201
7f2c1f4d 1202 return 0;
5008d581
LP
1203}
1204
3b63d2d3
LP
1205static int help(void) {
1206
522d4a49
LP
1207 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1208 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
3b63d2d3
LP
1209 " -h --help Show this help\n"
1210 " --create Create marked files/directories\n"
1211 " --clean Clean up marked directories\n"
fba6e687 1212 " --remove Remove marked files/directories\n"
522d4a49 1213 " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n",
3b63d2d3
LP
1214 program_invocation_short_name);
1215
1216 return 0;
1217}
1218
1219static int parse_argv(int argc, char *argv[]) {
1220
1221 enum {
1222 ARG_CREATE,
1223 ARG_CLEAN,
fba6e687
LP
1224 ARG_REMOVE,
1225 ARG_PREFIX
3b63d2d3
LP
1226 };
1227
1228 static const struct option options[] = {
1229 { "help", no_argument, NULL, 'h' },
1230 { "create", no_argument, NULL, ARG_CREATE },
1231 { "clean", no_argument, NULL, ARG_CLEAN },
1232 { "remove", no_argument, NULL, ARG_REMOVE },
fba6e687 1233 { "prefix", required_argument, NULL, ARG_PREFIX },
3b63d2d3
LP
1234 { NULL, 0, NULL, 0 }
1235 };
1236
1237 int c;
1238
1239 assert(argc >= 0);
1240 assert(argv);
1241
1242 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1243
1244 switch (c) {
1245
1246 case 'h':
1247 help();
1248 return 0;
1249
1250 case ARG_CREATE:
1251 arg_create = true;
1252 break;
1253
1254 case ARG_CLEAN:
1255 arg_clean = true;
1256 break;
1257
1258 case ARG_REMOVE:
1259 arg_remove = true;
1260 break;
1261
fba6e687
LP
1262 case ARG_PREFIX:
1263 arg_prefix = optarg;
1264 break;
1265
3b63d2d3
LP
1266 case '?':
1267 return -EINVAL;
1268
1269 default:
1270 log_error("Unknown option code %c", c);
1271 return -EINVAL;
1272 }
1273 }
1274
1275 if (!arg_clean && !arg_create && !arg_remove) {
35b8ca3a 1276 log_error("You need to specify at least one of --clean, --create or --remove.");
3b63d2d3
LP
1277 return -EINVAL;
1278 }
1279
1280 return 1;
1281}
1282
fba6e687
LP
1283static int read_config_file(const char *fn, bool ignore_enoent) {
1284 FILE *f;
1285 unsigned v = 0;
fabe5c0e 1286 int r;
78a92a5a
MS
1287 Iterator iterator;
1288 Item *i;
fba6e687
LP
1289
1290 assert(fn);
1291
fabe5c0e
LP
1292 r = search_and_fopen_nulstr(fn, "re", conf_file_dirs, &f);
1293 if (r < 0) {
1294 if (ignore_enoent && r == -ENOENT)
fba6e687
LP
1295 return 0;
1296
fabe5c0e
LP
1297 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1298 return r;
fba6e687
LP
1299 }
1300
772f8371 1301 log_debug("apply: %s\n", fn);
fba6e687
LP
1302 for (;;) {
1303 char line[LINE_MAX], *l;
1304 int k;
1305
1306 if (!(fgets(line, sizeof(line), f)))
1307 break;
1308
1309 v++;
1310
1311 l = strstrip(line);
1312 if (*l == '#' || *l == 0)
1313 continue;
1314
1315 if ((k = parse_line(fn, v, l)) < 0)
1316 if (r == 0)
1317 r = k;
1318 }
1319
78a92a5a
MS
1320 /* we have to determine age parameter for each entry of type X */
1321 HASHMAP_FOREACH(i, globs, iterator) {
1322 Iterator iter;
1323 Item *j, *candidate_item = NULL;
1324
1325 if (i->type != IGNORE_DIRECTORY_PATH)
1326 continue;
1327
1328 HASHMAP_FOREACH(j, items, iter) {
1329 if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY)
1330 continue;
1331
1332 if (path_equal(j->path, i->path)) {
1333 candidate_item = j;
1334 break;
1335 }
1336
1337 if ((!candidate_item && path_startswith(i->path, j->path)) ||
1338 (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
1339 candidate_item = j;
1340 }
1341
1342 if (candidate_item) {
1343 i->age = candidate_item->age;
1344 i->age_set = true;
1345 }
1346 }
1347
fba6e687
LP
1348 if (ferror(f)) {
1349 log_error("Failed to read from file %s: %m", fn);
1350 if (r == 0)
1351 r = -EIO;
1352 }
1353
1354 fclose(f);
1355
1356 return r;
1357}
1358
5008d581 1359int main(int argc, char *argv[]) {
fabe5c0e 1360 int r, k;
3b63d2d3
LP
1361 Item *i;
1362 Iterator iterator;
1363
fdcad0c2
LP
1364 r = parse_argv(argc, argv);
1365 if (r <= 0)
3b63d2d3 1366 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
5008d581 1367
eb0ca9eb 1368 log_set_target(LOG_TARGET_AUTO);
5008d581
LP
1369 log_parse_environment();
1370 log_open();
1371
4c12626c
LP
1372 umask(0022);
1373
e9a5ef7c 1374 label_init(NULL);
5008d581 1375
b8bb3e8f
LP
1376 items = hashmap_new(string_hash_func, string_compare_func);
1377 globs = hashmap_new(string_hash_func, string_compare_func);
1378
1379 if (!items || !globs) {
fabe5c0e 1380 r = log_oom();
3b63d2d3
LP
1381 goto finish;
1382 }
1383
fabe5c0e 1384 r = 0;
5008d581 1385
fba6e687
LP
1386 if (optind < argc) {
1387 int j;
5008d581 1388
9125670f 1389 for (j = optind; j < argc; j++) {
fabe5c0e
LP
1390 k = read_config_file(argv[j], false);
1391 if (k < 0 && r == 0)
1392 r = k;
9125670f 1393 }
5008d581 1394
fba6e687 1395 } else {
fabe5c0e
LP
1396 _cleanup_strv_free_ char **files = NULL;
1397 char **f;
5008d581 1398
fabe5c0e 1399 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
44143309 1400 if (r < 0) {
44143309
KS
1401 log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
1402 goto finish;
1403 }
3b63d2d3 1404
772f8371 1405 STRV_FOREACH(f, files) {
fabe5c0e
LP
1406 k = read_config_file(*f, true);
1407 if (k < 0 && r == 0)
1408 r = k;
5008d581 1409 }
772f8371 1410 }
5008d581 1411
b8bb3e8f 1412 HASHMAP_FOREACH(i, globs, iterator)
21bdae12 1413 process_item(i);
b8bb3e8f 1414
3b63d2d3 1415 HASHMAP_FOREACH(i, items, iterator)
21bdae12 1416 process_item(i);
3b63d2d3 1417
5008d581 1418finish:
3b63d2d3
LP
1419 while ((i = hashmap_steal_first(items)))
1420 item_free(i);
1421
17b90525
LP
1422 while ((i = hashmap_steal_first(globs)))
1423 item_free(i);
1424
3b63d2d3 1425 hashmap_free(items);
b8bb3e8f 1426 hashmap_free(globs);
5008d581 1427
17b90525
LP
1428 set_free_free(unix_sockets);
1429
29003cff
LP
1430 label_finish();
1431
fabe5c0e 1432 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
5008d581 1433}