@@ -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
@@ -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)
@@ -1,9 +1,312 @@
+#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;
+ sleep(1);
+ }
+
+ 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 +323,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 +330,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 +359,7 @@ int main(int argc, char **argv)
} else if (strlen(ret) > 0)
printf("%s\n", ret);
+ out_close:
xs_close(xsh);
out:
@@ -149,11 +149,41 @@ static int do_control_print(void *ctx, struct connection *conn,
return 0;
}
+static int do_control_lu(void *ctx, struct connection *conn,
+ char **vec, int num)
+{
+ const char *resp;
+
+ resp = talloc_strdup(ctx, "NYI");
+ send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1);
+ return 0;
+}
+
static int do_control_help(void *, struct connection *, char **, int);
static struct cmd_s cmds[] = {
{ "check", do_control_check, "" },
{ "log", do_control_log, "on|off" },
+
+ /*
+ * The parameters are those of the xenstore-control utility!
+ * Depending on environment (Mini-OS or daemon) the live-update
+ * sequence is split into several sub-operations:
+ * 1. Specification of new binary
+ * daemon: -f <filename>
+ * Mini-OS: -b <binary-size>
+ * -d <size> <data-bytes> (multiple of those)
+ * 2. New command-line (optional): -c <cmdline>
+ * 3. Start of update: -s [-F] [-t <timeout>]
+ * Any sub-operation needs to respond with the string "OK" in case
+ * of success, any other response indicates failure.
+ * A started live-update sequence can be aborted via "-a" (not
+ * needed in case of failure for the first or last live-update
+ * sub-operation).
+ */
+ { "live-update", do_control_lu,
+ "[-c <cmdline>] [-F] [-t <timeout>] <file>\n"
+ " Default timeout is 60 seconds.", 4 },
#ifdef __MINIOS__
{ "memreport", do_control_memreport, "" },
#else