Currently, socket_listen does not allow caller to set custom socket options, which is inconvenient when the caller wants a non-blocking socket or wants to set TCP_NODELAY. Therefore, two new structs are added and an extra parameter is provided to socket_listen. Existing functions are unaffected by providing a NULL pointer.
Signed-off-by: Zihan Yang <whois.zihan.y...@gmail.com> --- include/qemu/sockets.h | 17 +++++++++++- io/channel-socket.c | 2 +- qga/channel-posix.c | 2 +- util/qemu-sockets.c | 74 ++++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 81 insertions(+), 14 deletions(-) diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index 8889bcb..ab06943 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -11,6 +11,21 @@ int inet_aton(const char *cp, struct in_addr *ia); #include "qapi-types.h" +struct QemuSocketOption { + int level; + int optname; + void *optval; + socklen_t optlen; + struct QemuSocketOption *next; +}; +typedef struct QemuSocketOption QemuSocketOption; + +struct QemuSocketConfig { + bool nonblocking; + QemuSocketOption *options; +}; +typedef struct QemuSocketConfig QemuSocketConfig; + /* misc helpers */ int qemu_socket(int domain, int type, int protocol); int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); @@ -40,7 +55,7 @@ int unix_connect(const char *path, Error **errp); SocketAddress *socket_parse(const char *str, Error **errp); int socket_connect(SocketAddress *addr, Error **errp); -int socket_listen(SocketAddress *addr, Error **errp); +int socket_listen(SocketAddress *addr, Error **errp, QemuSocketConfig *sconf); void socket_listen_cleanup(int fd, Error **errp); int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp); diff --git a/io/channel-socket.c b/io/channel-socket.c index 563e297..9942cf0 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -198,7 +198,7 @@ int qio_channel_socket_listen_sync(QIOChannelSocket *ioc, int fd; trace_qio_channel_socket_listen_sync(ioc, addr); - fd = socket_listen(addr, errp); + fd = socket_listen(addr, errp, NULL); if (fd < 0) { trace_qio_channel_socket_listen_fail(ioc); return -1; diff --git a/qga/channel-posix.c b/qga/channel-posix.c index b812bf4..16c8524 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -215,7 +215,7 @@ static gboolean ga_channel_open(GAChannel *c, const gchar *path, return false; } - fd = socket_listen(addr, &local_err); + fd = socket_listen(addr, &local_err, NULL); qapi_free_SocketAddress(addr); if (local_err != NULL) { g_critical("%s", error_get_pretty(local_err)); diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index d6a1e17..b171f23 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -43,7 +43,6 @@ # define AI_NUMERICSERV 0 #endif - static int inet_getport(struct addrinfo *e) { struct sockaddr_in *i4; @@ -196,9 +195,40 @@ static int try_bind(int socket, InetSocketAddress *saddr, struct addrinfo *e) #endif } +static int parse_socket_options(int fd, + Error **errp, + QemuSocketConfig *sconf) +{ + QemuSocketOption *sopt; + + if(!sconf) { + /* No extra socket options are defined */ + return fd; + } + + if(sconf->nonblocking) { + qemu_set_nonblock(fd); + } + + for(sopt = sconf->options; sopt; sopt = sopt->next) { + if(sopt->level < IPPROTO_IP || sopt->optname < SO_DEBUG || + !sopt->optval || sopt->optlen <= 0) { + error_setg(errp, "Invalid socket option:\n" + "level=%d, optname=%d, optval=%p, optlen=%d\n", + sopt->level, sopt->optname, sopt->optval, sopt->optlen); + return -1; + } + qemu_setsockopt(fd, sopt->level, sopt->optname, + sopt->optval, sopt->optlen); + } + + return fd; +} + static int inet_listen_saddr(InetSocketAddress *saddr, int port_offset, - Error **errp) + Error **errp, + QemuSocketConfig *sconf) { struct addrinfo ai,*res,*e; char port[33]; @@ -287,6 +317,11 @@ static int inet_listen_saddr(InetSocketAddress *saddr, } socket_created = true; + if(parse_socket_options(slisten, errp, sconf) < 0) { + error_setg_errno(errp, errno, "Failed to set socket options"); + goto listen_failed; + } + rc = try_bind(slisten, saddr, e); if (rc < 0) { if (errno != EADDRINUSE) { @@ -701,7 +736,8 @@ static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp) } static int vsock_listen_saddr(VsockSocketAddress *vaddr, - Error **errp) + Error **errp, + QemuSocketConfig *sconf) { struct sockaddr_vm svm; int slisten; @@ -716,6 +752,12 @@ static int vsock_listen_saddr(VsockSocketAddress *vaddr, return -1; } + if(parse_socket_options(slisten, errp, sconf) < 0) { + error_setg_errno(errp, errno, "Failed to set socket options"); + closesocket(slisten); + return -1; + } + if (bind(slisten, (const struct sockaddr *)&svm, sizeof(svm)) != 0) { error_setg_errno(errp, errno, "Failed to bind socket"); closesocket(slisten); @@ -763,7 +805,8 @@ static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp) } static int vsock_listen_saddr(VsockSocketAddress *vaddr, - Error **errp) + Error **errp, + QemuSocketConfig *sconf) { vsock_unsupported(errp); return -1; @@ -780,7 +823,8 @@ static int vsock_parse(VsockSocketAddress *addr, const char *str, #ifndef _WIN32 static int unix_listen_saddr(UnixSocketAddress *saddr, - Error **errp) + Error **errp, + QemuSocketConfig *sconf) { struct sockaddr_un un; int sock, fd; @@ -793,6 +837,12 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, return -1; } + if(parse_socket_options(sock, errp, sconf) < 0) { + error_setg_errno(errp, errno, "Failed to set socket option"); + closesocket(sock); + return -1; + } + if (saddr->path && saddr->path[0]) { path = saddr->path; } else { @@ -904,7 +954,8 @@ static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) #else static int unix_listen_saddr(UnixSocketAddress *saddr, - Error **errp) + Error **errp, + QemuSocketConfig *sconf) { error_setg(errp, "unix sockets are not available on windows"); errno = ENOTSUP; @@ -940,7 +991,7 @@ int unix_listen(const char *str, Error **errp) saddr->path = g_strdup(str); } - sock = unix_listen_saddr(saddr, errp); + sock = unix_listen_saddr(saddr, errp, NULL); qapi_free_UnixSocketAddress(saddr); return sock; @@ -1025,17 +1076,18 @@ int socket_connect(SocketAddress *addr, Error **errp) return fd; } -int socket_listen(SocketAddress *addr, Error **errp) +int socket_listen(SocketAddress *addr, Error **errp, + QemuSocketConfig *sconf) { int fd; switch (addr->type) { case SOCKET_ADDRESS_TYPE_INET: - fd = inet_listen_saddr(&addr->u.inet, 0, errp); + fd = inet_listen_saddr(&addr->u.inet, 0, errp, sconf); break; case SOCKET_ADDRESS_TYPE_UNIX: - fd = unix_listen_saddr(&addr->u.q_unix, errp); + fd = unix_listen_saddr(&addr->u.q_unix, errp, sconf); break; case SOCKET_ADDRESS_TYPE_FD: @@ -1043,7 +1095,7 @@ int socket_listen(SocketAddress *addr, Error **errp) break; case SOCKET_ADDRESS_TYPE_VSOCK: - fd = vsock_listen_saddr(&addr->u.vsock, errp); + fd = vsock_listen_saddr(&addr->u.vsock, errp, sconf); break; default: -- 2.7.4