diff options
Diffstat (limited to 'monitor.c')
-rw-r--r-- | monitor.c | 569 |
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); +} |