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_allowedrpcs(gconstpointer data) 669 { 670 TestFixture fix; 671 QDict *ret, *error; 672 const gchar *class, *desc; 673 674 fixture_setup(&fix, "-a guest-ping,guest-get-time", NULL); 675 676 /* check allowed RPCs */ 677 ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); 678 qmp_assert_no_error(ret); 679 qobject_unref(ret); 680 681 ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); 682 qmp_assert_no_error(ret); 683 qobject_unref(ret); 684 685 /* check something else */ 686 ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); 687 g_assert_nonnull(ret); 688 error = qdict_get_qdict(ret, "error"); 689 class = qdict_get_try_str(error, "class"); 690 desc = qdict_get_try_str(error, "desc"); 691 g_assert_cmpstr(class, ==, "CommandNotFound"); 692 g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); 693 qobject_unref(ret); 694 695 fixture_tear_down(&fix, NULL); 696 } 697 698 static void test_qga_config(gconstpointer data) 699 { 700 GError *error = NULL; 701 g_autofree char *out = NULL; 702 g_autofree char *err = NULL; 703 g_autofree char *cwd = NULL; 704 g_autofree char *cmd = NULL; 705 g_auto(GStrv) argv = NULL; 706 g_auto(GStrv) strv = NULL; 707 g_autoptr(GKeyFile) kf = NULL; 708 char *str; 709 char *env[2]; 710 int status; 711 gsize n; 712 713 cwd = g_get_current_dir(); 714 cmd = g_strdup_printf("%s%cqga%cqemu-ga -D", 715 cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR); 716 g_shell_parse_argv(cmd, NULL, &argv, &error); 717 g_assert_no_error(error); 718 719 env[0] = g_strdup_printf("QGA_CONF=tests%cdata%ctest-qga-config", 720 G_DIR_SEPARATOR, G_DIR_SEPARATOR); 721 env[1] = NULL; 722 g_spawn_sync(NULL, argv, env, 0, 723 NULL, NULL, &out, &err, &status, &error); 724 725 g_assert_no_error(error); 726 g_assert_cmpstr(err, ==, ""); 727 g_assert_cmpint(status, ==, 0); 728 729 kf = g_key_file_new(); 730 g_key_file_load_from_data(kf, out, -1, G_KEY_FILE_NONE, &error); 731 g_assert_no_error(error); 732 733 str = g_key_file_get_start_group(kf); 734 g_assert_cmpstr(str, ==, "general"); 735 g_free(str); 736 737 g_assert_false(g_key_file_get_boolean(kf, "general", "daemon", &error)); 738 g_assert_no_error(error); 739 740 str = g_key_file_get_string(kf, "general", "method", &error); 741 g_assert_no_error(error); 742 g_assert_cmpstr(str, ==, "virtio-serial"); 743 g_free(str); 744 745 str = g_key_file_get_string(kf, "general", "path", &error); 746 g_assert_no_error(error); 747 g_assert_cmpstr(str, ==, "/path/to/org.qemu.guest_agent.0"); 748 g_free(str); 749 750 str = g_key_file_get_string(kf, "general", "pidfile", &error); 751 g_assert_no_error(error); 752 g_assert_cmpstr(str, ==, "/var/foo/qemu-ga.pid"); 753 g_free(str); 754 755 str = g_key_file_get_string(kf, "general", "statedir", &error); 756 g_assert_no_error(error); 757 g_assert_cmpstr(str, ==, "/var/state"); 758 g_free(str); 759 760 g_assert_true(g_key_file_get_boolean(kf, "general", "verbose", &error)); 761 g_assert_no_error(error); 762 763 strv = g_key_file_get_string_list(kf, "general", "block-rpcs", &n, &error); 764 g_assert_cmpint(n, ==, 2); 765 g_assert_true(g_strv_contains((const char * const *)strv, 766 "guest-ping")); 767 g_assert_true(g_strv_contains((const char * const *)strv, 768 "guest-get-time")); 769 g_assert_no_error(error); 770 771 g_free(env[0]); 772 } 773 774 static void test_qga_fsfreeze_status(gconstpointer fix) 775 { 776 const TestFixture *fixture = fix; 777 g_autoptr(QDict) ret = NULL; 778 const gchar *status; 779 780 ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); 781 g_assert_nonnull(ret); 782 qmp_assert_no_error(ret); 783 784 status = qdict_get_try_str(ret, "return"); 785 g_assert_cmpstr(status, ==, "thawed"); 786 } 787 788 static QDict *wait_for_guest_exec_completion(int fd, int64_t pid) 789 { 790 QDict *ret = NULL; 791 int64_t now; 792 bool exited; 793 QDict *val; 794 795 now = g_get_monotonic_time(); 796 do { 797 ret = qmp_fd(fd, 798 "{'execute': 'guest-exec-status'," 799 " 'arguments': { 'pid': %" PRId64 " } }", pid); 800 g_assert_nonnull(ret); 801 val = qdict_get_qdict(ret, "return"); 802 exited = qdict_get_bool(val, "exited"); 803 if (!exited) { 804 qobject_unref(ret); 805 } 806 } while (!exited && 807 g_get_monotonic_time() < now + 5 * G_TIME_SPAN_SECOND); 808 g_assert(exited); 809 810 return ret; 811 } 812 813 static void test_qga_guest_exec(gconstpointer fix) 814 { 815 const TestFixture *fixture = fix; 816 g_autoptr(QDict) ret = NULL; 817 QDict *val; 818 const gchar *out; 819 g_autofree guchar *decoded = NULL; 820 int64_t pid, exitcode; 821 gsize len; 822 823 /* exec 'echo foo bar' */ 824 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 825 " 'path': '/bin/echo', 'arg': [ '-n', '\" test_str \"' ]," 826 " 'capture-output': true } }"); 827 g_assert_nonnull(ret); 828 qmp_assert_no_error(ret); 829 val = qdict_get_qdict(ret, "return"); 830 pid = qdict_get_int(val, "pid"); 831 g_assert_cmpint(pid, >, 0); 832 qobject_unref(ret); 833 834 ret = wait_for_guest_exec_completion(fixture->fd, pid); 835 836 /* check stdout */ 837 val = qdict_get_qdict(ret, "return"); 838 exitcode = qdict_get_int(val, "exitcode"); 839 g_assert_cmpint(exitcode, ==, 0); 840 out = qdict_get_str(val, "out-data"); 841 decoded = g_base64_decode(out, &len); 842 g_assert_cmpint(len, ==, 12); 843 g_assert_cmpstr((char *)decoded, ==, "\" test_str \""); 844 } 845 846 #if defined(G_OS_WIN32) 847 static void test_qga_guest_exec_separated(gconstpointer fix) 848 { 849 } 850 static void test_qga_guest_exec_merged(gconstpointer fix) 851 { 852 const TestFixture *fixture = fix; 853 g_autoptr(QDict) ret = NULL; 854 QDict *val; 855 const gchar *class, *desc; 856 g_autofree guchar *decoded = NULL; 857 858 /* exec 'echo foo bar' */ 859 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 860 " 'path': 'echo'," 861 " 'arg': [ 'execution never reaches here' ]," 862 " 'capture-output': 'merged' } }"); 863 864 g_assert_nonnull(ret); 865 val = qdict_get_qdict(ret, "error"); 866 g_assert_nonnull(val); 867 class = qdict_get_str(val, "class"); 868 desc = qdict_get_str(val, "desc"); 869 g_assert_cmpstr(class, ==, "GenericError"); 870 g_assert_cmpint(strlen(desc), >, 0); 871 } 872 #else 873 static void test_qga_guest_exec_separated(gconstpointer fix) 874 { 875 const TestFixture *fixture = fix; 876 g_autoptr(QDict) ret = NULL; 877 QDict *val; 878 const gchar *out, *err; 879 g_autofree guchar *out_decoded = NULL; 880 g_autofree guchar *err_decoded = NULL; 881 int64_t pid, exitcode; 882 gsize len; 883 884 /* exec 'echo foo bar' */ 885 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 886 " 'path': '/bin/bash'," 887 " 'arg': [ '-c', 'for i in $(seq 4); do if (( $i %% 2 )); then echo stdout; else echo stderr 1>&2; fi; done;' ]," 888 " 'capture-output': 'separated' } }"); 889 g_assert_nonnull(ret); 890 qmp_assert_no_error(ret); 891 val = qdict_get_qdict(ret, "return"); 892 pid = qdict_get_int(val, "pid"); 893 g_assert_cmpint(pid, >, 0); 894 qobject_unref(ret); 895 896 ret = wait_for_guest_exec_completion(fixture->fd, pid); 897 898 val = qdict_get_qdict(ret, "return"); 899 exitcode = qdict_get_int(val, "exitcode"); 900 g_assert_cmpint(exitcode, ==, 0); 901 902 /* check stdout */ 903 out = qdict_get_str(val, "out-data"); 904 out_decoded = g_base64_decode(out, &len); 905 g_assert_cmpint(len, ==, 14); 906 g_assert_cmpstr((char *)out_decoded, ==, "stdout\nstdout\n"); 907 908 /* check stderr */ 909 err = qdict_get_try_str(val, "err-data"); 910 err_decoded = g_base64_decode(err, &len); 911 g_assert_cmpint(len, ==, 14); 912 g_assert_cmpstr((char *)err_decoded, ==, "stderr\nstderr\n"); 913 } 914 915 static void test_qga_guest_exec_merged(gconstpointer fix) 916 { 917 const TestFixture *fixture = fix; 918 g_autoptr(QDict) ret = NULL; 919 QDict *val; 920 const gchar *out, *err; 921 g_autofree guchar *decoded = NULL; 922 int64_t pid, exitcode; 923 gsize len; 924 925 /* exec 'echo foo bar' */ 926 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 927 " 'path': '/bin/bash'," 928 " 'arg': [ '-c', 'for i in $(seq 4); do if (( $i %% 2 )); then echo stdout; else echo stderr 1>&2; fi; done;' ]," 929 " 'capture-output': 'merged' } }"); 930 g_assert_nonnull(ret); 931 qmp_assert_no_error(ret); 932 val = qdict_get_qdict(ret, "return"); 933 pid = qdict_get_int(val, "pid"); 934 g_assert_cmpint(pid, >, 0); 935 qobject_unref(ret); 936 937 ret = wait_for_guest_exec_completion(fixture->fd, pid); 938 939 val = qdict_get_qdict(ret, "return"); 940 exitcode = qdict_get_int(val, "exitcode"); 941 g_assert_cmpint(exitcode, ==, 0); 942 943 /* check stdout */ 944 out = qdict_get_str(val, "out-data"); 945 decoded = g_base64_decode(out, &len); 946 g_assert_cmpint(len, ==, 28); 947 g_assert_cmpstr((char *)decoded, ==, "stdout\nstderr\nstdout\nstderr\n"); 948 949 /* check stderr */ 950 err = qdict_get_try_str(val, "err-data"); 951 g_assert_null(err); 952 } 953 #endif 954 955 static void test_qga_guest_exec_invalid(gconstpointer fix) 956 { 957 const TestFixture *fixture = fix; 958 g_autoptr(QDict) ret = NULL; 959 QDict *error; 960 const gchar *class, *desc; 961 962 /* invalid command */ 963 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 964 " 'path': '/bin/invalid-cmd42' } }"); 965 g_assert_nonnull(ret); 966 error = qdict_get_qdict(ret, "error"); 967 g_assert_nonnull(error); 968 class = qdict_get_str(error, "class"); 969 desc = qdict_get_str(error, "desc"); 970 g_assert_cmpstr(class, ==, "GenericError"); 971 g_assert_cmpint(strlen(desc), >, 0); 972 qobject_unref(ret); 973 974 /* invalid pid */ 975 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec-status'," 976 " 'arguments': { 'pid': 0 } }"); 977 g_assert_nonnull(ret); 978 error = qdict_get_qdict(ret, "error"); 979 g_assert_nonnull(error); 980 class = qdict_get_str(error, "class"); 981 desc = qdict_get_str(error, "desc"); 982 g_assert_cmpstr(class, ==, "GenericError"); 983 g_assert_cmpint(strlen(desc), >, 0); 984 } 985 986 static void test_qga_guest_get_host_name(gconstpointer fix) 987 { 988 const TestFixture *fixture = fix; 989 g_autoptr(QDict) ret = NULL; 990 QDict *val; 991 992 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-host-name'}"); 993 g_assert_nonnull(ret); 994 qmp_assert_no_error(ret); 995 996 val = qdict_get_qdict(ret, "return"); 997 g_assert(qdict_haskey(val, "host-name")); 998 } 999 1000 static void test_qga_guest_get_timezone(gconstpointer fix) 1001 { 1002 const TestFixture *fixture = fix; 1003 g_autoptr(QDict) ret = NULL; 1004 QDict *val; 1005 1006 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-timezone'}"); 1007 g_assert_nonnull(ret); 1008 qmp_assert_no_error(ret); 1009 1010 /* Make sure there's at least offset */ 1011 val = qdict_get_qdict(ret, "return"); 1012 g_assert(qdict_haskey(val, "offset")); 1013 } 1014 1015 static void test_qga_guest_get_users(gconstpointer fix) 1016 { 1017 const TestFixture *fixture = fix; 1018 g_autoptr(QDict) ret = NULL; 1019 QList *val; 1020 1021 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-users'}"); 1022 g_assert_nonnull(ret); 1023 qmp_assert_no_error(ret); 1024 1025 /* There is not much to test here */ 1026 val = qdict_get_qlist(ret, "return"); 1027 g_assert_nonnull(val); 1028 } 1029 1030 static void test_qga_guest_get_osinfo(gconstpointer data) 1031 { 1032 TestFixture fixture; 1033 const gchar *str; 1034 g_autoptr(QDict) ret = NULL; 1035 char *env[2]; 1036 QDict *val; 1037 1038 env[0] = g_strdup_printf( 1039 "QGA_OS_RELEASE=%s%c..%cdata%ctest-qga-os-release", 1040 g_test_get_dir(G_TEST_DIST), G_DIR_SEPARATOR, G_DIR_SEPARATOR, G_DIR_SEPARATOR); 1041 env[1] = NULL; 1042 fixture_setup(&fixture, NULL, env); 1043 1044 ret = qmp_fd(fixture.fd, "{'execute': 'guest-get-osinfo'}"); 1045 g_assert_nonnull(ret); 1046 qmp_assert_no_error(ret); 1047 1048 val = qdict_get_qdict(ret, "return"); 1049 1050 str = qdict_get_try_str(val, "id"); 1051 g_assert_nonnull(str); 1052 g_assert_cmpstr(str, ==, "qemu-ga-test"); 1053 1054 str = qdict_get_try_str(val, "name"); 1055 g_assert_nonnull(str); 1056 g_assert_cmpstr(str, ==, "QEMU-GA"); 1057 1058 str = qdict_get_try_str(val, "pretty-name"); 1059 g_assert_nonnull(str); 1060 g_assert_cmpstr(str, ==, "QEMU Guest Agent test"); 1061 1062 str = qdict_get_try_str(val, "version"); 1063 g_assert_nonnull(str); 1064 g_assert_cmpstr(str, ==, "Test 1"); 1065 1066 str = qdict_get_try_str(val, "version-id"); 1067 g_assert_nonnull(str); 1068 g_assert_cmpstr(str, ==, "1"); 1069 1070 str = qdict_get_try_str(val, "variant"); 1071 g_assert_nonnull(str); 1072 g_assert_cmpstr(str, ==, "Unit test \"'$`\\ and \\\\ etc."); 1073 1074 str = qdict_get_try_str(val, "variant-id"); 1075 g_assert_nonnull(str); 1076 g_assert_cmpstr(str, ==, "unit-test"); 1077 1078 g_free(env[0]); 1079 fixture_tear_down(&fixture, NULL); 1080 } 1081 1082 int main(int argc, char **argv) 1083 { 1084 TestFixture fix; 1085 int ret; 1086 1087 #ifdef QEMU_SANITIZE_THREAD 1088 { 1089 g_test_skip("tsan enabled, https://github.com/google/sanitizers/issues/1116"); 1090 return 0; 1091 } 1092 #endif 1093 1094 setlocale (LC_ALL, ""); 1095 g_test_init(&argc, &argv, NULL); 1096 fixture_setup(&fix, NULL, NULL); 1097 1098 g_test_add_data_func("/qga/sync-delimited", &fix, test_qga_sync_delimited); 1099 g_test_add_data_func("/qga/sync", &fix, test_qga_sync); 1100 g_test_add_data_func("/qga/ping", &fix, test_qga_ping); 1101 g_test_add_data_func("/qga/info", &fix, test_qga_info); 1102 g_test_add_data_func("/qga/network-get-interfaces", &fix, 1103 test_qga_network_get_interfaces); 1104 if (!access("/sys/devices/system/cpu/cpu0", F_OK)) { 1105 g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); 1106 } 1107 g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); 1108 g_test_add_data_func("/qga/get-memory-block-info", &fix, 1109 test_qga_get_memory_block_info); 1110 g_test_add_data_func("/qga/get-memory-blocks", &fix, 1111 test_qga_get_memory_blocks); 1112 g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); 1113 g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read); 1114 g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); 1115 g_test_add_data_func("/qga/id", &fix, test_qga_id); 1116 g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob); 1117 g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); 1118 g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args); 1119 g_test_add_data_func("/qga/fsfreeze-status", &fix, 1120 test_qga_fsfreeze_status); 1121 1122 g_test_add_data_func("/qga/blockedrpcs", NULL, test_qga_blockedrpcs); 1123 g_test_add_data_func("/qga/allowedrpcs", NULL, test_qga_allowedrpcs); 1124 g_test_add_data_func("/qga/config", NULL, test_qga_config); 1125 g_test_add_data_func("/qga/guest-exec", &fix, test_qga_guest_exec); 1126 g_test_add_data_func("/qga/guest-exec-separated", &fix, 1127 test_qga_guest_exec_separated); 1128 g_test_add_data_func("/qga/guest-exec-merged", &fix, 1129 test_qga_guest_exec_merged); 1130 g_test_add_data_func("/qga/guest-exec-invalid", &fix, 1131 test_qga_guest_exec_invalid); 1132 g_test_add_data_func("/qga/guest-get-osinfo", &fix, 1133 test_qga_guest_get_osinfo); 1134 g_test_add_data_func("/qga/guest-get-host-name", &fix, 1135 test_qga_guest_get_host_name); 1136 g_test_add_data_func("/qga/guest-get-timezone", &fix, 1137 test_qga_guest_get_timezone); 1138 g_test_add_data_func("/qga/guest-get-users", &fix, 1139 test_qga_guest_get_users); 1140 1141 ret = g_test_run(); 1142 1143 fixture_tear_down(&fix, NULL); 1144 1145 return ret; 1146 } 1147