diff --git a/man/portablectl.xml b/man/portablectl.xml index 3653207d72..bd0c9e0573 100644 --- a/man/portablectl.xml +++ b/man/portablectl.xml @@ -155,6 +155,20 @@ to be used in case the unit names do not match the image name as described in the attach. + + reattach IMAGE [PREFIX…] + + Detaches an existing portable service image from the host, and immediately attaches it again. + This is useful in case the image was replaced. Running units are not stopped during the process. Partial matching, + to allow for different versions in the image name, is allowed: only the part before the first _ + character has to match. If the new image doesn't exist, the existing one will not be detached. The parameters + follow the same syntax as the attach command. + + If and/or are passed, the portable service(s) are + immediately stopped if removed, started and/or enabled if added, or restarted if updated. Prefixes are also + accepted, in the same way as described in the attach case. + + inspect IMAGE [PREFIX…] @@ -328,7 +342,8 @@ - Immediately start/stop the portable service after attaching/before detaching. + Immediately start/stop/restart the portable service after attaching/before + detaching/after upgrading. diff --git a/shell-completion/bash/portablectl b/shell-completion/bash/portablectl index fe3d925d78..e18d04c7ea 100644 --- a/shell-completion/bash/portablectl +++ b/shell-completion/bash/portablectl @@ -40,7 +40,7 @@ _portablectl() { local -A VERBS=( [STANDALONE]='list' - [IMAGE]='attach detach inspect is-attached set-limit' + [IMAGE]='attach detach reattach inspect is-attached set-limit' [IMAGES]='remove' [IMAGE_WITH_BOOL]='read-only' ) diff --git a/src/portable/org.freedesktop.portable1.conf b/src/portable/org.freedesktop.portable1.conf index 1343e1d544..5a09f5c2d9 100644 --- a/src/portable/org.freedesktop.portable1.conf +++ b/src/portable/org.freedesktop.portable1.conf @@ -61,6 +61,10 @@ send_interface="org.freedesktop.portable1.Manager" send_member="DetachImage"/> + + @@ -99,6 +103,10 @@ send_interface="org.freedesktop.portable1.Image" send_member="Detach"/> + + diff --git a/src/portable/portable.c b/src/portable/portable.c index 77e8d21287..0a3b1e8fde 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -1003,13 +1003,13 @@ int portable_attach( r = unit_file_exists(UNIT_FILE_SYSTEM, &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) + if (!FLAGS_SET(flags, PORTABLE_REATTACH) && r > 0) return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit file '%s' exists on the host already, refusing.", item->name); r = unit_file_is_active(bus, item->name, error); if (r < 0) return r; - if (r > 0) + if (!FLAGS_SET(flags, PORTABLE_REATTACH) && r > 0) return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit file '%s' is active already, refusing.", item->name); } @@ -1193,7 +1193,7 @@ int portable_detach( r = unit_file_is_active(bus, de->d_name, error); if (r < 0) return r; - if (r > 0) + if (!FLAGS_SET(flags, PORTABLE_REATTACH) && r > 0) return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit file '%s' is active, can't detach.", de->d_name); r = set_put_strdup(&unit_files, de->d_name); diff --git a/src/portable/portable.h b/src/portable/portable.h index 00ff80c38a..724634feb8 100644 --- a/src/portable/portable.h +++ b/src/portable/portable.h @@ -21,6 +21,7 @@ typedef enum PortableFlags { PORTABLE_PREFER_COPY = 1 << 0, PORTABLE_PREFER_SYMLINK = 1 << 1, PORTABLE_RUNTIME = 1 << 2, + PORTABLE_REATTACH = 1 << 3, } PortableFlags; typedef enum PortableChangeType { diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c index edfd74d540..278dae0e1b 100644 --- a/src/portable/portablectl.c +++ b/src/portable/portablectl.c @@ -45,6 +45,10 @@ static bool arg_enable = false; static bool arg_now = false; static bool arg_no_block = false; +static bool is_portable_managed(const char *unit) { + return ENDSWITH_SET(unit, ".service", ".target", ".socket", ".path", ".timer"); +} + static int determine_image(const char *image, bool permit_non_existing, char **ret) { int r; @@ -436,12 +440,14 @@ static int maybe_enable_disable(sd_bus *bus, const char *path, bool enable) { return 0; } -static int maybe_start_stop(sd_bus *bus, const char *path, bool start, BusWaitForJobs *wait) { +static int maybe_start_stop_restart(sd_bus *bus, const char *path, const char *method, BusWaitForJobs *wait) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char *name = (char *)basename(path), *job = NULL; int r; + assert(STR_IN_SET(method, "StartUnit", "StopUnit", "RestartUnit")); + if (!arg_now) return 0; @@ -450,13 +456,13 @@ static int maybe_start_stop(sd_bus *bus, const char *path, bool start, BusWaitFo "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - start ? "StartUnit" : "StopUnit", + method, &error, &reply, "ss", name, "replace"); if (r < 0) - return log_error_errno(r, "Failed to %s the portable service %s: %s", - start ? "start" : "stop", + return log_error_errno(r, "Failed to call %s on the portable service %s: %s", + method, path, bus_error_message(&error, r)); @@ -465,13 +471,13 @@ static int maybe_start_stop(sd_bus *bus, const char *path, bool start, BusWaitFo return bus_log_parse_error(r); if (!arg_quiet) - log_info("Queued %s to %s portable service %s.", job, start ? "start" : "stop", name); + log_info("Queued %s to call %s on portable service %s.", job, method, name); if (wait) { r = bus_wait_for_jobs_add(wait, job); if (r < 0) - return log_error_errno(r, "Failed to watch %s job for %s %s: %m", - job, start ? "starting" : "stopping", name); + return log_error_errno(r, "Failed to watch %s job to call %s on %s: %m", + job, method, name); } return 0; @@ -506,9 +512,83 @@ static int maybe_enable_start(sd_bus *bus, sd_bus_message *reply) { if (r == 0) break; - if (STR_IN_SET(type, "symlink", "copy") && ENDSWITH_SET(path, ".service", ".target", ".socket")) { + if (STR_IN_SET(type, "symlink", "copy") && is_portable_managed(path)) { (void) maybe_enable_disable(bus, path, true); - (void) maybe_start_stop(bus, path, true, wait); + (void) maybe_start_stop_restart(bus, path, "StartUnit", wait); + } + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return r; + + if (!arg_no_block) { + r = bus_wait_for_jobs(wait, arg_quiet, NULL); + if (r < 0) + return r; + } + + return 0; +} + +static int maybe_stop_enable_restart(sd_bus *bus, sd_bus_message *reply) { + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *wait = NULL; + int r; + + if (!arg_enable && !arg_now) + return 0; + + if (!arg_no_block) { + r = bus_wait_for_jobs_new(bus, &wait); + if (r < 0) + return log_error_errno(r, "Could not watch jobs: %m"); + } + + r = sd_bus_message_rewind(reply, true); + if (r < 0) + return r; + + /* First we get a list of units that were definitely removed, not just re-attached, + * so we can also stop them if the user asked us to. */ + r = sd_bus_message_enter_container(reply, 'a', "(sss)"); + if (r < 0) + return bus_log_parse_error(r); + + for (;;) { + char *type, *path, *source; + + r = sd_bus_message_read(reply, "(sss)", &type, &path, &source); + if (r < 0) + return bus_log_parse_error(r); + if (r == 0) + break; + + if (streq(type, "unlink") && is_portable_managed(path)) + (void) maybe_start_stop_restart(bus, path, "StopUnit", wait); + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return r; + + /* Then we get a list of units that were either added or changed, so that we can + * enable them and/or restart them if the user asked us to. */ + r = sd_bus_message_enter_container(reply, 'a', "(sss)"); + if (r < 0) + return bus_log_parse_error(r); + + for (;;) { + char *type, *path, *source; + + r = sd_bus_message_read(reply, "(sss)", &type, &path, &source); + if (r < 0) + return bus_log_parse_error(r); + if (r == 0) + break; + + if (STR_IN_SET(type, "symlink", "copy") && is_portable_managed(path)) { + (void) maybe_enable_disable(bus, path, true); + (void) maybe_start_stop_restart(bus, path, "RestartUnit", wait); } } @@ -588,7 +668,7 @@ static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) { if (r < 0) return bus_log_parse_error(r); - (void) maybe_start_stop(bus, name, false, wait); + (void) maybe_start_stop_restart(bus, name, "StopUnit", wait); (void) maybe_enable_disable(bus, name, false); } @@ -604,7 +684,7 @@ static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) { return 0; } -static int attach_image(int argc, char *argv[], void *userdata) { +static int attach_reattach_image(int argc, char *argv[], const char *method) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -612,6 +692,9 @@ static int attach_image(int argc, char *argv[], void *userdata) { _cleanup_free_ char *image = NULL; int r; + assert(method); + assert(STR_IN_SET(method, "AttachImage", "ReattachImage")); + r = determine_image(argv[1], false, &image); if (r < 0) return r; @@ -626,7 +709,7 @@ static int attach_image(int argc, char *argv[], void *userdata) { (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = bus_message_new_method_call(bus, &m, bus_portable_mgr, "AttachImage"); + r = bus_message_new_method_call(bus, &m, bus_portable_mgr, method); if (r < 0) return bus_log_create_error(r); @@ -644,17 +727,31 @@ static int attach_image(int argc, char *argv[], void *userdata) { r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) - return log_error_errno(r, "Failed to attach image: %s", bus_error_message(&error, r)); + return log_error_errno(r, "%s failed: %s", method, bus_error_message(&error, r)); (void) maybe_reload(&bus); print_changes(reply); - (void) maybe_enable_start(bus, reply); + if (streq(method, "AttachImage")) + (void) maybe_enable_start(bus, reply); + else { + /* ReattachImage returns 2 lists - removed units first, and changed/added second */ + print_changes(reply); + (void) maybe_stop_enable_restart(bus, reply); + } return 0; } +static int attach_image(int argc, char *argv[], void *userdata) { + return attach_reattach_image(argc, argv, "AttachImage"); +} + +static int reattach_image(int argc, char *argv[], void *userdata) { + return attach_reattach_image(argc, argv, "ReattachImage"); +} + static int detach_image(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -920,6 +1017,8 @@ static int help(int argc, char *argv[], void *userdata) { " Attach the specified portable service image\n" " detach NAME|PATH [PREFIX...]\n" " Detach the specified portable service image\n" + " reattach NAME|PATH [PREFIX...]\n" + " Reattach the specified portable service image\n" " inspect NAME|PATH [PREFIX...]\n" " Show details of specified portable service image\n" " is-attached NAME|PATH Query if portable service image is attached\n" @@ -1108,6 +1207,7 @@ static int run(int argc, char *argv[]) { { "read-only", 2, 3, 0, read_only_image }, { "remove", 2, VERB_ANY, 0, remove_image }, { "set-limit", 3, 3, 0, set_limit }, + { "reattach", 2, VERB_ANY, 0, reattach_image }, {} }; diff --git a/src/portable/portabled-bus.c b/src/portable/portabled-bus.c index 20a33dc671..c31ab092b4 100644 --- a/src/portable/portabled-bus.c +++ b/src/portable/portabled-bus.c @@ -299,6 +299,10 @@ finish: return r; } +static int method_reattach_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return redirect_method_to_image(userdata, message, error, bus_image_common_reattach); +} + static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { return redirect_method_to_image(userdata, message, error, bus_image_common_remove); } @@ -362,6 +366,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("GetImageState", "s", "s", method_get_image_state, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("AttachImage", "sassbs", "a(sss)", method_attach_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("DetachImage", "sb", "a(sss)", method_detach_image, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReattachImage", "sassbs", "a(sss)a(sss)", method_reattach_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED), @@ -369,18 +374,13 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_VTABLE_END }; -int reply_portable_changes(sd_bus_message *m, const PortableChange *changes, size_t n_changes) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; +static int reply_portable_compose_message(sd_bus_message *reply, const PortableChange *changes, size_t n_changes) { size_t i; int r; - assert(m); + assert(reply); assert(changes || n_changes == 0); - r = sd_bus_message_new_method_return(m, &reply); - if (r < 0) - return r; - r = sd_bus_message_open_container(reply, 'a', "(sss)"); if (r < 0) return r; @@ -398,5 +398,49 @@ int reply_portable_changes(sd_bus_message *m, const PortableChange *changes, siz if (r < 0) return r; + return 0; +} + +int reply_portable_changes(sd_bus_message *m, const PortableChange *changes, size_t n_changes) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(m); + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + return r; + + r = reply_portable_compose_message(reply, changes, n_changes); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +int reply_portable_changes_pair( + sd_bus_message *m, + const PortableChange *changes_first, + size_t n_changes_first, + const PortableChange *changes_second, + size_t n_changes_second) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(m); + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + return r; + + r = reply_portable_compose_message(reply, changes_first, n_changes_first); + if (r < 0) + return r; + + r = reply_portable_compose_message(reply, changes_second, n_changes_second); + if (r < 0) + return r; + return sd_bus_send(NULL, reply, NULL); } diff --git a/src/portable/portabled-bus.h b/src/portable/portabled-bus.h index e8e4c3a600..7da366c1c3 100644 --- a/src/portable/portabled-bus.h +++ b/src/portable/portabled-bus.h @@ -8,3 +8,4 @@ extern const sd_bus_vtable manager_vtable[]; int reply_portable_changes(sd_bus_message *m, const PortableChange *changes, size_t n_changes); +int reply_portable_changes_pair(sd_bus_message *m, const PortableChange *changes_first, size_t n_changes_first, const PortableChange *changes_second, size_t n_changes_second); diff --git a/src/portable/portabled-image-bus.c b/src/portable/portabled-image-bus.c index babdf4197f..630648ba3c 100644 --- a/src/portable/portabled-image-bus.c +++ b/src/portable/portabled-image-bus.c @@ -422,6 +422,186 @@ static int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_b return bus_image_common_remove(NULL, message, NULL, userdata, error); } +/* Given two PortableChange arrays, return a new array that has all elements of the first that are + * not also present in the second, comparing the basename of the path values. */ +static int normalize_portable_changes( + const PortableChange *changes_attached, + size_t n_changes_attached, + const PortableChange *changes_detached, + size_t n_changes_detached, + PortableChange **ret_changes, + size_t *ret_n_changes) { + + PortableChange *changes = NULL; + size_t n_changes = 0; + int r = 0; + + assert(ret_n_changes); + assert(ret_changes); + + if (n_changes_detached == 0) + return 0; /* Nothing to do */ + + changes = new0(PortableChange, n_changes_attached + n_changes_detached); + if (!changes) + return -ENOMEM; + + /* Corner case: only detached, nothing attached */ + if (n_changes_attached == 0) { + memcpy(changes, changes_detached, sizeof(PortableChange) * n_changes_detached); + *ret_changes = TAKE_PTR(changes); + *ret_n_changes = n_changes_detached; + + return 0; + } + + for (size_t i = 0; i < n_changes_detached; ++i) { + bool found = false; + + for (size_t j = 0; j < n_changes_attached; ++j) + if (streq(basename(changes_detached[i].path), basename(changes_attached[j].path))) { + found = true; + break; + } + + if (!found) { + _cleanup_free_ char *path = NULL, *source = NULL; + + path = strdup(changes_detached[i].path); + if (!path) { + r = -ENOMEM; + goto fail; + } + + if (changes_detached[i].source) { + source = strdup(changes_detached[i].source); + if (!source) { + r = -ENOMEM; + goto fail; + } + } + + changes[n_changes++] = (PortableChange) { + .type = changes_detached[i].type, + .path = TAKE_PTR(path), + .source = TAKE_PTR(source), + }; + } + } + + *ret_n_changes = n_changes; + *ret_changes = TAKE_PTR(changes); + + return 0; + +fail: + portable_changes_free(changes, n_changes); + return r; +} + +int bus_image_common_reattach( + Manager *m, + sd_bus_message *message, + const char *name_or_path, + Image *image, + sd_bus_error *error) { + + PortableChange *changes_detached = NULL, *changes_attached = NULL, *changes_gone = NULL; + size_t n_changes_detached = 0, n_changes_attached = 0, n_changes_gone = 0; + _cleanup_strv_free_ char **matches = NULL; + PortableFlags flags = PORTABLE_REATTACH; + const char *profile, *copy_mode; + int runtime, r; + + assert(message); + assert(name_or_path || image); + + if (!m) { + assert(image); + m = image->userdata; + } + + r = sd_bus_message_read_strv(message, &matches); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "sbs", &profile, &runtime, ©_mode); + if (r < 0) + return r; + + if (streq(copy_mode, "symlink")) + flags |= PORTABLE_PREFER_SYMLINK; + else if (streq(copy_mode, "copy")) + flags |= PORTABLE_PREFER_COPY; + else if (!isempty(copy_mode)) + return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", copy_mode); + + if (runtime) + flags |= PORTABLE_RUNTIME; + + r = bus_image_acquire(m, + message, + name_or_path, + image, + BUS_IMAGE_AUTHENTICATE_ALL, + "org.freedesktop.portable1.attach-images", + &image, + error); + if (r < 0) + return r; + if (r == 0) /* Will call us back */ + return 1; + + r = portable_detach( + sd_bus_message_get_bus(message), + image->path, + flags, + &changes_detached, + &n_changes_detached, + error); + if (r < 0) + goto finish; + + r = portable_attach( + sd_bus_message_get_bus(message), + image->path, + matches, + profile, + flags, + &changes_attached, + &n_changes_attached, + error); + if (r < 0) + goto finish; + + /* We want to return the list of units really removed by the detach, + * and not added again by the attach */ + r = normalize_portable_changes(changes_attached, n_changes_attached, + changes_detached, n_changes_detached, + &changes_gone, &n_changes_gone); + if (r < 0) + goto finish; + + /* First, return the units that are gone (so that the caller can stop them) + * Then, return the units that are changed/added (so that the caller can + * start/restart/enable them) */ + r = reply_portable_changes_pair(message, + changes_gone, n_changes_gone, + changes_attached, n_changes_attached); + if (r < 0) + goto finish; + +finish: + portable_changes_free(changes_detached, n_changes_detached); + portable_changes_free(changes_attached, n_changes_attached); + portable_changes_free(changes_gone, n_changes_gone); + return r; +} + +static int bus_image_method_reattach(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_image_common_reattach(NULL, message, NULL, userdata, error); +} + int bus_image_common_mark_read_only( Manager *m, sd_bus_message *message, @@ -532,6 +712,7 @@ const sd_bus_vtable image_vtable[] = { SD_BUS_METHOD("GetState", NULL, "s", bus_image_method_get_state, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Attach", "assbs", "a(sss)", bus_image_method_attach, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Detach", "b", "a(sss)", bus_image_method_detach, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Reattach", "assbs", "a(sss)a(sss)", bus_image_method_reattach, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, 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), diff --git a/src/portable/portabled-image-bus.h b/src/portable/portabled-image-bus.h index 50f1d935b1..e5faf43d90 100644 --- a/src/portable/portabled-image-bus.h +++ b/src/portable/portabled-image-bus.h @@ -10,6 +10,7 @@ int bus_image_common_get_os_release(Manager *m, sd_bus_message *message, const c int bus_image_common_get_metadata(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error); int bus_image_common_attach(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error); int bus_image_common_remove(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error); +int bus_image_common_reattach(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error); int bus_image_common_mark_read_only(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error); int bus_image_common_set_limit(Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error); diff --git a/test/TEST-58-PORTABLE/Makefile b/test/TEST-58-PORTABLE/Makefile new file mode 120000 index 0000000000..e9f93b1104 --- /dev/null +++ b/test/TEST-58-PORTABLE/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-58-PORTABLE/test.sh b/test/TEST-58-PORTABLE/test.sh new file mode 100755 index 0000000000..98e697962e --- /dev/null +++ b/test/TEST-58-PORTABLE/test.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -e +TEST_DESCRIPTION="test systemd-portabled" +IMAGE_NAME="portabled" +TEST_NO_NSPAWN=1 +TEST_INSTALL_VERITY_MINIMAL=1 + +. $TEST_BASE_DIR/test-functions + +# Need loop devices for mounting images +test_append_files() { + ( + instmods loop =block + instmods squashfs =squashfs + instmods dm_verity =md + install_dmevent + generate_module_dependencies + inst_binary losetup + inst_binary mksquashfs + inst_binary unsquashfs + install_verity_minimal + ) +} + +do_test "$@" 58 diff --git a/test/units/testsuite-58.service b/test/units/testsuite-58.service new file mode 100644 index 0000000000..47deba7d53 --- /dev/null +++ b/test/units/testsuite-58.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-58-PORTABLE + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh new file mode 100755 index 0000000000..b5b05b42d9 --- /dev/null +++ b/test/units/testsuite-58.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -ex +set -o pipefail + +export SYSTEMD_LOG_LEVEL=debug + +portablectl attach --now --runtime /usr/share/minimal_0.raw app0 + +systemctl is-active app0.service +systemctl is-active app0-foo.service +set +o pipefail +set +e +systemctl is-active app0-bar.service && exit 1 +set -e +set -o pipefail + +portablectl reattach --now --runtime /usr/share/minimal_1.raw app0 + +systemctl is-active app0.service +systemctl is-active app0-bar.service +set +o pipefail +set +e +systemctl is-active app0-foo.service && exit 1 +set -e +set -o pipefail + +portablectl list | grep -q -F "minimal_1" + +portablectl detach --now --runtime /usr/share/minimal_1.raw app0 + +portablectl list | grep -q -F "No images." + +# portablectl also works with directory paths rather than images + +unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw +unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw + +portablectl attach --copy=symlink --now --runtime /tmp/minimal_0 app0 + +systemctl is-active app0.service +systemctl is-active app0-foo.service +set +o pipefail +set +e +systemctl is-active app0-bar.service && exit 1 +set -e +set -o pipefail + +portablectl reattach --now --enable --runtime /tmp/minimal_1 app0 + +systemctl is-active app0.service +systemctl is-active app0-bar.service +set +o pipefail +set +e +systemctl is-active app0-foo.service && exit 1 +set -e +set -o pipefail + +portablectl list | grep -q -F "minimal_1" + +portablectl detach --now --enable --runtime /tmp/minimal_1 app0 + +portablectl list | grep -q -F "No images." + +echo OK > /testok + +exit 0