1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering, Kay Sievers
8 systemd is free software; you can redistribute it and/or modify it
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
11 (at your option) any later version.
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
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
37 #include <sys/types.h>
38 #include <sys/param.h>
41 #include <sys/capability.h>
48 #include "path-util.h"
52 #include "conf-files.h"
53 #include "capability.h"
54 #include "specifier.h"
58 /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
59 * them in the file system. This is intended to be used to create
60 * properly owned directories beneath /tmp, /var/tmp, /run, which are
61 * volatile and hence need to be recreated on bootup. */
63 typedef enum ItemType {
64 /* These ones take file names */
67 CREATE_DIRECTORY = 'd',
68 TRUNCATE_DIRECTORY = 'D',
71 CREATE_CHAR_DEVICE = 'c',
72 CREATE_BLOCK_DEVICE = 'b',
75 /* These ones take globs */
78 IGNORE_DIRECTORY_PATH = 'X',
80 RECURSIVE_REMOVE_PATH = 'R',
81 ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
83 RECURSIVE_RELABEL_PATH = 'Z',
103 bool keep_first_level:1;
108 static bool arg_create = false;
109 static bool arg_clean = false;
110 static bool arg_remove = false;
111 static bool arg_boot = false;
113 static char **arg_include_prefixes = NULL;
114 static char **arg_exclude_prefixes = NULL;
115 static char *arg_root = NULL;
117 static const char conf_file_dirs[] =
120 "/usr/local/lib/tmpfiles.d\0"
121 "/usr/lib/tmpfiles.d\0"
122 #ifdef HAVE_SPLIT_USR
127 #define MAX_DEPTH 256
129 static Hashmap *items = NULL, *globs = NULL;
130 static Set *unix_sockets = NULL;
132 static bool needs_glob(ItemType t) {
136 IGNORE_DIRECTORY_PATH,
138 RECURSIVE_REMOVE_PATH,
141 RECURSIVE_RELABEL_PATH);
144 static struct Item* find_glob(Hashmap *h, const char *match) {
148 HASHMAP_FOREACH(j, h, i)
149 if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
155 static void load_unix_sockets(void) {
156 _cleanup_fclose_ FILE *f = NULL;
162 /* We maintain a cache of the sockets we found in
163 * /proc/net/unix to speed things up a little. */
165 unix_sockets = set_new(string_hash_func, string_compare_func);
169 f = fopen("/proc/net/unix", "re");
174 if (!fgets(line, sizeof(line), f))
181 if (!fgets(line, sizeof(line), f))
186 p = strchr(line, ':');
194 p += strspn(p, WHITESPACE);
195 p += strcspn(p, WHITESPACE); /* skip one more word */
196 p += strspn(p, WHITESPACE);
205 path_kill_slashes(s);
207 k = set_consume(unix_sockets, s);
208 if (k < 0 && k != -EEXIST)
215 set_free_free(unix_sockets);
219 static bool unix_socket_alive(const char *fn) {
225 return !!set_get(unix_sockets, (char*) fn);
227 /* We don't know, so assume yes */
231 static int dir_is_mount_point(DIR *d, const char *subdir) {
233 union file_handle_union h = {
234 .handle.handle_bytes = MAX_HANDLE_SZ
237 int mount_id_parent, mount_id;
240 r_p = name_to_handle_at(dirfd(d), ".", &h.handle, &mount_id_parent, 0);
244 h.handle.handle_bytes = MAX_HANDLE_SZ;
245 r = name_to_handle_at(dirfd(d), subdir, &h.handle, &mount_id, 0);
249 /* got no handle; make no assumptions, return error */
250 if (r_p < 0 && r < 0)
253 /* got both handles; if they differ, it is a mount point */
254 if (r_p >= 0 && r >= 0)
255 return mount_id_parent != mount_id;
257 /* got only one handle; assume different mount points if one
258 * of both queries was not supported by the filesystem */
259 if (r_p == -ENOSYS || r_p == -ENOTSUP || r == -ENOSYS || r == -ENOTSUP)
268 static int dir_cleanup(
272 const struct stat *ds,
277 bool keep_this_level) {
280 struct timespec times[2];
281 bool deleted = false;
284 while ((dent = readdir(d))) {
287 _cleanup_free_ char *sub_path = NULL;
289 if (streq(dent->d_name, ".") ||
290 streq(dent->d_name, ".."))
293 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
297 /* FUSE, NFS mounts, SELinux might return EACCES */
299 log_debug("stat(%s/%s) failed: %m", p, dent->d_name);
301 log_error("stat(%s/%s) failed: %m", p, dent->d_name);
306 /* Stay on the same filesystem */
307 if (s.st_dev != rootdev)
310 /* Try to detect bind mounts of the same filesystem instance; they
311 * do not differ in device major/minors. This type of query is not
312 * supported on all kernels or filesystem types though. */
313 if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0)
316 /* Do not delete read-only files owned by root */
317 if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
320 sub_path = strjoin(p, "/", dent->d_name, NULL);
326 /* Is there an item configured for this path? */
327 if (hashmap_get(items, sub_path))
330 if (find_glob(globs, sub_path))
333 if (S_ISDIR(s.st_mode)) {
336 streq(dent->d_name, "lost+found") &&
341 log_warning("Reached max depth on %s.", sub_path);
343 _cleanup_closedir_ DIR *sub_dir;
346 sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
348 if (errno != ENOENT) {
349 log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
356 q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
361 /* Note: if you are wondering why we don't
362 * support the sticky bit for excluding
363 * directories from cleaning like we do it for
364 * other file system objects: well, the sticky
365 * bit already has a meaning for directories,
366 * so we don't want to overload that. */
371 /* Ignore ctime, we change it when deleting */
372 age = MAX(timespec_load(&s.st_mtim),
373 timespec_load(&s.st_atim));
377 if (i->type != IGNORE_DIRECTORY_PATH || !streq(dent->d_name, p)) {
378 log_debug("rmdir '%s'", sub_path);
380 if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
381 if (errno != ENOENT && errno != ENOTEMPTY) {
382 log_error("rmdir(%s): %m", sub_path);
389 /* Skip files for which the sticky bit is
390 * set. These are semantics we define, and are
391 * unknown elsewhere. See XDG_RUNTIME_DIR
392 * specification for details. */
393 if (s.st_mode & S_ISVTX)
396 if (mountpoint && S_ISREG(s.st_mode)) {
397 if (streq(dent->d_name, ".journal") &&
401 if (streq(dent->d_name, "aquota.user") ||
402 streq(dent->d_name, "aquota.group"))
406 /* Ignore sockets that are listed in /proc/net/unix */
407 if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
410 /* Ignore device nodes */
411 if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
414 /* Keep files on this level around if this is
419 age = MAX3(timespec_load(&s.st_mtim),
420 timespec_load(&s.st_atim),
421 timespec_load(&s.st_ctim));
426 log_debug("unlink '%s'", sub_path);
428 if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
429 if (errno != ENOENT) {
430 log_error("unlink(%s): %m", sub_path);
441 /* Restore original directory timestamps */
442 times[0] = ds->st_atim;
443 times[1] = ds->st_mtim;
445 if (futimens(dirfd(d), times) < 0)
446 log_error("utimensat(%s): %m", p);
452 static int item_set_perms_full(Item *i, const char *path, bool ignore_enoent) {
456 /* not using i->path directly because it may be a glob */
458 if (chmod(path, i->mode) < 0) {
459 if (errno != ENOENT || !ignore_enoent) {
460 log_error("chmod(%s) failed: %m", path);
465 if (i->uid_set || i->gid_set)
467 i->uid_set ? i->uid : (uid_t) -1,
468 i->gid_set ? i->gid : (gid_t) -1) < 0) {
470 if (errno != ENOENT || !ignore_enoent) {
471 log_error("chown(%s) failed: %m", path);
476 return label_fix(path, ignore_enoent, false);
479 static int item_set_perms(Item *i, const char *path) {
480 return item_set_perms_full(i, path, false);
483 static int write_one_file(Item *i, const char *path) {
492 flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
493 i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
496 label_context_set(path, S_IFREG);
497 fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
498 label_context_clear();
502 if (i->type == WRITE_FILE && errno == ENOENT)
505 log_error("Failed to create file %s: %m", path);
510 _cleanup_free_ char *unescaped;
514 unescaped = cunescape(i->argument);
515 if (unescaped == NULL) {
520 l = strlen(unescaped);
521 n = write(fd, unescaped, l);
523 if (n < 0 || (size_t) n < l) {
524 log_error("Failed to write file %s: %s", path, n < 0 ? strerror(-n) : "Short write");
526 return n < 0 ? n : -EIO;
532 if (stat(path, &st) < 0) {
533 log_error("stat(%s) failed: %m", path);
537 if (!S_ISREG(st.st_mode)) {
538 log_error("%s is not a file.", path);
542 r = item_set_perms(i, path);
549 static int item_set_perms_children(Item *i, const char *path) {
550 _cleanup_closedir_ DIR *d;
556 /* This returns the first error we run into, but nevertheless
561 return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
564 _cleanup_free_ char *p = NULL;
571 if (errno != 0 && r == 0)
577 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
580 p = strjoin(path, "/", de->d_name, NULL);
584 q = item_set_perms(i, p);
585 if (q < 0 && q != -ENOENT && r == 0)
588 if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) {
589 q = item_set_perms_children(i, p);
598 static int item_set_perms_recursive(Item *i, const char *path) {
604 r = item_set_perms(i, path);
608 q = item_set_perms_children(i, path);
615 static int glob_item(Item *i, int (*action)(Item *, const char *)) {
616 _cleanup_globfree_ glob_t g = {};
621 k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
622 if (k != 0 && k != GLOB_NOMATCH) {
626 log_error("glob(%s) failed: %m", i->path);
630 STRV_FOREACH(fn, g.gl_pathv) {
639 static int create_item(Item *i) {
648 case IGNORE_DIRECTORY_PATH:
650 case RECURSIVE_REMOVE_PATH:
655 r = write_one_file(i, i->path);
661 r = copy_tree(i->argument, i->path);
663 log_error("Failed to copy files: %s", strerror(-r));
667 r = item_set_perms(i, i->path);
674 r = glob_item(i, write_one_file);
680 case TRUNCATE_DIRECTORY:
681 case CREATE_DIRECTORY:
683 RUN_WITH_UMASK(0000) {
684 mkdir_parents_label(i->path, 0755);
685 r = mkdir(i->path, i->mode);
688 if (r < 0 && errno != EEXIST) {
689 log_error("Failed to create directory %s: %m", i->path);
693 if (stat(i->path, &st) < 0) {
694 log_error("stat(%s) failed: %m", i->path);
698 if (!S_ISDIR(st.st_mode)) {
699 log_error("%s is not a directory.", i->path);
703 r = item_set_perms(i, i->path);
711 RUN_WITH_UMASK(0000) {
712 r = mkfifo(i->path, i->mode);
715 if (r < 0 && errno != EEXIST) {
716 log_error("Failed to create fifo %s: %m", i->path);
720 if (stat(i->path, &st) < 0) {
721 log_error("stat(%s) failed: %m", i->path);
725 if (!S_ISFIFO(st.st_mode)) {
726 log_error("%s is not a fifo.", i->path);
730 r = item_set_perms(i, i->path);
736 case CREATE_SYMLINK: {
737 _cleanup_free_ char *x = NULL;
739 label_context_set(i->path, S_IFLNK);
740 r = symlink(i->argument, i->path);
741 label_context_clear();
743 if (r < 0 && errno != EEXIST) {
744 log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
748 r = readlink_malloc(i->path, &x);
750 log_error("readlink(%s) failed: %s", i->path, strerror(-r));
754 if (!streq(i->argument, x)) {
755 log_error("%s is not the right symlink.", i->path);
762 case CREATE_BLOCK_DEVICE:
763 case CREATE_CHAR_DEVICE: {
766 if (have_effective_cap(CAP_MKNOD) == 0) {
767 /* In a container we lack CAP_MKNOD. We
768 shouldn't attempt to create the device node in
769 that case to avoid noise, and we don't support
770 virtualized devices in containers anyway. */
772 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
776 file_type = (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
778 RUN_WITH_UMASK(0000) {
779 label_context_set(i->path, file_type);
780 r = mknod(i->path, i->mode | file_type, i->major_minor);
781 label_context_clear();
784 if (r < 0 && errno != EEXIST) {
785 log_error("Failed to create device node %s: %m", i->path);
789 if (stat(i->path, &st) < 0) {
790 log_error("stat(%s) failed: %m", i->path);
794 if ((st.st_mode & S_IFMT) != file_type) {
795 log_error("%s is not a device node.", i->path);
799 r = item_set_perms(i, i->path);
809 r = glob_item(i, item_set_perms);
814 case RECURSIVE_RELABEL_PATH:
816 r = glob_item(i, item_set_perms_recursive);
823 log_debug("%s created successfully.", i->path);
828 static int remove_item_instance(Item *i, const char *instance) {
837 case CREATE_DIRECTORY:
840 case CREATE_BLOCK_DEVICE:
841 case CREATE_CHAR_DEVICE:
843 case IGNORE_DIRECTORY_PATH:
846 case RECURSIVE_RELABEL_PATH:
852 if (remove(instance) < 0 && errno != ENOENT) {
853 log_error("remove(%s): %m", instance);
859 case TRUNCATE_DIRECTORY:
860 case RECURSIVE_REMOVE_PATH:
861 /* FIXME: we probably should use dir_cleanup() here
862 * instead of rm_rf() so that 'x' is honoured. */
863 r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
864 if (r < 0 && r != -ENOENT) {
865 log_error("rm_rf(%s): %s", instance, strerror(-r));
875 static int remove_item(Item *i) {
884 case CREATE_DIRECTORY:
887 case CREATE_CHAR_DEVICE:
888 case CREATE_BLOCK_DEVICE:
890 case IGNORE_DIRECTORY_PATH:
893 case RECURSIVE_RELABEL_PATH:
899 case TRUNCATE_DIRECTORY:
900 case RECURSIVE_REMOVE_PATH:
901 r = glob_item(i, remove_item_instance);
908 static int clean_item_instance(Item *i, const char* instance) {
909 _cleanup_closedir_ DIR *d = NULL;
920 n = now(CLOCK_REALTIME);
926 d = opendir(instance);
928 if (errno == ENOENT || errno == ENOTDIR)
931 log_error("Failed to open directory %s: %m", i->path);
935 if (fstat(dirfd(d), &s) < 0) {
936 log_error("stat(%s) failed: %m", i->path);
940 if (!S_ISDIR(s.st_mode)) {
941 log_error("%s is not a directory.", i->path);
945 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
946 log_error("stat(%s/..) failed: %m", i->path);
950 mountpoint = s.st_dev != ps.st_dev ||
951 (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
953 r = dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
954 MAX_DEPTH, i->keep_first_level);
958 static int clean_item(Item *i) {
964 case CREATE_DIRECTORY:
965 case TRUNCATE_DIRECTORY:
968 clean_item_instance(i, i->path);
970 case IGNORE_DIRECTORY_PATH:
971 r = glob_item(i, clean_item_instance);
980 static int process_item(Item *i) {
982 char prefix[PATH_MAX];
991 PATH_FOREACH_PREFIX(prefix, i->path) {
994 j = hashmap_get(items, prefix);
999 r = arg_create ? create_item(i) : 0;
1000 q = arg_remove ? remove_item(i) : 0;
1001 p = arg_clean ? clean_item(i) : 0;
1012 static void item_free(Item *i) {
1020 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1022 static bool item_equal(Item *a, Item *b) {
1026 if (!streq_ptr(a->path, b->path))
1029 if (a->type != b->type)
1032 if (a->uid_set != b->uid_set ||
1033 (a->uid_set && a->uid != b->uid))
1036 if (a->gid_set != b->gid_set ||
1037 (a->gid_set && a->gid != b->gid))
1040 if (a->mode_set != b->mode_set ||
1041 (a->mode_set && a->mode != b->mode))
1044 if (a->age_set != b->age_set ||
1045 (a->age_set && a->age != b->age))
1048 if ((a->type == CREATE_FILE ||
1049 a->type == TRUNCATE_FILE ||
1050 a->type == WRITE_FILE ||
1051 a->type == CREATE_SYMLINK ||
1052 a->type == COPY_FILES) &&
1053 !streq_ptr(a->argument, b->argument))
1056 if ((a->type == CREATE_CHAR_DEVICE ||
1057 a->type == CREATE_BLOCK_DEVICE) &&
1058 a->major_minor != b->major_minor)
1064 static bool should_include_path(const char *path) {
1067 STRV_FOREACH(prefix, arg_exclude_prefixes) {
1068 if (path_startswith(path, *prefix))
1072 STRV_FOREACH(prefix, arg_include_prefixes) {
1073 if (path_startswith(path, *prefix))
1077 /* no matches, so we should include this path only if we
1078 * have no whitelist at all */
1079 return strv_length(arg_include_prefixes) == 0;
1082 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1084 static const Specifier specifier_table[] = {
1085 { 'm', specifier_machine_id, NULL },
1086 { 'b', specifier_boot_id, NULL },
1087 { 'H', specifier_host_name, NULL },
1088 { 'v', specifier_kernel_release, NULL },
1092 _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
1093 _cleanup_(item_freep) Item *i = NULL;
1104 "%ms %ms %ms %ms %ms %ms %n",
1113 log_error("[%s:%u] Syntax error.", fname, line);
1117 if (strlen(action) > 2 || (strlen(action) > 1 && action[1] != '!')) {
1118 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1120 } else if (strlen(action) > 1 && !arg_boot)
1129 r = specifier_printf(path, specifier_table, NULL, &i->path);
1131 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path);
1136 n += strspn(buffer+n, WHITESPACE);
1137 if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
1138 i->argument = unquote(buffer+n, "\"");
1148 case CREATE_DIRECTORY:
1149 case TRUNCATE_DIRECTORY:
1152 case IGNORE_DIRECTORY_PATH:
1154 case RECURSIVE_REMOVE_PATH:
1157 case RECURSIVE_RELABEL_PATH:
1160 case CREATE_SYMLINK:
1162 log_error("[%s:%u] Symlink file requires argument.", fname, line);
1170 log_error("[%s:%u] Write file requires argument.", fname, line);
1177 log_error("[%s:%u] Copy files requires argument.", fname, line);
1181 if (!path_is_absolute(i->argument)) {
1182 log_error("[%s:%u] Source path is not absolute.", fname, line);
1186 path_kill_slashes(i->argument);
1189 case CREATE_CHAR_DEVICE:
1190 case CREATE_BLOCK_DEVICE: {
1191 unsigned major, minor;
1194 log_error("[%s:%u] Device file requires argument.", fname, line);
1198 if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) {
1199 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument);
1203 i->major_minor = makedev(major, minor);
1208 log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
1214 if (!path_is_absolute(i->path)) {
1215 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
1219 path_kill_slashes(i->path);
1221 if (!should_include_path(i->path))
1227 p = strappend(arg_root, i->path);
1235 if (user && !streq(user, "-")) {
1236 const char *u = user;
1238 r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
1240 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
1247 if (group && !streq(group, "-")) {
1248 const char *g = group;
1250 r = get_group_creds(&g, &i->gid);
1252 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
1259 if (mode && !streq(mode, "-")) {
1262 if (sscanf(mode, "%o", &m) != 1) {
1263 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
1271 i->type == CREATE_DIRECTORY ||
1272 i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
1274 if (age && !streq(age, "-")) {
1275 const char *a = age;
1278 i->keep_first_level = true;
1282 if (parse_sec(a, &i->age) < 0) {
1283 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
1290 h = needs_glob(i->type) ? globs : items;
1292 existing = hashmap_get(h, i->path);
1295 /* Two identical items are fine */
1296 if (!item_equal(existing, i))
1297 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
1302 r = hashmap_put(h, i->path, i);
1304 log_error("Failed to insert item %s: %s", i->path, strerror(-r));
1308 i = NULL; /* avoid cleanup */
1313 static int help(void) {
1315 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1316 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
1317 " -h --help Show this help\n"
1318 " --version Show package version\n"
1319 " --create Create marked files/directories\n"
1320 " --clean Clean up marked directories\n"
1321 " --remove Remove marked files/directories\n"
1322 " --boot Execute actions only safe at boot\n"
1323 " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n"
1324 " --exclude-prefix=PATH Ignore rules that apply to paths with the specified prefix\n"
1325 " --root=PATH Operate on an alternate filesystem root\n",
1326 program_invocation_short_name);
1331 static int parse_argv(int argc, char *argv[]) {
1334 ARG_VERSION = 0x100,
1344 static const struct option options[] = {
1345 { "help", no_argument, NULL, 'h' },
1346 { "version", no_argument, NULL, ARG_VERSION },
1347 { "create", no_argument, NULL, ARG_CREATE },
1348 { "clean", no_argument, NULL, ARG_CLEAN },
1349 { "remove", no_argument, NULL, ARG_REMOVE },
1350 { "boot", no_argument, NULL, ARG_BOOT },
1351 { "prefix", required_argument, NULL, ARG_PREFIX },
1352 { "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
1353 { "root", required_argument, NULL, ARG_ROOT },
1362 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1370 puts(PACKAGE_STRING);
1371 puts(SYSTEMD_FEATURES);
1391 if (strv_push(&arg_include_prefixes, optarg) < 0)
1395 case ARG_EXCLUDE_PREFIX:
1396 if (strv_push(&arg_exclude_prefixes, optarg) < 0)
1401 arg_root = path_make_absolute_cwd(optarg);
1404 path_kill_slashes(arg_root);
1411 assert_not_reached("Unhandled option");
1415 if (!arg_clean && !arg_create && !arg_remove) {
1416 log_error("You need to specify at least one of --clean, --create or --remove.");
1423 static int read_config_file(const char *fn, bool ignore_enoent) {
1424 _cleanup_fclose_ FILE *f = NULL;
1425 char line[LINE_MAX];
1433 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1435 if (ignore_enoent && r == -ENOENT)
1438 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1442 FOREACH_LINE(line, f, break) {
1449 if (*l == '#' || *l == 0)
1452 k = parse_line(fn, v, l);
1453 if (k < 0 && r == 0)
1457 /* we have to determine age parameter for each entry of type X */
1458 HASHMAP_FOREACH(i, globs, iterator) {
1460 Item *j, *candidate_item = NULL;
1462 if (i->type != IGNORE_DIRECTORY_PATH)
1465 HASHMAP_FOREACH(j, items, iter) {
1466 if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY)
1469 if (path_equal(j->path, i->path)) {
1474 if ((!candidate_item && path_startswith(i->path, j->path)) ||
1475 (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
1479 if (candidate_item) {
1480 i->age = candidate_item->age;
1486 log_error("Failed to read from file %s: %m", fn);
1494 int main(int argc, char *argv[]) {
1499 r = parse_argv(argc, argv);
1501 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1503 log_set_target(LOG_TARGET_AUTO);
1504 log_parse_environment();
1511 items = hashmap_new(string_hash_func, string_compare_func);
1512 globs = hashmap_new(string_hash_func, string_compare_func);
1514 if (!items || !globs) {
1521 if (optind < argc) {
1524 for (j = optind; j < argc; j++) {
1525 k = read_config_file(argv[j], false);
1526 if (k < 0 && r == 0)
1531 _cleanup_strv_free_ char **files = NULL;
1534 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1536 log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
1540 STRV_FOREACH(f, files) {
1541 k = read_config_file(*f, true);
1542 if (k < 0 && r == 0)
1547 HASHMAP_FOREACH(i, globs, iterator)
1550 HASHMAP_FOREACH(i, items, iterator)
1554 while ((i = hashmap_steal_first(items)))
1557 while ((i = hashmap_steal_first(globs)))
1560 hashmap_free(items);
1561 hashmap_free(globs);
1563 free(arg_include_prefixes);
1564 free(arg_exclude_prefixes);
1567 set_free_free(unix_sockets);
1571 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;