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