Annotation of src/usr.bin/mail/popen.c, Revision 1.21
1.21 ! millert 1: /* $OpenBSD: popen.c,v 1.20 1998/09/08 15:24:38 millert 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.
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
1.2 deraadt 38: #if 0
39: static char sccsid[] = "@(#)popen.c 8.1 (Berkeley) 6/6/93";
40: #else
1.21 ! millert 41: static char rcsid[] = "$OpenBSD: popen.c,v 1.20 1998/09/08 15:24:38 millert Exp $";
1.2 deraadt 42: #endif
1.1 deraadt 43: #endif /* not lint */
44:
45: #include "rcv.h"
46: #include <sys/wait.h>
47: #include <fcntl.h>
1.12 deraadt 48: #include <errno.h>
1.1 deraadt 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;
1.13 millert 66: int status;
1.1 deraadt 67: struct child *link;
68: };
1.15 niklas 69: static struct child *child, *child_freelist = NULL;
70: static struct child *findchild __P((int, int));
1.1 deraadt 71: static void delchild __P((struct child *));
1.2 deraadt 72: static int file_pid __P((FILE *));
1.4 millert 73: static int handle_spool_locks __P((int));
1.1 deraadt 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);
1.7 millert 83: (void)fcntl(fileno(fp), F_SETFD, 1);
1.1 deraadt 84: }
1.6 millert 85: return(fp);
1.1 deraadt 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);
1.7 millert 97: (void)fcntl(fileno(fp), F_SETFD, 1);
1.1 deraadt 98: }
1.6 millert 99: return(fp);
1.1 deraadt 100: }
101:
102: int
103: Fclose(fp)
104: FILE *fp;
105: {
106: unregister_file(fp);
1.6 millert 107: return(fclose(fp));
1.1 deraadt 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;
1.2 deraadt 118: sigset_t nset;
1.1 deraadt 119: FILE *fp;
120:
121: if (pipe(p) < 0)
1.6 millert 122: return(NULL);
1.7 millert 123: (void)fcntl(p[READ], F_SETFD, 1);
124: (void)fcntl(p[WRITE], F_SETFD, 1);
1.1 deraadt 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: }
1.2 deraadt 134: sigemptyset(&nset);
1.8 millert 135: if ((pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL)) < 0) {
1.6 millert 136: (void)close(p[READ]);
137: (void)close(p[WRITE]);
138: return(NULL);
1.1 deraadt 139: }
1.6 millert 140: (void)close(hisside);
1.1 deraadt 141: if ((fp = fdopen(myside, mode)) != NULL)
142: register_file(fp, 1, pid);
1.6 millert 143: return(fp);
1.1 deraadt 144: }
145:
146: int
147: Pclose(ptr)
148: FILE *ptr;
149: {
150: int i;
1.2 deraadt 151: sigset_t nset, oset;
1.1 deraadt 152:
153: i = file_pid(ptr);
154: unregister_file(ptr);
1.6 millert 155: (void)fclose(ptr);
1.2 deraadt 156: sigemptyset(&nset);
157: sigaddset(&nset, SIGINT);
158: sigaddset(&nset, SIGHUP);
159: sigprocmask(SIG_BLOCK, &nset, &oset);
1.1 deraadt 160: i = wait_child(i);
1.2 deraadt 161: sigprocmask(SIG_SETMASK, &oset, NULL);
1.6 millert 162: return(i);
1.1 deraadt 163: }
164:
165: void
166: close_all_files()
167: {
168:
169: while (fp_head)
170: if (fp_head->pipe)
1.6 millert 171: (void)Pclose(fp_head->fp);
1.1 deraadt 172: else
1.6 millert 173: (void)Fclose(fp_head->fp);
1.1 deraadt 174: }
175:
176: void
177: register_file(fp, pipe, pid)
178: FILE *fp;
179: int pipe, pid;
180: {
181: struct fp *fpp;
182:
1.7 millert 183: if ((fpp = (struct fp *)malloc(sizeof(*fpp))) == NULL)
1.14 millert 184: errx(1, "Out of memory");
1.1 deraadt 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:
1.2 deraadt 198: for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
1.1 deraadt 199: if (p->fp == fp) {
200: *pp = p->link;
1.7 millert 201: (void)free(p);
1.1 deraadt 202: return;
203: }
1.14 millert 204: errx(1, "Invalid file pointer");
1.1 deraadt 205: }
206:
1.2 deraadt 207: static int
1.1 deraadt 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)
1.6 millert 215: return(p->pid);
1.14 millert 216: errx(1, "Invalid file pointer");
1.1 deraadt 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.
1.20 millert 224: * "nset" contains the signals to ignore in the new process.
225: * SIGINT is enabled unless it's in "nset".
1.1 deraadt 226: */
227: /*VARARGS4*/
228: int
1.10 millert 229: run_command(cmd, nset, infd, outfd, a0, a1, a2)
1.1 deraadt 230: char *cmd;
1.10 millert 231: sigset_t *nset;
1.2 deraadt 232: int infd, outfd;
1.1 deraadt 233: char *a0, *a1, *a2;
234: {
235: int pid;
236:
1.10 millert 237: if ((pid = start_command(cmd, nset, infd, outfd, a0, a1, a2)) < 0)
1.6 millert 238: return(-1);
239: return(wait_command(pid));
1.1 deraadt 240: }
241:
242: /*VARARGS4*/
243: int
1.10 millert 244: start_command(cmd, nset, infd, outfd, a0, a1, a2)
1.1 deraadt 245: char *cmd;
1.10 millert 246: sigset_t *nset;
1.2 deraadt 247: int infd, outfd;
1.1 deraadt 248: char *a0, *a1, *a2;
249: {
250: int pid;
251:
252: if ((pid = vfork()) < 0) {
1.6 millert 253: warn("fork");
254: return(-1);
1.1 deraadt 255: }
256: if (pid == 0) {
257: char *argv[100];
1.6 millert 258: int i = getrawlist(cmd, argv, sizeof(argv)/ sizeof(*argv));
1.1 deraadt 259:
1.8 millert 260: if ((argv[i++] = a0) != NULL &&
261: (argv[i++] = a1) != NULL &&
262: (argv[i++] = a2) != NULL)
263: argv[i] = NULL;
1.10 millert 264: prepare_child(nset, infd, outfd);
1.1 deraadt 265: execvp(argv[0], argv);
1.6 millert 266: warn(argv[0]);
1.1 deraadt 267: _exit(1);
268: }
1.6 millert 269: return(pid);
1.1 deraadt 270: }
271:
272: void
1.2 deraadt 273: prepare_child(nset, infd, outfd)
274: sigset_t *nset;
275: int infd, outfd;
1.1 deraadt 276: {
277: int i;
1.9 millert 278: sigset_t eset;
1.1 deraadt 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);
1.6 millert 288: if (nset == NULL)
289: return;
290: if (nset != NULL) {
1.5 deraadt 291: for (i = 1; i < NSIG; i++)
1.3 dm 292: if (sigismember(nset, i))
1.7 millert 293: (void)signal(i, SIG_IGN);
1.3 dm 294: }
1.6 millert 295: if (nset == NULL || !sigismember(nset, SIGINT))
1.7 millert 296: (void)signal(SIGINT, SIG_DFL);
1.9 millert 297: sigemptyset(&eset);
298: (void)sigprocmask(SIG_SETMASK, &eset, NULL);
1.1 deraadt 299: }
300:
301: int
302: wait_command(pid)
303: int pid;
304: {
305:
306: if (wait_child(pid) < 0) {
1.6 millert 307: puts("Fatal error in process.");
308: return(-1);
1.1 deraadt 309: }
1.6 millert 310: return(0);
1.1 deraadt 311: }
312:
313: static struct child *
1.15 niklas 314: findchild(pid, dont_alloc)
1.1 deraadt 315: int pid;
1.15 niklas 316: int dont_alloc;
1.1 deraadt 317: {
1.14 millert 318: struct child **cpp;
1.1 deraadt 319:
320: for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
321: cpp = &(*cpp)->link)
322: ;
323: if (*cpp == NULL) {
1.15 niklas 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));
1.1 deraadt 331: (*cpp)->pid = pid;
332: (*cpp)->done = (*cpp)->free = 0;
333: (*cpp)->link = NULL;
334: }
1.6 millert 335: return(*cpp);
1.1 deraadt 336: }
337:
338: static void
339: delchild(cp)
1.14 millert 340: struct child *cp;
1.1 deraadt 341: {
1.14 millert 342: struct child **cpp;
1.1 deraadt 343:
344: for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
345: ;
346: *cpp = cp->link;
1.15 niklas 347: cp->link = child_freelist;
348: child_freelist = cp;
1.1 deraadt 349: }
350:
351: void
352: sigchild(signo)
353: int signo;
354: {
355: int pid;
1.13 millert 356: int status;
1.14 millert 357: struct child *cp;
1.11 deraadt 358: int save_errno = errno;
1.1 deraadt 359:
360: while ((pid =
1.13 millert 361: waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
1.15 niklas 362: cp = findchild(pid, 1);
363: if (!cp)
364: continue;
1.1 deraadt 365: if (cp->free)
366: delchild(cp);
367: else {
368: cp->done = 1;
369: cp->status = status;
370: }
371: }
1.11 deraadt 372: errno = save_errno;
1.1 deraadt 373: }
374:
1.13 millert 375: int wait_status;
1.1 deraadt 376:
377: /*
378: * Wait for a specific child to die.
379: */
380: int
381: wait_child(pid)
382: int pid;
383: {
1.20 millert 384: struct child *cp;
1.2 deraadt 385: sigset_t nset, oset;
1.21 ! millert 386: int rv = 0;
1.9 millert 387:
1.2 deraadt 388: sigemptyset(&nset);
389: sigaddset(&nset, SIGCHLD);
390: sigprocmask(SIG_BLOCK, &nset, &oset);
1.21 ! millert 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)))
1.20 millert 405: return(-1);
1.21 ! millert 406: else
! 407: return(0);
1.1 deraadt 408: }
409:
410: /*
411: * Mark a child as don't care.
412: */
413: void
414: free_child(pid)
415: int pid;
416: {
1.20 millert 417: struct child *cp;
1.2 deraadt 418: sigset_t nset, oset;
1.17 millert 419:
1.2 deraadt 420: sigemptyset(&nset);
421: sigaddset(&nset, SIGCHLD);
422: sigprocmask(SIG_BLOCK, &nset, &oset);
1.21 ! millert 423: if ((cp = findchild(pid, 0)) != NULL) {
! 424: if (cp->done)
! 425: delchild(cp);
! 426: else
! 427: cp->free = 1;
! 428: }
1.2 deraadt 429: sigprocmask(SIG_SETMASK, &oset, NULL);
1.4 millert 430: }
431:
432: /*
433: * Lock(1)/unlock(0) mail spool using mail.local's -H flag.
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) {
1.6 millert 446: fputs("handle_spool_locks: no spool lock to remove.\n",
447: stderr);
448: return(-1);
1.4 millert 449: }
450: (void)kill(lock_pid, SIGTERM);
1.6 millert 451: (void)Pclose(lockfp);
1.4 millert 452: lockfp = NULL;
453: } else if (action == 1) {
454: /* Create the lock */
1.19 millert 455: if ((lockfp = Popen(_PATH_LOCKSPOOL, "r")) == NULL ||
456: getc(lockfp) != '1') {
1.4 millert 457: lockfp = NULL;
1.6 millert 458: return(0);
1.4 millert 459: }
460:
461: lock_pid = fp_head->pid; /* new entries added at head */
462: } else {
1.19 millert 463: (void)fprintf(stderr, "handle_spool_locks: unknown action %d\n",
1.4 millert 464: action);
1.6 millert 465: return(-1);
1.4 millert 466: }
467:
1.6 millert 468: return(1);
1.4 millert 469: }
470:
471: int
472: spool_lock()
473: {
474: return(handle_spool_locks(1));
475: }
476:
477: int
478: spool_unlock()
479: {
480: return(handle_spool_locks(0));
1.1 deraadt 481: }