qemu/tests/unit/test-qga.c
<<
>>
Prefs
   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
  11typedef struct {
  12    char *test_dir;
  13    GMainLoop *loop;
  14    int fd;
  15    GPid pid;
  16} TestFixture;
  17
  18static 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
  43static 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
  51static void
  52fixture_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
  87static void
  88fixture_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
 115static 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
 141static 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
 182static 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
 210static 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
 220static 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
 231static 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
 242static 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
 261static 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
 279static 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
 295static 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
 313static 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
 335static 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
 354static 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
 377static 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
 394static 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
 534static 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
 619static 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
 633static 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
 668static 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
 744static 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
 758static 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
 805static 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
 836static 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
 850static 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
 865static 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
 880static 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
 932int main(int argc, char **argv)
 933{
 934    TestFixture fix;
 935    int ret;
 936
 937#ifdef QEMU_SANITIZE_THREAD
 938    {
 939        g_test_skip("tsan enabled, https://github.com/google/sanitizers/issues/1116");
 940        return 0;
 941    }
 942#endif
 943
 944    setlocale (LC_ALL, "");
 945    g_test_init(&argc, &argv, NULL);
 946    fixture_setup(&fix, NULL, NULL);
 947
 948    g_test_add_data_func("/qga/sync-delimited", &fix, test_qga_sync_delimited);
 949    g_test_add_data_func("/qga/sync", &fix, test_qga_sync);
 950    g_test_add_data_func("/qga/ping", &fix, test_qga_ping);
 951    g_test_add_data_func("/qga/info", &fix, test_qga_info);
 952    g_test_add_data_func("/qga/network-get-interfaces", &fix,
 953                         test_qga_network_get_interfaces);
 954    if (!access("/sys/devices/system/cpu/cpu0", F_OK)) {
 955        g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus);
 956    }
 957    g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo);
 958    g_test_add_data_func("/qga/get-memory-block-info", &fix,
 959                         test_qga_get_memory_block_info);
 960    g_test_add_data_func("/qga/get-memory-blocks", &fix,
 961                         test_qga_get_memory_blocks);
 962    g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops);
 963    g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read);
 964    g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time);
 965    g_test_add_data_func("/qga/id", &fix, test_qga_id);
 966    g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob);
 967    g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd);
 968    g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args);
 969    g_test_add_data_func("/qga/fsfreeze-status", &fix,
 970                         test_qga_fsfreeze_status);
 971
 972    g_test_add_data_func("/qga/blockedrpcs", NULL, test_qga_blockedrpcs);
 973    g_test_add_data_func("/qga/config", NULL, test_qga_config);
 974    g_test_add_data_func("/qga/guest-exec", &fix, test_qga_guest_exec);
 975    g_test_add_data_func("/qga/guest-exec-invalid", &fix,
 976                         test_qga_guest_exec_invalid);
 977    g_test_add_data_func("/qga/guest-get-osinfo", &fix,
 978                         test_qga_guest_get_osinfo);
 979    g_test_add_data_func("/qga/guest-get-host-name", &fix,
 980                         test_qga_guest_get_host_name);
 981    g_test_add_data_func("/qga/guest-get-timezone", &fix,
 982                         test_qga_guest_get_timezone);
 983    g_test_add_data_func("/qga/guest-get-users", &fix,
 984                         test_qga_guest_get_users);
 985
 986    ret = g_test_run();
 987
 988    fixture_tear_down(&fix, NULL);
 989
 990    return ret;
 991}
 992