Merge pull request #18440 from bluca/portable_upgrade

portable: add 'reattach' verb and DBUS interface
This commit is contained in:
Lennart Poettering
2021-02-10 21:26:14 +01:00
committed by GitHub
17 changed files with 548 additions and 52 deletions
@@ -61,6 +61,10 @@
send_interface="org.freedesktop.portable1.Manager"
send_member="DetachImage"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Manager"
send_member="ReattachImage"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Manager"
send_member="RemoveImage"/>
@@ -99,6 +103,10 @@
send_interface="org.freedesktop.portable1.Image"
send_member="Detach"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Image"
send_member="Reattach"/>
<allow send_destination="org.freedesktop.portable1"
send_interface="org.freedesktop.portable1.Image"
send_member="Remove"/>
+15 -7
View File
@@ -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);
}
@@ -1035,10 +1035,14 @@ static bool marker_matches_image(const char *marker, const char *name_or_path) {
a = last_path_component(marker);
if (image_name_is_valid(name_or_path)) {
const char *e;
const char *e, *underscore;
/* We shall match against an image name. In that case let's compare the last component, and optionally
* allow either a suffix of ".raw" or a series of "/". */
* allow either a suffix of ".raw" or a series of "/".
* But allow matching on a different version of the same image, when a "_" is used as a separator. */
underscore = strchr(name_or_path, '_');
if (underscore)
return strneq(a, name_or_path, underscore - name_or_path);
e = startswith(a, name_or_path);
if (!e)
@@ -1048,7 +1052,7 @@ static bool marker_matches_image(const char *marker, const char *name_or_path) {
e[strspn(e, "/")] == 0 ||
streq(e, ".raw");
} else {
const char *b;
const char *b, *underscore;
size_t l;
/* We shall match against a path. Let's ignore any prefix here though, as often there are many ways to
@@ -1060,7 +1064,11 @@ static bool marker_matches_image(const char *marker, const char *name_or_path) {
if (strcspn(b, "/") != l)
return false;
return memcmp(a, b, l) == 0;
underscore = strchr(b, '_');
if (underscore)
l = underscore - b;
return strneq(a, b, l);
}
}
@@ -1185,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);
+1
View File
@@ -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 {
+114 -14
View File
@@ -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 },
{}
};
+51 -7
View File
@@ -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);
}
+1
View File
@@ -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);
+181
View File
@@ -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, &copy_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),
+1
View File
@@ -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);