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