xref: /openbmc/qemu/ui/ui-hmp-cmds.c (revision 243975c0)
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