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