xref: /openbmc/qemu/ui/ui-hmp-cmds.c (revision a9bc470ec208bd27a82100abc9dccf1b69f41b45)
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  #ifdef CONFIG_PIXMAN
441  void coroutine_fn
442  hmp_screendump(Monitor *mon, const QDict *qdict)
443  {
444      const char *filename = qdict_get_str(qdict, "filename");
445      const char *id = qdict_get_try_str(qdict, "device");
446      int64_t head = qdict_get_try_int(qdict, "head", 0);
447      const char *input_format  = qdict_get_try_str(qdict, "format");
448      Error *err = NULL;
449      ImageFormat format;
450  
451      format = qapi_enum_parse(&ImageFormat_lookup, input_format,
452                                IMAGE_FORMAT_PPM, &err);
453      if (err) {
454          goto end;
455      }
456  
457      qmp_screendump(filename, id, id != NULL, head,
458                     input_format != NULL, format, &err);
459  end:
460      hmp_handle_error(mon, err);
461  }
462  #endif
463  
464  void hmp_client_migrate_info(Monitor *mon, const QDict *qdict)
465  {
466      Error *err = NULL;
467      const char *protocol = qdict_get_str(qdict, "protocol");
468      const char *hostname = qdict_get_str(qdict, "hostname");
469      bool has_port        = qdict_haskey(qdict, "port");
470      int port             = qdict_get_try_int(qdict, "port", -1);
471      bool has_tls_port    = qdict_haskey(qdict, "tls-port");
472      int tls_port         = qdict_get_try_int(qdict, "tls-port", -1);
473      const char *cert_subject = qdict_get_try_str(qdict, "cert-subject");
474  
475      qmp_client_migrate_info(protocol, hostname,
476                              has_port, port, has_tls_port, tls_port,
477                              cert_subject, &err);
478      hmp_handle_error(mon, err);
479  }
480