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>
49 /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
50 * them in the file system. This is intended to be used to create
51 * properly owned directories beneath /tmp, /var/tmp, /run, which are
52 * volatile and hence need to be recreated on bootup. */
54 typedef enum ItemType {
55 /* These ones take file names */
59 CREATE_DIRECTORY = 'd',
60 TRUNCATE_DIRECTORY = 'D',
63 CREATE_CHAR_DEVICE = 'c',
64 CREATE_BLOCK_DEVICE = 'b',
66 /* These ones take globs */
69 RECURSIVE_REMOVE_PATH = 'R',
71 RECURSIVE_RELABEL_PATH = 'Z'
92 static Hashmap *items = NULL, *globs = NULL;
93 static Set *unix_sockets = NULL;
95 static bool arg_create = false;
96 static bool arg_clean = false;
97 static bool arg_remove = false;
99 static const char *arg_prefix = NULL;
101 #define MAX_DEPTH 256
103 static bool needs_glob(ItemType t) {
104 return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH;
107 static struct Item* find_glob(Hashmap *h, const char *match) {
111 HASHMAP_FOREACH(j, h, i)
112 if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
118 static void load_unix_sockets(void) {
125 /* We maintain a cache of the sockets we found in
126 * /proc/net/unix to speed things up a little. */
128 unix_sockets = set_new(string_hash_func, string_compare_func);
132 f = fopen("/proc/net/unix", "re");
137 if (!fgets(line, sizeof(line), f))
144 if (!fgets(line, sizeof(line), f))
149 p = strchr(line, ':');
157 p += strspn(p, WHITESPACE);
158 p += strcspn(p, WHITESPACE); /* skip one more word */
159 p += strspn(p, WHITESPACE);
168 path_kill_slashes(s);
170 k = set_put(unix_sockets, s);
183 set_free_free(unix_sockets);
190 static bool unix_socket_alive(const char *fn) {
196 return !!set_get(unix_sockets, (char*) fn);
198 /* We don't know, so assume yes */
202 static int dir_cleanup(
205 const struct stat *ds,
212 struct timespec times[2];
213 bool deleted = false;
214 char *sub_path = NULL;
217 while ((dent = readdir(d))) {
221 if (streq(dent->d_name, ".") ||
222 streq(dent->d_name, ".."))
225 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
227 if (errno != ENOENT) {
228 log_error("stat(%s/%s) failed: %m", p, dent->d_name);
235 /* Stay on the same filesystem */
236 if (s.st_dev != rootdev)
239 /* Do not delete read-only files owned by root */
240 if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
246 if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
247 log_error("Out of memory");
252 /* Is there an item configured for this path? */
253 if (hashmap_get(items, sub_path))
256 if (find_glob(globs, sub_path))
259 if (S_ISDIR(s.st_mode)) {
262 streq(dent->d_name, "lost+found") &&
267 log_warning("Reached max depth on %s.", sub_path);
272 sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
273 if (sub_dir == NULL) {
274 if (errno != ENOENT) {
275 log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
282 q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1);
289 /* Ignore ctime, we change it when deleting */
290 age = MAX(timespec_load(&s.st_mtim),
291 timespec_load(&s.st_atim));
295 log_debug("rmdir '%s'\n", sub_path);
297 if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
298 if (errno != ENOENT && errno != ENOTEMPTY) {
299 log_error("rmdir(%s): %m", sub_path);
305 /* Skip files for which the sticky bit is
306 * set. These are semantics we define, and are
307 * unknown elsewhere. See XDG_RUNTIME_DIR
308 * specification for details. */
309 if (s.st_mode & S_ISVTX)
312 if (mountpoint && S_ISREG(s.st_mode)) {
313 if (streq(dent->d_name, ".journal") &&
317 if (streq(dent->d_name, "aquota.user") ||
318 streq(dent->d_name, "aquota.group"))
322 /* Ignore sockets that are listed in /proc/net/unix */
323 if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
326 /* Ignore device nodes */
327 if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
330 age = MAX3(timespec_load(&s.st_mtim),
331 timespec_load(&s.st_atim),
332 timespec_load(&s.st_ctim));
337 log_debug("unlink '%s'\n", sub_path);
339 if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
340 if (errno != ENOENT) {
341 log_error("unlink(%s): %m", sub_path);
352 /* Restore original directory timestamps */
353 times[0] = ds->st_atim;
354 times[1] = ds->st_mtim;
356 if (futimens(dirfd(d), times) < 0)
357 log_error("utimensat(%s): %m", p);
365 static int clean_item(Item *i) {
374 if (i->type != CREATE_DIRECTORY &&
375 i->type != TRUNCATE_DIRECTORY &&
376 i->type != IGNORE_PATH)
379 if (!i->age_set || i->age <= 0)
382 n = now(CLOCK_REALTIME);
388 d = opendir(i->path);
393 log_error("Failed to open directory %s: %m", i->path);
397 if (fstat(dirfd(d), &s) < 0) {
398 log_error("stat(%s) failed: %m", i->path);
403 if (!S_ISDIR(s.st_mode)) {
404 log_error("%s is not a directory.", i->path);
409 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
410 log_error("stat(%s/..) failed: %m", i->path);
415 mountpoint = s.st_dev != ps.st_dev ||
416 (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
418 r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH);
427 static int item_set_perms(Item *i, const char *path) {
428 /* not using i->path directly because it may be a glob */
430 if (chmod(path, i->mode) < 0) {
431 log_error("chmod(%s) failed: %m", path);
435 if (i->uid_set || i->gid_set)
437 i->uid_set ? i->uid : (uid_t) -1,
438 i->gid_set ? i->gid : (gid_t) -1) < 0) {
440 log_error("chown(%s) failed: %m", path);
444 return label_fix(path, false);
447 static int recursive_relabel_children(Item *i, const char *path) {
451 /* This returns the first error we run into, but nevertheless
456 return errno == ENOENT ? 0 : -errno;
459 struct dirent buf, *de;
464 r = readdir_r(d, &buf, &de);
474 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
477 if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) {
483 if (de->d_type == DT_UNKNOWN) {
486 if (lstat(entry_path, &st) < 0) {
487 if (ret == 0 && errno != ENOENT)
493 is_dir = S_ISDIR(st.st_mode);
496 is_dir = de->d_type == DT_DIR;
498 r = item_set_perms(i, entry_path);
500 if (ret == 0 && r != -ENOENT)
507 r = recursive_relabel_children(i, entry_path);
508 if (r < 0 && ret == 0)
520 static int recursive_relabel(Item *i, const char *path) {
524 r = item_set_perms(i, path);
528 if (lstat(path, &st) < 0)
531 if (S_ISDIR(st.st_mode))
532 r = recursive_relabel_children(i, path);
537 static int glob_item(Item *i, int (*action)(Item *, const char *)) {
545 if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) {
547 if (k != GLOB_NOMATCH) {
551 log_error("glob(%s) failed: %m", i->path);
556 STRV_FOREACH(fn, g.gl_pathv)
557 if ((k = action(i, *fn)) < 0)
564 static int create_item(Item *i) {
575 case RECURSIVE_REMOVE_PATH:
583 flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
584 i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
587 label_context_set(i->path, S_IFREG);
588 fd = open(i->path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
590 label_context_clear();
595 if (i->type == WRITE_FILE && errno == ENOENT)
598 log_error("Failed to create file %s: %m", i->path);
605 struct iovec iovec[2];
606 static const char new_line = '\n';
608 l = strlen(i->argument);
611 iovec[0].iov_base = i->argument;
612 iovec[0].iov_len = l;
614 iovec[1].iov_base = (void*) &new_line;
615 iovec[1].iov_len = 1;
617 n = writev(fd, iovec, 2);
618 if (n < 0 || (size_t) n != l+1) {
619 log_error("Failed to write file %s: %s", i->path, n < 0 ? strerror(-n) : "Short");
620 close_nointr_nofail(fd);
621 return n < 0 ? n : -EIO;
625 close_nointr_nofail(fd);
627 if (stat(i->path, &st) < 0) {
628 log_error("stat(%s) failed: %m", i->path);
632 if (!S_ISREG(st.st_mode)) {
633 log_error("%s is not a file.", i->path);
637 r = item_set_perms(i, i->path);
644 case TRUNCATE_DIRECTORY:
645 case CREATE_DIRECTORY:
648 mkdir_parents(i->path, 0755);
649 r = mkdir(i->path, i->mode);
652 if (r < 0 && errno != EEXIST) {
653 log_error("Failed to create directory %s: %m", i->path);
657 if (stat(i->path, &st) < 0) {
658 log_error("stat(%s) failed: %m", i->path);
662 if (!S_ISDIR(st.st_mode)) {
663 log_error("%s is not a directory.", i->path);
667 r = item_set_perms(i, i->path);
676 r = mkfifo(i->path, i->mode);
679 if (r < 0 && errno != EEXIST) {
680 log_error("Failed to create fifo %s: %m", i->path);
684 if (stat(i->path, &st) < 0) {
685 log_error("stat(%s) failed: %m", i->path);
689 if (!S_ISFIFO(st.st_mode)) {
690 log_error("%s is not a fifo.", i->path);
694 r = item_set_perms(i, i->path);
700 case CREATE_SYMLINK: {
703 label_context_set(i->path, S_IFLNK);
704 r = symlink(i->argument, i->path);
706 label_context_clear();
709 if (r < 0 && errno != EEXIST) {
710 log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
714 r = readlink_malloc(i->path, &x);
716 log_error("readlink(%s) failed: %s", i->path, strerror(-r));
720 if (!streq(i->argument, x)) {
722 log_error("%s is not the right symlinks.", i->path);
730 case CREATE_BLOCK_DEVICE:
731 case CREATE_CHAR_DEVICE: {
734 label_context_set(i->path, CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
735 r = mknod(i->path, i->mode | (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR), i->major_minor);
737 label_context_clear();
741 if (r < 0 && errno != EEXIST) {
742 log_error("Failed to create device node %s: %m", i->path);
746 if (stat(i->path, &st) < 0) {
747 log_error("stat(%s) failed: %m", i->path);
751 if (i->type == CREATE_BLOCK_DEVICE ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode)) {
752 log_error("%s is not a device node.", i->path);
756 r = item_set_perms(i, i->path);
765 r = glob_item(i, item_set_perms);
770 case RECURSIVE_RELABEL_PATH:
772 r = glob_item(i, recursive_relabel);
777 log_debug("%s created successfully.", i->path);
782 static int remove_item_instance(Item *i, const char *instance) {
791 case CREATE_DIRECTORY:
794 case CREATE_BLOCK_DEVICE:
795 case CREATE_CHAR_DEVICE:
798 case RECURSIVE_RELABEL_PATH:
803 if (remove(instance) < 0 && errno != ENOENT) {
804 log_error("remove(%s): %m", instance);
810 case TRUNCATE_DIRECTORY:
811 case RECURSIVE_REMOVE_PATH:
812 r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
813 if (r < 0 && r != -ENOENT) {
814 log_error("rm_rf(%s): %s", instance, strerror(-r));
824 static int remove_item(Item *i) {
833 case CREATE_DIRECTORY:
836 case CREATE_CHAR_DEVICE:
837 case CREATE_BLOCK_DEVICE:
840 case RECURSIVE_RELABEL_PATH:
845 case TRUNCATE_DIRECTORY:
846 case RECURSIVE_REMOVE_PATH:
847 r = glob_item(i, remove_item_instance);
854 static int process_item(Item *i) {
859 r = arg_create ? create_item(i) : 0;
860 q = arg_remove ? remove_item(i) : 0;
861 p = arg_clean ? clean_item(i) : 0;
872 static void item_free(Item *i) {
880 static bool item_equal(Item *a, Item *b) {
884 if (!streq_ptr(a->path, b->path))
887 if (a->type != b->type)
890 if (a->uid_set != b->uid_set ||
891 (a->uid_set && a->uid != b->uid))
894 if (a->gid_set != b->gid_set ||
895 (a->gid_set && a->gid != b->gid))
898 if (a->mode_set != b->mode_set ||
899 (a->mode_set && a->mode != b->mode))
902 if (a->age_set != b->age_set ||
903 (a->age_set && a->age != b->age))
906 if ((a->type == CREATE_FILE ||
907 a->type == TRUNCATE_FILE ||
908 a->type == WRITE_FILE ||
909 a->type == CREATE_SYMLINK) &&
910 !streq_ptr(a->argument, b->argument))
913 if ((a->type == CREATE_CHAR_DEVICE ||
914 a->type == CREATE_BLOCK_DEVICE) &&
915 a->major_minor != b->major_minor)
921 static int parse_line(const char *fname, unsigned line, const char *buffer) {
923 char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
934 log_error("Out of memory");
953 log_error("[%s:%u] Syntax error.", fname, line);
959 n += strspn(buffer+n, WHITESPACE);
960 if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
961 i->argument = unquote(buffer+n, "\"");
963 log_error("Out of memory");
973 case CREATE_DIRECTORY:
974 case TRUNCATE_DIRECTORY:
978 case RECURSIVE_REMOVE_PATH:
980 case RECURSIVE_RELABEL_PATH:
985 log_error("[%s:%u] Symlink file requires argument.", fname, line);
993 log_error("[%s:%u] Write file requires argument.", fname, line);
999 case CREATE_CHAR_DEVICE:
1000 case CREATE_BLOCK_DEVICE: {
1001 unsigned major, minor;
1004 log_error("[%s:%u] Device file requires argument.", fname, line);
1009 if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) {
1010 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument);
1015 i->major_minor = makedev(major, minor);
1020 log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
1027 if (!path_is_absolute(i->path)) {
1028 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
1033 path_kill_slashes(i->path);
1035 if (arg_prefix && !path_startswith(i->path, arg_prefix)) {
1040 if (user && !streq(user, "-")) {
1041 const char *u = user;
1043 r = get_user_creds(&u, &i->uid, NULL, NULL);
1045 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
1052 if (group && !streq(group, "-")) {
1053 const char *g = group;
1055 r = get_group_creds(&g, &i->gid);
1057 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
1064 if (mode && !streq(mode, "-")) {
1067 if (sscanf(mode, "%o", &m) != 1) {
1068 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
1077 i->type == CREATE_DIRECTORY ||
1078 i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
1080 if (age && !streq(age, "-")) {
1081 if (parse_usec(age, &i->age) < 0) {
1082 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
1090 h = needs_glob(i->type) ? globs : items;
1092 existing = hashmap_get(h, i->path);
1095 /* Two identical items are fine */
1096 if (!item_equal(existing, i))
1097 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
1103 r = hashmap_put(h, i->path, i);
1105 log_error("Failed to insert item %s: %s", i->path, strerror(-r));
1124 static int help(void) {
1126 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1127 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
1128 " -h --help Show this help\n"
1129 " --create Create marked files/directories\n"
1130 " --clean Clean up marked directories\n"
1131 " --remove Remove marked files/directories\n"
1132 " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n",
1133 program_invocation_short_name);
1138 static int parse_argv(int argc, char *argv[]) {
1147 static const struct option options[] = {
1148 { "help", no_argument, NULL, 'h' },
1149 { "create", no_argument, NULL, ARG_CREATE },
1150 { "clean", no_argument, NULL, ARG_CLEAN },
1151 { "remove", no_argument, NULL, ARG_REMOVE },
1152 { "prefix", required_argument, NULL, ARG_PREFIX },
1153 { NULL, 0, NULL, 0 }
1161 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1182 arg_prefix = optarg;
1189 log_error("Unknown option code %c", c);
1194 if (!arg_clean && !arg_create && !arg_remove) {
1195 log_error("You need to specify at least one of --clean, --create or --remove.");
1202 static int read_config_file(const char *fn, bool ignore_enoent) {
1209 f = fopen(fn, "re");
1212 if (ignore_enoent && errno == ENOENT)
1215 log_error("Failed to open %s: %m", fn);
1219 log_debug("apply: %s\n", fn);
1221 char line[LINE_MAX], *l;
1224 if (!(fgets(line, sizeof(line), f)))
1230 if (*l == '#' || *l == 0)
1233 if ((k = parse_line(fn, v, l)) < 0)
1239 log_error("Failed to read from file %s: %m", fn);
1249 int main(int argc, char *argv[]) {
1254 r = parse_argv(argc, argv);
1256 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1258 log_set_target(LOG_TARGET_AUTO);
1259 log_parse_environment();
1266 items = hashmap_new(string_hash_func, string_compare_func);
1267 globs = hashmap_new(string_hash_func, string_compare_func);
1269 if (!items || !globs) {
1270 log_error("Out of memory");
1277 if (optind < argc) {
1280 for (j = optind; j < argc; j++)
1281 if (read_config_file(argv[j], false) < 0)
1287 r = conf_files_list(&files, ".conf",
1290 "/usr/local/lib/tmpfiles.d",
1291 "/usr/lib/tmpfiles.d",
1294 log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
1299 STRV_FOREACH(f, files) {
1300 if (read_config_file(*f, true) < 0)
1307 HASHMAP_FOREACH(i, globs, iterator)
1310 HASHMAP_FOREACH(i, items, iterator)
1314 while ((i = hashmap_steal_first(items)))
1317 while ((i = hashmap_steal_first(globs)))
1320 hashmap_free(items);
1321 hashmap_free(globs);
1323 set_free_free(unix_sockets);