]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - tools/regression/sockets/unix_passfd/unix_passfd.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / tools / regression / sockets / unix_passfd / unix_passfd.c
1 /*-
2  * Copyright (c) 2005 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32
33 #include <err.h>
34 #include <fcntl.h>
35 #include <limits.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 /*
41  * UNIX domain sockets allow file descriptors to be passed via "ancillary
42  * data", or control messages.  This regression test is intended to exercise
43  * this facility, both performing some basic tests that it operates, and also
44  * causing some kernel edge cases to execute, such as garbage collection when
45  * there are cyclic file descriptor references.  Right now we test only with
46  * stream sockets, but ideally we'd also test with datagram sockets.
47  */
48
49 static void
50 domainsocketpair(const char *test, int *fdp)
51 {
52
53         if (socketpair(PF_UNIX, SOCK_STREAM, 0, fdp) < 0)
54                 err(-1, "%s: socketpair(PF_UNIX, SOCK_STREAM)", test);
55 }
56
57 static void
58 closesocketpair(int *fdp)
59 {
60
61         close(fdp[0]);
62         close(fdp[1]);
63 }
64
65 static void
66 devnull(const char *test, int *fdp)
67 {
68         int fd;
69
70         fd = open("/dev/null", O_RDONLY);
71         if (fd < 0)
72                 err(-1, "%s: open(/dev/null)", test);
73         *fdp = fd;
74 }
75
76 static void
77 tempfile(const char *test, int *fdp)
78 {
79         char path[PATH_MAX];
80         int fd;
81
82         snprintf(path, PATH_MAX, "/tmp/unix_passfd.XXXXXXXXXXXXXXX");
83         fd = mkstemp(path);
84         if (fd < 0)
85                 err(-1, "%s: mkstemp(%s)", test, path);
86         (void)unlink(path);
87         *fdp = fd;
88 }
89
90 static void
91 dofstat(const char *test, int fd, struct stat *sb)
92 {
93
94         if (fstat(fd, sb) < 0)
95                 err(-1, "%s: fstat", test);
96 }
97
98 static void
99 samefile(const char *test, struct stat *sb1, struct stat *sb2)
100 {
101
102         if (sb1->st_dev != sb2->st_dev)
103                 errx(-1, "%s: samefile: different device", test);
104         if (sb1->st_ino != sb2->st_ino)
105                 errx(-1, "%s: samefile: different inode", test);
106 }
107
108 static void
109 sendfd(const char *test, int sockfd, int sendfd)
110 {
111         struct iovec iovec;
112         char ch;
113
114         char message[CMSG_SPACE(sizeof(int))];
115         struct cmsghdr *cmsghdr;
116         struct msghdr msghdr;
117         ssize_t len;
118
119         bzero(&msghdr, sizeof(msghdr));
120         bzero(&message, sizeof(message));
121         ch = 0;
122
123         msghdr.msg_control = message;
124         msghdr.msg_controllen = sizeof(message);
125
126         iovec.iov_base = &ch;
127         iovec.iov_len = sizeof(ch);
128
129         msghdr.msg_iov = &iovec;
130         msghdr.msg_iovlen = 1;
131
132         cmsghdr = (struct cmsghdr *)message;
133         cmsghdr->cmsg_len = CMSG_LEN(sizeof(int));
134         cmsghdr->cmsg_level = SOL_SOCKET;
135         cmsghdr->cmsg_type = SCM_RIGHTS;
136         *(int *)CMSG_DATA(cmsghdr) = sendfd;
137
138         len = sendmsg(sockfd, &msghdr, 0);
139         if (len < 0)
140                 err(-1, "%s: sendmsg", test);
141         if (len != sizeof(ch))
142                 errx(-1, "%s: sendmsg: %zd bytes sent", test, len);
143 }
144
145 static void
146 recvfd(const char *test, int sockfd, int *recvfd)
147 {
148         struct cmsghdr *cmsghdr;
149         char message[CMSG_SPACE(sizeof(int))];
150         struct msghdr msghdr;
151         struct iovec iovec;
152         ssize_t len;
153         char ch;
154
155         bzero(&msghdr, sizeof(msghdr));
156         ch = 0;
157
158         msghdr.msg_control = message;
159         msghdr.msg_controllen = sizeof(message);
160
161         iovec.iov_base = &ch;
162         iovec.iov_len = sizeof(ch);
163
164         msghdr.msg_iov = &iovec;
165         msghdr.msg_iovlen = 1;
166
167         iovec.iov_len = sizeof(ch);
168
169         msghdr.msg_iov = &iovec;
170         msghdr.msg_iovlen = 1;
171
172         len = recvmsg(sockfd, &msghdr, 0);
173         if (len < 0)
174                 err(-1, "%s: recvmsg", test);
175         if (len != sizeof(ch))
176                 errx(-1, "%s: recvmsg: %zd bytes received", test, len);
177         cmsghdr = CMSG_FIRSTHDR(&msghdr);
178         if (cmsghdr == NULL)
179                 errx(-1, "%s: recvmsg: did not receive control message", test);
180         if (cmsghdr->cmsg_len != CMSG_LEN(sizeof(int)) ||
181             cmsghdr->cmsg_level != SOL_SOCKET ||
182             cmsghdr->cmsg_type != SCM_RIGHTS)
183                 errx(-1, "%s: recvmsg: did not receive single-fd message",
184                     test);
185         *recvfd = *(int *)CMSG_DATA(cmsghdr);
186         if (*recvfd == -1)
187                 errx(-1, "%s: recvmsg: received fd -1", test);
188 }
189
190 int
191 main(int argc, char *argv[])
192 {
193         struct stat putfd_1_stat, putfd_2_stat, getfd_1_stat, getfd_2_stat;
194         int fd[2], putfd_1, putfd_2, getfd_1, getfd_2;
195         const char *test;
196
197         /*
198          * First test: put a temporary file into a UNIX domain socket, then
199          * take it out and make sure it's the same file.  First time around,
200          * don't close the reference after sending.
201          */
202         test = "test1-simplesendfd";
203         printf("beginning %s\n", test);
204
205         domainsocketpair(test, fd);
206         tempfile(test, &putfd_1);
207         dofstat(test, putfd_1, &putfd_1_stat);
208         sendfd(test, fd[0], putfd_1);
209         recvfd(test, fd[1], &getfd_1);
210         dofstat(test, getfd_1, &getfd_1_stat);
211         samefile(test, &putfd_1_stat, &getfd_1_stat);
212         close(putfd_1);
213         close(getfd_1);
214         closesocketpair(fd);
215
216         printf("%s passed\n", test);
217
218         /*
219          * Second test: same as first, only close the file reference after
220          * sending, so that the only reference is the descriptor in the UNIX
221          * domain socket buffer.
222          */
223         test = "test2-sendandclose";
224         printf("beginning %s\n", test);
225
226         domainsocketpair(test, fd);
227         tempfile(test, &putfd_1);
228         dofstat(test, putfd_1, &putfd_1_stat);
229         sendfd(test, fd[0], putfd_1);
230         close(putfd_1);
231         recvfd(test, fd[1], &getfd_1);
232         dofstat(test, getfd_1, &getfd_1_stat);
233         samefile(test, &putfd_1_stat, &getfd_1_stat);
234         close(getfd_1);
235         closesocketpair(fd);
236
237         printf("%s passed\n", test);
238
239         /*
240          * Third test: put a temporary file into a UNIX domain socket, then
241          * close both endpoints causing garbage collection to kick off.
242          */
243         test = "test3-sendandcancel";
244         printf("beginning %s\n", test);
245
246         domainsocketpair(test, fd);
247         tempfile(test, &putfd_1);
248         sendfd(test, fd[0], putfd_1);
249         close(putfd_1);
250         closesocketpair(fd);
251
252         printf("%s passed\n", test);
253
254         /*
255          * Send two files.  Then receive them.  Make sure they are returned
256          * in the right order, and both get there.
257          */
258
259         test = "test4-twofile";
260         printf("beginning %s\n", test);
261
262         domainsocketpair(test, fd);
263         tempfile(test, &putfd_1);
264         tempfile(test, &putfd_2);
265         dofstat(test, putfd_1, &putfd_1_stat);
266         dofstat(test, putfd_2, &putfd_2_stat);
267         sendfd(test, fd[0], putfd_1);
268         sendfd(test, fd[0], putfd_2);
269         close(putfd_1);
270         close(putfd_2);
271         recvfd(test, fd[1], &getfd_1);
272         recvfd(test, fd[1], &getfd_2);
273         dofstat(test, getfd_1, &getfd_1_stat);
274         dofstat(test, getfd_2, &getfd_2_stat);
275         samefile(test, &putfd_1_stat, &getfd_1_stat);
276         samefile(test, &putfd_2_stat, &getfd_2_stat);
277         close(getfd_1);
278         close(getfd_2);
279         closesocketpair(fd);
280
281         printf("%s passed\n", test);
282
283         /*
284          * Big bundling test.  Send an endpoint of the UNIX domain socket
285          * over itself, closing the door behind it.
286          */
287
288         test = "test5-bundle";
289         printf("beginning %s\n", test);
290
291         domainsocketpair(test, fd);
292
293         sendfd(test, fd[0], fd[0]);
294         close(fd[0]);
295         recvfd(test, fd[1], &getfd_1);
296         close(getfd_1);
297         close(fd[1]);
298
299         printf("%s passed\n", test);
300
301         /*
302          * Big bundling test part two: Send an endpoint of the UNIX domain
303          * socket over itself, close the door behind it, and never remove it
304          * from the other end.
305          */
306
307         test = "test6-bundlecancel";
308         printf("beginning %s\n", test);
309
310         domainsocketpair(test, fd);
311         sendfd(test, fd[0], fd[0]);
312         sendfd(test, fd[1], fd[0]);
313         closesocketpair(fd);
314
315         printf("%s passed\n", test);
316
317         /*
318          * Test for PR 151758: Send an character device over the UNIX
319          * domain socket and then close both sockets to orphan the
320          * device.
321          */
322
323         test = "test7-devfsorphan";
324         printf("beginning %s\n", test);
325
326         domainsocketpair(test, fd);
327         devnull(test, &putfd_1);
328         sendfd(test, fd[0], putfd_1);
329         close(putfd_1);
330         closesocketpair(fd);
331
332         printf("%s passed\n", test);
333         
334         return (0);
335 }