1 #include "qemu/osdep.h"
2 #include "qemu/sockets.h"
3 #include "qemu/dbus.h"
4 #include "qemu/sockets.h"
5 #include <gio/gio.h>
6 #include <gio/gunixfdlist.h>
7 #include "libqtest.h"
8 #include "ui/dbus-display1.h"
9 
10 static GDBusConnection*
11 test_dbus_p2p_from_fd(int fd)
12 {
13     g_autoptr(GError) err = NULL;
14     g_autoptr(GSocket) socket = NULL;
15     g_autoptr(GSocketConnection) socketc = NULL;
16     GDBusConnection *conn;
17 
18 #ifdef WIN32
19     socket = g_socket_new_from_fd(_get_osfhandle(fd), &err);
20 #else
21     socket = g_socket_new_from_fd(fd, &err);
22 #endif
23     g_assert_no_error(err);
24 
25     socketc = g_socket_connection_factory_create_connection(socket);
26     g_assert(socketc != NULL);
27 
28     conn = g_dbus_connection_new_sync(
29         G_IO_STREAM(socketc), NULL,
30         G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
31         G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING,
32         NULL, NULL, &err);
33     g_assert_no_error(err);
34 
35     return conn;
36 }
37 
38 static void
39 test_setup(QTestState **qts, GDBusConnection **conn)
40 {
41     int pair[2];
42 
43     *qts = qtest_init("-display dbus,p2p=yes -name dbus-test");
44 
45     g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
46 
47     qtest_qmp_add_client(*qts, "@dbus-display", pair[1]);
48 
49     *conn = test_dbus_p2p_from_fd(pair[0]);
50     g_dbus_connection_start_message_processing(*conn);
51 }
52 
53 static void
54 test_dbus_display_vm(void)
55 {
56     g_autoptr(GError) err = NULL;
57     g_autoptr(GDBusConnection) conn = NULL;
58     g_autoptr(QemuDBusDisplay1VMProxy) vm = NULL;
59     QTestState *qts = NULL;
60 
61     test_setup(&qts, &conn);
62 
63     vm = QEMU_DBUS_DISPLAY1_VM_PROXY(
64         qemu_dbus_display1_vm_proxy_new_sync(
65             conn,
66             G_DBUS_PROXY_FLAGS_NONE,
67             NULL,
68             DBUS_DISPLAY1_ROOT "/VM",
69             NULL,
70             &err));
71     g_assert_no_error(err);
72 
73     g_assert_cmpstr(
74         qemu_dbus_display1_vm_get_name(QEMU_DBUS_DISPLAY1_VM(vm)),
75         ==,
76         "dbus-test");
77     qtest_quit(qts);
78 }
79 
80 typedef struct TestDBusConsoleRegister {
81     GMainLoop *loop;
82     GThread *thread;
83     GDBusConnection *listener_conn;
84     GDBusObjectManagerServer *server;
85 } TestDBusConsoleRegister;
86 
87 static gboolean listener_handle_scanout(
88     QemuDBusDisplay1Listener *object,
89     GDBusMethodInvocation *invocation,
90     guint arg_width,
91     guint arg_height,
92     guint arg_stride,
93     guint arg_pixman_format,
94     GVariant *arg_data,
95     TestDBusConsoleRegister *test)
96 {
97     g_main_loop_quit(test->loop);
98 
99     return DBUS_METHOD_INVOCATION_HANDLED;
100 }
101 
102 static void
103 test_dbus_console_setup_listener(TestDBusConsoleRegister *test)
104 {
105     g_autoptr(GDBusObjectSkeleton) listener = NULL;
106     g_autoptr(QemuDBusDisplay1ListenerSkeleton) iface = NULL;
107 
108     test->server = g_dbus_object_manager_server_new(DBUS_DISPLAY1_ROOT);
109     listener = g_dbus_object_skeleton_new(DBUS_DISPLAY1_ROOT "/Listener");
110     iface = QEMU_DBUS_DISPLAY1_LISTENER_SKELETON(
111         qemu_dbus_display1_listener_skeleton_new());
112     g_object_connect(iface,
113                      "signal::handle-scanout", listener_handle_scanout, test,
114                      NULL);
115     g_dbus_object_skeleton_add_interface(listener,
116                                          G_DBUS_INTERFACE_SKELETON(iface));
117     g_dbus_object_manager_server_export(test->server, listener);
118     g_dbus_object_manager_server_set_connection(test->server,
119                                                 test->listener_conn);
120 
121     g_dbus_connection_start_message_processing(test->listener_conn);
122 }
123 
124 static void
125 test_dbus_console_registered(GObject *source_object,
126                              GAsyncResult *res,
127                              gpointer user_data)
128 {
129     TestDBusConsoleRegister *test = user_data;
130     g_autoptr(GError) err = NULL;
131 
132     qemu_dbus_display1_console_call_register_listener_finish(
133         QEMU_DBUS_DISPLAY1_CONSOLE(source_object),
134 #ifndef WIN32
135         NULL,
136 #endif
137         res, &err);
138     g_assert_no_error(err);
139 
140     test->listener_conn = g_thread_join(test->thread);
141     test_dbus_console_setup_listener(test);
142 }
143 
144 static gpointer
145 test_dbus_p2p_server_setup_thread(gpointer data)
146 {
147     return test_dbus_p2p_from_fd(GPOINTER_TO_INT(data));
148 }
149 
150 static void
151 test_dbus_display_console(void)
152 {
153     g_autoptr(GError) err = NULL;
154     g_autoptr(GDBusConnection) conn = NULL;
155     g_autoptr(QemuDBusDisplay1ConsoleProxy) console = NULL;
156     g_autoptr(GMainLoop) loop = NULL;
157     QTestState *qts = NULL;
158     int pair[2];
159     TestDBusConsoleRegister test;
160 #ifdef WIN32
161     WSAPROTOCOL_INFOW info;
162     g_autoptr(GVariant) listener = NULL;
163 #else
164     g_autoptr(GUnixFDList) fd_list = NULL;
165     int idx;
166 #endif
167 
168     test_setup(&qts, &conn);
169 
170     g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
171 #ifndef WIN32
172     fd_list = g_unix_fd_list_new();
173     idx = g_unix_fd_list_append(fd_list, pair[1], NULL);
174 #endif
175 
176     console = QEMU_DBUS_DISPLAY1_CONSOLE_PROXY(
177         qemu_dbus_display1_console_proxy_new_sync(
178             conn,
179             G_DBUS_PROXY_FLAGS_NONE,
180             NULL,
181             "/org/qemu/Display1/Console_0",
182             NULL,
183             &err));
184     g_assert_no_error(err);
185 
186     test.loop = loop = g_main_loop_new(NULL, FALSE);
187     test.thread = g_thread_new(NULL, test_dbus_p2p_server_setup_thread,
188                                GINT_TO_POINTER(pair[0]));
189 
190 #ifdef WIN32
191     if (WSADuplicateSocketW(_get_osfhandle(pair[1]),
192                             GetProcessId((HANDLE) qtest_pid(qts)),
193                             &info) == SOCKET_ERROR)
194     {
195         g_autofree char *emsg = g_win32_error_message(WSAGetLastError());
196         g_error("WSADuplicateSocket failed: %s", emsg);
197     }
198     close(pair[1]);
199     listener = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
200                                          &info,
201                                          sizeof(info),
202                                          1);
203 #endif
204 
205     qemu_dbus_display1_console_call_register_listener(
206         QEMU_DBUS_DISPLAY1_CONSOLE(console),
207 #ifdef WIN32
208         listener,
209 #else
210         g_variant_new_handle(idx),
211 #endif
212         G_DBUS_CALL_FLAGS_NONE,
213         -1,
214 #ifndef WIN32
215         fd_list,
216 #endif
217         NULL,
218         test_dbus_console_registered,
219         &test);
220 
221     g_main_loop_run(loop);
222 
223     g_clear_object(&test.server);
224     g_clear_object(&test.listener_conn);
225     qtest_quit(qts);
226 }
227 
228 static void
229 test_dbus_display_keyboard(void)
230 {
231     g_autoptr(GError) err = NULL;
232     g_autoptr(GDBusConnection) conn = NULL;
233     g_autoptr(QemuDBusDisplay1KeyboardProxy) keyboard = NULL;
234     QTestState *qts = NULL;
235 
236     test_setup(&qts, &conn);
237 
238     keyboard = QEMU_DBUS_DISPLAY1_KEYBOARD_PROXY(
239         qemu_dbus_display1_keyboard_proxy_new_sync(
240             conn,
241             G_DBUS_PROXY_FLAGS_NONE,
242             NULL,
243             "/org/qemu/Display1/Console_0",
244             NULL,
245             &err));
246     g_assert_no_error(err);
247 
248 
249     g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 0);
250     g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0);
251 
252     qemu_dbus_display1_keyboard_call_press_sync(
253         QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard),
254         0x1C, /* qnum enter */
255         G_DBUS_CALL_FLAGS_NONE,
256         -1,
257         NULL,
258         &err);
259     g_assert_no_error(err);
260 
261     /* may be should wait for interrupt? */
262     g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 1);
263     g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0x5A); /* scan code 2 enter */
264 
265     qemu_dbus_display1_keyboard_call_release_sync(
266         QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard),
267         0x1C, /* qnum enter */
268         G_DBUS_CALL_FLAGS_NONE,
269         -1,
270         NULL,
271         &err);
272     g_assert_no_error(err);
273 
274     g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 1);
275     g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0xF0); /* scan code 2 release */
276     g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0x5A); /* scan code 2 enter */
277 
278     g_assert_cmpint(qemu_dbus_display1_keyboard_get_modifiers(
279                         QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard)), ==, 0);
280 
281     qtest_quit(qts);
282 }
283 
284 int
285 main(int argc, char **argv)
286 {
287     g_test_init(&argc, &argv, NULL);
288 
289     qtest_add_func("/dbus-display/vm", test_dbus_display_vm);
290     qtest_add_func("/dbus-display/console", test_dbus_display_console);
291     qtest_add_func("/dbus-display/keyboard", test_dbus_display_keyboard);
292 
293     return g_test_run();
294 }
295