diff --git a/qemu-char.c b/qemu-char.c index ac65a1c..aeb5afb 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -1915,7 +1915,8 @@ return_err: /* TCP Net console */ typedef struct { - int fd, listen_fd; + int fd; + FdList *listen_fds; int connected; int max_size; int do_telnetopt; @@ -2068,6 +2069,7 @@ static void tcp_chr_read(void *opaque) TCPCharDriver *s = chr->opaque; uint8_t buf[READ_BUF_LEN]; int len, size; + FdList *fdl; if (!s->connected || s->max_size <= 0) return; @@ -2078,9 +2080,8 @@ static void tcp_chr_read(void *opaque) if (size == 0) { /* connection closed */ s->connected = 0; - if (s->listen_fd >= 0) { - qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); - } + for (fdl = s->listen_fds; fdl != NULL; fdl = fdl->next) + qemu_set_fd_handler(fdl->fd, tcp_chr_accept, NULL, fdl); qemu_set_fd_handler(s->fd, NULL, NULL, NULL); closesocket(s->fd); s->fd = -1; @@ -2127,7 +2128,8 @@ static void socket_set_nodelay(int fd) static void tcp_chr_accept(void *opaque) { - CharDriverState *chr = opaque; + FdList *fds = opaque; + CharDriverState *chr = fds->opaque; TCPCharDriver *s = chr->opaque; struct sockaddr_in saddr; #ifndef _WIN32 @@ -2148,7 +2150,7 @@ static void tcp_chr_accept(void *opaque) len = sizeof(saddr); addr = (struct sockaddr *)&saddr; } - fd = qemu_accept(s->listen_fd, addr, &len); + fd = qemu_accept(fds->fd, addr, &len); if (fd < 0 && errno != EINTR) { return; } else if (fd >= 0) { @@ -2161,21 +2163,24 @@ static void tcp_chr_accept(void *opaque) if (s->do_nodelay) socket_set_nodelay(fd); s->fd = fd; - qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); + for (fds = s->listen_fds; fds != NULL; fds = fds->next) + qemu_set_fd_handler(fds->fd, NULL, NULL, NULL); tcp_chr_connect(chr); } static void tcp_chr_close(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; + FdList *fds; if (s->fd >= 0) { qemu_set_fd_handler(s->fd, NULL, NULL, NULL); closesocket(s->fd); } - if (s->listen_fd >= 0) { - qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); - closesocket(s->listen_fd); + for (fds = s->listen_fds; fds != NULL; fds = fds->next) { + qemu_set_fd_handler(fds->fd, NULL, NULL, NULL); + closesocket(fds->fd); } + fdlist_free(fds); qemu_free(s); qemu_chr_event(chr, CHR_EVENT_CLOSED); } @@ -2190,6 +2195,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) int do_nodelay; int is_unix; int is_telnet; + FdList *socks = NULL; is_listen = qemu_opt_get_bool(opts, "server", 0); is_waitconnect = qemu_opt_get_bool(opts, "wait", 1); @@ -2205,17 +2211,20 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) if (is_unix) { if (is_listen) { fd = unix_listen_opts(opts); + if (fd <= 0) + socks = fdlist_new(fd); } else { fd = unix_connect_opts(opts); } } else { if (is_listen) { - fd = inet_listen_opts(opts, 0); + socks = inet_listen_opts(opts, 0); } else { fd = inet_connect_opts(opts); } } - if (fd < 0) + + if (socks == NULL && fd < 0) goto fail; if (!is_waitconnect) @@ -2223,7 +2232,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) s->connected = 0; s->fd = -1; - s->listen_fd = -1; + s->listen_fds = NULL; s->msgfd = -1; s->is_unix = is_unix; s->do_nodelay = do_nodelay && !is_unix; @@ -2234,8 +2243,11 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) chr->get_msgfd = tcp_get_msgfd; if (is_listen) { - s->listen_fd = fd; - qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); + s->listen_fds = socks; + for (; socks != NULL; socks = socks->next) { + socks->opaque = chr; + qemu_set_fd_handler(socks->fd, tcp_chr_accept, NULL, socks); + } if (is_telnet) s->do_telnetopt = 1; @@ -2266,7 +2278,9 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) printf("QEMU waiting for connection on: %s\n", chr->filename); tcp_chr_accept(chr); - socket_set_nonblock(s->listen_fd); + for (socks = s->listen_fds; socks != NULL; socks = socks->next) { + socket_set_nonblock(socks->fd); + } } return chr; @@ -2275,6 +2289,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) closesocket(fd); qemu_free(s); qemu_free(chr); + fdlist_free(socks); return NULL; } diff --git a/qemu-sockets.c b/qemu-sockets.c index a7399aa..ecda69d 100644 --- a/qemu-sockets.c +++ b/qemu-sockets.c @@ -57,6 +57,25 @@ static QemuOptsList dummy_opts = { }, }; +FdList* fdlist_new(int fd) +{ + FdList *new = malloc(sizeof(FdList)); + new->fd = fd; + new->opaque = NULL; + new->next = NULL; + return new; +} + +void fdlist_free(FdList *fds) +{ + FdList *tmp; + while (fds) { + tmp = fds->next; + free(fds); + fds = tmp; + } +} + static int inet_getport(struct addrinfo *e) { struct sockaddr_in *i4; @@ -116,14 +135,15 @@ static void inet_print_addrinfo(const char *tag, struct addrinfo *res) } } -int inet_listen_opts(QemuOpts *opts, int port_offset) +FdList *inet_listen_opts(QemuOpts *opts, int port_offset) { - struct addrinfo ai,*res,*e; + struct addrinfo ai,*res,*e,*last=NULL; const char *addr; char port[33]; char uaddr[INET6_ADDRSTRLEN+1]; char uport[33]; int slisten,rc,to,try_next; + FdList *fds = NULL, *newfd; memset(&ai,0, sizeof(ai)); ai.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; @@ -132,7 +152,7 @@ int inet_listen_opts(QemuOpts *opts, int port_offset) if (qemu_opt_get(opts, "port") == NULL) { fprintf(stderr, "%s: host and/or port not specified\n", __FUNCTION__); - return -1; + return NULL; } pstrcpy(port, sizeof(port), qemu_opt_get(opts, "port")); addr = qemu_opt_get(opts, "host"); @@ -150,7 +170,7 @@ int inet_listen_opts(QemuOpts *opts, int port_offset) if (rc != 0) { fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port, gai_strerror(rc)); - return -1; + return NULL; } if (sockets_debug) inet_print_addrinfo(__FUNCTION__, res); @@ -170,9 +190,9 @@ int inet_listen_opts(QemuOpts *opts, int port_offset) setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on)); #ifdef IPV6_V6ONLY if (e->ai_family == PF_INET6) { - /* listen on both ipv4 and ipv6 */ - setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&off, - sizeof(off)); + /* listen on IPv6 only, IPv4 is handled by a separate socket */ + setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&on, + sizeof(on)); } #endif @@ -181,9 +201,19 @@ int inet_listen_opts(QemuOpts *opts, int port_offset) if (sockets_debug) fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__, inet_strfamily(e->ai_family), uaddr, inet_getport(e)); - goto listen; + if (listen(slisten, 1) != 0) { + closesocket(slisten); + } else { + fprintf(stderr, "slisten= %d\n", slisten); + newfd = fdlist_new(slisten); + newfd->next = fds; + fds = newfd; + last = e; + } + break; } try_next = to && (inet_getport(e) <= to + port_offset); + /* FIXME: should make sure that all sockets get the same port */ if (!try_next || sockets_debug) fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__, inet_strfamily(e->ai_family), uaddr, inet_getport(e), @@ -192,28 +222,23 @@ int inet_listen_opts(QemuOpts *opts, int port_offset) inet_setport(e, inet_getport(e) + 1); continue; } + closesocket(slisten); break; } - closesocket(slisten); } + if (fds == NULL) { fprintf(stderr, "%s: FAILED\n", __FUNCTION__); - freeaddrinfo(res); - return -1; - -listen: - if (listen(slisten,1) != 0) { - perror("listen"); - closesocket(slisten); - freeaddrinfo(res); - return -1; - } - snprintf(uport, sizeof(uport), "%d", inet_getport(e) - port_offset); + } else { + /* FIXME: should save all addresses here, not just the last one */ + snprintf(uport, sizeof(uport), "%d", inet_getport(last) - port_offset); qemu_opt_set(opts, "host", uaddr); qemu_opt_set(opts, "port", uport); - qemu_opt_set(opts, "ipv6", (e->ai_family == PF_INET6) ? "on" : "off"); - qemu_opt_set(opts, "ipv4", (e->ai_family != PF_INET6) ? "on" : "off"); + qemu_opt_set(opts, "ipv6", (last->ai_family == PF_INET6) ? "on" : "off"); + qemu_opt_set(opts, "ipv4", (last->ai_family != PF_INET6) ? "on" : "off"); + } freeaddrinfo(res); - return slisten; + fprintf (stderr, "fds=%p\n", fds); + return fds; } int inet_connect_opts(QemuOpts *opts) @@ -456,17 +481,17 @@ static int inet_parse(QemuOpts *opts, const char *str) return 0; } -int inet_listen(const char *str, char *ostr, int olen, +FdList* inet_listen(const char *str, char *ostr, int olen, int socktype, int port_offset) { QemuOpts *opts; char *optstr; - int sock = -1; + FdList *socks = NULL; opts = qemu_opts_create(&dummy_opts, NULL, 0); if (inet_parse(opts, str) == 0) { - sock = inet_listen_opts(opts, port_offset); - if (sock != -1 && ostr) { + socks = inet_listen_opts(opts, port_offset); + if (socks != NULL && ostr) { optstr = strchr(str, ','); if (qemu_opt_get_bool(opts, "ipv6", 0)) { snprintf(ostr, olen, "[%s]:%s%s", @@ -482,7 +507,7 @@ int inet_listen(const char *str, char *ostr, int olen, } } qemu_opts_del(opts); - return sock; + return socks; } int inet_connect(const char *str, int socktype) diff --git a/qemu_socket.h b/qemu_socket.h index 164ae3e..1f52663 100644 --- a/qemu_socket.h +++ b/qemu_socket.h @@ -29,6 +29,13 @@ int inet_aton(const char *cp, struct in_addr *ia); #endif /* !_WIN32 */ +struct FdList; +typedef struct FdList { + int fd; + void *opaque; + struct FdList *next; +} FdList; + #include "qemu-option.h" /* misc helpers */ @@ -36,10 +43,12 @@ int qemu_socket(int domain, int type, int protocol); int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); void socket_set_nonblock(int fd); int send_all(int fd, const void *buf, int len1); +FdList *fdlist_new(int fd); +void fdlist_free(FdList *fds); /* New, ipv6-ready socket helper functions, see qemu-sockets.c */ -int inet_listen_opts(QemuOpts *opts, int port_offset); -int inet_listen(const char *str, char *ostr, int olen, +FdList *inet_listen_opts(QemuOpts *opts, int port_offset); +FdList *inet_listen(const char *str, char *ostr, int olen, int socktype, int port_offset); int inet_connect_opts(QemuOpts *opts); int inet_connect(const char *str, int socktype); diff --git a/vnc.c b/vnc.c index b1a3fdb..aab8931 100644 --- a/vnc.c +++ b/vnc.c @@ -26,7 +26,6 @@ #include "vnc.h" #include "sysemu.h" -#include "qemu_socket.h" #include "qemu-timer.h" #include "acl.h" #include "qemu-objects.h" @@ -202,7 +201,8 @@ static const char *vnc_auth_name(VncDisplay *vd) { static int vnc_server_info_put(QDict *qdict) { - if (vnc_server_addr_put(qdict, vnc_display->lsock) < 0) { + /* FIXME: need to consider all sockets here, not just the first one */ + if (vnc_server_addr_put(qdict, vnc_display->lsocks->fd) < 0) { return -1; } @@ -2247,14 +2247,15 @@ static void vnc_connect(VncDisplay *vd, int csock) static void vnc_listen_read(void *opaque) { - VncDisplay *vs = opaque; + FdList *sock = opaque; + VncDisplay *vs = sock->opaque; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); /* Catch-up */ vga_hw_update(); - int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); + int csock = qemu_accept(sock->fd, (struct sockaddr *)&addr, &addrlen); if (csock != -1) { vnc_connect(vs, csock); } @@ -2270,7 +2271,7 @@ void vnc_display_init(DisplayState *ds) dcl->idle = 1; vnc_display = vs; - vs->lsock = -1; + vs->lsocks = NULL; vs->ds = ds; QTAILQ_INIT(&vs->clients); @@ -2294,6 +2295,7 @@ void vnc_display_init(DisplayState *ds) void vnc_display_close(DisplayState *ds) { VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + FdList *fds, *fdtmp; if (!vs) return; @@ -2301,11 +2303,13 @@ void vnc_display_close(DisplayState *ds) qemu_free(vs->display); vs->display = NULL; } - if (vs->lsock != -1) { - qemu_set_fd_handler2(vs->lsock, NULL, NULL, NULL, NULL); - close(vs->lsock); - vs->lsock = -1; + for (fds = vs->lsocks; fds != NULL; fds = fdtmp) { + fdtmp = fds->next; + qemu_set_fd_handler2(fds->fd, NULL, NULL, NULL, NULL); + close(fds->fd); + free(fds); } + vs->lsocks = NULL; vs->auth = VNC_AUTH_INVALID; #ifdef CONFIG_VNC_TLS vs->subauth = VNC_AUTH_INVALID; @@ -2342,7 +2346,8 @@ char *vnc_display_local_addr(DisplayState *ds) { VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; - return vnc_socket_local_addr("%s:%s", vs->lsock); + /* FIXME: should return all addresses */ + return vnc_socket_local_addr("%s:%s", vs->lsocks->fd); } int vnc_display_open(DisplayState *ds, const char *display) @@ -2527,39 +2532,49 @@ int vnc_display_open(DisplayState *ds, const char *display) vs->lock_key_sync = lock_key_sync; if (reverse) { + int sock; /* connect to viewer */ if (strncmp(display, "unix:", 5) == 0) - vs->lsock = unix_connect(display+5); + sock = unix_connect(display+5); else - vs->lsock = inet_connect(display, SOCK_STREAM); - if (-1 == vs->lsock) { + sock = inet_connect(display, SOCK_STREAM); + if (-1 == sock) { free(vs->display); vs->display = NULL; return -1; } else { - int csock = vs->lsock; - vs->lsock = -1; - vnc_connect(vs, csock); + vnc_connect(vs, sock); } return 0; } else { /* listen for connects */ char *dpy; + int fd; + FdList *socks = NULL; + dpy = qemu_malloc(256); if (strncmp(display, "unix:", 5) == 0) { pstrcpy(dpy, 256, "unix:"); - vs->lsock = unix_listen(display+5, dpy+5, 256-5); + fd = unix_listen(display+5, dpy+5, 256-5); + if (fd >= 0) + socks = fdlist_new(fd); } else { - vs->lsock = inet_listen(display, dpy, 256, SOCK_STREAM, 5900); + socks = inet_listen(display, dpy, 256, SOCK_STREAM, 5900); } - if (-1 == vs->lsock) { + + if (socks == NULL) { free(dpy); return -1; } else { + vs->lsocks = socks; free(vs->display); vs->display = dpy; } + for (; socks != NULL; socks = socks->next) { + socks->opaque = vs; + qemu_set_fd_handler2(socks->fd, NULL, vnc_listen_read, NULL, socks); + } + return 0; } - return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs); } diff --git a/vnc.h b/vnc.h index 1aa71b0..a4fa9a9 100644 --- a/vnc.h +++ b/vnc.h @@ -28,6 +28,7 @@ #define __QEMU_VNC_H #include "qemu-common.h" +#include "qemu_socket.h" #include "qemu-queue.h" #include "console.h" #include "monitor.h" @@ -96,7 +97,7 @@ struct VncDisplay QTAILQ_HEAD(, VncState) clients; QEMUTimer *timer; int timer_interval; - int lsock; + FdList *lsocks; DisplayState *ds; kbd_layout_t *kbd_layout; int lock_key_sync;