aboutsummaryrefslogtreecommitdiff
path: root/qemu-char.c
diff options
context:
space:
mode:
authorMark McLoughlin <markmc@redhat.com>2009-07-22 09:11:39 +0100
committerAnthony Liguori <aliguori@us.ibm.com>2009-07-27 08:39:28 -0500
commit7d1740590b6550b2b8ab4901e4f0b704d9733383 (patch)
treef193c3ae6b89a9ed5b0c3609a73319ba77bdeb60 /qemu-char.c
parent9977c8943a56b06908555ea6d1706142a3c9da4d (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.c55
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;