diff options
author | Mark McLoughlin <markmc@redhat.com> | 2009-07-22 09:11:39 +0100 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2009-07-27 08:39:28 -0500 |
commit | 7d1740590b6550b2b8ab4901e4f0b704d9733383 (patch) | |
tree | f193c3ae6b89a9ed5b0c3609a73319ba77bdeb60 /qemu-char.c | |
parent | 9977c8943a56b06908555ea6d1706142a3c9da4d (diff) |
Add SCM_RIGHTS support to unix socket character devices
If a file descriptor is passed via a message with SCM_RIGHTS ancillary
data on a unix socket, store the file descriptor for use in the
chr_read() handler. Close the file descriptor if it was not used.
The qemu_chr_get_msgfd() API provides access to the passed descriptor.
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Diffstat (limited to 'qemu-char.c')
-rw-r--r-- | qemu-char.c | 55 |
1 files changed, 54 insertions, 1 deletions
diff --git a/qemu-char.c b/qemu-char.c index 9886228151..6e2cd315a5 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -168,6 +168,11 @@ void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len) s->chr_read(s->handler_opaque, buf, len); } +int qemu_chr_get_msgfd(CharDriverState *s) +{ + return s->get_msgfd ? s->get_msgfd(s) : -1; +} + void qemu_chr_accept_input(CharDriverState *s) { if (s->chr_accept_input) @@ -1832,6 +1837,7 @@ typedef struct { int do_telnetopt; int do_nodelay; int is_unix; + int msgfd; } TCPCharDriver; static void tcp_chr_accept(void *opaque); @@ -1907,20 +1913,61 @@ static void tcp_chr_process_IAC_bytes(CharDriverState *chr, *size = j; } +static int tcp_get_msgfd(CharDriverState *chr) +{ + TCPCharDriver *s = chr->opaque; + + return s->msgfd; +} + #ifndef WIN32 +static void unix_process_msgfd(CharDriverState *chr, struct msghdr *msg) +{ + TCPCharDriver *s = chr->opaque; + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + int fd; + + if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || + cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) + continue; + + fd = *((int *)CMSG_DATA(cmsg)); + if (fd < 0) + continue; + + if (s->msgfd != -1) + close(s->msgfd); + s->msgfd = fd; + } +} + static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) { TCPCharDriver *s = chr->opaque; struct msghdr msg = { 0, }; struct iovec iov[1]; + union { + struct cmsghdr cmsg; + char control[CMSG_SPACE(sizeof(int))]; + } msg_control; + ssize_t ret; iov[0].iov_base = buf; iov[0].iov_len = len; msg.msg_iov = iov; msg.msg_iovlen = 1; + msg.msg_control = &msg_control; + msg.msg_controllen = sizeof(msg_control); + + ret = recvmsg(s->fd, &msg, 0); + if (ret > 0 && s->is_unix) + unix_process_msgfd(chr, &msg); - return recvmsg(s->fd, &msg, 0); + return ret; } #else static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) @@ -1957,6 +2004,10 @@ static void tcp_chr_read(void *opaque) tcp_chr_process_IAC_bytes(chr, s, buf, &size); if (size > 0) qemu_chr_read(chr, buf, size); + if (s->msgfd != -1) { + close(s->msgfd); + s->msgfd = -1; + } } } @@ -2118,12 +2169,14 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, s->connected = 0; s->fd = -1; s->listen_fd = -1; + s->msgfd = -1; s->is_unix = is_unix; s->do_nodelay = do_nodelay && !is_unix; chr->opaque = s; chr->chr_write = tcp_chr_write; chr->chr_close = tcp_chr_close; + chr->get_msgfd = tcp_get_msgfd; if (is_listen) { s->listen_fd = fd; |