chiark / gitweb /
tmpfiles: skip mknod() on -EPERM (device cgroup)
[elogind.git] / src / tmpfiles / tmpfiles.c
index 89f6c6bd15696037e3004d2f2a6a886a93b45002..df52085ff4f7aa49eee3304f70a0ef3bf730c5cb 100644 (file)
@@ -99,20 +99,20 @@ typedef struct Item {
         bool gid_set:1;
         bool mode_set:1;
         bool age_set:1;
+        bool mask_perms:1;
 
         bool keep_first_level:1;
-} Item;
 
-static Hashmap *items = NULL, *globs = NULL;
-static Set *unix_sockets = NULL;
+        bool done:1;
+} Item;
 
 static bool arg_create = false;
 static bool arg_clean = false;
 static bool arg_remove = false;
 static bool arg_boot = false;
 
-static char **include_prefixes = NULL;
-static char **exclude_prefixes = NULL;
+static char **arg_include_prefixes = NULL;
+static char **arg_exclude_prefixes = NULL;
 static char *arg_root = NULL;
 
 static const char conf_file_dirs[] =
@@ -127,6 +127,9 @@ static const char conf_file_dirs[] =
 
 #define MAX_DEPTH 256
 
+static Hashmap *items = NULL, *globs = NULL;
+static Set *unix_sockets = NULL;
+
 static bool needs_glob(ItemType t) {
         return IN_SET(t,
                       WRITE_FILE,
@@ -447,35 +450,45 @@ finish:
         return r;
 }
 
-static int item_set_perms_full(Item *i, const char *path, bool ignore_enoent) {
+static int item_set_perms(Item *i, const char *path) {
         assert(i);
         assert(path);
 
         /* not using i->path directly because it may be a glob */
-        if (i->mode_set)
-                if (chmod(path, i->mode) < 0) {
-                        if (errno != ENOENT || !ignore_enoent) {
-                                log_error("chmod(%s) failed: %m", path);
-                                return -errno;
+        if (i->mode_set) {
+                mode_t m = i->mode;
+
+                if (i->mask_perms) {
+                        struct stat st;
+
+                        if (stat(path, &st) >= 0) {
+                                if (!(st.st_mode & 0111))
+                                        m &= ~0111;
+                                if (!(st.st_mode & 0222))
+                                        m &= ~0222;
+                                if (!(st.st_mode & 0444))
+                                        m &= ~0444;
+                                if (!S_ISDIR(st.st_mode))
+                                        m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
                         }
                 }
 
+                if (chmod(path, m) < 0) {
+                        log_error("chmod(%s) failed: %m", path);
+                        return -errno;
+                }
+        }
+
         if (i->uid_set || i->gid_set)
                 if (chown(path,
                           i->uid_set ? i->uid : (uid_t) -1,
                           i->gid_set ? i->gid : (gid_t) -1) < 0) {
 
-                        if (errno != ENOENT || !ignore_enoent) {
-                                log_error("chown(%s) failed: %m", path);
-                                return -errno;
-                        }
+                        log_error("chown(%s) failed: %m", path);
+                        return -errno;
                 }
 
-        return label_fix(path, ignore_enoent, false);
-}
-
-static int item_set_perms(Item *i, const char *path) {
-        return item_set_perms_full(i, path, false);
+        return label_fix(path, false, false);
 }
 
 static int write_one_file(Item *i, const char *path) {
@@ -750,7 +763,7 @@ static int create_item(Item *i) {
                 }
 
                 if (!streq(i->argument, x)) {
-                        log_error("%s is not the right symlinks.", i->path);
+                        log_error("%s is not the right symlink.", i->path);
                         return -EEXIST;
                 }
 
@@ -779,9 +792,17 @@ static int create_item(Item *i) {
                         label_context_clear();
                 }
 
-                if (r < 0 && errno != EEXIST) {
-                        log_error("Failed to create device node %s: %m", i->path);
-                        return -errno;
+                if (r < 0) {
+                        if (errno == EPERM) {
+                                log_debug("We lack permissions, possibly because of cgroup configuration; "
+                                          "skipping creation of device node %s.", i->path);
+                                return 0;
+                        }
+
+                        if (errno != EEXIST) {
+                                log_error("Failed to create device node %s: %m", i->path);
+                                return -errno;
+                        }
                 }
 
                 if (stat(i->path, &st) < 0) {
@@ -977,9 +998,23 @@ static int clean_item(Item *i) {
 
 static int process_item(Item *i) {
         int r, q, p;
+        char prefix[PATH_MAX];
 
         assert(i);
 
+        if (i->done)
+                return 0;
+
+        i->done = true;
+
+        PATH_FOREACH_PREFIX(prefix, i->path) {
+                Item *j;
+
+                j = hashmap_get(items, prefix);
+                if (j)
+                        process_item(j);
+        }
+
         r = arg_create ? create_item(i) : 0;
         q = arg_remove ? remove_item(i) : 0;
         p = arg_clean ? clean_item(i) : 0;
@@ -994,7 +1029,9 @@ static int process_item(Item *i) {
 }
 
 static void item_free(Item *i) {
-        assert(i);
+
+        if (!i)
+                return;
 
         free(i->path);
         free(i->argument);
@@ -1048,19 +1085,17 @@ static bool item_equal(Item *a, Item *b) {
 static bool should_include_path(const char *path) {
         char **prefix;
 
-        STRV_FOREACH(prefix, exclude_prefixes) {
+        STRV_FOREACH(prefix, arg_exclude_prefixes)
                 if (path_startswith(path, *prefix))
                         return false;
-        }
 
-        STRV_FOREACH(prefix, include_prefixes) {
+        STRV_FOREACH(prefix, arg_include_prefixes)
                 if (path_startswith(path, *prefix))
                         return true;
-        }
 
         /* no matches, so we should include this path only if we
          * have no whitelist at all */
-        return strv_length(include_prefixes) == 0;
+        return strv_length(arg_include_prefixes) == 0;
 }
 
 static int parse_line(const char *fname, unsigned line, const char *buffer) {
@@ -1125,7 +1160,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                 }
         }
 
-        switch(type) {
+        switch (type) {
 
         case CREATE_FILE:
         case TRUNCATE_FILE:
@@ -1189,7 +1224,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
         }
 
         default:
-                log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
+                log_error("[%s:%u] Unknown command type '%c'.", fname, line, type);
                 return -EBADMSG;
         }
 
@@ -1241,9 +1276,15 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
         }
 
         if (mode && !streq(mode, "-")) {
+                const char *mm = mode;
                 unsigned m;
 
-                if (sscanf(mode, "%o", &m) != 1) {
+                if (*mm == '~') {
+                        i->mask_perms = true;
+                        mm++;
+                }
+
+                if (sscanf(mm, "%o", &m) != 1) {
                         log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
                         return -ENOENT;
                 }
@@ -1372,19 +1413,21 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_PREFIX:
-                        if (strv_push(&include_prefixes, optarg) < 0)
+                        if (strv_push(&arg_include_prefixes, optarg) < 0)
                                 return log_oom();
                         break;
 
                 case ARG_EXCLUDE_PREFIX:
-                        if (strv_push(&exclude_prefixes, optarg) < 0)
+                        if (strv_push(&arg_exclude_prefixes, optarg) < 0)
                                 return log_oom();
                         break;
 
                 case ARG_ROOT:
+                        free(arg_root);
                         arg_root = path_make_absolute_cwd(optarg);
                         if (!arg_root)
                                 return log_oom();
+
                         path_kill_slashes(arg_root);
                         break;
 
@@ -1482,7 +1525,7 @@ int main(int argc, char *argv[]) {
 
         r = parse_argv(argc, argv);
         if (r <= 0)
-                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+                goto finish;
 
         log_set_target(LOG_TARGET_AUTO);
         log_parse_environment();
@@ -1544,8 +1587,8 @@ finish:
         hashmap_free(items);
         hashmap_free(globs);
 
-        free(include_prefixes);
-        free(exclude_prefixes);
+        free(arg_include_prefixes);
+        free(arg_exclude_prefixes);
         free(arg_root);
 
         set_free_free(unix_sockets);