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