Home | History | Annotate | Download | only in src
      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