diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index 867bbc467b..400d8ec7b0 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -17,14 +17,23 @@ along with systemd; If not, see . ***/ +#include + #include "alloc-util.h" #include "bus-label.h" #include "bus-util.h" +#include "copy.h" +#include "dissect-image.h" #include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" #include "image-dbus.h" #include "io-util.h" +#include "loop-util.h" #include "machine-image.h" +#include "mount-util.h" #include "process-util.h" +#include "raw-clone.h" #include "strv.h" #include "user-util.h" @@ -279,6 +288,161 @@ int bus_image_method_set_limit( return sd_bus_reply_method_return(message, NULL); } +#define EXIT_NOT_FOUND 2 + +static int directory_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) { + + _cleanup_free_ char *path = NULL; + _cleanup_close_ int fd = -1; + int r; + + assert(image); + assert(ret); + + r = chase_symlinks("/etc/os-release", image->path, CHASE_PREFIX_ROOT, &path); + if (r == -ENOENT) + r = chase_symlinks("/usr/lib/os-release", image->path, CHASE_PREFIX_ROOT, &path); + if (r == -ENOENT) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Image does not contain OS release information"); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to resolve %s: %m", image->path); + + r = load_env_file_pairs(NULL, path, NULL, ret); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to open %s: %m", path); + + return 0; +} + +static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) { + _cleanup_(rmdir_and_freep) char *t = NULL; + _cleanup_(loop_device_unrefp) LoopDevice *d = NULL; + _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; + _cleanup_(sigkill_waitp) pid_t child = 0; + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_strv_free_ char **v = NULL; + siginfo_t si; + int r; + + assert(image); + assert(ret); + + r = mkdtemp_malloc("/tmp/machined-root-XXXXXX", &t); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to create temporary directory: %m"); + + r = loop_device_make_by_path(image->path, O_RDONLY, &d); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to set up loop block device for %s: %m", image->path); + + r = dissect_image(d->fd, &m); + if (r == -ENOPKG) + return sd_bus_error_set_errnof(error, r, "Disk image %s not understood: %m", image->path); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to dissect image %s: %m", image->path); + + if (pipe2(pair, O_CLOEXEC) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to create communication pipe: %m"); + + child = raw_clone(SIGCHLD|CLONE_NEWNS); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + int fd; + + pair[0] = safe_close(pair[0]); + + /* Make sure we never propagate to the host */ + if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) + _exit(EXIT_FAILURE); + + r = dissected_image_mount(m, t, DISSECTED_IMAGE_READ_ONLY); + if (r < 0) + _exit(EXIT_FAILURE); + + r = mount_move_root(t); + if (r < 0) + _exit(EXIT_FAILURE); + + fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0 && errno == ENOENT) { + fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0 && errno == ENOENT) + _exit(EXIT_NOT_FOUND); + } + if (fd < 0) + _exit(EXIT_FAILURE); + + r = copy_bytes(fd, pair[1], (uint64_t) -1, false); + if (r < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + pair[1] = safe_close(pair[1]); + + f = fdopen(pair[0], "re"); + if (!f) + return -errno; + + pair[0] = -1; + + r = load_env_file_pairs(f, "os-release", NULL, &v); + if (r < 0) + return r; + + r = wait_for_terminate(child, &si); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); + child = 0; + if (si.si_code == CLD_EXITED && si.si_status == EXIT_NOT_FOUND) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Image does not contain OS release information"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); + + *ret = v; + v = NULL; + + return 0; +} + +int bus_image_method_get_os_release( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + _cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT; + _cleanup_strv_free_ char **v = NULL; + Image *image = userdata; + int r; + + r = image_path_lock(image->path, LOCK_SH|LOCK_NB, &tree_global_lock, &tree_local_lock); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to lock image: %m"); + + switch (image->type) { + + case IMAGE_DIRECTORY: + case IMAGE_SUBVOLUME: + r = directory_image_get_os_release(image, &v, error); + break; + + case IMAGE_RAW: + r = raw_image_get_os_release(image, &v, error); + break; + + default: + assert_not_reached("Unknown image type"); + } + if (r < 0) + return r; + + return bus_reply_pair_array(message, v); +} + const sd_bus_vtable image_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0), @@ -296,6 +460,7 @@ const sd_bus_vtable image_vtable[] = { SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END }; diff --git a/src/machine/image-dbus.h b/src/machine/image-dbus.h index b62da996c6..bc8a6c3400 100644 --- a/src/machine/image-dbus.h +++ b/src/machine/image-dbus.h @@ -33,3 +33,4 @@ int bus_image_method_rename(sd_bus_message *message, void *userdata, sd_bus_erro int bus_image_method_clone(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_image_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 28e4867cb3..af745b6567 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -356,11 +356,11 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd return sd_bus_send(NULL, reply, NULL); } +#define EXIT_NOT_FOUND 2 + int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_strv_free_ char **l = NULL; Machine *m = userdata; - char **k, **v; int r; assert(message); @@ -394,7 +394,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); if (child == 0) { - _cleanup_close_ int fd = -1; + int fd = -1; pair[0] = safe_close(pair[0]); @@ -402,12 +402,14 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s if (r < 0) _exit(EXIT_FAILURE); - fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) { - fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) - _exit(EXIT_FAILURE); + fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0 && errno == ENOENT) { + fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0 && errno == ENOENT) + _exit(EXIT_NOT_FOUND); } + if (fd < 0) + _exit(EXIT_FAILURE); r = copy_bytes(fd, pair[1], (uint64_t) -1, false); if (r < 0) @@ -431,6 +433,8 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s r = wait_for_terminate(child, &si); if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); + if (si.si_code == CLD_EXITED && si.si_status == EXIT_NOT_FOUND) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information"); if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); @@ -441,25 +445,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); } - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "{ss}"); - if (r < 0) - return r; - - STRV_FOREACH_PAIR(k, v, l) { - r = sd_bus_message_append(reply, "{ss}", *k, *v); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); + return bus_reply_pair_array(message, l); } int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) { diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h index 241b23c7ec..c513783480 100644 --- a/src/machine/machine-dbus.h +++ b/src/machine/machine-dbus.h @@ -42,3 +42,5 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda int machine_send_signal(Machine *m, bool new_machine); int machine_send_create_reply(Machine *m, sd_bus_error *error); + +int bus_reply_pair_array(sd_bus_message *m, char **l); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 9c754b4327..3294ea7821 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -138,7 +138,7 @@ static void clean_machine_info(MachineInfo *machines, size_t n_machines) { free(machines); } -static int get_os_release_property(sd_bus *bus, const char *name, const char *query, ...) { +static int call_get_os_release(sd_bus *bus, const char *method, const char *name, const char *query, ...) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; const char *k, *v, *iter, **query_res = NULL; size_t count = 0, awaited_args = 0; @@ -153,12 +153,13 @@ static int get_os_release_property(sd_bus *bus, const char *name, const char *qu awaited_args++; query_res = newa0(const char *, awaited_args); - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachineOSRelease", - NULL, &reply, "s", name); + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + method, + NULL, &reply, "s", name); if (r < 0) return r; @@ -193,7 +194,7 @@ static int get_os_release_property(sd_bus *bus, const char *name, const char *qu val = strdup(query_res[count]); if (!val) { va_end(ap); - return log_oom(); + return -ENOMEM; } *out = val; } @@ -249,8 +250,12 @@ static int list_machines(int argc, char *argv[], void *userdata) { machines[n_machines].os = NULL; machines[n_machines].version_id = NULL; - r = get_os_release_property(bus, name, - "ID\0" "VERSION_ID\0", + r = call_get_os_release( + bus, + "GetMachineOSRelease", + name, + "ID\0" + "VERSION_ID\0", &machines[n_machines].os, &machines[n_machines].version_id); if (r < 0) @@ -610,7 +615,7 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p return 0; } -static int print_os_release(sd_bus *bus, const char *name, const char *prefix) { +static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) { _cleanup_free_ char *pretty = NULL; int r; @@ -618,7 +623,7 @@ static int print_os_release(sd_bus *bus, const char *name, const char *prefix) { assert(name); assert(prefix); - r = get_os_release_property(bus, name, "PRETTY_NAME\0", &pretty, NULL); + r = call_get_os_release(bus, method, name, "PRETTY_NAME\0", &pretty, NULL); if (r < 0) return r; @@ -729,7 +734,7 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) { "\n\t ", ALL_IP_ADDRESSES); - print_os_release(bus, i->name, "\t OS: "); + print_os_release(bus, "GetMachineOSRelease", i->name, "\t OS: "); if (i->unit) { printf("\t Unit: %s\n", i->unit); @@ -927,6 +932,8 @@ static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) { if (i->path) printf("\t Path: %s\n", i->path); + print_os_release(bus, "GetImageOSRelease", i->name, "\t OS: "); + printf("\t RO: %s%s%s\n", i->read_only ? ansi_highlight_red() : "", i->read_only ? "read-only" : "writable", diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 3ee3938ebb..fd9e5b56fc 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -825,6 +825,30 @@ static int method_mark_image_read_only(sd_bus_message *message, void *userdata, return bus_image_method_mark_read_only(message, i, error); } +static int method_get_image_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(image_unrefp) Image *i = NULL; + const char *name; + int r; + + assert(message); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + if (!image_name_is_valid(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name); + + r = image_find(name, &i); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name); + + i->userdata = userdata; + return bus_image_method_get_os_release(message, i, error); +} + static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -1396,6 +1420,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetImageOSRelease", "s", "a{ss}", method_get_image_os_release, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CleanPool", "s", "a(st)", method_clean_pool, SD_BUS_VTABLE_UNPRIVILEGED), @@ -1804,3 +1829,30 @@ int manager_add_machine(Manager *m, const char *name, Machine **_machine) { return 0; } + +int bus_reply_pair_array(sd_bus_message *m, char **l) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + char **k, **v; + int r; + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "{ss}"); + if (r < 0) + return r; + + STRV_FOREACH_PAIR(k, v, l) { + r = sd_bus_message_append(reply, "{ss}", *k, *v); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); + +} diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf index 562b9d3cc0..82ebfba50c 100644 --- a/src/machine/org.freedesktop.machine1.conf +++ b/src/machine/org.freedesktop.machine1.conf @@ -116,6 +116,10 @@ send_interface="org.freedesktop.machine1.Manager" send_member="SetImageLimit"/> + + @@ -192,6 +196,10 @@ send_interface="org.freedesktop.machine1.Image" send_member="MarkReadOnly"/> + +