#include "capability.h"
#include "specifier.h"
#include "build.h"
+#include "copy.h"
/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
* them in the file system. This is intended to be used to create
/* These ones take file names */
CREATE_FILE = 'f',
TRUNCATE_FILE = 'F',
- WRITE_FILE = 'w',
CREATE_DIRECTORY = 'd',
TRUNCATE_DIRECTORY = 'D',
CREATE_FIFO = 'p',
CREATE_SYMLINK = 'L',
CREATE_CHAR_DEVICE = 'c',
CREATE_BLOCK_DEVICE = 'b',
- ADJUST_MODE = 'm',
+ COPY_FILES = 'C',
/* These ones take globs */
+ WRITE_FILE = 'w',
IGNORE_PATH = 'x',
IGNORE_DIRECTORY_PATH = 'X',
REMOVE_PATH = 'r',
RECURSIVE_REMOVE_PATH = 'R',
+ ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
RELABEL_PATH = 'z',
- RECURSIVE_RELABEL_PATH = 'Z'
+ RECURSIVE_RELABEL_PATH = 'Z',
} ItemType;
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 force:1;
+
+ 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[] =
"/etc/tmpfiles.d\0"
#define MAX_DEPTH 256
+static Hashmap *items = NULL, *globs = NULL;
+static Set *unix_sockets = NULL;
+
static bool needs_glob(ItemType t) {
- return t == IGNORE_PATH || t == IGNORE_DIRECTORY_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH;
+ return IN_SET(t,
+ WRITE_FILE,
+ IGNORE_PATH,
+ IGNORE_DIRECTORY_PATH,
+ REMOVE_PATH,
+ RECURSIVE_REMOVE_PATH,
+ ADJUST_MODE,
+ RELABEL_PATH,
+ RECURSIVE_RELABEL_PATH);
}
static struct Item* find_glob(Hashmap *h, const char *match) {
}
static int dir_is_mount_point(DIR *d, const char *subdir) {
- struct file_handle *h;
+
+ union file_handle_union h = {
+ .handle.handle_bytes = MAX_HANDLE_SZ
+ };
+
int mount_id_parent, mount_id;
int r_p, r;
- h = alloca(MAX_HANDLE_SZ);
-
- h->handle_bytes = MAX_HANDLE_SZ;
- r_p = name_to_handle_at(dirfd(d), ".", h, &mount_id_parent, 0);
+ r_p = name_to_handle_at(dirfd(d), ".", &h.handle, &mount_id_parent, 0);
if (r_p < 0)
r_p = -errno;
- h->handle_bytes = MAX_HANDLE_SZ;
- r = name_to_handle_at(dirfd(d), subdir, h, &mount_id, 0);
+ h.handle.handle_bytes = MAX_HANDLE_SZ;
+ r = name_to_handle_at(dirfd(d), subdir, &h.handle, &mount_id, 0);
if (r < 0)
r = -errno;
if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
continue;
- if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
+ sub_path = strjoin(p, "/", dent->d_name, NULL);
+ if (!sub_path) {
r = log_oom();
goto finish;
}
int q;
sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
- if (sub_dir == NULL) {
+ if (!sub_dir) {
if (errno != ENOENT) {
log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
r = -errno;
}
q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
-
if (q < 0)
r = q;
}
continue;
if (i->type != IGNORE_DIRECTORY_PATH || !streq(dent->d_name, p)) {
- log_debug("rmdir '%s'\n", sub_path);
+ log_debug("rmdir '%s'", sub_path);
if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
if (errno != ENOENT && errno != ENOTEMPTY) {
if (age >= cutoff)
continue;
- log_debug("unlink '%s'\n", sub_path);
+ log_debug("unlink '%s'", sub_path);
if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
if (errno != ENOENT) {
return r;
}
-static int item_set_perms_full(Item *i, const char *path, bool ignore_enoent) {
- int r;
+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;
}
- r = label_fix(path, false, false);
- return r == -ENOENT && ignore_enoent ? 0 : r;
-}
-
-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) {
- int e, flags;
- int fd = -1;
+ _cleanup_close_ int fd = -1;
+ int flags, r = 0;
struct stat st;
- int r = 0;
- flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
- i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
+ assert(i);
+ assert(path);
- RUN_WITH_UMASK(0) {
+ flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW :
+ i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0;
+
+ RUN_WITH_UMASK(0000) {
label_context_set(path, S_IFREG);
- fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
- e = errno;
+ fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
label_context_clear();
- errno = e;
}
if (fd < 0) {
}
if (i->argument) {
+ _cleanup_free_ char *unescaped;
ssize_t n;
size_t l;
- _cleanup_free_ char *unescaped;
unescaped = cunescape(i->argument);
- if (unescaped == NULL) {
- close_nointr_nofail(fd);
+ if (!unescaped)
return log_oom();
- }
l = strlen(unescaped);
n = write(fd, unescaped, l);
if (n < 0 || (size_t) n < l) {
log_error("Failed to write file %s: %s", path, n < 0 ? strerror(-n) : "Short write");
- close_nointr_nofail(fd);
return n < 0 ? n : -EIO;
}
}
- close_nointr_nofail(fd);
+ fd = safe_close(fd);
if (stat(path, &st) < 0) {
log_error("stat(%s) failed: %m", path);
return 0;
}
-static int recursive_relabel_children(Item *i, const char *path) {
+static int item_set_perms_children(Item *i, const char *path) {
_cleanup_closedir_ DIR *d;
- int ret = 0;
+ int r = 0;
+
+ assert(i);
+ assert(path);
/* This returns the first error we run into, but nevertheless
* tries to go on */
d = opendir(path);
if (!d)
- return errno == ENOENT ? 0 : -errno;
+ return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
for (;;) {
+ _cleanup_free_ char *p = NULL;
struct dirent *de;
- union dirent_storage buf;
- bool is_dir;
- int r;
- _cleanup_free_ char *entry_path = NULL;
-
- r = readdir_r(d, &buf.de, &de);
- if (r != 0) {
- if (ret == 0)
- ret = -r;
- break;
- }
+ int q;
+
+ errno = 0;
+ de = readdir(d);
+ if (!de) {
+ if (errno != 0 && r == 0)
+ r = -errno;
- if (!de)
break;
+ }
if (streq(de->d_name, ".") || streq(de->d_name, ".."))
continue;
- if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) {
- if (ret == 0)
- ret = -ENOMEM;
- continue;
- }
-
- if (de->d_type == DT_UNKNOWN) {
- struct stat st;
-
- if (lstat(entry_path, &st) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- is_dir = S_ISDIR(st.st_mode);
+ p = strjoin(path, "/", de->d_name, NULL);
+ if (!p)
+ return -ENOMEM;
- } else
- is_dir = de->d_type == DT_DIR;
+ q = item_set_perms(i, p);
+ if (q < 0 && q != -ENOENT && r == 0)
+ r = q;
- r = item_set_perms(i, entry_path);
- if (r < 0) {
- if (ret == 0 && r != -ENOENT)
- ret = r;
- continue;
- }
-
- if (is_dir) {
- r = recursive_relabel_children(i, entry_path);
- if (r < 0 && ret == 0)
- ret = r;
+ if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) {
+ q = item_set_perms_children(i, p);
+ if (q < 0 && r == 0)
+ r = q;
}
}
- return ret;
+ return r;
}
-static int recursive_relabel(Item *i, const char *path) {
- int r;
- struct stat st;
+static int item_set_perms_recursive(Item *i, const char *path) {
+ int r, q;
+
+ assert(i);
+ assert(path);
r = item_set_perms(i, path);
if (r < 0)
return r;
- if (lstat(path, &st) < 0)
- return -errno;
-
- if (S_ISDIR(st.st_mode))
- r = recursive_relabel_children(i, path);
+ q = item_set_perms_children(i, path);
+ if (q < 0 && r == 0)
+ r = q;
return r;
}
static int glob_item(Item *i, int (*action)(Item *, const char *)) {
- int r = 0, k;
_cleanup_globfree_ glob_t g = {};
+ int r = 0, k;
char **fn;
errno = 0;
k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
- if (k != 0)
- if (k != GLOB_NOMATCH) {
- if (errno > 0)
- errno = EIO;
+ if (k != 0 && k != GLOB_NOMATCH) {
+ if (errno == 0)
+ errno = EIO;
- log_error("glob(%s) failed: %m", i->path);
- return -errno;
- }
+ log_error("glob(%s) failed: %m", i->path);
+ return -errno;
+ }
STRV_FOREACH(fn, g.gl_pathv) {
k = action(i, *fn);
- if (k < 0)
+ if (k < 0 && r == 0)
r = k;
}
}
static int create_item(Item *i) {
- int e;
struct stat st;
int r = 0;
return r;
break;
- case WRITE_FILE:
- r = glob_item(i, write_one_file);
+ case COPY_FILES:
+ r = copy_tree(i->argument, i->path, false);
+ if (r < 0) {
+ struct stat a, b;
+
+ if (r != -EEXIST) {
+ log_error("Failed to copy files to %s: %s", i->path, strerror(-r));
+ return -r;
+ }
+
+ if (stat(i->argument, &a) < 0) {
+ log_error("stat(%s) failed: %m", i->argument);
+ return -errno;
+ }
+
+ if (stat(i->path, &b) < 0) {
+ log_error("stat(%s) failed: %m", i->path);
+ return -errno;
+ }
+
+ if ((a.st_mode ^ b.st_mode) & S_IFMT) {
+ log_debug("Can't copy to %s, file exists already and is of different type", i->path);
+ return 0;
+ }
+ }
+
+ r = item_set_perms(i, i->path);
if (r < 0)
return r;
break;
- case ADJUST_MODE:
- r = item_set_perms_full(i, i->path, true);
+ case WRITE_FILE:
+ r = glob_item(i, write_one_file);
if (r < 0)
return r;
RUN_WITH_UMASK(0000) {
mkdir_parents_label(i->path, 0755);
- r = mkdir(i->path, i->mode);
+ r = mkdir_label(i->path, i->mode);
}
- if (r < 0 && errno != EEXIST) {
- log_error("Failed to create directory %s: %m", i->path);
- return -errno;
- }
+ if (r < 0) {
+ if (r != -EEXIST) {
+ log_error("Failed to create directory %s: %s", i->path, strerror(-r));
+ return r;
+ }
- if (stat(i->path, &st) < 0) {
- log_error("stat(%s) failed: %m", i->path);
- return -errno;
- }
+ if (stat(i->path, &st) < 0) {
+ log_error("stat(%s) failed: %m", i->path);
+ return -errno;
+ }
- if (!S_ISDIR(st.st_mode)) {
- log_error("%s is not a directory.", i->path);
- return -EEXIST;
+ if (!S_ISDIR(st.st_mode)) {
+ log_debug("%s already exists and is not a directory.", i->path);
+ return 0;
+ }
}
r = item_set_perms(i, i->path);
case CREATE_FIFO:
RUN_WITH_UMASK(0000) {
+ label_context_set(i->path, S_IFIFO);
r = mkfifo(i->path, i->mode);
+ label_context_clear();
}
- if (r < 0 && errno != EEXIST) {
- log_error("Failed to create fifo %s: %m", i->path);
- return -errno;
- }
+ if (r < 0) {
+ if (errno != EEXIST) {
+ log_error("Failed to create fifo %s: %m", i->path);
+ return -errno;
+ }
- if (stat(i->path, &st) < 0) {
- log_error("stat(%s) failed: %m", i->path);
- return -errno;
- }
+ if (stat(i->path, &st) < 0) {
+ log_error("stat(%s) failed: %m", i->path);
+ return -errno;
+ }
+
+ if (!S_ISFIFO(st.st_mode)) {
+
+ if (i->force) {
- if (!S_ISFIFO(st.st_mode)) {
- log_error("%s is not a fifo.", i->path);
- return -EEXIST;
+ RUN_WITH_UMASK(0000) {
+ label_context_set(i->path, S_IFIFO);
+ r = mkfifo_atomic(i->path, i->mode);
+ label_context_clear();
+ }
+
+ if (r < 0) {
+ log_error("Failed to create fifo %s: %s", i->path, strerror(-r));
+ return r;
+ }
+ } else {
+ log_debug("%s is not a fifo.", i->path);
+ return 0;
+ }
+ }
}
r = item_set_perms(i, i->path);
break;
- case CREATE_SYMLINK: {
- char *x;
+ case CREATE_SYMLINK:
label_context_set(i->path, S_IFLNK);
r = symlink(i->argument, i->path);
- e = errno;
label_context_clear();
- errno = e;
- if (r < 0 && errno != EEXIST) {
- log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
- return -errno;
- }
-
- r = readlink_malloc(i->path, &x);
if (r < 0) {
- log_error("readlink(%s) failed: %s", i->path, strerror(-r));
- return -errno;
- }
+ _cleanup_free_ char *x = NULL;
- if (!streq(i->argument, x)) {
- free(x);
- log_error("%s is not the right symlinks.", i->path);
- return -EEXIST;
+ if (errno != EEXIST) {
+ log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
+ return -errno;
+ }
+
+ r = readlink_malloc(i->path, &x);
+ if (r < 0 || !streq(i->argument, x)) {
+
+ if (i->force) {
+ label_context_set(i->path, S_IFLNK);
+ r = symlink_atomic(i->argument, i->path);
+ label_context_clear();
+
+ if (r < 0) {
+ log_error("symlink(%s, %s) failed: %s", i->argument, i->path, strerror(-r));
+ return r;
+ }
+ } else {
+ log_debug("%s is not a symlink or does not point to the correct path.", i->path);
+ return 0;
+ }
+ }
}
- free(x);
break;
- }
case CREATE_BLOCK_DEVICE:
case CREATE_CHAR_DEVICE: {
return 0;
}
- file_type = (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
+ file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR;
RUN_WITH_UMASK(0000) {
label_context_set(i->path, file_type);
r = mknod(i->path, i->mode | file_type, i->major_minor);
- e = errno;
label_context_clear();
- errno = e;
}
- 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 (stat(i->path, &st) < 0) {
- log_error("stat(%s) failed: %m", i->path);
- return -errno;
- }
+ if (errno != EEXIST) {
+ log_error("Failed to create device node %s: %m", i->path);
+ return -errno;
+ }
+
+ if (stat(i->path, &st) < 0) {
+ log_error("stat(%s) failed: %m", i->path);
+ return -errno;
+ }
+
+ if ((st.st_mode & S_IFMT) != file_type) {
- if ((st.st_mode & S_IFMT) != file_type) {
- log_error("%s is not a device node.", i->path);
- return -EEXIST;
+ if (i->force) {
+
+ RUN_WITH_UMASK(0000) {
+ label_context_set(i->path, file_type);
+ r = mknod_atomic(i->path, i->mode | file_type, i->major_minor);
+ label_context_clear();
+ }
+
+ if (r < 0) {
+ log_error("Failed to create device node %s: %s", i->path, strerror(-r));
+ return r;
+ }
+ } else {
+ log_debug("%s is not a device node.", i->path);
+ return 0;
+ }
+ }
}
r = item_set_perms(i, i->path);
break;
}
+ case ADJUST_MODE:
case RELABEL_PATH:
r = glob_item(i, item_set_perms);
case RECURSIVE_RELABEL_PATH:
- r = glob_item(i, recursive_relabel);
+ r = glob_item(i, item_set_perms_recursive);
if (r < 0)
return r;
+
+ break;
}
log_debug("%s created successfully.", i->path);
case CREATE_CHAR_DEVICE:
case IGNORE_PATH:
case IGNORE_DIRECTORY_PATH:
+ case ADJUST_MODE:
case RELABEL_PATH:
case RECURSIVE_RELABEL_PATH:
case WRITE_FILE:
- case ADJUST_MODE:
+ case COPY_FILES:
break;
case REMOVE_PATH:
case CREATE_BLOCK_DEVICE:
case IGNORE_PATH:
case IGNORE_DIRECTORY_PATH:
+ case ADJUST_MODE:
case RELABEL_PATH:
case RECURSIVE_RELABEL_PATH:
case WRITE_FILE:
- case ADJUST_MODE:
+ case COPY_FILES:
break;
case REMOVE_PATH:
case CREATE_DIRECTORY:
case TRUNCATE_DIRECTORY:
case IGNORE_PATH:
+ case COPY_FILES:
clean_item_instance(i, i->path);
break;
case IGNORE_DIRECTORY_PATH:
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;
}
static void item_free(Item *i) {
- assert(i);
+
+ if (!i)
+ return;
free(i->path);
free(i->argument);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
-#define _cleanup_item_free_ _cleanup_(item_freep)
static bool item_equal(Item *a, Item *b) {
assert(a);
if ((a->type == CREATE_FILE ||
a->type == TRUNCATE_FILE ||
a->type == WRITE_FILE ||
- a->type == CREATE_SYMLINK) &&
+ a->type == CREATE_SYMLINK ||
+ a->type == COPY_FILES) &&
!streq_ptr(a->argument, b->argument))
return false;
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) {
{}
};
- _cleanup_item_free_ Item *i = NULL;
+ _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
+ _cleanup_(item_freep) Item *i = NULL;
Item *existing;
- _cleanup_free_ char
- *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
char type;
Hashmap *h;
int r, n = -1;
assert(buffer);
r = sscanf(buffer,
- "%c %ms %ms %ms %ms %ms %n",
- &type,
+ "%ms %ms %ms %ms %ms %ms %n",
+ &action,
&path,
&mode,
&user,
return -EIO;
}
+ if (isempty(action)) {
+ log_error("[%s:%u] Command too short '%s'.", fname, line, action);
+ return -EINVAL;
+ }
+
+ if (strlen(action) > 1 && !in_charset(action+1, "!+")) {
+ log_error("[%s:%u] Unknown modifiers in command '%s'", fname, line, action);
+ return -EINVAL;
+ }
+
+ if (strchr(action+1, '!') && !arg_boot)
+ return 0;
+
+ type = action[0];
+
i = new0(Item, 1);
if (!i)
return log_oom();
+ i->force = !!strchr(action+1, '+');
+
r = specifier_printf(path, specifier_table, NULL, &i->path);
if (r < 0) {
log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path);
}
}
- switch(type) {
+ switch (type) {
case CREATE_FILE:
case TRUNCATE_FILE:
case IGNORE_DIRECTORY_PATH:
case REMOVE_PATH:
case RECURSIVE_REMOVE_PATH:
+ case ADJUST_MODE:
case RELABEL_PATH:
case RECURSIVE_RELABEL_PATH:
- case ADJUST_MODE:
break;
case CREATE_SYMLINK:
if (!i->argument) {
- log_error("[%s:%u] Symlink file requires argument.", fname, line);
- return -EBADMSG;
+ i->argument = strappend("/usr/share/factory", i->path);
+ if (!i->argument)
+ return log_oom();
}
break;
}
break;
+ case COPY_FILES:
+ if (!i->argument) {
+ i->argument = strappend("/usr/share/factory", i->path);
+ if (!i->argument)
+ return log_oom();
+ }
+
+ if (!path_is_absolute(i->argument)) {
+ log_error("[%s:%u] Source path is not absolute.", fname, line);
+ return -EBADMSG;
+ }
+
+ path_kill_slashes(i->argument);
+ break;
+
case CREATE_CHAR_DEVICE:
case CREATE_BLOCK_DEVICE: {
unsigned major, minor;
}
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;
}
if (!should_include_path(i->path))
return 0;
+ if (arg_root) {
+ char *p;
+
+ p = strappend(arg_root, i->path);
+ if (!p)
+ return log_oom();
+
+ free(i->path);
+ i->path = p;
+ }
+
if (user && !streq(user, "-")) {
const char *u = user;
}
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;
}
" --create Create marked files/directories\n"
" --clean Clean up marked directories\n"
" --remove Remove marked files/directories\n"
+ " --boot Execute actions only safe at boot\n"
" --prefix=PATH Only apply rules that apply to paths with the specified prefix\n"
- " --exclude-prefix=PATH Ignore rules that apply to paths with the specified prefix\n",
+ " --exclude-prefix=PATH Ignore rules that apply to paths with the specified prefix\n"
+ " --root=PATH Operate on an alternate filesystem root\n",
program_invocation_short_name);
return 0;
ARG_CREATE,
ARG_CLEAN,
ARG_REMOVE,
+ ARG_BOOT,
ARG_PREFIX,
ARG_EXCLUDE_PREFIX,
+ ARG_ROOT,
};
static const struct option options[] = {
{ "create", no_argument, NULL, ARG_CREATE },
{ "clean", no_argument, NULL, ARG_CLEAN },
{ "remove", no_argument, NULL, ARG_REMOVE },
+ { "boot", no_argument, NULL, ARG_BOOT },
{ "prefix", required_argument, NULL, ARG_PREFIX },
{ "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
+ { "root", required_argument, NULL, ARG_ROOT },
{}
};
arg_remove = true;
break;
+ case ARG_BOOT:
+ arg_boot = true;
+ break;
+
case ARG_PREFIX:
- if (strv_extend(&include_prefixes, optarg) < 0)
+ if (strv_push(&arg_include_prefixes, optarg) < 0)
return log_oom();
break;
case ARG_EXCLUDE_PREFIX:
- if (strv_extend(&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;
case '?':
assert(fn);
- r = search_and_fopen_nulstr(fn, "re", conf_file_dirs, &f);
+ r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
if (r < 0) {
if (ignore_enoent && r == -ENOENT)
return 0;
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();
_cleanup_strv_free_ char **files = NULL;
char **f;
- r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
+ r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
if (r < 0) {
log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
goto finish;
hashmap_free(items);
hashmap_free(globs);
- strv_free(include_prefixes);
+ free(arg_include_prefixes);
+ free(arg_exclude_prefixes);
+ free(arg_root);
set_free_free(unix_sockets);