X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=src%2Flxc%2Faf_unix.c;h=2ff98dc1b6e6de6cdc3f2ac430219d3dcf4c27a3;hb=970ef13dd2eed4c8dd24056045a1951d8c5f7696;hp=02f32c4542bcb62ae0f3e0f0e6e206cedc7ae037;hpb=d7b58715b6f34f9aba010449225a47d374d6d5fa;p=mirror_lxc.git diff --git a/src/lxc/af_unix.c b/src/lxc/af_unix.c index 02f32c454..2ff98dc1b 100644 --- a/src/lxc/af_unix.c +++ b/src/lxc/af_unix.c @@ -37,6 +37,7 @@ #include "config.h" #include "log.h" +#include "memory_utils.h" #include "raw_syscalls.h" #include "utils.h" @@ -80,7 +81,7 @@ int lxc_abstract_unix_open(const char *path, int type, int flags) ssize_t len; struct sockaddr_un addr; - fd = socket(PF_UNIX, type, 0); + fd = socket(PF_UNIX, type | SOCK_CLOEXEC, 0); if (fd < 0) return -1; @@ -128,7 +129,7 @@ int lxc_abstract_unix_connect(const char *path) ssize_t len; struct sockaddr_un addr; - fd = socket(PF_UNIX, SOCK_STREAM, 0); + fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) return -1; @@ -152,19 +153,16 @@ int lxc_abstract_unix_connect(const char *path) return fd; } -int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds, - void *data, size_t size) +int lxc_abstract_unix_send_fds_iov(int fd, int *sendfds, int num_sendfds, + struct iovec *iov, size_t iovlen) { + __do_free char *cmsgbuf = NULL; int ret; struct msghdr msg; - struct iovec iov; struct cmsghdr *cmsg = NULL; - char buf[1] = {0}; - char *cmsgbuf; size_t cmsgbufsize = CMSG_SPACE(num_sendfds * sizeof(int)); memset(&msg, 0, sizeof(msg)); - memset(&iov, 0, sizeof(iov)); cmsgbuf = malloc(cmsgbufsize); if (!cmsgbuf) { @@ -184,29 +182,48 @@ int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds, memcpy(CMSG_DATA(cmsg), sendfds, num_sendfds * sizeof(int)); - iov.iov_base = data ? data : buf; - iov.iov_len = data ? size : sizeof(buf); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; + msg.msg_iov = iov; + msg.msg_iovlen = iovlen; +again: ret = sendmsg(fd, &msg, MSG_NOSIGNAL); - free(cmsgbuf); + if (ret < 0) + if (errno == EINTR) + goto again; + return ret; } -int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, +int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds, void *data, size_t size) { + char buf[1] = {0}; + struct iovec iov = { + .iov_base = data ? data : buf, + .iov_len = data ? size : sizeof(buf), + }; + return lxc_abstract_unix_send_fds_iov(fd, sendfds, num_sendfds, &iov, + 1); +} + +int lxc_unix_send_fds(int fd, int *sendfds, int num_sendfds, void *data, + size_t size) +{ + return lxc_abstract_unix_send_fds(fd, sendfds, num_sendfds, data, size); +} + +static int lxc_abstract_unix_recv_fds_iov(int fd, int *recvfds, int num_recvfds, + struct iovec *iov, size_t iovlen) +{ + __do_free char *cmsgbuf = NULL; int ret; struct msghdr msg; - struct iovec iov; struct cmsghdr *cmsg = NULL; char buf[1] = {0}; - char *cmsgbuf; - size_t cmsgbufsize = CMSG_SPACE(num_recvfds * sizeof(int)); + size_t cmsgbufsize = CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(num_recvfds * sizeof(int)); memset(&msg, 0, sizeof(msg)); - memset(&iov, 0, sizeof(iov)); cmsgbuf = malloc(cmsgbufsize); if (!cmsgbuf) { @@ -217,27 +234,50 @@ int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, msg.msg_control = cmsgbuf; msg.msg_controllen = cmsgbufsize; - iov.iov_base = data ? data : buf; - iov.iov_len = data ? size : sizeof(buf); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; + msg.msg_iov = iov; + msg.msg_iovlen = iovlen; +again: ret = recvmsg(fd, &msg, 0); - if (ret <= 0) - goto out; + if (ret < 0) { + if (errno == EINTR) + goto again; - cmsg = CMSG_FIRSTHDR(&msg); + goto out; + } + if (ret == 0) + goto out; - memset(recvfds, -1, num_recvfds * sizeof(int)); - if (cmsg && cmsg->cmsg_len == CMSG_LEN(num_recvfds * sizeof(int)) && - cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) - memcpy(recvfds, CMSG_DATA(cmsg), num_recvfds * sizeof(int)); + /* + * If SO_PASSCRED is set we will always get a ucred message. + */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_type != SCM_RIGHTS) + continue; + + memset(recvfds, -1, num_recvfds * sizeof(int)); + if (cmsg && + cmsg->cmsg_len == CMSG_LEN(num_recvfds * sizeof(int)) && + cmsg->cmsg_level == SOL_SOCKET) + memcpy(recvfds, CMSG_DATA(cmsg), num_recvfds * sizeof(int)); + break; + } out: - free(cmsgbuf); return ret; } +int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, + void *data, size_t size) +{ + char buf[1] = {0}; + struct iovec iov = { + .iov_base = data ? data : buf, + .iov_len = data ? size : sizeof(buf), + }; + return lxc_abstract_unix_recv_fds_iov(fd, recvfds, num_recvfds, &iov, 1); +} + int lxc_abstract_unix_send_credential(int fd, void *data, size_t size) { struct msghdr msg = {0}; @@ -310,3 +350,83 @@ int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size) out: return ret; } + +int lxc_unix_sockaddr(struct sockaddr_un *ret, const char *path) +{ + size_t len; + + len = strlen(path); + if (len == 0) + return minus_one_set_errno(EINVAL); + if (path[0] != '/' && path[0] != '@') + return minus_one_set_errno(EINVAL); + if (path[1] == '\0') + return minus_one_set_errno(EINVAL); + + if (len + 1 > sizeof(ret->sun_path)) + return minus_one_set_errno(EINVAL); + + *ret = (struct sockaddr_un){ + .sun_family = AF_UNIX, + }; + + if (path[0] == '@') { + memcpy(ret->sun_path + 1, path + 1, len); + return (int)(offsetof(struct sockaddr_un, sun_path) + len); + } + + memcpy(ret->sun_path, path, len + 1); + return (int)(offsetof(struct sockaddr_un, sun_path) + len + 1); +} + +int lxc_unix_connect_type(struct sockaddr_un *addr, int type) +{ + __do_close_prot_errno int fd = -EBADF; + int ret; + ssize_t len; + + fd = socket(AF_UNIX, type | SOCK_CLOEXEC, 0); + if (fd < 0) { + SYSERROR("Failed to open new AF_UNIX socket"); + return -1; + } + + if (addr->sun_path[0] == '\0') + len = strlen(&addr->sun_path[1]); + else + len = strlen(&addr->sun_path[0]); + + ret = connect(fd, (struct sockaddr *)addr, + offsetof(struct sockaddr_un, sun_path) + len); + if (ret < 0) { + SYSERROR("Failed to bind new AF_UNIX socket"); + return -1; + } + + return move_fd(fd); +} + +int lxc_unix_connect(struct sockaddr_un *addr, int type) +{ + return lxc_unix_connect_type(addr, SOCK_STREAM); +} + +int lxc_socket_set_timeout(int fd, int rcv_timeout, int snd_timeout) +{ + struct timeval out = {0}; + int ret; + + out.tv_sec = snd_timeout; + ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const void *)&out, + sizeof(out)); + if (ret < 0) + return -1; + + out.tv_sec = rcv_timeout; + ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&out, + sizeof(out)); + if (ret < 0) + return -1; + + return 0; +}