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 "qobject/qdict.h" 9 #include "qobject/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_load(gconstpointer fix) 336 { 337 const TestFixture *fixture = fix; 338 g_autoptr(QDict) ret = NULL; 339 QDict *load; 340 341 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-load'}"); 342 g_assert_nonnull(ret); 343 qmp_assert_no_error(ret); 344 345 load = qdict_get_qdict(ret, "return"); 346 g_assert(qdict_haskey(load, "load1m")); 347 g_assert(qdict_haskey(load, "load5m")); 348 g_assert(qdict_haskey(load, "load15m")); 349 } 350 351 static void test_qga_get_memory_block_info(gconstpointer fix) 352 { 353 const TestFixture *fixture = fix; 354 g_autoptr(QDict) ret = NULL; 355 QDict *val; 356 int64_t size; 357 358 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-block-info'}"); 359 g_assert_nonnull(ret); 360 361 /* some systems might not expose memory block info in sysfs */ 362 if (!qdict_haskey(ret, "error")) { 363 /* check there is at least some memory */ 364 val = qdict_get_qdict(ret, "return"); 365 size = qdict_get_int(val, "size"); 366 g_assert_cmpint(size, >, 0); 367 } 368 } 369 370 static void test_qga_get_memory_blocks(gconstpointer fix) 371 { 372 const TestFixture *fixture = fix; 373 g_autoptr(QDict) ret = NULL; 374 QList *list; 375 const QListEntry *entry; 376 377 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-blocks'}"); 378 g_assert_nonnull(ret); 379 380 /* some systems might not expose memory block info in sysfs */ 381 if (!qdict_haskey(ret, "error")) { 382 list = qdict_get_qlist(ret, "return"); 383 entry = qlist_first(list); 384 /* newer versions of qga may return empty list without error */ 385 if (entry) { 386 g_assert(qdict_haskey(qobject_to(QDict, entry->value), 387 "phys-index")); 388 g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); 389 } 390 } 391 } 392 393 static void test_qga_network_get_interfaces(gconstpointer fix) 394 { 395 const TestFixture *fixture = fix; 396 g_autoptr(QDict) ret = NULL; 397 QList *list; 398 const QListEntry *entry; 399 400 ret = qmp_fd(fixture->fd, "{'execute': 'guest-network-get-interfaces'}"); 401 g_assert_nonnull(ret); 402 qmp_assert_no_error(ret); 403 404 /* check there is at least an interface */ 405 list = qdict_get_qlist(ret, "return"); 406 entry = qlist_first(list); 407 g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); 408 } 409 410 static void test_qga_file_ops(gconstpointer fix) 411 { 412 const TestFixture *fixture = fix; 413 const unsigned char helloworld[] = "Hello World!\n"; 414 const char *b64; 415 gchar *path, *enc; 416 unsigned char *dec; 417 QDict *ret, *val; 418 int64_t id, eof; 419 gsize count; 420 FILE *f; 421 char tmp[100]; 422 423 /* open */ 424 ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," 425 " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); 426 g_assert_nonnull(ret); 427 qmp_assert_no_error(ret); 428 id = qdict_get_int(ret, "return"); 429 qobject_unref(ret); 430 431 enc = g_base64_encode(helloworld, sizeof(helloworld)); 432 /* write */ 433 ret = qmp_fd(fixture->fd, 434 "{'execute': 'guest-file-write'," 435 " 'arguments': { 'handle': %" PRId64 ", 'buf-b64': %s } }", 436 id, enc); 437 g_assert_nonnull(ret); 438 qmp_assert_no_error(ret); 439 440 val = qdict_get_qdict(ret, "return"); 441 count = qdict_get_int(val, "count"); 442 eof = qdict_get_bool(val, "eof"); 443 g_assert_cmpint(count, ==, sizeof(helloworld)); 444 g_assert_cmpint(eof, ==, 0); 445 qobject_unref(ret); 446 447 /* flush */ 448 ret = qmp_fd(fixture->fd, 449 "{'execute': 'guest-file-flush'," 450 " 'arguments': {'handle': %" PRId64 "} }", 451 id); 452 qobject_unref(ret); 453 454 /* close */ 455 ret = qmp_fd(fixture->fd, 456 "{'execute': 'guest-file-close'," 457 " 'arguments': {'handle': %" PRId64 "} }", 458 id); 459 qobject_unref(ret); 460 461 /* check content */ 462 path = g_build_filename(fixture->test_dir, "foo", NULL); 463 f = fopen(path, "r"); 464 g_free(path); 465 g_assert_nonnull(f); 466 count = fread(tmp, 1, sizeof(tmp), f); 467 g_assert_cmpint(count, ==, sizeof(helloworld)); 468 tmp[count] = 0; 469 g_assert_cmpstr(tmp, ==, (char *)helloworld); 470 fclose(f); 471 472 /* open */ 473 ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," 474 " 'arguments': { 'path': 'foo', 'mode': 'r' } }"); 475 g_assert_nonnull(ret); 476 qmp_assert_no_error(ret); 477 id = qdict_get_int(ret, "return"); 478 qobject_unref(ret); 479 480 /* read */ 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, ==, sizeof(helloworld)); 490 g_assert(eof); 491 g_assert_cmpstr(b64, ==, enc); 492 493 qobject_unref(ret); 494 g_free(enc); 495 496 /* read eof */ 497 ret = qmp_fd(fixture->fd, 498 "{'execute': 'guest-file-read'," 499 " 'arguments': { 'handle': %" PRId64 "} }", 500 id); 501 val = qdict_get_qdict(ret, "return"); 502 count = qdict_get_int(val, "count"); 503 eof = qdict_get_bool(val, "eof"); 504 b64 = qdict_get_str(val, "buf-b64"); 505 g_assert_cmpint(count, ==, 0); 506 g_assert(eof); 507 g_assert_cmpstr(b64, ==, ""); 508 qobject_unref(ret); 509 510 /* seek */ 511 ret = qmp_fd(fixture->fd, 512 "{'execute': 'guest-file-seek'," 513 " 'arguments': { 'handle': %" PRId64 ", " 514 " 'offset': %d, 'whence': %s } }", 515 id, 6, "set"); 516 qmp_assert_no_error(ret); 517 val = qdict_get_qdict(ret, "return"); 518 count = qdict_get_int(val, "position"); 519 eof = qdict_get_bool(val, "eof"); 520 g_assert_cmpint(count, ==, 6); 521 g_assert(!eof); 522 qobject_unref(ret); 523 524 /* partial read */ 525 ret = qmp_fd(fixture->fd, 526 "{'execute': 'guest-file-read'," 527 " 'arguments': { 'handle': %" PRId64 "} }", 528 id); 529 val = qdict_get_qdict(ret, "return"); 530 count = qdict_get_int(val, "count"); 531 eof = qdict_get_bool(val, "eof"); 532 b64 = qdict_get_str(val, "buf-b64"); 533 g_assert_cmpint(count, ==, sizeof(helloworld) - 6); 534 g_assert(eof); 535 dec = g_base64_decode(b64, &count); 536 g_assert_cmpint(count, ==, sizeof(helloworld) - 6); 537 g_assert_cmpmem(dec, count, helloworld + 6, sizeof(helloworld) - 6); 538 g_free(dec); 539 540 qobject_unref(ret); 541 542 /* close */ 543 ret = qmp_fd(fixture->fd, 544 "{'execute': 'guest-file-close'," 545 " 'arguments': {'handle': %" PRId64 "} }", 546 id); 547 qobject_unref(ret); 548 } 549 550 static void test_qga_file_write_read(gconstpointer fix) 551 { 552 const TestFixture *fixture = fix; 553 const unsigned char helloworld[] = "Hello World!\n"; 554 const char *b64; 555 gchar *enc; 556 QDict *ret, *val; 557 int64_t id, eof; 558 gsize count; 559 560 /* open */ 561 ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," 562 " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); 563 g_assert_nonnull(ret); 564 qmp_assert_no_error(ret); 565 id = qdict_get_int(ret, "return"); 566 qobject_unref(ret); 567 568 enc = g_base64_encode(helloworld, sizeof(helloworld)); 569 /* write */ 570 ret = qmp_fd(fixture->fd, 571 "{'execute': 'guest-file-write'," 572 " 'arguments': { 'handle': %" PRId64 "," 573 " 'buf-b64': %s } }", id, enc); 574 g_assert_nonnull(ret); 575 qmp_assert_no_error(ret); 576 577 val = qdict_get_qdict(ret, "return"); 578 count = qdict_get_int(val, "count"); 579 eof = qdict_get_bool(val, "eof"); 580 g_assert_cmpint(count, ==, sizeof(helloworld)); 581 g_assert_cmpint(eof, ==, 0); 582 qobject_unref(ret); 583 584 /* read (check implicit flush) */ 585 ret = qmp_fd(fixture->fd, 586 "{'execute': 'guest-file-read'," 587 " 'arguments': { 'handle': %" PRId64 "} }", 588 id); 589 val = qdict_get_qdict(ret, "return"); 590 count = qdict_get_int(val, "count"); 591 eof = qdict_get_bool(val, "eof"); 592 b64 = qdict_get_str(val, "buf-b64"); 593 g_assert_cmpint(count, ==, 0); 594 g_assert(eof); 595 g_assert_cmpstr(b64, ==, ""); 596 qobject_unref(ret); 597 598 /* seek to 0 */ 599 ret = qmp_fd(fixture->fd, 600 "{'execute': 'guest-file-seek'," 601 " 'arguments': { 'handle': %" PRId64 ", " 602 " 'offset': %d, 'whence': %s } }", 603 id, 0, "set"); 604 qmp_assert_no_error(ret); 605 val = qdict_get_qdict(ret, "return"); 606 count = qdict_get_int(val, "position"); 607 eof = qdict_get_bool(val, "eof"); 608 g_assert_cmpint(count, ==, 0); 609 g_assert(!eof); 610 qobject_unref(ret); 611 612 /* read */ 613 ret = qmp_fd(fixture->fd, 614 "{'execute': 'guest-file-read'," 615 " 'arguments': { 'handle': %" PRId64 "} }", 616 id); 617 val = qdict_get_qdict(ret, "return"); 618 count = qdict_get_int(val, "count"); 619 eof = qdict_get_bool(val, "eof"); 620 b64 = qdict_get_str(val, "buf-b64"); 621 g_assert_cmpint(count, ==, sizeof(helloworld)); 622 g_assert(eof); 623 g_assert_cmpstr(b64, ==, enc); 624 qobject_unref(ret); 625 g_free(enc); 626 627 /* close */ 628 ret = qmp_fd(fixture->fd, 629 "{'execute': 'guest-file-close'," 630 " 'arguments': {'handle': %" PRId64 "} }", 631 id); 632 qobject_unref(ret); 633 } 634 635 static void test_qga_get_time(gconstpointer fix) 636 { 637 const TestFixture *fixture = fix; 638 g_autoptr(QDict) ret = NULL; 639 int64_t time; 640 641 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); 642 g_assert_nonnull(ret); 643 qmp_assert_no_error(ret); 644 645 time = qdict_get_int(ret, "return"); 646 g_assert_cmpint(time, >, 0); 647 } 648 649 static void test_qga_blockedrpcs(gconstpointer data) 650 { 651 TestFixture fix; 652 QDict *ret, *error; 653 const gchar *class, *desc; 654 655 fixture_setup(&fix, "-b guest-ping,guest-get-time", NULL); 656 657 /* check blocked RPCs */ 658 ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); 659 g_assert_nonnull(ret); 660 error = qdict_get_qdict(ret, "error"); 661 class = qdict_get_try_str(error, "class"); 662 desc = qdict_get_try_str(error, "desc"); 663 g_assert_cmpstr(class, ==, "CommandNotFound"); 664 g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); 665 qobject_unref(ret); 666 667 ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); 668 g_assert_nonnull(ret); 669 error = qdict_get_qdict(ret, "error"); 670 class = qdict_get_try_str(error, "class"); 671 desc = qdict_get_try_str(error, "desc"); 672 g_assert_cmpstr(class, ==, "CommandNotFound"); 673 g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); 674 qobject_unref(ret); 675 676 /* check something work */ 677 ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); 678 qmp_assert_no_error(ret); 679 qobject_unref(ret); 680 681 fixture_tear_down(&fix, NULL); 682 } 683 684 static void test_qga_allowedrpcs(gconstpointer data) 685 { 686 TestFixture fix; 687 QDict *ret, *error; 688 const gchar *class, *desc; 689 690 fixture_setup(&fix, "-a guest-ping,guest-get-time", NULL); 691 692 /* check allowed RPCs */ 693 ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); 694 qmp_assert_no_error(ret); 695 qobject_unref(ret); 696 697 ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); 698 qmp_assert_no_error(ret); 699 qobject_unref(ret); 700 701 /* check something else */ 702 ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); 703 g_assert_nonnull(ret); 704 error = qdict_get_qdict(ret, "error"); 705 class = qdict_get_try_str(error, "class"); 706 desc = qdict_get_try_str(error, "desc"); 707 g_assert_cmpstr(class, ==, "CommandNotFound"); 708 g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); 709 qobject_unref(ret); 710 711 fixture_tear_down(&fix, NULL); 712 } 713 714 static void test_qga_config(gconstpointer data) 715 { 716 GError *error = NULL; 717 g_autofree char *out = NULL; 718 g_autofree char *err = NULL; 719 g_autofree char *cwd = NULL; 720 g_autofree char *cmd = NULL; 721 g_auto(GStrv) argv = NULL; 722 g_auto(GStrv) strv = NULL; 723 g_autoptr(GKeyFile) kf = NULL; 724 char *str; 725 char *env[2]; 726 int status; 727 gsize n; 728 729 cwd = g_get_current_dir(); 730 cmd = g_strdup_printf("%s%cqga%cqemu-ga -D", 731 cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR); 732 g_shell_parse_argv(cmd, NULL, &argv, &error); 733 g_assert_no_error(error); 734 735 env[0] = g_strdup_printf("QGA_CONF=tests%cdata%ctest-qga-config", 736 G_DIR_SEPARATOR, G_DIR_SEPARATOR); 737 env[1] = NULL; 738 g_spawn_sync(NULL, argv, env, 0, 739 NULL, NULL, &out, &err, &status, &error); 740 741 g_assert_no_error(error); 742 g_assert_cmpstr(err, ==, ""); 743 g_assert_cmpint(status, ==, 0); 744 745 kf = g_key_file_new(); 746 g_key_file_load_from_data(kf, out, -1, G_KEY_FILE_NONE, &error); 747 g_assert_no_error(error); 748 749 str = g_key_file_get_start_group(kf); 750 g_assert_cmpstr(str, ==, "general"); 751 g_free(str); 752 753 g_assert_false(g_key_file_get_boolean(kf, "general", "daemon", &error)); 754 g_assert_no_error(error); 755 756 str = g_key_file_get_string(kf, "general", "method", &error); 757 g_assert_no_error(error); 758 g_assert_cmpstr(str, ==, "virtio-serial"); 759 g_free(str); 760 761 str = g_key_file_get_string(kf, "general", "path", &error); 762 g_assert_no_error(error); 763 g_assert_cmpstr(str, ==, "/path/to/org.qemu.guest_agent.0"); 764 g_free(str); 765 766 str = g_key_file_get_string(kf, "general", "pidfile", &error); 767 g_assert_no_error(error); 768 g_assert_cmpstr(str, ==, "/var/foo/qemu-ga.pid"); 769 g_free(str); 770 771 str = g_key_file_get_string(kf, "general", "statedir", &error); 772 g_assert_no_error(error); 773 g_assert_cmpstr(str, ==, "/var/state"); 774 g_free(str); 775 776 g_assert_true(g_key_file_get_boolean(kf, "general", "verbose", &error)); 777 g_assert_no_error(error); 778 779 strv = g_key_file_get_string_list(kf, "general", "block-rpcs", &n, &error); 780 g_assert_cmpint(n, ==, 2); 781 g_assert_true(g_strv_contains((const char * const *)strv, 782 "guest-ping")); 783 g_assert_true(g_strv_contains((const char * const *)strv, 784 "guest-get-time")); 785 g_assert_no_error(error); 786 787 g_free(env[0]); 788 } 789 790 static void test_qga_fsfreeze_status(gconstpointer fix) 791 { 792 const TestFixture *fixture = fix; 793 g_autoptr(QDict) ret = NULL; 794 const gchar *status; 795 796 ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); 797 g_assert_nonnull(ret); 798 qmp_assert_no_error(ret); 799 800 status = qdict_get_try_str(ret, "return"); 801 g_assert_cmpstr(status, ==, "thawed"); 802 } 803 804 static QDict *wait_for_guest_exec_completion(int fd, int64_t pid) 805 { 806 QDict *ret = NULL; 807 int64_t now; 808 bool exited; 809 QDict *val; 810 811 now = g_get_monotonic_time(); 812 do { 813 ret = qmp_fd(fd, 814 "{'execute': 'guest-exec-status'," 815 " 'arguments': { 'pid': %" PRId64 " } }", pid); 816 g_assert_nonnull(ret); 817 val = qdict_get_qdict(ret, "return"); 818 exited = qdict_get_bool(val, "exited"); 819 if (!exited) { 820 qobject_unref(ret); 821 } 822 } while (!exited && 823 g_get_monotonic_time() < now + 5 * G_TIME_SPAN_SECOND); 824 g_assert(exited); 825 826 return ret; 827 } 828 829 static void test_qga_guest_exec(gconstpointer fix) 830 { 831 const TestFixture *fixture = fix; 832 g_autoptr(QDict) ret = NULL; 833 QDict *val; 834 const gchar *out; 835 g_autofree guchar *decoded = NULL; 836 int64_t pid, exitcode; 837 gsize len; 838 839 /* exec 'echo foo bar' */ 840 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 841 " 'path': 'echo', 'arg': [ '-n', '\" test_str \"' ]," 842 " 'capture-output': true } }"); 843 g_assert_nonnull(ret); 844 qmp_assert_no_error(ret); 845 val = qdict_get_qdict(ret, "return"); 846 pid = qdict_get_int(val, "pid"); 847 g_assert_cmpint(pid, >, 0); 848 qobject_unref(ret); 849 850 ret = wait_for_guest_exec_completion(fixture->fd, pid); 851 852 /* check stdout */ 853 val = qdict_get_qdict(ret, "return"); 854 exitcode = qdict_get_int(val, "exitcode"); 855 g_assert_cmpint(exitcode, ==, 0); 856 out = qdict_get_str(val, "out-data"); 857 decoded = g_base64_decode(out, &len); 858 g_assert_cmpint(len, ==, 12); 859 g_assert_cmpstr((char *)decoded, ==, "\" test_str \""); 860 } 861 862 #if defined(G_OS_WIN32) 863 static void test_qga_guest_exec_separated(gconstpointer fix) 864 { 865 } 866 static void test_qga_guest_exec_merged(gconstpointer fix) 867 { 868 const TestFixture *fixture = fix; 869 g_autoptr(QDict) ret = NULL; 870 QDict *val; 871 const gchar *class, *desc; 872 g_autofree guchar *decoded = NULL; 873 874 /* exec 'echo foo bar' */ 875 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 876 " 'path': 'echo'," 877 " 'arg': [ 'execution never reaches here' ]," 878 " 'capture-output': 'merged' } }"); 879 880 g_assert_nonnull(ret); 881 val = qdict_get_qdict(ret, "error"); 882 g_assert_nonnull(val); 883 class = qdict_get_str(val, "class"); 884 desc = qdict_get_str(val, "desc"); 885 g_assert_cmpstr(class, ==, "GenericError"); 886 g_assert_cmpint(strlen(desc), >, 0); 887 } 888 #else 889 static void test_qga_guest_exec_separated(gconstpointer fix) 890 { 891 const TestFixture *fixture = fix; 892 g_autoptr(QDict) ret = NULL; 893 QDict *val; 894 const gchar *out, *err; 895 g_autofree guchar *out_decoded = NULL; 896 g_autofree guchar *err_decoded = NULL; 897 int64_t pid, exitcode; 898 gsize len; 899 900 /* exec 'echo foo bar' */ 901 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 902 " 'path': 'bash'," 903 " 'arg': [ '-c', 'for i in $(seq 4); do if (( $i %% 2 )); then echo stdout; else echo stderr 1>&2; fi; done;' ]," 904 " 'capture-output': 'separated' } }"); 905 g_assert_nonnull(ret); 906 qmp_assert_no_error(ret); 907 val = qdict_get_qdict(ret, "return"); 908 pid = qdict_get_int(val, "pid"); 909 g_assert_cmpint(pid, >, 0); 910 qobject_unref(ret); 911 912 ret = wait_for_guest_exec_completion(fixture->fd, pid); 913 914 val = qdict_get_qdict(ret, "return"); 915 exitcode = qdict_get_int(val, "exitcode"); 916 g_assert_cmpint(exitcode, ==, 0); 917 918 /* check stdout */ 919 out = qdict_get_str(val, "out-data"); 920 out_decoded = g_base64_decode(out, &len); 921 g_assert_cmpint(len, ==, 14); 922 g_assert_cmpstr((char *)out_decoded, ==, "stdout\nstdout\n"); 923 924 /* check stderr */ 925 err = qdict_get_try_str(val, "err-data"); 926 err_decoded = g_base64_decode(err, &len); 927 g_assert_cmpint(len, ==, 14); 928 g_assert_cmpstr((char *)err_decoded, ==, "stderr\nstderr\n"); 929 } 930 931 static void test_qga_guest_exec_merged(gconstpointer fix) 932 { 933 const TestFixture *fixture = fix; 934 g_autoptr(QDict) ret = NULL; 935 QDict *val; 936 const gchar *out, *err; 937 g_autofree guchar *decoded = NULL; 938 int64_t pid, exitcode; 939 gsize len; 940 941 /* exec 'echo foo bar' */ 942 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 943 " 'path': 'bash'," 944 " 'arg': [ '-c', 'for i in $(seq 4); do if (( $i %% 2 )); then echo stdout; else echo stderr 1>&2; fi; done;' ]," 945 " 'capture-output': 'merged' } }"); 946 g_assert_nonnull(ret); 947 qmp_assert_no_error(ret); 948 val = qdict_get_qdict(ret, "return"); 949 pid = qdict_get_int(val, "pid"); 950 g_assert_cmpint(pid, >, 0); 951 qobject_unref(ret); 952 953 ret = wait_for_guest_exec_completion(fixture->fd, pid); 954 955 val = qdict_get_qdict(ret, "return"); 956 exitcode = qdict_get_int(val, "exitcode"); 957 g_assert_cmpint(exitcode, ==, 0); 958 959 /* check stdout */ 960 out = qdict_get_str(val, "out-data"); 961 decoded = g_base64_decode(out, &len); 962 g_assert_cmpint(len, ==, 28); 963 g_assert_cmpstr((char *)decoded, ==, "stdout\nstderr\nstdout\nstderr\n"); 964 965 /* check stderr */ 966 err = qdict_get_try_str(val, "err-data"); 967 g_assert_null(err); 968 } 969 #endif 970 971 static void test_qga_guest_exec_invalid(gconstpointer fix) 972 { 973 const TestFixture *fixture = fix; 974 g_autoptr(QDict) ret = NULL; 975 QDict *error; 976 const gchar *class, *desc; 977 978 /* invalid command */ 979 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" 980 " 'path': '/bin/invalid-cmd42' } }"); 981 g_assert_nonnull(ret); 982 error = qdict_get_qdict(ret, "error"); 983 g_assert_nonnull(error); 984 class = qdict_get_str(error, "class"); 985 desc = qdict_get_str(error, "desc"); 986 g_assert_cmpstr(class, ==, "GenericError"); 987 g_assert_cmpint(strlen(desc), >, 0); 988 qobject_unref(ret); 989 990 /* invalid pid */ 991 ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec-status'," 992 " 'arguments': { 'pid': 0 } }"); 993 g_assert_nonnull(ret); 994 error = qdict_get_qdict(ret, "error"); 995 g_assert_nonnull(error); 996 class = qdict_get_str(error, "class"); 997 desc = qdict_get_str(error, "desc"); 998 g_assert_cmpstr(class, ==, "GenericError"); 999 g_assert_cmpint(strlen(desc), >, 0); 1000 } 1001 1002 static void test_qga_guest_get_host_name(gconstpointer fix) 1003 { 1004 const TestFixture *fixture = fix; 1005 g_autoptr(QDict) ret = NULL; 1006 QDict *val; 1007 1008 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-host-name'}"); 1009 g_assert_nonnull(ret); 1010 qmp_assert_no_error(ret); 1011 1012 val = qdict_get_qdict(ret, "return"); 1013 g_assert(qdict_haskey(val, "host-name")); 1014 } 1015 1016 static void test_qga_guest_get_timezone(gconstpointer fix) 1017 { 1018 const TestFixture *fixture = fix; 1019 g_autoptr(QDict) ret = NULL; 1020 QDict *val; 1021 1022 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-timezone'}"); 1023 g_assert_nonnull(ret); 1024 qmp_assert_no_error(ret); 1025 1026 /* Make sure there's at least offset */ 1027 val = qdict_get_qdict(ret, "return"); 1028 g_assert(qdict_haskey(val, "offset")); 1029 } 1030 1031 static void test_qga_guest_get_users(gconstpointer fix) 1032 { 1033 const TestFixture *fixture = fix; 1034 g_autoptr(QDict) ret = NULL; 1035 QList *val; 1036 1037 ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-users'}"); 1038 g_assert_nonnull(ret); 1039 qmp_assert_no_error(ret); 1040 1041 /* There is not much to test here */ 1042 val = qdict_get_qlist(ret, "return"); 1043 g_assert_nonnull(val); 1044 } 1045 1046 static void test_qga_guest_get_osinfo(gconstpointer data) 1047 { 1048 TestFixture fixture; 1049 const gchar *str; 1050 g_autoptr(QDict) ret = NULL; 1051 char *env[2]; 1052 QDict *val; 1053 1054 env[0] = g_strdup_printf( 1055 "QGA_OS_RELEASE=%s%c..%cdata%ctest-qga-os-release", 1056 g_test_get_dir(G_TEST_DIST), G_DIR_SEPARATOR, G_DIR_SEPARATOR, G_DIR_SEPARATOR); 1057 env[1] = NULL; 1058 fixture_setup(&fixture, NULL, env); 1059 1060 ret = qmp_fd(fixture.fd, "{'execute': 'guest-get-osinfo'}"); 1061 g_assert_nonnull(ret); 1062 qmp_assert_no_error(ret); 1063 1064 val = qdict_get_qdict(ret, "return"); 1065 1066 str = qdict_get_try_str(val, "id"); 1067 g_assert_nonnull(str); 1068 g_assert_cmpstr(str, ==, "qemu-ga-test"); 1069 1070 str = qdict_get_try_str(val, "name"); 1071 g_assert_nonnull(str); 1072 g_assert_cmpstr(str, ==, "QEMU-GA"); 1073 1074 str = qdict_get_try_str(val, "pretty-name"); 1075 g_assert_nonnull(str); 1076 g_assert_cmpstr(str, ==, "QEMU Guest Agent test"); 1077 1078 str = qdict_get_try_str(val, "version"); 1079 g_assert_nonnull(str); 1080 g_assert_cmpstr(str, ==, "Test 1"); 1081 1082 str = qdict_get_try_str(val, "version-id"); 1083 g_assert_nonnull(str); 1084 g_assert_cmpstr(str, ==, "1"); 1085 1086 str = qdict_get_try_str(val, "variant"); 1087 g_assert_nonnull(str); 1088 g_assert_cmpstr(str, ==, "Unit test \"'$`\\ and \\\\ etc."); 1089 1090 str = qdict_get_try_str(val, "variant-id"); 1091 g_assert_nonnull(str); 1092 g_assert_cmpstr(str, ==, "unit-test"); 1093 1094 g_free(env[0]); 1095 fixture_tear_down(&fixture, NULL); 1096 } 1097 1098 int main(int argc, char **argv) 1099 { 1100 TestFixture fix; 1101 int ret; 1102 1103 #ifdef QEMU_SANITIZE_THREAD 1104 { 1105 g_test_skip("tsan enabled, https://github.com/google/sanitizers/issues/1116"); 1106 return 0; 1107 } 1108 #endif 1109 1110 setlocale (LC_ALL, ""); 1111 g_test_init(&argc, &argv, NULL); 1112 fixture_setup(&fix, NULL, NULL); 1113 1114 g_test_add_data_func("/qga/sync-delimited", &fix, test_qga_sync_delimited); 1115 g_test_add_data_func("/qga/sync", &fix, test_qga_sync); 1116 g_test_add_data_func("/qga/ping", &fix, test_qga_ping); 1117 g_test_add_data_func("/qga/info", &fix, test_qga_info); 1118 g_test_add_data_func("/qga/network-get-interfaces", &fix, 1119 test_qga_network_get_interfaces); 1120 if (!access("/sys/devices/system/cpu/cpu0", F_OK)) { 1121 g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); 1122 } 1123 g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); 1124 g_test_add_data_func("/qga/get-load", &fix, test_qga_get_load); 1125 g_test_add_data_func("/qga/get-memory-block-info", &fix, 1126 test_qga_get_memory_block_info); 1127 g_test_add_data_func("/qga/get-memory-blocks", &fix, 1128 test_qga_get_memory_blocks); 1129 g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); 1130 g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read); 1131 g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); 1132 g_test_add_data_func("/qga/id", &fix, test_qga_id); 1133 g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob); 1134 g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); 1135 g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args); 1136 g_test_add_data_func("/qga/fsfreeze-status", &fix, 1137 test_qga_fsfreeze_status); 1138 1139 g_test_add_data_func("/qga/blockedrpcs", NULL, test_qga_blockedrpcs); 1140 g_test_add_data_func("/qga/allowedrpcs", NULL, test_qga_allowedrpcs); 1141 g_test_add_data_func("/qga/config", NULL, test_qga_config); 1142 g_test_add_data_func("/qga/guest-exec", &fix, test_qga_guest_exec); 1143 g_test_add_data_func("/qga/guest-exec-separated", &fix, 1144 test_qga_guest_exec_separated); 1145 g_test_add_data_func("/qga/guest-exec-merged", &fix, 1146 test_qga_guest_exec_merged); 1147 g_test_add_data_func("/qga/guest-exec-invalid", &fix, 1148 test_qga_guest_exec_invalid); 1149 g_test_add_data_func("/qga/guest-get-osinfo", &fix, 1150 test_qga_guest_get_osinfo); 1151 g_test_add_data_func("/qga/guest-get-host-name", &fix, 1152 test_qga_guest_get_host_name); 1153 g_test_add_data_func("/qga/guest-get-timezone", &fix, 1154 test_qga_guest_get_timezone); 1155 g_test_add_data_func("/qga/guest-get-users", &fix, 1156 test_qga_guest_get_users); 1157 1158 ret = g_test_run(); 1159 1160 fixture_tear_down(&fix, NULL); 1161 1162 return ret; 1163 } 1164