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