From e0cd19007ec89edc7e27abcd914a6786a6964e5d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Nov 2022 16:38:48 +0100 Subject: [PATCH 1/3] discover-image: add stringification helpers for ImageClass --- src/shared/discover-image.c | 8 ++++++++ src/shared/discover-image.h | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 9a7a1c990a..43582d0dd3 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -1303,3 +1303,11 @@ static const char* const image_type_table[_IMAGE_TYPE_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType); + +static const char* const image_class_table[_IMAGE_CLASS_MAX] = { + [IMAGE_MACHINE] = "machine", + [IMAGE_PORTABLE] = "portable", + [IMAGE_EXTENSION] = "extension", +}; + +DEFINE_STRING_TABLE_LOOKUP(image_class, ImageClass); diff --git a/src/shared/discover-image.h b/src/shared/discover-image.h index 3726e98d30..3286061d61 100644 --- a/src/shared/discover-image.h +++ b/src/shared/discover-image.h @@ -76,6 +76,9 @@ int image_read_only(Image *i, bool b); const char* image_type_to_string(ImageType t) _const_; ImageType image_type_from_string(const char *s) _pure_; +const char* image_class_to_string(ImageClass cl) _const_; +ImageClass image_class_from_string(const char *s) _pure_; + int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local); int image_name_lock(const char *name, int operation, LockFile *ret); From 3775e1410cebc45a6472dd1f9bc539f27e73d552 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Nov 2022 16:40:15 +0100 Subject: [PATCH 2/3] discover-image: store image class in Image object too, if known --- src/shared/discover-image.c | 21 ++++++++++++++------- src/shared/discover-image.h | 1 + 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 43582d0dd3..8bc165ef4c 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -123,6 +123,7 @@ static char *image_roothash_path(Image *image) { static int image_new( ImageType t, + ImageClass c, const char *pretty, const char *path, const char *filename, @@ -146,6 +147,7 @@ static int image_new( *i = (Image) { .n_ref = 1, .type = t, + .class = c, .read_only = read_only, .crtime = crtime, .mtime = mtime, @@ -203,6 +205,7 @@ static int extract_pretty(const char *path, const char *suffix, char **ret) { } static int image_make( + ImageClass c, const char *pretty, int dfd, const char *path, @@ -278,6 +281,7 @@ static int image_make( return r; r = image_new(IMAGE_SUBVOLUME, + c, pretty, path, filename, @@ -314,6 +318,7 @@ static int image_make( /* It's just a normal directory. */ r = image_new(IMAGE_DIRECTORY, + c, pretty, path, filename, @@ -345,6 +350,7 @@ static int image_make( } r = image_new(IMAGE_RAW, + c, pretty, path, filename, @@ -405,6 +411,7 @@ static int image_make( } r = image_new(IMAGE_BLOCK, + c, pretty, path, filename, @@ -475,13 +482,13 @@ int image_find(ImageClass class, if (!S_ISREG(st.st_mode)) continue; - r = image_make(name, dirfd(d), resolved, raw, &st, ret); + r = image_make(class, name, dirfd(d), resolved, raw, &st, ret); } else { if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode)) continue; - r = image_make(name, dirfd(d), resolved, name, &st, ret); + r = image_make(class, name, dirfd(d), resolved, name, &st, ret); } if (IN_SET(r, -ENOENT, -EMEDIUMTYPE)) continue; @@ -495,7 +502,7 @@ int image_find(ImageClass class, } if (class == IMAGE_MACHINE && streq(name, ".host")) { - r = image_make(".host", AT_FDCWD, NULL, empty_to_root(root), NULL, ret); + r = image_make(class, ".host", AT_FDCWD, NULL, empty_to_root(root), NULL, ret); if (r < 0) return r; @@ -515,9 +522,9 @@ int image_from_path(const char *path, Image **ret) { * overridden by another, different image earlier in the search path */ if (path_equal(path, "/")) - return image_make(".host", AT_FDCWD, NULL, "/", NULL, ret); + return image_make(IMAGE_MACHINE, ".host", AT_FDCWD, NULL, "/", NULL, ret); - return image_make(NULL, AT_FDCWD, NULL, path, NULL, ret); + return image_make(_IMAGE_CLASS_INVALID, NULL, AT_FDCWD, NULL, path, NULL, ret); } int image_find_harder(ImageClass class, const char *name_or_path, const char *root, Image **ret) { @@ -591,7 +598,7 @@ int image_discover( if (hashmap_contains(h, pretty)) continue; - r = image_make(pretty, dirfd(d), resolved, de->d_name, &st, &image); + r = image_make(class, pretty, dirfd(d), resolved, de->d_name, &st, &image); if (IN_SET(r, -ENOENT, -EMEDIUMTYPE)) continue; if (r < 0) @@ -610,7 +617,7 @@ int image_discover( if (class == IMAGE_MACHINE && !hashmap_contains(h, ".host")) { _cleanup_(image_unrefp) Image *image = NULL; - r = image_make(".host", AT_FDCWD, NULL, empty_to_root("/"), NULL, &image); + r = image_make(IMAGE_MACHINE, ".host", AT_FDCWD, NULL, empty_to_root("/"), NULL, &image); if (r < 0) return r; diff --git a/src/shared/discover-image.h b/src/shared/discover-image.h index 3286061d61..a8874228dc 100644 --- a/src/shared/discover-image.h +++ b/src/shared/discover-image.h @@ -34,6 +34,7 @@ typedef struct Image { unsigned n_ref; ImageType type; + ImageClass class; char *name; char *path; bool read_only; From 0305cf6e9d208067d249898f1d7bf638f27020fb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Nov 2022 16:39:06 +0100 Subject: [PATCH 3/3] dissect: add simple --discover command --- man/systemd-dissect.xml | 11 +++++++ src/dissect/dissect.c | 66 ++++++++++++++++++++++++++++++++++++++ test/units/testsuite-50.sh | 9 ++++++ 3 files changed, 86 insertions(+) diff --git a/man/systemd-dissect.xml b/man/systemd-dissect.xml index 2eb8972fee..8c6211b601 100644 --- a/man/systemd-dissect.xml +++ b/man/systemd-dissect.xml @@ -230,6 +230,17 @@ operation begins. + + + + Show a list of DDIs in well known directories. This will show machine, portable + service and system extension disk images in the usual directories + /usr/lib/machines/, /usr/lib/portables/, + /usr/lib/extensions/, /var/lib/machines/, + /var/lib/portables/, /var/lib/extensions/ and so + on. + + diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 3a882ee12c..e7ea582300 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -17,6 +17,7 @@ #include "copy.h" #include "device-util.h" #include "devnum-util.h" +#include "discover-image.h" #include "dissect-image.h" #include "env-util.h" #include "escape.h" @@ -56,6 +57,7 @@ static enum { ACTION_WITH, ACTION_COPY_FROM, ACTION_COPY_TO, + ACTION_DISCOVER, } arg_action = ACTION_DISSECT; static const char *arg_image = NULL; static const char *arg_path = NULL; @@ -128,6 +130,7 @@ static int help(void) { " --with Mount, run command, unmount\n" " -x --copy-from Copy files from image to host\n" " -a --copy-to Copy files from host to image\n" + " --discover Discover DDIs in well known directories\n" "\nSee the %2$s for details.\n", program_invocation_short_name, link, @@ -199,6 +202,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_RMDIR, ARG_JSON, ARG_MTREE, + ARG_DISCOVER, }; static const struct option options[] = { @@ -223,6 +227,7 @@ static int parse_argv(int argc, char *argv[]) { { "copy-from", no_argument, NULL, 'x' }, { "copy-to", no_argument, NULL, 'a' }, { "json", required_argument, NULL, ARG_JSON }, + { "discover", no_argument, NULL, ARG_DISCOVER }, {} }; @@ -400,6 +405,10 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_DISCOVER: + arg_action = ACTION_DISCOVER; + break; + case '?': return -EINVAL; @@ -491,6 +500,13 @@ static int parse_argv(int argc, char *argv[]) { break; + case ACTION_DISCOVER: + if (optind != argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Expected no argument."); + + break; + default: assert_not_reached(); } @@ -1325,6 +1341,54 @@ static int action_with(DissectedImage *m, LoopDevice *d) { return rcode; } +static int action_discover(void) { + _cleanup_(hashmap_freep) Hashmap *images = NULL; + _cleanup_(table_unrefp) Table *t = NULL; + Image *img; + int r; + + images = hashmap_new(&image_hash_ops); + if (!images) + return log_oom(); + + for (ImageClass cl = 0; cl < _IMAGE_CLASS_MAX; cl++) { + r = image_discover(cl, NULL, images); + if (r < 0) + return log_error_errno(r, "Failed to discover images: %m"); + } + + if ((arg_json_format_flags & JSON_FORMAT_OFF) && hashmap_isempty(images)) { + log_info("No images found."); + return 0; + } + + t = table_new("name", "type", "class", "ro", "path", "time", "usage"); + if (!t) + return log_oom(); + + HASHMAP_FOREACH(img, images) { + + if (!IN_SET(img->type, IMAGE_RAW, IMAGE_BLOCK)) + continue; + + r = table_add_many( + t, + TABLE_STRING, img->name, + TABLE_STRING, image_type_to_string(img->type), + TABLE_STRING, image_class_to_string(img->class), + TABLE_BOOLEAN, img->read_only, + TABLE_PATH, img->path, + TABLE_TIMESTAMP, img->mtime != 0 ? img->mtime : img->crtime, + TABLE_SIZE, img->usage); + if (r < 0) + return table_log_add_error(r); + } + + (void) table_set_sort(t, (size_t) 0); + + return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend); +} + static int run(int argc, char *argv[]) { _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; _cleanup_(loop_device_unrefp) LoopDevice *d = NULL; @@ -1338,6 +1402,8 @@ static int run(int argc, char *argv[]) { if (arg_action == ACTION_UMOUNT) return action_umount(arg_path); + if (arg_action == ACTION_DISCOVER) + return action_discover(); r = verity_settings_load( &arg_verity_settings, diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh index a5c1eaba26..031803f5b8 100755 --- a/test/units/testsuite-50.sh +++ b/test/units/testsuite-50.sh @@ -409,6 +409,15 @@ systemd-sysext unmerge rmdir /etc/extensions/app-nodistro rm /var/lib/extensions/app-nodistro.raw +mkdir -p /run/machines /run/portables /run/extensions +touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw + +systemd-dissect --discover --json=short > /tmp/discover.json +grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json +grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json +grep -q -F '{"name":"c","type":"raw","class":"extension","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 + echo OK >/testok exit 0