1681c28a3SPeter Maydell #include "qemu/osdep.h" 262c39b30SMarc-André Lureau #include <locale.h> 362c39b30SMarc-André Lureau #include <glib/gstdio.h> 462c39b30SMarc-André Lureau #include <sys/socket.h> 562c39b30SMarc-André Lureau #include <sys/un.h> 662c39b30SMarc-André Lureau 7907b5105SMarc-André Lureau #include "../qtest/libqtest.h" 8452fcdbcSMarkus Armbruster #include "qapi/qmp/qdict.h" 947e6b297SMarkus Armbruster #include "qapi/qmp/qlist.h" 1062c39b30SMarc-André Lureau 1162c39b30SMarc-André Lureau typedef struct { 1262c39b30SMarc-André Lureau char *test_dir; 1362c39b30SMarc-André Lureau GMainLoop *loop; 1462c39b30SMarc-André Lureau int fd; 1562c39b30SMarc-André Lureau GPid pid; 1662c39b30SMarc-André Lureau } TestFixture; 1762c39b30SMarc-André Lureau 1862c39b30SMarc-André Lureau static int connect_qga(char *path) 1962c39b30SMarc-André Lureau { 2062c39b30SMarc-André Lureau int s, ret, len, i = 0; 2162c39b30SMarc-André Lureau struct sockaddr_un remote; 2262c39b30SMarc-André Lureau 2362c39b30SMarc-André Lureau s = socket(AF_UNIX, SOCK_STREAM, 0); 2462c39b30SMarc-André Lureau g_assert(s != -1); 2562c39b30SMarc-André Lureau 2662c39b30SMarc-André Lureau remote.sun_family = AF_UNIX; 2762c39b30SMarc-André Lureau do { 2862c39b30SMarc-André Lureau strcpy(remote.sun_path, path); 2962c39b30SMarc-André Lureau len = strlen(remote.sun_path) + sizeof(remote.sun_family); 3062c39b30SMarc-André Lureau ret = connect(s, (struct sockaddr *)&remote, len); 3162c39b30SMarc-André Lureau if (ret == -1) { 3262c39b30SMarc-André Lureau g_usleep(G_USEC_PER_SEC); 3362c39b30SMarc-André Lureau } 3462c39b30SMarc-André Lureau if (i++ == 10) { 3562c39b30SMarc-André Lureau return -1; 3662c39b30SMarc-André Lureau } 3762c39b30SMarc-André Lureau } while (ret == -1); 3862c39b30SMarc-André Lureau 3962c39b30SMarc-André Lureau return s; 4062c39b30SMarc-André Lureau } 4162c39b30SMarc-André Lureau 4262c39b30SMarc-André Lureau static void qga_watch(GPid pid, gint status, gpointer user_data) 4362c39b30SMarc-André Lureau { 4462c39b30SMarc-André Lureau TestFixture *fixture = user_data; 4562c39b30SMarc-André Lureau 4662c39b30SMarc-André Lureau g_assert_cmpint(status, ==, 0); 4762c39b30SMarc-André Lureau g_main_loop_quit(fixture->loop); 4862c39b30SMarc-André Lureau } 4962c39b30SMarc-André Lureau 5062c39b30SMarc-André Lureau static void 51c28afa76STomáš Golembiovský fixture_setup(TestFixture *fixture, gconstpointer data, gchar **envp) 5262c39b30SMarc-André Lureau { 5362c39b30SMarc-André Lureau const gchar *extra_arg = data; 5462c39b30SMarc-André Lureau GError *error = NULL; 55bb6960a1SMarc-André Lureau g_autofree char *cwd = NULL; 56bb6960a1SMarc-André Lureau g_autofree char *path = NULL; 57bb6960a1SMarc-André Lureau g_autofree char *cmd = NULL; 58bb6960a1SMarc-André Lureau g_auto(GStrv) argv = NULL; 5962c39b30SMarc-André Lureau 6062c39b30SMarc-André Lureau fixture->loop = g_main_loop_new(NULL, FALSE); 6162c39b30SMarc-André Lureau 6262c39b30SMarc-André Lureau fixture->test_dir = g_strdup("/tmp/qgatest.XXXXXX"); 633c239aa7SBin Meng g_assert_nonnull(g_mkdtemp(fixture->test_dir)); 6462c39b30SMarc-André Lureau 6562c39b30SMarc-André Lureau path = g_build_filename(fixture->test_dir, "sock", NULL); 6662c39b30SMarc-André Lureau cwd = g_get_current_dir(); 67f15bff25SPaolo Bonzini cmd = g_strdup_printf("%s%cqga%cqemu-ga -m unix-listen -t %s -p %s %s %s", 68f15bff25SPaolo Bonzini cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR, 6962c39b30SMarc-André Lureau fixture->test_dir, path, 7062c39b30SMarc-André Lureau getenv("QTEST_LOG") ? "-v" : "", 7162c39b30SMarc-André Lureau extra_arg ?: ""); 7262c39b30SMarc-André Lureau g_shell_parse_argv(cmd, NULL, &argv, &error); 7362c39b30SMarc-André Lureau g_assert_no_error(error); 7462c39b30SMarc-André Lureau 75c28afa76STomáš Golembiovský g_spawn_async(fixture->test_dir, argv, envp, 7662c39b30SMarc-André Lureau G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, 7762c39b30SMarc-André Lureau NULL, NULL, &fixture->pid, &error); 7862c39b30SMarc-André Lureau g_assert_no_error(error); 7962c39b30SMarc-André Lureau 8062c39b30SMarc-André Lureau g_child_watch_add(fixture->pid, qga_watch, fixture); 8162c39b30SMarc-André Lureau 8262c39b30SMarc-André Lureau fixture->fd = connect_qga(path); 8362c39b30SMarc-André Lureau g_assert_cmpint(fixture->fd, !=, -1); 8462c39b30SMarc-André Lureau } 8562c39b30SMarc-André Lureau 8662c39b30SMarc-André Lureau static void 8762c39b30SMarc-André Lureau fixture_tear_down(TestFixture *fixture, gconstpointer data) 8862c39b30SMarc-André Lureau { 89bb6960a1SMarc-André Lureau g_autofree char *tmp = NULL; 9062c39b30SMarc-André Lureau 9162c39b30SMarc-André Lureau kill(fixture->pid, SIGTERM); 9262c39b30SMarc-André Lureau 9362c39b30SMarc-André Lureau g_main_loop_run(fixture->loop); 9462c39b30SMarc-André Lureau g_main_loop_unref(fixture->loop); 9562c39b30SMarc-André Lureau 9662c39b30SMarc-André Lureau g_spawn_close_pid(fixture->pid); 9762c39b30SMarc-André Lureau 9862c39b30SMarc-André Lureau tmp = g_build_filename(fixture->test_dir, "foo", NULL); 9962c39b30SMarc-André Lureau g_unlink(tmp); 10062c39b30SMarc-André Lureau g_free(tmp); 10162c39b30SMarc-André Lureau 10262c39b30SMarc-André Lureau tmp = g_build_filename(fixture->test_dir, "qga.state", NULL); 10362c39b30SMarc-André Lureau g_unlink(tmp); 10462c39b30SMarc-André Lureau g_free(tmp); 10562c39b30SMarc-André Lureau 10662c39b30SMarc-André Lureau tmp = g_build_filename(fixture->test_dir, "sock", NULL); 10762c39b30SMarc-André Lureau g_unlink(tmp); 10862c39b30SMarc-André Lureau 10962c39b30SMarc-André Lureau g_rmdir(fixture->test_dir); 11062c39b30SMarc-André Lureau g_free(fixture->test_dir); 11143d1da7cSAlex Chen close(fixture->fd); 11262c39b30SMarc-André Lureau } 11362c39b30SMarc-André Lureau 11462c39b30SMarc-André Lureau static void qmp_assertion_message_error(const char *domain, 11562c39b30SMarc-André Lureau const char *file, 11662c39b30SMarc-André Lureau int line, 11762c39b30SMarc-André Lureau const char *func, 11862c39b30SMarc-André Lureau const char *expr, 11962c39b30SMarc-André Lureau QDict *dict) 12062c39b30SMarc-André Lureau { 12162c39b30SMarc-André Lureau const char *class, *desc; 122bb6960a1SMarc-André Lureau g_autofree char *s = NULL; 12362c39b30SMarc-André Lureau QDict *error; 12462c39b30SMarc-André Lureau 12562c39b30SMarc-André Lureau error = qdict_get_qdict(dict, "error"); 12662c39b30SMarc-André Lureau class = qdict_get_try_str(error, "class"); 12762c39b30SMarc-André Lureau desc = qdict_get_try_str(error, "desc"); 12862c39b30SMarc-André Lureau 12962c39b30SMarc-André Lureau s = g_strdup_printf("assertion failed %s: %s %s", expr, class, desc); 13062c39b30SMarc-André Lureau g_assertion_message(domain, file, line, func, s); 13162c39b30SMarc-André Lureau } 13262c39b30SMarc-André Lureau 13362c39b30SMarc-André Lureau #define qmp_assert_no_error(err) do { \ 13462c39b30SMarc-André Lureau if (qdict_haskey(err, "error")) { \ 13562c39b30SMarc-André Lureau qmp_assertion_message_error(G_LOG_DOMAIN, __FILE__, __LINE__, \ 13662c39b30SMarc-André Lureau G_STRFUNC, #err, err); \ 13762c39b30SMarc-André Lureau } \ 13862c39b30SMarc-André Lureau } while (0) 13962c39b30SMarc-André Lureau 14062c39b30SMarc-André Lureau static void test_qga_sync_delimited(gconstpointer fix) 14162c39b30SMarc-André Lureau { 14262c39b30SMarc-André Lureau const TestFixture *fixture = fix; 1430f555602SPaolo Bonzini guint32 v, r = g_test_rand_int(); 14462c39b30SMarc-André Lureau unsigned char c; 145bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 14662c39b30SMarc-André Lureau 147e2f64a68SMarkus Armbruster qmp_fd_send_raw(fixture->fd, "\xff"); 148015715f5SMarkus Armbruster qmp_fd_send(fixture->fd, 149e2f64a68SMarkus Armbruster "{'execute': 'guest-sync-delimited'," 150015715f5SMarkus Armbruster " 'arguments': {'id': %u } }", 151015715f5SMarkus Armbruster r); 15262c39b30SMarc-André Lureau 1535229564bSEric Blake /* 1545229564bSEric Blake * Read and ignore garbage until resynchronized. 1555229564bSEric Blake * 1565229564bSEric Blake * Note that the full reset sequence would involve checking the 1575229564bSEric Blake * response of guest-sync-delimited and repeating the loop if 1585229564bSEric Blake * 'id' field of the response does not match the 'id' field of 1595229564bSEric Blake * the request. Testing this fully would require inserting 1605229564bSEric Blake * garbage in the response stream and is left as a future test 1615229564bSEric Blake * to implement. 1625229564bSEric Blake * 1635229564bSEric Blake * TODO: The server shouldn't emit so much garbage (among other 1645229564bSEric Blake * things, it loudly complains about the client's \xff being 1655229564bSEric Blake * invalid JSON, even though it is a documented part of the 1665229564bSEric Blake * handshake. 1675229564bSEric Blake */ 1685229564bSEric Blake do { 16962c39b30SMarc-André Lureau v = read(fixture->fd, &c, 1); 17062c39b30SMarc-André Lureau g_assert_cmpint(v, ==, 1); 1715229564bSEric Blake } while (c != 0xff); 17262c39b30SMarc-André Lureau 17362c39b30SMarc-André Lureau ret = qmp_fd_receive(fixture->fd); 17462c39b30SMarc-André Lureau g_assert_nonnull(ret); 17562c39b30SMarc-André Lureau qmp_assert_no_error(ret); 17662c39b30SMarc-André Lureau 17762c39b30SMarc-André Lureau v = qdict_get_int(ret, "return"); 17862c39b30SMarc-André Lureau g_assert_cmpint(r, ==, v); 17962c39b30SMarc-André Lureau } 18062c39b30SMarc-André Lureau 18162c39b30SMarc-André Lureau static void test_qga_sync(gconstpointer fix) 18262c39b30SMarc-André Lureau { 18362c39b30SMarc-André Lureau const TestFixture *fixture = fix; 1840f555602SPaolo Bonzini guint32 v, r = g_test_rand_int(); 185bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 18662c39b30SMarc-André Lureau 1875229564bSEric Blake /* 1885229564bSEric Blake * TODO guest-sync is inherently limited: we cannot distinguish 1895229564bSEric Blake * failure caused by reacting to garbage on the wire prior to this 1905229564bSEric Blake * command, from failure of this actual command. Clients are 1915229564bSEric Blake * supposed to be able to send a raw '\xff' byte to at least 1925229564bSEric Blake * re-synchronize the server's parser prior to this command, but 1935229564bSEric Blake * we are not in a position to test that here because (at least 1945229564bSEric Blake * for now) it causes the server to issue an error message about 1955229564bSEric Blake * invalid JSON. Testing of '\xff' handling is done in 1965229564bSEric Blake * guest-sync-delimited instead. 1975229564bSEric Blake */ 198015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 199015715f5SMarkus Armbruster "{'execute': 'guest-sync', 'arguments': {'id': %u } }", 200015715f5SMarkus Armbruster r); 20162c39b30SMarc-André Lureau 20262c39b30SMarc-André Lureau g_assert_nonnull(ret); 20362c39b30SMarc-André Lureau qmp_assert_no_error(ret); 20462c39b30SMarc-André Lureau 20562c39b30SMarc-André Lureau v = qdict_get_int(ret, "return"); 20662c39b30SMarc-André Lureau g_assert_cmpint(r, ==, v); 20762c39b30SMarc-André Lureau } 20862c39b30SMarc-André Lureau 20962c39b30SMarc-André Lureau static void test_qga_ping(gconstpointer fix) 21062c39b30SMarc-André Lureau { 21162c39b30SMarc-André Lureau const TestFixture *fixture = fix; 212bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 21362c39b30SMarc-André Lureau 21462c39b30SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping'}"); 21562c39b30SMarc-André Lureau g_assert_nonnull(ret); 21662c39b30SMarc-André Lureau qmp_assert_no_error(ret); 21762c39b30SMarc-André Lureau } 21862c39b30SMarc-André Lureau 2194eaca8deSMarc-André Lureau static void test_qga_id(gconstpointer fix) 220b5f84310SMarkus Armbruster { 221b5f84310SMarkus Armbruster const TestFixture *fixture = fix; 222bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 223b5f84310SMarkus Armbruster 224b5f84310SMarkus Armbruster ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}"); 225b5f84310SMarkus Armbruster g_assert_nonnull(ret); 2264eaca8deSMarc-André Lureau qmp_assert_no_error(ret); 2274eaca8deSMarc-André Lureau g_assert_cmpint(qdict_get_int(ret, "id"), ==, 1); 228b5f84310SMarkus Armbruster } 229b5f84310SMarkus Armbruster 230d4d7ed73SMarkus Armbruster static void test_qga_invalid_oob(gconstpointer fix) 231d4d7ed73SMarkus Armbruster { 232d4d7ed73SMarkus Armbruster const TestFixture *fixture = fix; 233ebb4d82dSMarc-André Lureau QDict *ret; 234d4d7ed73SMarkus Armbruster 23500ecec15SMarkus Armbruster ret = qmp_fd(fixture->fd, "{'exec-oob': 'guest-ping'}"); 236d4d7ed73SMarkus Armbruster g_assert_nonnull(ret); 237d4d7ed73SMarkus Armbruster 2383bc1b8eeSMarkus Armbruster qmp_expect_error_and_unref(ret, "GenericError"); 239d4d7ed73SMarkus Armbruster } 240d4d7ed73SMarkus Armbruster 2414bdadd86SMarc-André Lureau static void test_qga_invalid_args(gconstpointer fix) 2424bdadd86SMarc-André Lureau { 2434bdadd86SMarc-André Lureau const TestFixture *fixture = fix; 244bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 245bb6960a1SMarc-André Lureau QDict *error; 2464bdadd86SMarc-André Lureau const gchar *class, *desc; 2474bdadd86SMarc-André Lureau 2484bdadd86SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', " 2494bdadd86SMarc-André Lureau "'arguments': {'foo': 42 }}"); 2504bdadd86SMarc-André Lureau g_assert_nonnull(ret); 2514bdadd86SMarc-André Lureau 2524bdadd86SMarc-André Lureau error = qdict_get_qdict(ret, "error"); 2534bdadd86SMarc-André Lureau class = qdict_get_try_str(error, "class"); 2544bdadd86SMarc-André Lureau desc = qdict_get_try_str(error, "desc"); 2554bdadd86SMarc-André Lureau 2564bdadd86SMarc-André Lureau g_assert_cmpstr(class, ==, "GenericError"); 257910f738bSMarkus Armbruster g_assert_cmpstr(desc, ==, "Parameter 'foo' is unexpected"); 2584bdadd86SMarc-André Lureau } 2594bdadd86SMarc-André Lureau 26062c39b30SMarc-André Lureau static void test_qga_invalid_cmd(gconstpointer fix) 26162c39b30SMarc-André Lureau { 26262c39b30SMarc-André Lureau const TestFixture *fixture = fix; 263bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 264bb6960a1SMarc-André Lureau QDict *error; 26562c39b30SMarc-André Lureau const gchar *class, *desc; 26662c39b30SMarc-André Lureau 26762c39b30SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-invalid-cmd'}"); 26862c39b30SMarc-André Lureau g_assert_nonnull(ret); 26962c39b30SMarc-André Lureau 27062c39b30SMarc-André Lureau error = qdict_get_qdict(ret, "error"); 27162c39b30SMarc-André Lureau class = qdict_get_try_str(error, "class"); 27262c39b30SMarc-André Lureau desc = qdict_get_try_str(error, "desc"); 27362c39b30SMarc-André Lureau 27462c39b30SMarc-André Lureau g_assert_cmpstr(class, ==, "CommandNotFound"); 27562c39b30SMarc-André Lureau g_assert_cmpint(strlen(desc), >, 0); 27662c39b30SMarc-André Lureau } 27762c39b30SMarc-André Lureau 27862c39b30SMarc-André Lureau static void test_qga_info(gconstpointer fix) 27962c39b30SMarc-André Lureau { 28062c39b30SMarc-André Lureau const TestFixture *fixture = fix; 281bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 282bb6960a1SMarc-André Lureau QDict *val; 28362c39b30SMarc-André Lureau const gchar *version; 28462c39b30SMarc-André Lureau 28562c39b30SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-info'}"); 28662c39b30SMarc-André Lureau g_assert_nonnull(ret); 28762c39b30SMarc-André Lureau qmp_assert_no_error(ret); 28862c39b30SMarc-André Lureau 28962c39b30SMarc-André Lureau val = qdict_get_qdict(ret, "return"); 29062c39b30SMarc-André Lureau version = qdict_get_try_str(val, "version"); 29162c39b30SMarc-André Lureau g_assert_cmpstr(version, ==, QEMU_VERSION); 29262c39b30SMarc-André Lureau } 29362c39b30SMarc-André Lureau 29462c39b30SMarc-André Lureau static void test_qga_get_vcpus(gconstpointer fix) 29562c39b30SMarc-André Lureau { 29662c39b30SMarc-André Lureau const TestFixture *fixture = fix; 297bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 29862c39b30SMarc-André Lureau QList *list; 29962c39b30SMarc-André Lureau const QListEntry *entry; 30062c39b30SMarc-André Lureau 30162c39b30SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-vcpus'}"); 30262c39b30SMarc-André Lureau g_assert_nonnull(ret); 30362c39b30SMarc-André Lureau qmp_assert_no_error(ret); 30462c39b30SMarc-André Lureau 30562c39b30SMarc-André Lureau /* check there is at least a cpu */ 30662c39b30SMarc-André Lureau list = qdict_get_qlist(ret, "return"); 30762c39b30SMarc-André Lureau entry = qlist_first(list); 3087dc847ebSMax Reitz g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); 3097dc847ebSMax Reitz g_assert(qdict_haskey(qobject_to(QDict, entry->value), "logical-id")); 31062c39b30SMarc-André Lureau } 31162c39b30SMarc-André Lureau 31262c39b30SMarc-André Lureau static void test_qga_get_fsinfo(gconstpointer fix) 31362c39b30SMarc-André Lureau { 31462c39b30SMarc-André Lureau const TestFixture *fixture = fix; 315bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 31662c39b30SMarc-André Lureau QList *list; 31762c39b30SMarc-André Lureau const QListEntry *entry; 31862c39b30SMarc-André Lureau 31962c39b30SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-fsinfo'}"); 32062c39b30SMarc-André Lureau g_assert_nonnull(ret); 32162c39b30SMarc-André Lureau qmp_assert_no_error(ret); 32262c39b30SMarc-André Lureau 323b3e9e584SMichael Roth /* sanity-check the response if there are any filesystems */ 32462c39b30SMarc-André Lureau list = qdict_get_qlist(ret, "return"); 32562c39b30SMarc-André Lureau entry = qlist_first(list); 326b3e9e584SMichael Roth if (entry) { 3277dc847ebSMax Reitz g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); 3287dc847ebSMax Reitz g_assert(qdict_haskey(qobject_to(QDict, entry->value), "mountpoint")); 3297dc847ebSMax Reitz g_assert(qdict_haskey(qobject_to(QDict, entry->value), "type")); 3307dc847ebSMax Reitz g_assert(qdict_haskey(qobject_to(QDict, entry->value), "disk")); 331b3e9e584SMichael Roth } 33262c39b30SMarc-André Lureau } 33362c39b30SMarc-André Lureau 33462c39b30SMarc-André Lureau static void test_qga_get_memory_block_info(gconstpointer fix) 33562c39b30SMarc-André Lureau { 33662c39b30SMarc-André Lureau const TestFixture *fixture = fix; 337bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 338bb6960a1SMarc-André Lureau QDict *val; 33962c39b30SMarc-André Lureau int64_t size; 34062c39b30SMarc-André Lureau 34162c39b30SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-block-info'}"); 34262c39b30SMarc-André Lureau g_assert_nonnull(ret); 34362c39b30SMarc-André Lureau 34462c39b30SMarc-André Lureau /* some systems might not expose memory block info in sysfs */ 34562c39b30SMarc-André Lureau if (!qdict_haskey(ret, "error")) { 34662c39b30SMarc-André Lureau /* check there is at least some memory */ 34762c39b30SMarc-André Lureau val = qdict_get_qdict(ret, "return"); 34862c39b30SMarc-André Lureau size = qdict_get_int(val, "size"); 34962c39b30SMarc-André Lureau g_assert_cmpint(size, >, 0); 35062c39b30SMarc-André Lureau } 35162c39b30SMarc-André Lureau } 35262c39b30SMarc-André Lureau 35362c39b30SMarc-André Lureau static void test_qga_get_memory_blocks(gconstpointer fix) 35462c39b30SMarc-André Lureau { 35562c39b30SMarc-André Lureau const TestFixture *fixture = fix; 356bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 35762c39b30SMarc-André Lureau QList *list; 35862c39b30SMarc-André Lureau const QListEntry *entry; 35962c39b30SMarc-André Lureau 36062c39b30SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-blocks'}"); 36162c39b30SMarc-André Lureau g_assert_nonnull(ret); 36262c39b30SMarc-André Lureau 36362c39b30SMarc-André Lureau /* some systems might not expose memory block info in sysfs */ 36462c39b30SMarc-André Lureau if (!qdict_haskey(ret, "error")) { 36562c39b30SMarc-André Lureau list = qdict_get_qlist(ret, "return"); 36662c39b30SMarc-André Lureau entry = qlist_first(list); 36762c39b30SMarc-André Lureau /* newer versions of qga may return empty list without error */ 36862c39b30SMarc-André Lureau if (entry) { 3697dc847ebSMax Reitz g_assert(qdict_haskey(qobject_to(QDict, entry->value), 3707dc847ebSMax Reitz "phys-index")); 3717dc847ebSMax Reitz g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); 37262c39b30SMarc-André Lureau } 37362c39b30SMarc-André Lureau } 37462c39b30SMarc-André Lureau } 37562c39b30SMarc-André Lureau 37662c39b30SMarc-André Lureau static void test_qga_network_get_interfaces(gconstpointer fix) 37762c39b30SMarc-André Lureau { 37862c39b30SMarc-André Lureau const TestFixture *fixture = fix; 379bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 38062c39b30SMarc-André Lureau QList *list; 38162c39b30SMarc-André Lureau const QListEntry *entry; 38262c39b30SMarc-André Lureau 38362c39b30SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-network-get-interfaces'}"); 38462c39b30SMarc-André Lureau g_assert_nonnull(ret); 38562c39b30SMarc-André Lureau qmp_assert_no_error(ret); 38662c39b30SMarc-André Lureau 38762c39b30SMarc-André Lureau /* check there is at least an interface */ 38862c39b30SMarc-André Lureau list = qdict_get_qlist(ret, "return"); 38962c39b30SMarc-André Lureau entry = qlist_first(list); 3907dc847ebSMax Reitz g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); 39162c39b30SMarc-André Lureau } 39262c39b30SMarc-André Lureau 39362c39b30SMarc-André Lureau static void test_qga_file_ops(gconstpointer fix) 39462c39b30SMarc-André Lureau { 39562c39b30SMarc-André Lureau const TestFixture *fixture = fix; 3964eaab85cSMarc-André Lureau const unsigned char helloworld[] = "Hello World!\n"; 39762c39b30SMarc-André Lureau const char *b64; 398015715f5SMarkus Armbruster gchar *path, *enc; 3994eaab85cSMarc-André Lureau unsigned char *dec; 40062c39b30SMarc-André Lureau QDict *ret, *val; 40162c39b30SMarc-André Lureau int64_t id, eof; 40262c39b30SMarc-André Lureau gsize count; 40362c39b30SMarc-André Lureau FILE *f; 40462c39b30SMarc-André Lureau char tmp[100]; 40562c39b30SMarc-André Lureau 40662c39b30SMarc-André Lureau /* open */ 40762c39b30SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," 40862c39b30SMarc-André Lureau " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); 40962c39b30SMarc-André Lureau g_assert_nonnull(ret); 41062c39b30SMarc-André Lureau qmp_assert_no_error(ret); 41162c39b30SMarc-André Lureau id = qdict_get_int(ret, "return"); 412cb3e7f08SMarc-André Lureau qobject_unref(ret); 41362c39b30SMarc-André Lureau 41462c39b30SMarc-André Lureau enc = g_base64_encode(helloworld, sizeof(helloworld)); 41562c39b30SMarc-André Lureau /* write */ 416015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 417015715f5SMarkus Armbruster "{'execute': 'guest-file-write'," 418015715f5SMarkus Armbruster " 'arguments': { 'handle': %" PRId64 ", 'buf-b64': %s } }", 419015715f5SMarkus Armbruster id, enc); 42062c39b30SMarc-André Lureau g_assert_nonnull(ret); 42162c39b30SMarc-André Lureau qmp_assert_no_error(ret); 42262c39b30SMarc-André Lureau 42362c39b30SMarc-André Lureau val = qdict_get_qdict(ret, "return"); 42462c39b30SMarc-André Lureau count = qdict_get_int(val, "count"); 42562c39b30SMarc-André Lureau eof = qdict_get_bool(val, "eof"); 42662c39b30SMarc-André Lureau g_assert_cmpint(count, ==, sizeof(helloworld)); 42762c39b30SMarc-André Lureau g_assert_cmpint(eof, ==, 0); 428cb3e7f08SMarc-André Lureau qobject_unref(ret); 42962c39b30SMarc-André Lureau 43062c39b30SMarc-André Lureau /* flush */ 431015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 432015715f5SMarkus Armbruster "{'execute': 'guest-file-flush'," 43362c39b30SMarc-André Lureau " 'arguments': {'handle': %" PRId64 "} }", 43462c39b30SMarc-André Lureau id); 435cb3e7f08SMarc-André Lureau qobject_unref(ret); 43662c39b30SMarc-André Lureau 43762c39b30SMarc-André Lureau /* close */ 438015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 439015715f5SMarkus Armbruster "{'execute': 'guest-file-close'," 44062c39b30SMarc-André Lureau " 'arguments': {'handle': %" PRId64 "} }", 44162c39b30SMarc-André Lureau id); 442cb3e7f08SMarc-André Lureau qobject_unref(ret); 44362c39b30SMarc-André Lureau 44462c39b30SMarc-André Lureau /* check content */ 44562c39b30SMarc-André Lureau path = g_build_filename(fixture->test_dir, "foo", NULL); 44662c39b30SMarc-André Lureau f = fopen(path, "r"); 4471e271338SMarc-André Lureau g_free(path); 44862c39b30SMarc-André Lureau g_assert_nonnull(f); 44962c39b30SMarc-André Lureau count = fread(tmp, 1, sizeof(tmp), f); 45062c39b30SMarc-André Lureau g_assert_cmpint(count, ==, sizeof(helloworld)); 45162c39b30SMarc-André Lureau tmp[count] = 0; 45262c39b30SMarc-André Lureau g_assert_cmpstr(tmp, ==, (char *)helloworld); 45362c39b30SMarc-André Lureau fclose(f); 45462c39b30SMarc-André Lureau 45562c39b30SMarc-André Lureau /* open */ 45662c39b30SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," 45762c39b30SMarc-André Lureau " 'arguments': { 'path': 'foo', 'mode': 'r' } }"); 45862c39b30SMarc-André Lureau g_assert_nonnull(ret); 45962c39b30SMarc-André Lureau qmp_assert_no_error(ret); 46062c39b30SMarc-André Lureau id = qdict_get_int(ret, "return"); 461cb3e7f08SMarc-André Lureau qobject_unref(ret); 46262c39b30SMarc-André Lureau 46362c39b30SMarc-André Lureau /* read */ 464015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 465015715f5SMarkus Armbruster "{'execute': 'guest-file-read'," 46662c39b30SMarc-André Lureau " 'arguments': { 'handle': %" PRId64 "} }", 46762c39b30SMarc-André Lureau id); 46862c39b30SMarc-André Lureau val = qdict_get_qdict(ret, "return"); 46962c39b30SMarc-André Lureau count = qdict_get_int(val, "count"); 47062c39b30SMarc-André Lureau eof = qdict_get_bool(val, "eof"); 47162c39b30SMarc-André Lureau b64 = qdict_get_str(val, "buf-b64"); 47262c39b30SMarc-André Lureau g_assert_cmpint(count, ==, sizeof(helloworld)); 47362c39b30SMarc-André Lureau g_assert(eof); 47462c39b30SMarc-André Lureau g_assert_cmpstr(b64, ==, enc); 47562c39b30SMarc-André Lureau 476cb3e7f08SMarc-André Lureau qobject_unref(ret); 47762c39b30SMarc-André Lureau g_free(enc); 47862c39b30SMarc-André Lureau 47962c39b30SMarc-André Lureau /* read eof */ 480015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 481015715f5SMarkus Armbruster "{'execute': 'guest-file-read'," 48262c39b30SMarc-André Lureau " 'arguments': { 'handle': %" PRId64 "} }", 48362c39b30SMarc-André Lureau id); 48462c39b30SMarc-André Lureau val = qdict_get_qdict(ret, "return"); 48562c39b30SMarc-André Lureau count = qdict_get_int(val, "count"); 48662c39b30SMarc-André Lureau eof = qdict_get_bool(val, "eof"); 48762c39b30SMarc-André Lureau b64 = qdict_get_str(val, "buf-b64"); 48862c39b30SMarc-André Lureau g_assert_cmpint(count, ==, 0); 48962c39b30SMarc-André Lureau g_assert(eof); 49062c39b30SMarc-André Lureau g_assert_cmpstr(b64, ==, ""); 491cb3e7f08SMarc-André Lureau qobject_unref(ret); 49262c39b30SMarc-André Lureau 49362c39b30SMarc-André Lureau /* seek */ 494015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 495015715f5SMarkus Armbruster "{'execute': 'guest-file-seek'," 49662c39b30SMarc-André Lureau " 'arguments': { 'handle': %" PRId64 ", " 497015715f5SMarkus Armbruster " 'offset': %d, 'whence': %s } }", 4980b4b4938SEric Blake id, 6, "set"); 49962c39b30SMarc-André Lureau qmp_assert_no_error(ret); 50062c39b30SMarc-André Lureau val = qdict_get_qdict(ret, "return"); 50162c39b30SMarc-André Lureau count = qdict_get_int(val, "position"); 50262c39b30SMarc-André Lureau eof = qdict_get_bool(val, "eof"); 50362c39b30SMarc-André Lureau g_assert_cmpint(count, ==, 6); 50462c39b30SMarc-André Lureau g_assert(!eof); 505cb3e7f08SMarc-André Lureau qobject_unref(ret); 50662c39b30SMarc-André Lureau 50762c39b30SMarc-André Lureau /* partial read */ 508015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 509015715f5SMarkus Armbruster "{'execute': 'guest-file-read'," 51062c39b30SMarc-André Lureau " 'arguments': { 'handle': %" PRId64 "} }", 51162c39b30SMarc-André Lureau id); 51262c39b30SMarc-André Lureau val = qdict_get_qdict(ret, "return"); 51362c39b30SMarc-André Lureau count = qdict_get_int(val, "count"); 51462c39b30SMarc-André Lureau eof = qdict_get_bool(val, "eof"); 51562c39b30SMarc-André Lureau b64 = qdict_get_str(val, "buf-b64"); 51662c39b30SMarc-André Lureau g_assert_cmpint(count, ==, sizeof(helloworld) - 6); 51762c39b30SMarc-André Lureau g_assert(eof); 51862c39b30SMarc-André Lureau dec = g_base64_decode(b64, &count); 51962c39b30SMarc-André Lureau g_assert_cmpint(count, ==, sizeof(helloworld) - 6); 52062c39b30SMarc-André Lureau g_assert_cmpmem(dec, count, helloworld + 6, sizeof(helloworld) - 6); 52162c39b30SMarc-André Lureau g_free(dec); 52262c39b30SMarc-André Lureau 523cb3e7f08SMarc-André Lureau qobject_unref(ret); 52462c39b30SMarc-André Lureau 52562c39b30SMarc-André Lureau /* close */ 526015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 527015715f5SMarkus Armbruster "{'execute': 'guest-file-close'," 52862c39b30SMarc-André Lureau " 'arguments': {'handle': %" PRId64 "} }", 52962c39b30SMarc-André Lureau id); 530cb3e7f08SMarc-André Lureau qobject_unref(ret); 53162c39b30SMarc-André Lureau } 53262c39b30SMarc-André Lureau 5334eaab85cSMarc-André Lureau static void test_qga_file_write_read(gconstpointer fix) 5344eaab85cSMarc-André Lureau { 5354eaab85cSMarc-André Lureau const TestFixture *fixture = fix; 5364eaab85cSMarc-André Lureau const unsigned char helloworld[] = "Hello World!\n"; 5374eaab85cSMarc-André Lureau const char *b64; 538015715f5SMarkus Armbruster gchar *enc; 5394eaab85cSMarc-André Lureau QDict *ret, *val; 5404eaab85cSMarc-André Lureau int64_t id, eof; 5414eaab85cSMarc-André Lureau gsize count; 5424eaab85cSMarc-André Lureau 5434eaab85cSMarc-André Lureau /* open */ 5444eaab85cSMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," 5454eaab85cSMarc-André Lureau " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); 5464eaab85cSMarc-André Lureau g_assert_nonnull(ret); 5474eaab85cSMarc-André Lureau qmp_assert_no_error(ret); 5484eaab85cSMarc-André Lureau id = qdict_get_int(ret, "return"); 549cb3e7f08SMarc-André Lureau qobject_unref(ret); 5504eaab85cSMarc-André Lureau 5514eaab85cSMarc-André Lureau enc = g_base64_encode(helloworld, sizeof(helloworld)); 5524eaab85cSMarc-André Lureau /* write */ 553015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 554015715f5SMarkus Armbruster "{'execute': 'guest-file-write'," 5554eaab85cSMarc-André Lureau " 'arguments': { 'handle': %" PRId64 "," 556015715f5SMarkus Armbruster " 'buf-b64': %s } }", id, enc); 5574eaab85cSMarc-André Lureau g_assert_nonnull(ret); 5584eaab85cSMarc-André Lureau qmp_assert_no_error(ret); 5594eaab85cSMarc-André Lureau 5604eaab85cSMarc-André Lureau val = qdict_get_qdict(ret, "return"); 5614eaab85cSMarc-André Lureau count = qdict_get_int(val, "count"); 5624eaab85cSMarc-André Lureau eof = qdict_get_bool(val, "eof"); 5634eaab85cSMarc-André Lureau g_assert_cmpint(count, ==, sizeof(helloworld)); 5644eaab85cSMarc-André Lureau g_assert_cmpint(eof, ==, 0); 565cb3e7f08SMarc-André Lureau qobject_unref(ret); 5664eaab85cSMarc-André Lureau 5674eaab85cSMarc-André Lureau /* read (check implicit flush) */ 568015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 569015715f5SMarkus Armbruster "{'execute': 'guest-file-read'," 5704eaab85cSMarc-André Lureau " 'arguments': { 'handle': %" PRId64 "} }", 5714eaab85cSMarc-André Lureau id); 5724eaab85cSMarc-André Lureau val = qdict_get_qdict(ret, "return"); 5734eaab85cSMarc-André Lureau count = qdict_get_int(val, "count"); 5744eaab85cSMarc-André Lureau eof = qdict_get_bool(val, "eof"); 5754eaab85cSMarc-André Lureau b64 = qdict_get_str(val, "buf-b64"); 5764eaab85cSMarc-André Lureau g_assert_cmpint(count, ==, 0); 5774eaab85cSMarc-André Lureau g_assert(eof); 5784eaab85cSMarc-André Lureau g_assert_cmpstr(b64, ==, ""); 579cb3e7f08SMarc-André Lureau qobject_unref(ret); 5804eaab85cSMarc-André Lureau 5814eaab85cSMarc-André Lureau /* seek to 0 */ 582015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 583015715f5SMarkus Armbruster "{'execute': 'guest-file-seek'," 5844eaab85cSMarc-André Lureau " 'arguments': { 'handle': %" PRId64 ", " 585015715f5SMarkus Armbruster " 'offset': %d, 'whence': %s } }", 5860b4b4938SEric Blake id, 0, "set"); 5874eaab85cSMarc-André Lureau qmp_assert_no_error(ret); 5884eaab85cSMarc-André Lureau val = qdict_get_qdict(ret, "return"); 5894eaab85cSMarc-André Lureau count = qdict_get_int(val, "position"); 5904eaab85cSMarc-André Lureau eof = qdict_get_bool(val, "eof"); 5914eaab85cSMarc-André Lureau g_assert_cmpint(count, ==, 0); 5924eaab85cSMarc-André Lureau g_assert(!eof); 593cb3e7f08SMarc-André Lureau qobject_unref(ret); 5944eaab85cSMarc-André Lureau 5954eaab85cSMarc-André Lureau /* read */ 596015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 597015715f5SMarkus Armbruster "{'execute': 'guest-file-read'," 5984eaab85cSMarc-André Lureau " 'arguments': { 'handle': %" PRId64 "} }", 5994eaab85cSMarc-André Lureau id); 6004eaab85cSMarc-André Lureau val = qdict_get_qdict(ret, "return"); 6014eaab85cSMarc-André Lureau count = qdict_get_int(val, "count"); 6024eaab85cSMarc-André Lureau eof = qdict_get_bool(val, "eof"); 6034eaab85cSMarc-André Lureau b64 = qdict_get_str(val, "buf-b64"); 6044eaab85cSMarc-André Lureau g_assert_cmpint(count, ==, sizeof(helloworld)); 6054eaab85cSMarc-André Lureau g_assert(eof); 6064eaab85cSMarc-André Lureau g_assert_cmpstr(b64, ==, enc); 607cb3e7f08SMarc-André Lureau qobject_unref(ret); 6084eaab85cSMarc-André Lureau g_free(enc); 6094eaab85cSMarc-André Lureau 6104eaab85cSMarc-André Lureau /* close */ 611015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 612015715f5SMarkus Armbruster "{'execute': 'guest-file-close'," 6134eaab85cSMarc-André Lureau " 'arguments': {'handle': %" PRId64 "} }", 6144eaab85cSMarc-André Lureau id); 615cb3e7f08SMarc-André Lureau qobject_unref(ret); 6164eaab85cSMarc-André Lureau } 6174eaab85cSMarc-André Lureau 61862c39b30SMarc-André Lureau static void test_qga_get_time(gconstpointer fix) 61962c39b30SMarc-André Lureau { 62062c39b30SMarc-André Lureau const TestFixture *fixture = fix; 621bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 62262c39b30SMarc-André Lureau int64_t time; 62362c39b30SMarc-André Lureau 62462c39b30SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); 62562c39b30SMarc-André Lureau g_assert_nonnull(ret); 62662c39b30SMarc-André Lureau qmp_assert_no_error(ret); 62762c39b30SMarc-André Lureau 62862c39b30SMarc-André Lureau time = qdict_get_int(ret, "return"); 62962c39b30SMarc-André Lureau g_assert_cmpint(time, >, 0); 63062c39b30SMarc-André Lureau } 63162c39b30SMarc-André Lureau 632ebf70554SThomas Huth static void test_qga_blockedrpcs(gconstpointer data) 63362c39b30SMarc-André Lureau { 63462c39b30SMarc-André Lureau TestFixture fix; 63562c39b30SMarc-André Lureau QDict *ret, *error; 63662c39b30SMarc-André Lureau const gchar *class, *desc; 63762c39b30SMarc-André Lureau 638c28afa76STomáš Golembiovský fixture_setup(&fix, "-b guest-ping,guest-get-time", NULL); 63962c39b30SMarc-André Lureau 640ebf70554SThomas Huth /* check blocked RPCs */ 64162c39b30SMarc-André Lureau ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); 64262c39b30SMarc-André Lureau g_assert_nonnull(ret); 64362c39b30SMarc-André Lureau error = qdict_get_qdict(ret, "error"); 64462c39b30SMarc-André Lureau class = qdict_get_try_str(error, "class"); 64562c39b30SMarc-André Lureau desc = qdict_get_try_str(error, "desc"); 6462546be1cSMichal Privoznik g_assert_cmpstr(class, ==, "CommandNotFound"); 64762c39b30SMarc-André Lureau g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); 648cb3e7f08SMarc-André Lureau qobject_unref(ret); 64962c39b30SMarc-André Lureau 65062c39b30SMarc-André Lureau ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); 65162c39b30SMarc-André Lureau g_assert_nonnull(ret); 65262c39b30SMarc-André Lureau error = qdict_get_qdict(ret, "error"); 65362c39b30SMarc-André Lureau class = qdict_get_try_str(error, "class"); 65462c39b30SMarc-André Lureau desc = qdict_get_try_str(error, "desc"); 6552546be1cSMichal Privoznik g_assert_cmpstr(class, ==, "CommandNotFound"); 65662c39b30SMarc-André Lureau g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); 657cb3e7f08SMarc-André Lureau qobject_unref(ret); 65862c39b30SMarc-André Lureau 65962c39b30SMarc-André Lureau /* check something work */ 66062c39b30SMarc-André Lureau ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); 66162c39b30SMarc-André Lureau qmp_assert_no_error(ret); 662cb3e7f08SMarc-André Lureau qobject_unref(ret); 66362c39b30SMarc-André Lureau 66462c39b30SMarc-André Lureau fixture_tear_down(&fix, NULL); 66562c39b30SMarc-André Lureau } 66662c39b30SMarc-André Lureau 66762c39b30SMarc-André Lureau static void test_qga_config(gconstpointer data) 66862c39b30SMarc-André Lureau { 66962c39b30SMarc-André Lureau GError *error = NULL; 670bb6960a1SMarc-André Lureau g_autofree char *out = NULL; 671bb6960a1SMarc-André Lureau g_autofree char *err = NULL; 672bb6960a1SMarc-André Lureau g_autofree char *cwd = NULL; 673bb6960a1SMarc-André Lureau g_autofree char *cmd = NULL; 674bb6960a1SMarc-André Lureau g_auto(GStrv) argv = NULL; 675bb6960a1SMarc-André Lureau g_auto(GStrv) strv = NULL; 676bb6960a1SMarc-André Lureau g_autoptr(GKeyFile) kf = NULL; 677bb6960a1SMarc-André Lureau char *str; 67862c39b30SMarc-André Lureau char *env[2]; 6791741b945SMarc-André Lureau int status; 68062c39b30SMarc-André Lureau gsize n; 68162c39b30SMarc-André Lureau 68262c39b30SMarc-André Lureau cwd = g_get_current_dir(); 683f15bff25SPaolo Bonzini cmd = g_strdup_printf("%s%cqga%cqemu-ga -D", 684f15bff25SPaolo Bonzini cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR); 68562c39b30SMarc-André Lureau g_shell_parse_argv(cmd, NULL, &argv, &error); 68662c39b30SMarc-André Lureau g_assert_no_error(error); 68762c39b30SMarc-André Lureau 6881741b945SMarc-André Lureau env[0] = g_strdup_printf("QGA_CONF=tests%cdata%ctest-qga-config", 6891741b945SMarc-André Lureau G_DIR_SEPARATOR, G_DIR_SEPARATOR); 69062c39b30SMarc-André Lureau env[1] = NULL; 69162c39b30SMarc-André Lureau g_spawn_sync(NULL, argv, env, 0, 69262c39b30SMarc-André Lureau NULL, NULL, &out, &err, &status, &error); 6931e271338SMarc-André Lureau 69462c39b30SMarc-André Lureau g_assert_no_error(error); 69562c39b30SMarc-André Lureau g_assert_cmpstr(err, ==, ""); 69662c39b30SMarc-André Lureau g_assert_cmpint(status, ==, 0); 69762c39b30SMarc-André Lureau 69862c39b30SMarc-André Lureau kf = g_key_file_new(); 69962c39b30SMarc-André Lureau g_key_file_load_from_data(kf, out, -1, G_KEY_FILE_NONE, &error); 70062c39b30SMarc-André Lureau g_assert_no_error(error); 70162c39b30SMarc-André Lureau 70262c39b30SMarc-André Lureau str = g_key_file_get_start_group(kf); 70362c39b30SMarc-André Lureau g_assert_cmpstr(str, ==, "general"); 70462c39b30SMarc-André Lureau g_free(str); 70562c39b30SMarc-André Lureau 70662c39b30SMarc-André Lureau g_assert_false(g_key_file_get_boolean(kf, "general", "daemon", &error)); 70762c39b30SMarc-André Lureau g_assert_no_error(error); 70862c39b30SMarc-André Lureau 70962c39b30SMarc-André Lureau str = g_key_file_get_string(kf, "general", "method", &error); 71062c39b30SMarc-André Lureau g_assert_no_error(error); 71162c39b30SMarc-André Lureau g_assert_cmpstr(str, ==, "virtio-serial"); 71262c39b30SMarc-André Lureau g_free(str); 71362c39b30SMarc-André Lureau 71462c39b30SMarc-André Lureau str = g_key_file_get_string(kf, "general", "path", &error); 71562c39b30SMarc-André Lureau g_assert_no_error(error); 71662c39b30SMarc-André Lureau g_assert_cmpstr(str, ==, "/path/to/org.qemu.guest_agent.0"); 71762c39b30SMarc-André Lureau g_free(str); 71862c39b30SMarc-André Lureau 71962c39b30SMarc-André Lureau str = g_key_file_get_string(kf, "general", "pidfile", &error); 72062c39b30SMarc-André Lureau g_assert_no_error(error); 72162c39b30SMarc-André Lureau g_assert_cmpstr(str, ==, "/var/foo/qemu-ga.pid"); 72262c39b30SMarc-André Lureau g_free(str); 72362c39b30SMarc-André Lureau 72462c39b30SMarc-André Lureau str = g_key_file_get_string(kf, "general", "statedir", &error); 72562c39b30SMarc-André Lureau g_assert_no_error(error); 72662c39b30SMarc-André Lureau g_assert_cmpstr(str, ==, "/var/state"); 72762c39b30SMarc-André Lureau g_free(str); 72862c39b30SMarc-André Lureau 72962c39b30SMarc-André Lureau g_assert_true(g_key_file_get_boolean(kf, "general", "verbose", &error)); 73062c39b30SMarc-André Lureau g_assert_no_error(error); 73162c39b30SMarc-André Lureau 732*582a098eSThomas Huth strv = g_key_file_get_string_list(kf, "general", "block-rpcs", &n, &error); 73362c39b30SMarc-André Lureau g_assert_cmpint(n, ==, 2); 73462c39b30SMarc-André Lureau g_assert_true(g_strv_contains((const char * const *)strv, 73562c39b30SMarc-André Lureau "guest-ping")); 73662c39b30SMarc-André Lureau g_assert_true(g_strv_contains((const char * const *)strv, 73762c39b30SMarc-André Lureau "guest-get-time")); 73862c39b30SMarc-André Lureau g_assert_no_error(error); 73962c39b30SMarc-André Lureau 74062c39b30SMarc-André Lureau g_free(env[0]); 74162c39b30SMarc-André Lureau } 74262c39b30SMarc-André Lureau 74362c39b30SMarc-André Lureau static void test_qga_fsfreeze_status(gconstpointer fix) 74462c39b30SMarc-André Lureau { 74562c39b30SMarc-André Lureau const TestFixture *fixture = fix; 746bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 74762c39b30SMarc-André Lureau const gchar *status; 74862c39b30SMarc-André Lureau 74962c39b30SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); 75062c39b30SMarc-André Lureau g_assert_nonnull(ret); 75162c39b30SMarc-André Lureau qmp_assert_no_error(ret); 75262c39b30SMarc-André Lureau 75362c39b30SMarc-André Lureau status = qdict_get_try_str(ret, "return"); 75462c39b30SMarc-André Lureau g_assert_cmpstr(status, ==, "thawed"); 75562c39b30SMarc-André Lureau } 75662c39b30SMarc-André Lureau 7573dab9fa1SMarc-André Lureau static void test_qga_guest_exec(gconstpointer fix) 7583dab9fa1SMarc-André Lureau { 7593dab9fa1SMarc-André Lureau const TestFixture *fixture = fix; 760bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 761bb6960a1SMarc-André Lureau QDict *val; 7623dab9fa1SMarc-André Lureau const gchar *out; 763bb6960a1SMarc-André Lureau g_autofree guchar *decoded = NULL; 7643dab9fa1SMarc-André Lureau int64_t pid, now, exitcode; 7653dab9fa1SMarc-André Lureau gsize len; 7663dab9fa1SMarc-André Lureau bool exited; 7673dab9fa1SMarc-André Lureau 7683dab9fa1SMarc-André Lureau /* exec 'echo foo bar' */ 7693dab9fa1SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 7703dab9fa1SMarc-André Lureau " 'path': '/bin/echo', 'arg': [ '-n', '\" test_str \"' ]," 7713dab9fa1SMarc-André Lureau " 'capture-output': true } }"); 7723dab9fa1SMarc-André Lureau g_assert_nonnull(ret); 7733dab9fa1SMarc-André Lureau qmp_assert_no_error(ret); 7743dab9fa1SMarc-André Lureau val = qdict_get_qdict(ret, "return"); 7753dab9fa1SMarc-André Lureau pid = qdict_get_int(val, "pid"); 7763dab9fa1SMarc-André Lureau g_assert_cmpint(pid, >, 0); 777cb3e7f08SMarc-André Lureau qobject_unref(ret); 7783dab9fa1SMarc-André Lureau 7793dab9fa1SMarc-André Lureau /* wait for completion */ 7803dab9fa1SMarc-André Lureau now = g_get_monotonic_time(); 7811792d7d0SEric Blake do { 782015715f5SMarkus Armbruster ret = qmp_fd(fixture->fd, 783015715f5SMarkus Armbruster "{'execute': 'guest-exec-status'," 784015715f5SMarkus Armbruster " 'arguments': { 'pid': %" PRId64 " } }", pid); 7853dab9fa1SMarc-André Lureau g_assert_nonnull(ret); 7863dab9fa1SMarc-André Lureau val = qdict_get_qdict(ret, "return"); 7873dab9fa1SMarc-André Lureau exited = qdict_get_bool(val, "exited"); 7883dab9fa1SMarc-André Lureau if (!exited) { 789cb3e7f08SMarc-André Lureau qobject_unref(ret); 7903dab9fa1SMarc-André Lureau } 7913dab9fa1SMarc-André Lureau } while (!exited && 7923dab9fa1SMarc-André Lureau g_get_monotonic_time() < now + 5 * G_TIME_SPAN_SECOND); 7933dab9fa1SMarc-André Lureau g_assert(exited); 7943dab9fa1SMarc-André Lureau 7953dab9fa1SMarc-André Lureau /* check stdout */ 7963dab9fa1SMarc-André Lureau exitcode = qdict_get_int(val, "exitcode"); 7973dab9fa1SMarc-André Lureau g_assert_cmpint(exitcode, ==, 0); 7983dab9fa1SMarc-André Lureau out = qdict_get_str(val, "out-data"); 7993dab9fa1SMarc-André Lureau decoded = g_base64_decode(out, &len); 8003dab9fa1SMarc-André Lureau g_assert_cmpint(len, ==, 12); 8013dab9fa1SMarc-André Lureau g_assert_cmpstr((char *)decoded, ==, "\" test_str \""); 8023dab9fa1SMarc-André Lureau } 8033dab9fa1SMarc-André Lureau 8043dab9fa1SMarc-André Lureau static void test_qga_guest_exec_invalid(gconstpointer fix) 8053dab9fa1SMarc-André Lureau { 8063dab9fa1SMarc-André Lureau const TestFixture *fixture = fix; 807bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 808bb6960a1SMarc-André Lureau QDict *error; 8093dab9fa1SMarc-André Lureau const gchar *class, *desc; 8103dab9fa1SMarc-André Lureau 8113dab9fa1SMarc-André Lureau /* invalid command */ 8123dab9fa1SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 8133dab9fa1SMarc-André Lureau " 'path': '/bin/invalid-cmd42' } }"); 8143dab9fa1SMarc-André Lureau g_assert_nonnull(ret); 8153dab9fa1SMarc-André Lureau error = qdict_get_qdict(ret, "error"); 8163dab9fa1SMarc-André Lureau g_assert_nonnull(error); 8173dab9fa1SMarc-André Lureau class = qdict_get_str(error, "class"); 8183dab9fa1SMarc-André Lureau desc = qdict_get_str(error, "desc"); 8193dab9fa1SMarc-André Lureau g_assert_cmpstr(class, ==, "GenericError"); 8203dab9fa1SMarc-André Lureau g_assert_cmpint(strlen(desc), >, 0); 821cb3e7f08SMarc-André Lureau qobject_unref(ret); 8223dab9fa1SMarc-André Lureau 8233dab9fa1SMarc-André Lureau /* invalid pid */ 8243dab9fa1SMarc-André Lureau ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec-status'," 8253dab9fa1SMarc-André Lureau " 'arguments': { 'pid': 0 } }"); 8263dab9fa1SMarc-André Lureau g_assert_nonnull(ret); 8273dab9fa1SMarc-André Lureau error = qdict_get_qdict(ret, "error"); 8283dab9fa1SMarc-André Lureau g_assert_nonnull(error); 8293dab9fa1SMarc-André Lureau class = qdict_get_str(error, "class"); 8303dab9fa1SMarc-André Lureau desc = qdict_get_str(error, "desc"); 8313dab9fa1SMarc-André Lureau g_assert_cmpstr(class, ==, "GenericError"); 8323dab9fa1SMarc-André Lureau g_assert_cmpint(strlen(desc), >, 0); 8333dab9fa1SMarc-André Lureau } 8343dab9fa1SMarc-André Lureau 835509b97fdSTomáš Golembiovský static void test_qga_guest_get_host_name(gconstpointer fix) 836509b97fdSTomáš Golembiovský { 837509b97fdSTomáš Golembiovský const TestFixture *fixture = fix; 838bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 839bb6960a1SMarc-André Lureau QDict *val; 840509b97fdSTomáš Golembiovský 841509b97fdSTomáš Golembiovský ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-host-name'}"); 842509b97fdSTomáš Golembiovský g_assert_nonnull(ret); 843509b97fdSTomáš Golembiovský qmp_assert_no_error(ret); 844509b97fdSTomáš Golembiovský 845509b97fdSTomáš Golembiovský val = qdict_get_qdict(ret, "return"); 846509b97fdSTomáš Golembiovský g_assert(qdict_haskey(val, "host-name")); 847509b97fdSTomáš Golembiovský } 848509b97fdSTomáš Golembiovský 849509b97fdSTomáš Golembiovský static void test_qga_guest_get_timezone(gconstpointer fix) 850509b97fdSTomáš Golembiovský { 851509b97fdSTomáš Golembiovský const TestFixture *fixture = fix; 852bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 853bb6960a1SMarc-André Lureau QDict *val; 854509b97fdSTomáš Golembiovský 855509b97fdSTomáš Golembiovský ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-timezone'}"); 856509b97fdSTomáš Golembiovský g_assert_nonnull(ret); 857509b97fdSTomáš Golembiovský qmp_assert_no_error(ret); 858509b97fdSTomáš Golembiovský 859509b97fdSTomáš Golembiovský /* Make sure there's at least offset */ 860509b97fdSTomáš Golembiovský val = qdict_get_qdict(ret, "return"); 861509b97fdSTomáš Golembiovský g_assert(qdict_haskey(val, "offset")); 862509b97fdSTomáš Golembiovský } 863509b97fdSTomáš Golembiovský 864509b97fdSTomáš Golembiovský static void test_qga_guest_get_users(gconstpointer fix) 865509b97fdSTomáš Golembiovský { 866509b97fdSTomáš Golembiovský const TestFixture *fixture = fix; 867bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 868509b97fdSTomáš Golembiovský QList *val; 869509b97fdSTomáš Golembiovský 870509b97fdSTomáš Golembiovský ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-users'}"); 871509b97fdSTomáš Golembiovský g_assert_nonnull(ret); 872509b97fdSTomáš Golembiovský qmp_assert_no_error(ret); 873509b97fdSTomáš Golembiovský 874509b97fdSTomáš Golembiovský /* There is not much to test here */ 875509b97fdSTomáš Golembiovský val = qdict_get_qlist(ret, "return"); 876509b97fdSTomáš Golembiovský g_assert_nonnull(val); 877509b97fdSTomáš Golembiovský } 878509b97fdSTomáš Golembiovský 879339ca68bSTomáš Golembiovský static void test_qga_guest_get_osinfo(gconstpointer data) 880339ca68bSTomáš Golembiovský { 881339ca68bSTomáš Golembiovský TestFixture fixture; 882339ca68bSTomáš Golembiovský const gchar *str; 883bb6960a1SMarc-André Lureau g_autoptr(QDict) ret = NULL; 884a85d0926SMarc-André Lureau char *env[2]; 885a85d0926SMarc-André Lureau QDict *val; 886339ca68bSTomáš Golembiovský 887339ca68bSTomáš Golembiovský env[0] = g_strdup_printf( 888a85d0926SMarc-André Lureau "QGA_OS_RELEASE=%s%c..%cdata%ctest-qga-os-release", 889a85d0926SMarc-André Lureau g_test_get_dir(G_TEST_DIST), G_DIR_SEPARATOR, G_DIR_SEPARATOR, G_DIR_SEPARATOR); 890339ca68bSTomáš Golembiovský env[1] = NULL; 891339ca68bSTomáš Golembiovský fixture_setup(&fixture, NULL, env); 892339ca68bSTomáš Golembiovský 893339ca68bSTomáš Golembiovský ret = qmp_fd(fixture.fd, "{'execute': 'guest-get-osinfo'}"); 894339ca68bSTomáš Golembiovský g_assert_nonnull(ret); 895339ca68bSTomáš Golembiovský qmp_assert_no_error(ret); 896339ca68bSTomáš Golembiovský 897339ca68bSTomáš Golembiovský val = qdict_get_qdict(ret, "return"); 898339ca68bSTomáš Golembiovský 899339ca68bSTomáš Golembiovský str = qdict_get_try_str(val, "id"); 900339ca68bSTomáš Golembiovský g_assert_nonnull(str); 901339ca68bSTomáš Golembiovský g_assert_cmpstr(str, ==, "qemu-ga-test"); 902339ca68bSTomáš Golembiovský 903339ca68bSTomáš Golembiovský str = qdict_get_try_str(val, "name"); 904339ca68bSTomáš Golembiovský g_assert_nonnull(str); 905339ca68bSTomáš Golembiovský g_assert_cmpstr(str, ==, "QEMU-GA"); 906339ca68bSTomáš Golembiovský 907339ca68bSTomáš Golembiovský str = qdict_get_try_str(val, "pretty-name"); 908339ca68bSTomáš Golembiovský g_assert_nonnull(str); 909339ca68bSTomáš Golembiovský g_assert_cmpstr(str, ==, "QEMU Guest Agent test"); 910339ca68bSTomáš Golembiovský 911339ca68bSTomáš Golembiovský str = qdict_get_try_str(val, "version"); 912339ca68bSTomáš Golembiovský g_assert_nonnull(str); 913339ca68bSTomáš Golembiovský g_assert_cmpstr(str, ==, "Test 1"); 914339ca68bSTomáš Golembiovský 915339ca68bSTomáš Golembiovský str = qdict_get_try_str(val, "version-id"); 916339ca68bSTomáš Golembiovský g_assert_nonnull(str); 917339ca68bSTomáš Golembiovský g_assert_cmpstr(str, ==, "1"); 918339ca68bSTomáš Golembiovský 919339ca68bSTomáš Golembiovský str = qdict_get_try_str(val, "variant"); 920339ca68bSTomáš Golembiovský g_assert_nonnull(str); 921339ca68bSTomáš Golembiovský g_assert_cmpstr(str, ==, "Unit test \"'$`\\ and \\\\ etc."); 922339ca68bSTomáš Golembiovský 923339ca68bSTomáš Golembiovský str = qdict_get_try_str(val, "variant-id"); 924339ca68bSTomáš Golembiovský g_assert_nonnull(str); 925339ca68bSTomáš Golembiovský g_assert_cmpstr(str, ==, "unit-test"); 926339ca68bSTomáš Golembiovský 927339ca68bSTomáš Golembiovský g_free(env[0]); 928339ca68bSTomáš Golembiovský fixture_tear_down(&fixture, NULL); 929339ca68bSTomáš Golembiovský } 930339ca68bSTomáš Golembiovský 93162c39b30SMarc-André Lureau int main(int argc, char **argv) 93262c39b30SMarc-André Lureau { 93362c39b30SMarc-André Lureau TestFixture fix; 93462c39b30SMarc-André Lureau int ret; 93562c39b30SMarc-André Lureau 936a7bd942cSMarc-André Lureau #ifdef QEMU_SANITIZE_THREAD 937a7bd942cSMarc-André Lureau { 938a7bd942cSMarc-André Lureau g_test_skip("tsan enabled, https://github.com/google/sanitizers/issues/1116"); 939a7bd942cSMarc-André Lureau return 0; 940a7bd942cSMarc-André Lureau } 941a7bd942cSMarc-André Lureau #endif 942a7bd942cSMarc-André Lureau 94362c39b30SMarc-André Lureau setlocale (LC_ALL, ""); 94462c39b30SMarc-André Lureau g_test_init(&argc, &argv, NULL); 945c28afa76STomáš Golembiovský fixture_setup(&fix, NULL, NULL); 94662c39b30SMarc-André Lureau 94762c39b30SMarc-André Lureau g_test_add_data_func("/qga/sync-delimited", &fix, test_qga_sync_delimited); 94862c39b30SMarc-André Lureau g_test_add_data_func("/qga/sync", &fix, test_qga_sync); 94962c39b30SMarc-André Lureau g_test_add_data_func("/qga/ping", &fix, test_qga_ping); 95062c39b30SMarc-André Lureau g_test_add_data_func("/qga/info", &fix, test_qga_info); 95162c39b30SMarc-André Lureau g_test_add_data_func("/qga/network-get-interfaces", &fix, 95262c39b30SMarc-André Lureau test_qga_network_get_interfaces); 953ec72c0e2SBruce Rogers if (!access("/sys/devices/system/cpu/cpu0", F_OK)) { 95462c39b30SMarc-André Lureau g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); 955ec72c0e2SBruce Rogers } 95662c39b30SMarc-André Lureau g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); 95762c39b30SMarc-André Lureau g_test_add_data_func("/qga/get-memory-block-info", &fix, 95862c39b30SMarc-André Lureau test_qga_get_memory_block_info); 95962c39b30SMarc-André Lureau g_test_add_data_func("/qga/get-memory-blocks", &fix, 96062c39b30SMarc-André Lureau test_qga_get_memory_blocks); 96162c39b30SMarc-André Lureau g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); 9624eaab85cSMarc-André Lureau g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read); 96362c39b30SMarc-André Lureau g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); 9644eaca8deSMarc-André Lureau g_test_add_data_func("/qga/id", &fix, test_qga_id); 965d4d7ed73SMarkus Armbruster g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob); 96662c39b30SMarc-André Lureau g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); 9674bdadd86SMarc-André Lureau g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args); 96862c39b30SMarc-André Lureau g_test_add_data_func("/qga/fsfreeze-status", &fix, 96962c39b30SMarc-André Lureau test_qga_fsfreeze_status); 97062c39b30SMarc-André Lureau 971ebf70554SThomas Huth g_test_add_data_func("/qga/blockedrpcs", NULL, test_qga_blockedrpcs); 97262c39b30SMarc-André Lureau g_test_add_data_func("/qga/config", NULL, test_qga_config); 9733dab9fa1SMarc-André Lureau g_test_add_data_func("/qga/guest-exec", &fix, test_qga_guest_exec); 9743dab9fa1SMarc-André Lureau g_test_add_data_func("/qga/guest-exec-invalid", &fix, 9753dab9fa1SMarc-André Lureau test_qga_guest_exec_invalid); 976339ca68bSTomáš Golembiovský g_test_add_data_func("/qga/guest-get-osinfo", &fix, 977339ca68bSTomáš Golembiovský test_qga_guest_get_osinfo); 978509b97fdSTomáš Golembiovský g_test_add_data_func("/qga/guest-get-host-name", &fix, 979509b97fdSTomáš Golembiovský test_qga_guest_get_host_name); 980509b97fdSTomáš Golembiovský g_test_add_data_func("/qga/guest-get-timezone", &fix, 981509b97fdSTomáš Golembiovský test_qga_guest_get_timezone); 982509b97fdSTomáš Golembiovský g_test_add_data_func("/qga/guest-get-users", &fix, 983509b97fdSTomáš Golembiovský test_qga_guest_get_users); 98462c39b30SMarc-André Lureau 98562c39b30SMarc-André Lureau ret = g_test_run(); 98662c39b30SMarc-André Lureau 98762c39b30SMarc-André Lureau fixture_tear_down(&fix, NULL); 98862c39b30SMarc-André Lureau 98962c39b30SMarc-André Lureau return ret; 99062c39b30SMarc-André Lureau } 991