MERGE_EXIT_SKIP_REFRESH = 124,
};
-static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default for sysext and /etc by default for confext */
static char *arg_root = NULL;
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
static PagerFlags arg_pager_flags = 0;
static bool arg_always_refresh = false;
static int arg_noexec = -1;
static ImagePolicy *arg_image_policy = NULL;
-static bool arg_image_policy_set = false; /* Tracks initialization */
+static bool arg_image_policy_argv_set = false;
static bool arg_varlink = false;
static MutableMode arg_mutable = MUTABLE_NO;
-static bool arg_mutable_set = false; /* Tracks initialization */
-static const char *arg_overlayfs_mount_options = NULL;
+static bool arg_mutable_argv_set = false;
/* Is set to IMAGE_CONFEXT when systemd is called with the confext functionality instead of the default */
static ImageClass arg_image_class = IMAGE_SYSEXT;
* files from partial upcopies after umount, index=off allows reuse of the upper/work dirs */
#define MUTABLE_EXTENSIONS_MOUNT_OPTIONS "redirect_dir=on,noatime,metacopy=off,index=off"
-STATIC_DESTRUCTOR_REGISTER(arg_hierarchies, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
static DEFINE_CONFIG_PARSE_ENUM(config_parse_mutable_mode, mutable_mode, MutableMode);
-static int parse_config_file(ImageClass image_class) {
+typedef struct Context {
+ ImageClass image_class;
+ char **hierarchies;
+ char *root;
+ bool force;
+ bool no_reload;
+ bool always_refresh;
+ int noexec;
+ MutableMode mutable;
+ bool mutable_set;
+ ImagePolicy *image_policy;
+ bool image_policy_set;
+ /* image_policy is usually borrowed from arg_image_policy. When parsed from configuration, this
+ * context owns it and frees it in context_done(). */
+ bool image_policy_owned;
+ const char *overlayfs_mount_options;
+} Context;
+
+#define CONTEXT_NULL \
+ (Context) { \
+ .image_class = IMAGE_SYSEXT, \
+ .noexec = -1, \
+ .mutable = MUTABLE_NO, \
+ }
+
+static void context_done(Context *c) {
+ assert(c);
+
+ c->hierarchies = strv_free(c->hierarchies);
+ c->root = mfree(c->root);
+
+ if (c->image_policy_owned)
+ c->image_policy = image_policy_free(c->image_policy);
+}
+
+static void parse_env_image_class_config(Context *c) {
+ const char *env_var;
+ int r;
+
+ assert(c);
+
+ env_var = secure_getenv(image_class_info[c->image_class].mode_env);
+ if (env_var) {
+ r = parse_mutable_mode(env_var);
+ if (r < 0)
+ log_warning("Failed to parse %s environment variable value '%s'. Ignoring.",
+ image_class_info[c->image_class].mode_env, env_var);
+ else {
+ c->mutable = r;
+ c->mutable_set = true;
+ }
+ }
+
+ env_var = secure_getenv(image_class_info[c->image_class].opts_env);
+ if (env_var)
+ c->overlayfs_mount_options = env_var;
+}
+
+static int parse_config_file(Context *c) {
_cleanup_(image_policy_freep) ImagePolicy *config_image_policy = NULL;
MutableMode config_mutable = MUTABLE_NO;
- const char *section = image_class == IMAGE_SYSEXT ? "SysExt" : "ConfExt";
- const char *sections = image_class == IMAGE_SYSEXT ? "SysExt\0" : "ConfExt\0";
+ const char *section, *sections;
+ _cleanup_free_ char *config_file = NULL;
+ int r;
+
+ assert(c);
+
+ section = c->image_class == IMAGE_SYSEXT ? "SysExt" : "ConfExt";
+ sections = c->image_class == IMAGE_SYSEXT ? "SysExt\0" : "ConfExt\0";
const ConfigTableItem items[] = {
{ section, "Mutable", config_parse_mutable_mode, 0, &config_mutable },
{ section, "ImagePolicy", config_parse_image_policy, 0, &config_image_policy },
{}
};
- _cleanup_free_ char *config_file = NULL;
- int r;
- config_file = strjoin("systemd/", image_class_info[image_class].short_identifier, ".conf");
+ config_file = strjoin("systemd/", image_class_info[c->image_class].short_identifier, ".conf");
if (!config_file)
return log_oom();
r = config_parse_standard_file_with_dropins_full(
- arg_root,
+ c->root,
/* root_fd= */ -EBADF,
config_file,
sections,
if (r < 0)
return r;
- /* Because this runs after parse_argv we only overwrite when things aren't set yet. */
- if (!arg_mutable_set) {
- arg_mutable = config_mutable;
- arg_mutable_set = true;
+ /* Configuration has the lowest priority, so only fill in values that neither the environment nor
+ * argv initialized already. */
+ if (!c->mutable_set) {
+ c->mutable = config_mutable;
+ c->mutable_set = true;
}
- if (!arg_image_policy_set) {
- arg_image_policy = TAKE_PTR(config_image_policy);
- arg_image_policy_set = true;
+ if (!c->image_policy_set) {
+ c->image_policy = TAKE_PTR(config_image_policy);
+ c->image_policy_set = true;
+ c->image_policy_owned = true;
}
return 0;
}
+static int context_from_cmdline(Context *ret, ImageClass image_class) {
+ _cleanup_(context_done) Context c = CONTEXT_NULL;
+ int r;
+
+ assert(ret);
+
+ /* Start with command-line settings shared by both image classes. Class-specific environment and
+ * configuration are applied below, with argv values explicitly overriding both. */
+ c.image_class = image_class;
+ c.force = arg_force;
+ c.no_reload = arg_no_reload;
+ c.always_refresh = arg_always_refresh;
+ c.noexec = arg_noexec;
+
+ if (arg_root && strdup_to(&c.root, arg_root) < 0)
+ return log_oom();
+
+ r = parse_env_extension_hierarchies(&c.hierarchies, image_class_info[image_class].name_env);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine %s hierarchies: %m", image_class_info[image_class].short_identifier);
+
+ parse_env_image_class_config(&c);
+
+ if (arg_mutable_argv_set) {
+ c.mutable = arg_mutable;
+ c.mutable_set = true;
+ }
+
+ if (arg_image_policy_argv_set) {
+ c.image_policy = arg_image_policy;
+ c.image_policy_set = true;
+ c.image_policy_owned = false;
+ }
+
+ r = parse_config_file(&c);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse %s config file, ignoring: %m", image_class_info[image_class].short_identifier);
+
+ *ret = TAKE_GENERIC(c, Context, CONTEXT_NULL);
+ return 0;
+}
+
static int is_our_mount_point(
ImageClass image_class,
const char *p) {
}
static int get_extension_release_metadata(
- ImageClass image_class,
- char **hierarchies,
- bool no_reload,
+ const Context *c,
bool *ret_need_reload,
Set **ret_restart_units,
Set **ret_reload_or_restart_units) {
bool need_to_reload = false;
int r;
- if (no_reload || !isempty(arg_root)) {
+ assert(c);
+
+ if (c->no_reload || !isempty(c->root)) {
/* With --root= we'd be talking to the host service manager about extensions merged
* into a foreign root, which is never what we want. */
if (ret_need_reload)
return 0;
}
- STRV_FOREACH(p, hierarchies) {
+ STRV_FOREACH(p, c->hierarchies) {
_cleanup_free_ char *f = NULL, *buf = NULL, *resolved = NULL;
_cleanup_strv_free_ char **mounted_extensions = NULL;
- r = chase(*p, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
+ r = chase(*p, c->root, CHASE_PREFIX_ROOT, &resolved, NULL);
if (r == -ENOENT) {
- log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(arg_root), *p);
+ log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(c->root), *p);
continue;
}
if (r < 0) {
- log_warning_errno(r, "Failed to resolve path to hierarchy '%s%s': %m, ignoring.", strempty(arg_root), *p);
+ log_warning_errno(r, "Failed to resolve path to hierarchy '%s%s': %m, ignoring.", strempty(c->root), *p);
continue;
}
- r = is_our_mount_point(image_class, resolved);
+ r = is_our_mount_point(c->image_class, resolved);
if (r < 0)
return r;
if (!r)
continue;
- f = path_join(resolved, image_class_info[image_class].dot_directory_name, image_class_info[image_class].short_identifier_plural);
+ f = path_join(resolved, image_class_info[c->image_class].dot_directory_name, image_class_info[c->image_class].short_identifier_plural);
if (!f)
return log_oom();
_cleanup_strv_free_ char **extension_release = NULL;
const char *value;
- r = load_extension_release_pairs(arg_root, image_class, *extension, /* relax_extension_release_check= */ true, &extension_release);
+ r = load_extension_release_pairs(c->root, c->image_class, *extension, /* relax_extension_release_check= */ true, &extension_release);
if (r < 0) {
log_debug_errno(r, "Failed to parse extension-release metadata of %s, ignoring: %m", *extension);
continue;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(OverlayFSPaths *, overlayfs_paths_free);
-static int parse_env(void) {
- const char *env_var;
- int r;
-
- env_var = secure_getenv(image_class_info[arg_image_class].mode_env);
- if (env_var) {
- r = parse_mutable_mode(env_var);
- if (r < 0)
- log_warning("Failed to parse %s environment variable value '%s'. Ignoring.",
- image_class_info[arg_image_class].mode_env, env_var);
- else {
- arg_mutable = r;
- arg_mutable_set = true;
- }
- }
-
- env_var = secure_getenv(image_class_info[arg_image_class].opts_env);
- if (env_var)
- arg_overlayfs_mount_options = env_var;
-
- /* For debugging purposes it might make sense to do this for other hierarchies than /usr/ and
- * /opt/, but let's make that a hacker/debugging feature, i.e. env var instead of cmdline
- * switch. */
- r = parse_env_extension_hierarchies(&arg_hierarchies, image_class_info[arg_image_class].name_env);
- if (r < 0)
- return log_error_errno(r, "Failed to parse %s environment variable: %m", image_class_info[arg_image_class].name_env);
-
- return 0;
-}
-
-static int parse_image_class_parameter(sd_varlink *link, const char *value, ImageClass *image_class, char ***hierarchies) {
- _cleanup_strv_free_ char **h = NULL;
+static int parse_image_class_parameter(sd_varlink *link, const char *value, ImageClass *image_class) {
ImageClass c;
- int r;
assert(link);
assert(image_class);
if (!IN_SET(c, IMAGE_SYSEXT, IMAGE_CONFEXT))
return sd_varlink_error_invalid_parameter_name(link, "class");
- if (hierarchies) {
- r = parse_env_extension_hierarchies(&h, image_class_info[c].name_env);
- if (r < 0)
- return log_error_errno(r, "Failed to parse environment variable: %m");
-
- strv_free_and_replace(*hierarchies, h);
- }
-
*image_class = c;
return 0;
}
-static int resolve_hierarchy(const char *hierarchy, char **ret_resolved_hierarchy) {
+static int resolve_hierarchy(const Context *c, const char *hierarchy, char **ret_resolved_hierarchy) {
_cleanup_free_ char *resolved_path = NULL;
int r;
+ assert(c);
assert(hierarchy);
assert(ret_resolved_hierarchy);
- r = chase(hierarchy, arg_root, CHASE_PREFIX_ROOT, &resolved_path, NULL);
+ r = chase(hierarchy, c->root, CHASE_PREFIX_ROOT, &resolved_path, NULL);
if (r < 0 && r != -ENOENT)
return log_error_errno(r, "Failed to resolve hierarchy '%s': %m", hierarchy);
}
static int resolve_mutable_directory(
+ const Context *c,
const char *hierarchy,
mode_t hierarchy_mode,
const char *workspace,
char **ret_resolved_mutable_directory) {
_cleanup_free_ char *path = NULL, *resolved_path = NULL, *dir_name = NULL;
- const char *root = arg_root, *base = MUTABLE_EXTENSIONS_BASE_DIR;
+ const char *root, *base = MUTABLE_EXTENSIONS_BASE_DIR;
int r;
+ assert(c);
assert(hierarchy);
assert(ret_resolved_mutable_directory);
- if (arg_mutable == MUTABLE_NO) {
+ root = c->root;
+
+ if (c->mutable == MUTABLE_NO) {
log_debug("Mutability for hierarchy '%s' is disabled, not resolving mutable directory.", hierarchy);
*ret_resolved_mutable_directory = NULL;
return 0;
}
- if (IN_SET(arg_mutable, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT)) {
+ if (IN_SET(c->mutable, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT)) {
/* We create mutable directory inside the temporary tmpfs workspace, which is a fixed
- * location that ignores arg_root. */
+ * location that ignores the configured root. */
root = NULL;
base = workspace;
}
if (!path)
return log_oom();
- if (IN_SET(arg_mutable, MUTABLE_YES, MUTABLE_AUTO)) {
+ if (IN_SET(c->mutable, MUTABLE_YES, MUTABLE_AUTO)) {
/* If there already is a mutable directory, check if its mode matches hierarchy. Merged
* hierarchy will have the same mode as the mutable directory, so we want no surprising mode
* changes here. */
return r;
}
- if (IN_SET(arg_mutable, MUTABLE_YES, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT)) {
+ if (IN_SET(c->mutable, MUTABLE_YES, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT)) {
_cleanup_close_ int path_fd = -EBADF, chmod_fd = -EBADF;
/* This also creates, e.g., /var/lib/extensions.mutable/usr if needed and all parent
return 0;
}
-static int overlayfs_paths_new(const char *hierarchy, const char *workspace_path, OverlayFSPaths **ret_op) {
+static int overlayfs_paths_new(const Context *c, const char *hierarchy, const char *workspace_path, OverlayFSPaths **ret_op) {
_cleanup_free_ char *hierarchy_copy = NULL, *resolved_hierarchy = NULL, *resolved_mutable_directory = NULL;
mode_t hierarchy_mode;
int r;
- assert (hierarchy);
- assert (ret_op);
+ assert(c);
+ assert(hierarchy);
+ assert(ret_op);
hierarchy_copy = strdup(hierarchy);
if (!hierarchy_copy)
return log_oom();
- r = resolve_hierarchy(hierarchy, &resolved_hierarchy);
+ r = resolve_hierarchy(c, hierarchy, &resolved_hierarchy);
if (r < 0)
return r;
} else
hierarchy_mode = 0755;
- r = resolve_mutable_directory(hierarchy, hierarchy_mode, workspace_path, &resolved_mutable_directory);
+ r = resolve_mutable_directory(c, hierarchy, hierarchy_mode, workspace_path, &resolved_mutable_directory);
if (r < 0)
return r;
return 0;
}
-static int maybe_import_mutable_directory(OverlayFSPaths *op) {
+static int maybe_import_mutable_directory(const Context *c, OverlayFSPaths *op) {
int r;
+ assert(c);
assert(op);
/* If importing mutable layer and it actually exists and is not a hierarchy itself, add it just below
* the meta path */
- if (arg_mutable != MUTABLE_IMPORT || !op->resolved_mutable_directory)
+ if (c->mutable != MUTABLE_IMPORT || !op->resolved_mutable_directory)
return 0;
r = path_equal_or_inode_same_full(op->resolved_hierarchy, op->resolved_mutable_directory, 0);
return 0;
}
-static int maybe_import_ignored_mutable_directory(OverlayFSPaths *op) {
+static int maybe_import_ignored_mutable_directory(const Context *c, OverlayFSPaths *op) {
_cleanup_free_ char *dir_name = NULL, *path = NULL, *resolved_path = NULL;
int r;
+ assert(c);
assert(op);
/* If importing the ignored mutable layer and it actually exists and is not a hierarchy itself, add
* it just below the meta path */
- if (arg_mutable != MUTABLE_EPHEMERAL_IMPORT)
+ if (c->mutable != MUTABLE_EPHEMERAL_IMPORT)
return 0;
dir_name = hierarchy_as_single_path_component(op->hierarchy);
if (!path)
return log_oom();
- r = chase(path, arg_root, CHASE_PREFIX_ROOT, &resolved_path, NULL);
+ r = chase(path, c->root, CHASE_PREFIX_ROOT, &resolved_path, NULL);
if (r == -ENOENT) {
log_debug("Mutable directory for %s does not exist, not importing", op->hierarchy);
return 0;
return 0;
}
-static int determine_top_lower_dirs(OverlayFSPaths *op, const char *meta_path) {
+static int determine_top_lower_dirs(const Context *c, OverlayFSPaths *op, const char *meta_path) {
int r;
+ assert(c);
assert(op);
assert(meta_path);
if (r < 0)
return log_oom();
- r = maybe_import_mutable_directory(op);
+ r = maybe_import_mutable_directory(c, op);
if (r < 0)
return r;
- r = maybe_import_ignored_mutable_directory(op);
+ r = maybe_import_ignored_mutable_directory(c, op);
if (r < 0)
return r;
return 0;
}
-static int hierarchy_as_lower_dir(OverlayFSPaths *op) {
+static int hierarchy_as_lower_dir(const Context *c, OverlayFSPaths *op) {
int r;
/* return 0 if hierarchy should be used as lower dir, >0, if not */
+ assert(c);
assert(op);
if (!op->resolved_hierarchy) {
return 1;
}
- if (arg_mutable == MUTABLE_IMPORT) {
+ if (c->mutable == MUTABLE_IMPORT) {
log_debug("Mutability for host hierarchy '%s' is disabled, so host hierarchy will be a lowerdir", op->resolved_hierarchy);
return 0;
}
- if (arg_mutable == MUTABLE_EPHEMERAL_IMPORT) {
+ if (c->mutable == MUTABLE_EPHEMERAL_IMPORT) {
log_debug("Mutability for host hierarchy '%s' is ephemeral, so host hierarchy will be a lowerdir", op->resolved_hierarchy);
return 0;
}
return 0;
}
-static int determine_bottom_lower_dirs(OverlayFSPaths *op, dev_t *ret_backing_devnum) {
+static int determine_bottom_lower_dirs(const Context *c, OverlayFSPaths *op, dev_t *ret_backing_devnum) {
int r;
+ assert(c);
assert(op);
assert(ret_backing_devnum);
- r = hierarchy_as_lower_dir(op);
+ r = hierarchy_as_lower_dir(c, op);
if (r < 0)
return r;
if (!r) {
}
static int determine_lower_dirs(
+ const Context *c,
OverlayFSPaths *op,
char **paths,
const char *meta_path,
int r;
+ assert(c);
assert(op);
assert(meta_path);
assert(ret_backing_devnum);
- r = determine_top_lower_dirs(op, meta_path);
+ r = determine_top_lower_dirs(c, op, meta_path);
if (r < 0)
return r;
if (r < 0)
return r;
- r = determine_bottom_lower_dirs(op, ret_backing_devnum);
+ r = determine_bottom_lower_dirs(c, op, ret_backing_devnum);
if (r < 0)
return r;
return 0;
}
-static int determine_upper_dir(OverlayFSPaths *op) {
+static int determine_upper_dir(const Context *c, OverlayFSPaths *op) {
int r;
+ assert(c);
assert(op);
assert(!op->upper_dir);
- if (arg_mutable == MUTABLE_IMPORT) {
+ if (c->mutable == MUTABLE_IMPORT) {
log_debug("Mutability is disabled, there will be no upperdir for host hierarchy '%s'", op->hierarchy);
return 0;
}
return 0;
}
-static int determine_work_dir(OverlayFSPaths *op) {
+static int determine_work_dir(const Context *c, OverlayFSPaths *op) {
_cleanup_free_ char *work_dir = NULL;
int r;
+ assert(c);
assert(op);
assert(!op->work_dir);
if (!op->upper_dir)
return 0;
- if (arg_mutable == MUTABLE_IMPORT)
+ if (c->mutable == MUTABLE_IMPORT)
return 0;
r = work_dir_for_hierarchy(op->hierarchy, op->upper_dir, &work_dir);
return 0;
}
-static int write_work_dir_file(ImageClass image_class, const char *meta_path, const char *work_dir, const char* hierarchy) {
+static int write_work_dir_file(const Context *c, const char *meta_path, const char *work_dir, const char* hierarchy) {
_cleanup_free_ char *escaped_work_dir_in_root = NULL, *f = NULL;
char *work_dir_in_root = NULL;
int r;
+ assert(c);
assert(meta_path);
if (!work_dir)
return 0;
/* Do not store work dir path for ephemeral mode, it will be gone once this process is done. */
- if (IN_SET(arg_mutable, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT))
+ if (IN_SET(c->mutable, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT))
return 0;
- work_dir_in_root = path_startswith(work_dir, empty_to_root(arg_root));
+ work_dir_in_root = path_startswith(work_dir, empty_to_root(c->root));
if (!work_dir_in_root)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Workdir '%s' must not be outside root '%s'", work_dir, empty_to_root(arg_root));
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Workdir '%s' must not be outside root '%s'", work_dir, empty_to_root(c->root));
- f = path_join(meta_path, image_class_info[image_class].dot_directory_name, "work_dir");
+ f = path_join(meta_path, image_class_info[c->image_class].dot_directory_name, "work_dir");
if (!f)
return log_oom();
if (!escaped_work_dir_in_root)
return log_oom();
- _cleanup_free_ char *hierarchy_path = path_join(hierarchy, image_class_info[image_class].dot_directory_name, "work_dir");
+ _cleanup_free_ char *hierarchy_path = path_join(hierarchy, image_class_info[c->image_class].dot_directory_name, "work_dir");
if (!hierarchy_path)
return log_oom();
}
static int store_info_in_meta(
- ImageClass image_class,
+ const Context *c,
char **extensions,
const char *origin_content,
const char *meta_path,
int r;
+ assert(c);
assert(meta_path);
assert(overlay_path);
/* work_dir may be NULL */
- _cleanup_free_ char *f = path_join(meta_path, image_class_info[image_class].dot_directory_name);
+ _cleanup_free_ char *f = path_join(meta_path, image_class_info[c->image_class].dot_directory_name);
if (!f)
return log_oom();
if (r < 0)
return log_error_errno(r, "Failed to fix SELinux label for '%s': %m", hierarchy);
- r = write_extensions_file(image_class, extensions, meta_path, hierarchy);
+ r = write_extensions_file(c->image_class, extensions, meta_path, hierarchy);
if (r < 0)
return r;
- r = write_origin_file(image_class, origin_content, meta_path, hierarchy);
+ r = write_origin_file(c->image_class, origin_content, meta_path, hierarchy);
if (r < 0)
return r;
- r = write_dev_file(image_class, meta_path, overlay_path, hierarchy);
+ r = write_dev_file(c->image_class, meta_path, overlay_path, hierarchy);
if (r < 0)
return r;
- r = write_work_dir_file(image_class, meta_path, work_dir, hierarchy);
+ r = write_work_dir_file(c, meta_path, work_dir, hierarchy);
if (r < 0)
return r;
- r = write_backing_file(image_class, meta_path, overlay_path, hierarchy, backing);
+ r = write_backing_file(c->image_class, meta_path, overlay_path, hierarchy, backing);
if (r < 0)
return r;
}
static int merge_hierarchy(
- ImageClass image_class,
+ const Context *c,
const char *hierarchy,
- int noexec,
char **extensions,
char **paths,
const char *origin_content,
size_t extensions_used = 0;
int r;
+ assert(c);
assert(hierarchy);
assert(paths);
assert(meta_path);
if (r < 0)
return r;
- if (extensions_used == 0 && arg_mutable == MUTABLE_NO) /* No extension with files in this hierarchy? Then don't do anything. */
+ if (extensions_used == 0 && c->mutable == MUTABLE_NO) /* No extension with files in this hierarchy? Then don't do anything. */
return 0;
- r = overlayfs_paths_new(hierarchy, workspace_path, &op);
+ r = overlayfs_paths_new(c, hierarchy, workspace_path, &op);
if (r < 0)
return r;
dev_t backing;
- r = determine_lower_dirs(op, used_paths, meta_path, &backing);
+ r = determine_lower_dirs(c, op, used_paths, meta_path, &backing);
if (r < 0)
return r;
- r = determine_upper_dir(op);
+ r = determine_upper_dir(c, op);
if (r < 0)
return r;
- r = determine_work_dir(op);
+ r = determine_work_dir(c, op);
if (r < 0)
return r;
- r = mount_overlayfs_with_op(op, image_class, noexec, overlay_path, meta_path, arg_overlayfs_mount_options);
+ r = mount_overlayfs_with_op(op, c->image_class, c->noexec, overlay_path, meta_path, c->overlayfs_mount_options);
if (r < 0)
return r;
- r = store_info_in_meta(image_class, extensions, origin_content, meta_path, overlay_path, op->work_dir, op->hierarchy, backing);
+ r = store_info_in_meta(c, extensions, origin_content, meta_path, overlay_path, op->work_dir, op->hierarchy, backing);
if (r < 0)
return r;
- r = make_mounts_read_only(image_class, overlay_path, op->upper_dir && op->work_dir);
+ r = make_mounts_read_only(c->image_class, overlay_path, op->upper_dir && op->work_dir);
if (r < 0)
return r;
return strverscmp_improved(*a, *b);
}
-static const ImagePolicy* pick_image_policy(const Image *img) {
+static const ImagePolicy* pick_image_policy(const Context *c, const Image *img) {
+ assert(c);
assert(img);
assert(img->path);
/* Explicitly specified policy always wins */
- if (arg_image_policy)
- return arg_image_policy;
+ if (c->image_policy)
+ return c->image_policy;
/* If located in /.extra/ in the initrd, then it was placed there by systemd-stub, and was
* picked up from an untrusted ESP. Thus, require a stricter policy by default for them. (For the
* other directories we assume the appropriate level of trust was already established.)
* With --root= we default to the regular policy, though. (To change that, the check would need
- * to prepend (or cut away) arg_root.) */
+ * to prepend (or cut away) c->root.) */
- if (in_initrd() && !arg_root) {
+ if (in_initrd() && !c->root) {
if (path_startswith(img->path, "/.extra/sysext/"))
return &image_policy_sysext_strict;
if (path_startswith(img->path, "/.extra/global_sysext/"))
return image_class_info[img->class].default_image_policy;
}
-static int unmerge_hierarchy(ImageClass image_class, const char *p, const char *submounts_path) {
+static int unmerge_hierarchy(const Context *c, const char *p, const char *submounts_path) {
_cleanup_free_ char *dot_dir = NULL, *work_dir_info_file = NULL;
int n_unmerged = 0;
int r;
+ assert(c);
assert(p);
- dot_dir = path_join(p, image_class_info[image_class].dot_directory_name);
+ dot_dir = path_join(p, image_class_info[c->image_class].dot_directory_name);
if (!dot_dir)
return log_oom();
/* We only unmount /usr/ if it is a mount point and really one of ours, in order not to break
* systems where /usr/ is a mount point of its own already. */
- r = is_our_mount_point(image_class, p);
+ r = is_our_mount_point(c->image_class, p);
if (r < 0)
return r;
if (r == 0)
l = cunescape_length(escaped_work_dir_in_root, r, 0, &work_dir_in_root);
if (l < 0)
return log_error_errno(l, "Failed to unescape work directory path: %m");
- work_dir = path_join(arg_root, work_dir_in_root);
+ work_dir = path_join(c->root, work_dir_in_root);
if (!work_dir)
return log_oom();
}
}
static int unmerge_subprocess(
- ImageClass image_class,
- char **hierarchies,
+ const Context *c,
const char *workspace) {
int r, ret = 0;
+ assert(c);
assert(workspace);
assert(path_startswith(workspace, "/run/"));
if (r < 0)
return log_error_errno(r, "Failed to create '%s': %m", workspace);
- STRV_FOREACH(h, hierarchies) {
+ STRV_FOREACH(h, c->hierarchies) {
_cleanup_free_ char *submounts_path = NULL, *resolved = NULL;
submounts_path = path_join(workspace, "submounts", *h);
if (!submounts_path)
return log_oom();
- r = chase(*h, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
+ r = chase(*h, c->root, CHASE_PREFIX_ROOT, &resolved, NULL);
if (r == -ENOENT) {
- log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(arg_root), *h);
+ log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(c->root), *h);
continue;
}
if (r < 0) {
- RET_GATHER(ret, log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(arg_root), *h));
+ RET_GATHER(ret, log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(c->root), *h));
continue;
}
- r = unmerge_hierarchy(image_class, resolved, submounts_path);
+ r = unmerge_hierarchy(c, resolved, submounts_path);
if (r < 0) {
RET_GATHER(ret, r);
continue;
return ret;
}
-static int unmerge(
- ImageClass image_class,
- char **hierarchies,
- bool no_reload) {
+static int unmerge(const Context *c) {
_cleanup_set_free_ Set *units_to_restart = NULL, *units_to_reload_or_restart = NULL;
bool need_to_reload;
int r;
+ assert(c);
+
(void) DLOPEN_LIBMOUNT(LOG_DEBUG, required);
- r = get_extension_release_metadata(image_class, hierarchies, no_reload, &need_to_reload, &units_to_restart, &units_to_reload_or_restart);
+ r = get_extension_release_metadata(c, &need_to_reload, &units_to_restart, &units_to_reload_or_restart);
if (r < 0)
return r;
if (r == 0) {
/* Child with its own mount namespace */
- r = unmerge_subprocess(image_class, hierarchies, "/run/systemd/sysext");
+ r = unmerge_subprocess(c, "/run/systemd/sysext");
/* Our namespace ceases to exist here, also implicitly detaching all temporary mounts we
* created below /run. Nice! */
}
static int merge_subprocess(
- ImageClass image_class,
- char **hierarchies,
- bool force,
- bool always_refresh,
- int noexec,
+ const Context *c,
Hashmap *images,
const char *workspace) {
Image *img;
int r;
- if (!isempty(arg_root)) {
- r = chase(arg_root, /* root= */ NULL, CHASE_MUST_BE_DIRECTORY, &root_resolved, /* ret_fd= */ NULL);
+ assert(c);
+
+ if (!isempty(c->root)) {
+ r = chase(c->root, /* root= */ NULL, CHASE_MUST_BE_DIRECTORY, &root_resolved, /* ret_fd= */ NULL);
if (r < 0)
- return log_error_errno(r, "Failed to resolve --root='%s': %m", strempty(arg_root));
+ return log_error_errno(r, "Failed to resolve --root='%s': %m", strempty(c->root));
}
assert(path_startswith(workspace, "/run/"));
* but let the kernel do that entirely automatically, once our namespace dies. Note that this file
* system won't be visible to anyone but us, since we opened our own namespace and then made the
* /run/ hierarchy (which our workspace is contained in) MS_SLAVE, see above. */
- r = mount_nofollow_verbose(LOG_ERR, image_class_info[image_class].short_identifier, workspace, "tmpfs", 0, "mode=0700");
+ r = mount_nofollow_verbose(LOG_ERR, image_class_info[c->image_class].short_identifier, workspace, "tmpfs", 0, "mode=0700");
if (r < 0)
return r;
/* Acquire host OS release info, so that we can compare it with the extension's data */
r = parse_os_release(
- arg_root,
+ c->root,
"ID", &host_os_release_id,
"ID_LIKE", &host_os_release_id_like,
"VERSION_ID", &host_os_release_version_id,
- image_class_info[image_class].level_env, &host_os_release_api_level);
+ image_class_info[c->image_class].level_env, &host_os_release_api_level);
if (r < 0)
- return log_error_errno(r, "Failed to acquire 'os-release' data of OS tree '%s': %m", empty_to_root(arg_root));
+ return log_error_errno(r, "Failed to acquire 'os-release' data of OS tree '%s': %m", empty_to_root(c->root));
if (isempty(host_os_release_id))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"'ID' field not found or empty in 'os-release' data of OS tree '%s'.",
- empty_to_root(arg_root));
+ empty_to_root(c->root));
/* Let's now mount all images */
HASHMAP_FOREACH(img, images) {
_cleanup_free_ char *p = NULL, *path_without_root = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *verity_hash = NULL;
- p = path_join(workspace, image_class_info[image_class].short_identifier_plural, img->name);
+ p = path_join(workspace, image_class_info[c->image_class].short_identifier_plural, img->name);
if (!p)
return log_oom();
case IMAGE_DIRECTORY:
case IMAGE_SUBVOLUME:
- if (!force) {
+ if (!c->force) {
r = extension_has_forbidden_content(p);
if (r < 0)
return r;
if (verity_settings.data_path)
flags |= DISSECT_IMAGE_NO_PARTITION_TABLE;
- if (!force)
+ if (!c->force)
flags |= DISSECT_IMAGE_VALIDATE_OS_EXT;
r = loop_device_make_by_path(
d,
&verity_settings,
/* mount_options= */ NULL,
- pick_image_policy(img),
+ pick_image_policy(c, img),
/* image_filter= */ NULL,
flags,
&m);
return log_error_errno(r, "Failed to create origin verity entry for '%s': %m", img->name);
}
- r = dissected_image_decrypt(m, arg_root, /* passphrase= */ NULL, &verity_settings, pick_image_policy(img), flags);
+ r = dissected_image_decrypt(m, c->root, /* passphrase= */ NULL, &verity_settings, pick_image_policy(c, img), flags);
if (r < 0)
return r;
flags);
if (r < 0 && r != -ENOMEDIUM)
return r;
- if (r == -ENOMEDIUM && !force) {
+ if (r == -ENOMEDIUM && !c->force) {
n_ignored++;
continue;
}
assert_not_reached();
}
- if (force)
+ if (c->force)
log_debug("Force mode enabled, skipping version validation.");
else {
bool is_initrd;
- r = chase_and_access("/etc/initrd-release", arg_root, CHASE_PREFIX_ROOT, F_OK, /* ret_path= */ NULL);
+ r = chase_and_access("/etc/initrd-release", c->root, CHASE_PREFIX_ROOT, F_OK, /* ret_path= */ NULL);
if (r < 0 && r != -ENOENT)
return log_error_errno(r, "Failed to check for /etc/initrd-release: %m");
is_initrd = r >= 0;
host_os_release_version_id,
host_os_release_api_level,
is_initrd ? "initrd" : "system",
- image_extension_release(img, image_class),
- image_class);
+ image_extension_release(img, c->image_class),
+ c->image_class);
if (r < 0)
return r;
if (r == 0) {
/* Encode extension image origin to check if we can skip the refresh.
* It can also be used to provide more detail in "systemd-sysext status". */
- if (!isempty(arg_root)) {
+ if (!isempty(c->root)) {
const char *without_root = NULL;
without_root = path_startswith(img->path, root_resolved);
if (!isempty(without_root)) {
}
/* Nothing left? Then shortcut things */
- if (n_extensions == 0 && arg_mutable == MUTABLE_NO) {
+ if (n_extensions == 0 && c->mutable == MUTABLE_NO) {
if (n_ignored > 0)
log_info("No suitable extensions found (%u ignored due to incompatible image(s)).", n_ignored);
else
typesafe_qsort(extensions, n_extensions, strverscmp_improvedp);
typesafe_qsort(extensions_v, n_extensions, strverscmp_improvedp);
- STRV_FOREACH(h, hierarchies) {
+ STRV_FOREACH(h, c->hierarchies) {
_cleanup_(overlayfs_paths_freep) OverlayFSPaths *op = NULL;
_cleanup_free_ char *f = NULL, *buf = NULL, *resolved = NULL, *mutable_directory_without_root = NULL;
/* The origin file includes the backing directories for mutable overlays. */
- r = overlayfs_paths_new(*h, workspace, &op);
+ r = overlayfs_paths_new(c, *h, workspace, &op);
if (r < 0)
return r;
- if (op->resolved_mutable_directory && !isempty(arg_root)) {
+ if (op->resolved_mutable_directory && !isempty(c->root)) {
const char *without_root = NULL;
without_root = path_startswith(op->resolved_mutable_directory, root_resolved);
if (!isempty(without_root)) {
}
/* Find existing origin file for comparison. */
- r = chase(*h, arg_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, /* ret_fd= */ NULL);
+ r = chase(*h, c->root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, /* ret_fd= */ NULL);
if (r < 0)
- return log_error_errno(r, "Failed to resolve hierarchy '%s%s': %m", strempty(arg_root), *h);
+ return log_error_errno(r, "Failed to resolve hierarchy '%s%s': %m", strempty(c->root), *h);
- f = path_join(resolved, image_class_info[image_class].dot_directory_name, "origin");
+ f = path_join(resolved, image_class_info[c->image_class].dot_directory_name, "origin");
if (!f)
return log_oom();
- r = is_our_mount_point(image_class, resolved);
+ r = is_our_mount_point(c->image_class, resolved);
if (r < 0)
return r;
if (r == 0)
r = sd_json_buildo(&extensions_origin_json,
SD_JSON_BUILD_PAIR_OBJECT("mutable",
- SD_JSON_BUILD_PAIR_STRING("mode", mutable_mode_to_string(arg_mutable)),
+ SD_JSON_BUILD_PAIR_STRING("mode", mutable_mode_to_string(c->mutable)),
SD_JSON_BUILD_PAIR_CONDITION(!!mutable_dir_entries,
"mutableDirs",
SD_JSON_BUILD_VARIANT(mutable_dir_entries))),
- SD_JSON_BUILD_PAIR_CONDITION(!isempty(arg_overlayfs_mount_options),
+ SD_JSON_BUILD_PAIR_CONDITION(!isempty(c->overlayfs_mount_options),
"mountOptions",
- SD_JSON_BUILD_STRING(arg_overlayfs_mount_options)),
+ SD_JSON_BUILD_STRING(c->overlayfs_mount_options)),
SD_JSON_BUILD_PAIR_CONDITION(!!extensions_origin_entries,
"extensions",
SD_JSON_BUILD_VARIANT(extensions_origin_entries)));
/* This works well with unordered entries. */
if (sd_json_variant_equal(extensions_origin_json, old_origin_json)) {
- if (!always_refresh) {
+ if (!c->always_refresh) {
/* This only happens during refresh, not merge, thus talk about refresh here. */
log_info("Skipping extension refresh because no change was found, use --always-refresh=yes to always do a refresh.");
return MERGE_SKIP_REFRESH;
}
if (n_extensions == 0) {
- assert(arg_mutable != MUTABLE_NO);
+ assert(c->mutable != MUTABLE_NO);
log_info("No extensions found, proceeding in mutable mode.");
} else {
_cleanup_free_ char *buf = strv_join(extensions_v, "', '");
assert_se(img = hashmap_get(images, extensions[n_extensions - 1 - k]));
- p = path_join(workspace, image_class_info[image_class].short_identifier_plural, img->name);
+ p = path_join(workspace, image_class_info[c->image_class].short_identifier_plural, img->name);
if (!p)
return log_oom();
/* Let's now unmerge the status quo ante, since to build the new overlayfs we need a reference to the
* underlying fs. */
- STRV_FOREACH(h, hierarchies) {
+ STRV_FOREACH(h, c->hierarchies) {
_cleanup_free_ char *submounts_path = NULL, *resolved = NULL;
submounts_path = path_join(workspace, "submounts", *h);
if (!submounts_path)
return log_oom();
- r = chase(*h, arg_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, NULL);
+ r = chase(*h, c->root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to resolve hierarchy '%s%s': %m", strempty(arg_root), *h);
+ return log_error_errno(r, "Failed to resolve hierarchy '%s%s': %m", strempty(c->root), *h);
- r = unmerge_hierarchy(image_class, resolved, submounts_path);
+ r = unmerge_hierarchy(c, resolved, submounts_path);
if (r < 0)
return r;
if (r > 0)
}
/* Create overlayfs mounts for all hierarchies */
- STRV_FOREACH(h, hierarchies) {
+ STRV_FOREACH(h, c->hierarchies) {
_cleanup_free_ char *meta_path = NULL, *overlay_path = NULL, *merge_hierarchy_workspace = NULL, *submounts_path = NULL;
meta_path = path_join(workspace, "meta", *h); /* The place where to store metadata about this instance */
return log_oom();
r = merge_hierarchy(
- image_class,
+ c,
*h,
- noexec,
extensions,
paths,
extensions_origin_content,
}
/* And move them all into place. This is where things appear in the host namespace */
- STRV_FOREACH(h, hierarchies) {
+ STRV_FOREACH(h, c->hierarchies) {
_cleanup_free_ char *p = NULL, *resolved = NULL;
p = path_join(workspace, "overlay", *h);
if (r < 0)
return log_error_errno(r, "Failed to check if '%s' exists: %m", p);
- r = chase(*h, arg_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, NULL);
+ r = chase(*h, c->root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to resolve hierarchy '%s%s': %m", strempty(arg_root), *h);
+ return log_error_errno(r, "Failed to resolve hierarchy '%s%s': %m", strempty(c->root), *h);
r = mkdir_p(resolved, 0755);
if (r < 0)
return MERGE_MOUNTED;
}
-static int merge(ImageClass image_class,
- char **hierarchies,
- bool force,
- bool no_reload,
- bool always_refresh,
- int noexec,
- Hashmap *images) {
+static int merge(const Context *c, Hashmap *images) {
int r;
+ assert(c);
+
(void) DLOPEN_CRYPTSETUP(LOG_DEBUG, recommended);
(void) DLOPEN_LIBBLKID(LOG_DEBUG, required);
(void) DLOPEN_LIBMOUNT(LOG_DEBUG, required);
if (r == 0) {
/* Child with its own mount namespace */
- r = merge_subprocess(image_class, hierarchies, force, always_refresh, noexec, images, "/run/systemd/sysext");
+ r = merge_subprocess(c, images, "/run/systemd/sysext");
/* Our namespace ceases to exist here, also implicitly detaching all temporary mounts we
* created below /run. Nice! */
_cleanup_set_free_ Set *units_to_restart = NULL, *units_to_reload_or_restart = NULL;
bool need_to_reload;
- r = get_extension_release_metadata(image_class, hierarchies, no_reload, &need_to_reload, &units_to_restart, &units_to_reload_or_restart);
+ r = get_extension_release_metadata(c, &need_to_reload, &units_to_restart, &units_to_reload_or_restart);
if (r < 0)
return r;
if (need_to_reload) {
VERB_DEFAULT_NOARG(verb_status, "status", "Show current merge status (default)");
static int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(context_done) Context c = CONTEXT_NULL;
_cleanup_(table_unrefp) Table *t = NULL;
int r, ret = 0;
+ r = context_from_cmdline(&c, arg_image_class);
+ if (r < 0)
+ return r;
+
t = table_new("hierarchy", "extensions", "since");
if (!t)
return log_oom();
table_set_ersatz_string(t, TABLE_ERSATZ_DASH);
- STRV_FOREACH(p, arg_hierarchies) {
+ STRV_FOREACH(p, c.hierarchies) {
_cleanup_free_ char *resolved = NULL, *f = NULL, *buf = NULL;
_cleanup_strv_free_ char **l = NULL;
struct stat st;
- r = chase(*p, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
+ r = chase(*p, c.root, CHASE_PREFIX_ROOT, &resolved, NULL);
if (r == -ENOENT) {
- log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(arg_root), *p);
+ log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(c.root), *p);
continue;
}
if (r < 0) {
- log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(arg_root), *p);
+ log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(c.root), *p);
goto inner_fail;
}
- r = is_our_mount_point(arg_image_class, resolved);
+ r = is_our_mount_point(c.image_class, resolved);
if (r < 0)
goto inner_fail;
if (r == 0) {
continue;
}
- f = path_join(resolved, image_class_info[arg_image_class].dot_directory_name, image_class_info[arg_image_class].short_identifier_plural);
+ f = path_join(resolved, image_class_info[c.image_class].dot_directory_name, image_class_info[c.image_class].short_identifier_plural);
if (!f)
return log_oom();
return ret;
}
-static int image_discover_and_read_metadata(ImageClass image_class, Hashmap **ret_images) {
+static int image_discover_and_read_metadata(const Context *c, Hashmap **ret_images) {
_cleanup_hashmap_free_ Hashmap *images = NULL;
Image *img;
int r;
+ assert(c);
assert(ret_images);
- r = image_discover(RUNTIME_SCOPE_SYSTEM, image_class, arg_root, &images);
+ r = image_discover(RUNTIME_SCOPE_SYSTEM, c->image_class, c->root, &images);
if (r < 0)
return log_error_errno(r, "Failed to discover images: %m");
HASHMAP_FOREACH(img, images) {
- r = image_read_metadata(img, arg_root, image_class_info[image_class].default_image_policy, RUNTIME_SCOPE_SYSTEM);
+ r = image_read_metadata(img, c->root, image_class_info[c->image_class].default_image_policy, RUNTIME_SCOPE_SYSTEM);
if (r < 0)
return log_error_errno(r, "Failed to read metadata for image %s: %m", img->name);
}
return 0;
}
-static int look_for_merged_hierarchies(
- ImageClass image_class,
- char **hierarchies,
- const char **ret_which) {
+static int look_for_merged_hierarchies(const Context *c, const char **ret_which) {
int r;
+ assert(c);
assert(ret_which);
/* In merge mode fail if things are already merged. (In --refresh mode below we'll unmerge if we find
* things are already merged...) */
- STRV_FOREACH(p, hierarchies) {
+ STRV_FOREACH(p, c->hierarchies) {
_cleanup_free_ char *resolved = NULL;
- r = chase(*p, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
+ r = chase(*p, c->root, CHASE_PREFIX_ROOT, &resolved, NULL);
if (r == -ENOENT) {
- log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(arg_root), *p);
+ log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(c->root), *p);
continue;
}
if (r < 0)
- return log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(arg_root), *p);
+ return log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(c->root), *p);
- r = is_our_mount_point(image_class, resolved);
+ r = is_our_mount_point(c->image_class, resolved);
if (r < 0)
return r;
if (r > 0) {
VERB_NOARG(verb_merge, "merge", "Merge extensions into relevant hierarchies");
static int verb_merge(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(context_done) Context c = CONTEXT_NULL;
_cleanup_hashmap_free_ Hashmap *images = NULL;
const char *which;
int r;
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
- r = image_discover_and_read_metadata(arg_image_class, &images);
+ r = context_from_cmdline(&c, arg_image_class);
if (r < 0)
return r;
- r = look_for_merged_hierarchies(arg_image_class, arg_hierarchies, &which);
+ r = image_discover_and_read_metadata(&c, &images);
+ if (r < 0)
+ return r;
+
+ r = look_for_merged_hierarchies(&c, &which);
if (r < 0)
return r;
if (r > 0)
return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Hierarchy '%s' is already merged.", which);
- return merge(arg_image_class,
- arg_hierarchies,
- arg_force,
- arg_no_reload,
- arg_always_refresh,
- arg_noexec,
- images);
+ return merge(&c, images);
}
typedef struct MethodMergeParameters {
static int vl_method_merge(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
Hashmap **polkit_registry = ASSERT_PTR(userdata);
+ _cleanup_(context_done) Context c = CONTEXT_NULL;
_cleanup_hashmap_free_ Hashmap *images = NULL;
MethodMergeParameters p = {
.force = -1,
.always_refresh = -1,
.noexec = -1,
};
- _cleanup_strv_free_ char **hierarchies = NULL;
ImageClass image_class = arg_image_class;
- bool force, no_reload, always_refresh;
- int r, noexec;
+ int r;
assert(link);
if (r != 0)
return r;
- r = parse_image_class_parameter(link, p.class, &image_class, &hierarchies);
+ r = parse_image_class_parameter(link, p.class, &image_class);
if (r < 0)
return r;
- force = p.force >= 0 ? p.force : arg_force;
- no_reload = p.no_reload >= 0 ? p.no_reload : arg_no_reload;
- always_refresh = p.always_refresh >= 0 ? p.always_refresh : arg_always_refresh;
- noexec = p.noexec >= 0 ? p.noexec : arg_noexec;
+ r = context_from_cmdline(&c, image_class);
+ if (r < 0)
+ return r;
+
+ if (p.force >= 0)
+ c.force = p.force;
+ if (p.no_reload >= 0)
+ c.no_reload = p.no_reload;
+ if (p.always_refresh >= 0)
+ c.always_refresh = p.always_refresh;
+ if (p.noexec >= 0)
+ c.noexec = p.noexec;
r = varlink_verify_polkit_async(
link,
/* bus= */ NULL,
- image_class_info[image_class].polkit_rw_action_id,
+ image_class_info[c.image_class].polkit_rw_action_id,
(const char**) STRV_MAKE(
"verb", "merge",
- "force", one_zero(force),
- "noReload", one_zero(no_reload),
- "noexec", one_zero(noexec > 0)),
+ "force", one_zero(c.force),
+ "noReload", one_zero(c.no_reload),
+ "noexec", one_zero(c.noexec > 0)),
polkit_registry);
if (r <= 0)
return r;
- r = image_discover_and_read_metadata(image_class, &images);
+ r = image_discover_and_read_metadata(&c, &images);
if (r < 0)
return r;
const char *which;
- r = look_for_merged_hierarchies(
- image_class,
- hierarchies ?: arg_hierarchies,
- &which);
+ r = look_for_merged_hierarchies(&c, &which);
if (r < 0)
return r;
if (r > 0)
return sd_varlink_errorbo(link, "io.systemd.sysext.AlreadyMerged", SD_JSON_BUILD_PAIR_STRING("hierarchy", which));
- r = merge(image_class, hierarchies ?: arg_hierarchies, force, no_reload, always_refresh, noexec, images);
+ r = merge(&c, images);
if (r < 0)
return r;
VERB_NOARG(verb_unmerge, "unmerge", "Unmerge extensions from relevant hierarchies");
static int verb_unmerge(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(context_done) Context c = CONTEXT_NULL;
int r;
r = have_effective_cap(CAP_SYS_ADMIN);
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
- return unmerge(arg_image_class,
- arg_hierarchies,
- arg_no_reload);
+ r = context_from_cmdline(&c, arg_image_class);
+ if (r < 0)
+ return r;
+
+ return unmerge(&c);
}
typedef struct MethodUnmergeParameters {
.no_reload = -1,
};
Hashmap **polkit_registry = ASSERT_PTR(userdata);
- _cleanup_strv_free_ char **hierarchies = NULL;
+ _cleanup_(context_done) Context c = CONTEXT_NULL;
ImageClass image_class = arg_image_class;
- bool no_reload;
int r;
assert(link);
if (r != 0)
return r;
- no_reload = p.no_reload >= 0 ? p.no_reload : arg_no_reload;
+ r = parse_image_class_parameter(link, p.class, &image_class);
+ if (r < 0)
+ return r;
- r = parse_image_class_parameter(link, p.class, &image_class, &hierarchies);
+ r = context_from_cmdline(&c, image_class);
if (r < 0)
return r;
+ if (p.no_reload >= 0)
+ c.no_reload = p.no_reload;
+
r = varlink_verify_polkit_async(
link,
/* bus= */ NULL,
- image_class_info[image_class].polkit_rw_action_id,
+ image_class_info[c.image_class].polkit_rw_action_id,
(const char**) STRV_MAKE(
"verb", "unmerge",
- "noReload", one_zero(no_reload)),
+ "noReload", one_zero(c.no_reload)),
polkit_registry);
if (r <= 0)
return r;
- r = unmerge(image_class, hierarchies ?: arg_hierarchies, no_reload);
+ r = unmerge(&c);
if (r < 0)
return r;
return sd_varlink_reply(link, NULL);
}
-static int refresh(
- ImageClass image_class,
- char **hierarchies,
- bool force,
- bool no_reload,
- bool always_refresh,
- int noexec) {
+static int refresh(const Context *c) {
_cleanup_hashmap_free_ Hashmap *images = NULL;
int r;
- r = image_discover_and_read_metadata(image_class, &images);
+ assert(c);
+
+ r = image_discover_and_read_metadata(c, &images);
if (r < 0)
return r;
* implicitly unmounts any overlayfs placed there before. It also returns == 1 if there were
* no changes found to apply and the mount stays intact. Returns == 0 if it did nothing, i.e. no
* extension images found. In this case the old overlayfs remains in place if there was one. */
- r = merge(image_class, hierarchies, force, no_reload, always_refresh, noexec, images);
+ r = merge(c, images);
if (r < 0)
return r;
if (r == 0) /* No images found? Then unmerge. The goal of --refresh is after all that after having
* called there's a guarantee that the merge status matches the installed extensions. */
- r = unmerge(image_class, hierarchies, no_reload);
+ r = unmerge(c);
/* Net result here is that:
*
VERB_NOARG(verb_refresh, "refresh", "Unmerge/merge extensions again");
static int verb_refresh(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(context_done) Context c = CONTEXT_NULL;
int r;
r = have_effective_cap(CAP_SYS_ADMIN);
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
- return refresh(arg_image_class,
- arg_hierarchies,
- arg_force,
- arg_no_reload,
- arg_always_refresh,
- arg_noexec);
+ r = context_from_cmdline(&c, arg_image_class);
+ if (r < 0)
+ return r;
+
+ return refresh(&c);
}
static int vl_method_refresh(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ _cleanup_(context_done) Context c = CONTEXT_NULL;
MethodMergeParameters p = {
.force = -1,
.no_reload = -1,
.noexec = -1,
};
Hashmap **polkit_registry = ASSERT_PTR(userdata);
- _cleanup_strv_free_ char **hierarchies = NULL;
ImageClass image_class = arg_image_class;
- bool force, no_reload, always_refresh;
- int r, noexec;
+ int r;
assert(link);
if (r != 0)
return r;
- r = parse_image_class_parameter(link, p.class, &image_class, &hierarchies);
+ r = parse_image_class_parameter(link, p.class, &image_class);
+ if (r < 0)
+ return r;
+
+ r = context_from_cmdline(&c, image_class);
if (r < 0)
return r;
- force = p.force >= 0 ? p.force : arg_force;
- no_reload = p.no_reload >= 0 ? p.no_reload : arg_no_reload;
- always_refresh = p.always_refresh >= 0 ? p.always_refresh : arg_always_refresh;
- noexec = p.noexec >= 0 ? p.noexec : arg_noexec;
+ if (p.force >= 0)
+ c.force = p.force;
+ if (p.no_reload >= 0)
+ c.no_reload = p.no_reload;
+ if (p.always_refresh >= 0)
+ c.always_refresh = p.always_refresh;
+ if (p.noexec >= 0)
+ c.noexec = p.noexec;
r = varlink_verify_polkit_async(
link,
/* bus= */ NULL,
- image_class_info[image_class].polkit_rw_action_id,
+ image_class_info[c.image_class].polkit_rw_action_id,
(const char**) STRV_MAKE(
"verb", "refresh",
- "force", one_zero(force),
- "noReload", one_zero(no_reload),
- "noexec", one_zero(noexec > 0)),
+ "force", one_zero(c.force),
+ "noReload", one_zero(c.no_reload),
+ "noexec", one_zero(c.noexec > 0)),
polkit_registry);
if (r <= 0)
return r;
- r = refresh(image_class, hierarchies ?: arg_hierarchies, force, no_reload, always_refresh, noexec);
+ r = refresh(&c);
if (r < 0)
return r;
}
static int refresh_class(ImageClass image_class) {
- _cleanup_strv_free_ char **hierarchies = NULL;
+ _cleanup_(context_done) Context c = CONTEXT_NULL;
int r;
- r = parse_env_extension_hierarchies(&hierarchies, image_class_info[image_class].name_env);
+ /* The sysupdate notification service is systemd-sysext, but it refreshes both sysexts and
+ * confexts. Apply the relevant class-specific environment and configuration for each refresh. */
+ r = context_from_cmdline(&c, image_class);
if (r < 0)
- return log_error_errno(r, "Failed to determine %s hierarchies: %m", image_class_info[image_class].short_identifier);
+ return r;
- return refresh(image_class, hierarchies, arg_force, arg_no_reload, arg_always_refresh, arg_noexec);
+ return refresh(&c);
}
static int vl_method_on_completed_update(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
return r;
ImageClass image_class = arg_image_class;
- r = parse_image_class_parameter(link, class, &image_class, NULL);
+ r = parse_image_class_parameter(link, class, &image_class);
if (r < 0)
return r;
if (r < 0)
return log_error_errno(r, "Failed to parse argument to --mutable=: %s", opts.arg);
arg_mutable = r;
- arg_mutable_set = true;
+ arg_mutable_argv_set = true;
break;
OPTION_LONG("image-policy", "POLICY", "Specify disk image dissection policy"):
r = parse_image_policy_argument(opts.arg, &arg_image_policy);
if (r < 0)
return r;
- /* When the CLI flag is given we initialize even if NULL
- * so that the config file entry won't overwrite it */
- arg_image_policy_set = true;
+ arg_image_policy_argv_set = true;
break;
OPTION_LONG("noexec", "BOOL", "Whether to mount extension overlay with noexec"):
arg_image_class = invoked_as(argv, "systemd-confext") ? IMAGE_CONFEXT : IMAGE_SYSEXT;
- /* Parse environment variables first */
- r = parse_env();
- if (r < 0)
- return r;
-
/* Parse command line */
r = parse_argv(argc, argv, &args);
if (r <= 0)
return 0;
}
- /* Parse configuration file after argv because it needs --root=.
- * The config entries will not overwrite values set already by
- * env/argv because we track initialization. */
- r = parse_config_file(arg_image_class);
- if (r < 0)
- log_warning_errno(r, "Failed to parse global config file, ignoring: %m");
-
if (arg_varlink) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
_cleanup_hashmap_free_ Hashmap *polkit_registry = NULL;