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