1 /* 2 * Copyright 2012 Collabora, Ltd. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files (the 6 * "Software"), to deal in the Software without restriction, including 7 * without limitation the rights to use, copy, modify, merge, publish, 8 * distribute, sublicense, and/or sell copies of the Software, and to 9 * permit persons to whom the Software is furnished to do so, subject to 10 * the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the 13 * next paragraph) shall be included in all copies or substantial 14 * portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. 24 */ 25 26 #define _GNU_SOURCE 27 28 #include <sys/types.h> 29 #include <sys/socket.h> 30 #include <unistd.h> 31 #include <fcntl.h> 32 #include <errno.h> 33 #include <sys/epoll.h> 34 35 #include "../config.h" 36 #include "wayland-os.h" 37 38 static int 39 set_cloexec_or_close(int fd) 40 { 41 long flags; 42 43 if (fd == -1) 44 return -1; 45 46 flags = fcntl(fd, F_GETFD); 47 if (flags == -1) 48 goto err; 49 50 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) 51 goto err; 52 53 return fd; 54 55 err: 56 close(fd); 57 return -1; 58 } 59 60 int 61 wl_os_socket_cloexec(int domain, int type, int protocol) 62 { 63 int fd; 64 65 fd = socket(domain, type | SOCK_CLOEXEC, protocol); 66 if (fd >= 0) 67 return fd; 68 if (errno != EINVAL) 69 return -1; 70 71 fd = socket(domain, type, protocol); 72 return set_cloexec_or_close(fd); 73 } 74 75 int 76 wl_os_dupfd_cloexec(int fd, long minfd) 77 { 78 int newfd; 79 80 newfd = fcntl(fd, F_DUPFD_CLOEXEC, minfd); 81 if (newfd >= 0) 82 return newfd; 83 if (errno != EINVAL) 84 return -1; 85 86 newfd = fcntl(fd, F_DUPFD, minfd); 87 return set_cloexec_or_close(newfd); 88 } 89 90 static ssize_t 91 recvmsg_cloexec_fallback(int sockfd, struct msghdr *msg, int flags) 92 { 93 ssize_t len; 94 struct cmsghdr *cmsg; 95 unsigned char *data; 96 int *fd; 97 int *end; 98 99 len = recvmsg(sockfd, msg, flags); 100 if (len == -1) 101 return -1; 102 103 if (!msg->msg_control || msg->msg_controllen == 0) 104 return len; 105 106 cmsg = CMSG_FIRSTHDR(msg); 107 for (; cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { 108 if (cmsg->cmsg_level != SOL_SOCKET || 109 cmsg->cmsg_type != SCM_RIGHTS) 110 continue; 111 112 data = CMSG_DATA(cmsg); 113 end = (int *)(data + cmsg->cmsg_len - CMSG_LEN(0)); 114 for (fd = (int *)data; fd < end; ++fd) 115 *fd = set_cloexec_or_close(*fd); 116 } 117 118 return len; 119 } 120 121 ssize_t 122 wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags) 123 { 124 ssize_t len; 125 126 len = recvmsg(sockfd, msg, flags | MSG_CMSG_CLOEXEC); 127 if (len >= 0) 128 return len; 129 if (errno != EINVAL) 130 return -1; 131 132 return recvmsg_cloexec_fallback(sockfd, msg, flags); 133 } 134 135 int 136 wl_os_epoll_create_cloexec(void) 137 { 138 int fd; 139 140 #ifdef EPOLL_CLOEXEC 141 fd = epoll_create1(EPOLL_CLOEXEC); 142 if (fd >= 0) 143 return fd; 144 if (errno != EINVAL) 145 return -1; 146 #endif 147 148 fd = epoll_create(1); 149 return set_cloexec_or_close(fd); 150 } 151 152 int 153 wl_os_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen) 154 { 155 int fd; 156 157 #ifdef HAVE_ACCEPT4 158 fd = accept4(sockfd, addr, addrlen, SOCK_CLOEXEC); 159 if (fd >= 0) 160 return fd; 161 if (errno != ENOSYS) 162 return -1; 163 #endif 164 165 fd = accept(sockfd, addr, addrlen); 166 return set_cloexec_or_close(fd); 167 } 168