[PATCH 5/8] Add support for receiving fds in replies

Keith Packard keithp at keithp.com
Wed Nov 6 19:45:42 PST 2013


Requests signal which replies will have fds, and the replies report
how many fds they expect in byte 1.

Signed-off-by: Keith Packard <keithp at keithp.com>
---
 src/c_client.py | 65 ++++++++++++++++++++++++++++++++++---
 src/xcb_in.c    | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 src/xcbext.h    |  4 ++-
 src/xcbint.h    |  4 +++
 4 files changed, 163 insertions(+), 9 deletions(-)

diff --git a/src/c_client.py b/src/c_client.py
index cc26f72..7280004 100644
--- a/src/c_client.py
+++ b/src/c_client.py
@@ -303,6 +303,7 @@ def _c_type_setup(self, name, postfix):
     self.c_reply_name = _n(name + ('reply',))
     self.c_reply_type = _t(name + ('reply',))
     self.c_cookie_type = _t(name + ('cookie',))
+    self.c_reply_fds_name = _n(name + ('reply_fds',))
 
     self.need_aux = False
     self.need_serialize = False
@@ -1835,7 +1836,7 @@ def c_union(self, name):
     _c_complex(self)
     _c_iterator(self, name)
 
-def _c_request_helper(self, name, cookie_type, void, regular, aux=False):
+def _c_request_helper(self, name, cookie_type, void, regular, aux=False, reply_fds=False):
     '''
     Declares a request function.
     '''
@@ -1864,6 +1865,12 @@ def _c_request_helper(self, name, cookie_type, void, regular, aux=False):
     # What flag is passed to xcb_request
     func_flags = '0' if (void and regular) or (not void and not regular) else 'XCB_REQUEST_CHECKED'
 
+    if reply_fds:
+        if func_flags == '0':
+            func_flags = 'XCB_REQUEST_REPLY_FDS'
+        else:
+            func_flags = func_flags + '|XCB_REQUEST_REPLY_FDS'
+
     # Global extension id variable or NULL for xproto
     func_ext_global = '&' + _ns.c_ext_global_name if _ns.is_ext else '0'
 
@@ -2256,6 +2263,51 @@ def _c_reply(self, name):
 
     _c('}')
 
