]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - tools/regression/sockets/unix_passfd/unix_passfd.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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 <limits.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 /*
40  * UNIX domain sockets allow file descriptors to be passed via "ancillary
41  * data", or control messages.  This regression test is intended to exercise
42  * this facility, both performing some basic tests that it operates, and also
43  * causing some kernel edge cases to execute, such as garbage collection when
44  * there are cyclic file descriptor references.  Right now we test only with
45  * stream sockets, but ideally we'd also test with datagram sockets.
46  */
47
48 static void
49 domainsocketpair(const char *test, int *fdp)
50 {
51
52         if (socketpair(PF_UNIX, SOCK_STREAM, 0, fdp) < 0)
53                 err(-1, "%s: socketpair(PF_UNIX, SOCK_STREAM)", test);
54 }
55
56 static void
57 closesocketpair(int *fdp)
58 {
59
60         close(fdp[0]);
61         close(fdp[1]);
62 }
63
64 static void
65 tempfile(const char *test, int *fdp)
66 {
67         char path[PATH_MAX];
68         int fd;
69
70         snprintf(path, PATH_MAX, "/tmp/unix_passfd.XXXXXXXXXXXXXXX");
71         fd = mkstemp(path);
72         if (fd < 0)
73                 err(-1, "%s: mkstemp(%s)", test, path);
74         (void)unlink(path);
75         *fdp = fd;
76 }
77
78 static void
79 dofstat(const char *test, int fd, struct stat *sb)
80 {
81
82         if (fstat(fd, sb) < 0)
83                 err(-1, "%s: fstat", test);
84 }
85
86 static void
87 samefile(const char *test, struct stat *sb1, struct stat *sb2)
88 {
89
90         if (sb1->st_dev != sb2->st_dev)
91                 err(-1, "%s: samefile: different device", test);
92         if (sb1->st_ino != sb2->st_ino)
93                 err(-1, "%s: samefile: different inode", test);
94 }
95
96 static void
97 sendfd(const char *test, int sockfd, int sendfd)
98 {
99         struct iovec iovec;
100         char ch;
101
102         struct {
103                 struct cmsghdr cmsghdr;
104                 int fd;
105         } message;
106         struct msghdr msghdr;
107         ssize_t len;
108
109         bzero(&msghdr, sizeof(msghdr));
110         bzero(&message, sizeof(message));
111         ch = 0;
112
113         msghdr.msg_control = &message;
114         msghdr.msg_controllen = sizeof(message);
115
116         iovec.iov_base = &ch;
117         iovec.iov_len = sizeof(ch);
118
119         msghdr.msg_iov = &iovec;
120         msghdr.msg_iovlen = 1;
121
122         message.cmsghdr.cmsg_len = sizeof(message);
123         message.cmsghdr.cmsg_level = SOL_SOCKET;
124         message.cmsghdr.cmsg_type = SCM_RIGHTS;
125         message.fd = sendfd;
126
127         len = sendmsg(sockfd, &msghdr, 0);
128         if (len < 0)
129                 err(-1, "%s: sendmsg", test);
130         if (len != sizeof(ch))
131                 errx(-1, "%s: sendmsg: %d bytes sent", test, len);
132 }
133
134 static void
135 recvfd(const char *test, int sockfd, int *recvfd)
136 {
137         struct {
138                 struct cmsghdr cmsghdr;
139                 int fd;
140         } message;
141         struct msghdr msghdr;
142         struct iovec iovec;
143         ssize_t len;
144         char ch;
145
146         bzero(&msghdr, sizeof(msghdr));
147         bzero(&message, sizeof(message));
148         ch = 0;
149
150         msghdr.msg_control = &message;
151         msghdr.msg_controllen = sizeof(message);
152
153         iovec.iov_base = &ch;
154         iovec.iov_len = sizeof(ch);
155
156         msghdr.msg_iov = &iovec;
157         msghdr.msg_iovlen = 1;
158
159         iovec.iov_len = sizeof(ch);
160
161         msghdr.msg_iov = &iovec;
162         msghdr.msg_iovlen = 1;
163
164         message.cmsghdr.cmsg_len = sizeof(message);
165         message.cmsghdr.cmsg_level = SOL_SOCKET;
166         message.cmsghdr.cmsg_type = SCM_RIGHTS;
167         message.fd = -1;
168
169         len = recvmsg(sockfd, &msghdr, 0);
170         if (len < 0)
171                 err(-1, "%s: recvmsg", test);
172         if (len != sizeof(ch))
173                 errx(-1, "%s: recvmsg: %d bytes received", test, len);
174         if (message.fd == -1)
175                 errx(-1, "%s: recvmsg: received fd -1", test);
176         *recvfd = message.fd;
177 }
178
179 int
180 main(int argc, char *argv[])
181 {
182         struct stat putfd_1_stat, putfd_2_stat, getfd_1_stat, getfd_2_stat;
183         int fd[2], putfd_1, putfd_2, getfd_1, getfd_2;
184         const char *test;
185
186         /*
187          * First test: put a temporary file into a UNIX domain socket, then
188          * take it out and make sure it's the same file.  First time around,
189          * don't close the reference after sending.
190          */
191         test = "test1-simplesendfd";
192         printf("beginning %s\n", test);
193
194         domainsocketpair(test, fd);
195         tempfile(test, &putfd_1);
196         dofstat(test, putfd_1, &putfd_1_stat);
197         sendfd(test, fd[0], putfd_1);
198         recvfd(test, fd[1], &getfd_1);
199         dofstat(test, getfd_1, &getfd_1_stat);
200         samefile(test, &putfd_1_stat, &getfd_1_stat);
201         close(putfd_1);
202         close(getfd_1);
203         closesocketpair(fd);
204
205         printf("%s passed\n", test);
206
207         /*
208          * Second test: same as first, only close the file reference after
209          * sending, so that the only reference is the descriptor in the UNIX
210          * domain socket buffer.
211          */
212         test = "test2-sendandclose";
213         printf("beginning %s\n", test);
214
215         domainsocketpair(test, fd);
216         tempfile(test, &putfd_1);
217         dofstat(test, putfd_1, &putfd_1_stat);
218         sendfd(test, fd[0], putfd_1);
219         close(putfd_1);
220         recvfd(test, fd[1], &getfd_1);
221         dofstat(test, getfd_1, &getfd_1_stat);
222         samefile(test, &putfd_1_stat, &getfd_1_stat);
223         close(getfd_1);
224         closesocketpair(fd);
225
226         printf("%s passed\n", test);
227
228         /*
229          * Third test: put a temporary file into a UNIX domain socket, then
230          * close both endpoints causing garbage collection to kick off.
231          */
232         test = "test3-sendandcancel";
233         printf("beginning %s\n", test);
234
235         domainsocketpair(test, fd);
236         tempfile(test, &putfd_1);
237         sendfd(test, fd[0], putfd_1);
238         close(putfd_1);
239         closesocketpair(fd);
240
241         printf("%s passed\n", test);
242
243         /*
244          * Send two files.  Then receive them.  Make sure they are returned
245          * in the right order, and both get there.
246          */
247
248         test = "test4-twofile";
249         printf("beginning %s\n", test);
250
251         domainsocketpair(test, fd);
252         tempfile(test, &putfd_1);
253         tempfile(test, &putfd_2);
254         dofstat(test, putfd_1, &putfd_1_stat);
255         dofstat(test, putfd_2, &putfd_2_stat);
256         sendfd(test, fd[0], putfd_1);
257         sendfd(test, fd[0], putfd_2);
258         close(putfd_1);
259         close(putfd_2);
260         recvfd(test, fd[1], &getfd_1);
261         recvfd(test, fd[1], &getfd_2);
262         dofstat(test, getfd_1, &getfd_1_stat);
263         dofstat(test, getfd_2, &getfd_2_stat);
264         samefile(test, &putfd_1_stat, &getfd_1_stat);
265         samefile(test, &putfd_2_stat, &getfd_2_stat);
266         close(getfd_1);
267         close(getfd_2);
268         closesocketpair(fd);
269
270         printf("%s passed\n", test);
271
272         /*
273          * Big bundling test.  Send an endpoint of the UNIX domain socket
274          * over itself, closing the door behind it.
275          */
276
277         test = "test5-bundle";
278         printf("beginning %s\n", test);
279
280         domainsocketpair(test, fd);
281
282         sendfd(test, fd[0], fd[0]);
283         close(fd[0]);
284         recvfd(test, fd[1], &getfd_1);
285         close(getfd_1);
286         close(fd[1]);
287
288         printf("%s passed\n", test);
289
290         /*
291          * Big bundling test part two: Send an endpoint of the UNIX domain
292          * socket over itself, close the door behind it, and never remove it
293          * from the other end.
294          */
295
296         test = "test6-bundlecancel";
297         printf("beginning %s\n", test);
298
299         domainsocketpair(test, fd);
300         sendfd(test, fd[0], fd[0]);
301         sendfd(test, fd[1], fd[0]);
302         closesocketpair(fd);
303
304         printf("%s passed\n", test);
305
306         return (0);
307 }