This is a simplified variant of gnulib's passfd module without the portability code that we do not require. Reviewed-by: Pavel Hrdina <phrdina@xxxxxxxxxx> Signed-off-by: Daniel P. Berrangé <berrange@xxxxxxxxxx> --- src/libvirt_private.syms | 5 ++ src/qemu/qemu_interface.c | 4 +- src/rpc/virnetsocket.c | 5 +- src/util/virfile.c | 5 +- src/util/virsocket.c | 139 +++++++++++++++++++++++++++++++++++++- src/util/virsocket.h | 3 + 6 files changed, 151 insertions(+), 10 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ed451f7bfc..022ab29aa0 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3006,6 +3006,11 @@ virSecretLookupFormatSecret; virSecretLookupParseSecret; +# util/virsocket.h +virSocketRecvFD; +virSocketSendFD; + + # util/virsocketaddr.h virSocketAddrBroadcast; virSocketAddrBroadcastByPrefix; diff --git a/src/qemu/qemu_interface.c b/src/qemu/qemu_interface.c index bb62b53c04..48bb0043fe 100644 --- a/src/qemu/qemu_interface.c +++ b/src/qemu/qemu_interface.c @@ -25,7 +25,6 @@ #include "domain_audit.h" #include "domain_nwfilter.h" #include "qemu_interface.h" -#include "passfd.h" #include "viralloc.h" #include "virlog.h" #include "virstring.h" @@ -34,6 +33,7 @@ #include "virnetdevmacvlan.h" #include "virnetdevbridge.h" #include "virnetdevvportprofile.h" +#include "virsocket.h" #include <sys/stat.h> #include <fcntl.h> @@ -347,7 +347,7 @@ qemuCreateInBridgePortWithHelper(virQEMUDriverConfigPtr cfg, } do { - *tapfd = recvfd(pair[0], 0); + *tapfd = virSocketRecvFD(pair[0], 0); } while (*tapfd < 0 && errno == EINTR); if (*tapfd < 0) { diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c index 23384e5250..b25c57d01e 100644 --- a/src/rpc/virnetsocket.c +++ b/src/rpc/virnetsocket.c @@ -56,7 +56,6 @@ #include "virprobe.h" #include "virprocess.h" #include "virstring.h" -#include "passfd.h" #if WITH_SSH2 # include "virnetsshsession.h" @@ -2029,7 +2028,7 @@ int virNetSocketSendFD(virNetSocketPtr sock, int fd) virObjectLock(sock); PROBE(RPC_SOCKET_SEND_FD, "sock=%p fd=%d", sock, fd); - if (sendfd(sock->fd, fd) < 0) { + if (virSocketSendFD(sock->fd, fd) < 0) { if (errno == EAGAIN) ret = 0; else @@ -2062,7 +2061,7 @@ int virNetSocketRecvFD(virNetSocketPtr sock, int *fd) } virObjectLock(sock); - if ((*fd = recvfd(sock->fd, O_CLOEXEC)) < 0) { + if ((*fd = virSocketRecvFD(sock->fd, O_CLOEXEC)) < 0) { if (errno == EAGAIN) ret = 0; else diff --git a/src/util/virfile.c b/src/util/virfile.c index b3a63fa2ea..51a0d40b50 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -25,7 +25,6 @@ #include <config.h> #include "internal.h" -#include <passfd.h> #include <fcntl.h> #ifndef WIN32 # include <termios.h> @@ -2268,7 +2267,7 @@ virFileOpenForked(const char *path, int openflags, mode_t mode, } do { - ret = sendfd(pair[1], fd); + ret = virSocketSendFD(pair[1], fd); } while (ret < 0 && errno == EINTR); if (ret < 0) { @@ -2302,7 +2301,7 @@ virFileOpenForked(const char *path, int openflags, mode_t mode, VIR_FORCE_CLOSE(pair[1]); do { - fd = recvfd(pair[0], 0); + fd = virSocketRecvFD(pair[0], 0); } while (fd < 0 && errno == EINTR); VIR_FORCE_CLOSE(pair[0]); /* NB: this preserves errno */ if (fd < 0) diff --git a/src/util/virsocket.c b/src/util/virsocket.c index 96b9ece2b7..9aa29f1eb6 100644 --- a/src/util/virsocket.c +++ b/src/util/virsocket.c @@ -19,10 +19,12 @@ #include <config.h> #include "virsocket.h" +#include "virutil.h" +#include "virfile.h" -#ifdef WIN32 +#include <fcntl.h> -# include <fcntl.h> +#ifdef WIN32 # define FD2SK(fd) _get_osfhandle(fd) # define SK2FD(sk) (_open_osfhandle((intptr_t) (sk), O_RDWR | O_BINARY)) @@ -365,3 +367,136 @@ vir_socket(int domain, int type, int protocol) } #endif /* WIN32 */ + + +/* The following two methods are derived from GNULIB's + * passfd code */ + +/* MSG_CMSG_CLOEXEC is defined only on Linux, as of 2011. */ +#ifndef MSG_CMSG_CLOEXEC +# define MSG_CMSG_CLOEXEC 0 +#endif + +#ifndef WIN32 +/* virSocketSendFD sends the file descriptor fd along the socket + to a process calling virSocketRecvFD on the other end. + + Return 0 on success, or -1 with errno set in case of error. +*/ +int +virSocketSendFD(int sock, int fd) +{ + char byte = 0; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + char buf[CMSG_SPACE(sizeof(fd))]; + + /* send at least one char */ + memset(&msg, 0, sizeof(msg)); + iov.iov_base = &byte; + iov.iov_len = 1; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + /* Initialize the payload: */ + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + msg.msg_controllen = cmsg->cmsg_len; + + if (sendmsg(sock, &msg, 0) != iov.iov_len) + return -1; + return 0; +} + + +/* virSocketRecvFD receives a file descriptor through the socket. + The flags are a bitmask, possibly including O_CLOEXEC (defined in <fcntl.h>). + + Return the fd on success, or -1 with errno set in case of error. +*/ +int +virSocketRecvFD(int sock, int fdflags) +{ + char byte = 0; + struct iovec iov; + struct msghdr msg; + int fd = -1; + ssize_t len; + struct cmsghdr *cmsg; + char buf[CMSG_SPACE(sizeof(fd))]; + int fdflags_recvmsg = fdflags & O_CLOEXEC ? MSG_CMSG_CLOEXEC : 0; + + if ((fdflags & ~O_CLOEXEC) != 0) { + errno = EINVAL; + return -1; + } + + /* send at least one char */ + memset(&msg, 0, sizeof(msg)); + iov.iov_base = &byte; + iov.iov_len = 1; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + /* Initialize the payload: */ + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + msg.msg_controllen = cmsg->cmsg_len; + + len = recvmsg(sock, &msg, fdflags_recvmsg); + if (len < 0) + return -1; + + cmsg = CMSG_FIRSTHDR(&msg); + /* be paranoiac */ + if (len == 0 || cmsg == NULL || cmsg->cmsg_len != CMSG_LEN(sizeof(fd)) + || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { + /* fake errno: at end the file is not available */ + errno = len ? EACCES : ENOTCONN; + return -1; + } + + memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd)); + + /* set close-on-exec flag */ + if (!MSG_CMSG_CLOEXEC && (fdflags & O_CLOEXEC)) { + if (virSetCloseExec(fd) < 0) { + int saved_errno = errno; + VIR_FORCE_CLOSE(fd); + errno = saved_errno; + return -1; + } + } + + return fd; +} +#else /* WIN32 */ +int +virSocketSendFD(int sock G_GNUC_UNUSED, int fd G_GNUC_UNUSED) +{ + errno = ENOSYS; + return -1; +} + +int +virSocketRecvFD(int sock G_GNUC_UNUSED, int fdflags G_GNUC_UNUSED) +{ + errno = ENOSYS; + return -1; +} +#endif /* WIN32 */ diff --git a/src/util/virsocket.h b/src/util/virsocket.h index 33f237886f..e1e7d08bb4 100644 --- a/src/util/virsocket.h +++ b/src/util/virsocket.h @@ -20,6 +20,9 @@ #include "internal.h" +int virSocketSendFD(int sock, int fd); +int virSocketRecvFD(int sock, int fdflags); + #ifdef WIN32 # define WIN32_LEAN_AND_MEAN -- 2.24.1