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