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: src/usr.bin/mail/popen.c,v 1.7 2002/06/30 05:25:06 obrien Exp $"; 40#endif /* not lint */ 41#include <sys/cdefs.h> 42 43 44#include "rcv.h" 45#include <sys/wait.h> 46#include <fcntl.h> 47#include "extern.h" 48 49#define READ 0 50#define WRITE 1 51 52struct fp { 53 FILE *fp; 54 int pipe; 55 int pid; 56 struct fp *link; 57}; 58static struct fp *fp_head; 59 60struct child { 61 int pid; 62 char done; 63 char free; 64 int status; 65 struct child *link; 66}; 67static struct child *child; 68static struct child *findchild(int); 69static void delchild(struct child *); 70static int file_pid(FILE *); 71 72FILE * 73Fopen(path, mode) 74 const char *path, *mode; 75{ 76 FILE *fp; 77 78 if ((fp = fopen(path, mode)) != NULL) { 79 register_file(fp, 0, 0); 80 (void)fcntl(fileno(fp), F_SETFD, 1); 81 } 82 return (fp); 83} 84 85FILE * 86Fdopen(fd, mode) 87 int fd; 88 const char *mode; 89{ 90 FILE *fp; 91 92 if ((fp = fdopen(fd, mode)) != NULL) { 93 register_file(fp, 0, 0); 94 (void)fcntl(fileno(fp), F_SETFD, 1); 95 } 96 return (fp); 97} 98 99int 100Fclose(fp) 101 FILE *fp; 102{ 103 unregister_file(fp); 104 return (fclose(fp)); 105} 106 107FILE * 108Popen(cmd, mode) 109 char *cmd; 110 const char *mode; 111{ 112 int p[2]; 113 int myside, hisside, fd0, fd1; 114 int pid; 115 sigset_t nset; 116 FILE *fp; 117 118 if (pipe(p) < 0) 119 return (NULL); 120 (void)fcntl(p[READ], F_SETFD, 1); 121 (void)fcntl(p[WRITE], F_SETFD, 1); 122 if (*mode == 'r') { 123 myside = p[READ]; 124 fd0 = -1; 125 hisside = fd1 = p[WRITE]; 126 } else { 127 myside = p[WRITE]; 128 hisside = fd0 = p[READ]; 129 fd1 = -1; 130 } 131 (void)sigemptyset(&nset); 132 if ((pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL)) < 0) { 133 (void)close(p[READ]); 134 (void)close(p[WRITE]); 135 return (NULL); 136 } 137 (void)close(hisside); 138 if ((fp = fdopen(myside, mode)) != NULL) 139 register_file(fp, 1, pid); 140 return (fp); 141} 142 143int 144Pclose(ptr) 145 FILE *ptr; 146{ 147 int i; 148 sigset_t nset, oset; 149 150 i = file_pid(ptr); 151 unregister_file(ptr); 152 (void)fclose(ptr); 153 (void)sigemptyset(&nset); 154 (void)sigaddset(&nset, SIGINT); 155 (void)sigaddset(&nset, SIGHUP); 156 (void)sigprocmask(SIG_BLOCK, &nset, &oset); 157 i = wait_child(i); 158 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 159 return (i); 160} 161 162void 163close_all_files() 164{ 165 166 while (fp_head != NULL) 167 if (fp_head->pipe) 168 (void)Pclose(fp_head->fp); 169 else 170 (void)Fclose(fp_head->fp); 171} 172 173void 174register_file(fp, pipe, pid) 175 FILE *fp; 176 int pipe, pid; 177{ 178 struct fp *fpp; 179 180 if ((fpp = malloc(sizeof(*fpp))) == NULL) 181 err(1, "Out of memory"); 182 fpp->fp = fp; 183 fpp->pipe = pipe; 184 fpp->pid = pid; 185 fpp->link = fp_head; 186 fp_head = fpp; 187} 188 189void 190unregister_file(fp) 191 FILE *fp; 192{ 193 struct fp **pp, *p; 194 195 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link) 196 if (p->fp == fp) { 197 *pp = p->link; 198 (void)free(p); 199 return; 200 } 201 errx(1, "Invalid file pointer"); 202 /*NOTREACHED*/ 203} 204 205int 206file_pid(fp) 207 FILE *fp; 208{ 209 struct fp *p; 210 211 for (p = fp_head; p != NULL; p = p->link) 212 if (p->fp == fp) 213 return (p->pid); 214 errx(1, "Invalid file pointer"); 215 /*NOTREACHED*/ 216} 217 218/* 219 * Run a command without a shell, with optional arguments and splicing 220 * of stdin and stdout. The command name can be a sequence of words. 221 * Signals must be handled by the caller. 222 * "Mask" contains the signals to ignore in the new process. 223 * SIGINT is enabled unless it's in the mask. 224 */ 225/*VARARGS4*/ 226int 227run_command(cmd, mask, infd, outfd, a0, a1, a2) 228 char *cmd; 229 sigset_t *mask; 230 int infd, outfd; 231 char *a0, *a1, *a2; 232{ 233 int pid; 234 235 if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0) 236 return (-1); 237 return (wait_command(pid)); 238} 239 240/*VARARGS4*/ 241int 242start_command(cmd, mask, infd, outfd, a0, a1, a2) 243 char *cmd; 244 sigset_t *mask; 245 int infd, outfd; 246 char *a0, *a1, *a2; 247{ 248 int pid; 249 250 if ((pid = fork()) < 0) { 251 warn("fork"); 252 return (-1); 253 } 254 if (pid == 0) { 255 char *argv[100]; 256 int i = getrawlist(cmd, argv, sizeof(argv) / sizeof(*argv)); 257 258 if ((argv[i++] = a0) != NULL && 259 (argv[i++] = a1) != NULL && 260 (argv[i++] = a2) != NULL) 261 argv[i] = NULL; 262 prepare_child(mask, infd, outfd); 263 execvp(argv[0], argv); 264 warn("%s", argv[0]); 265 _exit(1); 266 } 267 return (pid); 268} 269 270void 271prepare_child(nset, infd, outfd) 272 sigset_t *nset; 273 int infd, outfd; 274{ 275 int i; 276 sigset_t eset; 277 278 /* 279 * All file descriptors other than 0, 1, and 2 are supposed to be 280 * close-on-exec. 281 */ 282 if (infd >= 0) 283 dup2(infd, 0); 284 if (outfd >= 0) 285 dup2(outfd, 1); 286 for (i = 1; i < NSIG; i++) 287 if (nset != NULL && sigismember(nset, i)) 288 (void)signal(i, SIG_IGN); 289 if (nset == NULL || !sigismember(nset, SIGINT)) 290 (void)signal(SIGINT, SIG_DFL); 291 (void)sigemptyset(&eset); 292 (void)sigprocmask(SIG_SETMASK, &eset, NULL); 293} 294 295int 296wait_command(pid) 297 int pid; 298{ 299 300 if (wait_child(pid) < 0) { 301 printf("Fatal error in process.\n"); 302 return (-1); 303 } 304 return (0); 305} 306 307static struct child * 308findchild(pid) 309 int pid; 310{ 311 struct child **cpp; 312 313 for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid; 314 cpp = &(*cpp)->link) 315 ; 316 if (*cpp == NULL) { 317 *cpp = malloc(sizeof(struct child)); 318 if (*cpp == NULL) 319 err(1, "Out of memory"); 320 (*cpp)->pid = pid; 321 (*cpp)->done = (*cpp)->free = 0; 322 (*cpp)->link = NULL; 323 } 324 return (*cpp); 325} 326 327static void 328delchild(cp) 329 struct child *cp; 330{ 331 struct child **cpp; 332 333 for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link) 334 ; 335 *cpp = cp->link; 336 (void)free(cp); 337} 338 339/*ARGSUSED*/ 340void 341sigchild(signo) 342 int signo; 343{ 344 int pid; 345 int status; 346 struct child *cp; 347 348 while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) { 349 cp = findchild(pid); 350 if (cp->free) 351 delchild(cp); 352 else { 353 cp->done = 1; 354 cp->status = status; 355 } 356 } 357} 358 359int wait_status; 360 361/* 362 * Wait for a specific child to die. 363 */ 364int 365wait_child(pid) 366 int pid; 367{ 368 sigset_t nset, oset; 369 struct child *cp = findchild(pid); 370 371 (void)sigemptyset(&nset); 372 (void)sigaddset(&nset, SIGCHLD); 373 (void)sigprocmask(SIG_BLOCK, &nset, &oset); 374 375 while (!cp->done) 376 (void)sigsuspend(&oset); 377 wait_status = cp->status; 378 delchild(cp); 379 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 380 return ((WIFEXITED(wait_status) && WEXITSTATUS(wait_status)) ? -1 : 0); 381} 382 383/* 384 * Mark a child as don't care. 385 */ 386void 387free_child(pid) 388 int pid; 389{ 390 sigset_t nset, oset; 391 struct child *cp = findchild(pid); 392 393 (void)sigemptyset(&nset); 394 (void)sigaddset(&nset, SIGCHLD); 395 (void)sigprocmask(SIG_BLOCK, &nset, &oset); 396 397 if (cp->done) 398 delchild(cp); 399 else 400 cp->free = 1; 401 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 402} 403