<xi:include href="version-info.xml" xpointer="v254"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--system</option></term>
+ <term><option>--user</option></term>
+
+ <listitem><para>When used together with <option>--discover</option> controls whether to search for
+ images installed system-wide or in the user's directories in <varname>$HOME</varname>. If neither
+ switch is specified, will search within both scopes.</para>
+
+ <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="image-policy-open" />
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="no-legend" />
local cur=${COMP_WORDS[COMP_CWORD]} prev_1=${COMP_WORDS[COMP_CWORD-1]} prev_2=${COMP_WORDS[COMP_CWORD-2]} words cword
local -A OPTS=(
[STANDALONE]='-h --help --version
+ --user
+ --system
--discover
--no-pager
--no-legend
static ImagePolicy *arg_image_policy = NULL;
static bool arg_mtree_hash = true;
static bool arg_via_service = false;
+static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
" Generate JSON output\n"
" --loop-ref=NAME Set reference string for loopback device\n"
" --mtree-hash=BOOL Whether to include SHA256 hash in the mtree output\n"
+ " --user Discover user images\n"
+ " --system Discover system images\n"
"\n%3$sCommands:%4$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
ARG_VALIDATE,
ARG_MTREE_HASH,
ARG_MAKE_ARCHIVE,
+ ARG_SYSTEM,
+ ARG_USER,
};
static const struct option options[] = {
{ "validate", no_argument, NULL, ARG_VALIDATE },
{ "mtree-hash", required_argument, NULL, ARG_MTREE_HASH },
{ "make-archive", no_argument, NULL, ARG_MAKE_ARCHIVE },
+ { "system", no_argument, NULL, ARG_SYSTEM },
+ { "user", no_argument, NULL, ARG_USER },
{}
};
_cleanup_free_ char **buf = NULL; /* we use free(), not strv_free() here, as we don't copy the strings here */
+ bool system_scope_requested = false, user_scope_requested = false;
int c, r;
assert(argc >= 0);
break;
case ARG_MAKE_ARCHIVE:
-
r = dlopen_libarchive();
if (r < 0)
return log_error_errno(r, "Archive support not available (compiled without libarchive, or libarchive not installed?).");
arg_action = ACTION_MAKE_ARCHIVE;
break;
+ case ARG_SYSTEM:
+ system_scope_requested = true;
+ break;
+
+ case ARG_USER:
+ user_scope_requested = true;
+ break;
+
case '?':
return -EINVAL;
}
}
+ if (system_scope_requested || user_scope_requested)
+ arg_runtime_scope = system_scope_requested && user_scope_requested ? _RUNTIME_SCOPE_INVALID :
+ system_scope_requested ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
+
switch (arg_action) {
case ACTION_DISSECT:
return log_oom();
for (ImageClass cl = 0; cl < _IMAGE_CLASS_MAX; cl++) {
- r = image_discover(cl, NULL, images);
+ r = image_discover(arg_runtime_scope, cl, NULL, images);
if (r < 0)
return log_error_errno(r, "Failed to discover images: %m");
}
static ImportCompressType arg_compress = IMPORT_COMPRESS_UNKNOWN;
static ImageClass arg_class = IMAGE_MACHINE;
+static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
static void determine_compression_from_filename(const char *p) {
local = argv[1];
if (image_name_is_valid(local)) {
- r = image_find(arg_class, local, NULL, &image);
+ r = image_find(arg_runtime_scope, arg_class, local, NULL, &image);
if (r == -ENOENT)
return log_error_errno(r, "Image %s not found.", local);
if (r < 0)
local = argv[1];
if (image_name_is_valid(local)) {
- r = image_find(arg_class, local, NULL, &image);
+ r = image_find(arg_runtime_scope, arg_class, local, NULL, &image);
if (r == -ENOENT)
return log_error_errno(r, "Image %s not found.", local);
if (r < 0)
static bool arg_direct = false;
static const char *arg_image_root = NULL;
static ImageClass arg_class = IMAGE_MACHINE;
+static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
typedef struct ProgressInfo {
RateLimit limit;
return log_oom();
if (!arg_force) {
- r = image_find(arg_class, local, NULL, NULL);
+ r = image_find(arg_runtime_scope, arg_class, local, NULL, NULL);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
static ImportFlags arg_import_flags = IMPORT_BTRFS_SUBVOL | IMPORT_BTRFS_QUOTA | IMPORT_CONVERT_QCOW2 | IMPORT_SYNC;
static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
static ImageClass arg_class = IMAGE_MACHINE;
+static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
static int normalize_local(const char *local, char **ret) {
_cleanup_free_ char *ll = NULL;
local = "imported";
if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
- r = image_find(arg_class, local, NULL, NULL);
+ r = image_find(arg_runtime_scope, arg_class, local, NULL, NULL);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
bool use_btrfs_subvol;
bool use_btrfs_quota;
+
+ RuntimeScope runtime_scope; /* for now: always RUNTIME_SCOPE_SYSTEM */
};
#define TRANSFERS_MAX 64
*m = (Manager) {
.use_btrfs_subvol = true,
.use_btrfs_quota = true,
+ .runtime_scope = RUNTIME_SCOPE_SYSTEM,
};
r = sd_event_default(&m->event);
static int method_list_images(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
ImageClass class = _IMAGE_CLASS_INVALID;
+ Manager *m = ASSERT_PTR(userdata);
int r;
assert(msg);
if (!h)
return -ENOMEM;
- r = image_discover(c, /* root= */ NULL, h);
+ r = image_discover(m->runtime_scope, c, /* root= */ NULL, h);
if (r < 0) {
if (class >= 0)
return r;
static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
static char *arg_checksum = NULL;
static ImageClass arg_class = IMAGE_MACHINE;
+static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
STATIC_DESTRUCTOR_REGISTER(arg_checksum, freep);
local);
if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
- r = image_find(arg_class, local, NULL, NULL);
+ r = image_find(arg_runtime_scope, arg_class, local, NULL, NULL);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
if (r == 0) {
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
- r = image_clone(image, new_name, read_only);
+ r = image_clone(image, new_name, read_only, m->runtime_scope);
report_errno_and_exit(errno_pipe_fd[1], r);
}
static int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
_cleanup_hashmap_free_ Hashmap *images = NULL;
_cleanup_strv_free_ char **l = NULL;
+ Manager *m = ASSERT_PTR(userdata);
Image *image;
int r;
if (!images)
return -ENOMEM;
- r = image_discover(IMAGE_MACHINE, NULL, images);
+ r = image_discover(m->runtime_scope, IMAGE_MACHINE, NULL, images);
if (r < 0)
return r;
return log_debug_errno(r, "Failed to fork: %m");
if (r == 0) {
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
- r = image_clone(image, p.new_name, p.read_only > 0);
+ r = image_clone(image, p.new_name, p.read_only > 0, manager->runtime_scope);
report_errno_and_exit(errno_pipe_fd[1], r);
}
return log_debug_errno(r, "Failed to enable source: %m") ;
_cleanup_(image_unrefp) Image *image = NULL;
- r = image_find(IMAGE_MACHINE, name, NULL, &image);
+ r = image_find(m->runtime_scope, IMAGE_MACHINE, name, NULL, &image);
if (r < 0)
return log_debug_errno(r, "Failed to find image: %m");
/* The image is cached with its name, hence it is necessary to remove from the cache before renaming. */
assert_se(hashmap_remove_value(m->image_cache, image->name, image));
- r = image_rename(image, new_name);
+ r = image_rename(image, new_name, m->runtime_scope);
if (r < 0) {
image = image_unref(image);
return r;
if (r < 0)
return r;
- r = image_find(IMAGE_MACHINE, name, NULL, NULL);
+ r = image_find(m->runtime_scope, IMAGE_MACHINE, name, NULL, NULL);
if (r == -ENOENT)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
if (r < 0)
if (!images)
return -ENOMEM;
- r = image_discover(IMAGE_MACHINE, NULL, images);
+ r = image_discover(m->runtime_scope, IMAGE_MACHINE, NULL, images);
if (r < 0)
return r;
goto child_fail;
}
- r = image_discover(IMAGE_MACHINE, NULL, images);
+ r = image_discover(m->runtime_scope, IMAGE_MACHINE, NULL, images);
if (r < 0)
goto child_fail;
}
static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ Manager *m = ASSERT_PTR(userdata);
struct params {
const char *image_name;
AcquireMetadata acquire_metadata;
if (!image_name_is_valid(p.image_name))
return sd_varlink_error_invalid_parameter_name(link, "name");
- r = image_find(IMAGE_MACHINE, p.image_name, /* root = */ NULL, &found);
+ r = image_find(m->runtime_scope, IMAGE_MACHINE, p.image_name, /* root = */ NULL, &found);
if (r == -ENOENT)
return sd_varlink_error(link, "io.systemd.MachineImage.NoSuchImage", NULL);
if (r < 0)
if (!images)
return -ENOMEM;
- r = image_discover(IMAGE_MACHINE, /* root = */ NULL, images);
+ r = image_discover(m->runtime_scope, IMAGE_MACHINE, /* root = */ NULL, images);
if (r < 0)
return log_debug_errno(r, "Failed to discover images: %m");
assert(ret);
- m = new0(Manager, 1);
+ m = new(Manager, 1);
if (!m)
return -ENOMEM;
+ *m = (Manager) {
+ .runtime_scope = RUNTIME_SCOPE_SYSTEM,
+ };
+
m->machines = hashmap_new(&machine_hash_ops);
if (!m->machines)
return -ENOMEM;
sd_varlink_server *varlink_userdb_server;
sd_varlink_server *varlink_machine_server;
+
+ RuntimeScope runtime_scope; /* for now: always RUNTIME_SCOPE_SYSTEM */
};
int manager_add_machine(Manager *m, const char *name, Machine **ret);
if (arg_machine) {
_cleanup_(image_unrefp) Image *i = NULL;
- r = image_find(IMAGE_MACHINE, arg_machine, NULL, &i);
+ r = image_find(arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER,
+ IMAGE_MACHINE, arg_machine, NULL, &i);
if (r == -ENOENT)
return log_error_errno(r, "No image for machine '%s'.", arg_machine);
if (r < 0)
PortableMetadata, portable_metadata_unref);
static int extract_now(
+ RuntimeScope scope,
const char *where,
char **matches,
const char *image_name,
* parent. To handle both cases in one call this function also gets a 'socket_fd' parameter, which when >= 0 is
* used to send the data to the parent. */
+ assert(scope < _RUNTIME_SCOPE_MAX);
assert(where);
/* First, find os-release/extension-release and send it upstream (or just save it). */
/* Then, send unit file data to the parent (or/and add it to the hashmap). For that we use our usual unit
* discovery logic. Note that we force looking inside of /lib/systemd/system/ for units too, as the
* image might have a legacy split-usr layout. */
- r = lookup_paths_init(&paths, RUNTIME_SCOPE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, where);
+ r = lookup_paths_init(&paths, scope, LOOKUP_PATHS_SPLIT_USR, where);
if (r < 0)
return log_debug_errno(r, "Failed to acquire lookup paths: %m");
}
static int portable_extract_by_path(
+ RuntimeScope scope,
const char *path,
bool path_is_extension,
bool relax_extension_release_check,
if (r < 0)
return log_error_errno(r, "Failed to extract image name from path '%s': %m", path);
- r = extract_now(path, matches, image_name, path_is_extension, /* relax_extension_release_check= */ false, -1, &os_release, &unit_files);
+ r = extract_now(scope, path, matches, image_name, path_is_extension, /* relax_extension_release_check= */ false, -1, &os_release, &unit_files);
if (r < 0)
return r;
goto child_finish;
}
- r = extract_now(tmpdir, matches, m->image_name, path_is_extension, relax_extension_release_check, seq[1], NULL, NULL);
+ r = extract_now(scope, tmpdir, matches, m->image_name, path_is_extension, relax_extension_release_check, seq[1], NULL, NULL);
child_finish:
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
static int extract_image_and_extensions(
+ RuntimeScope scope,
const char *name_or_path,
char **matches,
char **extension_image_paths,
name_or_path = result.path;
}
- r = image_find_harder(IMAGE_PORTABLE, name_or_path, /* root= */ NULL, &image);
+ r = image_find_harder(scope, IMAGE_PORTABLE, name_or_path, /* root= */ NULL, &image);
if (r < 0)
return r;
path = ext_result.path;
}
- r = image_find_harder(IMAGE_PORTABLE, path, NULL, &new);
+ r = image_find_harder(scope, IMAGE_PORTABLE, path, NULL, &new);
if (r < 0)
return r;
}
r = portable_extract_by_path(
+ scope,
image->path,
/* path_is_extension= */ false,
/* relax_extension_release_check= */ false,
const char *e;
r = portable_extract_by_path(
+ scope,
ext->path,
/* path_is_extension= */ true,
relax_extension_release_check,
}
int portable_extract(
+ RuntimeScope scope,
const char *name_or_path,
char **matches,
char **extension_image_paths,
assert(name_or_path);
r = extract_image_and_extensions(
+ scope,
name_or_path,
matches,
extension_image_paths,
}
static int install_image(
+ RuntimeScope scope,
const char *image_path,
PortableFlags flags,
PortableChange **changes,
_cleanup_free_ char *target = NULL;
int r;
+ assert(scope < _RUNTIME_SCOPE_MAX);
assert(image_path);
/* If the image is outside of the image search also link it into it, so that it can be found with
* short image names and is listed among the images. If we are operating in mixed mode, the image is
* copied instead. */
- if (image_in_search_path(IMAGE_PORTABLE, NULL, image_path))
+ if (image_in_search_path(scope, IMAGE_PORTABLE, NULL, image_path))
return 0;
r = image_target_path(image_path, flags, &target);
}
static int install_image_and_extensions(
+ RuntimeScope scope,
const Image *image,
OrderedHashmap *extension_images,
PortableFlags flags,
assert(image);
ORDERED_HASHMAP_FOREACH(ext, extension_images) {
- r = install_image(ext->path, flags, changes, n_changes);
+ r = install_image(scope, ext->path, flags, changes, n_changes);
if (r < 0)
return r;
}
- r = install_image(image->path, flags, changes, n_changes);
+ r = install_image(scope, image->path, flags, changes, n_changes);
if (r < 0)
return r;
}
int portable_attach(
+ RuntimeScope scope,
sd_bus *bus,
const char *name_or_path,
char **matches,
PortableMetadata *item;
int r;
+ assert(scope < _RUNTIME_SCOPE_MAX);
+
r = extract_image_and_extensions(
+ scope,
name_or_path,
matches,
extension_image_paths,
strempty(extensions_joined));
}
- r = lookup_paths_init(&paths, RUNTIME_SCOPE_SYSTEM, /* flags= */ 0, NULL);
+ r = lookup_paths_init(&paths, scope, /* flags= */ 0, NULL);
if (r < 0)
return r;
if (!FLAGS_SET(flags, PORTABLE_REATTACH) && !FLAGS_SET(flags, PORTABLE_FORCE_ATTACH))
HASHMAP_FOREACH(item, unit_files) {
- r = unit_file_exists(RUNTIME_SCOPE_SYSTEM, &paths, item->name);
+ r = unit_file_exists(scope, &paths, item->name);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to determine whether unit '%s' exists on the host: %m", item->name);
if (r > 0)
/* We don't care too much for the image symlink/copy, it's just a convenience thing, it's not necessary for
* proper operation otherwise. */
- (void) install_image_and_extensions(image, extension_images, flags, changes, n_changes);
+ (void) install_image_and_extensions(scope, image, extension_images, flags, changes, n_changes);
log_portable_verb(
"attached",
}
int portable_detach(
+ RuntimeScope scope,
sd_bus *bus,
const char *name_or_path,
char **extension_image_paths,
_cleanup_free_ char *extensions = NULL;
_cleanup_closedir_ DIR *d = NULL;
const char *where, *item;
- int ret = 0;
- int r;
+ int r, ret = 0;
+ assert(scope < _RUNTIME_SCOPE_MAX);
assert(name_or_path);
- r = lookup_paths_init(&paths, RUNTIME_SCOPE_SYSTEM, /* flags= */ 0, NULL);
+ r = lookup_paths_init(&paths, scope, /* flags= */ 0, NULL);
if (r < 0)
return r;
if (r == 0)
break;
- if (path_is_absolute(image) && !image_in_search_path(IMAGE_PORTABLE, NULL, image)) {
+ if (path_is_absolute(image) && !image_in_search_path(scope, IMAGE_PORTABLE, NULL, image)) {
r = set_ensure_consume(&markers, &path_hash_ops_free, TAKE_PTR(image));
if (r < 0)
return r;
}
static int portable_get_state_internal(
+ RuntimeScope scope,
sd_bus *bus,
const char *name_or_path,
char **extension_image_paths,
const char *where;
int r;
+ assert(scope < _RUNTIME_SCOPE_MAX);
assert(name_or_path);
assert(ret);
- r = lookup_paths_init(&paths, RUNTIME_SCOPE_SYSTEM, /* flags= */ 0, NULL);
+ r = lookup_paths_init(&paths, scope, /* flags= */ 0, NULL);
if (r < 0)
return r;
if (r == 0)
continue;
- r = unit_file_lookup_state(RUNTIME_SCOPE_SYSTEM, &paths, de->d_name, &state);
+ r = unit_file_lookup_state(scope, &paths, de->d_name, &state);
if (r < 0)
return log_debug_errno(r, "Failed to determine unit file state of '%s': %m", de->d_name);
if (!IN_SET(state, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED, UNIT_FILE_LINKED_RUNTIME))
}
int portable_get_state(
+ RuntimeScope scope,
sd_bus *bus,
const char *name_or_path,
char **extension_image_paths,
/* We look for matching units twice: once in the regular directories, and once in the runtime directories — but
* the latter only if we didn't find anything in the former. */
- r = portable_get_state_internal(bus, name_or_path, extension_image_paths, flags & ~PORTABLE_RUNTIME, &state, error);
+ r = portable_get_state_internal(
+ scope,
+ bus,
+ name_or_path,
+ extension_image_paths,
+ flags & ~PORTABLE_RUNTIME,
+ &state,
+ error);
if (r < 0)
return r;
if (state == PORTABLE_DETACHED) {
- r = portable_get_state_internal(bus, name_or_path, extension_image_paths, flags | PORTABLE_RUNTIME, &state, error);
+ r = portable_get_state_internal(scope, bus, name_or_path, extension_image_paths, flags | PORTABLE_RUNTIME, &state, error);
if (r < 0)
return r;
}
#include "dissect-image.h"
#include "hashmap.h"
#include "macro.h"
+#include "runtime-scope.h"
#include "set.h"
#include "string-util.h"
int portable_metadata_hashmap_to_sorted_array(Hashmap *unit_files, PortableMetadata ***ret);
-int portable_extract(const char *image, char **matches, char **extension_image_paths, const ImagePolicy *image_policy, PortableFlags flags, PortableMetadata **ret_os_release, OrderedHashmap **ret_extension_releases, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
+int portable_extract(RuntimeScope scope, const char *image, char **matches, char **extension_image_paths, const ImagePolicy *image_policy, PortableFlags flags, PortableMetadata **ret_os_release, OrderedHashmap **ret_extension_releases, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
-int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, char **extension_images, const ImagePolicy* image_policy, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
-int portable_detach(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
+int portable_attach(RuntimeScope scope, sd_bus *bus, const char *name_or_path, char **matches, const char *profile, char **extension_images, const ImagePolicy* image_policy, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
+int portable_detach(RuntimeScope scope, sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
-int portable_get_state(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableState *ret, sd_bus_error *error);
+int portable_get_state(RuntimeScope scope, sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableState *ret, sd_bus_error *error);
int portable_get_profiles(char ***ret);
return r;
r = portable_get_state(
+ m->runtime_scope,
sd_bus_message_get_bus(message),
image->path,
NULL,
static int method_get_image_state(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_strv_free_ char **extension_images = NULL;
+ Manager *m = ASSERT_PTR(userdata);
const char *name_or_path;
PortableState state;
int r;
}
r = portable_get_state(
+ m->runtime_scope,
sd_bus_message_get_bus(message),
name_or_path,
extension_images,
return 1; /* Will call us back */
r = portable_detach(
+ m->runtime_scope,
sd_bus_message_get_bus(message),
name_or_path,
extension_images,
assert(name_or_path || image);
assert(message);
- if (!m) {
- assert(image);
- m = image->userdata;
- }
+ if (!m)
+ m = ASSERT_PTR(ASSERT_PTR(image)->userdata);
bool have_exti = sd_bus_message_is_method_call(message, NULL, "GetImageMetadataWithExtensions") ||
sd_bus_message_is_method_call(message, NULL, "GetMetadataWithExtensions");
return 1;
r = portable_extract(
+ m->runtime_scope,
image->path,
matches,
extension_images,
_cleanup_strv_free_ char **extension_images = NULL;
Image *image = ASSERT_PTR(userdata);
+ Manager *m = ASSERT_PTR(image->userdata);
PortableState state;
int r;
}
r = portable_get_state(
+ m->runtime_scope,
sd_bus_message_get_bus(message),
image->path,
extension_images,
return 1;
r = portable_attach(
+ m->runtime_scope,
sd_bus_message_get_bus(message),
image->path,
matches,
return 1; /* Will call us back */
r = portable_detach(
+ m->runtime_scope,
sd_bus_message_get_bus(message),
image->path,
extension_images,
return 1; /* Will call us back */
r = portable_get_state(
+ m->runtime_scope,
sd_bus_message_get_bus(message),
image->path,
NULL,
return 1;
r = portable_detach(
+ m->runtime_scope,
sd_bus_message_get_bus(message),
image->path,
extension_images,
return r;
r = portable_attach(
+ m->runtime_scope,
sd_bus_message_get_bus(message),
image->path,
matches,
if (image_name_is_valid(name_or_path)) {
/* If it's a short name, let's search for it */
- r = image_find(IMAGE_PORTABLE, name_or_path, NULL, &loaded);
+ r = image_find(m->runtime_scope, IMAGE_PORTABLE, name_or_path, NULL, &loaded);
if (r == -ENOENT)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE,
"No image '%s' found.", name_or_path);
/* A wrapper around image_discover() (for finding images in search path) and portable_discover_attached() (for
* finding attached images). */
- r = image_discover(IMAGE_PORTABLE, NULL, images);
+ r = image_discover(m->runtime_scope, IMAGE_PORTABLE, NULL, images);
if (r < 0)
return r;
assert(ret);
- m = new0(Manager, 1);
+ m = new(Manager, 1);
if (!m)
return -ENOMEM;
+ *m = (Manager) {
+ .runtime_scope = RUNTIME_SCOPE_SYSTEM,
+ };
+
r = sd_event_default(&m->event);
if (r < 0)
return r;
#include "bus-object.h"
#include "hashmap.h"
#include "list.h"
+#include "runtime-scope.h"
typedef struct Manager Manager;
LIST_HEAD(Operation, operations);
unsigned n_operations;
+
+ RuntimeScope runtime_scope; /* for now always RUNTIME_SCOPE_SYSTEM */
};
extern const BusObjectImplementation manager_object;
#include <sys/stat.h>
#include <unistd.h>
+#include "sd-path.h"
+
#include "alloc-util.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
return -EMEDIUMTYPE;
}
-static const char *pick_image_search_path(ImageClass class) {
- if (class < 0 || class >= _IMAGE_CLASS_MAX)
- return NULL;
+static int pick_image_search_path(
+ RuntimeScope scope,
+ ImageClass class,
+ char ***ret) {
+
+ int r;
+
+ assert(scope < _RUNTIME_SCOPE_MAX && scope != RUNTIME_SCOPE_GLOBAL);
+ assert(class < _IMAGE_CLASS_MAX);
+ assert(ret);
+
+ if (class < 0) {
+ *ret = NULL;
+ return 0;
+ }
+
+ if (scope < 0) {
+ _cleanup_strv_free_ char **a = NULL, **b = NULL;
+
+ r = pick_image_search_path(RUNTIME_SCOPE_USER, class, &a);
+ if (r < 0)
+ return r;
+
+ r = pick_image_search_path(RUNTIME_SCOPE_SYSTEM, class, &b);
+ if (r < 0)
+ return r;
+
+ r = strv_extend_strv(&a, b, /* filter_duplicates= */ false);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(a);
+ return 0;
+ }
+
+ switch (scope) {
+
+ case RUNTIME_SCOPE_SYSTEM: {
+ const char *ns;
+ /* Use the initrd search path if there is one, otherwise use the common one */
+ ns = in_initrd() && image_search_path_initrd[class] ?
+ image_search_path_initrd[class] :
+ image_search_path[class];
+ if (!ns)
+ break;
+
+ _cleanup_strv_free_ char **search = strv_split_nulstr(ns);
+ if (!search)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(search);
+ return 0;
+ }
+
+ case RUNTIME_SCOPE_USER: {
+ if (class != IMAGE_MACHINE)
+ break;
+
+ static const uint64_t dirs[] = {
+ SD_PATH_USER_RUNTIME,
+ SD_PATH_USER_STATE_PRIVATE,
+ SD_PATH_USER_LIBRARY_PRIVATE,
+ };
- /* Use the initrd search path if there is one, otherwise use the common one */
- return in_initrd() && image_search_path_initrd[class] ? image_search_path_initrd[class] : image_search_path[class];
+ _cleanup_strv_free_ char **search = NULL;
+ FOREACH_ELEMENT(d, dirs) {
+ _cleanup_free_ char *p = NULL;
+
+ r = sd_path_lookup(*d, "machines", &p);
+ if (r == -ENXIO) /* No XDG_RUNTIME_DIR set */
+ continue;
+ if (r < 0)
+ return r;
+
+ r = strv_consume(&search, TAKE_PTR(p));
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(search);
+ return 0;
+ }
+
+ default:
+ assert_not_reached();
+ }
+
+ *ret = NULL;
+ return 0;
}
static char **make_possible_filenames(ImageClass class, const char *image_name) {
return TAKE_PTR(l);
}
-int image_find(ImageClass class,
+int image_find(RuntimeScope scope,
+ ImageClass class,
const char *name,
const char *root,
Image **ret) {
* some root directory.) */
int open_flags = root ? O_NOFOLLOW : 0, r;
+ assert(scope < _RUNTIME_SCOPE_MAX && scope != RUNTIME_SCOPE_GLOBAL);
assert(class >= 0);
assert(class < _IMAGE_CLASS_MAX);
assert(name);
if (!names)
return -ENOMEM;
- NULSTR_FOREACH(path, pick_image_search_path(class)) {
+ _cleanup_strv_free_ char **search = NULL;
+ r = pick_image_search_path(scope, class, &search);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(path, search) {
_cleanup_free_ char *resolved = NULL;
_cleanup_closedir_ DIR *d = NULL;
- r = chase_and_opendir(path, root, CHASE_PREFIX_ROOT, &resolved, &d);
+ r = chase_and_opendir(*path, root, CHASE_PREFIX_ROOT, &resolved, &d);
if (r == -ENOENT)
continue;
if (r < 0)
}
}
- if (class == IMAGE_MACHINE && streq(name, ".host")) {
+ if (scope == RUNTIME_SCOPE_SYSTEM && class == IMAGE_MACHINE && streq(name, ".host")) {
r = image_make(class,
".host",
/* dir_fd= */ AT_FDCWD,
ret);
}
-int image_find_harder(ImageClass class, const char *name_or_path, const char *root, Image **ret) {
+int image_find_harder(
+ RuntimeScope scope,
+ ImageClass class,
+ const char *name_or_path,
+ const char *root,
+ Image **ret) {
+
if (image_name_is_valid(name_or_path))
- return image_find(class, name_or_path, root, ret);
+ return image_find(scope, class, name_or_path, root, ret);
return image_from_path(name_or_path, ret);
}
int image_discover(
+ RuntimeScope scope,
ImageClass class,
const char *root,
Hashmap *h) {
* some root directory.) */
int open_flags = root ? O_NOFOLLOW : 0, r;
+ assert(scope < _RUNTIME_SCOPE_MAX && scope != RUNTIME_SCOPE_GLOBAL);
assert(class >= 0);
assert(class < _IMAGE_CLASS_MAX);
assert(h);
- NULSTR_FOREACH(path, pick_image_search_path(class)) {
+ _cleanup_strv_free_ char **search = NULL;
+ r = pick_image_search_path(scope, class, &search);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(path, search) {
_cleanup_free_ char *resolved = NULL;
_cleanup_closedir_ DIR *d = NULL;
- r = chase_and_opendir(path, root, CHASE_PREFIX_ROOT, &resolved, &d);
+ r = chase_and_opendir(*path, root, CHASE_PREFIX_ROOT, &resolved, &d);
if (r == -ENOENT)
continue;
if (r < 0)
}
}
- if (class == IMAGE_MACHINE && !hashmap_contains(h, ".host")) {
+ if (scope == RUNTIME_SCOPE_SYSTEM && class == IMAGE_MACHINE && !hashmap_contains(h, ".host")) {
_cleanup_(image_unrefp) Image *image = NULL;
r = image_make(IMAGE_MACHINE,
return rename_noreplace(AT_FDCWD, path, AT_FDCWD, rs);
}
-int image_rename(Image *i, const char *new_name) {
+int image_rename(Image *i, const char *new_name, RuntimeScope scope) {
_cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT, name_lock = LOCK_FILE_INIT;
_cleanup_free_ char *new_path = NULL, *nn = NULL, *roothash = NULL;
_cleanup_strv_free_ char **settings = NULL;
if (r < 0)
return r;
- r = image_find(IMAGE_MACHINE, new_name, NULL, NULL);
+ r = image_find(scope, IMAGE_MACHINE, new_name, NULL, NULL);
if (r >= 0)
return -EEXIST;
if (r != -ENOENT)
return copy_file_atomic(path, rs, 0664, COPY_REFLINK);
}
-int image_clone(Image *i, const char *new_name, bool read_only) {
+int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope scope) {
_cleanup_(release_lock_file) LockFile name_lock = LOCK_FILE_INIT;
_cleanup_strv_free_ char **settings = NULL;
_cleanup_free_ char *roothash = NULL;
if (r < 0)
return r;
- r = image_find(IMAGE_MACHINE, new_name, NULL, NULL);
+ r = image_find(scope, IMAGE_MACHINE, new_name, NULL, NULL);
if (r >= 0)
return -EEXIST;
if (r != -ENOENT)
}
bool image_in_search_path(
+ RuntimeScope scope,
ImageClass class,
const char *root,
const char *image) {
+ int r;
+
+ assert(scope < _RUNTIME_SCOPE_MAX && scope != RUNTIME_SCOPE_GLOBAL);
+ assert(class >= 0);
+ assert(class < _IMAGE_CLASS_MAX);
assert(image);
- NULSTR_FOREACH(path, pick_image_search_path(class)) {
+ _cleanup_strv_free_ char **search = NULL;
+ r = pick_image_search_path(scope, class, &search);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(path, search) {
const char *p, *q;
size_t k;
if (!empty_or_root(root)) {
- q = path_startswith(path, root);
+ q = path_startswith(*path, root);
if (!q)
continue;
} else
- q = path;
+ q = *path;
- p = path_startswith(q, path);
+ p = path_startswith(q, *path);
if (!p)
continue;
#include "macro.h"
#include "os-util.h"
#include "path-util.h"
+#include "runtime-scope.h"
#include "string-util.h"
#include "time-util.h"
DEFINE_TRIVIAL_CLEANUP_FUNC(Image*, image_unref);
-int image_find(ImageClass class, const char *name, const char *root, Image **ret);
+int image_find(RuntimeScope scope, ImageClass class, const char *name, const char *root, Image **ret);
int image_from_path(const char *path, Image **ret);
-int image_find_harder(ImageClass class, const char *name_or_path, const char *root, Image **ret);
-int image_discover(ImageClass class, const char *root, Hashmap *map);
+int image_find_harder(RuntimeScope scope, ImageClass class, const char *name_or_path, const char *root, Image **ret);
+int image_discover(RuntimeScope scope, ImageClass class, const char *root, Hashmap *map);
int image_remove(Image *i);
-int image_rename(Image *i, const char *new_name);
-int image_clone(Image *i, const char *new_name, bool read_only);
+int image_rename(Image *i, const char *new_name, RuntimeScope scope);
+int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope scope);
int image_read_only(Image *i, bool b);
const char* image_type_to_string(ImageType t) _const_;
int image_read_metadata(Image *i, const ImagePolicy *image_policy);
-bool image_in_search_path(ImageClass class, const char *root, const char *image);
+bool image_in_search_path(RuntimeScope scope, ImageClass class, const char *root, const char *image);
static inline char **image_extension_release(Image *image, ImageClass class) {
assert(image);
if (!images)
return log_oom();
- r = image_discover(image_class, arg_root, images);
+ r = image_discover(RUNTIME_SCOPE_SYSTEM, image_class, arg_root, images);
if (r < 0)
return log_error_errno(r, "Failed to discover images: %m");
if (!images)
return log_oom();
- r = image_discover(arg_image_class, arg_root, images);
+ r = image_discover(RUNTIME_SCOPE_SYSTEM, arg_image_class, arg_root, images);
if (r < 0)
return log_error_errno(r, "Failed to discover images: %m");
if (!images)
return -ENOMEM;
- r = image_discover(image_class, arg_root, images);
+ r = image_discover(RUNTIME_SCOPE_SYSTEM, image_class, arg_root, images);
if (r < 0)
return r;
Hashmap *polkit_registry;
sd_event_source *notify_event;
+
+ RuntimeScope runtime_scope; /* For now only RUNTIME_SCOPE_SYSTEM */
} Manager;
/* Forward declare so that jobs can call it on exit */
assert(ret);
- m = new0(Manager, 1);
+ m = new(Manager, 1);
if (!m)
return -ENOMEM;
+ *m = (Manager) {
+ .runtime_scope = RUNTIME_SCOPE_SYSTEM,
+ };
+
r = sd_event_default(&m->event);
if (r < 0)
return r;
if (!images)
return -ENOMEM;
- r = image_discover((ImageClass) class, NULL, images);
+ r = image_discover(m->runtime_scope, (ImageClass) class, NULL, images);
if (r < 0)
return r;
if (arg_machine) {
_cleanup_(image_unrefp) Image *i = NULL;
- r = image_find(IMAGE_MACHINE, arg_machine, NULL, &i);
+ r = image_find(arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER,
+ IMAGE_MACHINE, arg_machine, NULL, &i);
if (r == -ENOENT)
return log_error_errno(r, "No image for machine '%s'.", arg_machine);
if (r < 0)
local tmpdir name
tmpdir="$(mktemp -d /var/tmp/TEST-13-NSPAWN.unpriv.XXX)"
- name="unpriv-${tmpdir##*.}"
+ name="unprv-${tmpdir##*.}"
trap 'rm -fr ${tmpdir@Q} || true; rm -f /run/verity.d/test-13-nspawn-${name@Q} || true' RETURN ERR
create_dummy_ddi "$tmpdir" "$name"
chown --recursive testuser: "$tmpdir"
-- \
systemd-nspawn --pipe --private-network --register=no --keep-unit --image="$tmpdir/$name.raw" echo hello >"$tmpdir/stdout.txt"
echo hello | cmp "$tmpdir/stdout.txt" -
+
+ # Make sure per-user search path logic works
+ systemd-run --pipe --uid=testuser mkdir -p /home/testuser/.local/state/machines
+ systemd-run --pipe --uid=testuser ln -s "$tmpdir/$name.raw" /home/testuser/.local/state/machines/"x$name.raw"
+ systemd-run \
+ --pipe \
+ --uid=testuser \
+ --property=Delegate=yes \
+ -- \
+ systemd-nspawn --pipe --private-network --register=no --keep-unit --machine="x$name" echo hello >"$tmpdir/stdout.txt"
+ echo hello | cmp "$tmpdir/stdout.txt" -
}
testcase_fuse() {
grep -q -F '{"name":"c","type":"raw","class":"sysext","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json
rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
+systemd-dissect --discover --system
+systemd-dissect --discover --user
+systemd-dissect --discover --system --user
+
LOOP="$(systemd-dissect --attach --loop-ref=waldo "$MINIMAL_IMAGE.raw")"
# Wait until the symlinks we want to test are established