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