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