qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] qemu vl.c qemu-doc.texi


From: Jason Wessel
Subject: Re: [Qemu-devel] qemu vl.c qemu-doc.texi
Date: Mon, 26 Jun 2006 09:27:42 -0500
User-agent: Thunderbird 1.5.0.2 (Windows/20060308)

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 <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
address@hidden
http://lists.nongnu.org/mailman/listinfo/qemu-devel

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.
address@hidden wtcpl:host:port
+TCP Net Console: Same as "tcpl", but pause QEMU infinitely waiting for
+the first connection
+
address@hidden 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.
address@hidden wtelnet:host:port
+TCP Net Console: Same as "telnet", but pause QEMU infinitely waiting for
+the first connection
+
+
 @end table
 
 @item -parallel dev

reply via email to

[Prev in Thread] Current Thread [Next in Thread]