1 /* 2 * HMP commands related to UI 3 * 4 * Copyright IBM, Corp. 2011 5 * 6 * Authors: 7 * Anthony Liguori <aliguori@us.ibm.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2. See 10 * the COPYING file in the top-level directory. 11 * 12 * Contributions after 2012-01-13 are licensed under the terms of the 13 * GNU GPL, version 2 or (at your option) any later version. 14 */ 15 16 #include "qemu/osdep.h" 17 #ifdef CONFIG_SPICE 18 #include <spice/enums.h> 19 #endif 20 #include "monitor/hmp.h" 21 #include "monitor/monitor-internal.h" 22 #include "qapi/error.h" 23 #include "qapi/qapi-commands-ui.h" 24 #include "qapi/qmp/qdict.h" 25 #include "qemu/cutils.h" 26 #include "ui/console.h" 27 #include "ui/input.h" 28 29 static int mouse_button_state; 30 31 void hmp_mouse_move(Monitor *mon, const QDict *qdict) 32 { 33 int dx, dy, dz, button; 34 const char *dx_str = qdict_get_str(qdict, "dx_str"); 35 const char *dy_str = qdict_get_str(qdict, "dy_str"); 36 const char *dz_str = qdict_get_try_str(qdict, "dz_str"); 37 38 dx = strtol(dx_str, NULL, 0); 39 dy = strtol(dy_str, NULL, 0); 40 qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx); 41 qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy); 42 43 if (dz_str) { 44 dz = strtol(dz_str, NULL, 0); 45 if (dz != 0) { 46 button = (dz > 0) ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN; 47 qemu_input_queue_btn(NULL, button, true); 48 qemu_input_event_sync(); 49 qemu_input_queue_btn(NULL, button, false); 50 } 51 } 52 qemu_input_event_sync(); 53 } 54 55 void hmp_mouse_button(Monitor *mon, const QDict *qdict) 56 { 57 static uint32_t bmap[INPUT_BUTTON__MAX] = { 58 [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, 59 [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, 60 [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, 61 }; 62 int button_state = qdict_get_int(qdict, "button_state"); 63 64 if (mouse_button_state == button_state) { 65 return; 66 } 67 qemu_input_update_buttons(NULL, bmap, mouse_button_state, button_state); 68 qemu_input_event_sync(); 69 mouse_button_state = button_state; 70 } 71 72 void hmp_mouse_set(Monitor *mon, const QDict *qdict) 73 { 74 Error *err = NULL; 75 76 qemu_mouse_set(qdict_get_int(qdict, "index"), &err); 77 hmp_handle_error(mon, err); 78 } 79 80 void hmp_info_mice(Monitor *mon, const QDict *qdict) 81 { 82 MouseInfoList *mice_list, *mouse; 83 84 mice_list = qmp_query_mice(NULL); 85 if (!mice_list) { 86 monitor_printf(mon, "No mouse devices connected\n"); 87 return; 88 } 89 90 for (mouse = mice_list; mouse; mouse = mouse->next) { 91 monitor_printf(mon, "%c Mouse #%" PRId64 ": %s%s\n", 92 mouse->value->current ? '*' : ' ', 93 mouse->value->index, mouse->value->name, 94 mouse->value->absolute ? " (absolute)" : ""); 95 } 96 97 qapi_free_MouseInfoList(mice_list); 98 } 99 100 #ifdef CONFIG_VNC 101 /* Helper for hmp_info_vnc_clients, _servers */ 102 static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info, 103 const char *name) 104 { 105 monitor_printf(mon, " %s: %s:%s (%s%s)\n", 106 name, 107 info->host, 108 info->service, 109 NetworkAddressFamily_str(info->family), 110 info->websocket ? " (Websocket)" : ""); 111 } 112 113 /* Helper displaying and auth and crypt info */ 114 static void hmp_info_vnc_authcrypt(Monitor *mon, const char *indent, 115 VncPrimaryAuth auth, 116 VncVencryptSubAuth *vencrypt) 117 { 118 monitor_printf(mon, "%sAuth: %s (Sub: %s)\n", indent, 119 VncPrimaryAuth_str(auth), 120 vencrypt ? VncVencryptSubAuth_str(*vencrypt) : "none"); 121 } 122 123 static void hmp_info_vnc_clients(Monitor *mon, VncClientInfoList *client) 124 { 125 while (client) { 126 VncClientInfo *cinfo = client->value; 127 128 hmp_info_VncBasicInfo(mon, qapi_VncClientInfo_base(cinfo), "Client"); 129 monitor_printf(mon, " x509_dname: %s\n", 130 cinfo->x509_dname ?: "none"); 131 monitor_printf(mon, " sasl_username: %s\n", 132 cinfo->sasl_username ?: "none"); 133 134 client = client->next; 135 } 136 } 137 138 static void hmp_info_vnc_servers(Monitor *mon, VncServerInfo2List *server) 139 { 140 while (server) { 141 VncServerInfo2 *sinfo = server->value; 142 hmp_info_VncBasicInfo(mon, qapi_VncServerInfo2_base(sinfo), "Server"); 143 hmp_info_vnc_authcrypt(mon, " ", sinfo->auth, 144 sinfo->has_vencrypt ? &sinfo->vencrypt : NULL); 145 server = server->next; 146 } 147 } 148 149 void hmp_info_vnc(Monitor *mon, const QDict *qdict) 150 { 151 VncInfo2List *info2l, *info2l_head; 152 Error *err = NULL; 153 154 info2l = qmp_query_vnc_servers(&err); 155 info2l_head = info2l; 156 if (hmp_handle_error(mon, err)) { 157 return; 158 } 159 if (!info2l) { 160 monitor_printf(mon, "None\n"); 161 return; 162 } 163 164 while (info2l) { 165 VncInfo2 *info = info2l->value; 166 monitor_printf(mon, "%s:\n", info->id); 167 hmp_info_vnc_servers(mon, info->server); 168 hmp_info_vnc_clients(mon, info->clients); 169 if (!info->server) { 170 /* 171 * The server entry displays its auth, we only need to 172 * display in the case of 'reverse' connections where 173 * there's no server. 174 */ 175 hmp_info_vnc_authcrypt(mon, " ", info->auth, 176 info->has_vencrypt ? &info->vencrypt : NULL); 177 } 178 if (info->display) { 179 monitor_printf(mon, " Display: %s\n", info->display); 180 } 181 info2l = info2l->next; 182 } 183 184 qapi_free_VncInfo2List(info2l_head); 185 186 } 187 #endif 188 189 #ifdef CONFIG_SPICE 190 void hmp_info_spice(Monitor *mon, const QDict *qdict) 191 { 192 SpiceChannelList *chan; 193 SpiceInfo *info; 194 const char *channel_name; 195 static const char *const channel_names[] = { 196 [SPICE_CHANNEL_MAIN] = "main", 197 [SPICE_CHANNEL_DISPLAY] = "display", 198 [SPICE_CHANNEL_INPUTS] = "inputs", 199 [SPICE_CHANNEL_CURSOR] = "cursor", 200 [SPICE_CHANNEL_PLAYBACK] = "playback", 201 [SPICE_CHANNEL_RECORD] = "record", 202 [SPICE_CHANNEL_TUNNEL] = "tunnel", 203 [SPICE_CHANNEL_SMARTCARD] = "smartcard", 204 [SPICE_CHANNEL_USBREDIR] = "usbredir", 205 [SPICE_CHANNEL_PORT] = "port", 206 [SPICE_CHANNEL_WEBDAV] = "webdav", 207 }; 208 209 info = qmp_query_spice(NULL); 210 211 if (!info->enabled) { 212 monitor_printf(mon, "Server: disabled\n"); 213 goto out; 214 } 215 216 monitor_printf(mon, "Server:\n"); 217 if (info->has_port) { 218 monitor_printf(mon, " address: %s:%" PRId64 "\n", 219 info->host, info->port); 220 } 221 if (info->has_tls_port) { 222 monitor_printf(mon, " address: %s:%" PRId64 " [tls]\n", 223 info->host, info->tls_port); 224 } 225 monitor_printf(mon, " migrated: %s\n", 226 info->migrated ? "true" : "false"); 227 monitor_printf(mon, " auth: %s\n", info->auth); 228 monitor_printf(mon, " compiled: %s\n", info->compiled_version); 229 monitor_printf(mon, " mouse-mode: %s\n", 230 SpiceQueryMouseMode_str(info->mouse_mode)); 231 232 if (!info->has_channels || info->channels == NULL) { 233 monitor_printf(mon, "Channels: none\n"); 234 } else { 235 for (chan = info->channels; chan; chan = chan->next) { 236 monitor_printf(mon, "Channel:\n"); 237 monitor_printf(mon, " address: %s:%s%s\n", 238 chan->value->host, chan->value->port, 239 chan->value->tls ? " [tls]" : ""); 240 monitor_printf(mon, " session: %" PRId64 "\n", 241 chan->value->connection_id); 242 monitor_printf(mon, " channel: %" PRId64 ":%" PRId64 "\n", 243 chan->value->channel_type, chan->value->channel_id); 244 245 channel_name = "unknown"; 246 if (chan->value->channel_type > 0 && 247 chan->value->channel_type < ARRAY_SIZE(channel_names) && 248 channel_names[chan->value->channel_type]) { 249 channel_name = channel_names[chan->value->channel_type]; 250 } 251 252 monitor_printf(mon, " channel name: %s\n", channel_name); 253 } 254 } 255 256 out: 257 qapi_free_SpiceInfo(info); 258 } 259 #endif 260 261 void hmp_set_password(Monitor *mon, const QDict *qdict) 262 { 263 const char *protocol = qdict_get_str(qdict, "protocol"); 264 const char *password = qdict_get_str(qdict, "password"); 265 const char *display = qdict_get_try_str(qdict, "display"); 266 const char *connected = qdict_get_try_str(qdict, "connected"); 267 Error *err = NULL; 268 269 SetPasswordOptions opts = { 270 .password = (char *)password, 271 .has_connected = !!connected, 272 }; 273 274 opts.connected = qapi_enum_parse(&SetPasswordAction_lookup, connected, 275 SET_PASSWORD_ACTION_KEEP, &err); 276 if (err) { 277 goto out; 278 } 279 280 opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol, 281 DISPLAY_PROTOCOL_VNC, &err); 282 if (err) { 283 goto out; 284 } 285 286 if (opts.protocol == DISPLAY_PROTOCOL_VNC) { 287 opts.u.vnc.display = (char *)display; 288 } 289 290 qmp_set_password(&opts, &err); 291 292 out: 293 hmp_handle_error(mon, err); 294 } 295 296 void hmp_expire_password(Monitor *mon, const QDict *qdict) 297 { 298 const char *protocol = qdict_get_str(qdict, "protocol"); 299 const char *whenstr = qdict_get_str(qdict, "time"); 300 const char *display = qdict_get_try_str(qdict, "display"); 301 Error *err = NULL; 302 303 ExpirePasswordOptions opts = { 304 .time = (char *)whenstr, 305 }; 306 307 opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol, 308 DISPLAY_PROTOCOL_VNC, &err); 309 if (err) { 310 goto out; 311 } 312 313 if (opts.protocol == DISPLAY_PROTOCOL_VNC) { 314 opts.u.vnc.display = (char *)display; 315 } 316 317 qmp_expire_password(&opts, &err); 318 319 out: 320 hmp_handle_error(mon, err); 321 } 322 323 #ifdef CONFIG_VNC 324 static void hmp_change_read_arg(void *opaque, const char *password, 325 void *readline_opaque) 326 { 327 qmp_change_vnc_password(password, NULL); 328 monitor_read_command(opaque, 1); 329 } 330 331 void hmp_change_vnc(Monitor *mon, const char *device, const char *target, 332 const char *arg, const char *read_only, bool force, 333 Error **errp) 334 { 335 if (read_only) { 336 error_setg(errp, "Parameter 'read-only-mode' is invalid for VNC"); 337 return; 338 } 339 if (strcmp(target, "passwd") && strcmp(target, "password")) { 340 error_setg(errp, "Expected 'password' after 'vnc'"); 341 return; 342 } 343 if (!arg) { 344 MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common); 345 monitor_read_password(hmp_mon, hmp_change_read_arg, NULL); 346 } else { 347 qmp_change_vnc_password(arg, errp); 348 } 349 } 350 #endif 351 352 void hmp_sendkey(Monitor *mon, const QDict *qdict) 353 { 354 const char *keys = qdict_get_str(qdict, "keys"); 355 KeyValue *v = NULL; 356 KeyValueList *head = NULL, **tail = &head; 357 int has_hold_time = qdict_haskey(qdict, "hold-time"); 358 int hold_time = qdict_get_try_int(qdict, "hold-time", -1); 359 Error *err = NULL; 360 const char *separator; 361 int keyname_len; 362 363 while (1) { 364 separator = qemu_strchrnul(keys, '-'); 365 keyname_len = separator - keys; 366 367 /* Be compatible with old interface, convert user inputted "<" */ 368 if (keys[0] == '<' && keyname_len == 1) { 369 keys = "less"; 370 keyname_len = 4; 371 } 372 373 v = g_malloc0(sizeof(*v)); 374 375 if (strstart(keys, "0x", NULL)) { 376 const char *endp; 377 int value; 378 379 if (qemu_strtoi(keys, &endp, 0, &value) < 0) { 380 goto err_out; 381 } 382 assert(endp <= keys + keyname_len); 383 if (endp != keys + keyname_len) { 384 goto err_out; 385 } 386 v->type = KEY_VALUE_KIND_NUMBER; 387 v->u.number.data = value; 388 } else { 389 int idx = index_from_key(keys, keyname_len); 390 if (idx == Q_KEY_CODE__MAX) { 391 goto err_out; 392 } 393 v->type = KEY_VALUE_KIND_QCODE; 394 v->u.qcode.data = idx; 395 } 396 QAPI_LIST_APPEND(tail, v); 397 v = NULL; 398 399 if (!*separator) { 400 break; 401 } 402 keys = separator + 1; 403 } 404 405 qmp_send_key(head, has_hold_time, hold_time, &err); 406 hmp_handle_error(mon, err); 407 408 out: 409 qapi_free_KeyValue(v); 410 qapi_free_KeyValueList(head); 411 return; 412 413 err_out: 414 monitor_printf(mon, "invalid parameter: %.*s\n", keyname_len, keys); 415 goto out; 416 } 417 418 void sendkey_completion(ReadLineState *rs, int nb_args, const char *str) 419 { 420 int i; 421 char *sep; 422 size_t len; 423 424 if (nb_args != 2) { 425 return; 426 } 427 sep = strrchr(str, '-'); 428 if (sep) { 429 str = sep + 1; 430 } 431 len = strlen(str); 432 readline_set_completion_index(rs, len); 433 for (i = 0; i < Q_KEY_CODE__MAX; i++) { 434 if (!strncmp(str, QKeyCode_str(i), len)) { 435 readline_add_completion(rs, QKeyCode_str(i)); 436 } 437 } 438 } 439 440 void coroutine_fn 441 hmp_screendump(Monitor *mon, const QDict *qdict) 442 { 443 const char *filename = qdict_get_str(qdict, "filename"); 444 const char *id = qdict_get_try_str(qdict, "device"); 445 int64_t head = qdict_get_try_int(qdict, "head", 0); 446 const char *input_format = qdict_get_try_str(qdict, "format"); 447 Error *err = NULL; 448 ImageFormat format; 449 450 format = qapi_enum_parse(&ImageFormat_lookup, input_format, 451 IMAGE_FORMAT_PPM, &err); 452 if (err) { 453 goto end; 454 } 455 456 qmp_screendump(filename, id, id != NULL, head, 457 input_format != NULL, format, &err); 458 end: 459 hmp_handle_error(mon, err); 460 } 461 462 void hmp_client_migrate_info(Monitor *mon, const QDict *qdict) 463 { 464 Error *err = NULL; 465 const char *protocol = qdict_get_str(qdict, "protocol"); 466 const char *hostname = qdict_get_str(qdict, "hostname"); 467 bool has_port = qdict_haskey(qdict, "port"); 468 int port = qdict_get_try_int(qdict, "port", -1); 469 bool has_tls_port = qdict_haskey(qdict, "tls-port"); 470 int tls_port = qdict_get_try_int(qdict, "tls-port", -1); 471 const char *cert_subject = qdict_get_try_str(qdict, "cert-subject"); 472 473 qmp_client_migrate_info(protocol, hostname, 474 has_port, port, has_tls_port, tls_port, 475 cert_subject, &err); 476 hmp_handle_error(mon, err); 477 } 478