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;
106 static Hashmap *items = NULL, *globs = NULL;
107 static Set *unix_sockets = NULL;
109 static bool arg_create = false;
110 static bool arg_clean = false;
111 static bool arg_remove = false;
112 static bool arg_boot = false;
114 static char **include_prefixes = NULL;
115 static char **exclude_prefixes = NULL;
116 static char *arg_root = NULL;
118 static const char conf_file_dirs[] =
121 "/usr/local/lib/tmpfiles.d\0"
122 "/usr/lib/tmpfiles.d\0"
123 #ifdef HAVE_SPLIT_USR
128 #define MAX_DEPTH 256
130 static bool needs_glob(ItemType t) {
134 IGNORE_DIRECTORY_PATH,
136 RECURSIVE_REMOVE_PATH,
139 RECURSIVE_RELABEL_PATH);
142 static struct Item* find_glob(Hashmap *h, const char *match) {
146 HASHMAP_FOREACH(j, h, i)
147 if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
153 static void load_unix_sockets(void) {
154 _cleanup_fclose_ FILE *f = NULL;
160 /* We maintain a cache of the sockets we found in
161 * /proc/net/unix to speed things up a little. */
163 unix_sockets = set_new(string_hash_func, string_compare_func);
167 f = fopen("/proc/net/unix", "re");
172 if (!fgets(line, sizeof(line), f))
179 if (!fgets(line, sizeof(line), f))
184 p = strchr(line, ':');
192 p += strspn(p, WHITESPACE);
193 p += strcspn(p, WHITESPACE); /* skip one more word */
194 p += strspn(p, WHITESPACE);
203 path_kill_slashes(s);
205 k = set_consume(unix_sockets, s);
206 if (k < 0 && k != -EEXIST)
213 set_free_free(unix_sockets);
217 static bool unix_socket_alive(const char *fn) {
223 return !!set_get(unix_sockets, (char*) fn);
225 /* We don't know, so assume yes */
229 static int dir_is_mount_point(DIR *d, const char *subdir) {
231 union file_handle_union h = {
232 .handle.handle_bytes = MAX_HANDLE_SZ
235 int mount_id_parent, mount_id;
238 r_p = name_to_handle_at(dirfd(d), ".", &h.handle, &mount_id_parent, 0);
242 h.handle.handle_bytes = MAX_HANDLE_SZ;
243 r = name_to_handle_at(dirfd(d), subdir, &h.handle, &mount_id, 0);
247 /* got no handle; make no assumptions, return error */
248 if (r_p < 0 && r < 0)
251 /* got both handles; if they differ, it is a mount point */
252 if (r_p >= 0 && r >= 0)
253 return mount_id_parent != mount_id;
255 /* got only one handle; assume different mount points if one
256 * of both queries was not supported by the filesystem */
257 if (r_p == -ENOSYS || r_p == -ENOTSUP || r == -ENOSYS || r == -ENOTSUP)
266 static int dir_cleanup(
270 const struct stat *ds,
275 bool keep_this_level) {
278 struct timespec times[2];
279 bool deleted = false;
282 while ((dent = readdir(d))) {
285 _cleanup_free_ char *sub_path = NULL;
287 if (streq(dent->d_name, ".") ||
288 streq(dent->d_name, ".."))
291 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
295 /* FUSE, NFS mounts, SELinux might return EACCES */
297 log_debug("stat(%s/%s) failed: %m", p, dent->d_name);
299 log_error("stat(%s/%s) failed: %m", p, dent->d_name);
304 /* Stay on the same filesystem */
305 if (s.st_dev != rootdev)
308 /* Try to detect bind mounts of the same filesystem instance; they
309 * do not differ in device major/minors. This type of query is not
310 * supported on all kernels or filesystem types though. */
311 if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0)
314 /* Do not delete read-only files owned by root */
315 if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
318 sub_path = strjoin(p, "/", dent->d_name, NULL);
324 /* Is there an item configured for this path? */
325 if (hashmap_get(items, sub_path))
328 if (find_glob(globs, sub_path))
331 if (S_ISDIR(s.st_mode)) {
334 streq(dent->d_name, "lost+found") &&
339 log_warning("Reached max depth on %s.", sub_path);
341 _cleanup_closedir_ DIR *sub_dir;
344 sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
346 if (errno != ENOENT) {
347 log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
354 q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
359 /* Note: if you are wondering why we don't
360 * support the sticky bit for excluding
361 * directories from cleaning like we do it for
362 * other file system objects: well, the sticky
363 * bit already has a meaning for directories,
364 * so we don't want to overload that. */
369 /* Ignore ctime, we change it when deleting */
370 age = MAX(timespec_load(&s.st_mtim),
371 timespec_load(&s.st_atim));
375 if (i->type != IGNORE_DIRECTORY_PATH || !streq(dent->d_name, p)) {
376 log_debug("rmdir '%s'", sub_path);
378 if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
379 if (errno != ENOENT && errno != ENOTEMPTY) {
380 log_error("rmdir(%s): %m", sub_path);
387 /* Skip files for which the sticky bit is
388 * set. These are semantics we define, and are
389 * unknown elsewhere. See XDG_RUNTIME_DIR
390 * specification for details. */
391 if (s.st_mode & S_ISVTX)
394 if (mountpoint && S_ISREG(s.st_mode)) {
395 if (streq(dent->d_name, ".journal") &&
399 if (streq(dent->d_name, "aquota.user") ||
400 streq(dent->d_name, "aquota.group"))
404 /* Ignore sockets that are listed in /proc/net/unix */
405 if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
408 /* Ignore device nodes */
409 if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
412 /* Keep files on this level around if this is
417 age = MAX3(timespec_load(&s.st_mtim),
418 timespec_load(&s.st_atim),
419 timespec_load(&s.st_ctim));
424 log_debug("unlink '%s'", sub_path);
426 if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
427 if (errno != ENOENT) {
428 log_error("unlink(%s): %m", sub_path);
439 /* Restore original directory timestamps */
440 times[0] = ds->st_atim;
441 times[1] = ds->st_mtim;
443 if (futimens(dirfd(d), times) < 0)
444 log_error("utimensat(%s): %m", p);
450 static int item_set_perms_full(Item *i, const char *path, bool ignore_enoent) {
454 /* not using i->path directly because it may be a glob */
456 if (chmod(path, i->mode) < 0) {
457 if (errno != ENOENT || !ignore_enoent) {
458 log_error("chmod(%s) failed: %m", path);
463 if (i->uid_set || i->gid_set)
465 i->uid_set ? i->uid : (uid_t) -1,
466 i->gid_set ? i->gid : (gid_t) -1) < 0) {
468 if (errno != ENOENT || !ignore_enoent) {
469 log_error("chown(%s) failed: %m", path);
474 return label_fix(path, ignore_enoent, false);
477 static int item_set_perms(Item *i, const char *path) {
478 return item_set_perms_full(i, path, false);
481 static int write_one_file(Item *i, const char *path) {
490 flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
491 i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
494 label_context_set(path, S_IFREG);
495 fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
496 label_context_clear();
500 if (i->type == WRITE_FILE && errno == ENOENT)
503 log_error("Failed to create file %s: %m", path);
508 _cleanup_free_ char *unescaped;
512 unescaped = cunescape(i->argument);
513 if (unescaped == NULL) {
518 l = strlen(unescaped);
519 n = write(fd, unescaped, l);
521 if (n < 0 || (size_t) n < l) {
522 log_error("Failed to write file %s: %s", path, n < 0 ? strerror(-n) : "Short write");
524 return n < 0 ? n : -EIO;
530 if (stat(path, &st) < 0) {
531 log_error("stat(%s) failed: %m", path);
535 if (!S_ISREG(st.st_mode)) {
536 log_error("%s is not a file.", path);
540 r = item_set_perms(i, path);
547 static int item_set_perms_children(Item *i, const char *path) {
548 _cleanup_closedir_ DIR *d;
554 /* This returns the first error we run into, but nevertheless
559 return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
562 _cleanup_free_ char *p = NULL;
569 if (errno != 0 && r == 0)
575 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
578 p = strjoin(path, "/", de->d_name, NULL);
582 q = item_set_perms(i, p);
583 if (q < 0 && q != -ENOENT && r == 0)
586 if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) {
587 q = item_set_perms_children(i, p);
596 static int item_set_perms_recursive(Item *i, const char *path) {
602 r = item_set_perms(i, path);
606 q = item_set_perms_children(i, path);
613 static int glob_item(Item *i, int (*action)(Item *, const char *)) {
614 _cleanup_globfree_ glob_t g = {};
619 k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
620 if (k != 0 && k != GLOB_NOMATCH) {
624 log_error("glob(%s) failed: %m", i->path);
628 STRV_FOREACH(fn, g.gl_pathv) {
637 static int create_item(Item *i) {
646 case IGNORE_DIRECTORY_PATH:
648 case RECURSIVE_REMOVE_PATH:
653 r = write_one_file(i, i->path);
659 r = copy_tree(i->argument, i->path);
661 log_error("Failed to copy files: %s", strerror(-r));
665 r = item_set_perms(i, i->path);
672 r = glob_item(i, write_one_file);
678 case TRUNCATE_DIRECTORY:
679 case CREATE_DIRECTORY:
681 RUN_WITH_UMASK(0000) {
682 mkdir_parents_label(i->path, 0755);
683 r = mkdir(i->path, i->mode);
686 if (r < 0 && errno != EEXIST) {
687 log_error("Failed to create directory %s: %m", i->path);
691 if (stat(i->path, &st) < 0) {
692 log_error("stat(%s) failed: %m", i->path);
696 if (!S_ISDIR(st.st_mode)) {
697 log_error("%s is not a directory.", i->path);
701 r = item_set_perms(i, i->path);
709 RUN_WITH_UMASK(0000) {
710 r = mkfifo(i->path, i->mode);
713 if (r < 0 && errno != EEXIST) {
714 log_error("Failed to create fifo %s: %m", i->path);
718 if (stat(i->path, &st) < 0) {
719 log_error("stat(%s) failed: %m", i->path);
723 if (!S_ISFIFO(st.st_mode)) {
724 log_error("%s is not a fifo.", i->path);
728 r = item_set_perms(i, i->path);
734 case CREATE_SYMLINK: {
735 _cleanup_free_ char *x = NULL;
737 label_context_set(i->path, S_IFLNK);
738 r = symlink(i->argument, i->path);
739 label_context_clear();
741 if (r < 0 && errno != EEXIST) {
742 log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
746 r = readlink_malloc(i->path, &x);
748 log_error("readlink(%s) failed: %s", i->path, strerror(-r));
752 if (!streq(i->argument, x)) {
753 log_error("%s is not the right symlink.", i->path);
760 case CREATE_BLOCK_DEVICE:
761 case CREATE_CHAR_DEVICE: {
764 if (have_effective_cap(CAP_MKNOD) == 0) {
765 /* In a container we lack CAP_MKNOD. We
766 shouldn't attempt to create the device node in
767 that case to avoid noise, and we don't support
768 virtualized devices in containers anyway. */
770 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
774 file_type = (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
776 RUN_WITH_UMASK(0000) {
777 label_context_set(i->path, file_type);
778 r = mknod(i->path, i->mode | file_type, i->major_minor);
779 label_context_clear();
782 if (r < 0 && errno != EEXIST) {
783 log_error("Failed to create device node %s: %m", i->path);
787 if (stat(i->path, &st) < 0) {
788 log_error("stat(%s) failed: %m", i->path);
792 if ((st.st_mode & S_IFMT) != file_type) {
793 log_error("%s is not a device node.", i->path);
797 r = item_set_perms(i, i->path);
807 r = glob_item(i, item_set_perms);
812 case RECURSIVE_RELABEL_PATH:
814 r = glob_item(i, item_set_perms_recursive);
821 log_debug("%s created successfully.", i->path);
826 static int remove_item_instance(Item *i, const char *instance) {
835 case CREATE_DIRECTORY:
838 case CREATE_BLOCK_DEVICE:
839 case CREATE_CHAR_DEVICE:
841 case IGNORE_DIRECTORY_PATH:
844 case RECURSIVE_RELABEL_PATH:
850 if (remove(instance) < 0 && errno != ENOENT) {
851 log_error("remove(%s): %m", instance);
857 case TRUNCATE_DIRECTORY:
858 case RECURSIVE_REMOVE_PATH:
859 /* FIXME: we probably should use dir_cleanup() here
860 * instead of rm_rf() so that 'x' is honoured. */
861 r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
862 if (r < 0 && r != -ENOENT) {
863 log_error("rm_rf(%s): %s", instance, strerror(-r));
873 static int remove_item(Item *i) {
882 case CREATE_DIRECTORY:
885 case CREATE_CHAR_DEVICE:
886 case CREATE_BLOCK_DEVICE:
888 case IGNORE_DIRECTORY_PATH:
891 case RECURSIVE_RELABEL_PATH:
897 case TRUNCATE_DIRECTORY:
898 case RECURSIVE_REMOVE_PATH:
899 r = glob_item(i, remove_item_instance);
906 static int clean_item_instance(Item *i, const char* instance) {
907 _cleanup_closedir_ DIR *d = NULL;
918 n = now(CLOCK_REALTIME);
924 d = opendir(instance);
926 if (errno == ENOENT || errno == ENOTDIR)
929 log_error("Failed to open directory %s: %m", i->path);
933 if (fstat(dirfd(d), &s) < 0) {
934 log_error("stat(%s) failed: %m", i->path);
938 if (!S_ISDIR(s.st_mode)) {
939 log_error("%s is not a directory.", i->path);
943 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
944 log_error("stat(%s/..) failed: %m", i->path);
948 mountpoint = s.st_dev != ps.st_dev ||
949 (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
951 r = dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
952 MAX_DEPTH, i->keep_first_level);
956 static int clean_item(Item *i) {
962 case CREATE_DIRECTORY:
963 case TRUNCATE_DIRECTORY:
966 clean_item_instance(i, i->path);
968 case IGNORE_DIRECTORY_PATH:
969 r = glob_item(i, clean_item_instance);
978 static int process_item(Item *i) {
983 r = arg_create ? create_item(i) : 0;
984 q = arg_remove ? remove_item(i) : 0;
985 p = arg_clean ? clean_item(i) : 0;
996 static void item_free(Item *i) {
1004 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1006 static bool item_equal(Item *a, Item *b) {
1010 if (!streq_ptr(a->path, b->path))
1013 if (a->type != b->type)
1016 if (a->uid_set != b->uid_set ||
1017 (a->uid_set && a->uid != b->uid))
1020 if (a->gid_set != b->gid_set ||
1021 (a->gid_set && a->gid != b->gid))
1024 if (a->mode_set != b->mode_set ||
1025 (a->mode_set && a->mode != b->mode))
1028 if (a->age_set != b->age_set ||
1029 (a->age_set && a->age != b->age))
1032 if ((a->type == CREATE_FILE ||
1033 a->type == TRUNCATE_FILE ||
1034 a->type == WRITE_FILE ||
1035 a->type == CREATE_SYMLINK ||
1036 a->type == COPY_FILES) &&
1037 !streq_ptr(a->argument, b->argument))
1040 if ((a->type == CREATE_CHAR_DEVICE ||
1041 a->type == CREATE_BLOCK_DEVICE) &&
1042 a->major_minor != b->major_minor)
1048 static bool should_include_path(const char *path) {
1051 STRV_FOREACH(prefix, exclude_prefixes) {
1052 if (path_startswith(path, *prefix))
1056 STRV_FOREACH(prefix, include_prefixes) {
1057 if (path_startswith(path, *prefix))
1061 /* no matches, so we should include this path only if we
1062 * have no whitelist at all */
1063 return strv_length(include_prefixes) == 0;
1066 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1068 static const Specifier specifier_table[] = {
1069 { 'm', specifier_machine_id, NULL },
1070 { 'b', specifier_boot_id, NULL },
1071 { 'H', specifier_host_name, NULL },
1072 { 'v', specifier_kernel_release, NULL },
1076 _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
1077 _cleanup_(item_freep) Item *i = NULL;
1088 "%ms %ms %ms %ms %ms %ms %n",
1097 log_error("[%s:%u] Syntax error.", fname, line);
1101 if (strlen(action) > 2 || (strlen(action) > 1 && action[1] != '!')) {
1102 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1104 } else if (strlen(action) > 1 && !arg_boot)
1113 r = specifier_printf(path, specifier_table, NULL, &i->path);
1115 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path);
1120 n += strspn(buffer+n, WHITESPACE);
1121 if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
1122 i->argument = unquote(buffer+n, "\"");
1132 case CREATE_DIRECTORY:
1133 case TRUNCATE_DIRECTORY:
1136 case IGNORE_DIRECTORY_PATH:
1138 case RECURSIVE_REMOVE_PATH:
1141 case RECURSIVE_RELABEL_PATH:
1144 case CREATE_SYMLINK:
1146 log_error("[%s:%u] Symlink file requires argument.", fname, line);
1154 log_error("[%s:%u] Write file requires argument.", fname, line);
1161 log_error("[%s:%u] Copy files requires argument.", fname, line);
1165 if (!path_is_absolute(i->argument)) {
1166 log_error("[%s:%u] Source path is not absolute.", fname, line);
1170 path_kill_slashes(i->argument);
1173 case CREATE_CHAR_DEVICE:
1174 case CREATE_BLOCK_DEVICE: {
1175 unsigned major, minor;
1178 log_error("[%s:%u] Device file requires argument.", fname, line);
1182 if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) {
1183 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument);
1187 i->major_minor = makedev(major, minor);
1192 log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
1198 if (!path_is_absolute(i->path)) {
1199 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
1203 path_kill_slashes(i->path);
1205 if (!should_include_path(i->path))
1211 p = strappend(arg_root, i->path);
1219 if (user && !streq(user, "-")) {
1220 const char *u = user;
1222 r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
1224 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
1231 if (group && !streq(group, "-")) {
1232 const char *g = group;
1234 r = get_group_creds(&g, &i->gid);
1236 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
1243 if (mode && !streq(mode, "-")) {
1246 if (sscanf(mode, "%o", &m) != 1) {
1247 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
1255 i->type == CREATE_DIRECTORY ||
1256 i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
1258 if (age && !streq(age, "-")) {
1259 const char *a = age;
1262 i->keep_first_level = true;
1266 if (parse_sec(a, &i->age) < 0) {
1267 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
1274 h = needs_glob(i->type) ? globs : items;
1276 existing = hashmap_get(h, i->path);
1279 /* Two identical items are fine */
1280 if (!item_equal(existing, i))
1281 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
1286 r = hashmap_put(h, i->path, i);
1288 log_error("Failed to insert item %s: %s", i->path, strerror(-r));
1292 i = NULL; /* avoid cleanup */
1297 static int help(void) {
1299 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1300 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
1301 " -h --help Show this help\n"
1302 " --version Show package version\n"
1303 " --create Create marked files/directories\n"
1304 " --clean Clean up marked directories\n"
1305 " --remove Remove marked files/directories\n"
1306 " --boot Execute actions only safe at boot\n"
1307 " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n"
1308 " --exclude-prefix=PATH Ignore rules that apply to paths with the specified prefix\n"
1309 " --root=PATH Operate on an alternate filesystem root\n",
1310 program_invocation_short_name);
1315 static int parse_argv(int argc, char *argv[]) {
1318 ARG_VERSION = 0x100,
1328 static const struct option options[] = {
1329 { "help", no_argument, NULL, 'h' },
1330 { "version", no_argument, NULL, ARG_VERSION },
1331 { "create", no_argument, NULL, ARG_CREATE },
1332 { "clean", no_argument, NULL, ARG_CLEAN },
1333 { "remove", no_argument, NULL, ARG_REMOVE },
1334 { "boot", no_argument, NULL, ARG_BOOT },
1335 { "prefix", required_argument, NULL, ARG_PREFIX },
1336 { "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
1337 { "root", required_argument, NULL, ARG_ROOT },
1346 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1354 puts(PACKAGE_STRING);
1355 puts(SYSTEMD_FEATURES);
1375 if (strv_push(&include_prefixes, optarg) < 0)
1379 case ARG_EXCLUDE_PREFIX:
1380 if (strv_push(&exclude_prefixes, optarg) < 0)
1385 arg_root = path_make_absolute_cwd(optarg);
1388 path_kill_slashes(arg_root);
1395 assert_not_reached("Unhandled option");
1399 if (!arg_clean && !arg_create && !arg_remove) {
1400 log_error("You need to specify at least one of --clean, --create or --remove.");
1407 static int read_config_file(const char *fn, bool ignore_enoent) {
1408 _cleanup_fclose_ FILE *f = NULL;
1409 char line[LINE_MAX];
1417 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1419 if (ignore_enoent && r == -ENOENT)
1422 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1426 FOREACH_LINE(line, f, break) {
1433 if (*l == '#' || *l == 0)
1436 k = parse_line(fn, v, l);
1437 if (k < 0 && r == 0)
1441 /* we have to determine age parameter for each entry of type X */
1442 HASHMAP_FOREACH(i, globs, iterator) {
1444 Item *j, *candidate_item = NULL;
1446 if (i->type != IGNORE_DIRECTORY_PATH)
1449 HASHMAP_FOREACH(j, items, iter) {
1450 if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY)
1453 if (path_equal(j->path, i->path)) {
1458 if ((!candidate_item && path_startswith(i->path, j->path)) ||
1459 (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
1463 if (candidate_item) {
1464 i->age = candidate_item->age;
1470 log_error("Failed to read from file %s: %m", fn);
1478 int main(int argc, char *argv[]) {
1483 r = parse_argv(argc, argv);
1485 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1487 log_set_target(LOG_TARGET_AUTO);
1488 log_parse_environment();
1495 items = hashmap_new(string_hash_func, string_compare_func);
1496 globs = hashmap_new(string_hash_func, string_compare_func);
1498 if (!items || !globs) {
1505 if (optind < argc) {
1508 for (j = optind; j < argc; j++) {
1509 k = read_config_file(argv[j], false);
1510 if (k < 0 && r == 0)
1515 _cleanup_strv_free_ char **files = NULL;
1518 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1520 log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
1524 STRV_FOREACH(f, files) {
1525 k = read_config_file(*f, true);
1526 if (k < 0 && r == 0)
1531 HASHMAP_FOREACH(i, globs, iterator)
1534 HASHMAP_FOREACH(i, items, iterator)
1538 while ((i = hashmap_steal_first(items)))
1541 while ((i = hashmap_steal_first(globs)))
1544 hashmap_free(items);
1545 hashmap_free(globs);
1547 free(include_prefixes);
1548 free(exclude_prefixes);
1551 set_free_free(unix_sockets);
1555 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;