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