xref: /openbmc/qemu/tests/unit/test-qga.c (revision 582a098e)
1 #include "qemu/osdep.h"
2 #include <locale.h>
3 #include <glib/gstdio.h>
4 #include <sys/socket.h>
5 #include <sys/un.h>
6 
7 #include "../qtest/libqtest.h"
8 #include "qapi/qmp/qdict.h"
9 #include "qapi/qmp/qlist.h"
10 
11 typedef struct {
12     char *test_dir;
13     GMainLoop *loop;
14     int fd;
15     GPid pid;
16 } TestFixture;
17 
18 static int connect_qga(char *path)
19 {
20     int s, ret, len, i = 0;
21     struct sockaddr_un remote;
22 
23     s = socket(AF_UNIX, SOCK_STREAM, 0);
24     g_assert(s != -1);
25 
26     remote.sun_family = AF_UNIX;
27     do {
28         strcpy(remote.sun_path, path);
29         len = strlen(remote.sun_path) + sizeof(remote.sun_family);
30         ret = connect(s, (struct sockaddr *)&remote, len);
31         if (ret == -1) {
32             g_usleep(G_USEC_PER_SEC);
33         }
34         if (i++ == 10) {
35             return -1;
36         }
37     } while (ret == -1);
38 
39     return s;
40 }
41 
42 static 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 
50 static void
51 fixture_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(g_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 
86 static void
87 fixture_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 
114 static 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 
140 static 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 
181 static 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 
209 static 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 
219 static 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 
230 static 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 
241 static 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 
260 static 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 
278 static 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 
294 static 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 
312 static 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 
334 static 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 
353 static 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 
376 static 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 
393 static 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 
533 static 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 
618 static 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 
632 static 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 
667 static 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", "block-rpcs", &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 
743 static 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 
757 static 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 
804 static 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 
835 static 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 
849 static 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 
864 static 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 
879 static 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 
931 int 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