unfdpass.c revision 1.16
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 54int main(int, char *[]); 55void child(int); 56void catch_sigchld(int); 57 58/* ARGSUSED */ 59int 60main(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 223void 224catch_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 234void 235child(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