summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2013-01-18 01:29:40 -0800
committerKeith Packard <keithp@keithp.com>2013-11-07 14:02:37 -0800
commit7983bf0fbdc2725403f9db6154d0f5bc944040e5 (patch)
treea72d78851c38521dc4c64ee3193c6d373210cc31
parent7b53fb0f9bddae77b3ab8823743db57faee4e99b (diff)
Add support for receiving fds in replies
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@keithp.com> Reviewed-By: Uli Schlachter <psychon@znc.in>
-rw-r--r--src/c_client.py65
-rw-r--r--src/xcb_in.c224
-rw-r--r--src/xcbext.h4
-rw-r--r--src/xcbint.h4
4 files changed, 288 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 8a7af92..b161e93 100644
--- a/src/xcb_in.c
+++ b/src/xcb_in.c
@@ -90,11 +90,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;
@@ -164,13 +179,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);
@@ -198,6 +218,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);
@@ -431,6 +462,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;
@@ -577,6 +613,120 @@ xcb_generic_error_t *xcb_request_check(xcb_connection_t *c, xcb_void_cookie_t co
return ret;
}
+static xcb_generic_event_t *get_special_event(xcb_connection_t *c,
+ xcb_special_event_t *se)
+{
+ xcb_generic_event_t *event = NULL;
+ struct event_list *events;
+
+ if ((events = se->events) != NULL) {
+ event = events->event;
+ if (!(se->events = events->next))
+ se->events_tail = &se->events;
+ free (events);
+ }
+ return event;
+}
+
+xcb_generic_event_t *xcb_poll_for_special_event(xcb_connection_t *c,
+ xcb_special_event_t *se)
+{
+ xcb_generic_event_t *event;
+
+ if(c->has_error)
+ return 0;
+ pthread_mutex_lock(&c->iolock);
+ event = get_special_event(c, se);
+ pthread_mutex_unlock(&c->iolock);
+ return event;
+}
+
+xcb_generic_event_t *xcb_wait_for_special_event(xcb_connection_t *c,
+ xcb_special_event_t *se)
+{
+ xcb_generic_event_t *event;
+
+ if(c->has_error)
+ return 0;
+ pthread_mutex_lock(&c->iolock);
+
+ /* get_special_event returns 0 on empty list. */
+ while(!(event = get_special_event(c, se)))
+ if(!_xcb_conn_wait(c, &se->special_event_cond, 0, 0))
+ break;
+
+ pthread_mutex_unlock(&c->iolock);
+ return event;
+}
+
+xcb_special_event_t *
+xcb_register_for_special_xge(xcb_connection_t *c,
+ uint8_t extension,
+ uint32_t eid,
+ uint32_t *stamp)
+{
+ xcb_special_event_t *se;
+
+ if(c->has_error)
+ return NULL;
+ pthread_mutex_lock(&c->iolock);
+ for (se = c->in.special_events; se; se = se->next) {
+ if (se->extension == extension &&
+ se->eid == eid) {
+ pthread_mutex_unlock(&c->iolock);
+ return NULL;
+ }
+ }
+ se = calloc(1, sizeof(xcb_special_event_t));
+ if (!se) {
+ pthread_mutex_unlock(&c->iolock);
+ return NULL;
+ }
+
+ se->extension = extension;
+ se->eid = eid;
+
+ se->events = NULL;
+ se->events_tail = &se->events;
+ se->stamp = stamp;
+
+ pthread_cond_init(&se->special_event_cond, 0);
+
+ se->next = c->in.special_events;
+ c->in.special_events = se;
+
+ pthread_mutex_unlock(&c->iolock);
+ return se;
+}
+
+void
+xcb_unregister_for_special_event(xcb_connection_t *c,
+ xcb_special_event_t *se)
+{
+ xcb_special_event_t *s, **prev;
+ struct event_list *events, *next;
+
+ if (c->has_error)
+ return;
+
+ pthread_mutex_lock(&c->iolock);
+
+ for (prev = &c->in.special_events; (s = *prev) != NULL; prev = &(s->next)) {
+ if (s == se) {
+ *prev = se->next;
+ for (events = se->events; events; events = next) {
+ next = events->next;
+ free (events->event);
+ free (events);
+ }
+ pthread_cond_destroy(&se->special_event_cond);
+ free (se);
+ break;
+ }
+ }
+ pthread_mutex_unlock(&c->iolock);
+}
+
/* Private interface */
int _xcb_in_init(_xcb_in *in)
@@ -665,11 +815,79 @@ 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 {
+ struct cmsghdr cmsghdr;
+ int fd[XCB_MAX_PASS_FD];
+ } fds;
+ struct msghdr msg = {
+ .msg_name = NULL,
+ .msg_namelen = 0,
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = &fds,
+ .msg_controllen = sizeof (struct cmsghdr) + sizeof(int) * (XCB_MAX_PASS_FD - c->in.in_fd.nfd),
+ };
+ 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_CLOSED_FDPASSING_FAILED);
+ 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 (fds.cmsghdr.cmsg_level == SOL_SOCKET &&
+ fds.cmsghdr.cmsg_type == SCM_RIGHTS)
+ {
+ int nfd = (msg.msg_controllen - sizeof (struct cmsghdr)) / sizeof (int);
+ memmove(&c->in.in_fd.fd[c->in.in_fd.nfd],
+ fds.fd,
+ nfd);
+ c->in.in_fd.nfd += nfd;
+ }
+ }
+#endif
c->in.queue_len += n;
+ }
while(read_packet(c))
/* empty */;
+#if HAVE_SENDMSG
+ if (c->in.in_fd.nfd) {
+ c->in.in_fd.nfd -= c->in.in_fd.ifd;
+ memmove(&c->in.in_fd.fd[0],
+ &c->in.in_fd.fd[c->in.in_fd.ifd],
+ c->in.in_fd.nfd * sizeof (int));
+ c->in.in_fd.ifd = 0;
+
+ /* If we have any left-over file descriptors after emptying
+ * the input buffer, then the server sent some that we weren't
+ * expecting. Close them and mark the connection as broken;
+ */
+ if (c->in.queue_len == 0 && 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_CLOSED_FDPASSING_FAILED);
+ 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 391a4e1..4a01f6f 100644
--- a/src/xcbint.h
+++ b/src/xcbint.h
@@ -92,6 +92,7 @@ typedef struct _xcb_fd {
struct cmsghdr cmsghdr;
int fd[XCB_MAX_PASS_FD];
int nfd;
+ int ifd;
} _xcb_fd;
#endif
@@ -150,6 +151,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);