qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 4/4] sockets: switch over tcp/telnet/unix serial line to new helper functions.
  2008-10-28 12:55 [Qemu-devel] [PATCH 0/4] ipv6 and autoport patches Gerd Hoffmann
@ 2008-10-28 12:55 ` Gerd Hoffmann
  0 siblings, 0 replies; 12+ messages in thread
From: Gerd Hoffmann @ 2008-10-28 12:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This switches the tcp, telnet and unix socket support for character
devices (serial/parallel, ...) to the new socket helpers.  Thereby they
gain IPv6 support and also get ability to search for a free tcp port.
Syntax is the same as for vnc, using a to= option, like this:

	-serial tcp:localhost:5000,to=5099,server

This will check the 5000 -> 5099 port range (inclusive) for a free tcp
port.  Likewise you can get auto-allocated unix sockets by specifying an
empty path:

	-serial unix:,server

qemu will create a randomly named socket in $TMPDIR then.  You can use
the "info chardev" command added by the first patch in this series to
figure the tcp port / unix socket actually allocated.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 vl.c |  129 ++++++++++++++++-------------------------------------------------
 1 files changed, 32 insertions(+), 97 deletions(-)

diff --git a/vl.c b/vl.c
index 2112585..7d45c7f 100644
--- a/vl.c
+++ b/vl.c
@@ -3370,9 +3370,6 @@ static void udp_chr_update_read_handler(CharDriverState *chr)
     }
 }
 
-#ifndef _WIN32
-static int parse_unix_path(struct sockaddr_un *uaddr, const char *str);
-#endif
 int parse_host_src_port(struct sockaddr_in *haddr,
                         struct sockaddr_in *saddr,
                         const char *str);
@@ -3629,32 +3626,11 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
 {
     CharDriverState *chr = NULL;
     TCPCharDriver *s = NULL;
-    int fd = -1, ret, err, val;
+    int fd = -1, offset = 0;
     int is_listen = 0;
     int is_waitconnect = 1;
     int do_nodelay = 0;
     const char *ptr;
-    struct sockaddr_in saddr;
-#ifndef _WIN32
-    struct sockaddr_un uaddr;
-#endif
-    struct sockaddr *addr;
-    socklen_t addrlen;
-
-#ifndef _WIN32
-    if (is_unix) {
-	addr = (struct sockaddr *)&uaddr;
-	addrlen = sizeof(uaddr);
-	if (parse_unix_path(&uaddr, host_str) < 0)
-	    goto fail;
-    } else
-#endif
-    {
-	addr = (struct sockaddr *)&saddr;
-	addrlen = sizeof(saddr);
-	if (parse_host_port(&saddr, host_str) < 0)
-	    goto fail;
-    }
 
     ptr = host_str;
     while((ptr = strchr(ptr,','))) {
@@ -3665,6 +3641,8 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
             is_waitconnect = 0;
         } else if (!strncmp(ptr,"nodelay",6)) {
             do_nodelay = 1;
+        } else if (!strncmp(ptr,"to=",3)) {
+            /* nothing, inet_listen() parses this one */;
         } else {
             printf("Unknown option: %s\n", ptr);
             goto fail;
@@ -3680,13 +3658,31 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
     if (!s)
         goto fail;
 
-#ifndef _WIN32
-    if (is_unix)
-	fd = socket(PF_UNIX, SOCK_STREAM, 0);
-    else
-#endif
-	fd = socket(PF_INET, SOCK_STREAM, 0);
-
+    if (is_listen) {
+        chr->filename = qemu_malloc(256);
+        if (is_unix) {
+            strcpy(chr->filename, "unix:");
+        } else if (is_telnet) {
+            strcpy(chr->filename, "telnet:");
+        } else {
+            strcpy(chr->filename, "tcp:");
+        }
+        offset = strlen(chr->filename);
+    }
+    if (is_unix) {
+        if (is_listen) {
+            fd = unix_listen(host_str, chr->filename + offset, 256 - offset);
+        } else {
+            fd = unix_connect(host_str);
+        }
+    } else {
+        if (is_listen) {
+            fd = inet_listen(host_str, chr->filename + offset, 256 - offset,
+                             SOCK_STREAM, 0);
+        } else {
+            fd = inet_connect(host_str, SOCK_STREAM);
+        }
+    }
     if (fd < 0)
         goto fail;
 
@@ -3704,61 +3700,20 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
     chr->chr_close = tcp_chr_close;
 
     if (is_listen) {
-        /* allow fast reuse */
-#ifndef _WIN32
-	if (is_unix) {
-	    char path[109];
-	    pstrcpy(path, sizeof(path), uaddr.sun_path);
-	    unlink(path);
-	} else
-#endif
-	{
-	    val = 1;
-	    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
-	}
-
-        ret = bind(fd, addr, addrlen);
-        if (ret < 0)
-            goto fail;
-
-        ret = listen(fd, 0);
-        if (ret < 0)
-            goto fail;
-
         s->listen_fd = fd;
         qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
         if (is_telnet)
             s->do_telnetopt = 1;
     } else {
-        for(;;) {
-            ret = connect(fd, addr, addrlen);
-            if (ret < 0) {
-                err = socket_error();
-                if (err == EINTR || err == EWOULDBLOCK) {
-                } else if (err == EINPROGRESS) {
-                    break;
-#ifdef _WIN32
-                } else if (err == WSAEALREADY) {
-                    break;
-#endif
-                } else {
-                    goto fail;
-                }
-            } else {
-                s->connected = 1;
-                break;
-            }
-        }
+        s->connected = 1;
         s->fd = fd;
         socket_set_nodelay(fd);
-        if (s->connected)
-            tcp_chr_connect(chr);
-        else
-            qemu_set_fd_handler(s->fd, NULL, tcp_chr_connect, chr);
+        tcp_chr_connect(chr);
     }
 
     if (is_listen && is_waitconnect) {
-        printf("QEMU waiting for connection on: %s\n", host_str);
+        printf("QEMU waiting for connection on: %s\n",
+               chr->filename ? chr->filename : host_str);
         tcp_chr_accept(chr);
         socket_set_nonblock(s->listen_fd);
     }
@@ -4035,26 +3990,6 @@ int parse_host_port(struct sockaddr_in *saddr, const char *str)
     return 0;
 }
 
-#ifndef _WIN32
-static int parse_unix_path(struct sockaddr_un *uaddr, const char *str)
-{
-    const char *p;
-    int len;
-
-    len = MIN(108, strlen(str));
-    p = strchr(str, ',');
-    if (p)
-	len = MIN(len, p - str);
-
-    memset(uaddr, 0, sizeof(*uaddr));
-
-    uaddr->sun_family = AF_UNIX;
-    memcpy(uaddr->sun_path, str, len);
-
-    return 0;
-}
-#endif
-
 /* find or alloc a new VLAN */
 VLANState *qemu_find_vlan(int id)
 {
-- 
1.5.6.5

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [Qemu-devel] [PATCH v2 0/4] ipv6 and autoport patches.
@ 2008-10-31 12:47 Gerd Hoffmann
  2008-10-31 12:47 ` [Qemu-devel] [PATCH 1/4] Implement "info chardev" command Gerd Hoffmann
                   ` (3 more replies)
  0 siblings, 4 replies; 12+ messages in thread
From: Gerd Hoffmann @ 2008-10-31 12:47 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

  Hi,

This patch series starts adding support for auto-allocating tcp ports
and for ipv6 to qemu.  It adds a new "info chardev" command (needed to
query the auto-allocated ports), adds socket helper functiond with
ipv6 support and finally switches chardevs and vnc over to the new
socket helpers.  Individual patches have more vebose descriptions.

New in v2:
 * address review comments from Daniel P. Berrange and Blue Swirl.
 * fixed windows build (mingw32 crosscompiler).
 * added -ipv4 and -ipv6 switches to the help text.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [Qemu-devel] [PATCH 1/4] Implement "info chardev" command.
  2008-10-31 12:47 [Qemu-devel] [PATCH v2 0/4] ipv6 and autoport patches Gerd Hoffmann
@ 2008-10-31 12:47 ` Gerd Hoffmann
  2008-10-31 17:32   ` Anthony Liguori
  2008-10-31 12:47 ` [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu Gerd Hoffmann
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 12+ messages in thread
From: Gerd Hoffmann @ 2008-10-31 12:47 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This patch makes qemu keep track of the character devices in use and
implements a "info chardev" monitor command to print a list.

qemu_chr_open() sticks the devices into a linked list now.  It got a new
argument (label), so there is a name for each device.  It also assigns a
filename to each character device.  By default it just copyes the
filename passed in.  Individual drivers can fill in something else
though.  qemu_chr_open_pty() sets the filename to name of the pseudo tty
allocated.

Output looks like this:

  (qemu) info chardev
  monitor: filename=unix:/tmp/run.sh-26827/monitor,server,nowait
  serial0: filename=unix:/tmp/run.sh-26827/console,server
  serial1: filename=pty:/dev/pts/5
  parallel0: filename=vc:640x480

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 gdbstub.c       |    2 +-
 hw/usb-serial.c |    5 ++-
 monitor.c       |    2 +
 qemu-char.h     |    7 +++-
 vl.c            |   98 ++++++++++++++++++++++++++++++++++++------------------
 5 files changed, 78 insertions(+), 36 deletions(-)

diff --git a/gdbstub.c b/gdbstub.c
index 1a85eda..15d38f0 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1852,7 +1852,7 @@ int gdbserver_start(const char *port)
         port = gdbstub_port_name;
     }
 
