]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
discover-image: introduce per-user image directories
authorLennart Poettering <lennart@poettering.net>
Mon, 9 Dec 2024 10:38:05 +0000 (11:38 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 20 Dec 2024 17:04:01 +0000 (18:04 +0100)
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.

30 files changed:
man/systemd-dissect.xml
shell-completion/bash/systemd-dissect
src/dissect/dissect.c
src/import/export.c
src/import/import-fs.c
src/import/import.c
src/import/importd.c
src/import/pull.c
src/machine/image-dbus.c
src/machine/image-varlink.c
src/machine/machined-core.c
src/machine/machined-dbus.c
src/machine/machined-varlink.c
src/machine/machined.c
src/machine/machined.h
src/nspawn/nspawn.c
src/portable/portable.c
src/portable/portable.h
src/portable/portabled-bus.c
src/portable/portabled-image-bus.c
src/portable/portabled-image.c
src/portable/portabled.c
src/portable/portabled.h
src/shared/discover-image.c
src/shared/discover-image.h
src/sysext/sysext.c
src/sysupdate/sysupdated.c
src/vmspawn/vmspawn.c
test/units/TEST-13-NSPAWN.nspawn.sh
test/units/TEST-50-DISSECT.dissect.sh

index b65a2c39f1d25fc2ed605b2abd588d16edafb536..191ae3394bb06b7f2cfd5f8201a6fb074c4ccb90 100644 (file)
         <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" />
index 8d2b43423cbc8d19009cdb7c0aabf1701a02d824..a9fa639c3f9afd4dfa11478de86f1265f8985ad4 100644 (file)
@@ -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
index 78a830d574b2c30fbc3589ce83c768954ba8e7a6..8739e130c38725ca17a8f8e5f748e0e704f2d8b2 100644 (file)
@@ -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");
         }
index 1e82e84d7d98f25c7af3bfd6628ba15f5fdf66b0..346aca9446036dd0f685073f08ab4dca93323db2 100644 (file)
@@ -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)
index e74d36288af0e82bed7a2c877740eb78dd56de93..440ae85ba168947346988aad7195a6fea0e911ba 100644 (file)
@@ -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);
index 97db0b836a4908dba05810d1f7139ad17f62e158..6568c5915605497ef5cef340cf807d8b1965c79c 100644 (file)
@@ -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);
index db081205ab75f1712c5cc94464e61e46290ffbd0..ea8257a833dfb519f57e2a2fef8447f6083b3f85 100644 (file)
@@ -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;
index 46055ce5e73ab828a54e011b7b924e7efaccf04e..b2614fa8f56f7bf901fd846b9d62f8402765b235 100644 (file)
@@ -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);
index 8eca1e4c9589d358015327c8241dd339cf90a914..8f18e78c7dd4e6d702de8f7796a2999940abf572 100644 (file)
@@ -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;
 
index 5eb636960de7f72d93fc1de7f066bf15f01424f8..1784de61465e273b8fc85227caa86559e08747af 100644 (file)
@@ -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);
         }
 
index b1468b62c65e5f6d11229873b101a6b54634ac24..52cb915c3094073059194c339eeccd8ad9fc1886 100644 (file)
@@ -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;
index f4915f67da4e3aa5118ff005635e13054d4877ce..fc50d3f14711065867f0bf045463b032e2c368a9 100644 (file)
@@ -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;
 
index e0e27c4496483dc3bc3838f85f29be83ad31c97e..104b841dd5e1b2821b48425486f40a000f6a0d49 100644 (file)
@@ -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");
 
index a0c4ef751ae6475aced23b72b1f5101cda553454..3a235aa0d4005a5c0eb3117081906b6cbf1f2438 100644 (file)
@@ -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;
index 3d1f50269974c84cb1a84e126831931e6cb59d09..758678a2059722c7ad3e30382135061a811d51b9 100644 (file)
@@ -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);
index 66a8771eea68dbe38510fd6d3f3c9e4a0379459b..1b452014e3473d4c0a71ef1dfaafea9cb5d1b47a 100644 (file)
@@ -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)
index 18a8060a9f82baa61d29e70dc637021bc20087a9..61c13abe12d150201ade0d08d09271281619f7e6 100644 (file)
@@ -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;
         }
index 281b57e321d341714700969801ec04e1bc814e3e..03977159f0835164dc25d168b88a7b99b982abb9 100644 (file)
@@ -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);
 
index 4f239e2b125eb85bfd8c84f67c2bd5b9bdcda009..0476796dbe47ca5056735699f45005f63534a56e 100644 (file)
@@ -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,
index 0ca04d3a0b9b101b81908b9d6474f07c5844e523..ab0e44e038fa568b8719e32a73330096d6879197 100644 (file)
@@ -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);
index 6d2839125f22914cad64f4e0a1f031e61d90ed41..50e0e7a99cbb25736c53dcad358b9299bc353637 100644 (file)
@@ -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;
 
index 766999ff0de0c417dbb4570c06cd1d18d4b2a08e..6d7c3b534f4e9813a9541b2cb27dc21c936ea5a4 100644 (file)
@@ -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;
index 71ec41d4f1b67dc17983b66f38732b251269630a..8503451ccce7e18e1ca6691bf96f87610ec94cb9 100644 (file)
@@ -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;
index 674811ca354b722a0925e8585257024cf4da3315..53e9821412865dc983ed0850603f3f166ee42e91 100644 (file)
@@ -12,6 +12,8 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#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) {
+
+        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) {
@@ -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;
 
index b20b586ae04e1d09a2cb608dc5da080a06cb6da5..c1fdf15d155d51c36951893fb7a26da782b47188 100644 (file)
@@ -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);
index 6401fc4c0fffa46644c59c16e534f63516a01acb..9b1839d43a96bc03ebf701cba12123cd12fc93da 100644 (file)
@@ -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;
 
index 7dda16b8b5aee5da3209a19b6ba79aeeac778280..58967930f774aa0539e273921e0b0edda61a6d9d 100644 (file)
@@ -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;
 
index 1c49aff4a3b78bc570d7d4b8b8d60e76c07b774f..d2a9a6ee019bd78a5352ba15a26fd7b73cbb63ee 100644 (file)
@@ -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)
index 3120d98bf4d0f51a1a66e840322c0b6e4493bd5f..f5615755820e00094822119c18f373dd14aef24c 100755 (executable)
@@ -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() {
index a7122855d9c1fa6327e1e038926fb3cc8cfa7a78..fc33d91f0fa9d68fa4ab7212e657dc5523d2c4bb 100755 (executable)
@@ -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