chiark / gitweb /
unit: introduce %s specifier for the user shell
[elogind.git] / src / tmpfiles / tmpfiles.c
index 22362eaa4c3b09f7601024f9500550899aaa1d86..7a453dcc2aeddef8bfb9564801f0589c6705dd36 100644 (file)
@@ -89,6 +89,8 @@ typedef struct Item {
         bool gid_set:1;
         bool mode_set:1;
         bool age_set:1;
+
+        bool keep_first_level:1;
 } Item;
 
 static Hashmap *items = NULL, *globs = NULL;
@@ -100,7 +102,7 @@ static bool arg_remove = false;
 
 static const char *arg_prefix = NULL;
 
-static const char *conf_file_dirs[] = {
+static const char * const conf_file_dirs[] = {
         "/etc/tmpfiles.d",
         "/run/tmpfiles.d",
         "/usr/local/lib/tmpfiles.d",
@@ -216,7 +218,8 @@ static int dir_cleanup(
                 usec_t cutoff,
                 dev_t rootdev,
                 bool mountpoint,
-                int maxdepth)
+                int maxdepth,
+                bool keep_this_level)
 {
         struct dirent *dent;
         struct timespec times[2];
@@ -289,13 +292,23 @@ static int dir_cleanup(
                                         continue;
                                 }
 
-                                q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1);
+                                q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
                                 closedir(sub_dir);
 
                                 if (q < 0)
                                         r = q;
                         }
 
+                        /* Note: if you are wondering why we don't
+                         * support the sticky bit for excluding
+                         * directories from cleaning like we do it for
+                         * other file system objects: well, the sticky
+                         * bit already has a meaning for directories,
+                         * so we don't want to overload that. */
+
+                        if (keep_this_level)
+                                continue;
+
                         /* Ignore ctime, we change it when deleting */
                         age = MAX(timespec_load(&s.st_mtim),
                                   timespec_load(&s.st_atim));
@@ -337,6 +350,11 @@ static int dir_cleanup(
                         if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
                                 continue;
 
+                        /* Keep files on this level around if this is
+                         * requested */
+                        if (keep_this_level)
+                                continue;
+
                         age = MAX3(timespec_load(&s.st_mtim),
                                    timespec_load(&s.st_atim),
                                    timespec_load(&s.st_ctim));
@@ -425,7 +443,7 @@ static int clean_item(Item *i) {
         mountpoint = s.st_dev != ps.st_dev ||
                      (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
 
-        r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH);
+        r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH, i->keep_first_level);
 
 finish:
         if (d)
@@ -451,7 +469,7 @@ static int item_set_perms(Item *i, const char *path) {
                         return -errno;
                 }
 
-        return label_fix(path, false);
+        return label_fix(path, false, false);
 }
 
 static int recursive_relabel_children(Item *i, const char *path) {
@@ -744,10 +762,11 @@ static int create_item(Item *i) {
 
         case CREATE_BLOCK_DEVICE:
         case CREATE_CHAR_DEVICE: {
+                mode_t file_type = (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
 
                 u = umask(0);
-                label_context_set(i->path, CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
-                r = mknod(i->path, i->mode | (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR), i->major_minor);
+                label_context_set(i->path, file_type);
+                r = mknod(i->path, i->mode | file_type, i->major_minor);
                 e = errno;
                 label_context_clear();
                 umask(u);
@@ -763,7 +782,7 @@ static int create_item(Item *i) {
                         return -errno;
                 }
 
-                if (i->type == CREATE_BLOCK_DEVICE ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode)) {
+                if ((st.st_mode & S_IFMT) != file_type) {
                         log_error("%s is not a device node.", i->path);
                         return -EEXIST;
                 }
@@ -824,7 +843,9 @@ static int remove_item_instance(Item *i, const char *instance) {
 
         case TRUNCATE_DIRECTORY:
         case RECURSIVE_REMOVE_PATH:
-                r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
+                /* FIXME: we probably should use dir_cleanup() here
+                 * instead of rm_rf() so that 'x' is honoured. */
+                r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
                 if (r < 0 && r != -ENOENT) {
                         log_error("rm_rf(%s): %s", instance, strerror(-r));
                         return r;
@@ -1055,7 +1076,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
         if (user && !streq(user, "-")) {
                 const char *u = user;
 
-                r = get_user_creds(&u, &i->uid, NULL, NULL);
+                r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
                 if (r < 0) {
                         log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
                         goto finish;
@@ -1093,7 +1114,14 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                         i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
 
         if (age && !streq(age, "-")) {
-                if (parse_usec(age, &i->age) < 0) {
+                const char *a = age;
+
+                if (*a == '~') {
+                        i->keep_first_level = true;
+                        a++;
+                }
+
+                if (parse_usec(a, &i->age) < 0) {
                         log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
                         r = -EBADMSG;
                         goto finish;
@@ -1269,7 +1297,7 @@ static char *resolve_fragment(const char *fragment, const char **search_paths) {
                 return strdup(fragment);
 
         STRV_FOREACH(p, search_paths) {
-                resolved_path = join(*p, "/", fragment, NULL);
+                resolved_path = strjoin(*p, "/", fragment, NULL);
                 if (resolved_path == NULL) {
                         log_error("Out of memory");
                         return NULL;
@@ -1319,7 +1347,7 @@ int main(int argc, char *argv[]) {
                 for (j = optind; j < argc; j++) {
                         char *fragment;
 
-                        fragment = resolve_fragment(argv[j], conf_file_dirs);
+                        fragment = resolve_fragment(argv[j], (const char**) conf_file_dirs);
                         if (!fragment) {
                                 log_error("Failed to find a %s file: %m", argv[j]);
                                 r = EXIT_FAILURE;
@@ -1334,7 +1362,7 @@ int main(int argc, char *argv[]) {
                 char **files, **f;
 
                 r = conf_files_list_strv(&files, ".conf",
-                                    (const char **)conf_file_dirs);
+                                    (const char **) conf_file_dirs);
                 if (r < 0) {
                         log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
                         r = EXIT_FAILURE;