Files
xen/tools/ioemu/monitor.c
T
kaf24@firebug.cl.cam.ac.uk 969807247d Restore command line parsing code from qemu-0.6.1
This enables cdrom ejection (both file based and physical i.e. /dev/cdrom)

Signed-off-by: Edwin Zhai <edwin.zhai@intel.com>
Signed-off-by: Arun Sharma <arun.sharma@intel.com>
2005-07-25 21:02:50 +00:00

634 lines
15 KiB
C

/*
* 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 "vl.h"
#include <dirent.h>
//#define DEBUG
//#define DEBUG_COMPLETION
#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif
/*
* Supported types:
*
* 'F' filename
* 'B' block device name
* 's' string (accept optional quote)
* 'i' integer
* '/' optional gdb-like print format (like "/10x")
*
* '?' optional type (for 'F', 's' and 'i')
*
*/
typedef struct term_cmd_t {
const char *name;
const char *args_type;
void (*handler)();
const char *params;
const char *help;
} term_cmd_t;
static CharDriverState *monitor_hd;
static term_cmd_t term_cmds[];
static term_cmd_t info_cmds[];
static char term_outbuf[1024];
static int term_outbuf_index;
static void monitor_start_input(void);
void term_flush(void)
{
if (term_outbuf_index > 0) {
if(monitor_hd)
qemu_chr_write(monitor_hd, term_outbuf, term_outbuf_index);
else
fwrite(term_outbuf, term_outbuf_index, 1, stderr);
term_outbuf_index = 0;
}
}
/* flush at every end of line or if the buffer is full */
void term_puts(const char *str)
{
int c;
for(;;) {
c = *str++;
if (c == '\0')
break;
term_outbuf[term_outbuf_index++] = c;
if (term_outbuf_index >= sizeof(term_outbuf) ||
c == '\n')
term_flush();
}
}
void term_vprintf(const char *fmt, va_list ap)
{
char buf[4096];
vsnprintf(buf, sizeof(buf), fmt, ap);
term_puts(buf);
}
void term_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
term_vprintf(fmt, ap);
va_end(ap);
}
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);
if (name && !strcmp(name, "log")) {
CPULogItem *item;
term_printf("Log items (comma separated):\n");
term_printf("%-10s %s\n", "none", "remove all logs");
for(item = cpu_log_items; item->mask != 0; item++) {
term_printf("%-10s %s\n", item->name, item->help);
}
}
}
}
static void do_help(const char *name)
{
help_cmd(name);
}
static void do_commit(void)
{
int i;
for (i = 0; i < MAX_DISKS; i++) {
if (bs_table[i]) {
bdrv_commit(bs_table[i]);
}
}
}
static void do_info(const char *item)
{
term_cmd_t *cmd;
if (!item)
goto help;
for(cmd = info_cmds; cmd->name != NULL; cmd++) {
if (compare_cmd(item, cmd->name))
goto found;
}
help:
help_cmd("info");
return;
found:
cmd->handler();
}
static void do_info_version(void)
{
term_printf("%s\n", QEMU_VERSION);
}
static void do_info_network(void)
{
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(void)
{
bdrv_info();
}
static void do_info_history (void)
{
int i;
const char *str;
i = 0;
for(;;) {
str = readline_get_history(i);
if (!str)
break;
term_printf("%d: '%s'\n", i, str);
i++;
}
}
static void do_quit(void)
{
extern int domid;
extern FILE* logfile;
char destroy_cmd[20];
sprintf(destroy_cmd, "xm destroy %d", domid);
if (system(destroy_cmd) == -1)
fprintf(logfile, "%s failed.!\n", destroy_cmd);
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 force, const char *filename)
{
BlockDriverState *bs;
bs = bdrv_find(filename);
if (!bs) {
term_printf("device not found\n");
return;
}
eject_device(bs, force);
}
static void do_change(const char *device, const char *filename)
{
BlockDriverState *bs;
#if 0
int i;
char password[256];
#endif
bs = bdrv_find(device);
if (!bs) {
term_printf("device not found\n");
return;
}
if (eject_device(bs, 0) < 0)
return;
bdrv_open(bs, filename, 0);
#if 0
if (bdrv_is_encrypted(bs)) {
term_printf("%s is encrypted.\n", device);
for(i = 0; i < 3; i++) {
monitor_readline("Password: ", 1, password, sizeof(password));
if (bdrv_set_key(bs, password) == 0)
break;
term_printf("invalid password\n");
}
}
#endif
}
static void do_screen_dump(const char *filename)
{
vga_screen_dump(filename);
}
static void do_log(const char *items)
{
int mask;
if (!strcmp(items, "none")) {
mask = 0;
} else {
mask = cpu_str_to_log_mask(items);
if (!mask) {
help_cmd("log");
return;
}
}
cpu_set_log(mask);
}
static term_cmd_t term_cmds[] = {
{ "help|?", "s?", do_help,
"[cmd]", "show the help" },
{ "commit", "", do_commit,
"", "commit changes to the disk images (if -snapshot is used)" },
{ "info", "s?", do_info,
"subcommand", "show various information about the system state" },
{ "q|quit", "", do_quit,
"", "quit the emulator" },
{ "eject", "-fB", do_eject,
"[-f] device", "eject a removable media (use -f to force it)" },
{ "change", "BF", do_change,
"device filename", "change a removable media" },
{ "screendump", "F", do_screen_dump,
"filename", "save screen into PPM image 'filename'" },
{ "log", "s", do_log,
"item1[,...]", "activate logging of the specified items to '/tmp/qemu.log'" },
{ "q|quit", "", do_quit,
"", "quit the emulator" },
{ NULL, NULL, },
};
static term_cmd_t info_cmds[] = {
{ "version", "", do_info_version,
"", "show the version of qemu" },
{ "network", "", do_info_network,
"", "show the network state" },
{ "block", "", do_info_block,
"", "show the block devices" },
{ "history", "", do_info_history,
"", "show the command line history", },
{ "irq", "", irq_info,
"", "show the interrupts statistics (if available)", },
{ "pic", "", pic_info,
"", "show i8259 (PIC) state", },
{ "pci", "", pci_info,
"", "show PCI info", },
{ "vmxiopage", "", sp_info,
"", "show VMX device model shared page info", },
{ NULL, NULL, },
};
static int get_str(char *buf, int buf_size, const char **pp)
{
const char *p;
char *q;
int c;
q = buf;
p = *pp;
while (isspace(*p))
p++;
if (*p == '\0') {
fail:
*q = '\0';
*pp = p;
return -1;
}
if (*p == '\"') {
p++;
while (*p != '\0' && *p != '\"') {
if (*p == '\\') {
p++;
c = *p++;
switch(c) {
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case '\\':
case '\'':
case '\"':
break;
default:
qemu_printf("unsupported escape code: '\\%c'\n", c);
goto fail;
}
if ((q - buf) < buf_size - 1) {
*q++ = c;
}
} else {
if ((q - buf) < buf_size - 1) {
*q++ = *p;
}
p++;
}
}
if (*p != '\"') {
qemu_printf("unterminated string\n");
goto fail;
}
p++;
} else {
while (*p != '\0' && !isspace(*p)) {
if ((q - buf) < buf_size - 1) {
*q++ = *p;
}
p++;
}
}
*q = '\0';
*pp = p;
return 0;
}
#define MAX_ARGS 16
static void monitor_handle_command(const char *cmdline)
{
const char *p, *pstart, *typestr;
char *q;
int c, nb_args, len, i;
term_cmd_t *cmd;
char cmdname[256];
char buf[1024];
void *str_allocated[MAX_ARGS];
void *args[MAX_ARGS];
#ifdef DEBUG
term_printf("command='%s'\n", cmdline);
#endif
/* extract the command name */
p = cmdline;
q = cmdname;
while (isspace(*p))
p++;
if (*p == '\0')
return;
pstart = p;
while (*p != '\0' && *p != '/' && !isspace(*p))
p++;
len = p - pstart;
if (len > sizeof(cmdname) - 1)
len = sizeof(cmdname) - 1;
memcpy(cmdname, pstart, len);
cmdname[len] = '\0';
/* find the command */
for(cmd = term_cmds; cmd->name != NULL; cmd++) {
if (compare_cmd(cmdname, cmd->name))
goto found;
}
term_printf("unknown command: '%s'\n", cmdname);
return;
found:
for(i = 0; i < MAX_ARGS; i++)
str_allocated[i] = NULL;
/* parse the parameters */
typestr = cmd->args_type;
nb_args = 0;
for(;;) {
c = *typestr;
if (c == '\0')
break;
typestr++;
switch(c) {
case 'F':
case 'B':
case 's':
{
int ret;
char *str;
while (isspace(*p))
p++;
if (*typestr == '?') {
typestr++;
if (*p == '\0') {
/* no optional string: NULL argument */
str = NULL;
goto add_str;
}
}
ret = get_str(buf, sizeof(buf), &p);
if (ret < 0) {
switch(c) {
case 'F':
term_printf("%s: filename expected\n", cmdname);
break;
case 'B':
term_printf("%s: block device name expected\n", cmdname);
break;
default:
term_printf("%s: string expected\n", cmdname);
break;
}
goto fail;
}
str = qemu_malloc(strlen(buf) + 1);
strcpy(str, buf);
str_allocated[nb_args] = str;
add_str:
if (nb_args >= MAX_ARGS) {
error_args:
term_printf("%s: too many arguments\n", cmdname);
goto fail;
}
args[nb_args++] = str;
}
break;
case '-':
{
int has_option;
/* option */
c = *typestr++;
if (c == '\0')
goto bad_type;
while (isspace(*p))
p++;
has_option = 0;
if (*p == '-') {
p++;
if (*p != c) {
term_printf("%s: unsupported option -%c\n",
cmdname, *p);
goto fail;
}
p++;
has_option = 1;
}
if (nb_args >= MAX_ARGS)
goto error_args;
args[nb_args++] = (void *)has_option;
}
break;
/* TODO: add more commands we need here to support vmx device model */
case '/':
case 'i':
default:
bad_type:
term_printf("%s: unknown type '%c',not support now.\n", cmdname, c);
goto fail;
}
}
/* check that all arguments were parsed */
while (isspace(*p))
p++;
if (*p != '\0') {
term_printf("%s: extraneous characters at the end of line\n",
cmdname);
goto fail;
}
switch(nb_args) {
case 0:
cmd->handler();
break;
case 1:
cmd->handler(args[0]);
break;
case 2:
cmd->handler(args[0], args[1]);
break;
case 3:
cmd->handler(args[0], args[1], args[2]);
break;
case 4:
cmd->handler(args[0], args[1], args[2], args[3]);
break;
case 5:
cmd->handler(args[0], args[1], args[2], args[3], args[4]);
break;
case 6:
cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5]);
break;
default:
term_printf("unsupported number of arguments: %d\n", nb_args);
goto fail;
}
fail:
for(i = 0; i < MAX_ARGS; i++)
qemu_free(str_allocated[i]);
return;
}
static int term_can_read(void *opaque)
{
return 128;
}
static void term_read(void *opaque, const uint8_t *buf, int size)
{
int i;
for(i = 0; i < size; i++)
readline_handle_byte(buf[i]);
}
static void monitor_start_input(void);
static void monitor_handle_command1(void *opaque, const char *cmdline)
{
monitor_handle_command(cmdline);
monitor_start_input();
}
static void monitor_start_input(void)
{
readline_start("(VTXen) ", 0, monitor_handle_command1, NULL);
}
void monitor_init(CharDriverState *hd, int show_banner)
{
monitor_hd = hd;
if (show_banner) {
term_printf("VMX device model. type 'q' to exit\n");
}
qemu_chr_add_read_handler(hd, term_can_read, term_read, NULL);
monitor_start_input();
}