]> xenbits.xensource.com Git - xen.git/commitdiff
tools/xenstore: dump the xenstore state for live update
authorJuergen Gross <jgross@suse.com>
Wed, 13 Jan 2021 13:00:19 +0000 (14:00 +0100)
committerJuergen Gross <jgross@suse.com>
Thu, 21 Jan 2021 16:31:36 +0000 (17:31 +0100)
Dump the complete Xenstore status to a file (daemon case) or memory
(stubdom case).

As we don't know the exact length of the needed area in advance we are
using an anonymous rather large mapping in stubdom case, which will
use only virtual address space until accessed. And as we are writing
the area in a sequential manner this is fine. As the initial size we
are choosing the double size of the memory allocated via talloc(),
which should be more than enough.

Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Paul Durrant <paul@xen.org>
Acked-by: Julien Grall <jgrall@amazon.com>
Acked-by: Wei Liu <wl@xen.org>
tools/xenstore/utils.c
tools/xenstore/utils.h
tools/xenstore/xenstored_control.c
tools/xenstore/xenstored_core.c
tools/xenstore/xenstored_core.h
tools/xenstore/xenstored_domain.c
tools/xenstore/xenstored_domain.h
tools/xenstore/xenstored_watch.c
tools/xenstore/xenstored_watch.h

index 633ce3b4fcbe3e99be022709ac25c686b287235b..0d80cb6de8cbbfc190abdcfb78c7aaa905b0881c 100644 (file)
@@ -62,3 +62,20 @@ void barf_perror(const char *fmt, ...)
        }
        exit(1);
 }
+
+const char *dump_state_align(FILE *fp)
+{
+       long len;
+       static char nul[8] = {};
+
+       len = ftell(fp);
+       if (len < 0)
+               return "Dump state align error";
+       len &= 7;
+       if (!len)
+               return NULL;
+
+       if (fwrite(nul, 8 - len, 1, fp) != 1)
+               return "Dump state align error";
+       return NULL;
+}
index 6a1b5de9bdc11807e6481a7091ae0b42f9f267bc..df1cb9a3bac66a7743239e718ccb7b99cbec3811 100644 (file)
@@ -3,6 +3,7 @@
 #include <stdbool.h>
 #include <string.h>
 #include <stdint.h>
+#include <stdio.h>
 
 #include <xen-tools/libs.h>
 
@@ -21,6 +22,11 @@ static inline bool strends(const char *a, const char *b)
        return streq(a + strlen(a) - strlen(b), b);
 }
 
+/*
+ * Write NUL bytes for aligning state data to 8 bytes.
+ */
+const char *dump_state_align(FILE *fp);
+
 void barf(const char *fmt, ...) __attribute__((noreturn));
 void barf_perror(const char *fmt, ...) __attribute__((noreturn));
 
index 52fbf313673ca40447b51651874bfff13f25ba73..72a03c99d4031abdaa91fbb9379f65863ad3e72c 100644 (file)
@@ -25,12 +25,21 @@ Interactive commands for Xen Store Daemon.
 #include <time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
 #include <unistd.h>
+#include <xenctrl.h>
 
 #include "utils.h"
 #include "talloc.h"
 #include "xenstored_core.h"
 #include "xenstored_control.h"
