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 139 if (g_error_matches(err, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { 140 g_test_skip("The VM doesn't have a console!"); 141 g_main_loop_quit(test->loop); 142 return; 143 } 144 145 g_assert_no_error(err); 146 147 test->listener_conn = g_thread_join(test->thread); 148 test_dbus_console_setup_listener(test); 149 } 150 151 static gpointer 152 test_dbus_p2p_server_setup_thread(gpointer data) 153 { 154 return test_dbus_p2p_from_fd(GPOINTER_TO_INT(data)); 155 } 156 157 static void 158 test_dbus_display_console(void) 159 { 160 g_autoptr(GError) err = NULL; 161 g_autoptr(GDBusConnection) conn = NULL; 162 g_autoptr(QemuDBusDisplay1ConsoleProxy) console = NULL; 163 g_autoptr(GMainLoop) loop = NULL; 164 QTestState *qts = NULL; 165 int pair[2]; 166 TestDBusConsoleRegister test = { 0, }; 167 #ifdef WIN32 168 WSAPROTOCOL_INFOW info; 169 g_autoptr(GVariant) listener = NULL; 170 #else 171 g_autoptr(GUnixFDList) fd_list = NULL; 172 int idx; 173 #endif 174 175 test_setup(&qts, &conn); 176 177 g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0); 178 #ifndef WIN32 179 fd_list = g_unix_fd_list_new(); 180 idx = g_unix_fd_list_append(fd_list, pair[1], NULL); 181 #endif 182 183 console = QEMU_DBUS_DISPLAY1_CONSOLE_PROXY( 184 qemu_dbus_display1_console_proxy_new_sync( 185 conn, 186 G_DBUS_PROXY_FLAGS_NONE, 187 NULL, 188 "/org/qemu/Display1/Console_0", 189 NULL, 190 &err)); 191 g_assert_no_error(err); 192 193 test.loop = loop = g_main_loop_new(NULL, FALSE); 194 test.thread = g_thread_new(NULL, test_dbus_p2p_server_setup_thread, 195 GINT_TO_POINTER(pair[0])); 196 197 #ifdef WIN32 198 if (WSADuplicateSocketW(_get_osfhandle(pair[1]), 199 GetProcessId((HANDLE) qtest_pid(qts)), 200 &info) == SOCKET_ERROR) 201 { 202 g_autofree char *emsg = g_win32_error_message(WSAGetLastError()); 203 g_error("WSADuplicateSocket failed: %s", emsg); 204 } 205 close(pair[1]); 206 listener = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, 207 &info, 208 sizeof(info), 209 1); 210 #endif 211 212 qemu_dbus_display1_console_call_register_listener( 213 QEMU_DBUS_DISPLAY1_CONSOLE(console), 214 #ifdef WIN32 215 listener, 216 #else 217 g_variant_new_handle(idx), 218 #endif 219 G_DBUS_CALL_FLAGS_NONE, 220 -1, 221 #ifndef WIN32 222 fd_list, 223 #endif 224 NULL, 225 test_dbus_console_registered, 226 &test); 227 228 g_main_loop_run(loop); 229 230 g_clear_object(&test.server); 231 g_clear_object(&test.listener_conn); 232 qtest_quit(qts); 233 } 234 235 static void 236 test_dbus_display_keyboard(void) 237 { 238 g_autoptr(GError) err = NULL; 239 g_autoptr(GDBusConnection) conn = NULL; 240 g_autoptr(QemuDBusDisplay1KeyboardProxy) keyboard = NULL; 241 QTestState *qts = NULL; 242 243 test_setup(&qts, &conn); 244 245 keyboard = QEMU_DBUS_DISPLAY1_KEYBOARD_PROXY( 246 qemu_dbus_display1_keyboard_proxy_new_sync( 247 conn, 248 G_DBUS_PROXY_FLAGS_NONE, 249 NULL, 250 "/org/qemu/Display1/Console_0", 251 NULL, 252 &err)); 253 g_assert_no_error(err); 254 255 g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 0); 256 g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0); 257 258 qemu_dbus_display1_keyboard_call_press_sync( 259 QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard), 260 0x1C, /* qnum enter */ 261 G_DBUS_CALL_FLAGS_NONE, 262 -1, 263 NULL, 264 &err); 265 if (g_error_matches(err, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { 266 g_test_skip("The VM doesn't have a console!"); 267 qtest_quit(qts); 268 return; 269 } 270 271 g_assert_no_error(err); 272 273 /* may be should wait for interrupt? */ 274 g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 1); 275 g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0x5A); /* scan code 2 enter */ 276 277 qemu_dbus_display1_keyboard_call_release_sync( 278 QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard), 279 0x1C, /* qnum enter */ 280 G_DBUS_CALL_FLAGS_NONE, 281 -1, 282 NULL, 283 &err); 284 g_assert_no_error(err); 285 286 g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 1); 287 g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0xF0); /* scan code 2 release */ 288 g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0x5A); /* scan code 2 enter */ 289 290 g_assert_cmpint(qemu_dbus_display1_keyboard_get_modifiers( 291 QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard)), ==, 0); 292 293 qtest_quit(qts); 294 } 295 296 int 297 main(int argc, char **argv) 298 { 299 g_test_init(&argc, &argv, NULL); 300 301 qtest_add_func("/dbus-display/vm", test_dbus_display_vm); 302 qtest_add_func("/dbus-display/console", test_dbus_display_console); 303 qtest_add_func("/dbus-display/keyboard", test_dbus_display_keyboard); 304 305 return g_test_run(); 306 } 307