Commit 7d1740590b6550b2b8ab4901e4f0b704d9733383
Committed by
Anthony Liguori
1 parent
9977c894
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>
Showing
2 changed files
with
56 additions
and
1 deletions
qemu-char.c
... | ... | @@ -168,6 +168,11 @@ void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len) |
168 | 168 | s->chr_read(s->handler_opaque, buf, len); |
169 | 169 | } |
170 | 170 | |
171 | +int qemu_chr_get_msgfd(CharDriverState *s) | |
172 | +{ | |
173 | + return s->get_msgfd ? s->get_msgfd(s) : -1; | |
174 | +} | |
175 | + | |
171 | 176 | void qemu_chr_accept_input(CharDriverState *s) |
172 | 177 | { |
173 | 178 | if (s->chr_accept_input) |
... | ... | @@ -1832,6 +1837,7 @@ typedef struct { |
1832 | 1837 | int do_telnetopt; |
1833 | 1838 | int do_nodelay; |
1834 | 1839 | int is_unix; |
1840 | + int msgfd; | |
1835 | 1841 | } TCPCharDriver; |
1836 | 1842 | |
1837 | 1843 | static void tcp_chr_accept(void *opaque); |
... | ... | @@ -1907,20 +1913,61 @@ static void tcp_chr_process_IAC_bytes(CharDriverState *chr, |
1907 | 1913 | *size = j; |
1908 | 1914 | } |
1909 | 1915 | |
1916 | +static int tcp_get_msgfd(CharDriverState *chr) | |
1917 | +{ | |
1918 | + TCPCharDriver *s = chr->opaque; | |
1919 | + | |
1920 | + return s->msgfd; | |
1921 | +} | |
1922 | + | |
1910 | 1923 | #ifndef WIN32 |
1924 | +static void unix_process_msgfd(CharDriverState *chr, struct msghdr *msg) | |
1925 | +{ | |
1926 | + TCPCharDriver *s = chr->opaque; | |
1927 | + struct cmsghdr *cmsg; | |
1928 | + | |
1929 | + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { | |
1930 | + int fd; | |
1931 | + | |
1932 | + if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || | |
1933 | + cmsg->cmsg_level != SOL_SOCKET || | |
1934 | + cmsg->cmsg_type != SCM_RIGHTS) | |
1935 | + continue; | |
1936 | + | |
1937 | + fd = *((int *)CMSG_DATA(cmsg)); | |
1938 | + if (fd < 0) | |
1939 | + continue; | |
1940 | + | |
1941 | + if (s->msgfd != -1) | |
1942 | + close(s->msgfd); | |
1943 | + s->msgfd = fd; | |
1944 | + } | |
1945 | +} | |
1946 | + | |
1911 | 1947 | static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) |
1912 | 1948 | { |
1913 | 1949 | TCPCharDriver *s = chr->opaque; |
1914 | 1950 | struct msghdr msg = { 0, }; |
1915 | 1951 | struct iovec iov[1]; |
1952 | + union { | |
1953 | + struct cmsghdr cmsg; | |
1954 | + char control[CMSG_SPACE(sizeof(int))]; | |
1955 | + } msg_control; | |
1956 | + ssize_t ret; | |
1916 | 1957 | |
1917 | 1958 | iov[0].iov_base = buf; |
1918 | 1959 | iov[0].iov_len = len; |
1919 | 1960 | |
1920 | 1961 | msg.msg_iov = iov; |
1921 | 1962 | msg.msg_iovlen = 1; |
1963 | + msg.msg_control = &msg_control; | |
1964 | + msg.msg_controllen = sizeof(msg_control); | |
1965 | + | |
1966 | + ret = recvmsg(s->fd, &msg, 0); | |
1967 | + if (ret > 0 && s->is_unix) | |
1968 | + unix_process_msgfd(chr, &msg); | |
1922 | 1969 | |
1923 | - return recvmsg(s->fd, &msg, 0); | |
1970 | + return ret; | |
1924 | 1971 | } |
1925 | 1972 | #else |
1926 | 1973 | static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) |
... | ... | @@ -1957,6 +2004,10 @@ static void tcp_chr_read(void *opaque) |
1957 | 2004 | tcp_chr_process_IAC_bytes(chr, s, buf, &size); |
1958 | 2005 | if (size > 0) |
1959 | 2006 | qemu_chr_read(chr, buf, size); |
2007 | + if (s->msgfd != -1) { | |
2008 | + close(s->msgfd); | |
2009 | + s->msgfd = -1; | |
2010 | + } | |
1960 | 2011 | } |
1961 | 2012 | } |
1962 | 2013 | |
... | ... | @@ -2118,12 +2169,14 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, |
2118 | 2169 | s->connected = 0; |
2119 | 2170 | s->fd = -1; |
2120 | 2171 | s->listen_fd = -1; |
2172 | + s->msgfd = -1; | |
2121 | 2173 | s->is_unix = is_unix; |
2122 | 2174 | s->do_nodelay = do_nodelay && !is_unix; |
2123 | 2175 | |
2124 | 2176 | chr->opaque = s; |
2125 | 2177 | chr->chr_write = tcp_chr_write; |
2126 | 2178 | chr->chr_close = tcp_chr_close; |
2179 | + chr->get_msgfd = tcp_get_msgfd; | |
2127 | 2180 | |
2128 | 2181 | if (is_listen) { |
2129 | 2182 | s->listen_fd = fd; | ... | ... |
qemu-char.h
... | ... | @@ -51,6 +51,7 @@ struct CharDriverState { |
51 | 51 | int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len); |
52 | 52 | void (*chr_update_read_handler)(struct CharDriverState *s); |
53 | 53 | int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg); |
54 | + int (*get_msgfd)(struct CharDriverState *s); | |
54 | 55 | IOEventHandler *chr_event; |
55 | 56 | IOCanRWHandler *chr_can_read; |
56 | 57 | IOReadHandler *chr_read; |
... | ... | @@ -81,6 +82,7 @@ void qemu_chr_reset(CharDriverState *s); |
81 | 82 | void qemu_chr_initial_reset(void); |
82 | 83 | int qemu_chr_can_read(CharDriverState *s); |
83 | 84 | void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len); |
85 | +int qemu_chr_get_msgfd(CharDriverState *s); | |
84 | 86 | void qemu_chr_accept_input(CharDriverState *s); |
85 | 87 | void qemu_chr_info(Monitor *mon); |
86 | 88 | ... | ... |