qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

test-qga.c (31008B)


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