diff --git a/configure b/configure index 82fb60a..5ce0236 100755 --- a/configure +++ b/configure @@ -1673,6 +1673,19 @@ if test "$fdt" = "yes" ; then echo "FDT_LIBS=-lfdt" >> $config_mak fi +cat > $TMPC << EOF +#include +int main(void) { + int val = 1; + if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) < 0) + return 1; + return 0; +} +EOF +if $cc $ARCH_CFLAGS -o $TMPE $TMPC >/dev/null 2> /dev/null ; then + echo "#define HAVE_SO_KEEPALIVE 1" >> $config_h +fi + # XXX: suppress that if [ "$bsd" = "yes" ] ; then echo "#define O_LARGEFILE 0" >> $config_h diff --git a/net.c b/net.c index 7ae1e6d..e787883 100644 --- a/net.c +++ b/net.c @@ -2209,3 +2209,18 @@ void net_client_check(void) vlan->id); } } + +int enable_tcp_keepalive(int sd) +{ +#ifdef HAVE_SO_KEEPALIVE + int val = 1; + + if (setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) < 0) { + return -1; + } +#else + fprintf(stderr, "Warning: TCP keepalives not supported\n"); +#endif + + return 0; +} diff --git a/net.h b/net.h index cdf63a4..29d00fa 100644 --- a/net.h +++ b/net.h @@ -119,6 +119,8 @@ void net_client_check(void); void net_host_device_add(Monitor *mon, const char *device, const char *opts); void net_host_device_remove(Monitor *mon, int vlan_id, const char *device); +int enable_tcp_keepalive(int sd); + #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown" #ifdef __sun__ diff --git a/qemu-char.c b/qemu-char.c index 664cbfd..2d21022 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -1825,6 +1825,7 @@ typedef struct { int max_size; int do_telnetopt; int do_nodelay; + int do_keepalive; int is_unix; } TCPCharDriver; @@ -1998,6 +1999,10 @@ static void tcp_chr_accept(void *opaque) socket_set_nonblock(fd); if (s->do_nodelay) socket_set_nodelay(fd); + if (s->do_keepalive && enable_tcp_keepalive(fd) != 0) { + fprintf(stderr, + "tcp_chr_accept: failed to enable TCP keepalive probes\n"); + } s->fd = fd; qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); tcp_chr_connect(chr); @@ -2027,6 +2032,7 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, int is_listen = 0; int is_waitconnect = 1; int do_nodelay = 0; + int do_keepalive = 0; const char *ptr; ptr = host_str; @@ -2038,6 +2044,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,"keepalive",9) && !is_unix) { + do_keepalive = 1; } else if (!strncmp(ptr,"to=",3)) { /* nothing, inet_listen() parses this one */; } else if (!strncmp(ptr,"ipv4",4)) { @@ -2091,6 +2099,7 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, s->listen_fd = -1; s->is_unix = is_unix; s->do_nodelay = do_nodelay && !is_unix; + s->do_keepalive = do_keepalive; chr->opaque = s; chr->chr_write = tcp_chr_write; @@ -2105,6 +2114,10 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, s->connected = 1; s->fd = fd; socket_set_nodelay(fd); + if (s->do_keepalive && enable_tcp_keepalive(fd) != 0) { + fprintf(stderr, + "failed to enable TCP keepalive probes on %d\n", fd); + } tcp_chr_connect(chr); } diff --git a/qemu-options.hx b/qemu-options.hx index aa786e3..a57e335 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1165,16 +1165,16 @@ telnet on port 5555 to access the qemu port. localhost 5555 @end table -@item tcp:[@var{host}]:@var{port}[,@var{server}][,nowait][,nodelay] +@item tcp:[@var{host}]:@var{port}[,@var{server}][,nowait][,nodelay][,keepalive] The TCP Net Console has two modes of operation. It can send the serial I/O to a location or wait for a connection from a location. By default the TCP Net Console is sent to @var{host} at the @var{port}. If you use the @var{server} option QEMU will wait for a client socket application to connect to the port before continuing, unless the @code{nowait} option was specified. The @code{nodelay} option disables the Nagle buffering -algorithm. If @var{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. +algorithm. The @code{keepalive} option enables TCP keepalives. If @var{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. @table @code @item Example to send tcp console to 192.168.0.2 port 4444 -serial tcp:192.168.0.2:4444 @@ -1184,7 +1184,7 @@ connect to the corresponding character device. -serial tcp:192.168.0.100:4444,server,nowait @end table -@item telnet:@var{host}:@var{port}[,server][,nowait][,nodelay] +@item telnet:@var{host}:@var{port}[,server][,nowait][,nodelay][,keepalive] The telnet protocol is used instead of raw tcp sockets. The options work the same as if you had specified @code{-serial tcp}. The difference is that the port acts like a telnet server or client using diff --git a/vnc.c b/vnc.c index ab1f044..52af93d 100644 --- a/vnc.c +++ b/vnc.c @@ -29,6 +29,7 @@ #include "qemu_socket.h" #include "qemu-timer.h" #include "acl.h" +#include "net.h" #define VNC_REFRESH_INTERVAL (1000 / 30) @@ -2021,6 +2022,10 @@ static void vnc_listen_read(void *opaque) int csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); if (csock != -1) { + if (enable_tcp_keepalive(csock) != 0) { + fprintf(stderr, "VNC: failed to enable TCP keepalive probes\n"); + } + vnc_connect(vs, csock); } }