Mailing List Archive

[PATCH v2 1/8] tools/xenstore: add live update command to xenstore-control
From: Juergen Gross <jgross@suse.com>

Add the "live-update" command to xenstore-control enabling updating
xenstored to a new version in a running Xen system.

With -c <arg> it is possible to pass a different command line to the
new instance of xenstored. This will replace the command line used
for the invocation of the just running xenstored instance.

The running xenstored (or xenstore-stubdom) needs to support live
updating, of course.

For now just add a small dummy handler to C xenstore denying any
live update action.

Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Paul Durrant <paul@xen.org>
Reviewed-by: Julien Grall <jgrall@amazon.com>
---
docs/misc/xenstore.txt | 21 ++
tools/xenstore/Makefile | 3 +-
tools/xenstore/xenstore_control.c | 332 ++++++++++++++++++++++++++++--
3 files changed, 339 insertions(+), 17 deletions(-)

diff --git a/docs/misc/xenstore.txt b/docs/misc/xenstore.txt
index 2081f20f55..1480742330 100644
--- a/docs/misc/xenstore.txt
+++ b/docs/misc/xenstore.txt
@@ -317,6 +317,27 @@ CONTROL <command>|[<parameters>|]
Current commands are:
check
checks xenstored innards
+ live-update|<params>|+
+ perform a live-update of the Xenstore daemon, only to
+ be used via xenstore-control command.
+ <params> are implementation specific and are used for
+ different steps of the live-update processing. Currently
+ supported <params> are:
+ -f <file> specify new daemon binary
+ -b <size> specify size of new stubdom binary
+ -d <chunk-size> <binary-chunk> transfer chunk of new
+ stubdom binary
+ -c <pars> specify new command line to use
+ -s [-t <sec>] [-F] start live update process (-t specifies
+ timeout in seconds to wait for active transactions
+ to finish, default is 60 seconds; -F will force
+ live update to happen even with running transactions
+ after timeout elapsed)
+ -a abort live update handling
+ All sub-options will return "OK" in case of success or an
+ error string in case of failure. -s can return "BUSY" in case
+ of an active transaction, a retry of -s can be done in that
+ case.
log|on
turn xenstore logging on
log|off
diff --git a/tools/xenstore/Makefile b/tools/xenstore/Makefile
index 9a0f0d012d..ab89e22d3a 100644
--- a/tools/xenstore/Makefile
+++ b/tools/xenstore/Makefile
@@ -11,6 +11,7 @@ CFLAGS += -include $(XEN_ROOT)/tools/config.h
CFLAGS += -I./include
CFLAGS += $(CFLAGS_libxenevtchn)
CFLAGS += $(CFLAGS_libxenctrl)
+CFLAGS += $(CFLAGS_libxenguest)
CFLAGS += $(CFLAGS_libxentoolcore)
CFLAGS += -DXEN_LIB_STORED="\"$(XEN_LIB_STORED)\""
CFLAGS += -DXEN_RUN_STORED="\"$(XEN_RUN_STORED)\""
@@ -81,7 +82,7 @@ xenstore: xenstore_client.o
$(CC) $< $(LDFLAGS) $(LDLIBS_libxenstore) $(LDLIBS_libxentoolcore) $(SOCKET_LIBS) -o $@ $(APPEND_LDFLAGS)

xenstore-control: xenstore_control.o
- $(CC) $< $(LDFLAGS) $(LDLIBS_libxenstore) $(LDLIBS_libxentoolcore) $(SOCKET_LIBS) -o $@ $(APPEND_LDFLAGS)
+ $(CC) $< $(LDFLAGS) $(LDLIBS_libxenstore) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxentoolcore) $(SOCKET_LIBS) -o $@ $(APPEND_LDFLAGS)

xs_tdb_dump: xs_tdb_dump.o utils.o tdb.o talloc.o
$(CC) $^ $(LDFLAGS) -o $@ $(APPEND_LDFLAGS)
diff --git a/tools/xenstore/xenstore_control.c b/tools/xenstore/xenstore_control.c
index afa04495a7..5ca015a07d 100644
--- a/tools/xenstore/xenstore_control.c
+++ b/tools/xenstore/xenstore_control.c
@@ -1,9 +1,311 @@
+#define _GNU_SOURCE
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
+#include <xenctrl.h>
+#include <xenguest.h>

#include "xenstore.h"

