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