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
set_cloexec_or_close(int fd)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
wl_os_socket_cloexec(int domain,int type,int protocol)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
wl_os_dupfd_cloexec(int fd,long minfd)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
recvmsg_cloexec_fallback(int sockfd,struct msghdr * msg,int flags)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
wl_os_recvmsg_cloexec(int sockfd,struct msghdr * msg,int flags)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
wl_os_epoll_create_cloexec(void)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
wl_os_accept_cloexec(int sockfd,struct sockaddr * addr,socklen_t * addrlen)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