-    chr = qemu_chr_open(port);
+    chr = qemu_chr_open("gdb", port);
     if (!chr)
         return -1;
 
diff --git a/hw/usb-serial.c b/hw/usb-serial.c
index 40d04cb..a6a756d 100644
--- a/hw/usb-serial.c
+++ b/hw/usb-serial.c
@@ -521,6 +521,8 @@ USBDevice *usb_serial_init(const char *filename)
     USBSerialState *s;
     CharDriverState *cdrv;
     unsigned short vendorid = 0x0403, productid = 0x6001;
+    char label[32];
+    static int index;
 
     while (*filename && *filename != ':') {
         const char *p;
@@ -555,7 +557,8 @@ USBDevice *usb_serial_init(const char *filename)
     if (!s)
         return NULL;
 
-    cdrv = qemu_chr_open(filename);
+    snprintf(label, sizeof(label), "usbserial%d", index++);
+    cdrv = qemu_chr_open(label, filename);
     if (!cdrv)
         goto fail;
     s->cs = cdrv;
diff --git a/monitor.c b/monitor.c
index ae034e2..611bde5 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1470,6 +1470,8 @@ static const term_cmd_t info_cmds[] = {
       "", "show the version of qemu" },
     { "network", "", do_info_network,
       "", "show the network state" },
+    { "chardev", "", qemu_chr_info,
+      "", "show the character devices" },
     { "block", "", do_info_block,
       "", "show the block devices" },
     { "blockstats", "", do_info_blockstats,
diff --git a/qemu-char.h b/qemu-char.h
index 05d6899..55d81cb 100644
--- a/qemu-char.h
+++ b/qemu-char.h
@@ -1,6 +1,7 @@
 #ifndef QEMU_CHAR_H
 #define QEMU_CHAR_H
 
+#include "sys-queue.h"
 /* character device */
 
 #define CHR_EVENT_BREAK 0 /* serial break char */
@@ -55,9 +56,12 @@ struct CharDriverState {
     void *opaque;
     int focus;
     QEMUBH *bh;
+    char *label;
+    char *filename;
+    TAILQ_ENTRY(CharDriverState) next;
 };
 
-CharDriverState *qemu_chr_open(const char *filename);
+CharDriverState *qemu_chr_open(const char *label, const char *filename);
 void qemu_chr_close(CharDriverState *chr);
 void qemu_chr_printf(CharDriverState *s, const char *fmt, ...);
 int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len);
@@ -72,6 +76,7 @@ void qemu_chr_reset(CharDriverState *s);
 int qemu_chr_can_read(CharDriverState *s);
 void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len);
 void qemu_chr_accept_input(CharDriverState *s);
+void qemu_chr_info(void);
 
 /* async I/O support */
 
diff --git a/vl.c b/vl.c
index 0278101..a9fae17 100644
--- a/vl.c
+++ b/vl.c
@@ -2582,7 +2582,7 @@ static CharDriverState *qemu_chr_open_pty(void)
     CharDriverState *chr;
     PtyCharDriver *s;
     struct termios tty;
-    int slave_fd;
+    int slave_fd, len;
 #if defined(__OpenBSD__)
     char pty_name[PATH_MAX];
 #define q_ptsname(x) pty_name
@@ -2609,6 +2609,9 @@ static CharDriverState *qemu_chr_open_pty(void)
     tcsetattr(slave_fd, TCSAFLUSH, &tty);
     close(slave_fd);
 
+    len = strlen(q_ptsname(s->fd)) + 5;
+    chr->filename = qemu_malloc(len);
+    snprintf(chr->filename, len, "pty:%s", q_ptsname(s->fd));
     fprintf(stderr, "char device redirected to %s\n", q_ptsname(s->fd));
 
     chr->opaque = s;
@@ -3768,90 +3771,115 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
     return NULL;
 }
 
-CharDriverState *qemu_chr_open(const char *filename)
+static TAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs
+= TAILQ_HEAD_INITIALIZER(chardevs);
+
+CharDriverState *qemu_chr_open(const char *label, const char *filename)
 {
     const char *p;
+    CharDriverState *chr;
 
     if (!strcmp(filename, "vc")) {
-        return text_console_init(&display_state, 0);
-    } else if (strstart(filename, "vc:", &p)) {
-        return text_console_init(&display_state, p);
-    } else if (!strcmp(filename, "null")) {
-        return qemu_chr_open_null();
+        chr = text_console_init(&display_state, 0);
+    } else
+    if (strstart(filename, "vc:", &p)) {
+        chr = text_console_init(&display_state, p);
+    } else
+    if (!strcmp(filename, "null")) {
+        chr = qemu_chr_open_null();
     } else
     if (strstart(filename, "tcp:", &p)) {
-        return qemu_chr_open_tcp(p, 0, 0);
+        chr = qemu_chr_open_tcp(p, 0, 0);
     } else
     if (strstart(filename, "telnet:", &p)) {
-        return qemu_chr_open_tcp(p, 1, 0);
+        chr = qemu_chr_open_tcp(p, 1, 0);
     } else
     if (strstart(filename, "udp:", &p)) {
-        return qemu_chr_open_udp(p);
+        chr = 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;
+        chr = qemu_chr_open(label, p);
+        if (chr) {
+            chr = qemu_chr_open_mux(chr);
+            monitor_init(chr, !nographic);
+        } else {
+            printf("Unable to open driver: %s\n", p);
         }
-        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);
+	chr = qemu_chr_open_tcp(p, 0, 1);
     } else if (strstart(filename, "file:", &p)) {
-        return qemu_chr_open_file_out(p);
+        chr = qemu_chr_open_file_out(p);
     } else if (strstart(filename, "pipe:", &p)) {
-        return qemu_chr_open_pipe(p);
+        chr = qemu_chr_open_pipe(p);
     } else if (!strcmp(filename, "pty")) {
-        return qemu_chr_open_pty();
+        chr = qemu_chr_open_pty();
     } else if (!strcmp(filename, "stdio")) {
-        return qemu_chr_open_stdio();
+        chr = qemu_chr_open_stdio();
     } else
 #if defined(__linux__)
     if (strstart(filename, "/dev/parport", NULL)) {
-        return qemu_chr_open_pp(filename);
+        chr = qemu_chr_open_pp(filename);
     } else
 #endif
 #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
     || defined(__NetBSD__) || defined(__OpenBSD__)
     if (strstart(filename, "/dev/", NULL)) {
-        return qemu_chr_open_tty(filename);
+        chr = qemu_chr_open_tty(filename);
     } else
 #endif
 #else /* !_WIN32 */
     if (strstart(filename, "COM", NULL)) {
-        return qemu_chr_open_win(filename);
+        chr = qemu_chr_open_win(filename);
     } else
     if (strstart(filename, "pipe:", &p)) {
-        return qemu_chr_open_win_pipe(p);
+        chr = qemu_chr_open_win_pipe(p);
     } else
     if (strstart(filename, "con:", NULL)) {
-        return qemu_chr_open_win_con(filename);
+        chr = qemu_chr_open_win_con(filename);
     } else
     if (strstart(filename, "file:", &p)) {
-        return qemu_chr_open_win_file_out(p);
+        chr = qemu_chr_open_win_file_out(p);
     } else
 #endif
 #ifdef CONFIG_BRLAPI
     if (!strcmp(filename, "braille")) {
-        return chr_baum_init();
+        chr = chr_baum_init();
     } else
 #endif
     {
-        return NULL;
+        chr = NULL;
+    }
+
+    if (chr) {
+        if (!chr->filename)
+            chr->filename = qemu_strdup(filename);
+        chr->label = qemu_strdup(label);
+        TAILQ_INSERT_TAIL(&chardevs, chr, next);
     }
