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