1 /* $OpenBSD: popen.c,v 1.31 2001/11/23 00:03:24 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 const char sccsid[] = "@(#)popen.c 8.1 (Berkeley) 6/6/93"; 40 #else 41 static const char rcsid[] = "$OpenBSD: popen.c,v 1.31 2001/11/23 00:03:24 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 #include <stdarg.h> 50 #include "extern.h" 51 52 #define READ 0 53 #define WRITE 1 54 55 struct fp { 56 FILE *fp; 57 int pipe; 58 pid_t pid; 59 struct fp *link; 60 }; 61 static struct fp *fp_head; 62 63 struct child { 64 pid_t pid; 65 char done; 66 char free; 67 int status; 68 struct child *link; 69 }; 70 static struct child *child, *child_freelist = NULL; 71 72 static struct child *findchild(pid_t, int); 73 static void delchild(struct child *); 74 static pid_t file_pid(FILE *); 75 static int handle_spool_locks(int); 76 77 FILE * 78 Fopen(char *file, char *mode) 79 { 80 FILE *fp; 81 82 if ((fp = fopen(file, mode)) != NULL) { 83 register_file(fp, 0, 0); 84 (void)fcntl(fileno(fp), F_SETFD, 1); 85 } 86 return(fp); 87 } 88 89 FILE * 90 Fdopen(int fd, char *mode) 91 { 92 FILE *fp; 93 94 if ((fp = fdopen(fd, mode)) != NULL) { 95 register_file(fp, 0, 0); 96 (void)fcntl(fileno(fp), F_SETFD, 1); 97 } 98 return(fp); 99 } 100 101 int 102 Fclose(FILE *fp) 103 { 104 105 unregister_file(fp); 106 return(fclose(fp)); 107 } 108 109 FILE * 110 Popen(char *cmd, char *mode) 111 { 112 int p[2]; 113 int myside, hisside, fd0, fd1; 114 pid_t 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 hisside = fd0 = fd1 = p[WRITE]; 125 } else { 126 myside = p[WRITE]; 127 hisside = fd0 = p[READ]; 128 fd1 = -1; 129 } 130 sigemptyset(&nset); 131 pid = start_command(value("SHELL"), &nset, fd0, fd1, "-c", cmd, NULL); 132 if (pid < 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 143 int 144 Pclose(FILE *ptr) 145 { 146 int i; 147 sigset_t nset, oset; 148 149 i = file_pid(ptr); 150 unregister_file(ptr); 151 (void)fclose(ptr); 152 sigemptyset(&nset); 153 sigaddset(&nset, SIGINT); 154 sigaddset(&nset, SIGHUP); 155 sigprocmask(SIG_BLOCK, &nset, &oset); 156 i = wait_child(i); 157 sigprocmask(SIG_SETMASK, &oset, NULL); 158 return(i); 159 } 160 161 void 162 close_all_files(void) 163 { 164 165 while (fp_head) 166 if (fp_head->pipe) 167 (void)Pclose(fp_head->fp); 168 else 169 (void)Fclose(fp_head->fp); 170 } 171 172 void 173 register_file(FILE *fp, int pipe, pid_t pid) 174 { 175 struct fp *fpp; 176 177 if ((fpp = (struct fp *)malloc(sizeof(*fpp))) == NULL) 178 errx(1, "Out of memory"); 179 fpp->fp = fp; 180 fpp->pipe = pipe; 181 fpp->pid = pid; 182 fpp->link = fp_head; 183 fp_head = fpp; 184 } 185 186 void 187 unregister_file(FILE *fp) 188 { 189 struct fp **pp, *p; 190 191 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link) 192 if (p->fp == fp) { 193 *pp = p->link; 194 (void)free(p); 195 return; 196 } 197 errx(1, "Invalid file pointer"); 198 } 199 200 static pid_t 201 file_pid(FILE *fp) 202 { 203 struct fp *p; 204 205 for (p = fp_head; p; p = p->link) 206 if (p->fp == fp) 207 return(p->pid); 208 errx(1, "Invalid file pointer"); 209 /*NOTREACHED*/ 210 } 211 212 /* 213 * Run a command without a shell, with optional arguments and splicing 214 * of stdin (-1 means none) and stdout. The command name can be a sequence 215 * of words. 216 * Signals must be handled by the caller. 217 * "nset" contains the signals to ignore in the new process. 218 * SIGINT is enabled unless it's in "nset". 219 */ 220 pid_t 221 start_commandv(char *cmd, sigset_t *nset, int infd, int outfd, va_list args) 222 { 223 pid_t pid; 224 225 if ((pid = fork()) < 0) { 226 warn("fork"); 227 return(-1); 228 } 229 if (pid == 0) { 230 char *argv[100]; 231 int i = getrawlist(cmd, argv, sizeof(argv)/ sizeof(*argv)); 232 233 while ((argv[i++] = va_arg(args, char *))) 234 ; 235 argv[i] = NULL; 236 prepare_child(nset, infd, outfd); 237 execvp(argv[0], argv); 238 warn("%s", argv[0]); 239 _exit(1); 240 } 241 return(pid); 242 } 243 244 int 245 run_command(char *cmd, sigset_t *nset, int infd, int outfd, ...) 246 { 247 pid_t pid; 248 va_list args; 249 250 va_start(args, outfd); 251 pid = start_commandv(cmd, nset, infd, outfd, args); 252 va_end(args); 253 if (pid < 0) 254 return(-1); 255 return(wait_command(pid)); 256 } 257 258 int 259 start_command(char *cmd, sigset_t *nset, int infd, int outfd, ...) 260 { 261 va_list args; 262 int r; 263 264 va_start(args, outfd); 265 r = start_commandv(cmd, nset, infd, outfd, args); 266 va_end(args); 267 return(r); 268 } 269 270 void 271 prepare_child(sigset_t *nset, int infd, int outfd) 272 { 273 int i; 274 sigset_t eset; 275 276 /* 277 * All file descriptors other than 0, 1, and 2 are supposed to be 278 * close-on-exec. 279 */ 280 if (infd > 0) { 281 dup2(infd, 0); 282 } else if (infd != 0) { 283 /* we don't want the child stealing my stdin input */ 284 close(0); 285 open(_PATH_DEVNULL, O_RDONLY, 0); 286 } 287 if (outfd >= 0 && outfd != 1) 288 dup2(outfd, 1); 289 if (nset == NULL) 290 return; 291 if (nset != NULL) { 292 for (i = 1; i < NSIG; i++) 293 if (sigismember(nset, i)) 294 (void)signal(i, SIG_IGN); 295 } 296 if (nset == NULL || !sigismember(nset, SIGINT)) 297 (void)signal(SIGINT, SIG_DFL); 298 sigemptyset(&eset); 299 (void)sigprocmask(SIG_SETMASK, &eset, NULL); 300 } 301 302 int 303 wait_command(pid_t pid) 304 { 305 306 if (wait_child(pid) < 0) { 307 puts("Fatal error in process."); 308 return(-1); 309 } 310 return(0); 311 } 312 313 static struct child * 314 findchild(pid_t pid, int dont_alloc) 315 { 316 struct child **cpp; 317 318 for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid; 319 cpp = &(*cpp)->link) 320 ; 321 if (*cpp == NULL) { 322 if (dont_alloc) 323 return(NULL); 324 if (child_freelist) { 325 *cpp = child_freelist; 326 child_freelist = (*cpp)->link; 327 } else { 328 *cpp = (struct child *)malloc(sizeof(struct child)); 329 if (*cpp == NULL) 330 errx(1, "Out of memory"); 331 } 332 (*cpp)->pid = pid; 333 (*cpp)->done = (*cpp)->free = 0; 334 (*cpp)->link = NULL; 335 } 336 return(*cpp); 337 } 338 339 static void 340 delchild(struct child *cp) 341 { 342 struct child **cpp; 343 344 for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link) 345 ; 346 *cpp = cp->link; 347 cp->link = child_freelist; 348 child_freelist = cp; 349 } 350 351 void 352 sigchild(int signo) 353 { 354 pid_t pid; 355 int status; 356 struct child *cp; 357 int save_errno = errno; 358 359 while ((pid = 360 waitpid((pid_t)-1, &status, WNOHANG)) > 0) { 361 cp = findchild(pid, 1); 362 if (!cp) 363 continue; 364 if (cp->free) 365 delchild(cp); 366 else { 367 cp->done = 1; 368 cp->status = status; 369 } 370 } 371 errno = save_errno; 372 } 373 374 int wait_status; 375 376 /* 377 * Wait for a specific child to die. 378 */ 379 int 380 wait_child(pid_t pid) 381 { 382 struct child *cp; 383 sigset_t nset, oset; 384 int rv = 0; 385 386 sigemptyset(&nset); 387 sigaddset(&nset, SIGCHLD); 388 sigprocmask(SIG_BLOCK, &nset, &oset); 389 /* 390 * If we have not already waited on the pid (via sigchild) 391 * wait on it now. Otherwise, use the wait status stashed 392 * by sigchild. 393 */ 394 cp = findchild(pid, 1); 395 if (cp == NULL || !cp->done) 396 rv = waitpid(pid, &wait_status, 0); 397 else 398 wait_status = cp->status; 399 if (cp != NULL) 400 delchild(cp); 401 sigprocmask(SIG_SETMASK, &oset, NULL); 402 if (rv == -1 || (WIFEXITED(wait_status) && WEXITSTATUS(wait_status))) 403 return(-1); 404 else 405 return(0); 406 } 407 408 /* 409 * Mark a child as don't care. 410 */ 411 void 412 free_child(pid_t pid) 413 { 414 struct child *cp; 415 sigset_t nset, oset; 416 417 sigemptyset(&nset); 418 sigaddset(&nset, SIGCHLD); 419 sigprocmask(SIG_BLOCK, &nset, &oset); 420 if ((cp = findchild(pid, 0)) != NULL) { 421 if (cp->done) 422 delchild(cp); 423 else 424 cp->free = 1; 425 } 426 sigprocmask(SIG_SETMASK, &oset, NULL); 427 } 428 429 /* 430 * Lock(1)/unlock(0) mail spool using lockspool(1). 431 * Returns 1 for success, 0 for failure, -1 for bad usage. 432 */ 433 static int 434 handle_spool_locks(int action) 435 { 436 static FILE *lockfp = NULL; 437 static pid_t lock_pid; 438 439 if (action == 0) { 440 /* Clear the lock */ 441 if (lockfp == NULL) { 442 fputs("handle_spool_locks: no spool lock to remove.\n", 443 stderr); 444 return(-1); 445 } 446 (void)Pclose(lockfp); 447 lockfp = NULL; 448 } else if (action == 1) { 449 char *cmd; 450 char buf[sizeof(_PATH_LOCKSPOOL) + MAXLOGNAME + 1]; 451 452 /* XXX - lockspool requires root for user arg, we do not */ 453 if (uflag) { 454 snprintf(buf, sizeof(buf), "%s %s", _PATH_LOCKSPOOL, 455 myname); 456 cmd = buf; 457 } else 458 cmd = _PATH_LOCKSPOOL; 459 460 /* Create the lock */ 461 lockfp = Popen(cmd, "r"); 462 if (lockfp == NULL) 463 return(0); 464 if (getc(lockfp) != '1') { 465 Pclose(lockfp); 466 lockfp = NULL; 467 return(0); 468 } 469 lock_pid = fp_head->pid; /* new entries added at head */ 470 } else { 471 (void)fprintf(stderr, "handle_spool_locks: unknown action %d\n", 472 action); 473 return(-1); 474 } 475 476 return(1); 477 } 478 479 int 480 spool_lock(void) 481 { 482 483 return(handle_spool_locks(1)); 484 } 485 486 int 487 spool_unlock(void) 488 { 489 490 return(handle_spool_locks(0)); 491 } 492