]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
machine-image: add a generic API to determine metadata of any image
authorLennart Poettering <lennart@poettering.net>
Tue, 14 Nov 2017 22:25:13 +0000 (23:25 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 20 Nov 2017 15:43:15 +0000 (16:43 +0100)
This adds an internal API that permits querying metadata from any type
of image, including both subvol/dir images, and raw/block images. In the
latter case we use the new dissection API we just added.

src/shared/machine-image.c
src/shared/machine-image.h

index a9e5d608a52268dd703e0801cb0cd49e011ec743..2dff66d00aad95bb24d8d7f0f6a06673abeede2c 100644 (file)
 #include "chattr-util.h"
 #include "copy.h"
 #include "dirent-util.h"
+#include "dissect-image.h"
 #include "env-util.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "fs-util.h"
 #include "hashmap.h"
+#include "hostname-util.h"
+#include "id128-util.h"
 #include "lockfile-util.h"
 #include "log.h"
+#include "loop-util.h"
 #include "machine-image.h"
 #include "macro.h"
 #include "mkdir.h"
@@ -65,6 +70,11 @@ Image *image_unref(Image *i) {
 
         free(i->name);
         free(i->path);
+
+        free(i->hostname);
+        strv_free(i->machine_info);
+        strv_free(i->os_release);
+
         return mfree(i);
 }
 
@@ -761,6 +771,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
 int image_read_only(Image *i, bool b) {
         _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
         int r;
+
         assert(i);
 
         if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
@@ -924,6 +935,118 @@ int image_set_limit(Image *i, uint64_t referenced_max) {
         return btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max);
 }
 
+int image_read_metadata(Image *i) {
+        _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
+        int r;
+
+        assert(i);
+
+        r = image_path_lock(i->path, LOCK_SH|LOCK_NB, &global_lock, &local_lock);
+        if (r < 0)
+                return r;
+
+        switch (i->type) {
+
+        case IMAGE_SUBVOLUME:
+        case IMAGE_DIRECTORY: {
+                _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL;
+                sd_id128_t machine_id = SD_ID128_NULL;
+                _cleanup_free_ char *hostname = NULL;
+                _cleanup_free_ char *path = NULL;
+
+                r = chase_symlinks("/etc/hostname", i->path, CHASE_PREFIX_ROOT, &path);
+                if (r < 0 && r != -ENOENT)
+                        log_debug_errno(r, "Failed to chase /etc/hostname in image %s: %m", i->name);
+                else if (r >= 0) {
+                        r = read_etc_hostname(path, &hostname);
+                        if (r < 0)
+                                log_debug_errno(errno, "Failed to read /etc/hostname of image %s: %m", i->name);
+                }
+
+                path = mfree(path);
+
+                r = chase_symlinks("/etc/machine-id", i->path, CHASE_PREFIX_ROOT, &path);
+                if (r < 0 && r != -ENOENT)
+                        log_debug_errno(r, "Failed to chase /etc/machine-id in image %s: %m", i->name);
+                else if (r >= 0) {
+                        _cleanup_close_ int fd = -1;
+
+                        fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                        if (fd < 0)
+                                log_debug_errno(errno, "Failed to open %s: %m", path);
+                        else {
+                                r = id128_read_fd(fd, ID128_PLAIN, &machine_id);
+                                if (r < 0)
+                                        log_debug_errno(r, "Image %s contains invalid machine ID.", i->name);
+                        }
+                }
+
+                path = mfree(path);
+
+                r = chase_symlinks("/etc/machine-info", i->path, CHASE_PREFIX_ROOT, &path);
+                if (r < 0 && r != -ENOENT)
+                        log_debug_errno(r, "Failed to chase /etc/machine-info in image %s: %m", i->name);
+                else if (r >= 0) {
+                        r = load_env_file_pairs(NULL, path, NULL, &machine_info);
+                        if (r < 0)
+                                log_debug_errno(r, "Failed to parse machine-info data of %s: %m", i->name);
+                }
+
+                path = mfree(path);
+
+                r = chase_symlinks("/etc/os-release", i->path, CHASE_PREFIX_ROOT, &path);
+                if (r == -ENOENT)
+                        r = chase_symlinks("/usr/lib/os-release", i->path, CHASE_PREFIX_ROOT, &path);
+                if (r < 0 && r != -ENOENT)
+                        log_debug_errno(r, "Failed to chase os-release in image: %m");
+                else if (r >= 0) {
+                        r = load_env_file_pairs(NULL, path, NULL, &os_release);
+                        if (r < 0)
+                                log_debug_errno(r, "Failed to parse os-release data of %s: %m", i->name);
+                }
+
+                free_and_replace(i->hostname, hostname);
+                i->machine_id = machine_id;
+                strv_free_and_replace(i->machine_info, machine_info);
+                strv_free_and_replace(i->os_release, os_release);
+
+                break;
+        }
+
+        case IMAGE_RAW:
+        case IMAGE_BLOCK: {
+                _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+                _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
+
+                r = loop_device_make_by_path(i->path, O_RDONLY, &d);
+                if (r < 0)
+                        return r;
+
+                r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m);
+                if (r < 0)
+                        return r;
+
+                r = dissected_image_acquire_metadata(m);
+                if (r < 0)
+                        return r;
+
+                free_and_replace(i->hostname, m->hostname);
+                i->machine_id = m->machine_id;
+                strv_free_and_replace(i->machine_info, m->machine_info);
+                strv_free_and_replace(i->os_release, m->os_release);
+
+                break;
+        }
+
+        default:
+                return -EOPNOTSUPP;
+        }
+
+        i->metadata_valid = true;
+
+        return 0;
+}
+
 int image_name_lock(const char *name, int operation, LockFile *ret) {
         const char *p;
 
index 1b1194e4426e649fb8a832c69a3ae503f754b5a9..9573000a5ac95d52228646384e40ae91d9eef483 100644 (file)
@@ -53,6 +53,13 @@ typedef struct Image {
         uint64_t limit;
         uint64_t limit_exclusive;
 
+        char *hostname;
+        sd_id128_t machine_id;
+        char **machine_info;
+        char **os_release;
+
+        bool metadata_valid;
+
         void *userdata;
 } Image;
 
@@ -80,6 +87,8 @@ int image_name_lock(const char *name, int operation, LockFile *ret);
 
 int image_set_limit(Image *i, uint64_t referenced_max);
 
+int image_read_metadata(Image *i);
+
 static inline bool IMAGE_IS_HIDDEN(const struct Image *i) {
         assert(i);