+#include "xenstored_domain.h"
+
+/* Mini-OS only knows about MAP_ANON. */
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
 
 struct live_update {
        /* For verification the correct connection is acting. */
@@ -40,6 +49,9 @@ struct live_update {
        void *kernel;
        unsigned int kernel_size;
        unsigned int kernel_off;
+
+       void *dump_state;
+       unsigned long dump_size;
 #else
        char *filename;
 #endif
@@ -56,6 +68,10 @@ static struct live_update *lu_status;
 
 static int lu_destroy(void *data)
 {
+#ifdef __MINIOS__
+       if (lu_status->dump_state)
+               munmap(lu_status->dump_state, lu_status->dump_size);
+#endif
        lu_status = NULL;
 
        return 0;
@@ -274,6 +290,31 @@ static const char *lu_arch(const void *ctx, struct connection *conn,
        errno = EINVAL;
        return NULL;
 }
+
+static FILE *lu_dump_open(const void *ctx)
+{
+       lu_status->dump_size = ROUNDUP(talloc_total_size(NULL) * 2,
+                                      XC_PAGE_SHIFT);
+       lu_status->dump_state = mmap(NULL, lu_status->dump_size,
+                                    PROT_READ | PROT_WRITE,
+                                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+       if (lu_status->dump_state == MAP_FAILED)
+               return NULL;
+
+       return fmemopen(lu_status->dump_state, lu_status->dump_size, "w");
+}
+
+static void lu_dump_close(FILE *fp)
+{
+       size_t size;
+
+       size = ftell(fp);
+       size = ROUNDUP(size, XC_PAGE_SHIFT);
+       munmap(lu_status->dump_state + size, lu_status->dump_size - size);
+       lu_status->dump_size = size;
+
+       fclose(fp);
+}
 #else
 static const char *lu_binary(const void *ctx, struct connection *conn,
                             const char *filename)
@@ -308,6 +349,27 @@ static const char *lu_arch(const void *ctx, struct connection *conn,
        errno = EINVAL;
        return NULL;
 }
+
+static FILE *lu_dump_open(const void *ctx)
+{
+       char *filename;
+       int fd;
+
+       filename = talloc_asprintf(ctx, "%s/state_dump", xs_daemon_rootdir());
+       if (!filename)
+               return NULL;
+
+       fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+       if (fd < 0)
+               return NULL;
+
+       return fdopen(fd, "w");
+}
+
+static void lu_dump_close(FILE *fp)
+{
+       fclose(fp);
+}
 #endif
 
 static bool lu_check_lu_allowed(void)
@@ -347,7 +409,45 @@ static const char *lu_reject_reason(const void *ctx)
 
 static const char *lu_dump_state(const void *ctx, struct connection *conn)
 {
-       return NULL;
+       FILE *fp;
+       const char *ret;
+       struct xs_state_record_header end;
+       struct xs_state_preamble pre;
+
+       fp = lu_dump_open(ctx);
+       if (!fp)
+               return "Dump state open error";
+
+       memcpy(pre.ident, XS_STATE_IDENT, sizeof(pre.ident));
+       pre.version = htobe32(XS_STATE_VERSION);
+       pre.flags = XS_STATE_FLAGS;
+       if (fwrite(&pre, sizeof(pre), 1, fp) != 1) {
+               ret = "Dump write error";
+               goto out;
+       }
+
+       ret = dump_state_global(fp);
+       if (ret)
+               goto out;
+       ret = dump_state_connections(fp, conn);
+       if (ret)
+               goto out;
+       ret = dump_state_special_nodes(fp);
+       if (ret)
+               goto out;
+       ret = dump_state_nodes(fp, ctx);
+       if (ret)
+               goto out;
+
+       end.type = XS_STATE_TYPE_END;
+       end.length = 0;
+       if (fwrite(&end, sizeof(end), 1, fp) != 1)
+               ret = "Dump write error";
+
+ out:
+       lu_dump_close(fp);
+
+       return ret;
 }
 
 static const char *lu_activate_binary(const void *ctx)
index 6f556d49ef021dd39f0942e378a015bb1f253a1e..97f9f0ffa924a0cc1eae9fe3e0ce6b9e75eaa8b7 100644 (file)
@@ -2279,6 +2279,219 @@ int main(int argc, char *argv[])
        }
 }
 
+const char *dump_state_global(FILE *fp)
+{
+       struct xs_state_record_header head;
+       struct xs_state_global glb;
+
+       head.type = XS_STATE_TYPE_GLOBAL;
+       head.length = sizeof(glb);
+       if (fwrite(&head, sizeof(head), 1, fp) != 1)
+               return "Dump global state error";
+       glb.socket_fd = sock;
+       glb.evtchn_fd = xenevtchn_fd(xce_handle);
+       if (fwrite(&glb, sizeof(glb), 1, fp) != 1)
+               return "Dump global state error";
+
+       return NULL;
+}
+
+/* Called twice: first with fp == NULL to get length, then for writing data. */
+const char *dump_state_buffered_data(FILE *fp, const struct connection *c,
+                                    const struct connection *conn,
+                                    struct xs_state_connection *sc)
+{
+       unsigned int len = 0, used;
+       struct buffered_data *out, *in = c->in;
+       bool partial = true;
+
+       if (in && c != conn) {
+               len = in->inhdr ? in->used : sizeof(in->hdr);
+               if (fp && fwrite(&in->hdr, len, 1, fp) != 1)
+                       return "Dump read data error";
+               if (!in->inhdr && in->used) {
+                       len += in->used;
+                       if (fp && fwrite(in->buffer, in->used, 1, fp) != 1)
+                               return "Dump read data error";
+               }
+       }
+
+       if (sc) {
+               sc->data_in_len = len;
+               sc->data_resp_len = 0;
+       }
+
+       len = 0;
+
+       list_for_each_entry(out, &c->out_list, list) {
+               used = out->used;
+               if (out->inhdr) {
+                       if (!used)
+                               partial = false;
+                       if (fp && fwrite(out->hdr.raw + out->used,
+                                 sizeof(out->hdr) - out->used, 1, fp) != 1)
+                               return "Dump buffered data error";
+                       len += sizeof(out->hdr) - out->used;
+                       used = 0;
+               }
+               if (fp && out->hdr.msg.len &&
+                   fwrite(out->buffer + used, out->hdr.msg.len - used,
+                          1, fp) != 1)
+                       return "Dump buffered data error";
+               len += out->hdr.msg.len - used;
+               if (partial && sc)
+                       sc->data_resp_len = len;
+               partial = false;
+       }
+
+       /* Add "OK" for live-update command. */
+       if (c == conn) {
+               struct xsd_sockmsg msg = conn->in->hdr.msg;
+
+               msg.len = sizeof("OK");
+               if (fp && fwrite(&msg, sizeof(msg), 1, fp) != 1)
+                       return "Dump buffered data error";
+               len += sizeof(msg);
+               if (fp && fwrite("OK", msg.len, 1, fp) != 1)
+
+                       return "Dump buffered data error";
+               len += msg.len;
+       }
+
+       if (sc)
+               sc->data_out_len = len;
+
+       return NULL;
+}
+
+const char *dump_state_node_perms(FILE *fp, struct xs_state_node *sn,
+                                 const struct xs_permissions *perms,
+                                 unsigned int n_perms)
+{
+       unsigned int p;
+
+       for (p = 0; p < n_perms; p++) {
+               switch ((int)perms[p].perms & ~XS_PERM_IGNORE) {
+               case XS_PERM_READ:
+                       sn->perms[p].access = XS_STATE_NODE_PERM_READ;
+                       break;
+               case XS_PERM_WRITE:
+                       sn->perms[p].access = XS_STATE_NODE_PERM_WRITE;
+                       break;
+               case XS_PERM_READ | XS_PERM_WRITE:
+                       sn->perms[p].access = XS_STATE_NODE_PERM_BOTH;
+                       break;
+               default:
+                       sn->perms[p].access = XS_STATE_NODE_PERM_NONE;
+                       break;
+               }
+               sn->perms[p].flags = (perms[p].perms & XS_PERM_IGNORE)
+                                    ? XS_STATE_NODE_PERM_IGNORE : 0;
+               sn->perms[p].domid = perms[p].id;
+       }
+
+       if (fwrite(sn->perms, sizeof(*sn->perms), n_perms, fp) != n_perms)
+               return "Dump node permissions error";
+
+       return NULL;
+}
+
+static const char *dump_state_node_tree(FILE *fp, char *path)
+{
+       unsigned int pathlen, childlen, p = 0;
+       struct xs_state_record_header head;
+       struct xs_state_node sn;
+       TDB_DATA key, data;
+       const struct xs_tdb_record_hdr *hdr;
+       const char *child;
+       const char *ret;
+
+       pathlen = strlen(path) + 1;
+
+       set_tdb_key(path, &key);
+       data = tdb_fetch(tdb_ctx, key);
+       if (data.dptr == NULL)
+               return "Error reading node";
+
+       /* Clean up in case of failure. */
+       talloc_steal(path, data.dptr);
+
+       hdr = (void *)data.dptr;
+
+       head.type = XS_STATE_TYPE_NODE;
+       head.length = sizeof(sn);
+       sn.conn_id = 0;
+       sn.ta_id = 0;
+       sn.ta_access = 0;
+       sn.perm_n = hdr->num_perms;
+       sn.path_len = pathlen;
+       sn.data_len = hdr->datalen;
+       head.length += hdr->num_perms * sizeof(*sn.perms);
+       head.length += pathlen;
+       head.length += hdr->datalen;
+       head.length = ROUNDUP(head.length, 3);
+
+       if (fwrite(&head, sizeof(head), 1, fp) != 1)
+               return "Dump node state error";
+       if (fwrite(&sn, sizeof(sn), 1, fp) != 1)
+               return "Dump node state error";
+
+       ret = dump_state_node_perms(fp, &sn, hdr->perms, hdr->num_perms);
+       if (ret)
+               return ret;
+
+       if (fwrite(path, pathlen, 1, fp) != 1)
+               return "Dump node path error";
+       if (hdr->datalen &&
+           fwrite(hdr->perms + hdr->num_perms, hdr->datalen, 1, fp) != 1)
+               return "Dump node data error";
+
+       ret = dump_state_align(fp);
+       if (ret)
+               return ret;
+
+       child = (char *)(hdr->perms + hdr->num_perms) + hdr->datalen;
+
+       /*
+        * Use path for constructing children paths.
+        * As we don't write out nodes without having written their parent
+        * already we will never clobber a part of the path we'll need later.
+        */
+       pathlen--;
+       if (path[pathlen - 1] != '/') {
+               path[pathlen] = '/';
+               pathlen++;
+       }
+       while (p < hdr->childlen) {
+               childlen = strlen(child) + 1;
+               if (pathlen + childlen > XENSTORE_ABS_PATH_MAX)
+                       return "Dump node path length error";
+               strcpy(path + pathlen, child);
+               ret = dump_state_node_tree(fp, path);
+               if (ret)
+                       return ret;
+               p += childlen;
+               child += childlen;
+       }
+
+       talloc_free(data.dptr);
+
+       return NULL;
+}
+
+const char *dump_state_nodes(FILE *fp, const void *ctx)
+{
+       char *path;
+
+       path = talloc_size(ctx, XENSTORE_ABS_PATH_MAX);
+       if (!path)
+               return "Path buffer allocation error";
+
+       strcpy(path, "/");
+
+       return dump_state_node_tree(fp, path);
+}
+
 /*
  * Local variables:
  *  mode: C
index db70f61f0deeeac86735cef4d8f67dbec83edd6f..22287ddfe94a303d9f18cda0d2395121ded4ea9d 100644 (file)
@@ -30,6 +30,7 @@
 #include <errno.h>
 
 #include "xenstore_lib.h"
+#include "xenstore_state.h"
 #include "list.h"
 #include "tdb.h"
 #include "hashtable.h"
@@ -41,6 +42,8 @@ typedef int32_t wrl_creditt;
 #define WRL_CREDIT_MAX (1000*1000*1000)
 /* ^ satisfies non-overflow condition for wrl_xfer_credit */
 
+struct xs_state_connection;
+
 struct buffered_data
 {
        struct list_head list;
@@ -245,6 +248,15 @@ int remember_string(struct hashtable *hash, const char *str);
 
 void set_tdb_key(const char *name, TDB_DATA *key);
 
+const char *dump_state_global(FILE *fp);
+const char *dump_state_buffered_data(FILE *fp, const struct connection *c,
+                                    const struct connection *conn,
+                                    struct xs_state_connection *sc);
+const char *dump_state_nodes(FILE *fp, const void *ctx);
+const char *dump_state_node_perms(FILE *fp, struct xs_state_node *sn,
+                                 const struct xs_permissions *perms,
+                                 unsigned int n_perms);
+
 #endif /* _XENSTORED_CORE_H */
 
 /*
index ed8e83b06b40a6e1b38256f39a8f800166edbe5b..919a4d98cf8bf89add4e092e3348cd57903bb1ca 100644 (file)
@@ -1143,6 +1143,111 @@ void wrl_apply_debit_trans_commit(struct connection *conn)
        wrl_apply_debit_actual(conn->domain);
 }
 
+const char *dump_state_connections(FILE *fp, struct connection *conn)
+{
+       const char *ret = NULL;
+       unsigned int conn_id = 1;
+       struct xs_state_connection sc;
+       struct xs_state_record_header head;
+       struct connection *c;
+
+       list_for_each_entry(c, &connections, list) {
+               head.type = XS_STATE_TYPE_CONN;
+               head.length = sizeof(sc);
+
+               sc.conn_id = conn_id++;
+               sc.pad = 0;
+               memset(&sc.spec, 0, sizeof(sc.spec));
+               if (c->domain) {
+                       sc.conn_type = XS_STATE_CONN_TYPE_RING;
+                       sc.spec.ring.domid = c->id;
+                       sc.spec.ring.tdomid = c->target ? c->target->id
+                                               : DOMID_INVALID;
+                       sc.spec.ring.evtchn = c->domain->port;
+               } else {
+                       sc.conn_type = XS_STATE_CONN_TYPE_SOCKET;
+                       sc.spec.socket_fd = c->fd;
+               }
+
+               ret = dump_state_buffered_data(NULL, c, conn, &sc);
+               if (ret)
+                       return ret;
+               head.length += sc.data_in_len + sc.data_out_len;
+               head.length = ROUNDUP(head.length, 3);
+               if (fwrite(&head, sizeof(head), 1, fp) != 1)
+                       return "Dump connection state error";
+               if (fwrite(&sc, offsetof(struct xs_state_connection, data),
+                          1, fp) != 1)
+                       return "Dump connection state error";
+               ret = dump_state_buffered_data(fp, c, conn, NULL);
+               if (ret)
+                       return ret;
+               ret = dump_state_align(fp);
+               if (ret)
+                       return ret;
+
+               ret = dump_state_watches(fp, c, sc.conn_id);
+               if (ret)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static const char *dump_state_special_node(FILE *fp, const char *name,
+                                          const struct node_perms *perms)
+{
+       struct xs_state_record_header head;
+       struct xs_state_node sn;
+       unsigned int pathlen;
+       const char *ret;
+
+       pathlen = strlen(name) + 1;
+
+       head.type = XS_STATE_TYPE_NODE;
+       head.length = sizeof(sn);
+
+       sn.conn_id = 0;
+       sn.ta_id = 0;
+       sn.ta_access = 0;
+       sn.perm_n = perms->num;
+       sn.path_len = pathlen;
+       sn.data_len = 0;
+       head.length += perms->num * sizeof(*sn.perms);
+       head.length += pathlen;
+       head.length = ROUNDUP(head.length, 3);
+       if (fwrite(&head, sizeof(head), 1, fp) != 1)
+               return "Dump special node error";
+       if (fwrite(&sn, sizeof(sn), 1, fp) != 1)
+               return "Dump special node error";
+
+       ret = dump_state_node_perms(fp, &sn, perms->p, perms->num);
+       if (ret)
+               return ret;
+
+       if (fwrite(name, pathlen, 1, fp) != 1)
+               return "Dump special node path error";
+
+       ret = dump_state_align(fp);
+
+       return ret;
+}
+
+const char *dump_state_special_nodes(FILE *fp)
+{
+       const char *ret;
+
+       ret = dump_state_special_node(fp, "@releaseDomain",
+                                     &dom_release_perms);
+       if (ret)
+               return ret;
+
+       ret = dump_state_special_node(fp, "@introduceDomain",
+                                     &dom_introduce_perms);
+
+       return ret;
+}
+
 /*
  * Local variables:
  *  mode: C
index 66e0a12654420db32177ec1bbfbc9eff704e80b5..413b97437566e6e20c4fa532ce1a0eb7cf7695f7 100644 (file)
@@ -97,4 +97,7 @@ void wrl_log_periodic(struct wrl_timestampt now);
 void wrl_apply_debit_direct(struct connection *conn);
 void wrl_apply_debit_trans_commit(struct connection *conn);
 
+const char *dump_state_connections(FILE *fp, struct connection *conn);
+const char *dump_state_special_nodes(FILE *fp);
+
 #endif /* _XENSTORED_DOMAIN_H */
index 9ff20690c0003e3d152115ce28cb2ebf80d34216..9248f08bd934ddcafa538bfef5f9e9e6a325b9c1 100644 (file)
@@ -72,6 +72,19 @@ static bool is_child(const char *child, const char *parent)
        return child[len] == '/' || child[len] == '\0';
 }
 
+static const char *get_watch_path(const struct watch *watch, const char *name)
+{
+       const char *path = name;
+
+       if (watch->relative_path) {
+               path += strlen(watch->relative_path);
+               if (*path == '/') /* Could be "" */
+                       path++;
+       }
+
+       return path;
+}
+
 /*
  * Send a watch event.
  * Temporary memory allocations are done with ctx.
@@ -85,11 +98,7 @@ static void add_event(struct connection *conn,
        unsigned int len;
        char *data;
 
-       if (watch->relative_path) {
-               name += strlen(watch->relative_path);
-               if (*name == '/') /* Could be "" */
-                       name++;
-       }
+       name = get_watch_path(watch, name);
 
        len = strlen(name) + 1 + strlen(watch->token) + 1;
        /* Don't try to send over-long events. */
@@ -291,6 +300,44 @@ void conn_delete_all_watches(struct connection *conn)
        }
 }
 
