Index: qemu/vl.c =================================================================== --- qemu.orig/vl.c +++ qemu/vl.c @@ -2312,6 +2312,7 @@ typedef struct { int fd, listen_fd; int connected; int max_size; + int do_telnetopt; } TCPCharDriver; static void tcp_chr_accept(void *opaque); @@ -2337,6 +2338,56 @@ static int tcp_chr_read_poll(void *opaqu return s->max_size; } +#define IAC 255 +#define IAC_BREAK 243 +static void tcp_chr_process_IAC_bytes(CharDriverState *chr, + TCPCharDriver *s, + char *buf, int *size) +{ + /* Handle any telnet client's basic IAC options to satisfy char by + * char mode with no echo. All IAC options will be removed from + * the buf and the do_telnetopt variable will be used to track the + * state of the width of the IAC information. + * + * IAC commands come in sets of 3 bytes with the exception of the + * "IAC BREAK" command and the double IAC. + */ + + int i; + int j = 0; + + for (i = 0; i < *size; i++) { + if (s->do_telnetopt > 1) { + if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) { + /* Double IAC means send an IAC */ + if (j != i) + buf[j] = buf[i]; + j++; + s->do_telnetopt = 1; + } else { + if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) { + /* Handle IAC break commands by sending a serial break */ + chr->chr_event(s->fd_opaque, CHR_EVENT_BREAK); + s->do_telnetopt++; + } + s->do_telnetopt++; + } + if (s->do_telnetopt >= 4) { + s->do_telnetopt = 1; + } + } else { + if ((unsigned char)buf[i] == IAC) { + s->do_telnetopt = 2; + } else { + if (j != i) + buf[j] = buf[i]; + j++; + } + } + } + *size = j; +} + static void tcp_chr_read(void *opaque) { CharDriverState *chr = opaque; @@ -2360,7 +2411,10 @@ static void tcp_chr_read(void *opaque) closesocket(s->fd); s->fd = -1; } else if (size > 0) { - s->fd_read(s->fd_opaque, buf, size); + if (s->do_telnetopt) + tcp_chr_process_IAC_bytes(chr, s, buf, &size); + if (size > 0) + s->fd_read(s->fd_opaque, buf, size); } } @@ -2385,6 +2439,20 @@ static void tcp_chr_connect(void *opaque tcp_chr_read, NULL, chr); } +static void tcp_chr_telnet_init(int fd) +{ + char buf[3]; + /* Send the telnet negotion to put telnet in binary, no echo, single char mode */ + sprintf(buf,"%c%c%c",0xff, 0xfb, 0x01); /* IAC WILL ECHO */ + send(fd, (char *)buf, 3, 0); + sprintf(buf,"%c%c%c",0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */ + send(fd, (char *)buf, 3, 0); + sprintf(buf,"%c%c%c",0xff, 0xfb, 0x00); /* IAC WILL Binary */ + send(fd, (char *)buf, 3, 0); + sprintf(buf,"%c%c%c",0xff, 0xfd, 0x00); /* IAC DO Binary */ + send(fd, (char *)buf, 3, 0); +} + static void tcp_chr_accept(void *opaque) { CharDriverState *chr = opaque; @@ -2399,6 +2467,8 @@ static void tcp_chr_accept(void *opaque) if (fd < 0 && errno != EINTR) { return; } else if (fd >= 0) { + if (s->do_telnetopt) + tcp_chr_telnet_init(fd); break; } } @@ -2419,15 +2489,30 @@ static void tcp_chr_close(CharDriverStat } static CharDriverState *qemu_chr_open_tcp(const char *host_str, - int is_listen) + int is_listen, + int is_waitconnect) { CharDriverState *chr = NULL; TCPCharDriver *s = NULL; int fd = -1, ret, err, val; struct sockaddr_in saddr; - if (parse_host_port(&saddr, host_str) < 0) - goto fail; + if (parse_host_port(&saddr, host_str) < 0) { + if (!strchr(host_str, ':')) { + unsigned long port; + char *r; + port = strtol(host_str, (char **)&r, 0); + if (r == host_str) { + fprintf(stderr, "Error parsing port number\n"); + goto fail; + } + + memset(&saddr,0,sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons((short)port); + } else + goto fail; + } chr = qemu_mallocz(sizeof(CharDriverState)); if (!chr) @@ -2439,7 +2524,8 @@ static CharDriverState *qemu_chr_open_tc fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) goto fail; - socket_set_nonblock(fd); + if (!is_waitconnect) + socket_set_nonblock(fd); s->connected = 0; s->fd = -1; @@ -2457,6 +2543,8 @@ static CharDriverState *qemu_chr_open_tc goto fail; s->listen_fd = fd; qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); + /* If is_listen > 1 then turn on telnet option negotiation */ + s->do_telnetopt = 1; } else { for(;;) { ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); @@ -2484,6 +2572,12 @@ static CharDriverState *qemu_chr_open_tc chr->chr_write = tcp_chr_write; chr->chr_add_read_handler = tcp_chr_add_read_handler; chr->chr_close = tcp_chr_close; + if (is_waitconnect) { + printf("QEMU waiting for connection on: %s\n", host_str); + tcp_chr_accept(chr); + socket_set_nonblock(s->listen_fd); + } + return chr; fail: if (fd >= 0) @@ -2503,10 +2597,19 @@ CharDriverState *qemu_chr_open(const cha return qemu_chr_open_null(); } else if (strstart(filename, "tcp:", &p)) { - return qemu_chr_open_tcp(p, 0); + return qemu_chr_open_tcp(p, 0, 0); } else if (strstart(filename, "tcpl:", &p)) { - return qemu_chr_open_tcp(p, 1); + return qemu_chr_open_tcp(p, 1, 0); + } else + if (strstart(filename, "wtcpl:", &p)) { + return qemu_chr_open_tcp(p, 1, 1); + } else + if (strstart(filename, "telnet:", &p)) { + return qemu_chr_open_tcp(p, 2, 0); + } else + if (strstart(filename, "wtelnet:", &p)) { + return qemu_chr_open_tcp(p, 2, 1); } else if (strstart(filename, "udp:", &p)) { return qemu_chr_open_udp(p); Index: qemu/qemu-doc.texi =================================================================== --- qemu.orig/qemu-doc.texi +++ qemu/qemu-doc.texi @@ -562,6 +562,21 @@ TCP Net Console: wait for connection on @var{port}. If host is omitted, 0.0.0.0 is assumed. Only one TCP connection at a time is accepted. You can use @code{telnet} to connect to the corresponding character device. +@item wtcpl:host:port +TCP Net Console: Same as "tcpl", but pause QEMU infinitely waiting for +the first connection + +@item telnet:host:port +TCP Net Console: Use telnet option negotiation to put telnet into +character mode. This will also allow you to send the MAGIC_SYSRQ +sequence if you use a telnet that supports sending the break sequence. +Typically in unix telnet you do it with Control-] and then type "send +break" followed by pressing the enter key. +@item wtelnet:host:port +TCP Net Console: Same as "telnet", but pause QEMU infinitely waiting for +the first connection + + @end table @item -parallel dev