]> Git Repo - qemu.git/blob - tests/test-char.c
test-char: start a /char/serial test
[qemu.git] / tests / test-char.c
1 #include "qemu/osdep.h"
2 #include <glib/gstdio.h>
3
4 #include "qemu-common.h"
5 #include "qemu/config-file.h"
6 #include "qemu/sockets.h"
7 #include "chardev/char-fe.h"
8 #include "sysemu/sysemu.h"
9 #include "qapi/error.h"
10 #include "qom/qom-qobject.h"
11 #include "qmp-commands.h"
12
13 static bool quit;
14
15 typedef struct FeHandler {
16     int read_count;
17     int last_event;
18     char read_buf[128];
19 } FeHandler;
20
21 static void main_loop(void)
22 {
23     bool nonblocking;
24     int last_io = 0;
25
26     quit = false;
27     do {
28         nonblocking = last_io > 0;
29         last_io = main_loop_wait(nonblocking);
30     } while (!quit);
31 }
32
33 static int fe_can_read(void *opaque)
34 {
35     FeHandler *h = opaque;
36
37     return sizeof(h->read_buf) - h->read_count;
38 }
39
40 static void fe_read(void *opaque, const uint8_t *buf, int size)
41 {
42     FeHandler *h = opaque;
43
44     g_assert_cmpint(size, <=, fe_can_read(opaque));
45
46     memcpy(h->read_buf + h->read_count, buf, size);
47     h->read_count += size;
48     quit = true;
49 }
50
51 static void fe_event(void *opaque, int event)
52 {
53     FeHandler *h = opaque;
54
55     h->last_event = event;
56     quit = true;
57 }
58
59 #ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
60 #ifdef _WIN32
61 static void char_console_test_subprocess(void)
62 {
63     QemuOpts *opts;
64     Chardev *chr;
65
66     opts = qemu_opts_create(qemu_find_opts("chardev"), "console-label",
67                             1, &error_abort);
68     qemu_opt_set(opts, "backend", "console", &error_abort);
69
70     chr = qemu_chr_new_from_opts(opts, NULL);
71     g_assert_nonnull(chr);
72
73     qemu_chr_write_all(chr, (const uint8_t *)"CONSOLE", 7);
74
75     qemu_opts_del(opts);
76     object_unparent(OBJECT(chr));
77 }
78
79 static void char_console_test(void)
80 {
81     g_test_trap_subprocess("/char/console/subprocess", 0, 0);
82     g_test_trap_assert_passed();
83     g_test_trap_assert_stdout("CONSOLE");
84 }
85 #endif
86 static void char_stdio_test_subprocess(void)
87 {
88     Chardev *chr;
89     CharBackend be;
90     int ret;
91
92     chr = qemu_chr_new("label", "stdio");
93     g_assert_nonnull(chr);
94
95     qemu_chr_fe_init(&be, chr, &error_abort);
96     qemu_chr_fe_set_open(&be, true);
97     ret = qemu_chr_fe_write(&be, (void *)"buf", 4);
98     g_assert_cmpint(ret, ==, 4);
99
100     qemu_chr_fe_deinit(&be, true);
101 }
102
103 static void char_stdio_test(void)
104 {
105     g_test_trap_subprocess("/char/stdio/subprocess", 0, 0);
106     g_test_trap_assert_passed();
107     g_test_trap_assert_stdout("buf");
108 }
109 #endif
110
111 static void char_ringbuf_test(void)
112 {
113     QemuOpts *opts;
114     Chardev *chr;
115     CharBackend be;
116     char *data;
117     int ret;
118
119     opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label",
120                             1, &error_abort);
121     qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
122
123     qemu_opt_set(opts, "size", "5", &error_abort);
124     chr = qemu_chr_new_from_opts(opts, NULL);
125     g_assert_null(chr);
126     qemu_opts_del(opts);
127
128     opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label",
129                             1, &error_abort);
130     qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
131     qemu_opt_set(opts, "size", "2", &error_abort);
132     chr = qemu_chr_new_from_opts(opts, &error_abort);
133     g_assert_nonnull(chr);
134     qemu_opts_del(opts);
135
136     qemu_chr_fe_init(&be, chr, &error_abort);
137     ret = qemu_chr_fe_write(&be, (void *)"buff", 4);
138     g_assert_cmpint(ret, ==, 4);
139
140     data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort);
141     g_assert_cmpstr(data, ==, "ff");
142     g_free(data);
143
144     data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort);
145     g_assert_cmpstr(data, ==, "");
146     g_free(data);
147
148     qemu_chr_fe_deinit(&be, true);
149
150     /* check alias */
151     opts = qemu_opts_create(qemu_find_opts("chardev"), "memory-label",
152                             1, &error_abort);
153     qemu_opt_set(opts, "backend", "memory", &error_abort);
154     qemu_opt_set(opts, "size", "2", &error_abort);
155     chr = qemu_chr_new_from_opts(opts, NULL);
156     g_assert_nonnull(chr);
157     object_unparent(OBJECT(chr));
158     qemu_opts_del(opts);
159 }
160
161 static void char_mux_test(void)
162 {
163     QemuOpts *opts;
164     Chardev *chr, *base;
165     char *data;
166     FeHandler h1 = { 0, }, h2 = { 0, };
167     CharBackend chr_be1, chr_be2;
168
169     opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label",
170                             1, &error_abort);
171     qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
172     qemu_opt_set(opts, "size", "128", &error_abort);
173     qemu_opt_set(opts, "mux", "on", &error_abort);
174     chr = qemu_chr_new_from_opts(opts, &error_abort);
175     g_assert_nonnull(chr);
176     qemu_opts_del(opts);
177
178     qemu_chr_fe_init(&chr_be1, chr, &error_abort);
179     qemu_chr_fe_set_handlers(&chr_be1,
180                              fe_can_read,
181                              fe_read,
182                              fe_event,
183                              &h1,
184                              NULL, true);
185
186     qemu_chr_fe_init(&chr_be2, chr, &error_abort);
187     qemu_chr_fe_set_handlers(&chr_be2,
188                              fe_can_read,
189                              fe_read,
190                              fe_event,
191                              &h2,
192                              NULL, true);
193     qemu_chr_fe_take_focus(&chr_be2);
194
195     base = qemu_chr_find("mux-label-base");
196     g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0);
197
198     qemu_chr_be_write(base, (void *)"hello", 6);
199     g_assert_cmpint(h1.read_count, ==, 0);
200     g_assert_cmpint(h2.read_count, ==, 6);
201     g_assert_cmpstr(h2.read_buf, ==, "hello");
202     h2.read_count = 0;
203
204     /* switch focus */
205     qemu_chr_be_write(base, (void *)"\1c", 2);
206
207     qemu_chr_be_write(base, (void *)"hello", 6);
208     g_assert_cmpint(h2.read_count, ==, 0);
209     g_assert_cmpint(h1.read_count, ==, 6);
210     g_assert_cmpstr(h1.read_buf, ==, "hello");
211     h1.read_count = 0;
212
213     /* remove first handler */
214     qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, NULL, true);
215     qemu_chr_be_write(base, (void *)"hello", 6);
216     g_assert_cmpint(h1.read_count, ==, 0);
217     g_assert_cmpint(h2.read_count, ==, 0);
218
219     qemu_chr_be_write(base, (void *)"\1c", 2);
220     qemu_chr_be_write(base, (void *)"hello", 6);
221     g_assert_cmpint(h1.read_count, ==, 0);
222     g_assert_cmpint(h2.read_count, ==, 6);
223     g_assert_cmpstr(h2.read_buf, ==, "hello");
224     h2.read_count = 0;
225
226     /* print help */
227     qemu_chr_be_write(base, (void *)"\1?", 2);
228     data = qmp_ringbuf_read("mux-label-base", 128, false, 0, &error_abort);
229     g_assert_cmpint(strlen(data), !=, 0);
230     g_free(data);
231
232     qemu_chr_fe_deinit(&chr_be1, false);
233     qemu_chr_fe_deinit(&chr_be2, true);
234 }
235
236 typedef struct SocketIdleData {
237     GMainLoop *loop;
238     Chardev *chr;
239     bool conn_expected;
240     CharBackend *be;
241     CharBackend *client_be;
242 } SocketIdleData;
243
244 static gboolean char_socket_test_idle(gpointer user_data)
245 {
246     SocketIdleData *data = user_data;
247
248     if (object_property_get_bool(OBJECT(data->chr), "connected", NULL)
249         == data->conn_expected) {
250         quit = true;
251         return FALSE;
252     }
253
254     return TRUE;
255 }
256
257 static void socket_read(void *opaque, const uint8_t *buf, int size)
258 {
259     SocketIdleData *data = opaque;
260
261     g_assert_cmpint(size, ==, 1);
262     g_assert_cmpint(*buf, ==, 'Z');
263
264     size = qemu_chr_fe_write(data->be, (const uint8_t *)"hello", 5);
265     g_assert_cmpint(size, ==, 5);
266 }
267
268 static int socket_can_read(void *opaque)
269 {
270     return 10;
271 }
272
273 static void socket_read_hello(void *opaque, const uint8_t *buf, int size)
274 {
275     g_assert_cmpint(size, ==, 5);
276     g_assert(strncmp((char *)buf, "hello", 5) == 0);
277
278     quit = true;
279 }
280
281 static int socket_can_read_hello(void *opaque)
282 {
283     return 10;
284 }
285
286 static void char_socket_test(void)
287 {
288     Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait");
289     Chardev *chr_client;
290     QObject *addr;
291     QDict *qdict;
292     const char *port;
293     SocketIdleData d = { .chr = chr };
294     CharBackend be;
295     CharBackend client_be;
296     char *tmp;
297
298     d.be = &be;
299     d.client_be = &be;
300
301     g_assert_nonnull(chr);
302     g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort));
303
304     addr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort);
305     qdict = qobject_to_qdict(addr);
306     port = qdict_get_str(qdict, "port");
307     tmp = g_strdup_printf("tcp:127.0.0.1:%s", port);
308     QDECREF(qdict);
309
310     qemu_chr_fe_init(&be, chr, &error_abort);
311     qemu_chr_fe_set_handlers(&be, socket_can_read, socket_read,
312                              NULL, &d, NULL, true);
313
314     chr_client = qemu_chr_new("client", tmp);
315     qemu_chr_fe_init(&client_be, chr_client, &error_abort);
316     qemu_chr_fe_set_handlers(&client_be, socket_can_read_hello,
317                              socket_read_hello,
318                              NULL, &d, NULL, true);
319     g_free(tmp);
320
321     d.conn_expected = true;
322     guint id = g_idle_add(char_socket_test_idle, &d);
323     g_source_set_name_by_id(id, "test-idle");
324     g_assert_cmpint(id, >, 0);
325     main_loop();
326
327     g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort));
328     g_assert(object_property_get_bool(OBJECT(chr_client),
329                                       "connected", &error_abort));
330
331     qemu_chr_write_all(chr_client, (const uint8_t *)"Z", 1);
332     main_loop();
333
334     object_unparent(OBJECT(chr_client));
335
336     d.conn_expected = false;
337     g_idle_add(char_socket_test_idle, &d);
338     main_loop();
339
340     object_unparent(OBJECT(chr));
341 }
342
343 #ifndef _WIN32
344 static void char_pipe_test(void)
345 {
346     gchar *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL);
347     gchar *tmp, *in, *out, *pipe = g_build_filename(tmp_path, "pipe", NULL);
348     Chardev *chr;
349     CharBackend be;
350     int ret, fd;
351     char buf[10];
352     FeHandler fe = { 0, };
353
354     in = g_strdup_printf("%s.in", pipe);
355     if (mkfifo(in, 0600) < 0) {
356         abort();
357     }
358     out = g_strdup_printf("%s.out", pipe);
359     if (mkfifo(out, 0600) < 0) {
360         abort();
361     }
362
363     tmp = g_strdup_printf("pipe:%s", pipe);
364     chr = qemu_chr_new("pipe", tmp);
365     g_assert_nonnull(chr);
366     g_free(tmp);
367
368     qemu_chr_fe_init(&be, chr, &error_abort);
369
370     ret = qemu_chr_fe_write(&be, (void *)"pipe-out", 9);
371     g_assert_cmpint(ret, ==, 9);
372
373     fd = open(out, O_RDWR);
374     ret = read(fd, buf, sizeof(buf));
375     g_assert_cmpint(ret, ==, 9);
376     g_assert_cmpstr(buf, ==, "pipe-out");
377     close(fd);
378
379     fd = open(in, O_WRONLY);
380     ret = write(fd, "pipe-in", 8);
381     g_assert_cmpint(ret, ==, 8);
382     close(fd);
383
384     qemu_chr_fe_set_handlers(&be,
385                              fe_can_read,
386                              fe_read,
387                              fe_event,
388                              &fe,
389                              NULL, true);
390
391     main_loop();
392
393     g_assert_cmpint(fe.read_count, ==, 8);
394     g_assert_cmpstr(fe.read_buf, ==, "pipe-in");
395
396     qemu_chr_fe_deinit(&be, true);
397
398     g_assert(g_unlink(in) == 0);
399     g_assert(g_unlink(out) == 0);
400     g_assert(g_rmdir(tmp_path) == 0);
401     g_free(in);
402     g_free(out);
403     g_free(tmp_path);
404     g_free(pipe);
405 }
406 #endif
407
408 static void char_udp_test(void)
409 {
410     struct sockaddr_in addr = { 0, }, other;
411     SocketIdleData d = { 0, };
412     Chardev *chr;
413     CharBackend be;
414     socklen_t alen = sizeof(addr);
415     int ret, sock = qemu_socket(PF_INET, SOCK_DGRAM, 0);
416     char buf[10];
417     char *tmp;
418
419     g_assert_cmpint(sock, >, 0);
420     addr.sin_family = AF_INET ;
421     addr.sin_addr.s_addr = htonl(INADDR_ANY);
422     addr.sin_port = 0;
423     ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
424     g_assert_cmpint(ret, ==, 0);
425     ret = getsockname(sock, (struct sockaddr *)&addr, &alen);
426     g_assert_cmpint(ret, ==, 0);
427
428     tmp = g_strdup_printf("udp:127.0.0.1:%d",
429                           ntohs(addr.sin_port));
430     chr = qemu_chr_new("client", tmp);
431     g_assert_nonnull(chr);
432
433     d.chr = chr;
434     qemu_chr_fe_init(&be, chr, &error_abort);
435     qemu_chr_fe_set_handlers(&be, socket_can_read_hello, socket_read_hello,
436                              NULL, &d, NULL, true);
437     ret = qemu_chr_write_all(chr, (uint8_t *)"hello", 5);
438     g_assert_cmpint(ret, ==, 5);
439
440     alen = sizeof(addr);
441     ret = recvfrom(sock, buf, sizeof(buf), 0,
442                    (struct sockaddr *)&other, &alen);
443     g_assert_cmpint(ret, ==, 5);
444     ret = sendto(sock, buf, 5, 0, (struct sockaddr *)&other, alen);
445     g_assert_cmpint(ret, ==, 5);
446
447     main_loop();
448
449     close(sock);
450     g_free(tmp);
451 }
452
453 #ifdef HAVE_CHARDEV_SERIAL
454 static void char_serial_test(void)
455 {
456     QemuOpts *opts;
457     Chardev *chr;
458
459     opts = qemu_opts_create(qemu_find_opts("chardev"), "serial-id",
460                             1, &error_abort);
461     qemu_opt_set(opts, "backend", "serial", &error_abort);
462     qemu_opt_set(opts, "path", "/dev/null", &error_abort);
463
464     chr = qemu_chr_new_from_opts(opts, NULL);
465     g_assert_nonnull(chr);
466     /* TODO: add more tests with a pty */
467     object_unparent(OBJECT(chr));
468
469     /* test tty alias */
470     qemu_opt_set(opts, "backend", "tty", &error_abort);
471     chr = qemu_chr_new_from_opts(opts, NULL);
472     g_assert_nonnull(chr);
473     object_unparent(OBJECT(chr));
474
475     qemu_opts_del(opts);
476 }
477 #endif
478
479 static void char_file_test(void)
480 {
481     char *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL);
482     char *out = g_build_filename(tmp_path, "out", NULL);
483     char *contents = NULL;
484     ChardevFile file = { .out = out };
485     ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE,
486                                .u.file.data = &file };
487     Chardev *chr;
488     gsize length;
489     int ret;
490
491     chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend,
492                            &error_abort);
493     ret = qemu_chr_write_all(chr, (uint8_t *)"hello!", 6);
494     g_assert_cmpint(ret, ==, 6);
495     object_unref(OBJECT(chr));
496
497     ret = g_file_get_contents(out, &contents, &length, NULL);
498     g_assert(ret == TRUE);
499     g_assert_cmpint(length, ==, 6);
500     g_assert(strncmp(contents, "hello!", 6) == 0);
501     g_free(contents);
502
503 #ifndef _WIN32
504     {
505         CharBackend be;
506         FeHandler fe = { 0, };
507         char *fifo = g_build_filename(tmp_path, "fifo", NULL);
508         int fd;
509
510         if (mkfifo(fifo, 0600) < 0) {
511             abort();
512         }
513
514         fd = open(fifo, O_RDWR);
515         ret = write(fd, "fifo-in", 8);
516         g_assert_cmpint(ret, ==, 8);
517
518         file.in = fifo;
519         file.has_in = true;
520         chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend,
521                                &error_abort);
522
523         qemu_chr_fe_init(&be, chr, &error_abort);
524         qemu_chr_fe_set_handlers(&be,
525                                  fe_can_read,
526                                  fe_read,
527                                  fe_event,
528                                  &fe, NULL, true);
529
530         main_loop();
531
532         close(fd);
533
534         g_assert_cmpint(fe.read_count, ==, 8);
535         g_assert_cmpstr(fe.read_buf, ==, "fifo-in");
536         qemu_chr_fe_deinit(&be, true);
537         g_unlink(fifo);
538         g_free(fifo);
539     }
540 #endif
541
542     g_unlink(out);
543     g_rmdir(tmp_path);
544     g_free(tmp_path);
545     g_free(out);
546 }
547
548 static void char_null_test(void)
549 {
550     Error *err = NULL;
551     Chardev *chr;
552     CharBackend be;
553     int ret;
554
555     chr = qemu_chr_find("label-null");
556     g_assert_null(chr);
557
558     chr = qemu_chr_new("label-null", "null");
559     chr = qemu_chr_find("label-null");
560     g_assert_nonnull(chr);
561
562     g_assert(qemu_chr_has_feature(chr,
563                  QEMU_CHAR_FEATURE_FD_PASS) == false);
564     g_assert(qemu_chr_has_feature(chr,
565                  QEMU_CHAR_FEATURE_RECONNECTABLE) == false);
566
567     /* check max avail */
568     qemu_chr_fe_init(&be, chr, &error_abort);
569     qemu_chr_fe_init(&be, chr, &err);
570     error_free_or_abort(&err);
571
572     /* deinit & reinit */
573     qemu_chr_fe_deinit(&be, false);
574     qemu_chr_fe_init(&be, chr, &error_abort);
575
576     qemu_chr_fe_set_open(&be, true);
577
578     qemu_chr_fe_set_handlers(&be,
579                              fe_can_read,
580                              fe_read,
581                              fe_event,
582                              NULL, NULL, true);
583
584     ret = qemu_chr_fe_write(&be, (void *)"buf", 4);
585     g_assert_cmpint(ret, ==, 4);
586
587     qemu_chr_fe_deinit(&be, true);
588 }
589
590 static void char_invalid_test(void)
591 {
592     Chardev *chr;
593
594     chr = qemu_chr_new("label-invalid", "invalid");
595     g_assert_null(chr);
596 }
597
598 int main(int argc, char **argv)
599 {
600     qemu_init_main_loop(&error_abort);
601     socket_init();
602
603     g_test_init(&argc, &argv, NULL);
604
605     module_call_init(MODULE_INIT_QOM);
606     qemu_add_opts(&qemu_chardev_opts);
607
608     g_test_add_func("/char/null", char_null_test);
609     g_test_add_func("/char/invalid", char_invalid_test);
610     g_test_add_func("/char/ringbuf", char_ringbuf_test);
611     g_test_add_func("/char/mux", char_mux_test);
612 #ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
613 #ifdef _WIN32
614     g_test_add_func("/char/console/subprocess", char_console_test_subprocess);
615     g_test_add_func("/char/console", char_console_test);
616 #endif
617     g_test_add_func("/char/stdio/subprocess", char_stdio_test_subprocess);
618     g_test_add_func("/char/stdio", char_stdio_test);
619 #endif
620 #ifndef _WIN32
621     g_test_add_func("/char/pipe", char_pipe_test);
622 #endif
623     g_test_add_func("/char/file", char_file_test);
624     g_test_add_func("/char/socket", char_socket_test);
625     g_test_add_func("/char/udp", char_udp_test);
626 #ifdef HAVE_CHARDEV_SERIAL
627     g_test_add_func("/char/serial", char_serial_test);
628 #endif
629
630     return g_test_run();
631 }
This page took 0.057865 seconds and 4 git commands to generate.