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