]>
Commit | Line | Data |
---|---|---|
5011d262 MA |
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" | |
f916a175 MA |
21 | #include "monitor/monitor-internal.h" |
22 | #include "qapi/error.h" | |
5011d262 MA |
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 | ||
ec843b97 MA |
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 | ||
5011d262 MA |
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 | ||
f916a175 MA |
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 | } | |
bcaf1fde | 339 | if (strcmp(target, "passwd") && strcmp(target, "password")) { |
f916a175 MA |
340 | error_setg(errp, "Expected 'password' after 'vnc'"); |
341 | return; | |
342 | } | |
bcaf1fde MA |
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 | } | |
f916a175 MA |
349 | } |
350 | #endif | |
351 | ||
5011d262 MA |
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 | } | |
f9e1ef74 JQ |
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 | } |