summaryrefslogtreecommitdiff
path: root/monitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'monitor.c')
-rw-r--r--monitor.c569
1 files changed, 569 insertions, 0 deletions
diff --git a/monitor.c b/monitor.c
new file mode 100644
index 0000000000..633b898921
--- /dev/null
+++ b/monitor.c
@@ -0,0 +1,569 @@
+/*
+ * QEMU monitor
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/time.h>
+#include <malloc.h>
+#include <termios.h>
+#include <sys/poll.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "cpu.h"
+#include "vl.h"
+
+//#define DEBUG
+
+#define TERM_CMD_BUF_SIZE 4095
+#define MAX_ARGS 64
+
+#define IS_NORM 0
+#define IS_ESC 1
+#define IS_CSI 2
+
+#define printf do_not_use_printf
+
+static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1];
+static int term_cmd_buf_index;
+static int term_cmd_buf_size;
+static int term_esc_state;
+static int term_esc_param;
+
+typedef struct term_cmd_t {
+ const char *name;
+ void (*handler)(int argc, const char **argv);
+ const char *params;
+ const char *help;
+} term_cmd_t;
+
+static term_cmd_t term_cmds[];
+static term_cmd_t info_cmds[];
+
+void term_printf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+void term_flush(void)
+{
+ fflush(stdout);
+}
+
+static int compare_cmd(const char *name, const char *list)
+{
+ const char *p, *pstart;
+ int len;
+ len = strlen(name);
+ p = list;
+ for(;;) {
+ pstart = p;
+ p = strchr(p, '|');
+ if (!p)
+ p = pstart + strlen(pstart);
+ if ((p - pstart) == len && !memcmp(pstart, name, len))
+ return 1;
+ if (*p == '\0')
+ break;
+ p++;
+ }
+ return 0;
+}
+
+static void help_cmd1(term_cmd_t *cmds, const char *prefix, const char *name)
+{
+ term_cmd_t *cmd;
+
+ for(cmd = cmds; cmd->name != NULL; cmd++) {
+ if (!name || !strcmp(name, cmd->name))
+ term_printf("%s%s %s -- %s\n", prefix, cmd->name, cmd->params, cmd->help);
+ }
+}
+
+static void help_cmd(const char *name)
+{
+ if (name && !strcmp(name, "info")) {
+ help_cmd1(info_cmds, "info ", NULL);
+ } else {
+ help_cmd1(term_cmds, "", name);
+ }
+}
+
+static void do_help(int argc, const char **argv)
+{
+ help_cmd(argv[1]);
+}
+
+static void do_commit(int argc, const char **argv)
+{
+ int i;
+
+ for (i = 0; i < MAX_DISKS; i++) {
+ if (bs_table[i])
+ bdrv_commit(bs_table[i]);
+ }
+}
+
+static void do_info(int argc, const char **argv)
+{
+ term_cmd_t *cmd;
+ const char *item;
+
+ if (argc < 2)
+ goto help;
+ item = argv[1];
+ for(cmd = info_cmds; cmd->name != NULL; cmd++) {
+ if (compare_cmd(argv[1], cmd->name))
+ goto found;
+ }
+ help:
+ help_cmd(argv[0]);
+ return;
+ found:
+ cmd->handler(argc, argv);
+}
+
+static void do_info_network(int argc, const char **argv)
+{
+ int i, j;
+ NetDriverState *nd;
+
+ for(i = 0; i < nb_nics; i++) {
+ nd = &nd_table[i];
+ term_printf("%d: ifname=%s macaddr=", i, nd->ifname);
+ for(j = 0; j < 6; j++) {
+ if (j > 0)
+ term_printf(":");
+ term_printf("%02x", nd->macaddr[j]);
+ }
+ term_printf("\n");
+ }
+}
+
+static void do_info_block(int argc, const char **argv)
+{
+ bdrv_info();
+}
+
+static void do_quit(int argc, const char **argv)
+{
+ exit(0);
+}
+
+static int eject_device(BlockDriverState *bs, int force)
+{
+ if (bdrv_is_inserted(bs)) {
+ if (!force) {
+ if (!bdrv_is_removable(bs)) {
+ term_printf("device is not removable\n");
+ return -1;
+ }
+ if (bdrv_is_locked(bs)) {
+ term_printf("device is locked\n");
+ return -1;
+ }
+ }
+ bdrv_close(bs);
+ }
+ return 0;
+}
+
+static void do_eject(int argc, const char **argv)
+{
+ BlockDriverState *bs;
+ const char **parg;
+ int force;
+
+ parg = argv + 1;
+ if (!*parg) {
+ fail:
+ help_cmd(argv[0]);
+ return;
+ }
+ force = 0;
+ if (!strcmp(*parg, "-f")) {
+ force = 1;
+ parg++;
+ }
+ if (!*parg)
+ goto fail;
+ bs = bdrv_find(*parg);
+ if (!bs) {
+ term_printf("device not found\n");
+ return;
+ }
+ eject_device(bs, force);
+}
+
+static void do_change(int argc, const char **argv)
+{
+ BlockDriverState *bs;
+
+ if (argc != 3) {
+ help_cmd(argv[0]);
+ return;
+ }
+ bs = bdrv_find(argv[1]);
+ if (!bs) {
+ term_printf("device not found\n");
+ return;
+ }
+ if (eject_device(bs, 0) < 0)
+ return;
+ bdrv_open(bs, argv[2], 0);
+}
+
+static term_cmd_t term_cmds[] = {
+ { "help|?", do_help,
+ "[cmd]", "show the help" },
+ { "commit", do_commit,
+ "", "commit changes to the disk images (if -snapshot is used)" },
+ { "info", do_info,
+ "subcommand", "show various information about the system state" },
+ { "q|quit", do_quit,
+ "", "quit the emulator" },
+ { "eject", do_eject,
+ "[-f] device", "eject a removable media (use -f to force it)" },
+ { "change", do_change,
+ "device filename", "change a removable media" },
+ { NULL, NULL, },
+};
+
+static term_cmd_t info_cmds[] = {
+ { "network", do_info_network,
+ "", "show the network state" },
+ { "block", do_info_block,
+ "", "show the block devices" },
+ { NULL, NULL, },
+};
+
+static void term_handle_command(char *cmdline)
+{
+ char *p, *pstart;
+ int argc;
+ const char *args[MAX_ARGS + 1];
+ term_cmd_t *cmd;
+
+#ifdef DEBUG
+ term_printf("command='%s'\n", cmdline);
+#endif
+
+ /* split command in words */
+ argc = 0;
+ p = cmdline;
+ for(;;) {
+ while (isspace(*p))
+ p++;
+ if (*p == '\0')
+ break;
+ pstart = p;
+ while (*p != '\0' && !isspace(*p))
+ p++;
+ args[argc] = pstart;
+ argc++;
+ if (argc >= MAX_ARGS)
+ break;
+ if (*p == '\0')
+ break;
+ *p++ = '\0';
+ }
+ args[argc] = NULL;
+#ifdef DEBUG
+ for(i=0;i<argc;i++) {
+ term_printf(" '%s'", args[i]);
+ }
+ term_printf("\n");
+#endif
+ if (argc <= 0)
+ return;
+ for(cmd = term_cmds; cmd->name != NULL; cmd++) {
+ if (compare_cmd(args[0], cmd->name))
+ goto found;
+ }
+ term_printf("unknown command: '%s'\n", args[0]);
+ return;
+ found:
+ cmd->handler(argc, args);
+}
+
+static void term_show_prompt(void)
+{
+ term_printf("(qemu) ");
+ fflush(stdout);
+ term_cmd_buf_index = 0;
+ term_cmd_buf_size = 0;
+ term_esc_state = IS_NORM;
+}
+
+static void term_insert_char(int ch)
+{
+ if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) {
+ memmove(term_cmd_buf + term_cmd_buf_index + 1,
+ term_cmd_buf + term_cmd_buf_index,
+ term_cmd_buf_size - term_cmd_buf_index);
+ term_cmd_buf[term_cmd_buf_index] = ch;
+ term_cmd_buf_size++;
+ term_printf("\033[@%c", ch);
+ term_cmd_buf_index++;
+ term_flush();
+ }
+}
+
+static void term_backward_char(void)
+{
+ if (term_cmd_buf_index > 0) {
+ term_cmd_buf_index--;
+ term_printf("\033[D");
+ term_flush();
+ }
+}
+
+static void term_forward_char(void)
+{
+ if (term_cmd_buf_index < term_cmd_buf_size) {
+ term_cmd_buf_index++;
+ term_printf("\033[C");
+ term_flush();
+ }
+}
+
+static void term_delete_char(void)
+{
+ if (term_cmd_buf_index < term_cmd_buf_size) {
+ memmove(term_cmd_buf + term_cmd_buf_index,
+ term_cmd_buf + term_cmd_buf_index + 1,
+ term_cmd_buf_size - term_cmd_buf_index - 1);
+ term_printf("\033[P");
+ term_cmd_buf_size--;
+ term_flush();
+ }
+}
+
+static void term_backspace(void)
+{
+ if (term_cmd_buf_index > 0) {
+ term_backward_char();
+ term_delete_char();
+ }
+}
+
+static void term_bol(void)
+{
+ while (term_cmd_buf_index > 0)
+ term_backward_char();
+}
+
+static void term_eol(void)
+{
+ while (term_cmd_buf_index < term_cmd_buf_size)
+ term_forward_char();
+}
+
+/* return true if command handled */
+static void term_handle_byte(int ch)
+{
+ switch(term_esc_state) {
+ case IS_NORM:
+ switch(ch) {
+ case 1:
+ term_bol();
+ break;
+ case 5:
+ term_eol();
+ break;
+ case 10:
+ case 13:
+ term_cmd_buf[term_cmd_buf_size] = '\0';
+ term_printf("\n");
+ term_handle_command(term_cmd_buf);
+ term_show_prompt();
+ break;
+ case 27:
+ term_esc_state = IS_ESC;
+ break;
+ case 127:
+ case 8:
+ term_backspace();
+ break;
+ default:
+ if (ch >= 32) {
+ term_insert_char(ch);
+ }
+ break;
+ }
+ break;
+ case IS_ESC:
+ if (ch == '[') {
+ term_esc_state = IS_CSI;
+ term_esc_param = 0;
+ } else {
+ term_esc_state = IS_NORM;
+ }
+ break;
+ case IS_CSI:
+ switch(ch) {
+ case 'D':
+ term_backward_char();
+ break;
+ case 'C':
+ term_forward_char();
+ break;
+ case '0' ... '9':
+ term_esc_param = term_esc_param * 10 + (ch - '0');
+ goto the_end;
+ case '~':
+ switch(term_esc_param) {
+ case 1:
+ term_bol();
+ break;
+ case 3:
+ term_delete_char();
+ break;
+ case 4:
+ term_eol();
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ term_esc_state = IS_NORM;
+ the_end:
+ break;
+ }
+}
+
+/*************************************************************/
+/* serial console support */
+
+#define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */
+
+static int term_got_escape, term_command;
+
+void term_print_help(void)
+{
+ term_printf("\n"
+ "C-a h print this help\n"
+ "C-a x exit emulatior\n"
+ "C-a d switch on/off debug log\n"
+ "C-a s save disk data back to file (if -snapshot)\n"
+ "C-a b send break (magic sysrq)\n"
+ "C-a c switch between console and monitor\n"
+ "C-a C-a send C-a\n"
+ );
+}
+
+/* called when a char is received */
+static void term_received_byte(int ch)
+{
+ if (!serial_console) {
+ /* if no serial console, handle every command */
+ term_handle_byte(ch);
+ } else {
+ if (term_got_escape) {
+ term_got_escape = 0;
+ switch(ch) {
+ case 'h':
+ term_print_help();
+ break;
+ case 'x':
+ exit(0);
+ break;
+ case 's':
+ {
+ int i;
+ for (i = 0; i < MAX_DISKS; i++) {
+ if (bs_table[i])
+ bdrv_commit(bs_table[i]);
+ }
+ }
+ break;
+ case 'b':
+ if (serial_console)
+ serial_receive_break(serial_console);
+ break;
+ case 'c':
+ if (!term_command) {
+ term_show_prompt();
+ term_command = 1;
+ } else {
+ term_command = 0;
+ }
+ break;
+ case 'd':
+ cpu_set_log(CPU_LOG_ALL);
+ break;
+ case TERM_ESCAPE:
+ goto send_char;
+ }
+ } else if (ch == TERM_ESCAPE) {
+ term_got_escape = 1;
+ } else {
+ send_char:
+ if (term_command) {
+ term_handle_byte(ch);
+ } else {
+ if (serial_console)
+ serial_receive_byte(serial_console, ch);
+ }
+ }
+ }
+}
+
+static int term_can_read(void *opaque)
+{
+ if (serial_console) {
+ return serial_can_receive(serial_console);
+ } else {
+ return 1;
+ }
+}
+
+static void term_read(void *opaque, const uint8_t *buf, int size)
+{
+ int i;
+ for(i = 0; i < size; i++)
+ term_received_byte(buf[i]);
+}
+
+void monitor_init(void)
+{
+ if (!serial_console) {
+ term_printf("QEMU %s monitor - type 'help' for more information\n",
+ QEMU_VERSION);
+ term_show_prompt();
+ }
+ add_fd_read_handler(0, term_can_read, term_read, NULL);
+}