+/* Add a string plus terminating 0 byte to buf, returning new len. */
+static int add_to_buf(char **buf, const char *val, int len)
+{
+ int vallen = strlen(val) + 1;
+
+ if (len < 0)
+ return -1;
+
+ *buf = realloc(*buf, len + vallen);
+ if (!*buf)
+ return -1;
+
+ strcpy(*buf + len, val);
+
+ return len + vallen;
+}
+
+static int live_update_start(struct xs_handle *xsh, bool force, unsigned int to)
+{
+ int len = 0;
+ char *buf = NULL, *ret;
+ time_t time_start;
+
+ if (asprintf(&ret, "%u", to) < 0)
+ return 1;
+ len = add_to_buf(&buf, "-s", len);
+ len = add_to_buf(&buf, "-t", len);
+ len = add_to_buf(&buf, ret, len);
+ free(ret);
+ if (force)
+ len = add_to_buf(&buf, "-F", len);
+ if (len < 0)
+ return 1;
+
+ for (time_start = time(NULL); time(NULL) - time_start < to;) {
+ ret = xs_control_command(xsh, "live-update", buf, len);
+ if (!ret)
+ goto err;
+ if (strcmp(ret, "BUSY"))
+ break;
+ }
+
+ if (strcmp(ret, "OK"))
+ goto err;
+
+ free(buf);
+ free(ret);
+
+ return 0;
+
+ err:
+ fprintf(stderr, "Starting live update failed:\n%s\n",
+ ret ? : strerror(errno));
+ free(buf);
+ free(ret);
+
+ return 3;
+}
+
+static int live_update_cmdline(struct xs_handle *xsh, const char *cmdline)
+{
+ int len = 0, rc = 0;
+ char *buf = NULL, *ret;
+
+ len = add_to_buf(&buf, "-c", len);
+ len = add_to_buf(&buf, cmdline, len);
+ if (len < 0)
+ return 1;
+
+ ret = xs_control_command(xsh, "live-update", buf, len);
+ free(buf);
+ if (!ret || strcmp(ret, "OK")) {
+ fprintf(stderr, "Setting update binary failed:\n%s\n",
+ ret ? : strerror(errno));
+ rc = 3;
+ }
+ free(ret);
+
+ return rc;
+}
+
+static int send_kernel_blob(struct xs_handle *xsh, const char *binary)
+{
+ int rc = 0, len = 0;
+ xc_interface *xch;
+ struct xc_dom_image *dom;
+ char *ret, *buf = NULL;
+ size_t off, sz;
+#define BLOB_CHUNK_SZ 2048
+
+ xch = xc_interface_open(NULL, NULL, 0);
+ if (!xch) {
+ fprintf(stderr, "xc_interface_open() failed\n");
+ return 1;
+ }
+
+ dom = xc_dom_allocate(xch, NULL, NULL);
+ if (!dom) {
+ rc = 1;
+ goto out_close;
+ }
+
+ rc = xc_dom_kernel_file(dom, binary);
+ if (rc) {
+ rc = 1;
+ goto out_rel;
+ }
+
+ if (asprintf(&ret, "%zu", dom->kernel_size) < 0) {
+ rc = 1;
+ goto out_rel;
+ }
+ len = add_to_buf(&buf, "-b", len);
+ len = add_to_buf(&buf, ret, len);
+ free(ret);
+ if (len < 0) {
+ rc = 1;
+ goto out_rel;
+ }
+ ret = xs_control_command(xsh, "live-update", buf, len);
+ free(buf);
+ if (!ret || strcmp(ret, "OK")) {
+ fprintf(stderr, "Starting live update failed:\n%s\n",
+ ret ? : strerror(errno));
+ rc = 3;
+ }
+ free(ret);
+ if (rc)
+ goto out_rel;
+
+ /* buf capable to hold "-d" <1..2048> BLOB_CHUNK_SZ and a terminating 0. */
+ buf = malloc(3 + 5 + BLOB_CHUNK_SZ + 1);
+ if (!buf) {
+ rc = 1;
+ goto out_rel;
+ }
+
+ strcpy(buf, "-d");
+ sz = BLOB_CHUNK_SZ;
+ for (off = 0; off < dom->kernel_size; off += BLOB_CHUNK_SZ) {
+ if (dom->kernel_size - off < BLOB_CHUNK_SZ)
+ sz = dom->kernel_size - off;
+ sprintf(buf + 3, "%zu", sz);
+ len = 3 + strlen(buf + 3) + 1;
+ memcpy(buf + len, dom->kernel_blob + off, sz);
+ buf[len + sz] = 0;
+ len += sz + 1;
+ ret = xs_control_command(xsh, "live-update", buf, len);
+ if (!ret || strcmp(ret, "OK")) {
+ fprintf(stderr, "Transfer of new binary failed:\n%s\n",
+ ret ? : strerror(errno));
+ rc = 3;
+ free(ret);
+ break;
+ }
+ free(ret);
+ }
+
+ free(buf);
+
+ out_rel:
+ xc_dom_release(dom);
+
+ out_close:
+ xc_interface_close(xch);
+
+ return rc;
+}
+
+/*
+ * Live update of Xenstore stubdom
+ *
+ * Sequence of actions:
+ * 1. transfer new stubdom binary
+ * a) specify size
+ * b) transfer unpacked binary in chunks
+ * 2. transfer new cmdline (optional)
+ * 3. start update (includes flags)
+ */
+static int live_update_stubdom(struct xs_handle *xsh, const char *binary,
+ const char *cmdline, bool force, unsigned int to)
+{
+ int rc;
+
+ rc = send_kernel_blob(xsh, binary);
+ if (rc)
+ goto abort;
+
+ if (cmdline) {
+ rc = live_update_cmdline(xsh, cmdline);
+ if (rc)
+ goto abort;
+ }
+
+ rc = live_update_start(xsh, force, to);
+ if (rc)
+ goto abort;
+
+ return 0;
+
+ abort:
+ xs_control_command(xsh, "live-update", "-a", 3);
+ return rc;
+}
+
+/*
+ * Live update of Xenstore daemon
+ *
+ * Sequence of actions:
+ * 1. transfer new binary filename
+ * 2. transfer new cmdline (optional)
+ * 3. start update (includes flags)
+ */
+static int live_update_daemon(struct xs_handle *xsh, const char *binary,
+ const char *cmdline, bool force, unsigned int to)
+{
+ int len = 0, rc;
+ char *buf = NULL, *ret;
+
+ len = add_to_buf(&buf, "-f", len);
+ len = add_to_buf(&buf, binary, len);
+ if (len < 0)
+ return 1;
+ ret = xs_control_command(xsh, "live-update", buf, len);
+ free(buf);
+ if (!ret || strcmp(ret, "OK")) {
+ fprintf(stderr, "Setting update binary failed:\n%s\n",
+ ret ? : strerror(errno));
+ free(ret);
+ return 3;
+ }
+ free(ret);
+
+ if (cmdline) {
+ rc = live_update_cmdline(xsh, cmdline);
+ if (rc)
+ goto abort;
+ }
+
+ rc = live_update_start(xsh, force, to);
+ if (rc)
+ goto abort;
+
+ return 0;
+
+ abort:
+ xs_control_command(xsh, "live-update", "-a", 3);
+ return rc;
+}
+
+static int live_update(struct xs_handle *xsh, int argc, char **argv)
+{
+ int rc = 0;
+ unsigned int i, to = 60;
+ char *binary = NULL, *cmdline = NULL, *val;
+ bool force = false;
+
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "-c")) {
+ i++;
+ if (i == argc) {
+ fprintf(stderr, "Missing command line value\n");
+ rc = 2;
+ goto out;
+ }
+ cmdline = argv[i];
+ } else if (!strcmp(argv[i], "-t")) {
+ i++;
+ if (i == argc) {
+ fprintf(stderr, "Missing timeout value\n");
+ rc = 2;
+ goto out;
+ }
+ to = atoi(argv[i]);
+ } else if (!strcmp(argv[i], "-F"))
+ force = true;
+ else
+ binary = argv[i];
+ }
+
+ if (!binary) {
+ fprintf(stderr, "Missing binary specification\n");
+ rc = 2;
+ goto out;
+ }
+
+ val = xs_read(xsh, XBT_NULL, "/tool/xenstored/domid", &i);
+ if (val)
+ rc = live_update_stubdom(xsh, binary, cmdline, force, to);
+ else
+ rc = live_update_daemon(xsh, binary, cmdline, force, to);
+
+ free(val);
+
+ out:
+ return rc;
+}