+    return chr;
 }
 
 void qemu_chr_close(CharDriverState *chr)
 {
+    TAILQ_REMOVE(&chardevs, chr, next);
     if (chr->chr_close)
         chr->chr_close(chr);
+    qemu_free(chr->filename);
+    qemu_free(chr->label);
     qemu_free(chr);
 }
 
+void qemu_chr_info(void)
+{
+    CharDriverState *chr;
+
+    TAILQ_FOREACH(chr, &chardevs, next) {
+        term_printf("%s: filename=%s\n", chr->label, chr->filename);
+    }
+}
+
 /***********************************************************/
 /* network device redirectors */
 
@@ -9719,7 +9747,7 @@ int main(int argc, char **argv)
         }
     }
     if (monitor_device) {
-        monitor_hd = qemu_chr_open(monitor_device);
+        monitor_hd = qemu_chr_open("monitor", monitor_device);
         if (!monitor_hd) {
             fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device);
             exit(1);
@@ -9730,7 +9758,9 @@ int main(int argc, char **argv)
     for(i = 0; i < MAX_SERIAL_PORTS; i++) {
         const char *devname = serial_devices[i];
         if (devname && strcmp(devname, "none")) {
-            serial_hds[i] = qemu_chr_open(devname);
+            char label[32];
+            snprintf(label, sizeof(label), "serial%d", i);
+            serial_hds[i] = qemu_chr_open(label, devname);
             if (!serial_hds[i]) {
                 fprintf(stderr, "qemu: could not open serial device '%s'\n",
                         devname);
@@ -9744,7 +9774,9 @@ int main(int argc, char **argv)
     for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
         const char *devname = parallel_devices[i];
         if (devname && strcmp(devname, "none")) {
-            parallel_hds[i] = qemu_chr_open(devname);
+            char label[32];
+            snprintf(label, sizeof(label), "parallel%d", i);
+            parallel_hds[i] = qemu_chr_open(label, devname);
             if (!parallel_hds[i]) {
                 fprintf(stderr, "qemu: could not open parallel device '%s'\n",
                         devname);
-- 
1.5.6.5

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
  2008-10-31 12:47 [Qemu-devel] [PATCH v2 0/4] ipv6 and autoport patches Gerd Hoffmann
  2008-10-31 12:47 ` [Qemu-devel] [PATCH 1/4] Implement "info chardev" command Gerd Hoffmann
@ 2008-10-31 12:47 ` Gerd Hoffmann
  2008-10-31 17:37   ` Anthony Liguori
  2008-10-31 12:47 ` [Qemu-devel] [PATCH 3/4] sockets: switch vnc to new code, support vnc port auto-allocation Gerd Hoffmann
  2008-10-31 12:47 ` [Qemu-devel] [PATCH 4/4] sockets: switch over tcp/telnet/unix serial line to new helper functions Gerd Hoffmann
  3 siblings, 1 reply; 12+ messages in thread
From: Gerd Hoffmann @ 2008-10-31 12:47 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This patch creates a new source and header file qemu-sockets.[ch] with
a bunch of helper functions to create listening and connected sockets.

New features of this code are (a) support for searching for a free port
in a given range and (b) support for IPv6.

The following patches put that code into use.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 Makefile.target |    1 +
 qemu-sockets.c  |  409 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-sockets.h  |    8 +
 vl.c            |   16 ++
 4 files changed, 434 insertions(+), 0 deletions(-)
 create mode 100644 qemu-sockets.c
 create mode 100644 qemu-sockets.h

diff --git a/Makefile.target b/Makefile.target
index 547ac02..5f71972 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -580,6 +580,7 @@ ifndef CONFIG_USER_ONLY
 
 OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o net-checksum.o
 OBJS+=fw_cfg.o aio.o buffered_file.o migration.o migration-tcp.o
+OBJS+=qemu-sockets.o
 ifdef CONFIG_WIN32
 OBJS+=block-raw-win32.o
 else
diff --git a/qemu-sockets.c b/qemu-sockets.c
new file mode 100644
index 0000000..9944d42
--- /dev/null
+++ b/qemu-sockets.c
@@ -0,0 +1,409 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef _WIN32
+# define WINVER 0x0501  /* needed for ipv6 bits */
+# include <windows.h>
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# define AI_ADDRCONFIG 0
+# define close closesocket
+#else
+# include <unistd.h>
+# include <errno.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <netinet/in.h>
+# include <netdb.h>
+#endif
+
+#include "qemu-sockets.h"
+
+static int default_family = PF_UNSPEC;
+static int sockets_debug = 0;
+static const int on=1, off=0;
+
+static int inet_getport(struct addrinfo *e)
+{
+    struct sockaddr_in *i4;
+    struct sockaddr_in6 *i6;
+
+    switch (e->ai_family) {
+    case PF_INET6:
+        i6 = (void*)e->ai_addr;
+        return ntohs(i6->sin6_port);
+    case PF_INET:
+        i4 = (void*)e->ai_addr;
+        return ntohs(i4->sin_port);
+    default:
+        return 0;
+    }
+}
+
+static void inet_setport(struct addrinfo *e, int port)
+{
+    struct sockaddr_in *i4;
+    struct sockaddr_in6 *i6;
+
+    switch (e->ai_family) {
+    case PF_INET6:
+        i6 = (void*)e->ai_addr;
+        i6->sin6_port = htons(port);
+        break;
+    case PF_INET:
+        i4 = (void*)e->ai_addr;
+        i4->sin_port = htons(port);
+        break;
+    }
+}
+
+static const char *inet_strfamily(int family)
+{
+    switch (family) {
+    case PF_INET6: return "ipv6";
+    case PF_INET:  return "ipv4";
+    case PF_UNIX:  return "unix";
+    }
+    return "????";
+}
+
+static void inet_print_addrinfo(const char *tag, struct addrinfo *res)
+{
+    struct addrinfo *e;
+    char uaddr[INET6_ADDRSTRLEN+1];
+    char uport[33];
+
+    for (e = res; e != NULL; e = e->ai_next) {
+        getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+                    uaddr,INET6_ADDRSTRLEN,uport,32,
+                    NI_NUMERICHOST | NI_NUMERICSERV);
+        fprintf(stderr,"%s: getaddrinfo: family %s, host %s, port %s\n",
+                tag, inet_strfamily(e->ai_family), uaddr, uport);
+    }
+}
+
+void inet_set_ipv4(void)
+{
+    default_family = PF_INET;
+}
+
+void inet_set_ipv6(void)
+{
+    default_family = PF_INET6;
+}
+
+int inet_listen(const char *str, char *ostr, int olen,
+                int socktype, int port_offset)
+{
+    struct addrinfo ai,*res,*e;
+    char addr[64];
+    char port[33];
+    char uaddr[INET6_ADDRSTRLEN+1];
+    char uport[33];
+    const char *opts, *h;
+    int slisten,rc,pos,to,try_next;
+
+    memset(&ai,0, sizeof(ai));
+    ai.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+    ai.ai_family = default_family;
+    ai.ai_socktype = socktype;
+
+    /* parse string */
+    if (str[0] == ':') {
+        /* no host given */
+        strcpy(addr,"");
+        if (1 != sscanf(str,":%32[^,]%n",port,&pos)) {
+            fprintf(stderr, "%s: portonly parse error (%s)\n",
+                    __FUNCTION__, str);
+            return -1;
+        }
+    } else if (str[0] == '[') {
+        /* IPv6 addr */
+        if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) {
+            fprintf(stderr, "%s: ipv6 parse error (%s)\n",
+                    __FUNCTION__, str);
+            return -1;
+        }
+        ai.ai_family = PF_INET6;
+    } else if (isdigit(str[0])) {
+        /* IPv4 addr */
+        if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) {
+            fprintf(stderr, "%s: ipv4 parse error (%s)\n",
+                    __FUNCTION__, str);
+            return -1;
+        }
+        ai.ai_family = PF_INET;
+    } else {
+        /* hostname */
+        if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) {
+            fprintf(stderr, "%s: hostname parse error (%s)\n",
+                    __FUNCTION__, str);
+            return -1;
+        }
+    }
+    opts = str + pos;
+    h = strstr(opts, ",to=");
+    to = h ? atoi(h+4) : 0;
+
+    /* lookup */
+    if (port_offset)
+        snprintf(port, sizeof(port), "%d", atoi(port) + port_offset);
+    if (0 != (rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res))) {
+        fprintf(stderr,"%s: getaddrinfo(%s,%s): %s\n", __FUNCTION__,
+                addr, port, gai_strerror(rc));
+        return -1;
+    }
+    if (sockets_debug)
+        inet_print_addrinfo(__FUNCTION__, res);
+
+    /* create socket + bind */
+    for (e = res; e != NULL; e = e->ai_next) {
+	getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+		    uaddr,INET6_ADDRSTRLEN,uport,32,
+		    NI_NUMERICHOST | NI_NUMERICSERV);
+	if (-1 == (slisten = socket(e->ai_family, e->ai_socktype,
+				    e->ai_protocol))) {
+            fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
+                    inet_strfamily(e->ai_family), strerror(errno));
+	    continue;
+	}
+
+        setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
+#ifdef IPV6_V6ONLY
+        if (e->ai_family == PF_INET6) {
+            if (default_family == PF_INET6) {
+                /* user specified -ipv6: listen on ipv6 only */
+                setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&on,sizeof(on));
+            } else {
+                /* default: listen on both ipv4 and ipv6 */
+                setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&off,sizeof(off));
+            }
+        }
+#endif
+
+        for (;;) {
+            if (0 == bind(slisten, e->ai_addr, e->ai_addrlen)) {
+                if (sockets_debug)
+                    fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
+                            inet_strfamily(e->ai_family), uaddr, inet_getport(e));
+                goto listen;
+            }
+            try_next = to && (inet_getport(e) <= to + port_offset);
+            if (!try_next || sockets_debug)
+                fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__,
+                        inet_strfamily(e->ai_family), uaddr, inet_getport(e),
+                        strerror(errno));
+            if (try_next) {
+                inet_setport(e, inet_getport(e) + 1);
+                continue;
+            }
+            break;
+        }
+        close(slisten);
+    }
+    fprintf(stderr, "%s: FAILED\n", __FUNCTION__);
+    freeaddrinfo(res);
+    return -1;
+
+listen:
+    if (0 != listen(slisten,1)) {
+        perror("listen");
+        close(slisten);
+        return -1;
+    }
+    if (ostr) {
+        if (e->ai_family == PF_INET6) {
+            snprintf(ostr, olen, "[%s]:%d%s", uaddr,
+                     inet_getport(e) - port_offset, opts);
+        } else {
+            snprintf(ostr, olen, "%s:%d%s", uaddr,
+                     inet_getport(e) - port_offset, opts);
+        }
+    }
+    freeaddrinfo(res);
+    return slisten;
+}
+
+int inet_connect(const char *str, int socktype)
+{
+    struct addrinfo ai,*res,*e;
+    char addr[64];
+    char port[33];
+    char uaddr[INET6_ADDRSTRLEN+1];
+    char uport[33];
+    int sock,rc;
+
+    memset(&ai,0, sizeof(ai));
+    ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
+    ai.ai_family = default_family;
+    ai.ai_socktype = socktype;
+
+    /* parse string */
+    if (str[0] == '[') {
+        /* IPv6 addr */
+        if (2 != sscanf(str,"[%64[^]]]:%32[^,]",addr,port)) {
+            fprintf(stderr, "%s: ipv6 parse error (%s)\n",
+                    __FUNCTION__, str);
+            return -1;
+        }
+        ai.ai_family = PF_INET6;
+    } else if (isdigit(str[0])) {
+        /* IPv4 addr */
+        if (2 != sscanf(str,"%64[0-9.]:%32[^,]",addr,port)) {
+            fprintf(stderr, "%s: ipv4 parse error (%s)\n",
+                    __FUNCTION__, str);
+            return -1;
+        }
+        ai.ai_family = PF_INET;
+    } else {
+        /* hostname */
+        if (2 != sscanf(str,"%64[^:]:%32[^,]",addr,port)) {
+            fprintf(stderr, "%s: hostname parse error (%s)\n",
+                    __FUNCTION__, str);
+            return -1;
+        }
+    }
+
+    /* lookup */
+    if (0 != (rc = getaddrinfo(addr, port, &ai, &res))) {
+        fprintf(stderr,"getaddrinfo(%s,%s): %s\n", gai_strerror(rc),
+                addr, port);
+	return -1;
+    }
+    if (sockets_debug)
+        inet_print_addrinfo(__FUNCTION__, res);
+
+    for (e = res; e != NULL; e = e->ai_next) {
+	if (0 != getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+			     uaddr,INET6_ADDRSTRLEN,uport,32,
+			     NI_NUMERICHOST | NI_NUMERICSERV)) {
+            fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__);
+	    continue;
+	}
+	if (-1 == (sock = socket(e->ai_family, e->ai_socktype,
+				 e->ai_protocol))) {
+            fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
+                    inet_strfamily(e->ai_family), strerror(errno));
+	    continue;
+	}
+        setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
+
+	/* connect to peer */
+	if (-1 == connect(sock,e->ai_addr,e->ai_addrlen)) {
+            if (sockets_debug || NULL == e->ai_next)
+                fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
+                        inet_strfamily(e->ai_family),
+                        e->ai_canonname, uaddr, uport, strerror(errno));
+            close(sock);
+	    continue;
+	}
+        if (sockets_debug)
+            fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__,
+                    inet_strfamily(e->ai_family),
+                    e->ai_canonname, uaddr, uport);
+        freeaddrinfo(res);
+	return sock;
+    }
+    freeaddrinfo(res);
+    return -1;
+}
+
+#ifndef _WIN32
+
+int unix_listen(const char *str, char *ostr, int olen)
+{
+    struct sockaddr_un un;
+    char *path, *opts;
+    int sock, fd, len;
+
+    if (-1 == (sock = socket(PF_UNIX, SOCK_STREAM, 0))) {
+	perror("socket(unix)");
+	return -1;
+    }
+
+    opts = strchr(str, ',');
+    if (opts) {
+        len = opts - str;
+        path = malloc(len+1);
+        snprintf(path, len+1, "%.*s", len, str);
+    } else
+        path = strdup(str);
+
+    un.sun_family = AF_UNIX;
+    if (path && strlen(path)) {
+        snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+    } else {
+        char *tmpdir = getenv("TMPDIR");
+        snprintf(un.sun_path, sizeof(un.sun_path), "%s/qemu-socket-XXXXXX",
+                 tmpdir ? tmpdir : "/tmp");
+        /*
+         * This dummy fd usage silences the mktemp() unsecure warning.
+         * Using mkstemp() doesn't make things more secure here
+         * though.  bind() complains about existing files, so we have
+         * to unlink first and thus re-open the race window.  The
+         * worst case possible is bind() failing, i.e. a DoS attack.
+         */
+        fd = mkstemp(un.sun_path); close(fd);
+    }
+    snprintf(ostr, olen, "%s%s", un.sun_path, opts ? opts : "");
+
+    unlink(un.sun_path);
+    if (bind(sock, (struct sockaddr*) &un, sizeof(un)) == -1) {
+        fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno));
+        goto err;
+    }
+    if (listen(sock, 1) == -1) {
+        fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno));
+        goto err;
+    }
+
+    if (sockets_debug)
+        fprintf(stderr, "bind(unix:%s): OK\n", un.sun_path);
+    free(path);
+    return sock;
+
+err:
+    free(path);
+    close(sock);
+    return -1;
+}
+
+int unix_connect(const char *path)
+{
+    struct sockaddr_un un;
+    int sock;
+
+    if (-1 == (sock = socket(PF_UNIX, SOCK_STREAM, 0))) {
+	perror("socket(unix)");
+	return -1;
+    }
+
+    un.sun_family = AF_UNIX;
+    snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+    if (-1 == connect(sock, (struct sockaddr*) &un, sizeof(un))) {
+        fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
+	return -1;
+    }
+
+    if (sockets_debug)
+        fprintf(stderr, "connect(unix:%s): OK\n", path);
+    return sock;
+}
+
+#else
+
+int unix_listen(const char *path, char *ostr, int olen)
+{
+    fprintf(stderr, "unix sockets are not available on windows\n");
+    return -1;
+}
+
+int unix_connect(const char *path)
+{
+    fprintf(stderr, "unix sockets are not available on windows\n");
+    return -1;
+}
+
+#endif
diff --git a/qemu-sockets.h b/qemu-sockets.h
new file mode 100644
index 0000000..8793af3
--- /dev/null
+++ b/qemu-sockets.h
@@ -0,0 +1,8 @@
+int inet_listen(const char *str, char *ostr, int olen,
+                int socktype, int port_offset);
+int inet_connect(const char *str, int socktype);
+void inet_set_ipv4(void);
+void inet_set_ipv6(void);
+
+int unix_listen(const char *path, char *ostr, int olen);
+int unix_connect(const char *path);
diff --git a/vl.c b/vl.c
index a9fae17..e9fe478 100644
--- a/vl.c
+++ b/vl.c
@@ -37,6 +37,7 @@
 #include "gdbstub.h"
 #include "qemu-timer.h"
 #include "qemu-char.h"
+#include "qemu-sockets.h"
 #include "block.h"
 #include "audio/audio.h"
 #include "migration.h"
@@ -8280,6 +8281,9 @@ static void help(int exitcode)
            "-redir [tcp|udp]:host-port:[guest-host]:guest-port\n"
            "                redirect TCP or UDP connections from host to guest [-net user]\n"
 #endif
+           "-ipv4           Use IPv4 only.\n"
+           "-ipv6           Use UPv6 only.\n"
+           "                By default qemu uses what is available on your machine.\n"
            "\n"
            "Linux boot specific:\n"
            "-kernel bzImage use 'bzImage' as kernel image\n"
@@ -8386,6 +8390,8 @@ enum {
     QEMU_OPTION_bootp,
     QEMU_OPTION_smb,
     QEMU_OPTION_redir,
+    QEMU_OPTION_ipv4,
+    QEMU_OPTION_ipv6,
 
     QEMU_OPTION_kernel,
     QEMU_OPTION_append,
@@ -8489,6 +8495,10 @@ static const QEMUOption qemu_options[] = {
 #endif
     { "redir", HAS_ARG, QEMU_OPTION_redir },
 #endif
+    { "4", 0, QEMU_OPTION_ipv4 },
+    { "6", 0, QEMU_OPTION_ipv6 },
+    { "ipv4", 0, QEMU_OPTION_ipv4 },
+    { "ipv6", 0, QEMU_OPTION_ipv6 },
 
     { "kernel", HAS_ARG, QEMU_OPTION_kernel },
     { "append", HAS_ARG, QEMU_OPTION_append },
@@ -9151,6 +9161,12 @@ int main(int argc, char **argv)
                 net_slirp_redir(optarg);
                 break;
 #endif
+            case QEMU_OPTION_ipv4:
+                inet_set_ipv4();
+                break;
+            case QEMU_OPTION_ipv6:
+                inet_set_ipv6();
+                break;
 #ifdef HAS_AUDIO
             case QEMU_OPTION_audio_help:
                 AUD_help ();
-- 
1.5.6.5

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [Qemu-devel] [PATCH 3/4] sockets: switch vnc to new code, support vnc port auto-allocation.
  2008-10-31 12:47 [Qemu-devel] [PATCH v2 0/4] ipv6 and autoport patches Gerd Hoffmann
  2008-10-31 12:47 ` [Qemu-devel] [PATCH 1/4] Implement "info chardev" command Gerd Hoffmann
  2008-10-31 12:47 ` [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu Gerd Hoffmann
@ 2008-10-31 12:47 ` Gerd Hoffmann
  2008-10-31 12:47 ` [Qemu-devel] [PATCH 4/4] sockets: switch over tcp/telnet/unix serial line to new helper functions Gerd Hoffmann
  3 siblings, 0 replies; 12+ messages in thread
From: Gerd Hoffmann @ 2008-10-31 12:47 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This patch switches the vnc code ofer to the new socket helper
functions.

It adds support IPv6 support and for automatically allocating an unused
vnc display port.  The latter is handled ising a to= option, specifying
the upper limit for the display number to try.  Scanning is started at
the display number given in the display specification, i.e. this command
line:

    -vnc localhost:7,to=11

will try displays 7 to 11 (inclusive).

The display actually allocated can be queried using the "info vnc"
monitor command.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 vl.c  |    1 +
 vnc.c |  114 +++++++++++++++-------------------------------------------------
 2 files changed, 28 insertions(+), 87 deletions(-)

diff --git a/vl.c b/vl.c
index e9fe478..e4648f9 100644
--- a/vl.c
+++ b/vl.c
@@ -8284,6 +8284,7 @@ static void help(int exitcode)
            "-ipv4           Use IPv4 only.\n"
            "-ipv6           Use UPv6 only.\n"
            "                By default qemu uses what is available on your machine.\n"
+           "                Applies to -vnc.\n"
            "\n"
            "Linux boot specific:\n"
            "-kernel bzImage use 'bzImage' as kernel image\n"
diff --git a/vnc.c b/vnc.c
index 9df4dbe..9aeaaa0 100644
--- a/vnc.c
+++ b/vnc.c
@@ -28,6 +28,7 @@
 #include "sysemu.h"
 #include "qemu_socket.h"
 #include "qemu-timer.h"
+#include "qemu-sockets.h"
 
 #define VNC_REFRESH_INTERVAL (1000 / 30)
 
@@ -2139,8 +2140,6 @@ static void vnc_listen_read(void *opaque)
     }
 }
 
-extern int parse_host_port(struct sockaddr_in *saddr, const char *str);
-
 void vnc_display_init(DisplayState *ds)
 {
     VncState *vs;
@@ -2291,18 +2290,11 @@ int vnc_display_password(DisplayState *ds, const char *password)
 
 int vnc_display_open(DisplayState *ds, const char *display)
 {
-    struct sockaddr *addr;
-    struct sockaddr_in iaddr;
-#ifndef _WIN32
-    struct sockaddr_un uaddr;
-    const char *p;
-#endif
-    int reuse_addr, ret;
-    socklen_t addrlen;
     VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
     const char *options;
     int password = 0;
     int reverse = 0;
+    int to_port = 0;
 #ifdef CONFIG_VNC_TLS
     int tls = 0, x509 = 0;
 #endif
@@ -2321,6 +2313,8 @@ int vnc_display_open(DisplayState *ds, const char *display)
 	    password = 1; /* Require password auth */
 	} else if (strncmp(options, "reverse", 7) == 0) {
 	    reverse = 1;
+	} else if (strncmp(options, "to=", 3) == 0) {
+            to_port = atoi(options+3) + 5900;
 #ifdef CONFIG_VNC_TLS
 	} else if (strncmp(options, "tls", 3) == 0) {
 	    tls = 1; /* Require TLS */
@@ -2398,67 +2392,14 @@ int vnc_display_open(DisplayState *ds, const char *display)
 	}
 #endif
     }
-#ifndef _WIN32
-    if (strstart(display, "unix:", &p)) {
-	addr = (struct sockaddr *)&uaddr;
-	addrlen = sizeof(uaddr);
-
-	vs->lsock = socket(PF_UNIX, SOCK_STREAM, 0);
-	if (vs->lsock == -1) {
-	    fprintf(stderr, "Could not create socket\n");
-	    free(vs->display);
-	    vs->display = NULL;
-	    return -1;
-	}
-
-	uaddr.sun_family = AF_UNIX;
-	memset(uaddr.sun_path, 0, 108);
-	snprintf(uaddr.sun_path, 108, "%s", p);
-
-	if (!reverse) {
-	    unlink(uaddr.sun_path);
-	}
-    } else
-#endif
-    {
-	addr = (struct sockaddr *)&iaddr;
-	addrlen = sizeof(iaddr);
-
-	if (parse_host_port(&iaddr, display) < 0) {
-	    fprintf(stderr, "Could not parse VNC address\n");
-	    free(vs->display);
-	    vs->display = NULL;
-	    return -1;
-	}
-
-	iaddr.sin_port = htons(ntohs(iaddr.sin_port) + (reverse ? 0 : 5900));
-
-	vs->lsock = socket(PF_INET, SOCK_STREAM, 0);
-	if (vs->lsock == -1) {
-	    fprintf(stderr, "Could not create socket\n");
-	    free(vs->display);
-	    vs->display = NULL;
-	    return -1;
-	}
-
-	reuse_addr = 1;
-	ret = setsockopt(vs->lsock, SOL_SOCKET, SO_REUSEADDR,
-			 (const char *)&reuse_addr, sizeof(reuse_addr));
-	if (ret == -1) {
-	    fprintf(stderr, "setsockopt() failed\n");
-	    close(vs->lsock);
-	    vs->lsock = -1;
-	    free(vs->display);
-	    vs->display = NULL;
-	    return -1;
-	}
-    }
 
     if (reverse) {
-        if (connect(vs->lsock, addr, addrlen) == -1) {
-            fprintf(stderr, "Connection to VNC client failed\n");
-            close(vs->lsock);
-            vs->lsock = -1;
+        /* connect to viewer */
+        if (strncmp(display, "unix:", 5) == 0)
+            vs->lsock = unix_connect(display+5);
+        else
+            vs->lsock = inet_connect(display, SOCK_STREAM);
+        if (-1 == vs->lsock) {
             free(vs->display);
             vs->display = NULL;
             return -1;
@@ -2466,26 +2407,25 @@ int vnc_display_open(DisplayState *ds, const char *display)
             vs->csock = vs->lsock;
             vs->lsock = -1;
             vnc_connect(vs);
-            return 0;
         }
-    }
-
-    if (bind(vs->lsock, addr, addrlen) == -1) {
-	fprintf(stderr, "bind() failed\n");
-	close(vs->lsock);
-	vs->lsock = -1;
-	free(vs->display);
-	vs->display = NULL;
-	return -1;
-    }
+        return 0;
 
-    if (listen(vs->lsock, 1) == -1) {
-	fprintf(stderr, "listen() failed\n");
-	close(vs->lsock);
-	vs->lsock = -1;
-	free(vs->display);
-	vs->display = NULL;
-	return -1;
+    } else {
+        /* listen for connects */
+        char *dpy;
+        dpy = qemu_malloc(256);
+        if (strncmp(display, "unix:", 5) == 0) {
+            strcpy(dpy, "unix:");
+            vs->lsock = unix_listen(display, dpy+5, 256-5);
+        } else {
+            vs->lsock = inet_listen(display, dpy, 256, SOCK_STREAM, 5900);
+        }
+        if (-1 == vs->lsock) {
+            free(dpy);
+        } else {
+            free(vs->display);
+            vs->display = dpy;
+        }
     }
 
     return qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs);
-- 
1.5.6.5

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [Qemu-devel] [PATCH 4/4] sockets: switch over tcp/telnet/unix serial line to new helper functions.
  2008-10-31 12:47 [Qemu-devel] [PATCH v2 0/4] ipv6 and autoport patches Gerd Hoffmann
                   ` (2 preceding siblings ...)
  2008-10-31 12:47 ` [Qemu-devel] [PATCH 3/4] sockets: switch vnc to new code, support vnc port auto-allocation Gerd Hoffmann
@ 2008-10-31 12:47 ` Gerd Hoffmann
  3 siblings, 0 replies; 12+ messages in thread
From: Gerd Hoffmann @ 2008-10-31 12:47 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This switches the tcp, telnet and unix socket support for character
devices (serial/parallel, ...) to the new socket helpers.  Thereby they
gain IPv6 support and also get ability to search for a free tcp port.
Syntax is the same as for vnc, using a to= option, like this:

	-serial tcp:localhost:5000,to=5099,server

This will check the 5000 -> 5099 port range (inclusive) for a free tcp
port.  Likewise you can get auto-allocated unix sockets by specifying an
empty path:

	-serial unix:,server

qemu will create a randomly named socket in $TMPDIR then.  You can use
the "info chardev" command added by the first patch in this series to
figure the tcp port / unix socket actually allocated.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 vl.c |  131 +++++++++++++++++-------------------------------------------------
 1 files changed, 33 insertions(+), 98 deletions(-)

diff --git a/vl.c b/vl.c
index e4648f9..b6c61be 100644
--- a/vl.c
+++ b/vl.c
@@ -3370,9 +3370,6 @@ static void udp_chr_update_read_handler(CharDriverState *chr)
     }
 }
 
-#ifndef _WIN32
-static int parse_unix_path(struct sockaddr_un *uaddr, const char *str);
-#endif
 int parse_host_src_port(struct sockaddr_in *haddr,
                         struct sockaddr_in *saddr,
                         const char *str);
@@ -3629,32 +3626,11 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
 {
     CharDriverState *chr = NULL;
     TCPCharDriver *s = NULL;
-    int fd = -1, ret, err, val;
+    int fd = -1, offset = 0;
     int is_listen = 0;
     int is_waitconnect = 1;
     int do_nodelay = 0;
     const char *ptr;
-    struct sockaddr_in saddr;
-#ifndef _WIN32
-    struct sockaddr_un uaddr;
-#endif
-    struct sockaddr *addr;
-    socklen_t addrlen;
-
-#ifndef _WIN32
-    if (is_unix) {
-	addr = (struct sockaddr *)&uaddr;
-	addrlen = sizeof(uaddr);
-	if (parse_unix_path(&uaddr, host_str) < 0)
-	    goto fail;
-    } else
-#endif
-    {
-	addr = (struct sockaddr *)&saddr;
-	addrlen = sizeof(saddr);
-	if (parse_host_port(&saddr, host_str) < 0)
-	    goto fail;
-    }
 
     ptr = host_str;
     while((ptr = strchr(ptr,','))) {
@@ -3665,6 +3641,8 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
             is_waitconnect = 0;
         } else if (!strncmp(ptr,"nodelay",6)) {
             do_nodelay = 1;
+        } else if (!strncmp(ptr,"to=",3)) {
+            /* nothing, inet_listen() parses this one */;
         } else {
             printf("Unknown option: %s\n", ptr);
             goto fail;
@@ -3680,13 +3658,31 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
     if (!s)
         goto fail;
 
-#ifndef _WIN32
-    if (is_unix)
-	fd = socket(PF_UNIX, SOCK_STREAM, 0);
-    else
-#endif
-	fd = socket(PF_INET, SOCK_STREAM, 0);
-
+    if (is_listen) {
+        chr->filename = qemu_malloc(256);
+        if (is_unix) {
+            strcpy(chr->filename, "unix:");
+        } else if (is_telnet) {
+            strcpy(chr->filename, "telnet:");
+        } else {
+            strcpy(chr->filename, "tcp:");
+        }
+        offset = strlen(chr->filename);
+    }
+    if (is_unix) {
+        if (is_listen) {
+            fd = unix_listen(host_str, chr->filename + offset, 256 - offset);
+        } else {
+            fd = unix_connect(host_str);
+        }
+    } else {
+        if (is_listen) {
+            fd = inet_listen(host_str, chr->filename + offset, 256 - offset,
+                             SOCK_STREAM, 0);
+        } else {
+            fd = inet_connect(host_str, SOCK_STREAM);
+        }
+    }
     if (fd < 0)
         goto fail;
 
@@ -3704,61 +3700,20 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str,
     chr->chr_close = tcp_chr_close;
 
     if (is_listen) {
-        /* allow fast reuse */
-#ifndef _WIN32
-	if (is_unix) {
-	    char path[109];
-	    pstrcpy(path, sizeof(path), uaddr.sun_path);
-	    unlink(path);
-	} else
-#endif
-	{
-	    val = 1;
-	    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
-	}
-
-        ret = bind(fd, addr, addrlen);
-        if (ret < 0)
-            goto fail;
-
-        ret = listen(fd, 0);
-        if (ret < 0)
-            goto fail;
-
         s->listen_fd = fd;
         qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
         if (is_telnet)
             s->do_telnetopt = 1;
     } else {
-        for(;;) {
-            ret = connect(fd, addr, addrlen);
-            if (ret < 0) {
-                err = socket_error();
-                if (err == EINTR || err == EWOULDBLOCK) {
-                } else if (err == EINPROGRESS) {
-                    break;
-#ifdef _WIN32
-                } else if (err == WSAEALREADY) {
-                    break;
-#endif
-                } else {
-                    goto fail;
-                }
-            } else {
-                s->connected = 1;
-                break;
-            }
-        }
+        s->connected = 1;
         s->fd = fd;
         socket_set_nodelay(fd);
-        if (s->connected)
-            tcp_chr_connect(chr);
-        else
-            qemu_set_fd_handler(s->fd, NULL, tcp_chr_connect, chr);
+        tcp_chr_connect(chr);
     }
 
     if (is_listen && is_waitconnect) {
-        printf("QEMU waiting for connection on: %s\n", host_str);
+        printf("QEMU waiting for connection on: %s\n",
+               chr->filename ? chr->filename : host_str);
         tcp_chr_accept(chr);
         socket_set_nonblock(s->listen_fd);
     }
@@ -4035,26 +3990,6 @@ int parse_host_port(struct sockaddr_in *saddr, const char *str)
     return 0;
 }
 
-#ifndef _WIN32
-static int parse_unix_path(struct sockaddr_un *uaddr, const char *str)
-{
-    const char *p;
-    int len;
-
-    len = MIN(108, strlen(str));
-    p = strchr(str, ',');
-    if (p)
-	len = MIN(len, p - str);
-
-    memset(uaddr, 0, sizeof(*uaddr));
-
-    uaddr->sun_family = AF_UNIX;
-    memcpy(uaddr->sun_path, str, len);
-
-    return 0;
-}
-#endif
-
 /* find or alloc a new VLAN */
 VLANState *qemu_find_vlan(int id)
 {
@@ -8284,7 +8219,7 @@ static void help(int exitcode)
            "-ipv4           Use IPv4 only.\n"
            "-ipv6           Use UPv6 only.\n"
            "                By default qemu uses what is available on your machine.\n"
-           "                Applies to -vnc.\n"
+           "                Applies to -vnc and -serial tcp.\n"
            "\n"
            "Linux boot specific:\n"
            "-kernel bzImage use 'bzImage' as kernel image\n"
-- 
1.5.6.5

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [Qemu-devel] [PATCH 1/4] Implement "info chardev" command.
  2008-10-31 12:47 ` [Qemu-devel] [PATCH 1/4] Implement "info chardev" command Gerd Hoffmann
@ 2008-10-31 17:32   ` Anthony Liguori
  0 siblings, 0 replies; 12+ messages in thread
From: Anthony Liguori @ 2008-10-31 17:32 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Gerd Hoffmann wrote:
> This patch makes qemu keep track of the character devices in use and
> implements a "info chardev" monitor command to print a list.
>
> qemu_chr_open() sticks the devices into a linked list now.  It got a new
> argument (label), so there is a name for each device.  It also assigns a
> filename to each character device.  By default it just copyes the
> filename passed in.  Individual drivers can fill in something else
> though.  qemu_chr_open_pty() sets the filename to name of the pseudo tty
> allocated.
>
> Output looks like this:
>
>   (qemu) info chardev
>   monitor: filename=unix:/tmp/run.sh-26827/monitor,server,nowait
>   serial0: filename=unix:/tmp/run.sh-26827/console,server
>   serial1: filename=pty:/dev/pts/5
>   parallel0: filename=vc:640x480
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
>   

Applied.  Thanks.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
  2008-10-31 12:47 ` [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu Gerd Hoffmann
@ 2008-10-31 17:37   ` Anthony Liguori
  2008-10-31 17:50     ` Daniel P. Berrange
  2008-11-03 15:35     ` Gerd Hoffmann
  0 siblings, 2 replies; 12+ messages in thread
From: Anthony Liguori @ 2008-10-31 17:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Gerd Hoffmann wrote:
> This patch creates a new source and header file qemu-sockets.[ch] with
> a bunch of helper functions to create listening and connected sockets.
>
> New features of this code are (a) support for searching for a free port
> in a given range and (b) support for IPv6.
>
> The following patches put that code into use.
>   

I like this patch, but we already have a qemu_socket.h.  Please remove 
qemu_socket.h if you're going to introduce qemu-sockets.h.

> +
> +listen:
> +    if (0 != listen(slisten,1)) {
>   

Please try to avoid this style of if().

> diff --git a/vl.c b/vl.c
> index a9fae17..e9fe478 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -37,6 +37,7 @@
>  #include "gdbstub.h"
>  #include "qemu-timer.h"
>  #include "qemu-char.h"
> +#include "qemu-sockets.h"
>  #include "block.h"
>  #include "audio/audio.h"
>  #include "migration.h"
> @@ -8280,6 +8281,9 @@ static void help(int exitcode)
>             "-redir [tcp|udp]:host-port:[guest-host]:guest-port\n"
>             "                redirect TCP or UDP connections from host to guest [-net user]\n"
>  #endif
> +           "-ipv4           Use IPv4 only.\n"
> +           "-ipv6           Use UPv6 only.\n"
> +           "                By default qemu uses what is available on your machine.\n"
>             "\n"
>             "Linux boot specific:\n"
>             "-kernel bzImage use 'bzImage' as kernel image\n"
> @@ -8386,6 +8390,8 @@ enum {
>      QEMU_OPTION_bootp,
>      QEMU_OPTION_smb,
>      QEMU_OPTION_redir,
> +    QEMU_OPTION_ipv4,
> +    QEMU_OPTION_ipv6,
>  
>      QEMU_OPTION_kernel,
>      QEMU_OPTION_append,
> @@ -8489,6 +8495,10 @@ static const QEMUOption qemu_options[] = {
>  #endif
>      { "redir", HAS_ARG, QEMU_OPTION_redir },
>  #endif
> +    { "4", 0, QEMU_OPTION_ipv4 },
> +    { "6", 0, QEMU_OPTION_ipv6 },
> +    { "ipv4", 0, QEMU_OPTION_ipv4 },
> +    { "ipv6", 0, QEMU_OPTION_ipv6 },

I don't like the idea of aliasing these options.  Please just stick with 
one set of options.

And do we really need to have options for this?  Can't we just do the 
right thing?  I can't believe that every application has to have an ipv6 
switch to be ipv6 enabled.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
  2008-10-31 17:37   ` Anthony Liguori
@ 2008-10-31 17:50     ` Daniel P. Berrange
  2008-10-31 17:58       ` Anthony Liguori
  2008-11-03 15:35     ` Gerd Hoffmann
  1 sibling, 1 reply; 12+ messages in thread
From: Daniel P. Berrange @ 2008-10-31 17:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

On Fri, Oct 31, 2008 at 12:37:16PM -0500, Anthony Liguori wrote:
> Gerd Hoffmann wrote:
> >@@ -8280,6 +8281,9 @@ static void help(int exitcode)
> >            "-redir [tcp|udp]:host-port:[guest-host]:guest-port\n"
> >            "                redirect TCP or UDP connections from host to 
> >            guest [-net user]\n"
> > #endif
> >+           "-ipv4           Use IPv4 only.\n"
> >+           "-ipv6           Use UPv6 only.\n"
> >+           "                By default qemu uses what is available on 
> >your machine.\n"
> >            "\n"
> >            "Linux boot specific:\n"
> >            "-kernel bzImage use 'bzImage' as kernel image\n"
> >@@ -8386,6 +8390,8 @@ enum {
> >     QEMU_OPTION_bootp,
> >     QEMU_OPTION_smb,
> >     QEMU_OPTION_redir,
> >+    QEMU_OPTION_ipv4,
> >+    QEMU_OPTION_ipv6,
> > 
> >     QEMU_OPTION_kernel,
> >     QEMU_OPTION_append,
> >@@ -8489,6 +8495,10 @@ static const QEMUOption qemu_options[] = {
> > #endif
> >     { "redir", HAS_ARG, QEMU_OPTION_redir },
> > #endif
> >+    { "4", 0, QEMU_OPTION_ipv4 },
> >+    { "6", 0, QEMU_OPTION_ipv6 },
> >+    { "ipv4", 0, QEMU_OPTION_ipv4 },
> >+    { "ipv6", 0, QEMU_OPTION_ipv6 },
> 
> I don't like the idea of aliasing these options.  Please just stick with 
> one set of options.
> 
> And do we really need to have options for this?  Can't we just do the 
> right thing?  I can't believe that every application has to have an ipv6 
> switch to be ipv6 enabled.

Gerd's patch does 'do the right thing' for 90% of people - it uses the
AI_ADDRCONFIG flag. This means that the getaddrinfo() name lookup will
only return an IPv6 address if there is at least one NIC with IPv6
configured, likewise only return an IPv4 address if there is at least
one NIC with IPv4 configured. There are still times when it is desirable
to restrict it to just IPv4 or just IPv6, regardless of your host NIC
config - mostly when needing to interact with other broken apps which are
not correctly IPv6 aware. Maybe it would be sufficient to just have it added
as a flag to the arg in question though, eg -serial tcp:localhost:123,ipv4
rather than a global -ipv4 flag

Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
  2008-10-31 17:50     ` Daniel P. Berrange
@ 2008-10-31 17:58       ` Anthony Liguori
  2008-10-31 19:44         ` Jamie Lokier
  0 siblings, 1 reply; 12+ messages in thread
From: Anthony Liguori @ 2008-10-31 17:58 UTC (permalink / raw)
  To: Daniel P. Berrange, qemu-devel; +Cc: Gerd Hoffmann

Daniel P. Berrange wrote:
> On Fri, Oct 31, 2008 at 12:37:16PM -0500, Anthony Liguori wrote:
>   
> Gerd's patch does 'do the right thing' for 90% of people - it uses the
> AI_ADDRCONFIG flag. This means that the getaddrinfo() name lookup will
> only return an IPv6 address if there is at least one NIC with IPv6
> configured, likewise only return an IPv4 address if there is at least
> one NIC with IPv4 configured. There are still times when it is desirable
> to restrict it to just IPv4 or just IPv6, regardless of your host NIC
> config - mostly when needing to interact with other broken apps which are
> not correctly IPv6 aware. Maybe it would be sufficient to just have it added
> as a flag to the arg in question though, eg -serial tcp:localhost:123,ipv4
> rather than a global -ipv4 flag
>   

Yes, this would be much nicer.  I think having the options as a global 
is likely to confuse users.

Regards,

Anthony Liguori

> Daniel
>   

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
  2008-10-31 17:58       ` Anthony Liguori
@ 2008-10-31 19:44         ` Jamie Lokier
  0 siblings, 0 replies; 12+ messages in thread
From: Jamie Lokier @ 2008-10-31 19:44 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Anthony Liguori wrote:
> >Maybe it would be sufficient to just have it added as a flag to the
> >arg in question though, eg -serial tcp:localhost:123,ipv4 rather
> >than a global -ipv4 flag
> >  
> 
> Yes, this would be much nicer.  I think having the options as a global 
> is likely to confuse users.

Yes.  In those few cases where you need to restrict it, a global
option would be wrong too: if you have multiple interfaces, and you
need to override the automatic default for some reason, there's a fair
chance you don't need that on every interface.

-- Jamie

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu.
  2008-10-31 17:37   ` Anthony Liguori
  2008-10-31 17:50     ` Daniel P. Berrange
@ 2008-11-03 15:35     ` Gerd Hoffmann
  1 sibling, 0 replies; 12+ messages in thread
From: Gerd Hoffmann @ 2008-11-03 15:35 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel

Anthony Liguori wrote:
> I like this patch, but we already have a qemu_socket.h.  Please remove
> qemu_socket.h if you're going to introduce qemu-sockets.h.

Will fix.

>> +
>> +listen:
>> +    if (0 != listen(slisten,1)) {
> 
> Please try to avoid this style of if().

Yea, old habit from the days where gcc didn't warn on "if (a = -1)".
Pointless these days.  Trying to get rid of it ...

>> +    { "ipv4", 0, QEMU_OPTION_ipv4 },
>> +    { "ipv6", 0, QEMU_OPTION_ipv6 },
> 
> I don't like the idea of aliasing these options.  Please just stick with
> one set of options.

> And do we really need to have options for this?

Yes, we do, unfortunaly.

> Can't we just do the
> right thing?  I can't believe that every application has to have an ipv6
> switch to be ipv6 enabled.

98% of the users should never ever need that.  Unfortunaly there are
always corner cases where you can't get it right automatically.  You can
easily check the ipv6 setup on the local machine, but you still don't
know how the network is setup and whenever reaching machine $foo via
ipv6 actually works.  Of course you can just try and in case it doesn't
work fallback to ipv4.  Which is what the code does btw.  But that
involves a noticeable delay, waiting for the ipv6 connect attempt time out.

Will make that a per-connection option as discussed further down this
thread.

Respin of the patches will follow later today or tomorrow.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2008-11-03 15:36 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-31 12:47 [Qemu-devel] [PATCH v2 0/4] ipv6 and autoport patches Gerd Hoffmann
2008-10-31 12:47 ` [Qemu-devel] [PATCH 1/4] Implement "info chardev" command Gerd Hoffmann
2008-10-31 17:32   ` Anthony Liguori
2008-10-31 12:47 ` [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu Gerd Hoffmann
2008-10-31 17:37   ` Anthony Liguori
2008-10-31 17:50     ` Daniel P. Berrange
2008-10-31 17:58       ` Anthony Liguori
2008-10-31 19:44         ` Jamie Lokier
2008-11-03 15:35     ` Gerd Hoffmann
2008-10-31 12:47 ` [Qemu-devel] [PATCH 3/4] sockets: switch vnc to new code, support vnc port auto-allocation Gerd Hoffmann
2008-10-31 12:47 ` [Qemu-devel] [PATCH 4/4] sockets: switch over tcp/telnet/unix serial line to new helper functions Gerd Hoffmann
  -- strict thread matches above, loose matches on Subject: below --
2008-10-28 12:55 [Qemu-devel] [PATCH 0/4] ipv6 and autoport patches Gerd Hoffmann
2008-10-28 12:55 ` [Qemu-devel] [PATCH 4/4] sockets: switch over tcp/telnet/unix serial line to new helper functions Gerd Hoffmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).