/* * 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 // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) #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 #include #ifdef USE_SHM #include #endif #include #include #include #include #include #include #include #include #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; (void)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; }