int main(int argc, char **argv)
{
@@ -20,22 +322,6 @@ int main(int argc, char **argv)
goto out;
}

- for (p = 2; p < argc; p++)
- len += strlen(argv[p]) + 1;
- if (len) {
- par = malloc(len);
- if (!par) {
- fprintf(stderr, "Allocation error.\n");
- rc = 1;
- goto out;
- }
- len = 0;
- for (p = 2; p < argc; p++) {
- memcpy(par + len, argv[p], strlen(argv[p]) + 1);
- len += strlen(argv[p]) + 1;
- }
- }
-
xsh = xs_open(0);
if (xsh == NULL) {
fprintf(stderr, "Failed to contact Xenstored.\n");
@@ -43,6 +329,19 @@ int main(int argc, char **argv)
goto out;
}

+ if (!strcmp(argv[1], "live-update")) {
+ rc = live_update(xsh, argc - 2, argv + 2);
+ goto out_close;
+ }
+
+ for (p = 2; p < argc; p++)
+ len = add_to_buf(&par, argv[p], len);
+ if (len < 0) {
+ fprintf(stderr, "Allocation error.\n");
+ rc = 1;
+ goto out_close;
+ }
+
ret = xs_control_command(xsh, argv[1], par, len);
if (!ret) {
rc = 3;
@@ -59,6 +358,7 @@ int main(int argc, char **argv)
} else if (strlen(ret) > 0)
printf("%s\n", ret);

