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