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