+ out_close:
xs_close(xsh);

out:
--
2.29.2
Re: [PATCH v2 1/8] tools/xenstore: add live update command to xenstore-control [ In reply to ]
On 15.01.21 23:28, Edwin Török wrote:
> From: Juergen Gross <jgross@suse.com>
>
> Add the "live-update" command to xenstore-control enabling updating
> xenstored to a new version in a running Xen system.
>
> With -c <arg> it is possible to pass a different command line to the
> new instance of xenstored. This will replace the command line used
> for the invocation of the just running xenstored instance.
>
> The running xenstored (or xenstore-stubdom) needs to support live
> updating, of course.
>
> For now just add a small dummy handler to C xenstore denying any
> live update action.
>
> Signed-off-by: Juergen Gross <jgross@suse.com>
> Reviewed-by: Paul Durrant <paul@xen.org>
> Reviewed-by: Julien Grall <jgrall@amazon.com>

Instead of merging multiple patches of mine into a single one and
sending it here with my S-o-b I'd prefer a simple caveat for your
series to depend on my C-xenstore series.

This additionally reduces the risk to miss any modifications in
my series done in a later iteration than the one you have taken.


Juergen
Re: [PATCH v2 1/8] tools/xenstore: add live update command to xenstore-control [ In reply to ]
On Mon, 2021-01-18 at 08:50 +0100, Jürgen Groß wrote:
> On 15.01.21 23:28, Edwin Török wrote:
> > From: Juergen Gross <jgross@suse.com>
> >
> > Add the "live-update" command to xenstore-control enabling updating
> > xenstored to a new version in a running Xen system.
> >
> > With -c <arg> it is possible to pass a different command line to
> > the
> > new instance of xenstored. This will replace the command line used
> > for the invocation of the just running xenstored instance.
> >
> > The running xenstored (or xenstore-stubdom) needs to support live
> > updating, of course.
> >
> > For now just add a small dummy handler to C xenstore denying any
> > live update action.
> >
> > Signed-off-by: Juergen Gross <jgross@suse.com>
> > Reviewed-by: Paul Durrant <paul@xen.org>
> > Reviewed-by: Julien Grall <jgrall@amazon.com>
>
> Instead of merging multiple patches of mine into a single one and
> sending it here with my S-o-b I'd prefer a simple caveat for your
> series to depend on my C-xenstore series.

Yes, I should've added a link to that in my cover letter,
in the next revision I'll point to:
https://lore.kernel.org/xen-devel/20210115083000.14186-1-jgross@suse.com/

>
> This additionally reduces the risk to miss any modifications in
> my series done in a later iteration than the one you have taken.

I've reordered the patches and changed the base, it should be gone the
next I do git format-patch:
https://github.com/edwintorok/xen/pull/1/commits

Will also remove
https://github.com/edwintorok/xen/pull/1/commits/47dd1d9b99b9210e94fbd4af28afee8088d5267
once I implemented the change in oxenstored to not return BUSY that we
discussed.

>
>
> Juergen