+const char *dump_state_watches(FILE *fp, struct connection *conn,
+                              unsigned int conn_id)
+{
+       const char *ret = NULL;
+       struct watch *watch;
+       struct xs_state_watch sw;
+       struct xs_state_record_header head;
+       const char *path;
+
+       head.type = XS_STATE_TYPE_WATCH;
+
+       list_for_each_entry(watch, &conn->watches, list) {
+               head.length = sizeof(sw);
+
+               sw.conn_id = conn_id;
+               path = get_watch_path(watch, watch->node);
+               sw.path_length = strlen(path) + 1;
+               sw.token_length = strlen(watch->token) + 1;
+               head.length += sw.path_length + sw.token_length;
+               head.length = ROUNDUP(head.length, 3);
+               if (fwrite(&head, sizeof(head), 1, fp) != 1)
+                       return "Dump watch state error";
+               if (fwrite(&sw, sizeof(sw), 1, fp) != 1)
+                       return "Dump watch state error";
+
+               if (fwrite(path, sw.path_length, 1, fp) != 1)
+                       return "Dump watch path error";
+               if (fwrite(watch->token, sw.token_length, 1, fp) != 1)
+                       return "Dump watch token error";
+
+               ret = dump_state_align(fp);
+               if (ret)
+                       return ret;
+       }
+
+       return ret;
+}
+
 /*
  * Local variables:
  *  mode: C
index 03094374f3793532ff1455498ea2259403020f05..3d81645f452d03a27f9a9c07574722e1ddf5175e 100644 (file)
@@ -30,4 +30,7 @@ void fire_watches(struct connection *conn, const void *tmp, const char *name,
 
 void conn_delete_all_watches(struct connection *conn);
 
+const char *dump_state_watches(FILE *fp, struct connection *conn,
+                              unsigned int conn_id);
+
 #endif /* _XENSTORED_WATCH_H */