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