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