From patchwork Tue Dec 15 16:35:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?SsO8cmdlbiBHcm/Dnw==?= X-Patchwork-Id: 11975199 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E1CD2C2BBCA for ; Tue, 15 Dec 2020 16:36:52 +0000 (UTC) Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 96FD8223D6 for ; Tue, 15 Dec 2020 16:36:52 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 96FD8223D6 Authentication-Results: mail.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=suse.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=xen-devel-bounces@lists.xenproject.org Received: from list by lists.xenproject.org with outflank-mailman.54557.94978 (Exim 4.92) (envelope-from ) id 1kpDJb-00074I-DL; Tue, 15 Dec 2020 16:36:43 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 54557.94978; Tue, 15 Dec 2020 16:36:43 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1kpDJb-00073m-11; Tue, 15 Dec 2020 16:36:43 +0000 Received: by outflank-mailman (input) for mailman id 54557; Tue, 15 Dec 2020 16:36:41 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1kpDJZ-00066M-Hy for xen-devel@lists.xenproject.org; Tue, 15 Dec 2020 16:36:41 +0000 Received: from mx2.suse.de (unknown [195.135.220.15]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id edb30b2c-898a-4786-a166-5a33fabf2be1; Tue, 15 Dec 2020 16:36:11 +0000 (UTC) Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id F269CAF73; Tue, 15 Dec 2020 16:36:08 +0000 (UTC) X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: edb30b2c-898a-4786-a166-5a33fabf2be1 X-Virus-Scanned: by amavisd-new at test-mx.suse.de DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1608050169; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Pqaa2EAISfoqMQYdGvAptWd4x6dr6DKrqL0LaEJ013g=; b=DvLlmnlO946ltBwXIbxkN5+QAUTA1nV9fl8ji2mxnxtW3FUSU67Y/rfR7kgIk/jJq8BQQf EkQBTUr++toDfK6MKVxRqoL/04i0qDACbBk8XjVKoR+VkC5k2L5ocESV91eNPHv8PO8Oi1 RlFinGRUo0e3mazFPD6fUMHulqGrKJM= From: Juergen Gross To: xen-devel@lists.xenproject.org Cc: Juergen Gross , Andrew Cooper , George Dunlap , Ian Jackson , Jan Beulich , Julien Grall , Stefano Stabellini , Wei Liu , Paul Durrant , Julien Grall Subject: [PATCH v10 06/25] tools/xenstore: add live update command to xenstore-control Date: Tue, 15 Dec 2020 17:35:44 +0100 Message-Id: <20201215163603.21700-7-jgross@suse.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201215163603.21700-1-jgross@suse.com> References: <20201215163603.21700-1-jgross@suse.com> MIME-Version: 1.0 Add the "live-update" command to xenstore-control enabling updating xenstored to a new version in a running Xen system. With -c 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 Reviewed-by: Paul Durrant Reviewed-by: Julien Grall --- V2: - add 0 byte after kernel chunk - add comment regrading add_to_buf() semantics (Pawel Wieczorkiewicz) - use %u for unsigned in format (Pawel Wieczorkiewicz) - explain buffer size better (Pawel Wieczorkiewicz) - add loop around "-s" option for client side retry in case of timeout V3: - add live-update command to docs/misc/xenstore.txt (Paul Durrant) - fix indent (Paul Durrant) V4: - made several parameters const (Julien Grall) - added more details to xenstore.txt (Julien Grall) V5: - set old_binary to NULL initially (Paul Durrant) V6: - use strerror(errno) in error message (Julien Grall) V10: - make binary specification mandatory (Andrew Cooper) Signed-off-by: Juergen Gross --- docs/misc/xenstore.txt | 21 ++ tools/xenstore/Makefile | 3 +- tools/xenstore/xenstore_control.c | 332 +++++++++++++++++++++++++++-- tools/xenstore/xenstored_control.c | 30 +++ 4 files changed, 369 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 |[|] Current commands are: check checks xenstored innards + live-update||+ + perform a live-update of the Xenstore daemon, only to + be used via xenstore-control command. + are implementation specific and are used for + different steps of the live-update processing. Currently + supported are: + -f specify new daemon binary + -b specify size of new stubdom binary + -d transfer chunk of new + stubdom binary + -c specify new command line to use + -s [-t ] [-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 #include #include #include +#include +#include +#include #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: diff --git a/tools/xenstore/xenstored_control.c b/tools/xenstore/xenstored_control.c index 8d29db8270..00fda5acdb 100644 --- a/tools/xenstore/xenstored_control.c +++ b/tools/xenstore/xenstored_control.c @@ -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 + * Mini-OS: -b + * -d (multiple of those) + * 2. New command-line (optional): -c + * 3. Start of update: -s [-F] [-t ] + * 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 ] [-F] [-t ] \n" + " Default timeout is 60 seconds.", 4 }, #ifdef __MINIOS__ { "memreport", do_control_memreport, "" }, #else