Requests signal which replies will have fds, and the replies report how many fds they expect in byte 1.
Signed-off-by: Keith Packard <[email protected]> --- src/c_client.py | 65 ++++++++++++++++++++++++++++++++++++++++++++---- src/xcb_in.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/xcbext.h | 4 ++- src/xcbint.h | 8 +++--- 4 files changed, 141 insertions(+), 12 deletions(-) diff --git a/src/c_client.py b/src/c_client.py index ed92185..4c5da0a 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..f9a10e7 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 -1; + memcpy(fds, ifds, nfd * sizeof (int)); + c->in.in_fd.ifd += nfd; + return nfd * sizeof (int); +} +#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) <= 0) + { + 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,9 +702,43 @@ 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, + }; + assert(c->in.in_fd.ifd == c->in.in_fd.nfd); + c->in.in_fd.ifd = 0; + c->in.in_fd.nfd = 0; + n = recvmsg(c->fd, &msg, 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 */; #ifndef _WIN32 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 356350b..75d159f 100644 --- a/src/xcbint.h +++ b/src/xcbint.h @@ -84,11 +84,12 @@ typedef void (*xcb_return_socket_func_t)(void *closure); #if HAVE_SENDMSG #define XCB_MAX_PASS_FD 16 -typedef struct _xcb_out_fd { +typedef struct _xcb_fd { struct cmsghdr cmsghdr; int fd[XCB_MAX_PASS_FD]; int nfd; -} _xcb_out_fd; + int ifd; +} _xcb_fd; #endif typedef struct _xcb_out { @@ -112,7 +113,7 @@ typedef struct _xcb_out { uint32_t value; } maximum_request_length; #if HAVE_SENDMSG - _xcb_out_fd out_fd; + _xcb_fd out_fd; #endif } _xcb_out; @@ -146,6 +147,7 @@ typedef struct _xcb_in { struct pending_reply *pending_replies; struct pending_reply **pending_replies_tail; + _xcb_fd in_fd; } _xcb_in; int _xcb_in_init(_xcb_in *in); -- 1.8.4.2 _______________________________________________ [email protected]: X.Org development Archives: http://lists.x.org/archives/xorg-devel Info: http://lists.x.org/mailman/listinfo/xorg-devel
