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