aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>2007-02-18 17:04:49 +0000
committerths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>2007-02-18 17:04:49 +0000
commit20d8a3edb062c96f9a08ccf0637f76ae2563c5e1 (patch)
tree1b9a0aa58961c3c2de88f899acc7e120a8150c73
parent925fd0f202e430fc18e1e4986cc066ea44504c9e (diff)
Monitor multiplexing, by Jason Wessel.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2434 c046a42c-6fe2-441c-8c8c-71466251a162
-rw-r--r--monitor.c32
-rw-r--r--qemu-doc.texi27
-rw-r--r--vl.c441
-rw-r--r--vl.h1
4 files changed, 335 insertions, 166 deletions
diff --git a/monitor.c b/monitor.c
index 1e9b904bf5..cd0663e937 100644
--- a/monitor.c
+++ b/monitor.c
@@ -54,7 +54,8 @@ typedef struct term_cmd_t {
const char *help;
} term_cmd_t;
-static CharDriverState *monitor_hd;
+#define MAX_MON 4
+static CharDriverState *monitor_hd[MAX_MON];
static int hide_banner;
static term_cmd_t term_cmds[];
@@ -69,8 +70,11 @@ CPUState *mon_cpu = NULL;
void term_flush(void)
{
+ int i;
if (term_outbuf_index > 0) {
- qemu_chr_write(monitor_hd, term_outbuf, term_outbuf_index);
+ for (i = 0; i < MAX_MON; i++)
+ if (monitor_hd[i] && monitor_hd[i]->focus == 0)
+ qemu_chr_write(monitor_hd[i], term_outbuf, term_outbuf_index);
term_outbuf_index = 0;
}
}
@@ -2452,9 +2456,25 @@ static void term_event(void *opaque, int event)
monitor_start_input();
}
+static int is_first_init = 1;
+
void monitor_init(CharDriverState *hd, int show_banner)
{
- monitor_hd = hd;
+ int i;
+
+ if (is_first_init) {
+ for (i = 0; i < MAX_MON; i++) {
+ monitor_hd[i] = NULL;
+ }
+ is_first_init = 0;
+ }
+ for (i = 0; i < MAX_MON; i++) {
+ if (monitor_hd[i] == NULL) {
+ monitor_hd[i] = hd;
+ break;
+ }
+ }
+
hide_banner = !show_banner;
qemu_chr_add_handlers(hd, term_can_read, term_read, term_event, NULL);
@@ -2475,8 +2495,12 @@ static void monitor_readline_cb(void *opaque, const char *input)
void monitor_readline(const char *prompt, int is_password,
char *buf, int buf_size)
{
+ int i;
+
if (is_password) {
- qemu_chr_send_event(monitor_hd, CHR_EVENT_FOCUS);
+ for (i = 0; i < MAX_MON; i++)
+ if (monitor_hd[i] && monitor_hd[i]->focus == 0)
+ qemu_chr_send_event(monitor_hd[i], CHR_EVENT_FOCUS);
}
readline_start(prompt, is_password, monitor_readline_cb, NULL);
monitor_readline_buf = buf;
diff --git a/qemu-doc.texi b/qemu-doc.texi
index a4c99708e9..b539747dfe 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -610,6 +610,18 @@ A unix domain socket is used instead of a tcp socket. The option works the
same as if you had specified @code{-serial tcp} except the unix domain socket
@var{path} is used for connections.
+@item mon:dev_string
+This is a special option to allow the monitor to be multiplexed onto
+another serial port. The monitor is accessed with key sequence of
+@key{Control-a} and then pressing @key{c}. See monitor access
+@ref{pcsys_keys} in the -nographic section for more keys.
+@var{dev_string} should be any one of the serial devices specified
+above. An example to multiplex the monitor onto a telnet server
+listening on port 4444 would be:
+@table @code
+@item -serial mon:telnet::4444,server,nowait
+@end table
+
@end table
@item -parallel dev
@@ -629,6 +641,19 @@ serial port).
The default device is @code{vc} in graphical mode and @code{stdio} in
non graphical mode.
+@item -echr numeric_ascii_value
+Change the escape character used for switching to the monitor when using
+monitor and serial sharing. The default is @code{0x01} when using the
+@code{-nographic} option. @code{0x01} is equal to pressing
+@code{Control-a}. You can select a different character from the ascii
+control keys where 1 through 26 map to Control-a through Control-z. For
+instance you could use the either of the following to change the escape
+character to Control-t.
+@table @code
+@item -echr 0x14
+@item -echr 20
+@end table
+
@item -s
Wait gdb connection to port 1234 (@pxref{gdb_usage}).
@item -p port
@@ -711,6 +736,8 @@ Print this help
Exit emulator
@item Ctrl-a s
Save disk data back to file (if -snapshot)
+@item Ctrl-a t
+toggle console timestamps
@item Ctrl-a b
Send break (magic sysrq in Linux)
@item Ctrl-a c
diff --git a/vl.c b/vl.c
index 0a527a77a9..de53b7219e 100644
--- a/vl.c
+++ b/vl.c
@@ -1228,6 +1228,218 @@ static CharDriverState *qemu_chr_open_null(void)
return chr;
}
+/* MUX driver for serial I/O splitting */
+static int term_timestamps;
+static int64_t term_timestamps_start;
+#define MAX_MUX 2
+typedef struct {
+ IOCanRWHandler *chr_can_read[MAX_MUX];
+ IOReadHandler *chr_read[MAX_MUX];
+ IOEventHandler *chr_event[MAX_MUX];
+ void *ext_opaque[MAX_MUX];
+ CharDriverState *drv;
+ int mux_cnt;
+ int term_got_escape;
+ int max_size;
+} MuxDriver;
+
+
+static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ MuxDriver *d = chr->opaque;
+ int ret;
+ if (!term_timestamps) {
+ ret = d->drv->chr_write(d->drv, buf, len);
+ } else {
+ int i;
+
+ ret = 0;
+ for(i = 0; i < len; i++) {
+ ret += d->drv->chr_write(d->drv, buf+i, 1);
+ if (buf[i] == '\n') {
+ char buf1[64];
+ int64_t ti;
+ int secs;
+
+ ti = get_clock();
+ if (term_timestamps_start == -1)
+ term_timestamps_start = ti;
+ ti -= term_timestamps_start;
+ secs = ti / 1000000000;
+ snprintf(buf1, sizeof(buf1),
+ "[%02d:%02d:%02d.%03d] ",
+ secs / 3600,
+ (secs / 60) % 60,
+ secs % 60,
+ (int)((ti / 1000000) % 1000));
+ d->drv->chr_write(d->drv, buf1, strlen(buf1));
+ }
+ }
+ }
+ return ret;
+}
+
+static char *mux_help[] = {
+ "% h print this help\n\r",
+ "% x exit emulator\n\r",
+ "% s save disk data back to file (if -snapshot)\n\r",
+ "% t toggle console timestamps\n\r"
+ "% b send break (magic sysrq)\n\r",
+ "% c switch between console and monitor\n\r",
+ "% % sends %\n\r",
+ NULL
+};
+
+static int term_escape_char = 0x01; /* ctrl-a is used for escape */
+static void mux_print_help(CharDriverState *chr)
+{
+ int i, j;
+ char ebuf[15] = "Escape-Char";
+ char cbuf[50] = "\n\r";
+
+ if (term_escape_char > 0 && term_escape_char < 26) {
+ sprintf(cbuf,"\n\r");
+ sprintf(ebuf,"C-%c", term_escape_char - 1 + 'a');
+ } else {
+ sprintf(cbuf,"\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r", term_escape_char);
+ }
+ chr->chr_write(chr, cbuf, strlen(cbuf));
+ for (i = 0; mux_help[i] != NULL; i++) {
+ for (j=0; mux_help[i][j] != '\0'; j++) {
+ if (mux_help[i][j] == '%')
+ chr->chr_write(chr, ebuf, strlen(ebuf));
+ else
+ chr->chr_write(chr, &mux_help[i][j], 1);
+ }
+ }
+}
+
+static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
+{
+ if (d->term_got_escape) {
+ d->term_got_escape = 0;
+ if (ch == term_escape_char)
+ goto send_char;
+ switch(ch) {
+ case '?':
+ case 'h':
+ mux_print_help(chr);
+ break;
+ case 'x':
+ {
+ char *term = "QEMU: Terminated\n\r";
+ chr->chr_write(chr,term,strlen(term));
+ 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 (chr->chr_event)
+ chr->chr_event(chr->opaque, CHR_EVENT_BREAK);
+ break;
+ case 'c':
+ /* Switch to the next registered device */
+ chr->focus++;
+ if (chr->focus >= d->mux_cnt)
+ chr->focus = 0;
+ break;
+ case 't':
+ term_timestamps = !term_timestamps;
+ term_timestamps_start = -1;
+ break;
+ }
+ } else if (ch == term_escape_char) {
+ d->term_got_escape = 1;
+ } else {
+ send_char:
+ return 1;
+ }
+ return 0;
+}
+
+static int mux_chr_can_read(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ MuxDriver *d = chr->opaque;
+ if (d->chr_can_read[chr->focus])
+ return d->chr_can_read[chr->focus](d->ext_opaque[chr->focus]);
+ return 0;
+}
+
+static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
+{
+ CharDriverState *chr = opaque;
+ MuxDriver *d = chr->opaque;
+ int i;
+ for(i = 0; i < size; i++)
+ if (mux_proc_byte(chr, d, buf[i]))
+ d->chr_read[chr->focus](d->ext_opaque[chr->focus], &buf[i], 1);
+}
+
+static void mux_chr_event(void *opaque, int event)
+{
+ CharDriverState *chr = opaque;
+ MuxDriver *d = chr->opaque;
+ int i;
+
+ /* Send the event to all registered listeners */
+ for (i = 0; i < d->mux_cnt; i++)
+ if (d->chr_event[i])
+ d->chr_event[i](d->ext_opaque[i], event);
+}
+
+static void mux_chr_update_read_handler(CharDriverState *chr)
+{
+ MuxDriver *d = chr->opaque;
+
+ if (d->mux_cnt >= MAX_MUX) {
+ fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n");
+ return;
+ }
+ d->ext_opaque[d->mux_cnt] = chr->handler_opaque;
+ d->chr_can_read[d->mux_cnt] = chr->chr_can_read;
+ d->chr_read[d->mux_cnt] = chr->chr_read;
+ d->chr_event[d->mux_cnt] = chr->chr_event;
+ /* Fix up the real driver with mux routines */
+ if (d->mux_cnt == 0) {
+ qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read,
+ mux_chr_event, chr);
+ }
+ chr->focus = d->mux_cnt;
+ d->mux_cnt++;
+}
+
+CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
+{
+ CharDriverState *chr;
+ MuxDriver *d;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ d = qemu_mallocz(sizeof(MuxDriver));
+ if (!d) {
+ free(chr);
+ return NULL;
+ }
+
+ chr->opaque = d;
+ d->drv = drv;
+ chr->focus = -1;
+ chr->chr_write = mux_chr_write;
+ chr->chr_update_read_handler = mux_chr_update_read_handler;
+ return chr;
+}
+
+
#ifdef _WIN32
static void socket_cleanup(void)
@@ -1319,10 +1531,8 @@ typedef struct {
int max_size;
} FDCharDriver;
-#define STDIO_MAX_CLIENTS 2
-
-static int stdio_nb_clients;
-static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS];
+#define STDIO_MAX_CLIENTS 1
+static int stdio_nb_clients = 0;
static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
@@ -1435,162 +1645,45 @@ static CharDriverState *qemu_chr_open_pipe(const char *filename)
/* for STDIO, we handle the case where several clients use it
(nographic mode) */
-#define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */
-
#define TERM_FIFO_MAX_SIZE 1
-static int term_got_escape, client_index;
static uint8_t term_fifo[TERM_FIFO_MAX_SIZE];
static int term_fifo_size;
-static int term_timestamps;
-static int64_t term_timestamps_start;
-
-void term_print_help(void)
-{
- printf("\n"
- "C-a h print this help\n"
- "C-a x exit emulator\n"
- "C-a s save disk data back to file (if -snapshot)\n"
- "C-a b send break (magic sysrq)\n"
- "C-a t toggle console timestamps\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 stdio_received_byte(int ch)
-{
- 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 (client_index < stdio_nb_clients) {
- CharDriverState *chr;
- FDCharDriver *s;
-
- chr = stdio_clients[client_index];
- s = chr->opaque;
- qemu_chr_event(chr, CHR_EVENT_BREAK);
- }
- break;
- case 'c':
- client_index++;
- if (client_index >= stdio_nb_clients)
- client_index = 0;
- if (client_index == 0) {
- /* send a new line in the monitor to get the prompt */
- ch = '\r';
- goto send_char;
- }
- break;
- case 't':
- term_timestamps = !term_timestamps;
- term_timestamps_start = -1;
- break;
- case TERM_ESCAPE:
- goto send_char;
- }
- } else if (ch == TERM_ESCAPE) {
- term_got_escape = 1;
- } else {
- send_char:
- if (client_index < stdio_nb_clients) {
- uint8_t buf[1];
- CharDriverState *chr;
-
- chr = stdio_clients[client_index];
- if (qemu_chr_can_read(chr) > 0) {
- buf[0] = ch;
- qemu_chr_read(chr, buf, 1);
- } else if (term_fifo_size == 0) {
- term_fifo[term_fifo_size++] = ch;
- }
- }
- }
-}
static int stdio_read_poll(void *opaque)
{
- CharDriverState *chr;
+ CharDriverState *chr = opaque;
- if (client_index < stdio_nb_clients) {
- chr = stdio_clients[client_index];
- /* try to flush the queue if needed */
- if (term_fifo_size != 0 && qemu_chr_can_read(chr) > 0) {
- qemu_chr_read(chr, term_fifo, 1);
- term_fifo_size = 0;
- }
- /* see if we can absorb more chars */
- if (term_fifo_size == 0)
- return 1;
- else
- return 0;
- } else {
- return 1;
+ /* try to flush the queue if needed */
+ if (term_fifo_size != 0 && qemu_chr_can_read(chr) > 0) {
+ qemu_chr_read(chr, term_fifo, 1);
+ term_fifo_size = 0;
}
+ /* see if we can absorb more chars */
+ if (term_fifo_size == 0)
+ return 1;
+ else
+ return 0;
}
static void stdio_read(void *opaque)
{
int size;
uint8_t buf[1];
-
+ CharDriverState *chr = opaque;
+
size = read(0, buf, 1);
if (size == 0) {
/* stdin has been closed. Remove it from the active list. */
qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL);
return;
}
- if (size > 0)
- stdio_received_byte(buf[0]);
-}
-
-static int stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
-{
- FDCharDriver *s = chr->opaque;
- if (!term_timestamps) {
- return unix_write(s->fd_out, buf, len);
- } else {
- int i;
- char buf1[64];
-
- for(i = 0; i < len; i++) {
- unix_write(s->fd_out, buf + i, 1);
- if (buf[i] == '\n') {
- int64_t ti;
- int secs;
-
- ti = get_clock();
- if (term_timestamps_start == -1)
- term_timestamps_start = ti;
- ti -= term_timestamps_start;
- secs = ti / 1000000000;
- snprintf(buf1, sizeof(buf1),
- "[%02d:%02d:%02d.%03d] ",
- secs / 3600,
- (secs / 60) % 60,
- secs % 60,
- (int)((ti / 1000000) % 1000));
- unix_write(s->fd_out, buf1, strlen(buf1));
- }
+ if (size > 0) {
+ if (qemu_chr_can_read(chr) > 0) {
+ qemu_chr_read(chr, buf, 1);
+ } else if (term_fifo_size == 0) {
+ term_fifo[term_fifo_size++] = buf[0];
}
- return len;
}
}
@@ -1635,24 +1728,13 @@ static CharDriverState *qemu_chr_open_stdio(void)
{
CharDriverState *chr;
- if (nographic) {
- if (stdio_nb_clients >= STDIO_MAX_CLIENTS)
- return NULL;
- chr = qemu_chr_open_fd(0, 1);
- chr->chr_write = stdio_write;
- if (stdio_nb_clients == 0)
- qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, NULL);
- client_index = stdio_nb_clients;
- } else {
- if (stdio_nb_clients != 0)
- return NULL;
- chr = qemu_chr_open_fd(0, 1);
- }
- stdio_clients[stdio_nb_clients++] = chr;
- if (stdio_nb_clients == 1) {
- /* set the terminal in raw mode */
- term_init();
- }
+ if (stdio_nb_clients >= STDIO_MAX_CLIENTS)
+ return NULL;
+ chr = qemu_chr_open_fd(0, 1);
+ qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr);
+ stdio_nb_clients++;
+ term_init();
+
return chr;
}
@@ -2815,6 +2897,16 @@ CharDriverState *qemu_chr_open(const char *filename)
if (strstart(filename, "udp:", &p)) {
return qemu_chr_open_udp(p);
} else
+ if (strstart(filename, "mon:", &p)) {
+ CharDriverState *drv = qemu_chr_open(p);
+ if (drv) {
+ drv = qemu_chr_open_mux(drv);
+ monitor_init(drv, !nographic);
+ return drv;
+ }
+ printf("Unable to open driver: %s\n", p);
+ return 0;
+ } else
#ifndef _WIN32
if (strstart(filename, "unix:", &p)) {
return qemu_chr_open_tcp(p, 0, 1);
@@ -6416,6 +6508,7 @@ enum {
QEMU_OPTION_cirrusvga,
QEMU_OPTION_g,
QEMU_OPTION_std_vga,
+ QEMU_OPTION_echr,
QEMU_OPTION_monitor,
QEMU_OPTION_serial,
QEMU_OPTION_parallel,
@@ -6497,6 +6590,7 @@ const QEMUOption qemu_options[] = {
#endif
{ "localtime", 0, QEMU_OPTION_localtime },
{ "std-vga", 0, QEMU_OPTION_std_vga },
+ { "echr", 1, QEMU_OPTION_echr },
{ "monitor", 1, QEMU_OPTION_monitor },
{ "serial", 1, QEMU_OPTION_serial },
{ "parallel", 1, QEMU_OPTION_parallel },
@@ -6932,8 +7026,8 @@ int main(int argc, char **argv)
}
break;
case QEMU_OPTION_nographic:
- pstrcpy(monitor_device, sizeof(monitor_device), "stdio");
pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "stdio");
+ pstrcpy(monitor_device, sizeof(monitor_device), "stdio");
nographic = 1;
break;
case QEMU_OPTION_kernel:
@@ -7094,6 +7188,14 @@ int main(int argc, char **argv)
graphic_depth = depth;
}
break;
+ case QEMU_OPTION_echr:
+ {
+ char *r;
+ term_escape_char = strtol(optarg, &r, 0);
+ if (r == optarg)
+ printf("Bad argument to echr\n");
+ break;
+ }
case QEMU_OPTION_monitor:
pstrcpy(monitor_device, sizeof(monitor_device), optarg);
break;
@@ -7384,12 +7486,27 @@ int main(int argc, char **argv)
#endif
}
- monitor_hd = qemu_chr_open(monitor_device);
- if (!monitor_hd) {
- fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device);
- exit(1);
+ /* Maintain compatibility with multiple stdio monitors */
+ if (!strcmp(monitor_device,"stdio")) {
+ for (i = 0; i < MAX_SERIAL_PORTS; i++) {
+ if (!strcmp(serial_devices[i],"mon:stdio")) {
+ monitor_device[0] = '\0';
+ break;
+ } else if (!strcmp(serial_devices[i],"stdio")) {
+ monitor_device[0] = '\0';
+ pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "mon:stdio");
+ break;
+ }
+ }
+ }
+ if (monitor_device[0] != '\0') {
+ monitor_hd = qemu_chr_open(monitor_device);
+ if (!monitor_hd) {
+ fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device);
+ exit(1);
+ }
+ monitor_init(monitor_hd, !nographic);
}
- monitor_init(monitor_hd, !nographic);
for(i = 0; i < MAX_SERIAL_PORTS; i++) {
const char *devname = serial_devices[i];
diff --git a/vl.h b/vl.h
index 79de55fa02..bcaf004dd0 100644
--- a/vl.h
+++ b/vl.h
@@ -303,6 +303,7 @@ typedef struct CharDriverState {
void (*chr_send_event)(struct CharDriverState *chr, int event);
void (*chr_close)(struct CharDriverState *chr);
void *opaque;
+ int focus;
QEMUBH *bh;
} CharDriverState;