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