1 /* $NetBSD: popen.c,v 1.24 2007/10/30 02:28:31 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)popen.c 8.1 (Berkeley) 6/6/93"; 36 #else 37 __RCSID("$NetBSD: popen.c,v 1.24 2007/10/30 02:28:31 christos Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <stdarg.h> 44 #include <util.h> 45 #include <sys/wait.h> 46 47 #include "rcv.h" 48 #include "extern.h" 49 50 #define READ 0 51 #define WRITE 1 52 53 struct fp { 54 FILE *fp; 55 int pipe; 56 pid_t pid; 57 struct fp *link; 58 }; 59 static struct fp *fp_head; 60 61 struct child { 62 pid_t pid; 63 char done; 64 char free; 65 int status; 66 struct child *link; 67 }; 68 static struct child *child, *child_freelist = NULL; 69 70 71 #if 0 /* XXX - debugging stuff. This should go away eventually! */ 72 static void 73 show_one_file(FILE *fo, struct fp *fpp) 74 { 75 (void)fprintf(fo, ">>> fp: %p, pipe: %d, pid: %d, link: %p\n", 76 fpp->fp, fpp->pipe, fpp->pid, fpp->link); 77 } 78 79 void show_all_files(FILE *fo); 80 __unused 81 PUBLIC void 82 show_all_files(FILE *fo) 83 { 84 struct fp *fpp; 85 86 (void)fprintf(fo, ">> FILES\n"); 87 for (fpp = fp_head; fpp; fpp = fpp->link) 88 show_one_file(fo, fpp); 89 (void)fprintf(fo, ">> -------\n"); 90 (void)fflush(fo); 91 } 92 #endif /* end debugging stuff */ 93 94 95 static void 96 unregister_file(FILE *fp) 97 { 98 struct fp **pp, *p; 99 100 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link) 101 if (p->fp == fp) { 102 *pp = p->link; 103 (void)free(p); 104 return; 105 } 106 errx(1, "Invalid file pointer"); 107 } 108 109 PUBLIC void 110 register_file(FILE *fp, int pipefd, pid_t pid) 111 { 112 struct fp *fpp; 113 114 fpp = emalloc(sizeof(*fpp)); 115 fpp->fp = fp; 116 fpp->pipe = pipefd; 117 fpp->pid = pid; 118 fpp->link = fp_head; 119 fp_head = fpp; 120 } 121 122 PUBLIC FILE * 123 Fopen(const char *fn, const char *mode) 124 { 125 FILE *fp; 126 127 if ((fp = fopen(fn, mode)) != NULL) { 128 register_file(fp, 0, 0); 129 (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC); 130 } 131 return fp; 132 } 133 134 PUBLIC FILE * 135 Fdopen(int fd, const char *mode) 136 { 137 FILE *fp; 138 139 if ((fp = fdopen(fd, mode)) != NULL) { 140 register_file(fp, 0, 0); 141 (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC); 142 } 143 return fp; 144 } 145 146 PUBLIC int 147 Fclose(FILE *fp) 148 { 149 unregister_file(fp); 150 return fclose(fp); 151 } 152 153 PUBLIC void 154 prepare_child(sigset_t *nset, int infd, int outfd) 155 { 156 int i; 157 sigset_t eset; 158 159 /* 160 * All file descriptors other than 0, 1, and 2 are supposed to be 161 * close-on-exec. 162 */ 163 if (infd > 0) { 164 (void)dup2(infd, 0); 165 } else if (infd != 0) { 166 /* we don't want the child stealing my stdin input */ 167 (void)close(0); 168 (void)open(_PATH_DEVNULL, O_RDONLY, 0); 169 } 170 if (outfd >= 0 && outfd != 1) 171 (void)dup2(outfd, 1); 172 if (nset == NULL) 173 return; 174 if (nset != NULL) { 175 for (i = 1; i < NSIG; i++) 176 if (sigismember(nset, i)) 177 (void)signal(i, SIG_IGN); 178 } 179 if (nset == NULL || !sigismember(nset, SIGINT)) 180 (void)signal(SIGINT, SIG_DFL); 181 (void)sigemptyset(&eset); 182 (void)sigprocmask(SIG_SETMASK, &eset, NULL); 183 } 184 185 /* 186 * Run a command without a shell, with optional arguments and splicing 187 * of stdin (-1 means none) and stdout. The command name can be a sequence 188 * of words. 189 * Signals must be handled by the caller. 190 * "nset" contains the signals to ignore in the new process. 191 * SIGINT is enabled unless it's in "nset". 192 */ 193 static pid_t 194 start_commandv(const char *cmd, sigset_t *nset, int infd, int outfd, 195 va_list args) 196 { 197 pid_t pid; 198 199 if ((pid = fork()) < 0) { 200 warn("fork"); 201 return -1; 202 } 203 if (pid == 0) { 204 char *argv[100]; 205 int i = getrawlist(cmd, argv, sizeof(argv)/ sizeof(*argv)); 206 207 while ((argv[i++] = va_arg(args, char *)) != NULL) 208 continue; 209 argv[i] = NULL; 210 prepare_child(nset, infd, outfd); 211 (void)execvp(argv[0], argv); 212 warn("%s", argv[0]); 213 _exit(1); 214 } 215 return pid; 216 } 217 218 PUBLIC int 219 start_command(const char *cmd, sigset_t *nset, int infd, int outfd, ...) 220 { 221 va_list args; 222 int r; 223 224 va_start(args, outfd); 225 r = start_commandv(cmd, nset, infd, outfd, args); 226 va_end(args); 227 return r; 228 } 229 230 PUBLIC FILE * 231 Popen(const char *cmd, const char *mode) 232 { 233 int p[2]; 234 int myside, hisside, fd0, fd1; 235 pid_t pid; 236 sigset_t nset; 237 FILE *fp; 238 char *shellcmd; 239 240 if (pipe(p) < 0) 241 return NULL; 242 (void)fcntl(p[READ], F_SETFD, FD_CLOEXEC); 243 (void)fcntl(p[WRITE], F_SETFD, FD_CLOEXEC); 244 if (*mode == 'r') { 245 myside = p[READ]; 246 hisside = fd0 = fd1 = p[WRITE]; 247 } else { 248 myside = p[WRITE]; 249 hisside = fd0 = p[READ]; 250 fd1 = -1; 251 } 252 (void)sigemptyset(&nset); 253 if ((shellcmd = value(ENAME_SHELL)) == NULL) 254 shellcmd = __UNCONST(_PATH_CSHELL); 255 pid = start_command(shellcmd, &nset, fd0, fd1, "-c", cmd, NULL); 256 if (pid < 0) { 257 (void)close(p[READ]); 258 (void)close(p[WRITE]); 259 return NULL; 260 } 261 (void)close(hisside); 262 if ((fp = fdopen(myside, mode)) != NULL) 263 register_file(fp, 1, pid); 264 return fp; 265 } 266 267 static struct child * 268 findchild(pid_t pid, int dont_alloc) 269 { 270 struct child **cpp; 271 272 for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid; 273 cpp = &(*cpp)->link) 274 continue; 275 if (*cpp == NULL) { 276 if (dont_alloc) 277 return NULL; 278 if (child_freelist) { 279 *cpp = child_freelist; 280 child_freelist = (*cpp)->link; 281 } else 282 *cpp = emalloc(sizeof(**cpp)); 283 284 (*cpp)->pid = pid; 285 (*cpp)->done = (*cpp)->free = 0; 286 (*cpp)->link = NULL; 287 } 288 return *cpp; 289 } 290 291 static void 292 delchild(struct child *cp) 293 { 294 struct child **cpp; 295 296 for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link) 297 continue; 298 *cpp = cp->link; 299 cp->link = child_freelist; 300 child_freelist = cp; 301 } 302 303 /* 304 * Wait for a specific child to die. 305 */ 306 PUBLIC int 307 wait_child(pid_t pid) 308 { 309 struct child *cp; 310 sigset_t nset, oset; 311 pid_t rv = 0; 312 313 (void)sigemptyset(&nset); 314 (void)sigaddset(&nset, SIGCHLD); 315 (void)sigprocmask(SIG_BLOCK, &nset, &oset); 316 /* 317 * If we have not already waited on the pid (via sigchild) 318 * wait on it now. Otherwise, use the wait status stashed 319 * by sigchild. 320 */ 321 cp = findchild(pid, 1); 322 if (cp == NULL || !cp->done) 323 rv = waitpid(pid, &wait_status, 0); 324 else 325 wait_status = cp->status; 326 if (cp != NULL) 327 delchild(cp); 328 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 329 if (rv == -1 || (WIFEXITED(wait_status) && WEXITSTATUS(wait_status))) 330 return -1; 331 else 332 return 0; 333 } 334 335 static pid_t 336 file_pid(FILE *fp) 337 { 338 struct fp *p; 339 340 for (p = fp_head; p; p = p->link) 341 if (p->fp == fp) 342 return p->pid; 343 errx(1, "Invalid file pointer"); 344 /*NOTREACHED*/ 345 } 346 347 PUBLIC int 348 Pclose(FILE *ptr) 349 { 350 int i; 351 sigset_t nset, oset; 352 353 i = file_pid(ptr); 354 unregister_file(ptr); 355 (void)fclose(ptr); 356 (void)sigemptyset(&nset); 357 (void)sigaddset(&nset, SIGINT); 358 (void)sigaddset(&nset, SIGHUP); 359 (void)sigprocmask(SIG_BLOCK, &nset, &oset); 360 i = wait_child(i); 361 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 362 return i; 363 } 364 365 PUBLIC void 366 close_all_files(void) 367 { 368 while (fp_head) 369 if (fp_head->pipe) 370 (void)Pclose(fp_head->fp); 371 else 372 (void)Fclose(fp_head->fp); 373 } 374 375 PUBLIC FILE * 376 last_registered_file(int last_pipe) 377 { 378 struct fp *fpp; 379 380 if (last_pipe == 0) 381 return fp_head ? fp_head->fp : NULL; 382 383 for (fpp = fp_head; fpp; fpp = fpp->link) 384 if (fpp->pipe) 385 return fpp->fp; 386 return NULL; 387 } 388 389 PUBLIC void 390 close_top_files(FILE *fp_stop) 391 { 392 while (fp_head && fp_head->fp != fp_stop) 393 if (fp_head->pipe) 394 (void)Pclose(fp_head->fp); 395 else 396 (void)Fclose(fp_head->fp); 397 } 398 399 #ifdef MIME_SUPPORT 400 PUBLIC void 401 flush_files(FILE *fo, int only_pipes) 402 { 403 struct fp *fpp; 404 405 if (fo) 406 (void)fflush(fo); 407 408 for (fpp = fp_head; fpp; fpp = fpp->link) 409 if (!only_pipes || fpp->pipe) 410 (void)fflush(fpp->fp); 411 412 (void)fflush(stdout); 413 } 414 #endif /* MIME_SUPPORT */ 415 416 static int 417 wait_command(pid_t pid) 418 { 419 420 if (wait_child(pid) < 0) { 421 (void)puts("Fatal error in process."); 422 return -1; 423 } 424 return 0; 425 } 426 427 PUBLIC int 428 run_command(const char *cmd, sigset_t *nset, int infd, int outfd, ...) 429 { 430 pid_t pid; 431 va_list args; 432 433 va_start(args, outfd); 434 pid = start_commandv(cmd, nset, infd, outfd, args); 435 va_end(args); 436 if (pid < 0) 437 return -1; 438 return wait_command(pid); 439 } 440 441 /*ARGSUSED*/ 442 PUBLIC void 443 sigchild(int signo __unused) 444 { 445 pid_t pid; 446 int status; 447 struct child *cp; 448 int save_errno = errno; 449 450 while ((pid = 451 waitpid((pid_t)-1, &status, WNOHANG)) > 0) { 452 cp = findchild(pid, 1); 453 if (!cp) 454 continue; 455 if (cp->free) 456 delchild(cp); 457 else { 458 cp->done = 1; 459 cp->status = status; 460 } 461 } 462 errno = save_errno; 463 } 464 465 /* 466 * Mark a child as don't care. 467 */ 468 PUBLIC void 469 free_child(pid_t pid) 470 { 471 struct child *cp; 472 sigset_t nset, oset; 473 474 (void)sigemptyset(&nset); 475 (void)sigaddset(&nset, SIGCHLD); 476 (void)sigprocmask(SIG_BLOCK, &nset, &oset); 477 if ((cp = findchild(pid, 0)) != NULL) { 478 if (cp->done) 479 delchild(cp); 480 else 481 cp->free = 1; 482 } 483 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 484 } 485