[libvirt PATCH v2 11/56] src: implement APIs for passing FDs over UNIX sockets

Daniel P. Berrangé berrange at redhat.com
Tue Jan 28 13:10:52 UTC 2020


This is a simplified variant of gnulib's passfd module
without the portability code that we do not require.

Reviewed-by: Pavel Hrdina <phrdina at redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange at redhat.com>
---
 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




More information about the libvir-list mailing list