+def _c_reply_has_fds(self):
+    for field in self.fields:
+        if field.isfd:
+            return True
+    return False
+
+def _c_reply_fds(self, name):
+    '''
+    Declares the function that returns fds related to the reply.
+    '''
+    spacing1 = ' ' * (len(self.c_reply_type) - len('xcb_connection_t'))
+    spacing3 = ' ' * (len(self.c_reply_fds_name) + 2)
+    _h('')
+    _h('/**')
+    _h(' * Return the reply fds')
+    _h(' * @param c      The connection')
+    _h(' * @param reply  The reply')
+    _h(' *')
+    _h(' * Returns the array of reply fds of the request asked by')
+    _h(' * ')
+    _h(' * The returned value must be freed by the caller using free().')
+    _h(' */')
+    _c('')
+    _hc('')
+    _hc('/*****************************************************************************')
+    _hc(' **')
+    _hc(' ** int * %s', self.c_reply_fds_name)
+    _hc(' ** ')
+    _hc(' ** @param xcb_connection_t%s  *c', spacing1)
+    _hc(' ** @param %s  *reply', self.c_reply_type)
+    _hc(' ** @returns int *')
+    _hc(' **')
+    _hc(' *****************************************************************************/')
+    _hc(' ')
+    _hc('int *')
+    _hc('%s (xcb_connection_t%s  *c  /**< */,', self.c_reply_fds_name, spacing1)
+    _h('%s%s  *reply  /**< */);', spacing3, self.c_reply_type)
+    _c('%s%s  *reply  /**< */)', spacing3, self.c_reply_type)
+    _c('{')
+    
+    _c('    return xcb_get_reply_fds(c, reply, sizeof(%s) + 4 * reply->length);', self.c_reply_type)
+
+    _c('}')
+    
+
 def _c_opcode(name, opcode):
     '''
     Declares the opcode define for requests, events, and errors.
@@ -2816,14 +2868,17 @@ def c_request(self, name):
         # Reply structure definition
         _c_complex(self.reply)
         # Request prototypes
-        _c_request_helper(self, name, self.c_cookie_type, False, True)
-        _c_request_helper(self, name, self.c_cookie_type, False, False)
+        has_fds = _c_reply_has_fds(self.reply)
+        _c_request_helper(self, name, self.c_cookie_type, False, True, False, has_fds)
+        _c_request_helper(self, name, self.c_cookie_type, False, False, False, has_fds)
         if self.need_aux:
-            _c_request_helper(self, name, self.c_cookie_type, False, True, True)
-            _c_request_helper(self, name, self.c_cookie_type, False, False, True)
+            _c_request_helper(self, name, self.c_cookie_type, False, True, True, has_fds)
+            _c_request_helper(self, name, self.c_cookie_type, False, False, True, has_fds)
         # Reply accessors
         _c_accessors(self.reply, name + ('reply',), name)
         _c_reply(self, name)
+        if has_fds:
+            _c_reply_fds(self, name)
     else:
         # Request prototypes
         _c_request_helper(self, name, 'xcb_void_cookie_t', True, False)
diff --git a/src/xcb_in.c b/src/xcb_in.c
index ac4d284..0a78ae6 100644
--- a/src/xcb_in.c
+++ b/src/xcb_in.c
@@ -91,11 +91,26 @@ static void remove_finished_readers(reader_list **prev_reader, uint64_t complete
     }
 }
 
+#if HAVE_SENDMSG
+static int read_fds(xcb_connection_t *c, int *fds, int nfd)
+{
+    int *ifds = &c->in.in_fd.fd[c->in.in_fd.ifd];
+    int infd = c->in.in_fd.nfd - c->in.in_fd.ifd;
+
+    if (nfd > infd)
+        return 0;
+    memcpy(fds, ifds, nfd * sizeof (int));
+    c->in.in_fd.ifd += nfd;
+    return 1;
+}
+#endif
+
 static int read_packet(xcb_connection_t *c)
 {
     xcb_generic_reply_t genrep;
     uint64_t length = 32;
     uint64_t eventlength = 0; /* length after first 32 bytes for GenericEvents */
+    int nfd = 0;         /* Number of file descriptors attached to the reply */
     uint64_t bufsize;
     void *buf;
     pending_reply *pend = 0;
@@ -165,13 +180,18 @@ static int read_packet(xcb_connection_t *c)
             genrep.length = p[2] * p[3] * 2;
         }
         length += genrep.length * 4;
+
+        /* XXX a bit of a hack -- we "know" that all FD replys place
+         * the number of fds in the pad0 byte */
+        if (pend && pend->flags & XCB_REQUEST_REPLY_FDS)
+            nfd = genrep.pad0;
     }
 
     /* XGE events may have sizes > 32 */
     if ((genrep.response_type & 0x7f) == XCB_XGE_EVENT)
         eventlength = genrep.length * 4;
 
-    bufsize = length + eventlength +
+    bufsize = length + eventlength + nfd * sizeof(int)  +
         (genrep.response_type == XCB_REPLY ? 0 : sizeof(uint32_t));
     if (bufsize < INT32_MAX)
         buf = malloc((size_t) bufsize);
@@ -199,6 +219,17 @@ static int read_packet(xcb_connection_t *c)
         }
     }
 
+#if HAVE_SENDMSG
+    if (nfd)
+    {
+        if (!read_fds(c, (int *) &((char *) buf)[length], nfd))
+        {
+            free(buf);
+            return 0;
+        }
+    }
+#endif
+
     if(pend && (pend->flags & XCB_REQUEST_DISCARD_REPLY))
     {
         free(buf);
@@ -432,6 +463,11 @@ void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_
     return ret;
 }
 
+int *xcb_get_reply_fds(xcb_connection_t *c, void *reply, size_t reply_size)
+{
+    return (int *) (&((char *) reply)[reply_size]);
+}
+
 static void insert_pending_discard(xcb_connection_t *c, pending_reply **prev_next, uint64_t seq)
 {
     pending_reply *pend;
@@ -666,11 +702,68 @@ void _xcb_in_replies_done(xcb_connection_t *c)
 
 int _xcb_in_read(xcb_connection_t *c)
 {
-    int n = recv(c->fd, c->in.queue + c->in.queue_len, sizeof(c->in.queue) - c->in.queue_len, 0);
-    if(n > 0)
+    int n;
+
+#if HAVE_SENDMSG
+    struct iovec    iov = {
+        .iov_base = c->in.queue + c->in.queue_len,
+        .iov_len = sizeof(c->in.queue) - c->in.queue_len,
+    };
+    struct msghdr msg = {
+        .msg_name = NULL,
+        .msg_namelen = 0,
+        .msg_iov = &iov,
+        .msg_iovlen = 1,
+        .msg_control = &c->in.in_fd,
+        .msg_controllen = sizeof (struct cmsghdr) + sizeof (int) * XCB_MAX_PASS_FD,
+    };
+    n = recvmsg(c->fd, &msg, 0);
+
+    /* Check for truncation errors. Only MSG_CTRUNC is
+     * probably possible here, which would indicate that
+     * the sender tried to transmit more than XCB_MAX_PASS_FD
+     * file descriptors.
+     */
+    if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
+        _xcb_conn_shutdown(c, XCB_CONN_ERROR);
+        return 0;
+    }
+#else
+    n = recv(c->fd, c->in.queue + c->in.queue_len, sizeof(c->in.queue) - c->in.queue_len, 0);
+#endif
+    if(n > 0) {
+#if HAVE_SENDMSG
+        if (msg.msg_controllen > sizeof (struct cmsghdr))
+        {
+            if (c->in.in_fd.cmsghdr.cmsg_level == SOL_SOCKET &&
+                c->in.in_fd.cmsghdr.cmsg_type == SCM_RIGHTS &&
+                !((msg.msg_flags & MSG_TRUNC) ||
+                  (msg.msg_flags & MSG_CTRUNC)))
+            {
+                c->in.in_fd.nfd = (msg.msg_controllen - sizeof (struct cmsghdr)) / sizeof (int);
+            }
+        }
+#endif
         c->in.queue_len += n;
+    }
     while(read_packet(c))
         /* empty */;
+#if HAVE_SENDMSG
+    c->in.in_fd.nfd -= c->in.in_fd.ifd;
+    c->in.in_fd.ifd = 0;
+
+    /* If we have any left-over file descriptors, then
+     * the server sent some that we weren't expecting.
+     * Close them and mark the connection as broken;
+     */
+    if (c->in.in_fd.nfd != 0) {
+        int i;
+        for (i = 0; i < c->in.in_fd.nfd; i++)
+            close(c->in.in_fd.fd[i]);
+        _xcb_conn_shutdown(c, XCB_CONN_ERROR);
+        return 0;
+    }
+#endif
 #ifndef _WIN32
     if((n > 0) || (n < 0 && errno == EAGAIN))
 #else
diff --git a/src/xcbext.h b/src/xcbext.h
index 44030c3..1eb1be7 100644
--- a/src/xcbext.h
+++ b/src/xcbext.h
@@ -54,7 +54,8 @@ typedef struct {
 enum xcb_send_request_flags_t {
     XCB_REQUEST_CHECKED = 1 << 0,
     XCB_REQUEST_RAW = 1 << 1,
-    XCB_REQUEST_DISCARD_REPLY = 1 << 2
+    XCB_REQUEST_DISCARD_REPLY = 1 << 2,
+    XCB_REQUEST_REPLY_FDS = 1 << 3
 };
 
 unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *request);
@@ -91,6 +92,7 @@ int xcb_writev(xcb_connection_t *c, struct iovec *vector, int count, uint64_t re
 
 void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_error_t **e);
 int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error);
+int *xcb_get_reply_fds(xcb_connection_t *c, void *reply, size_t replylen);
 
 
 /* xcb_util.c */
diff --git a/src/xcbint.h b/src/xcbint.h
index b2dfed3..bbc5398 100644
--- a/src/xcbint.h
+++ b/src/xcbint.h
@@ -88,6 +88,7 @@ typedef struct _xcb_fd {
     struct cmsghdr cmsghdr;
     int fd[XCB_MAX_PASS_FD];
     int nfd;
+    int ifd;
 } _xcb_fd;
 #endif
 
@@ -146,6 +147,9 @@ typedef struct _xcb_in {
 
     struct pending_reply *pending_replies;
     struct pending_reply **pending_replies_tail;
+#if HAVE_SENDMSG
+    _xcb_fd in_fd;
+#endif
 } _xcb_in;
 
 int _xcb_in_init(_xcb_in *in);
-- 
1.8.4.2



More information about the xorg-devel mailing list