1 #include "qemu/osdep.h" 2 #include <locale.h> 3 #include <glib/gstdio.h> 4 #include <sys/socket.h> 5 #include <sys/un.h> 6 7 #include "../qtest/libqtest.h" 8 #include "qapi/qmp/qdict.h" 9 #include "qapi/qmp/qlist.h" 10 11 typedef struct { 12 char *test_dir; 13 GMainLoop *loop; 14 int fd; 15 GPid pid; 16 } TestFixture; 17 18 static int connect_qga(char *path) 19 { 20 int s, ret, len, i = 0; 21 struct sockaddr_un remote; 22 23 s = socket(AF_UNIX, SOCK_STREAM, 0); 24 g_assert(s != -1); 25 26 remote.sun_family = AF_UNIX; 27 do { 28 strcpy(remote.sun_path, path); 29 len = strlen(remote.sun_path) + sizeof(remote.sun_family); 30 ret = connect(s, (struct sockaddr *)&remote, len); 31 if (ret == -1) { 32 g_usleep(G_USEC_PER_SEC); 33 } 34 if (i++ == 10) { 35 close(s); 36 return -1; 37 } 38 } while (ret == -1); 39 40 return s; 41 } 42 43 static void qga_watch(GPid pid, gint status, gpointer user_data) 44 { 45 TestFixture *fixture = user_data; 46 47 g_assert_cmpint(status, ==, 0); 48 g_main_loop_quit(fixture->loop); 49 } 50 51 static void 52 fixture_setup(TestFixture *fixture, gconstpointer data, gchar **envp) 53 { 54 const gchar *extra_arg = data; 55 GError *error = NULL; 56 g_autofree char *cwd = NULL; 57 g_autofree char *path = NULL; 58 g_autofree char *cmd = NULL; 59 g_auto(GStrv) argv = NULL; 60 61 fixture->loop = g_main_loop_new(NULL, FALSE); 62 63 fixture->test_dir = g_strdup_printf("%s/qgatest.XXXXXX", g_get_tmp_dir()); 64 g_assert_nonnull(g_mkdtemp(fixture->test_dir)); 65 66 path = g_build_filename(fixture->test_dir, "sock", NULL); 67 cwd = g_get_current_dir(); 68 cmd = g_strdup_printf("%s%cqga%cqemu-ga -m unix-listen -t %s -p %s %s %s", 69 cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR, 70 fixture->test_dir, path, 71 getenv("QTEST_LOG") ? "-v" : "", 72 extra_arg ?: ""); 73 g_shell_parse_argv(cmd, NULL, &argv, &error); 74 g_assert_no_error(error); 75 76 g_spawn_async(fixture->test_dir, argv, envp, 77 G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, 78 NULL, NULL, &fixture->pid, &error); 79 g_assert_no_error(error); 80 81 g_child_watch_add(fixture->pid, qga_watch, fixture); 82 83 fixture->fd = connect_qga(path); 84 g_assert_cmpint(fixture->fd, !=, -1); 85 } 86 87 static void 88 fixture_tear_down(TestFixture *fixture, gconstpointer data) 89 { 90 g_autofree char *tmp = NULL; 91 92 kill(fixture->pid, SIGTERM); 93 94 g_main_loop_run(fixture->loop); 95 g_main_loop_unref(fixture->loop); 96 97 g_spawn_close_pid(fixture->pid); 98 99 tmp = g_build_filename(fixture->test_dir, "foo", NULL); 100 g_unlink(tmp); 101 g_free(tmp); 102 103 tmp = g_build_filename(fixture->test_dir, "qga.state", NULL); 104 g_unlink(tmp); 105 g_free(tmp); 106 107 tmp = g_build_filename(fixture->test_dir, "sock", NULL); 108 g_unlink(tmp); 109 110 g_rmdir(fixture->test_dir); 111 g_free(fixture->test_dir); 112 close(fixture->fd); 113 } 114 115 static void qmp_assertion_message_error(const char *domain, 116 const char *file, 117 int line, 118 const char *func, 119 const char *expr, 120 QDict *dict) 121 { 122 const char *class, *desc; 123 g_autofree char *s = NULL; 124 QDict *error; 125 126 error = qdict_get_qdict(dict, "error"); 127 class = qdict_get_try_str(error, "class"); 128 desc = qdict_get_try_str(error, "desc"); 129 130 s = g_strdup_printf("assertion failed %s: %s %s", expr, class, desc); 131 g_assertion_message(domain, file, line, func, s); 132 } 133 134 #define qmp_assert_no_error(err) do { \ 135 if (qdict_haskey(err, "error")) { \ 136 qmp_assertion_message_error(G_LOG_DOMAIN, __FILE__, __LINE__, \ 137 G_STRFUNC, #err, err); \ 138 } \ 139 } while (0) 140 141 static void test_qga_sync_delimited(gconstpointer fix) 142 { 143 const TestFixture *fixture = fix; 144 guint32 v, r = g_test_rand_int(); 145 unsigned char c; 146 g_autoptr(QDict) ret = NULL; 147 148 qmp_fd_send_raw(fixture->fd, "\xff"); 149 qmp_fd_send(fixture->fd, 150 "{'execute': 'guest-sync-delimited'," 151 " 'arguments': {'id': %u } }", 152 r); 153 154 /* 155 * Read and ignore garbage until resynchronized. 156 * 157 * Note that the full reset sequence would involve checking the 158 * response of guest-sync-delimited and repeating the loop if 159 * 'id' field of the response does not match the 'id' field of 160 * the request. Testing this fully would require inserting 161 * garbage in the response stream and is left as a future test 162 * to implement. 163 * 164 * TODO: The server shouldn't emit so much garbage (among other 165 * things, it loudly complains about the client's \xff being 166 * invalid JSON, even though it is a documented part of the 167 * handshake. 168 */ 169 do { 170 v = read(fixture->fd, &c, 1); 171 g_assert_cmpint(v, ==, 1); 172 } while (c != 0xff); 173 174 ret = qmp_fd_receive(fixture->fd); 175 g_assert_nonnull(ret); 176 qmp_assert_no_error(ret); 177 178 v = qdict_get_int(ret, "return"); 179 g_assert_cmpint(r, ==, v); 180 } 181 182 static void test_qga_sync(gconstpointer fix) 183 { 184 const TestFixture *fixture = fix; 185 guint32 v, r = g_test_rand_int(); 186 g_autoptr(QDict) ret = NULL; 187 188 /* 189 * TODO guest-sync is inherently limited: we cannot distinguish 190 * failure caused by reacting to garbage on the wire prior to this 191 * command, from failure of this actual command. Clients are 192 * supposed to be able to send a raw '\xff' byte to at least 193 * re-synchronize the server's parser prior to this command, but 194 * we are not in a position to test that here because (at least 195 * for now) it causes the server to issue an error message about 196 * invalid JSON. Testing of '\xff' handling is done in 197 * guest-sync-delimited instead. 198 */ 199 ret = qmp_fd(fixture->fd, 200 "{'execute': 'guest-sync', 'arguments': {'id': %u } }", 201 r); 202 203 g_assert_nonnull(ret); 204 qmp_assert_no_error(ret); 205 206 v = qdict_get_int(ret, "return"); 207 g_assert_cmpint(r, ==, v); 208 } 209 210 static void test_qga_ping(gconstpointer fix) 211 { 212 const TestFixture *fixture = fix; 213 g_autoptr(QDict) ret = NULL; 214 215 ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping'}"); 216 g_assert_nonnull(ret); 217 qmp_assert_no_error(ret); 218 } 219 220 static void test_qga_id(gconstpointer fix) 221 { 222 const TestFixture *fixture = fix; 223 g_autoptr(QDict) ret = NULL; 224 225 ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}"); 226 g_assert_nonnull(ret); 227 qmp_assert_no_error(ret); 228 g_assert_cmpint(qdict_get_int(ret, "id"), ==, 1); 229 } 230 231 static void test_qga_invalid_oob(gconstpointer fix) 232 { 233 const TestFixture *fixture = fix; 234 QDict *ret; 235 236 ret = qmp_fd(fixture->fd, "{'exec-oob': 'guest-ping'}"); 237 g_assert_nonnull(ret); 238 239 qmp_expect_error_and_unref(ret, "GenericError"); 240 } 241 242 static void test_qga_invalid_args(gconstpointer fix) 243 { 244 const TestFixture *fixture = fix; 245 g_autoptr(QDict) ret = NULL; 246 QDict *error; 247 const gchar *class, *desc; 248 249 ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', " 250 "'arguments': {'foo': 42 }}"); 251 g_assert_nonnull(ret); 252 253 error = qdict_get_qdict(ret, "error"); 254 class = qdict_get_try_str(error, "class"); 255 desc = qdict_get_try_str(error, "desc"); 256 257 g_assert_cmpstr(class, ==, "GenericError"); 258 g_assert_cmpstr(desc, ==, "Parameter 'foo' is unexpected"); 259 } 260 261 static void test_qga_invalid_cmd(gconstpointer fix) 262 { 263 const TestFixture *fixture = fix; 264 g_autoptr(QDict) ret = NULL; 265 QDict *error; 266 const gchar *class, *desc; 267 268 ret = qmp_fd(fixture->fd, "{'execute': 'guest-invalid-cmd'}"); 269 g_assert_nonnull(ret); 270 271 error = qdict_get_qdict(ret, "error"); 272 class = qdict_get_try_str(error, "class"); 273 desc = qdict_get_try_str(error, "desc"); 274 275 g_assert_cmpstr(class, ==, "CommandNotFound"); 276 g_assert_cmpint(strlen(desc), >, 0); 277 } 278 279 static void test_qga_info(gconstpointer fix) 280 { 281 const TestFixture *fixture = fix; 282 g_autoptr(QDict) ret = NULL; 283 QDict *val; 284 const gchar *version; 285 286 ret = qmp_fd(fixture->fd, "{'execute': 'guest-info'}"); 287 g_assert_nonnull(ret); 288 qmp_assert_no_error(ret); 289 290 val = qdict_get_qdict(ret, "return"); 291 version = qdict_get_try_str(val, "version"); 292 g_assert_cmpstr(version, ==, QEMU_VERSION); 293 } 294 295 static void test_qga_get_vcpus(gconstpointer fix) 296 { 297 const TestFixture *fixture = fix; 298 g_autoptr(QDict) ret = NULL; 299 QList *list; 300 const QListEntry *entry; 301 302 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-vcpus'}"); 303 g_assert_nonnull(ret); 304 qmp_assert_no_error(ret); 305 306 /* check there is at least a cpu */ 307 list = qdict_get_qlist(ret, "return"); 308 entry = qlist_first(list); 309 g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); 310 g_assert(qdict_haskey(qobject_to(QDict, entry->value), "logical-id")); 311 } 312 313 static void test_qga_get_fsinfo(gconstpointer fix) 314 { 315 const TestFixture *fixture = fix; 316 g_autoptr(QDict) ret = NULL; 317 QList *list; 318 const QListEntry *entry; 319 320 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-fsinfo'}"); 321 g_assert_nonnull(ret); 322 qmp_assert_no_error(ret); 323 324 /* sanity-check the response if there are any filesystems */ 325 list = qdict_get_qlist(ret, "return"); 326 entry = qlist_first(list); 327 if (entry) { 328 g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); 329 g_assert(qdict_haskey(qobject_to(QDict, entry->value), "mountpoint")); 330 g_assert(qdict_haskey(qobject_to(QDict, entry->value), "type")); 331 g_assert(qdict_haskey(qobject_to(QDict, entry->value), "disk")); 332 } 333 } 334 335 static void test_qga_get_memory_block_info(gconstpointer fix) 336 { 337 const TestFixture *fixture = fix; 338 g_autoptr(QDict) ret = NULL; 339 QDict *val; 340 int64_t size; 341 342 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-block-info'}"); 343 g_assert_nonnull(ret); 344 345 /* some systems might not expose memory block info in sysfs */ 346 if (!qdict_haskey(ret, "error")) { 347 /* check there is at least some memory */ 348 val = qdict_get_qdict(ret, "return"); 349 size = qdict_get_int(val, "size"); 350 g_assert_cmpint(size, >, 0); 351 } 352 } 353 354 static void test_qga_get_memory_blocks(gconstpointer fix) 355 { 356 const TestFixture *fixture = fix; 357 g_autoptr(QDict) ret = NULL; 358 QList *list; 359 const QListEntry *entry; 360 361 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-blocks'}"); 362 g_assert_nonnull(ret); 363 364 /* some systems might not expose memory block info in sysfs */ 365 if (!qdict_haskey(ret, "error")) { 366 list = qdict_get_qlist(ret, "return"); 367 entry = qlist_first(list); 368 /* newer versions of qga may return empty list without error */ 369 if (entry) { 370 g_assert(qdict_haskey(qobject_to(QDict, entry->value), 371 "phys-index")); 372 g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); 373 } 374 } 375 } 376 377 static void test_qga_network_get_interfaces(gconstpointer fix) 378 { 379 const TestFixture *fixture = fix; 380 g_autoptr(QDict) ret = NULL; 381 QList *list; 382 const QListEntry *entry; 383 384 ret = qmp_fd(fixture->fd, "{'execute': 'guest-network-get-interfaces'}"); 385 g_assert_nonnull(ret); 386 qmp_assert_no_error(ret); 387 388 /* check there is at least an interface */ 389 list = qdict_get_qlist(ret, "return"); 390 entry = qlist_first(list); 391 g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); 392 } 393 394 static void test_qga_file_ops(gconstpointer fix) 395 { 396 const TestFixture *fixture = fix; 397 const unsigned char helloworld[] = "Hello World!\n"; 398 const char *b64; 399 gchar *path, *enc; 400 unsigned char *dec; 401 QDict *ret, *val; 402 int64_t id, eof; 403 gsize count; 404 FILE *f; 405 char tmp[100]; 406 407 /* open */ 408 ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," 409 " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); 410 g_assert_nonnull(ret); 411 qmp_assert_no_error(ret); 412 id = qdict_get_int(ret, "return"); 413 qobject_unref(ret); 414 415 enc = g_base64_encode(helloworld, sizeof(helloworld)); 416 /* write */ 417 ret = qmp_fd(fixture->fd, 418 "{'execute': 'guest-file-write'," 419 " 'arguments': { 'handle': %" PRId64 ", 'buf-b64': %s } }", 420 id, enc); 421 g_assert_nonnull(ret); 422 qmp_assert_no_error(ret); 423 424 val = qdict_get_qdict(ret, "return"); 425 count = qdict_get_int(val, "count"); 426 eof = qdict_get_bool(val, "eof"); 427 g_assert_cmpint(count, ==, sizeof(helloworld)); 428 g_assert_cmpint(eof, ==, 0); 429 qobject_unref(ret); 430 431 /* flush */ 432 ret = qmp_fd(fixture->fd, 433 "{'execute': 'guest-file-flush'," 434 " 'arguments': {'handle': %" PRId64 "} }", 435 id); 436 qobject_unref(ret); 437 438 /* close */ 439 ret = qmp_fd(fixture->fd, 440 "{'execute': 'guest-file-close'," 441 " 'arguments': {'handle': %" PRId64 "} }", 442 id); 443 qobject_unref(ret); 444 445 /* check content */ 446 path = g_build_filename(fixture->test_dir, "foo", NULL); 447 f = fopen(path, "r"); 448 g_free(path); 449 g_assert_nonnull(f); 450 count = fread(tmp, 1, sizeof(tmp), f); 451 g_assert_cmpint(count, ==, sizeof(helloworld)); 452 tmp[count] = 0; 453 g_assert_cmpstr(tmp, ==, (char *)helloworld); 454 fclose(f); 455 456 /* open */ 457 ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," 458 " 'arguments': { 'path': 'foo', 'mode': 'r' } }"); 459 g_assert_nonnull(ret); 460 qmp_assert_no_error(ret); 461 id = qdict_get_int(ret, "return"); 462 qobject_unref(ret); 463 464 /* read */ 465 ret = qmp_fd(fixture->fd, 466 "{'execute': 'guest-file-read'," 467 " 'arguments': { 'handle': %" PRId64 "} }", 468 id); 469 val = qdict_get_qdict(ret, "return"); 470 count = qdict_get_int(val, "count"); 471 eof = qdict_get_bool(val, "eof"); 472 b64 = qdict_get_str(val, "buf-b64"); 473 g_assert_cmpint(count, ==, sizeof(helloworld)); 474 g_assert(eof); 475 g_assert_cmpstr(b64, ==, enc); 476 477 qobject_unref(ret); 478 g_free(enc); 479 480 /* read eof */ 481 ret = qmp_fd(fixture->fd, 482 "{'execute': 'guest-file-read'," 483 " 'arguments': { 'handle': %" PRId64 "} }", 484 id); 485 val = qdict_get_qdict(ret, "return"); 486 count = qdict_get_int(val, "count"); 487 eof = qdict_get_bool(val, "eof"); 488 b64 = qdict_get_str(val, "buf-b64"); 489 g_assert_cmpint(count, ==, 0); 490 g_assert(eof); 491 g_assert_cmpstr(b64, ==, ""); 492 qobject_unref(ret); 493 494 /* seek */ 495 ret = qmp_fd(fixture->fd, 496 "{'execute': 'guest-file-seek'," 497 " 'arguments': { 'handle': %" PRId64 ", " 498 " 'offset': %d, 'whence': %s } }", 499 id, 6, "set"); 500 qmp_assert_no_error(ret); 501 val = qdict_get_qdict(ret, "return"); 502 count = qdict_get_int(val, "position"); 503 eof = qdict_get_bool(val, "eof"); 504 g_assert_cmpint(count, ==, 6); 505 g_assert(!eof); 506 qobject_unref(ret); 507 508 /* partial read */ 509 ret = qmp_fd(fixture->fd, 510 "{'execute': 'guest-file-read'," 511 " 'arguments': { 'handle': %" PRId64 "} }", 512 id); 513 val = qdict_get_qdict(ret, "return"); 514 count = qdict_get_int(val, "count"); 515 eof = qdict_get_bool(val, "eof"); 516 b64 = qdict_get_str(val, "buf-b64"); 517 g_assert_cmpint(count, ==, sizeof(helloworld) - 6); 518 g_assert(eof); 519 dec = g_base64_decode(b64, &count); 520 g_assert_cmpint(count, ==, sizeof(helloworld) - 6); 521 g_assert_cmpmem(dec, count, helloworld + 6, sizeof(helloworld) - 6); 522 g_free(dec); 523 524 qobject_unref(ret); 525 526 /* close */ 527 ret = qmp_fd(fixture->fd, 528 "{'execute': 'guest-file-close'," 529 " 'arguments': {'handle': %" PRId64 "} }", 530 id); 531 qobject_unref(ret); 532 } 533 534 static void test_qga_file_write_read(gconstpointer fix) 535 { 536 const TestFixture *fixture = fix; 537 const unsigned char helloworld[] = "Hello World!\n"; 538 const char *b64; 539 gchar *enc; 540 QDict *ret, *val; 541 int64_t id, eof; 542 gsize count; 543 544 /* open */ 545 ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," 546 " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); 547 g_assert_nonnull(ret); 548 qmp_assert_no_error(ret); 549 id = qdict_get_int(ret, "return"); 550 qobject_unref(ret); 551 552 enc = g_base64_encode(helloworld, sizeof(helloworld)); 553 /* write */ 554 ret = qmp_fd(fixture->fd, 555 "{'execute': 'guest-file-write'," 556 " 'arguments': { 'handle': %" PRId64 "," 557 " 'buf-b64': %s } }", id, enc); 558 g_assert_nonnull(ret); 559 qmp_assert_no_error(ret); 560 561 val = qdict_get_qdict(ret, "return"); 562 count = qdict_get_int(val, "count"); 563 eof = qdict_get_bool(val, "eof"); 564 g_assert_cmpint(count, ==, sizeof(helloworld)); 565 g_assert_cmpint(eof, ==, 0); 566 qobject_unref(ret); 567 568 /* read (check implicit flush) */ 569 ret = qmp_fd(fixture->fd, 570 "{'execute': 'guest-file-read'," 571 " 'arguments': { 'handle': %" PRId64 "} }", 572 id); 573 val = qdict_get_qdict(ret, "return"); 574 count = qdict_get_int(val, "count"); 575 eof = qdict_get_bool(val, "eof"); 576 b64 = qdict_get_str(val, "buf-b64"); 577 g_assert_cmpint(count, ==, 0); 578 g_assert(eof); 579 g_assert_cmpstr(b64, ==, ""); 580 qobject_unref(ret); 581 582 /* seek to 0 */ 583 ret = qmp_fd(fixture->fd, 584 "{'execute': 'guest-file-seek'," 585 " 'arguments': { 'handle': %" PRId64 ", " 586 " 'offset': %d, 'whence': %s } }", 587 id, 0, "set"); 588 qmp_assert_no_error(ret); 589 val = qdict_get_qdict(ret, "return"); 590 count = qdict_get_int(val, "position"); 591 eof = qdict_get_bool(val, "eof"); 592 g_assert_cmpint(count, ==, 0); 593 g_assert(!eof); 594 qobject_unref(ret); 595 596 /* read */ 597 ret = qmp_fd(fixture->fd, 598 "{'execute': 'guest-file-read'," 599 " 'arguments': { 'handle': %" PRId64 "} }", 600 id); 601 val = qdict_get_qdict(ret, "return"); 602 count = qdict_get_int(val, "count"); 603 eof = qdict_get_bool(val, "eof"); 604 b64 = qdict_get_str(val, "buf-b64"); 605 g_assert_cmpint(count, ==, sizeof(helloworld)); 606 g_assert(eof); 607 g_assert_cmpstr(b64, ==, enc); 608 qobject_unref(ret); 609 g_free(enc); 610 611 /* close */ 612 ret = qmp_fd(fixture->fd, 613 "{'execute': 'guest-file-close'," 614 " 'arguments': {'handle': %" PRId64 "} }", 615 id); 616 qobject_unref(ret); 617 } 618 619 static void test_qga_get_time(gconstpointer fix) 620 { 621 const TestFixture *fixture = fix; 622 g_autoptr(QDict) ret = NULL; 623 int64_t time; 624 625 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); 626 g_assert_nonnull(ret); 627 qmp_assert_no_error(ret); 628 629 time = qdict_get_int(ret, "return"); 630 g_assert_cmpint(time, >, 0); 631 } 632 633 static void test_qga_blockedrpcs(gconstpointer data) 634 { 635 TestFixture fix; 636 QDict *ret, *error; 637 const gchar *class, *desc; 638 639 fixture_setup(&fix, "-b guest-ping,guest-get-time", NULL); 640 641 /* check blocked RPCs */ 642 ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); 643 g_assert_nonnull(ret); 644 error = qdict_get_qdict(ret, "error"); 645 class = qdict_get_try_str(error, "class"); 646 desc = qdict_get_try_str(error, "desc"); 647 g_assert_cmpstr(class, ==, "CommandNotFound"); 648 g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); 649 qobject_unref(ret); 650 651 ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); 652 g_assert_nonnull(ret); 653 error = qdict_get_qdict(ret, "error"); 654 class = qdict_get_try_str(error, "class"); 655 desc = qdict_get_try_str(error, "desc"); 656 g_assert_cmpstr(class, ==, "CommandNotFound"); 657 g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); 658 qobject_unref(ret); 659 660 /* check something work */ 661 ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); 662 qmp_assert_no_error(ret); 663 qobject_unref(ret); 664 665 fixture_tear_down(&fix, NULL); 666 } 667 668 static void test_qga_config(gconstpointer data) 669 { 670 GError *error = NULL; 671 g_autofree char *out = NULL; 672 g_autofree char *err = NULL; 673 g_autofree char *cwd = NULL; 674 g_autofree char *cmd = NULL; 675 g_auto(GStrv) argv = NULL; 676 g_auto(GStrv) strv = NULL; 677 g_autoptr(GKeyFile) kf = NULL; 678 char *str; 679 char *env[2]; 680 int status; 681 gsize n; 682 683 cwd = g_get_current_dir(); 684 cmd = g_strdup_printf("%s%cqga%cqemu-ga -D", 685 cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR); 686 g_shell_parse_argv(cmd, NULL, &argv, &error); 687 g_assert_no_error(error); 688 689 env[0] = g_strdup_printf("QGA_CONF=tests%cdata%ctest-qga-config", 690 G_DIR_SEPARATOR, G_DIR_SEPARATOR); 691 env[1] = NULL; 692 g_spawn_sync(NULL, argv, env, 0, 693 NULL, NULL, &out, &err, &status, &error); 694 695 g_assert_no_error(error); 696 g_assert_cmpstr(err, ==, ""); 697 g_assert_cmpint(status, ==, 0); 698 699 kf = g_key_file_new(); 700 g_key_file_load_from_data(kf, out, -1, G_KEY_FILE_NONE, &error); 701 g_assert_no_error(error); 702 703 str = g_key_file_get_start_group(kf); 704 g_assert_cmpstr(str, ==, "general"); 705 g_free(str); 706 707 g_assert_false(g_key_file_get_boolean(kf, "general", "daemon", &error)); 708 g_assert_no_error(error); 709 710 str = g_key_file_get_string(kf, "general", "method", &error); 711 g_assert_no_error(error); 712 g_assert_cmpstr(str, ==, "virtio-serial"); 713 g_free(str); 714 715 str = g_key_file_get_string(kf, "general", "path", &error); 716 g_assert_no_error(error); 717 g_assert_cmpstr(str, ==, "/path/to/org.qemu.guest_agent.0"); 718 g_free(str); 719 720 str = g_key_file_get_string(kf, "general", "pidfile", &error); 721 g_assert_no_error(error); 722 g_assert_cmpstr(str, ==, "/var/foo/qemu-ga.pid"); 723 g_free(str); 724 725 str = g_key_file_get_string(kf, "general", "statedir", &error); 726 g_assert_no_error(error); 727 g_assert_cmpstr(str, ==, "/var/state"); 728 g_free(str); 729 730 g_assert_true(g_key_file_get_boolean(kf, "general", "verbose", &error)); 731 g_assert_no_error(error); 732 733 strv = g_key_file_get_string_list(kf, "general", "block-rpcs", &n, &error); 734 g_assert_cmpint(n, ==, 2); 735 g_assert_true(g_strv_contains((const char * const *)strv, 736 "guest-ping")); 737 g_assert_true(g_strv_contains((const char * const *)strv, 738 "guest-get-time")); 739 g_assert_no_error(error); 740 741 g_free(env[0]); 742 } 743 744 static void test_qga_fsfreeze_status(gconstpointer fix) 745 { 746 const TestFixture *fixture = fix; 747 g_autoptr(QDict) ret = NULL; 748 const gchar *status; 749 750 ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); 751 g_assert_nonnull(ret); 752 qmp_assert_no_error(ret); 753 754 status = qdict_get_try_str(ret, "return"); 755 g_assert_cmpstr(status, ==, "thawed"); 756 } 757 758 static void test_qga_guest_exec(gconstpointer fix) 759 { 760 const TestFixture *fixture = fix; 761 g_autoptr(QDict) ret = NULL; 762 QDict *val; 763 const gchar *out; 764 g_autofree guchar *decoded = NULL; 765 int64_t pid, now, exitcode; 766 gsize len; 767 bool exited; 768 769 /* exec 'echo foo bar' */ 770 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 771 " 'path': '/bin/echo', 'arg': [ '-n', '\" test_str \"' ]," 772 " 'capture-output': true } }"); 773 g_assert_nonnull(ret); 774 qmp_assert_no_error(ret); 775 val = qdict_get_qdict(ret, "return"); 776 pid = qdict_get_int(val, "pid"); 777 g_assert_cmpint(pid, >, 0); 778 qobject_unref(ret); 779 780 /* wait for completion */ 781 now = g_get_monotonic_time(); 782 do { 783 ret = qmp_fd(fixture->fd, 784 "{'execute': 'guest-exec-status'," 785 " 'arguments': { 'pid': %" PRId64 " } }", pid); 786 g_assert_nonnull(ret); 787 val = qdict_get_qdict(ret, "return"); 788 exited = qdict_get_bool(val, "exited"); 789 if (!exited) { 790 qobject_unref(ret); 791 } 792 } while (!exited && 793 g_get_monotonic_time() < now + 5 * G_TIME_SPAN_SECOND); 794 g_assert(exited); 795 796 /* check stdout */ 797 exitcode = qdict_get_int(val, "exitcode"); 798 g_assert_cmpint(exitcode, ==, 0); 799 out = qdict_get_str(val, "out-data"); 800 decoded = g_base64_decode(out, &len); 801 g_assert_cmpint(len, ==, 12); 802 g_assert_cmpstr((char *)decoded, ==, "\" test_str \""); 803 } 804 805 static void test_qga_guest_exec_invalid(gconstpointer fix) 806 { 807 const TestFixture *fixture = fix; 808 g_autoptr(QDict) ret = NULL; 809 QDict *error; 810 const gchar *class, *desc; 811 812 /* invalid command */ 813 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 814 " 'path': '/bin/invalid-cmd42' } }"); 815 g_assert_nonnull(ret); 816 error = qdict_get_qdict(ret, "error"); 817 g_assert_nonnull(error); 818 class = qdict_get_str(error, "class"); 819 desc = qdict_get_str(error, "desc"); 820 g_assert_cmpstr(class, ==, "GenericError"); 821 g_assert_cmpint(strlen(desc), >, 0); 822 qobject_unref(ret); 823 824 /* invalid pid */ 825 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec-status'," 826 " 'arguments': { 'pid': 0 } }"); 827 g_assert_nonnull(ret); 828 error = qdict_get_qdict(ret, "error"); 829 g_assert_nonnull(error); 830 class = qdict_get_str(error, "class"); 831 desc = qdict_get_str(error, "desc"); 832 g_assert_cmpstr(class, ==, "GenericError"); 833 g_assert_cmpint(strlen(desc), >, 0); 834 } 835 836 static void test_qga_guest_get_host_name(gconstpointer fix) 837 { 838 const TestFixture *fixture = fix; 839 g_autoptr(QDict) ret = NULL; 840 QDict *val; 841 842 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-host-name'}"); 843 g_assert_nonnull(ret); 844 qmp_assert_no_error(ret); 845 846 val = qdict_get_qdict(ret, "return"); 847 g_assert(qdict_haskey(val, "host-name")); 848 } 849 850 static void test_qga_guest_get_timezone(gconstpointer fix) 851 { 852 const TestFixture *fixture = fix; 853 g_autoptr(QDict) ret = NULL; 854 QDict *val; 855 856 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-timezone'}"); 857 g_assert_nonnull(ret); 858 qmp_assert_no_error(ret); 859 860 /* Make sure there's at least offset */ 861 val = qdict_get_qdict(ret, "return"); 862 g_assert(qdict_haskey(val, "offset")); 863 } 864 865 static void test_qga_guest_get_users(gconstpointer fix) 866 { 867 const TestFixture *fixture = fix; 868 g_autoptr(QDict) ret = NULL; 869 QList *val; 870 871 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-users'}"); 872 g_assert_nonnull(ret); 873 qmp_assert_no_error(ret); 874 875 /* There is not much to test here */ 876 val = qdict_get_qlist(ret, "return"); 877 g_assert_nonnull(val); 878 } 879 880 static void test_qga_guest_get_osinfo(gconstpointer data) 881 { 882 TestFixture fixture; 883 const gchar *str; 884 g_autoptr(QDict) ret = NULL; 885 char *env[2]; 886 QDict *val; 887 888 env[0] = g_strdup_printf( 889 "QGA_OS_RELEASE=%s%c..%cdata%ctest-qga-os-release", 890 g_test_get_dir(G_TEST_DIST), G_DIR_SEPARATOR, G_DIR_SEPARATOR, G_DIR_SEPARATOR); 891 env[1] = NULL; 892 fixture_setup(&fixture, NULL, env); 893 894 ret = qmp_fd(fixture.fd, "{'execute': 'guest-get-osinfo'}"); 895 g_assert_nonnull(ret); 896 qmp_assert_no_error(ret); 897 898 val = qdict_get_qdict(ret, "return"); 899 900 str = qdict_get_try_str(val, "id"); 901 g_assert_nonnull(str); 902 g_assert_cmpstr(str, ==, "qemu-ga-test"); 903 904 str = qdict_get_try_str(val, "name"); 905 g_assert_nonnull(str); 906 g_assert_cmpstr(str, ==, "QEMU-GA"); 907 908 str = qdict_get_try_str(val, "pretty-name"); 909 g_assert_nonnull(str); 910 g_assert_cmpstr(str, ==, "QEMU Guest Agent test"); 911 912 str = qdict_get_try_str(val, "version"); 913 g_assert_nonnull(str); 914 g_assert_cmpstr(str, ==, "Test 1"); 915 916 str = qdict_get_try_str(val, "version-id"); 917 g_assert_nonnull(str); 918 g_assert_cmpstr(str, ==, "1"); 919 920 str = qdict_get_try_str(val, "variant"); 921 g_assert_nonnull(str); 922 g_assert_cmpstr(str, ==, "Unit test \"'$`\\ and \\\\ etc."); 923 924 str = qdict_get_try_str(val, "variant-id"); 925 g_assert_nonnull(str); 926 g_assert_cmpstr(str, ==, "unit-test"); 927 928 g_free(env[0]); 929 fixture_tear_down(&fixture, NULL); 930 } 931 932 int main(int argc, char **argv) 933 { 934 TestFixture fix; 935 int ret; 936 937 #ifdef QEMU_SANITIZE_THREAD 938 { 939 g_test_skip("tsan enabled, https://github.com/google/sanitizers/issues/1116"); 940 return 0; 941 } 942 #endif 943 944 setlocale (LC_ALL, ""); 945 g_test_init(&argc, &argv, NULL); 946 fixture_setup(&fix, NULL, NULL); 947 948 g_test_add_data_func("/qga/sync-delimited", &fix, test_qga_sync_delimited); 949 g_test_add_data_func("/qga/sync", &fix, test_qga_sync); 950 g_test_add_data_func("/qga/ping", &fix, test_qga_ping); 951 g_test_add_data_func("/qga/info", &fix, test_qga_info); 952 g_test_add_data_func("/qga/network-get-interfaces", &fix, 953 test_qga_network_get_interfaces); 954 if (!access("/sys/devices/system/cpu/cpu0", F_OK)) { 955 g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); 956 } 957 g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); 958 g_test_add_data_func("/qga/get-memory-block-info", &fix, 959 test_qga_get_memory_block_info); 960 g_test_add_data_func("/qga/get-memory-blocks", &fix, 961 test_qga_get_memory_blocks); 962 g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); 963 g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read); 964 g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); 965 g_test_add_data_func("/qga/id", &fix, test_qga_id); 966 g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob); 967 g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); 968 g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args); 969 g_test_add_data_func("/qga/fsfreeze-status", &fix, 970 test_qga_fsfreeze_status); 971 972 g_test_add_data_func("/qga/blockedrpcs", NULL, test_qga_blockedrpcs); 973 g_test_add_data_func("/qga/config", NULL, test_qga_config); 974 g_test_add_data_func("/qga/guest-exec", &fix, test_qga_guest_exec); 975 g_test_add_data_func("/qga/guest-exec-invalid", &fix, 976 test_qga_guest_exec_invalid); 977 g_test_add_data_func("/qga/guest-get-osinfo", &fix, 978 test_qga_guest_get_osinfo); 979 g_test_add_data_func("/qga/guest-get-host-name", &fix, 980 test_qga_guest_get_host_name); 981 g_test_add_data_func("/qga/guest-get-timezone", &fix, 982 test_qga_guest_get_timezone); 983 g_test_add_data_func("/qga/guest-get-users", &fix, 984 test_qga_guest_get_users); 985 986 ret = g_test_run(); 987 988 fixture_tear_down(&fix, NULL); 989 990 return ret; 991 } 992