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