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