From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Fus4P-0004w6-VL for qemu-devel@nongnu.org; Mon, 26 Jun 2006 10:28:10 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Fus4O-0004vu-E3 for qemu-devel@nongnu.org; Mon, 26 Jun 2006 10:28:09 -0400 Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Fus4O-0004vq-6p for qemu-devel@nongnu.org; Mon, 26 Jun 2006 10:28:08 -0400 Received: from [147.11.1.11] (helo=mail.wrs.com) by monty-python.gnu.org with esmtp (Exim 4.52) id 1FusGG-0001pa-Me for qemu-devel@nongnu.org; Mon, 26 Jun 2006 10:40:25 -0400 Message-ID: <449FEEDE.8070503@windriver.com> Date: Mon, 26 Jun 2006 09:27:42 -0500 From: Jason Wessel MIME-Version: 1.0 Subject: Re: [Qemu-devel] qemu vl.c qemu-doc.texi References: In-Reply-To: Content-Type: multipart/mixed; boundary="------------050109050900010402070708" Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org, fabrice@bellard.org This is a multi-part message in MIME format. --------------050109050900010402070708 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Hi Fabrice, We ought to collaborate more about the intent to code something. I had already implemented the TCP net console as well. Since the code was similar I patched in the difference in functionality from my code into yours as well as further changing the docs. I fixed a small defect in the TCP net console where it did not accept only a port for the "tcpl" option. I added the following features with the attached patch which I had been using in my TCP net console version. wtcpl - Wait infinitely for the first connection so that you can get the console from the very start telnet - This allows you to fully make use of telnet in "char by char" mode. It also supports sending the telnet break which translates to sending a serial break just like you would do if you used a terminal server. This is frequently used to activate MAGIC_SYSRQ support in a kernel. wtelnet - Same as telnet, but wait infinitely for the first connect. The reason for having a separate tcpl vs telnet is to separate out the IAC option negotiation because it can mess up clients that are not expecting it. Question: If I resubmit the -mserial option would it stand a chance of being accepted? Please let me know if there is some change you might like to get that patch accepted as well. I will re-create the patch against the current CVS anyway because I still need the functionality of having the monitor and serial port redirected to the same remote socket. Thanks, Jason. Fabrice Bellard wrote: > CVSROOT: /sources/qemu > Module name: qemu > Changes by: Fabrice Bellard 06/06/25 14:49:44 > > Modified files: > . : vl.c qemu-doc.texi > > Log message: > UDP char device (initial patch by Jason Wessel) - TCP char device > > CVSWeb URLs: > http://cvs.savannah.gnu.org/viewcvs/qemu/vl.c?cvsroot=qemu&r1=1.190&r2=1.191 > http://cvs.savannah.gnu.org/viewcvs/qemu/qemu-doc.texi?cvsroot=qemu&r1=1.96&r2=1.97 > > > _______________________________________________ > Qemu-devel mailing list > Qemu-devel@nongnu.org > http://lists.nongnu.org/mailman/listinfo/qemu-devel > --------------050109050900010402070708 Content-Type: text/plain; name="telnet_IAC_options.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="telnet_IAC_options.patch" 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 --------------050109050900010402070708--