popen.c revision 77274
1/* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. 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 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35#if 0 36static char sccsid[] = "@(#)popen.c 8.1 (Berkeley) 6/6/93"; 37#endif 38static const char rcsid[] = 39 "$FreeBSD: head/usr.bin/mail/popen.c 77274 2001-05-27 20:26:22Z mikeh $"; 40#endif /* not lint */ 41 42#include "rcv.h" 43#include <sys/wait.h> 44#include <fcntl.h> 45#include "extern.h" 46 47#define READ 0 48#define WRITE 1 49 50struct fp { 51 FILE *fp; 52 int pipe; 53 int pid; 54 struct fp *link; 55}; 56static struct fp *fp_head; 57 58struct child { 59 int pid; 60 char done; 61 char free; 62 int status; 63 struct child *link; 64}; 65static struct child *child; 66static struct child *findchild __P((int)); 67static void delchild __P((struct child *)); 68static int file_pid __P((FILE *)); 69 70FILE * 71Fopen(path, mode) 72 const char *path, *mode; 73{ 74 FILE *fp; 75 76 if ((fp = fopen(path, mode)) != NULL) { 77 register_file(fp, 0, 0); 78 (void)fcntl(fileno(fp), F_SETFD, 1); 79 } 80 return (fp); 81} 82 83FILE * 84Fdopen(fd, mode) 85 int fd; 86 const char *mode; 87{ 88 FILE *fp; 89 90 if ((fp = fdopen(fd, mode)) != NULL) { 91 register_file(fp, 0, 0); 92 (void)fcntl(fileno(fp), F_SETFD, 1); 93 } 94 return (fp); 95} 96 97int 98Fclose(fp) 99 FILE *fp; 100{ 101 unregister_file(fp); 102 return (fclose(fp)); 103} 104 105FILE * 106Popen(cmd, mode) 107 char *cmd; 108 const char *mode; 109{ 110 int p[2]; 111 int myside, hisside, fd0, fd1; 112 int pid; 113 FILE *fp; 114 115 if (pipe(p) < 0) 116 return (NULL); 117 (void)fcntl(p[READ], F_SETFD, 1); 118 (void)fcntl(p[WRITE], F_SETFD, 1); 119 if (*mode == 'r') { 120 myside = p[READ]; 121 fd0 = -1; 122 hisside = fd1 = p[WRITE]; 123 } else { 124 myside = p[WRITE]; 125 hisside = fd0 = p[READ]; 126 fd1 = -1; 127 } 128 if ((pid = start_command(cmd, 0, fd0, fd1, NULL, NULL, NULL)) < 0) { 129 (void)close(p[READ]); 130 (void)close(p[WRITE]); 131 return (NULL); 132 } 133 (void)close(hisside); 134 if ((fp = fdopen(myside, mode)) != NULL) 135 register_file(fp, 1, pid); 136 return (fp); 137} 138 139int 140Pclose(ptr) 141 FILE *ptr; 142{ 143 int i; 144 int omask; 145 146 i = file_pid(ptr); 147 unregister_file(ptr); 148 (void)fclose(ptr); 149 omask = sigblock(sigmask(SIGINT)|sigmask(SIGHUP)); 150 i = wait_child(i); 151 (void)sigsetmask(omask); 152 return (i); 153} 154 155void 156close_all_files() 157{ 158 159 while (fp_head != NULL) 160 if (fp_head->pipe) 161 (void)Pclose(fp_head->fp); 162 else 163 (void)Fclose(fp_head->fp); 164} 165 166void 167register_file(fp, pipe, pid) 168 FILE *fp; 169 int pipe, pid; 170{ 171 struct fp *fpp; 172 173 if ((fpp = malloc(sizeof(*fpp))) == NULL) 174 err(1, "Out of memory"); 175 fpp->fp = fp; 176 fpp->pipe = pipe; 177 fpp->pid = pid; 178 fpp->link = fp_head; 179 fp_head = fpp; 180} 181 182void 183unregister_file(fp) 184 FILE *fp; 185{ 186 struct fp **pp, *p; 187 188 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link) 189 if (p->fp == fp) { 190 *pp = p->link; 191 (void)free(p); 192 return; 193 } 194 errx(1, "Invalid file pointer"); 195 /*NOTREACHED*/ 196} 197 198int 199file_pid(fp) 200 FILE *fp; 201{ 202 struct fp *p; 203 204 for (p = fp_head; p != NULL; p = p->link) 205 if (p->fp == fp) 206 return (p->pid); 207 errx(1, "Invalid file pointer"); 208 /*NOTREACHED*/ 209} 210 211/* 212 * Run a command without a shell, with optional arguments and splicing 213 * of stdin and stdout. The command name can be a sequence of words. 214 * Signals must be handled by the caller. 215 * "Mask" contains the signals to ignore in the new process. 216 * SIGINT is enabled unless it's in the mask. 217 */ 218/*VARARGS4*/ 219int 220run_command(cmd, mask, infd, outfd, a0, a1, a2) 221 char *cmd; 222 int mask, infd, outfd; 223 char *a0, *a1, *a2; 224{ 225 int pid; 226 227 if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0) 228 return (-1); 229 return (wait_command(pid)); 230} 231 232/*VARARGS4*/ 233int 234start_command(cmd, mask, infd, outfd, a0, a1, a2) 235 char *cmd; 236 int mask, infd, outfd; 237 char *a0, *a1, *a2; 238{ 239 int pid; 240 241 if ((pid = fork()) < 0) { 242 warn("fork"); 243 return (-1); 244 } 245 if (pid == 0) { 246 char *argv[100]; 247 int i = getrawlist(cmd, argv, sizeof(argv) / sizeof(*argv)); 248 249 if ((argv[i++] = a0) != NULL && 250 (argv[i++] = a1) != NULL && 251 (argv[i++] = a2) != NULL) 252 argv[i] = NULL; 253 prepare_child(mask, infd, outfd); 254 execvp(argv[0], argv); 255 warn("%s", argv[0]); 256 _exit(1); 257 } 258 return (pid); 259} 260 261void 262prepare_child(mask, infd, outfd) 263 int mask, infd, outfd; 264{ 265 int i; 266 267 /* 268 * All file descriptors other than 0, 1, and 2 are supposed to be 269 * close-on-exec. 270 */ 271 if (infd >= 0) 272 dup2(infd, 0); 273 if (outfd >= 0) 274 dup2(outfd, 1); 275 for (i = 1; i <= NSIG; i++) 276 if (mask & sigmask(i)) 277 (void)signal(i, SIG_IGN); 278 if ((mask & sigmask(SIGINT)) == 0) 279 (void)signal(SIGINT, SIG_DFL); 280 (void)sigsetmask(0); 281} 282 283int 284wait_command(pid) 285 int pid; 286{ 287 288 if (wait_child(pid) < 0) { 289 printf("Fatal error in process.\n"); 290 return (-1); 291 } 292 return (0); 293} 294 295static struct child * 296findchild(pid) 297 int pid; 298{ 299 struct child **cpp; 300 301 for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid; 302 cpp = &(*cpp)->link) 303 ; 304 if (*cpp == NULL) { 305 *cpp = malloc(sizeof(struct child)); 306 if (*cpp == NULL) 307 err(1, "Out of memory"); 308 (*cpp)->pid = pid; 309 (*cpp)->done = (*cpp)->free = 0; 310 (*cpp)->link = NULL; 311 } 312 return (*cpp); 313} 314 315static void 316delchild(cp) 317 struct child *cp; 318{ 319 struct child **cpp; 320 321 for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link) 322 ; 323 *cpp = cp->link; 324 (void)free(cp); 325} 326 327/*ARGSUSED*/ 328void 329sigchild(signo) 330 int signo; 331{ 332 int pid; 333 int status; 334 struct child *cp; 335 336 while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) { 337 cp = findchild(pid); 338 if (cp->free) 339 delchild(cp); 340 else { 341 cp->done = 1; 342 cp->status = status; 343 } 344 } 345} 346 347int wait_status; 348 349/* 350 * Wait for a specific child to die. 351 */ 352int 353wait_child(pid) 354 int pid; 355{ 356 int mask = sigblock(sigmask(SIGCHLD)); 357 struct child *cp = findchild(pid); 358 359 while (!cp->done) 360 sigpause(mask); 361 wait_status = cp->status; 362 delchild(cp); 363 (void)sigsetmask(mask); 364 return ((WIFEXITED(wait_status) && WEXITSTATUS(wait_status)) ? -1 : 0); 365} 366 367/* 368 * Mark a child as don't care. 369 */ 370void 371free_child(pid) 372 int pid; 373{ 374 int mask = sigblock(sigmask(SIGCHLD)); 375 struct child *cp = findchild(pid); 376 377 if (cp->done) 378 delchild(cp); 379 else 380 cp->free = 1; 381 (void)sigsetmask(mask); 382} 383