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 | ... | ... |