1d3a0bb77SLukas Straub /*
2d3a0bb77SLukas Straub * Tests for QEMU yank feature
3d3a0bb77SLukas Straub *
4d3a0bb77SLukas Straub * Copyright (c) Lukas Straub <lukasstraub2@web.de>
5d3a0bb77SLukas Straub *
6d3a0bb77SLukas Straub * This work is licensed under the terms of the GNU GPL, version 2 or later.
7d3a0bb77SLukas Straub * See the COPYING file in the top-level directory.
8d3a0bb77SLukas Straub */
9d3a0bb77SLukas Straub
10d3a0bb77SLukas Straub #include "qemu/osdep.h"
11d3a0bb77SLukas Straub #include <glib/gstdio.h>
12d3a0bb77SLukas Straub
13d3a0bb77SLukas Straub #include "qemu/config-file.h"
14d3a0bb77SLukas Straub #include "qemu/module.h"
15d3a0bb77SLukas Straub #include "qemu/option.h"
16d3a0bb77SLukas Straub #include "chardev/char-fe.h"
17d3a0bb77SLukas Straub #include "sysemu/sysemu.h"
18d3a0bb77SLukas Straub #include "qapi/error.h"
19d3a0bb77SLukas Straub #include "qapi/qapi-commands-char.h"
20d3a0bb77SLukas Straub #include "qapi/qapi-types-char.h"
21d3a0bb77SLukas Straub #include "qapi/qapi-commands-yank.h"
22d3a0bb77SLukas Straub #include "qapi/qapi-types-yank.h"
23d3a0bb77SLukas Straub #include "io/channel-socket.h"
24d3a0bb77SLukas Straub #include "socket-helpers.h"
25d3a0bb77SLukas Straub
26d3a0bb77SLukas Straub typedef struct {
27d3a0bb77SLukas Straub SocketAddress *addr;
28d3a0bb77SLukas Straub bool old_yank;
29d3a0bb77SLukas Straub bool new_yank;
30d3a0bb77SLukas Straub bool fail;
31d3a0bb77SLukas Straub } CharChangeTestConfig;
32d3a0bb77SLukas Straub
chardev_change(void * opaque)33d3a0bb77SLukas Straub static int chardev_change(void *opaque)
34d3a0bb77SLukas Straub {
35d3a0bb77SLukas Straub return 0;
36d3a0bb77SLukas Straub }
37d3a0bb77SLukas Straub
is_yank_instance_registered(void)38d3a0bb77SLukas Straub static bool is_yank_instance_registered(void)
39d3a0bb77SLukas Straub {
40d3a0bb77SLukas Straub YankInstanceList *list;
41d3a0bb77SLukas Straub bool ret;
42d3a0bb77SLukas Straub
43d3a0bb77SLukas Straub list = qmp_query_yank(&error_abort);
44d3a0bb77SLukas Straub
45d3a0bb77SLukas Straub ret = !!list;
46d3a0bb77SLukas Straub
47d3a0bb77SLukas Straub qapi_free_YankInstanceList(list);
48d3a0bb77SLukas Straub
49d3a0bb77SLukas Straub return ret;
50d3a0bb77SLukas Straub }
51d3a0bb77SLukas Straub
accept_thread(gpointer data)52d3a0bb77SLukas Straub static gpointer accept_thread(gpointer data)
53d3a0bb77SLukas Straub {
54d3a0bb77SLukas Straub QIOChannelSocket *ioc = data;
55d3a0bb77SLukas Straub QIOChannelSocket *cioc;
56d3a0bb77SLukas Straub
57d3a0bb77SLukas Straub cioc = qio_channel_socket_accept(ioc, &error_abort);
58d3a0bb77SLukas Straub object_unref(OBJECT(cioc));
59d3a0bb77SLukas Straub
60d3a0bb77SLukas Straub return NULL;
61d3a0bb77SLukas Straub }
62d3a0bb77SLukas Straub
char_change_test(gconstpointer opaque)63d3a0bb77SLukas Straub static void char_change_test(gconstpointer opaque)
64d3a0bb77SLukas Straub {
65d3a0bb77SLukas Straub CharChangeTestConfig *conf = (gpointer) opaque;
66d3a0bb77SLukas Straub SocketAddress *addr;
67d3a0bb77SLukas Straub Chardev *chr;
68d3a0bb77SLukas Straub CharBackend be;
69d3a0bb77SLukas Straub ChardevReturn *ret;
70d3a0bb77SLukas Straub QIOChannelSocket *ioc;
71d3a0bb77SLukas Straub QemuThread thread;
72d3a0bb77SLukas Straub
73d3a0bb77SLukas Straub /*
74d3a0bb77SLukas Straub * Setup a listener socket and determine its address
75d3a0bb77SLukas Straub * so we know the TCP port for the client later
76d3a0bb77SLukas Straub */
77d3a0bb77SLukas Straub ioc = qio_channel_socket_new();
78d3a0bb77SLukas Straub g_assert_nonnull(ioc);
79d3a0bb77SLukas Straub qio_channel_socket_listen_sync(ioc, conf->addr, 1, &error_abort);
80d3a0bb77SLukas Straub addr = qio_channel_socket_get_local_address(ioc, &error_abort);
81d3a0bb77SLukas Straub g_assert_nonnull(addr);
82d3a0bb77SLukas Straub
83d3a0bb77SLukas Straub ChardevBackend backend[2] = {
84d3a0bb77SLukas Straub /* doesn't support yank */
85d3a0bb77SLukas Straub { .type = CHARDEV_BACKEND_KIND_NULL },
86d3a0bb77SLukas Straub /* supports yank */
87d3a0bb77SLukas Straub {
88d3a0bb77SLukas Straub .type = CHARDEV_BACKEND_KIND_SOCKET,
89d3a0bb77SLukas Straub .u.socket.data = &(ChardevSocket) {
90d3a0bb77SLukas Straub .addr = &(SocketAddressLegacy) {
91*935a867cSMarkus Armbruster .type = SOCKET_ADDRESS_TYPE_INET,
92d3a0bb77SLukas Straub .u.inet.data = &addr->u.inet
93d3a0bb77SLukas Straub },
94d3a0bb77SLukas Straub .has_server = true,
95d3a0bb77SLukas Straub .server = false
96d3a0bb77SLukas Straub }
97d3a0bb77SLukas Straub } };
98d3a0bb77SLukas Straub
99d3a0bb77SLukas Straub ChardevBackend fail_backend[2] = {
100d3a0bb77SLukas Straub /* doesn't support yank */
101d3a0bb77SLukas Straub {
102d3a0bb77SLukas Straub .type = CHARDEV_BACKEND_KIND_UDP,
103d3a0bb77SLukas Straub .u.udp.data = &(ChardevUdp) {
104d3a0bb77SLukas Straub .remote = &(SocketAddressLegacy) {
105*935a867cSMarkus Armbruster .type = SOCKET_ADDRESS_TYPE_UNIX,
106d3a0bb77SLukas Straub .u.q_unix.data = &(UnixSocketAddress) {
107d3a0bb77SLukas Straub .path = (char *)""
108d3a0bb77SLukas Straub }
109d3a0bb77SLukas Straub }
110d3a0bb77SLukas Straub }
111d3a0bb77SLukas Straub },
112d3a0bb77SLukas Straub /* supports yank */
113d3a0bb77SLukas Straub {
114d3a0bb77SLukas Straub .type = CHARDEV_BACKEND_KIND_SOCKET,
115d3a0bb77SLukas Straub .u.socket.data = &(ChardevSocket) {
116d3a0bb77SLukas Straub .addr = &(SocketAddressLegacy) {
117*935a867cSMarkus Armbruster .type = SOCKET_ADDRESS_TYPE_INET,
118d3a0bb77SLukas Straub .u.inet.data = &(InetSocketAddress) {
119d3a0bb77SLukas Straub .host = (char *)"127.0.0.1",
120d3a0bb77SLukas Straub .port = (char *)"0"
121d3a0bb77SLukas Straub }
122d3a0bb77SLukas Straub },
123d3a0bb77SLukas Straub .has_server = true,
124d3a0bb77SLukas Straub .server = false
125d3a0bb77SLukas Straub }
126d3a0bb77SLukas Straub } };
127d3a0bb77SLukas Straub
128d3a0bb77SLukas Straub g_assert(!is_yank_instance_registered());
129d3a0bb77SLukas Straub
130d3a0bb77SLukas Straub if (conf->old_yank) {
131d3a0bb77SLukas Straub qemu_thread_create(&thread, "accept", accept_thread,
132d3a0bb77SLukas Straub ioc, QEMU_THREAD_JOINABLE);
133d3a0bb77SLukas Straub }
134d3a0bb77SLukas Straub
135d3a0bb77SLukas Straub ret = qmp_chardev_add("chardev", &backend[conf->old_yank], &error_abort);
136d3a0bb77SLukas Straub qapi_free_ChardevReturn(ret);
137d3a0bb77SLukas Straub chr = qemu_chr_find("chardev");
138d3a0bb77SLukas Straub g_assert_nonnull(chr);
139d3a0bb77SLukas Straub
140d3a0bb77SLukas Straub g_assert(is_yank_instance_registered() == conf->old_yank);
141d3a0bb77SLukas Straub
142d3a0bb77SLukas Straub qemu_chr_wait_connected(chr, &error_abort);
143d3a0bb77SLukas Straub if (conf->old_yank) {
144d3a0bb77SLukas Straub qemu_thread_join(&thread);
145d3a0bb77SLukas Straub }
146d3a0bb77SLukas Straub
147d3a0bb77SLukas Straub qemu_chr_fe_init(&be, chr, &error_abort);
148d3a0bb77SLukas Straub /* allow chardev-change */
149d3a0bb77SLukas Straub qemu_chr_fe_set_handlers(&be, NULL, NULL,
150d3a0bb77SLukas Straub NULL, chardev_change, NULL, NULL, true);
151d3a0bb77SLukas Straub
152d3a0bb77SLukas Straub if (conf->fail) {
153d3a0bb77SLukas Straub g_setenv("QTEST_SILENT_ERRORS", "1", 1);
154d3a0bb77SLukas Straub ret = qmp_chardev_change("chardev", &fail_backend[conf->new_yank],
155d3a0bb77SLukas Straub NULL);
156d3a0bb77SLukas Straub g_assert_null(ret);
157d3a0bb77SLukas Straub g_assert(be.chr == chr);
158d3a0bb77SLukas Straub g_assert(is_yank_instance_registered() == conf->old_yank);
159d3a0bb77SLukas Straub g_unsetenv("QTEST_SILENT_ERRORS");
160d3a0bb77SLukas Straub } else {
161d3a0bb77SLukas Straub if (conf->new_yank) {
162d3a0bb77SLukas Straub qemu_thread_create(&thread, "accept", accept_thread,
163d3a0bb77SLukas Straub ioc, QEMU_THREAD_JOINABLE);
164d3a0bb77SLukas Straub }
165d3a0bb77SLukas Straub ret = qmp_chardev_change("chardev", &backend[conf->new_yank],
166d3a0bb77SLukas Straub &error_abort);
167d3a0bb77SLukas Straub if (conf->new_yank) {
168d3a0bb77SLukas Straub qemu_thread_join(&thread);
169d3a0bb77SLukas Straub }
170d3a0bb77SLukas Straub g_assert_nonnull(ret);
171d3a0bb77SLukas Straub g_assert(be.chr != chr);
172d3a0bb77SLukas Straub g_assert(is_yank_instance_registered() == conf->new_yank);
173d3a0bb77SLukas Straub }
174d3a0bb77SLukas Straub
175d3a0bb77SLukas Straub object_unparent(OBJECT(be.chr));
176d3a0bb77SLukas Straub object_unref(OBJECT(ioc));
177d3a0bb77SLukas Straub qapi_free_ChardevReturn(ret);
178d3a0bb77SLukas Straub qapi_free_SocketAddress(addr);
179d3a0bb77SLukas Straub }
180d3a0bb77SLukas Straub
181d3a0bb77SLukas Straub static SocketAddress tcpaddr = {
182d3a0bb77SLukas Straub .type = SOCKET_ADDRESS_TYPE_INET,
183d3a0bb77SLukas Straub .u.inet.host = (char *)"127.0.0.1",
184d3a0bb77SLukas Straub .u.inet.port = (char *)"0",
185d3a0bb77SLukas Straub };
186d3a0bb77SLukas Straub
main(int argc,char ** argv)187d3a0bb77SLukas Straub int main(int argc, char **argv)
188d3a0bb77SLukas Straub {
189d3a0bb77SLukas Straub bool has_ipv4, has_ipv6;
190d3a0bb77SLukas Straub
191d3a0bb77SLukas Straub qemu_init_main_loop(&error_abort);
192d3a0bb77SLukas Straub socket_init();
193d3a0bb77SLukas Straub
194d3a0bb77SLukas Straub g_test_init(&argc, &argv, NULL);
195d3a0bb77SLukas Straub
196d3a0bb77SLukas Straub if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
197d3a0bb77SLukas Straub g_printerr("socket_check_protocol_support() failed\n");
198d3a0bb77SLukas Straub goto end;
199d3a0bb77SLukas Straub }
200d3a0bb77SLukas Straub
201d3a0bb77SLukas Straub if (!has_ipv4) {
202d3a0bb77SLukas Straub goto end;
203d3a0bb77SLukas Straub }
204d3a0bb77SLukas Straub
205d3a0bb77SLukas Straub module_call_init(MODULE_INIT_QOM);
206d3a0bb77SLukas Straub qemu_add_opts(&qemu_chardev_opts);
207d3a0bb77SLukas Straub
208d3a0bb77SLukas Straub g_test_add_data_func("/yank/char_change/success/to_yank",
209d3a0bb77SLukas Straub &(CharChangeTestConfig) { .addr = &tcpaddr,
210d3a0bb77SLukas Straub .old_yank = false,
211d3a0bb77SLukas Straub .new_yank = true,
212d3a0bb77SLukas Straub .fail = false },
213d3a0bb77SLukas Straub char_change_test);
214d3a0bb77SLukas Straub g_test_add_data_func("/yank/char_change/fail/to_yank",
215d3a0bb77SLukas Straub &(CharChangeTestConfig) { .addr = &tcpaddr,
216d3a0bb77SLukas Straub .old_yank = false,
217d3a0bb77SLukas Straub .new_yank = true,
218d3a0bb77SLukas Straub .fail = true },
219d3a0bb77SLukas Straub char_change_test);
220d3a0bb77SLukas Straub
221d3a0bb77SLukas Straub g_test_add_data_func("/yank/char_change/success/yank_to_yank",
222d3a0bb77SLukas Straub &(CharChangeTestConfig) { .addr = &tcpaddr,
223d3a0bb77SLukas Straub .old_yank = true,
224d3a0bb77SLukas Straub .new_yank = true,
225d3a0bb77SLukas Straub .fail = false },
226d3a0bb77SLukas Straub char_change_test);
227d3a0bb77SLukas Straub g_test_add_data_func("/yank/char_change/fail/yank_to_yank",
228d3a0bb77SLukas Straub &(CharChangeTestConfig) { .addr = &tcpaddr,
229d3a0bb77SLukas Straub .old_yank = true,
230d3a0bb77SLukas Straub .new_yank = true,
231d3a0bb77SLukas Straub .fail = true },
232d3a0bb77SLukas Straub char_change_test);
233d3a0bb77SLukas Straub
234d3a0bb77SLukas Straub g_test_add_data_func("/yank/char_change/success/from_yank",
235d3a0bb77SLukas Straub &(CharChangeTestConfig) { .addr = &tcpaddr,
236d3a0bb77SLukas Straub .old_yank = true,
237d3a0bb77SLukas Straub .new_yank = false,
238d3a0bb77SLukas Straub .fail = false },
239d3a0bb77SLukas Straub char_change_test);
240d3a0bb77SLukas Straub g_test_add_data_func("/yank/char_change/fail/from_yank",
241d3a0bb77SLukas Straub &(CharChangeTestConfig) { .addr = &tcpaddr,
242d3a0bb77SLukas Straub .old_yank = true,
243d3a0bb77SLukas Straub .new_yank = false,
244d3a0bb77SLukas Straub .fail = true },
245d3a0bb77SLukas Straub char_change_test);
246d3a0bb77SLukas Straub
247d3a0bb77SLukas Straub end:
248d3a0bb77SLukas Straub return g_test_run();
249d3a0bb77SLukas Straub }
250