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