From 1c0ade2e1f76b50431f941980b76e8ec5cdf3b12 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 9 Dec 2024 11:38:05 +0100 Subject: [PATCH 1/2] discover-image: introduce per-user image directories We nowadays support unprivileged invocation of systemd-nspawn + systemd-vmspawn, but there was no support for discovering suitable disk images (i.e. no per-user counterpart of /var/lib/machines). Add this now, and hook it up everywhere. Instead of hardcoding machined's, importd's, portabled's, sysupdated's image discovery to RUNTIME_SCOPE_SYSTEM I introduced a field that make the scope variable, even if this field is always initialized to RUNTIME_SCOPE_SYSTEM for now. I think these four services should eventually be updated to support a per-user concept too, this is preparation for that, even though it doesn't outright add support for this. This is for the largest part not user visible, except for in nspawn, vmspawn and the dissect tool. For the latter I added a pair of --user/--system switches to select the discovery scope. --- man/systemd-dissect.xml | 11 ++ shell-completion/bash/systemd-dissect | 2 + src/dissect/dissect.c | 23 +++- src/import/export.c | 5 +- src/import/import-fs.c | 3 +- src/import/import.c | 3 +- src/import/importd.c | 6 +- src/import/pull.c | 3 +- src/machine/image-dbus.c | 5 +- src/machine/image-varlink.c | 2 +- src/machine/machined-core.c | 4 +- src/machine/machined-dbus.c | 6 +- src/machine/machined-varlink.c | 5 +- src/machine/machined.c | 6 +- src/machine/machined.h | 2 + src/nspawn/nspawn.c | 3 +- src/portable/portable.c | 64 ++++++++--- src/portable/portable.h | 9 +- src/portable/portabled-bus.c | 4 + src/portable/portabled-image-bus.c | 16 ++- src/portable/portabled-image.c | 2 +- src/portable/portabled.c | 6 +- src/portable/portabled.h | 3 + src/shared/discover-image.c | 160 ++++++++++++++++++++++---- src/shared/discover-image.h | 13 ++- src/sysext/sysext.c | 6 +- src/sysupdate/sysupdated.c | 10 +- src/vmspawn/vmspawn.c | 3 +- test/units/TEST-13-NSPAWN.nspawn.sh | 13 ++- test/units/TEST-50-DISSECT.dissect.sh | 4 + 30 files changed, 317 insertions(+), 85 deletions(-) diff --git a/man/systemd-dissect.xml b/man/systemd-dissect.xml index b65a2c39f1d..191ae3394bb 100644 --- a/man/systemd-dissect.xml +++ b/man/systemd-dissect.xml @@ -503,6 +503,17 @@ + + + + + When used together with controls whether to search for + images installed system-wide or in the user's directories in $HOME. If neither + switch is specified, will search within both scopes. + + + + diff --git a/shell-completion/bash/systemd-dissect b/shell-completion/bash/systemd-dissect index 8d2b43423cb..a9fa639c3f9 100644 --- a/shell-completion/bash/systemd-dissect +++ b/shell-completion/bash/systemd-dissect @@ -29,6 +29,8 @@ _systemd_dissect() { 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 diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 78a830d574b..8739e130c38 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -95,6 +95,7 @@ static char *arg_loop_ref = NULL; 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); @@ -151,6 +152,8 @@ static int help(void) { " 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" @@ -274,6 +277,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_VALIDATE, ARG_MTREE_HASH, ARG_MAKE_ARCHIVE, + ARG_SYSTEM, + ARG_USER, }; static const struct option options[] = { @@ -307,10 +312,13 @@ static int parse_argv(int argc, char *argv[]) { { "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); @@ -531,7 +539,6 @@ static int parse_argv(int argc, char *argv[]) { 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?)."); @@ -539,6 +546,14 @@ static int parse_argv(int argc, char *argv[]) { 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; @@ -547,6 +562,10 @@ static int parse_argv(int argc, char *argv[]) { } } + 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: @@ -1851,7 +1870,7 @@ static int action_discover(void) { 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"); } diff --git a/src/import/export.c b/src/import/export.c index 1e82e84d7d9..346aca94460 100644 --- a/src/import/export.c +++ b/src/import/export.c @@ -25,6 +25,7 @@ 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) { @@ -66,7 +67,7 @@ static int export_tar(int argc, char *argv[], void *userdata) { 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) @@ -139,7 +140,7 @@ static int export_raw(int argc, char *argv[], void *userdata) { 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) diff --git a/src/import/import-fs.c b/src/import/import-fs.c index e74d36288af..440ae85ba16 100644 --- a/src/import/import-fs.c +++ b/src/import/import-fs.c @@ -34,6 +34,7 @@ static bool arg_sync = true; 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; @@ -145,7 +146,7 @@ static int import_fs(int argc, char *argv[], void *userdata) { 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); diff --git a/src/import/import.c b/src/import/import.c index 97db0b836a4..6568c591560 100644 --- a/src/import/import.c +++ b/src/import/import.c @@ -30,6 +30,7 @@ static const char *arg_image_root = NULL; 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; @@ -63,7 +64,7 @@ static int normalize_local(const char *local, char **ret) { 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); diff --git a/src/import/importd.c b/src/import/importd.c index db081205ab7..ea8257a833d 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -111,6 +111,8 @@ struct Manager { bool use_btrfs_subvol; bool use_btrfs_quota; + + RuntimeScope runtime_scope; /* for now: always RUNTIME_SCOPE_SYSTEM */ }; #define TRANSFERS_MAX 64 @@ -721,6 +723,7 @@ static int manager_new(Manager **ret) { *m = (Manager) { .use_btrfs_subvol = true, .use_btrfs_quota = true, + .runtime_scope = RUNTIME_SCOPE_SYSTEM, }; r = sd_event_default(&m->event); @@ -1332,6 +1335,7 @@ static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_er 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); @@ -1372,7 +1376,7 @@ static int method_list_images(sd_bus_message *msg, void *userdata, sd_bus_error 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; diff --git a/src/import/pull.c b/src/import/pull.c index 46055ce5e73..b2614fa8f56 100644 --- a/src/import/pull.c +++ b/src/import/pull.c @@ -33,6 +33,7 @@ static ImportFlags arg_import_flags = IMPORT_PULL_SETTINGS | IMPORT_PULL_ROOTHAS 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); @@ -66,7 +67,7 @@ static int normalize_local(const char *local, const char *url, char **ret) { 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); diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index 8eca1e4c958..8f18e78c7dd 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -178,7 +178,7 @@ int bus_image_method_clone( 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); } @@ -402,6 +402,7 @@ char* image_bus_path(const char *name) { 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; @@ -413,7 +414,7 @@ static int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, 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; diff --git a/src/machine/image-varlink.c b/src/machine/image-varlink.c index 5eb636960de..1784de61465 100644 --- a/src/machine/image-varlink.c +++ b/src/machine/image-varlink.c @@ -148,7 +148,7 @@ int vl_method_clone_image(sd_varlink *link, sd_json_variant *parameters, sd_varl 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); } diff --git a/src/machine/machined-core.c b/src/machine/machined-core.c index b1468b62c65..52cb915c309 100644 --- a/src/machine/machined-core.c +++ b/src/machine/machined-core.c @@ -440,7 +440,7 @@ int manager_acquire_image(Manager *m, const char *name, Image **ret) { 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"); @@ -467,7 +467,7 @@ int rename_image_and_update_cache(Manager *m, Image *image, const char* new_name /* 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; diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index f4915f67da4..fc50d3f1471 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -123,7 +123,7 @@ static int method_get_image(sd_bus_message *message, void *userdata, sd_bus_erro 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) @@ -476,7 +476,7 @@ static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_er 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; @@ -753,7 +753,7 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err 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; diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index e0e27c44964..104b841dd5e 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -641,6 +641,7 @@ static int list_image_one_and_maybe_read_metadata(sd_varlink *link, Image *image } 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; @@ -667,7 +668,7 @@ static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters, 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) @@ -683,7 +684,7 @@ static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters, 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"); diff --git a/src/machine/machined.c b/src/machine/machined.c index a0c4ef751ae..3a235aa0d40 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -40,10 +40,14 @@ static int manager_new(Manager **ret) { 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; diff --git a/src/machine/machined.h b/src/machine/machined.h index 3d1f5026997..758678a2059 100644 --- a/src/machine/machined.h +++ b/src/machine/machined.h @@ -42,6 +42,8 @@ struct Manager { 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); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 66a8771eea6..1b452014e34 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -3167,7 +3167,8 @@ static int determine_names(void) { 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) diff --git a/src/portable/portable.c b/src/portable/portable.c index 18a8060a9f8..61c13abe12d 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -173,6 +173,7 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(portable_metadata_hash_ops, char, PortableMetadata, portable_metadata_unref); static int extract_now( + RuntimeScope scope, const char *where, char **matches, const char *image_name, @@ -199,6 +200,7 @@ static int extract_now( * 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). */ @@ -248,7 +250,7 @@ static int extract_now( /* 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"); @@ -348,6 +350,7 @@ static int extract_now( } static int portable_extract_by_path( + RuntimeScope scope, const char *path, bool path_is_extension, bool relax_extension_release_check, @@ -381,7 +384,7 @@ static int portable_extract_by_path( 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; @@ -458,7 +461,7 @@ static int portable_extract_by_path( 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); @@ -549,6 +552,7 @@ static int portable_extract_by_path( } static int extract_image_and_extensions( + RuntimeScope scope, const char *name_or_path, char **matches, char **extension_image_paths, @@ -595,7 +599,7 @@ static int extract_image_and_extensions( 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; @@ -633,7 +637,7 @@ static int extract_image_and_extensions( 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; @@ -645,6 +649,7 @@ static int extract_image_and_extensions( } r = portable_extract_by_path( + scope, image->path, /* path_is_extension= */ false, /* relax_extension_release_check= */ false, @@ -687,6 +692,7 @@ static int extract_image_and_extensions( const char *e; r = portable_extract_by_path( + scope, ext->path, /* path_is_extension= */ true, relax_extension_release_check, @@ -754,6 +760,7 @@ static int extract_image_and_extensions( } int portable_extract( + RuntimeScope scope, const char *name_or_path, char **matches, char **extension_image_paths, @@ -775,6 +782,7 @@ int portable_extract( assert(name_or_path); r = extract_image_and_extensions( + scope, name_or_path, matches, extension_image_paths, @@ -1426,6 +1434,7 @@ static int image_target_path( } static int install_image( + RuntimeScope scope, const char *image_path, PortableFlags flags, PortableChange **changes, @@ -1434,13 +1443,14 @@ static int install_image( _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); @@ -1485,6 +1495,7 @@ static int install_image( } static int install_image_and_extensions( + RuntimeScope scope, const Image *image, OrderedHashmap *extension_images, PortableFlags flags, @@ -1497,12 +1508,12 @@ static int install_image_and_extensions( 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; @@ -1595,6 +1606,7 @@ static void log_portable_verb( } int portable_attach( + RuntimeScope scope, sd_bus *bus, const char *name_or_path, char **matches, @@ -1615,7 +1627,10 @@ int portable_attach( PortableMetadata *item; int r; + assert(scope < _RUNTIME_SCOPE_MAX); + r = extract_image_and_extensions( + scope, name_or_path, matches, extension_image_paths, @@ -1672,13 +1687,13 @@ int portable_attach( 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) @@ -1700,7 +1715,7 @@ int portable_attach( /* 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", @@ -1844,6 +1859,7 @@ static int test_chroot_dropin( } int portable_detach( + RuntimeScope scope, sd_bus *bus, const char *name_or_path, char **extension_image_paths, @@ -1857,12 +1873,12 @@ int portable_detach( _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; @@ -1930,7 +1946,7 @@ int portable_detach( 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; @@ -2031,6 +2047,7 @@ not_found: } static int portable_get_state_internal( + RuntimeScope scope, sd_bus *bus, const char *name_or_path, char **extension_image_paths, @@ -2045,10 +2062,11 @@ static int portable_get_state_internal( 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; @@ -2084,7 +2102,7 @@ static int portable_get_state_internal( 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)) @@ -2109,6 +2127,7 @@ static int portable_get_state_internal( } int portable_get_state( + RuntimeScope scope, sd_bus *bus, const char *name_or_path, char **extension_image_paths, @@ -2125,12 +2144,19 @@ int portable_get_state( /* 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; } diff --git a/src/portable/portable.h b/src/portable/portable.h index 281b57e321d..03977159f08 100644 --- a/src/portable/portable.h +++ b/src/portable/portable.h @@ -6,6 +6,7 @@ #include "dissect-image.h" #include "hashmap.h" #include "macro.h" +#include "runtime-scope.h" #include "set.h" #include "string-util.h" @@ -69,12 +70,12 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(PortableMetadata*, portable_metadata_unref); 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); diff --git a/src/portable/portabled-bus.c b/src/portable/portabled-bus.c index 4f239e2b125..0476796dbe4 100644 --- a/src/portable/portabled-bus.c +++ b/src/portable/portabled-bus.c @@ -165,6 +165,7 @@ static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_er return r; r = portable_get_state( + m->runtime_scope, sd_bus_message_get_bus(message), image->path, NULL, @@ -225,6 +226,7 @@ static int method_get_image_metadata(sd_bus_message *message, void *userdata, sd 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; @@ -254,6 +256,7 @@ static int method_get_image_state(sd_bus_message *message, void *userdata, sd_bu } r = portable_get_state( + m->runtime_scope, sd_bus_message_get_bus(message), name_or_path, extension_images, @@ -330,6 +333,7 @@ static int method_detach_image(sd_bus_message *message, void *userdata, sd_bus_e return 1; /* Will call us back */ r = portable_detach( + m->runtime_scope, sd_bus_message_get_bus(message), name_or_path, extension_images, diff --git a/src/portable/portabled-image-bus.c b/src/portable/portabled-image-bus.c index 0ca04d3a0b9..ab0e44e038f 100644 --- a/src/portable/portabled-image-bus.c +++ b/src/portable/portabled-image-bus.c @@ -114,10 +114,8 @@ int bus_image_common_get_metadata( 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"); @@ -160,6 +158,7 @@ int bus_image_common_get_metadata( return 1; r = portable_extract( + m->runtime_scope, image->path, matches, extension_images, @@ -264,6 +263,7 @@ static int bus_image_method_get_state( _cleanup_strv_free_ char **extension_images = NULL; Image *image = ASSERT_PTR(userdata); + Manager *m = ASSERT_PTR(image->userdata); PortableState state; int r; @@ -288,6 +288,7 @@ static int bus_image_method_get_state( } r = portable_get_state( + m->runtime_scope, sd_bus_message_get_bus(message), image->path, extension_images, @@ -385,6 +386,7 @@ int bus_image_common_attach( return 1; r = portable_attach( + m->runtime_scope, sd_bus_message_get_bus(message), image->path, matches, @@ -463,6 +465,7 @@ static int bus_image_method_detach( return 1; /* Will call us back */ r = portable_detach( + m->runtime_scope, sd_bus_message_get_bus(message), image->path, extension_images, @@ -513,6 +516,7 @@ int bus_image_common_remove( return 1; /* Will call us back */ r = portable_get_state( + m->runtime_scope, sd_bus_message_get_bus(message), image->path, NULL, @@ -716,6 +720,7 @@ int bus_image_common_reattach( return 1; r = portable_detach( + m->runtime_scope, sd_bus_message_get_bus(message), image->path, extension_images, @@ -727,6 +732,7 @@ int bus_image_common_reattach( return r; r = portable_attach( + m->runtime_scope, sd_bus_message_get_bus(message), image->path, matches, @@ -1039,7 +1045,7 @@ int bus_image_acquire( 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); diff --git a/src/portable/portabled-image.c b/src/portable/portabled-image.c index 6d2839125f2..50e0e7a99cb 100644 --- a/src/portable/portabled-image.c +++ b/src/portable/portabled-image.c @@ -91,7 +91,7 @@ int manager_image_cache_discover(Manager *m, Hashmap *images, sd_bus_error *erro /* 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; diff --git a/src/portable/portabled.c b/src/portable/portabled.c index 766999ff0de..6d7c3b534f4 100644 --- a/src/portable/portabled.c +++ b/src/portable/portabled.c @@ -28,10 +28,14 @@ static int manager_new(Manager **ret) { 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; diff --git a/src/portable/portabled.h b/src/portable/portabled.h index 71ec41d4f1b..8503451ccce 100644 --- a/src/portable/portabled.h +++ b/src/portable/portabled.h @@ -7,6 +7,7 @@ #include "bus-object.h" #include "hashmap.h" #include "list.h" +#include "runtime-scope.h" typedef struct Manager Manager; @@ -23,6 +24,8 @@ struct Manager { LIST_HEAD(Operation, operations); unsigned n_operations; + + RuntimeScope runtime_scope; /* for now always RUNTIME_SCOPE_SYSTEM */ }; extern const BusObjectImplementation manager_object; diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 674811ca354..53e98214128 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -12,6 +12,8 @@ #include #include +#include "sd-path.h" + #include "alloc-util.h" #include "blockdev-util.h" #include "btrfs-util.h" @@ -553,12 +555,95 @@ static int image_make( 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) { - /* 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]; + 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, + }; + + _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) { @@ -592,7 +677,8 @@ 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) { @@ -602,6 +688,7 @@ int image_find(ImageClass class, * 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); @@ -614,11 +701,16 @@ int image_find(ImageClass class, 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) @@ -722,7 +814,7 @@ int image_find(ImageClass class, } } - 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, @@ -771,14 +863,21 @@ int image_from_path(const char *path, Image **ret) { 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) { @@ -788,15 +887,21 @@ int image_discover( * 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) @@ -946,7 +1051,7 @@ int image_discover( } } - 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, @@ -1063,7 +1168,7 @@ static int rename_auxiliary_file(const char *path, const char *new_name, const c 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; @@ -1098,7 +1203,7 @@ int image_rename(Image *i, const char *new_name) { 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) @@ -1185,7 +1290,7 @@ static int clone_auxiliary_file(const char *path, const char *new_name, const ch 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; @@ -1212,7 +1317,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) { 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) @@ -1646,24 +1751,35 @@ int image_name_lock(const char *name, int operation, LockFile *ret) { } 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; diff --git a/src/shared/discover-image.h b/src/shared/discover-image.h index b20b586ae04..c1fdf15d155 100644 --- a/src/shared/discover-image.h +++ b/src/shared/discover-image.h @@ -13,6 +13,7 @@ #include "macro.h" #include "os-util.h" #include "path-util.h" +#include "runtime-scope.h" #include "string-util.h" #include "time-util.h" @@ -60,14 +61,14 @@ Image *image_ref(Image *i); 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_; @@ -80,7 +81,7 @@ int image_set_limit(Image *i, uint64_t referenced_max); 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); diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 6401fc4c0ff..9b1839d43a9 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -2031,7 +2031,7 @@ static int image_discover_and_read_metadata( 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"); @@ -2278,7 +2278,7 @@ static int verb_list(int argc, char **argv, void *userdata) { 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"); @@ -2339,7 +2339,7 @@ static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varl 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; diff --git a/src/sysupdate/sysupdated.c b/src/sysupdate/sysupdated.c index 7dda16b8b5a..58967930f77 100644 --- a/src/sysupdate/sysupdated.c +++ b/src/sysupdate/sysupdated.c @@ -46,6 +46,8 @@ typedef struct Manager { 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 */ @@ -1730,10 +1732,14 @@ static int manager_new(Manager **ret) { 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; @@ -1795,7 +1801,7 @@ static int manager_enumerate_image_class(Manager *m, TargetClass class) { 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; diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index 1c49aff4a3b..d2a9a6ee019 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -2236,7 +2236,8 @@ static int determine_names(void) { 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) diff --git a/test/units/TEST-13-NSPAWN.nspawn.sh b/test/units/TEST-13-NSPAWN.nspawn.sh index 3120d98bf4d..f5615755820 100755 --- a/test/units/TEST-13-NSPAWN.nspawn.sh +++ b/test/units/TEST-13-NSPAWN.nspawn.sh @@ -1129,7 +1129,7 @@ testcase_unpriv() { 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" @@ -1141,6 +1141,17 @@ testcase_unpriv() { -- \ 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() { diff --git a/test/units/TEST-50-DISSECT.dissect.sh b/test/units/TEST-50-DISSECT.dissect.sh index a7122855d9c..fc33d91f0fa 100755 --- a/test/units/TEST-50-DISSECT.dissect.sh +++ b/test/units/TEST-50-DISSECT.dissect.sh @@ -615,6 +615,10 @@ grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/ 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 From 2b717a7f14758c4a3dcea1b580d042ff588ff145 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 9 Dec 2024 11:46:54 +0100 Subject: [PATCH 2/2] update TODO --- TODO | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index 3fad822bc66..e44b583db1a 100644 --- a/TODO +++ b/TODO @@ -122,6 +122,18 @@ Deprecations and removals: Features: +* importd: introduce a per-user instance, that downloads into per-user DDI dirs + +* sysupdated: similar + +* portabled: similar + +* machined: implement a per-user instance, that manages per-user DDI dirs for + images. systemd-nspawn/systemd-vmspawn should probably register with both the + system and the user scoped machined instance. The former to get the machine + name registered as hostname, and the latter so that the image stuff is nicely + per-user managed. + * resolved: make resolved process DNR DHCP info * Teach systemd-ssh-generator to generated an /run/issue.d/ drop-in telling @@ -391,8 +403,6 @@ Features: the bg via vmspawn/nspawn if not done so yet and then requests a shell inside it for the invoking user. -* importd/…: define per-user dirs for container/VM images too. - * add a new specifier to unit files that figures out the DDI the unit file is from, tracing through overlayfs, DM, loopback block device.