diff options
Diffstat (limited to 'uwac/libuwac/uwac-os.c')
-rw-r--r-- | uwac/libuwac/uwac-os.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/uwac/libuwac/uwac-os.c b/uwac/libuwac/uwac-os.c new file mode 100644 index 0000000..c51c5a2 --- /dev/null +++ b/uwac/libuwac/uwac-os.c @@ -0,0 +1,290 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * This file is an adaptation of src/wayland-os.h from the wayland project and + * shared/os-compatiblity.h from the weston project. + * + * Functions have been renamed just to prevent name clashes. + */ + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif + +#define _GNU_SOURCE + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) +#define USE_SHM +#endif + +/* uClibc and uClibc-ng don't provide O_TMPFILE */ +#if !defined(O_TMPFILE) && !defined(__FreeBSD__) +#define O_TMPFILE (020000000 | O_DIRECTORY) +#endif + +#include <sys/types.h> +#include <sys/socket.h> +#ifdef USE_SHM +#include <sys/mman.h> +#endif +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/epoll.h> + +#include <uwac/config.h> + +#include "uwac-os.h" +#include "uwac-utils.h" + +static int set_cloexec_or_close(int fd) +{ + long flags = 0; + + if (fd == -1) + return -1; + + flags = fcntl(fd, F_GETFD); + + if (flags == -1) + goto err; + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + goto err; + + return fd; +err: + close(fd); + return -1; +} + +int uwac_os_socket_cloexec(int domain, int type, int protocol) +{ + int fd = 0; + fd = socket(domain, type | SOCK_CLOEXEC, protocol); + + if (fd >= 0) + return fd; + + if (errno != EINVAL) + return -1; + + fd = socket(domain, type, protocol); + return set_cloexec_or_close(fd); +} + +int uwac_os_dupfd_cloexec(int fd, long minfd) +{ + int newfd = 0; + newfd = fcntl(fd, F_DUPFD_CLOEXEC, minfd); + + if (newfd >= 0) + return newfd; + + if (errno != EINVAL) + return -1; + + newfd = fcntl(fd, F_DUPFD, minfd); + return set_cloexec_or_close(newfd); +} + +static ssize_t recvmsg_cloexec_fallback(int sockfd, struct msghdr* msg, int flags) +{ + ssize_t len = 0; + struct cmsghdr* cmsg = NULL; + unsigned char* data = NULL; + int* end = NULL; + len = recvmsg(sockfd, msg, flags); + + if (len == -1) + return -1; + + if (!msg->msg_control || msg->msg_controllen == 0) + return len; + + cmsg = CMSG_FIRSTHDR(msg); + + for (; cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) + { + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) + continue; + + data = CMSG_DATA(cmsg); + end = (int*)(data + cmsg->cmsg_len - CMSG_LEN(0)); + + for (int* fd = (int*)data; fd < end; ++fd) + *fd = set_cloexec_or_close(*fd); + } + + return len; +} + +ssize_t uwac_os_recvmsg_cloexec(int sockfd, struct msghdr* msg, int flags) +{ + ssize_t len = 0; + len = recvmsg(sockfd, msg, flags | MSG_CMSG_CLOEXEC); + + if (len >= 0) + return len; + + if (errno != EINVAL) + return -1; + + return recvmsg_cloexec_fallback(sockfd, msg, flags); +} + +int uwac_os_epoll_create_cloexec(void) +{ + int fd = 0; +#ifdef EPOLL_CLOEXEC + fd = epoll_create1(EPOLL_CLOEXEC); + + if (fd >= 0) + return fd; + + if (errno != EINVAL) + return -1; + +#endif + fd = epoll_create(1); + return set_cloexec_or_close(fd); +} + +static int create_tmpfile_cloexec(char* tmpname) +{ + int fd = 0; +#ifdef USE_SHM + fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600); +#elif defined(UWAC_HAVE_MKOSTEMP) + fd = mkostemp(tmpname, O_CLOEXEC); + + if (fd >= 0) + unlink(tmpname); + +#else + fd = mkstemp(tmpname); + + if (fd >= 0) + { + fd = set_cloexec_or_close(fd); + unlink(tmpname); + } + +#endif + return fd; +} + +/* + * Create a new, unique, anonymous file of the given size, and + * return the file descriptor for it. The file descriptor is set + * CLOEXEC. The file is immediately suitable for mmap()'ing + * the given size at offset zero. + * + * The file should not have a permanent backing store like a disk, + * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. + * + * The file name is deleted from the file system. + * + * The file is suitable for buffer sharing between processes by + * transmitting the file descriptor over Unix sockets using the + * SCM_RIGHTS methods. + * + * If the C library implements posix_fallocate(), it is used to + * guarantee that disk space is available for the file at the + * given size. If disk space is insufficient, errno is set to ENOSPC. + * If posix_fallocate() is not supported, program may receive + * SIGBUS on accessing mmap()'ed file contents instead. + */ +int uwac_create_anonymous_file(off_t size) +{ + static const char template[] = "/weston-shared-XXXXXX"; + size_t length = 0; + char* name = NULL; + const char* path = NULL; + int fd = 0; + int ret = 0; + path = getenv("XDG_RUNTIME_DIR"); + + if (!path) + { + errno = ENOENT; + return -1; + } + +#ifdef O_TMPFILE + fd = open(path, O_TMPFILE | O_RDWR | O_EXCL, 0600); +#else + /* + * Some platforms (e.g. FreeBSD) won't support O_TMPFILE and can't + * reasonably emulate it at first blush. Opt to make them rely on + * the create_tmpfile_cloexec() path instead. + */ + fd = -1; +#endif + + if (fd < 0) + { + length = strlen(path) + sizeof(template); + name = xmalloc(length); + + if (!name) + return -1; + + snprintf(name, length, "%s%s", path, template); + fd = create_tmpfile_cloexec(name); + free(name); + } + + if (fd < 0) + return -1; + +#ifdef UWAC_HAVE_POSIX_FALLOCATE + ret = posix_fallocate(fd, 0, size); + + if (ret != 0) + { + close(fd); + errno = ret; + return -1; + } + +#else + ret = ftruncate(fd, size); + + if (ret < 0) + { + close(fd); + return -1; + } + +#endif + return fd; +} |