xref: /openbsd-src/regress/sys/kern/unfdpass/unfdpass.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: unfdpass.c,v 1.16 2008/06/26 05:42:06 ray Exp $	*/
2 /*	$NetBSD: unfdpass.c,v 1.3 1998/06/24 23:51:30 thorpej Exp $	*/
3 
4 /*-
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
10  * NASA Ames Research Center.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * Test passing of file descriptors over Unix domain sockets and socketpairs.
36  */
37 
38 #include <sys/param.h>
39 #include <sys/socket.h>
40 #include <sys/time.h>
41 #include <sys/wait.h>
42 #include <sys/un.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 
52 #define	SOCK_NAME	"test-sock"
53 
54 int	main(int, char *[]);
55 void	child(int);
56 void	catch_sigchld(int);
57 
58 /* ARGSUSED */
59 int
60 main(int argc, char *argv[])
61 {
62 	struct msghdr msg;
63 	int listensock, sock, pfd[2], fd, i;
64 	char fname[16], buf[64];
65 	struct cmsghdr *cmp;
66 	int *files = NULL;
67 	struct sockaddr_un sun, csun;
68 	int csunlen;
69 	pid_t pid;
70 	union {
71 		struct cmsghdr hdr;
72 		char buf[CMSG_SPACE(sizeof(int) * 3)];
73 	} cmsgbuf;
74 	int pflag;
75 	extern char *__progname;
76 
77 	pflag = 0;
78 	while ((i = getopt(argc, argv, "p")) != -1) {
79 		switch (i) {
80 		case 'p':
81 			pflag = 1;
82 			break;
83 		default:
84 			fprintf(stderr, "usage: %s [-p]\n", __progname);
85 			exit(1);
86 		}
87 	}
88 
89 	/*
90 	 * Create the test files.
91 	 */
92 	for (i = 0; i < 3; i++) {
93 		(void) snprintf(fname, sizeof fname, "file%d", i + 1);
94 		if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
95 			err(1, "open %s", fname);
96 		(void) snprintf(buf, sizeof buf, "This is file %d.\n", i + 1);
97 		if (write(fd, buf, strlen(buf)) != strlen(buf))
98 			err(1, "write %s", fname);
99 		(void) close(fd);
100 	}
101 
102 	if (pflag) {
103 		/*
104 		 * Create the socketpair
105 		 */
106 		if (socketpair(PF_LOCAL, SOCK_STREAM, 0, pfd) == -1)
107 			err(1, "socketpair");
108 	} else {
109 		/*
110 		 * Create the listen socket.
111 		 */
112 		if ((listensock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1)
113 			err(1, "socket");
114 
115 		(void) unlink(SOCK_NAME);
116 		(void) memset(&sun, 0, sizeof(sun));
117 		sun.sun_family = AF_LOCAL;
118 		(void) strlcpy(sun.sun_path, SOCK_NAME, sizeof sun.sun_path);
119 		sun.sun_len = SUN_LEN(&sun);
120 
121 		if (bind(listensock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
122 			err(1, "bind");
123 
124 		if (listen(listensock, 1) == -1)
125 			err(1, "listen");
126 		pfd[0] = pfd[1] = -1;
127 	}
128 
129 	/*
130 	 * Create the sender.
131 	 */
132 	(void) signal(SIGCHLD, catch_sigchld);
133 	pid = fork();
134 	switch (pid) {
135 	case -1:
136 		err(1, "fork");
137 		/* NOTREACHED */
138 
139 	case 0:
140 		if (pfd[0] != -1)
141 			close(pfd[0]);
142 		child(pfd[1]);
143 		/* NOTREACHED */
144 	}
145 
146 	if (pfd[0] != -1) {
147 		close(pfd[1]);
148 		sock = pfd[0];
149 	} else {
150 		/*
151 		 * Wait for the sender to connect.
152 		 */
153 		if ((sock = accept(listensock, (struct sockaddr *)&csun,
154 		    &csunlen)) == -1)
155 		err(1, "accept");
156 	}
157 
158 	/*
159 	 * Give sender a chance to run.  We will get going again
160 	 * once the SIGCHLD arrives.
161 	 */
162 	(void) sleep(10);
163 
164 	/*
165 	 * Grab the descriptors passed to us.
166 	 */
167 	(void) memset(&msg, 0, sizeof(msg));
168 	msg.msg_control = &cmsgbuf.buf;
169 	msg.msg_controllen = sizeof(cmsgbuf.buf);
170 
171 	if (recvmsg(sock, &msg, 0) < 0)
172 		err(1, "recvmsg");
173 
174 	(void) close(sock);
175 
176 	if (msg.msg_controllen == 0)
177 		errx(1, "no control messages received");
178 
179 	if (msg.msg_flags & MSG_CTRUNC)
180 		errx(1, "lost control message data");
181 
182 	for (cmp = CMSG_FIRSTHDR(&msg); cmp != NULL;
183 	    cmp = CMSG_NXTHDR(&msg, cmp)) {
184 		if (cmp->cmsg_level != SOL_SOCKET)
185 			errx(1, "bad control message level %d",
186 			    cmp->cmsg_level);
187 
188 		switch (cmp->cmsg_type) {
189 		case SCM_RIGHTS:
190 			if (cmp->cmsg_len != CMSG_LEN(sizeof(int) * 3))
191 				errx(1, "bad fd control message length %d",
192 				    cmp->cmsg_len);
193 
194 			files = (int *)CMSG_DATA(cmp);
195 			break;
196 
197 		default:
198 			errx(1, "unexpected control message");
199 			/* NOTREACHED */
200 		}
201 	}
202 
203 	/*
204 	 * Read the files and print their contents.
205 	 */
206 	if (files == NULL)
207 		warnx("didn't get fd control message");
208 	else {
209 		for (i = 0; i < 3; i++) {
210 			(void) memset(buf, 0, sizeof(buf));
211 			if (read(files[i], buf, sizeof(buf)) <= 0)
212 				err(1, "read file %d (%d)", i + 1, files[i]);
213 			printf("%s", buf);
214 		}
215 	}
216 
217 	/*
218 	 * All done!
219 	 */
220 	exit(0);
221 }
222 
223 void
224 catch_sigchld(sig)
225 	int sig;
226 {
227 	int save_errno = errno;
228 	int status;
229 
230 	(void) wait(&status);
231 	errno = save_errno;
232 }
233 
234 void
235 child(int sock)
236 {
237 	struct msghdr msg;
238 	char fname[16];
239 	struct cmsghdr *cmp;
240 	int i, fd;
241 	struct sockaddr_un sun;
242 	union {
243 		struct cmsghdr hdr;
244 		char buf[CMSG_SPACE(sizeof(int) * 3)];
245 	} cmsgbuf;
246 	int *files;
247 
248 	/*
249 	 * Create socket if needed and connect to the receiver.
250 	 */
251 	if (sock == -1) {
252 		if ((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1)
253 			err(1, "child socket");
254 
255 		(void) memset(&sun, 0, sizeof(sun));
256 		sun.sun_family = AF_LOCAL;
257 		(void) strlcpy(sun.sun_path, SOCK_NAME, sizeof sun.sun_path);
258 		sun.sun_len = SUN_LEN(&sun);
259 
260 		if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
261 			err(1, "child connect");
262 	}
263 
264 	(void) memset(&msg, 0, sizeof(msg));
265 	msg.msg_control = &cmsgbuf.buf;
266 	msg.msg_controllen = sizeof(cmsgbuf.buf);
267 
268 	cmp = CMSG_FIRSTHDR(&msg);
269 	cmp->cmsg_len = CMSG_LEN(sizeof(int) * 3);
270 	cmp->cmsg_level = SOL_SOCKET;
271 	cmp->cmsg_type = SCM_RIGHTS;
272 
273 	/*
274 	 * Open the files again, and pass them to the child over the socket.
275 	 */
276 	files = (int *)CMSG_DATA(cmp);
277 	for (i = 0; i < 3; i++) {
278 		(void) snprintf(fname, sizeof fname, "file%d", i + 1);
279 		if ((fd = open(fname, O_RDONLY, 0666)) == -1)
280 			err(1, "child open %s", fname);
281 		files[i] = fd;
282 	}
283 
284 	if (sendmsg(sock, &msg, 0))
285 		err(1, "child sendmsg");
286 
287 	/*
288 	 * All done!
289 	 */
290 	exit(0);
291 }
292