]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
src: implement APIs for passing FDs over UNIX sockets
authorDaniel P. Berrangé <berrange@redhat.com>
Fri, 17 Jan 2020 11:57:17 +0000 (11:57 +0000)
committerDaniel P. Berrangé <berrange@redhat.com>
Wed, 29 Jan 2020 14:51:39 +0000 (14:51 +0000)
This is a simplified variant of gnulib's passfd module
without the portability code that we do not require.

Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
src/libvirt_private.syms
src/qemu/qemu_interface.c
src/rpc/virnetsocket.c
src/util/virfile.c
src/util/virsocket.c
src/util/virsocket.h

index 2a61eba3e1b41b9189636d4cd9ec7ecaf812b68f..ebf830791e7cba4b906d67dd6c9b59a4828c56d8 100644 (file)
@@ -3012,6 +3012,11 @@ virSecretLookupFormatSecret;
 virSecretLookupParseSecret;
 
 
+# util/virsocket.h
+virSocketRecvFD;
+virSocketSendFD;
+
+
 # util/virsocketaddr.h
 virSocketAddrBroadcast;
 virSocketAddrBroadcastByPrefix;
index bb62b53c0498fc57d1ee230ea90d0a2d38648235..48bb0043feb3de277064997c60b4e60838b3539e 100644 (file)
@@ -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) {
index 23384e5250511a8496ee1ede6547d01e089751ad..b25c57d01e68b9596ad30b1ebc493b0ee4460d6c 100644 (file)
@@ -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
index b3a63fa2eaa99d93df375700e870af6e9ffb37c6..51a0d40b50792f32d4639c4f04ef00275be2144e 100644 (file)
@@ -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)
index 96b9ece2b749908d8d062726a157e34c5ece4f54..9aa29f1eb6c3a144179734281fd85a2536fecae0 100644 (file)
 #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 */
index 33f237886f5641388c192318348e3be01a4c0cb4..e1e7d08bb48aa9b9adb19d6d28b02379ed3f23c7 100644 (file)
@@ -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