13ef7ff83SDavid Woodhouse /* 23ef7ff83SDavid Woodhouse * QEMU XenStore XsNode testing 33ef7ff83SDavid Woodhouse * 43ef7ff83SDavid Woodhouse * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. 53ef7ff83SDavid Woodhouse 63ef7ff83SDavid Woodhouse * This work is licensed under the terms of the GNU GPL, version 2 or later. 73ef7ff83SDavid Woodhouse * See the COPYING file in the top-level directory. 83ef7ff83SDavid Woodhouse */ 93ef7ff83SDavid Woodhouse 103ef7ff83SDavid Woodhouse #include "qemu/osdep.h" 113ef7ff83SDavid Woodhouse #include "qapi/error.h" 123ef7ff83SDavid Woodhouse #include "qemu/module.h" 133ef7ff83SDavid Woodhouse 143ef7ff83SDavid Woodhouse static int nr_xs_nodes; 153ef7ff83SDavid Woodhouse static GList *xs_node_list; 163ef7ff83SDavid Woodhouse 173ef7ff83SDavid Woodhouse #define XS_NODE_UNIT_TEST 183ef7ff83SDavid Woodhouse 193ef7ff83SDavid Woodhouse /* 203ef7ff83SDavid Woodhouse * We don't need the core Xen definitions. And we *do* want to be able 213ef7ff83SDavid Woodhouse * to run the unit tests even on architectures that Xen doesn't support 223ef7ff83SDavid Woodhouse * (because life's too short to bother doing otherwise, and test coverage 233ef7ff83SDavid Woodhouse * doesn't hurt). 243ef7ff83SDavid Woodhouse */ 253ef7ff83SDavid Woodhouse #define __XEN_PUBLIC_XEN_H__ 263ef7ff83SDavid Woodhouse 273ef7ff83SDavid Woodhouse #include "hw/i386/kvm/xenstore_impl.c" 283ef7ff83SDavid Woodhouse 293ef7ff83SDavid Woodhouse #define DOMID_QEMU 0 303ef7ff83SDavid Woodhouse #define DOMID_GUEST 1 313ef7ff83SDavid Woodhouse 323ef7ff83SDavid Woodhouse /* This doesn't happen in qemu but we want to make valgrind happy */ 333ef7ff83SDavid Woodhouse static void xs_impl_delete(XenstoreImplState *s) 343ef7ff83SDavid Woodhouse { 353ef7ff83SDavid Woodhouse int err; 363ef7ff83SDavid Woodhouse 37*6e133009SDavid Woodhouse xs_impl_reset_watches(s, DOMID_GUEST); 38*6e133009SDavid Woodhouse g_assert(!s->nr_domu_watches); 39*6e133009SDavid Woodhouse 403ef7ff83SDavid Woodhouse err = xs_impl_rm(s, DOMID_QEMU, XBT_NULL, "/local"); 413ef7ff83SDavid Woodhouse g_assert(!err); 423ef7ff83SDavid Woodhouse g_assert(s->nr_nodes == 1); 433ef7ff83SDavid Woodhouse 44*6e133009SDavid Woodhouse g_hash_table_unref(s->watches); 453ef7ff83SDavid Woodhouse xs_node_unref(s->root); 463ef7ff83SDavid Woodhouse g_free(s); 473ef7ff83SDavid Woodhouse 483ef7ff83SDavid Woodhouse if (xs_node_list) { 493ef7ff83SDavid Woodhouse GList *l; 503ef7ff83SDavid Woodhouse for (l = xs_node_list; l; l = l->next) { 513ef7ff83SDavid Woodhouse XsNode *n = l->data; 523ef7ff83SDavid Woodhouse printf("Remaining node at %p name %s ref %u\n", n, n->name, 533ef7ff83SDavid Woodhouse n->ref); 543ef7ff83SDavid Woodhouse } 553ef7ff83SDavid Woodhouse } 563ef7ff83SDavid Woodhouse g_assert(!nr_xs_nodes); 573ef7ff83SDavid Woodhouse } 583ef7ff83SDavid Woodhouse 593ef7ff83SDavid Woodhouse static int write_str(XenstoreImplState *s, unsigned int dom_id, 603ef7ff83SDavid Woodhouse unsigned int tx_id, const char *path, 613ef7ff83SDavid Woodhouse const char *content) 623ef7ff83SDavid Woodhouse { 633ef7ff83SDavid Woodhouse GByteArray *d = g_byte_array_new(); 643ef7ff83SDavid Woodhouse int err; 653ef7ff83SDavid Woodhouse 663ef7ff83SDavid Woodhouse g_byte_array_append(d, (void *)content, strlen(content)); 673ef7ff83SDavid Woodhouse err = xs_impl_write(s, dom_id, tx_id, path, d); 683ef7ff83SDavid Woodhouse g_byte_array_unref(d); 693ef7ff83SDavid Woodhouse return err; 703ef7ff83SDavid Woodhouse } 713ef7ff83SDavid Woodhouse 72*6e133009SDavid Woodhouse static void watch_cb(void *_str, const char *path, const char *token) 73*6e133009SDavid Woodhouse { 74*6e133009SDavid Woodhouse GString *str = _str; 75*6e133009SDavid Woodhouse 76*6e133009SDavid Woodhouse g_string_append(str, path); 77*6e133009SDavid Woodhouse g_string_append(str, token); 78*6e133009SDavid Woodhouse } 79*6e133009SDavid Woodhouse 803ef7ff83SDavid Woodhouse static XenstoreImplState *setup(void) 813ef7ff83SDavid Woodhouse { 823ef7ff83SDavid Woodhouse XenstoreImplState *s = xs_impl_create(); 833ef7ff83SDavid Woodhouse char *abspath; 843ef7ff83SDavid Woodhouse int err; 853ef7ff83SDavid Woodhouse 863ef7ff83SDavid Woodhouse abspath = g_strdup_printf("/local/domain/%u", DOMID_GUEST); 873ef7ff83SDavid Woodhouse 883ef7ff83SDavid Woodhouse err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, ""); 893ef7ff83SDavid Woodhouse g_assert(!err); 90*6e133009SDavid Woodhouse g_assert(s->nr_nodes == 4); 913ef7ff83SDavid Woodhouse 923ef7ff83SDavid Woodhouse g_free(abspath); 933ef7ff83SDavid Woodhouse 943ef7ff83SDavid Woodhouse abspath = g_strdup_printf("/local/domain/%u/some", DOMID_GUEST); 953ef7ff83SDavid Woodhouse 963ef7ff83SDavid Woodhouse err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, ""); 973ef7ff83SDavid Woodhouse g_assert(!err); 983ef7ff83SDavid Woodhouse g_assert(s->nr_nodes == 5); 993ef7ff83SDavid Woodhouse 1003ef7ff83SDavid Woodhouse g_free(abspath); 1013ef7ff83SDavid Woodhouse 1023ef7ff83SDavid Woodhouse return s; 1033ef7ff83SDavid Woodhouse } 1043ef7ff83SDavid Woodhouse 1053ef7ff83SDavid Woodhouse static void test_xs_node_simple(void) 1063ef7ff83SDavid Woodhouse { 1073ef7ff83SDavid Woodhouse GByteArray *data = g_byte_array_new(); 1083ef7ff83SDavid Woodhouse XenstoreImplState *s = setup(); 109*6e133009SDavid Woodhouse GString *guest_watches = g_string_new(NULL); 110*6e133009SDavid Woodhouse GString *qemu_watches = g_string_new(NULL); 1113ef7ff83SDavid Woodhouse GList *items = NULL; 1123ef7ff83SDavid Woodhouse XsNode *old_root; 1133ef7ff83SDavid Woodhouse uint64_t gencnt; 1143ef7ff83SDavid Woodhouse int err; 1153ef7ff83SDavid Woodhouse 1163ef7ff83SDavid Woodhouse g_assert(s); 1173ef7ff83SDavid Woodhouse 118*6e133009SDavid Woodhouse err = xs_impl_watch(s, DOMID_GUEST, "some", "guestwatch", 119*6e133009SDavid Woodhouse watch_cb, guest_watches); 120*6e133009SDavid Woodhouse g_assert(!err); 121*6e133009SDavid Woodhouse g_assert(guest_watches->len == strlen("someguestwatch")); 122*6e133009SDavid Woodhouse g_assert(!strcmp(guest_watches->str, "someguestwatch")); 123*6e133009SDavid Woodhouse g_string_truncate(guest_watches, 0); 124*6e133009SDavid Woodhouse 125*6e133009SDavid Woodhouse err = xs_impl_watch(s, 0, "/local/domain/1/some", "qemuwatch", 126*6e133009SDavid Woodhouse watch_cb, qemu_watches); 127*6e133009SDavid Woodhouse g_assert(!err); 128*6e133009SDavid Woodhouse g_assert(qemu_watches->len == strlen("/local/domain/1/someqemuwatch")); 129*6e133009SDavid Woodhouse g_assert(!strcmp(qemu_watches->str, "/local/domain/1/someqemuwatch")); 130*6e133009SDavid Woodhouse g_string_truncate(qemu_watches, 0); 131*6e133009SDavid Woodhouse 1323ef7ff83SDavid Woodhouse /* Read gives ENOENT when it should */ 1333ef7ff83SDavid Woodhouse err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "foo", data); 1343ef7ff83SDavid Woodhouse g_assert(err == ENOENT); 1353ef7ff83SDavid Woodhouse 1363ef7ff83SDavid Woodhouse /* Write works */ 1373ef7ff83SDavid Woodhouse err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", 1383ef7ff83SDavid Woodhouse "something"); 1393ef7ff83SDavid Woodhouse g_assert(s->nr_nodes == 7); 1403ef7ff83SDavid Woodhouse g_assert(!err); 141*6e133009SDavid Woodhouse g_assert(!strcmp(guest_watches->str, 142*6e133009SDavid Woodhouse "some/relative/pathguestwatch")); 143*6e133009SDavid Woodhouse g_assert(!strcmp(qemu_watches->str, 144*6e133009SDavid Woodhouse "/local/domain/1/some/relative/pathqemuwatch")); 145*6e133009SDavid Woodhouse 146*6e133009SDavid Woodhouse g_string_truncate(qemu_watches, 0); 147*6e133009SDavid Woodhouse g_string_truncate(guest_watches, 0); 148*6e133009SDavid Woodhouse xs_impl_reset_watches(s, 0); 1493ef7ff83SDavid Woodhouse 1503ef7ff83SDavid Woodhouse /* Read gives back what we wrote */ 1513ef7ff83SDavid Woodhouse err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); 1523ef7ff83SDavid Woodhouse g_assert(!err); 1533ef7ff83SDavid Woodhouse g_assert(data->len == strlen("something")); 1543ef7ff83SDavid Woodhouse g_assert(!memcmp(data->data, "something", data->len)); 1553ef7ff83SDavid Woodhouse 1563ef7ff83SDavid Woodhouse /* Even if we use an abolute path */ 1573ef7ff83SDavid Woodhouse g_byte_array_set_size(data, 0); 1583ef7ff83SDavid Woodhouse err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, 1593ef7ff83SDavid Woodhouse "/local/domain/1/some/relative/path", data); 1603ef7ff83SDavid Woodhouse g_assert(!err); 1613ef7ff83SDavid Woodhouse g_assert(data->len == strlen("something")); 1623ef7ff83SDavid Woodhouse 163*6e133009SDavid Woodhouse g_assert(!qemu_watches->len); 164*6e133009SDavid Woodhouse g_assert(!guest_watches->len); 1653ef7ff83SDavid Woodhouse /* Keep a copy, to force COW mode */ 1663ef7ff83SDavid Woodhouse old_root = xs_node_ref(s->root); 1673ef7ff83SDavid Woodhouse 1683ef7ff83SDavid Woodhouse /* Write works again */ 1693ef7ff83SDavid Woodhouse err = write_str(s, DOMID_GUEST, XBT_NULL, 1703ef7ff83SDavid Woodhouse "/local/domain/1/some/relative/path2", 1713ef7ff83SDavid Woodhouse "something else"); 1723ef7ff83SDavid Woodhouse g_assert(!err); 1733ef7ff83SDavid Woodhouse g_assert(s->nr_nodes == 8); 174*6e133009SDavid Woodhouse g_assert(!qemu_watches->len); 175*6e133009SDavid Woodhouse g_assert(!strcmp(guest_watches->str, "some/relative/path2guestwatch")); 176*6e133009SDavid Woodhouse g_string_truncate(guest_watches, 0); 1773ef7ff83SDavid Woodhouse 1783ef7ff83SDavid Woodhouse /* Overwrite an existing node */ 1793ef7ff83SDavid Woodhouse err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", 1803ef7ff83SDavid Woodhouse "another thing"); 1813ef7ff83SDavid Woodhouse g_assert(!err); 1823ef7ff83SDavid Woodhouse g_assert(s->nr_nodes == 8); 183*6e133009SDavid Woodhouse g_assert(!qemu_watches->len); 184*6e133009SDavid Woodhouse g_assert(!strcmp(guest_watches->str, "some/relative/pathguestwatch")); 185*6e133009SDavid Woodhouse g_string_truncate(guest_watches, 0); 1863ef7ff83SDavid Woodhouse 1873ef7ff83SDavid Woodhouse /* We can list the two files we wrote */ 1883ef7ff83SDavid Woodhouse err = xs_impl_directory(s, DOMID_GUEST, XBT_NULL, "some/relative", &gencnt, 1893ef7ff83SDavid Woodhouse &items); 1903ef7ff83SDavid Woodhouse g_assert(!err); 1913ef7ff83SDavid Woodhouse g_assert(items); 1923ef7ff83SDavid Woodhouse g_assert(gencnt == 2); 1933ef7ff83SDavid Woodhouse g_assert(!strcmp(items->data, "path")); 1943ef7ff83SDavid Woodhouse g_assert(items->next); 1953ef7ff83SDavid Woodhouse g_assert(!strcmp(items->next->data, "path2")); 1963ef7ff83SDavid Woodhouse g_assert(!items->next->next); 1973ef7ff83SDavid Woodhouse g_list_free_full(items, g_free); 1983ef7ff83SDavid Woodhouse 199*6e133009SDavid Woodhouse err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch", 200*6e133009SDavid Woodhouse watch_cb, guest_watches); 201*6e133009SDavid Woodhouse g_assert(!err); 202*6e133009SDavid Woodhouse 203*6e133009SDavid Woodhouse err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch", 204*6e133009SDavid Woodhouse watch_cb, guest_watches); 205*6e133009SDavid Woodhouse g_assert(err == ENOENT); 206*6e133009SDavid Woodhouse 207*6e133009SDavid Woodhouse err = xs_impl_watch(s, DOMID_GUEST, "some/relative/path2", "watchp2", 208*6e133009SDavid Woodhouse watch_cb, guest_watches); 209*6e133009SDavid Woodhouse g_assert(!err); 210*6e133009SDavid Woodhouse g_assert(guest_watches->len == strlen("some/relative/path2watchp2")); 211*6e133009SDavid Woodhouse g_assert(!strcmp(guest_watches->str, "some/relative/path2watchp2")); 212*6e133009SDavid Woodhouse g_string_truncate(guest_watches, 0); 213*6e133009SDavid Woodhouse 214*6e133009SDavid Woodhouse err = xs_impl_watch(s, DOMID_GUEST, "/local/domain/1/some/relative", 215*6e133009SDavid Woodhouse "watchrel", watch_cb, guest_watches); 216*6e133009SDavid Woodhouse g_assert(!err); 217*6e133009SDavid Woodhouse g_assert(guest_watches->len == 218*6e133009SDavid Woodhouse strlen("/local/domain/1/some/relativewatchrel")); 219*6e133009SDavid Woodhouse g_assert(!strcmp(guest_watches->str, 220*6e133009SDavid Woodhouse "/local/domain/1/some/relativewatchrel")); 221*6e133009SDavid Woodhouse g_string_truncate(guest_watches, 0); 222*6e133009SDavid Woodhouse 2233ef7ff83SDavid Woodhouse /* Write somewhere else which already existed */ 2243ef7ff83SDavid Woodhouse err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "moredata"); 2253ef7ff83SDavid Woodhouse g_assert(!err); 226*6e133009SDavid Woodhouse g_assert(s->nr_nodes == 8); 227*6e133009SDavid Woodhouse 228*6e133009SDavid Woodhouse g_assert(!strcmp(guest_watches->str, 229*6e133009SDavid Woodhouse "/local/domain/1/some/relativewatchrel")); 230*6e133009SDavid Woodhouse g_string_truncate(guest_watches, 0); 2313ef7ff83SDavid Woodhouse 2323ef7ff83SDavid Woodhouse g_byte_array_set_size(data, 0); 2333ef7ff83SDavid Woodhouse err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); 2343ef7ff83SDavid Woodhouse g_assert(!err); 2353ef7ff83SDavid Woodhouse g_assert(data->len == strlen("moredata")); 2363ef7ff83SDavid Woodhouse g_assert(!memcmp(data->data, "moredata", data->len)); 2373ef7ff83SDavid Woodhouse 2383ef7ff83SDavid Woodhouse /* Overwrite existing data */ 2393ef7ff83SDavid Woodhouse err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "otherdata"); 2403ef7ff83SDavid Woodhouse g_assert(!err); 241*6e133009SDavid Woodhouse g_string_truncate(guest_watches, 0); 2423ef7ff83SDavid Woodhouse 2433ef7ff83SDavid Woodhouse g_byte_array_set_size(data, 0); 2443ef7ff83SDavid Woodhouse err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); 2453ef7ff83SDavid Woodhouse g_assert(!err); 2463ef7ff83SDavid Woodhouse g_assert(data->len == strlen("otherdata")); 2473ef7ff83SDavid Woodhouse g_assert(!memcmp(data->data, "otherdata", data->len)); 2483ef7ff83SDavid Woodhouse 2493ef7ff83SDavid Woodhouse /* Remove the subtree */ 2503ef7ff83SDavid Woodhouse err = xs_impl_rm(s, DOMID_GUEST, XBT_NULL, "some/relative"); 2513ef7ff83SDavid Woodhouse g_assert(!err); 2523ef7ff83SDavid Woodhouse g_assert(s->nr_nodes == 5); 2533ef7ff83SDavid Woodhouse 254*6e133009SDavid Woodhouse /* Each watch fires with the least specific relevant path */ 255*6e133009SDavid Woodhouse g_assert(strstr(guest_watches->str, 256*6e133009SDavid Woodhouse "some/relative/path2watchp2")); 257*6e133009SDavid Woodhouse g_assert(strstr(guest_watches->str, 258*6e133009SDavid Woodhouse "/local/domain/1/some/relativewatchrel")); 259*6e133009SDavid Woodhouse g_string_truncate(guest_watches, 0); 260*6e133009SDavid Woodhouse 2613ef7ff83SDavid Woodhouse g_byte_array_set_size(data, 0); 2623ef7ff83SDavid Woodhouse err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); 2633ef7ff83SDavid Woodhouse g_assert(err == ENOENT); 2643ef7ff83SDavid Woodhouse g_byte_array_unref(data); 2653ef7ff83SDavid Woodhouse 266*6e133009SDavid Woodhouse xs_impl_reset_watches(s, DOMID_GUEST); 267*6e133009SDavid Woodhouse g_string_free(qemu_watches, true); 268*6e133009SDavid Woodhouse g_string_free(guest_watches, true); 2693ef7ff83SDavid Woodhouse xs_node_unref(old_root); 2703ef7ff83SDavid Woodhouse xs_impl_delete(s); 2713ef7ff83SDavid Woodhouse } 2723ef7ff83SDavid Woodhouse 2733ef7ff83SDavid Woodhouse 2743ef7ff83SDavid Woodhouse int main(int argc, char **argv) 2753ef7ff83SDavid Woodhouse { 2763ef7ff83SDavid Woodhouse g_test_init(&argc, &argv, NULL); 2773ef7ff83SDavid Woodhouse module_call_init(MODULE_INIT_QOM); 2783ef7ff83SDavid Woodhouse 2793ef7ff83SDavid Woodhouse g_test_add_func("/xs_node/simple", test_xs_node_simple); 2803ef7ff83SDavid Woodhouse 2813ef7ff83SDavid Woodhouse return g_test_run(); 2823ef7ff83SDavid Woodhouse } 283