Annotation of src/usr.bin/mail/popen.c, Revision 1.40
1.40 ! guenther 1: /* $OpenBSD: popen.c,v 1.39 2019/06/28 13:35:02 deraadt 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:
339: void
1.29 millert 340: sigchild(int signo)
1.1 deraadt 341: {
1.29 millert 342: pid_t pid;
1.13 millert 343: int status;
1.14 millert 344: struct child *cp;
1.11 deraadt 345: int save_errno = errno;
1.1 deraadt 346:
1.34 deraadt 347: while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
1.15 niklas 348: cp = findchild(pid, 1);
349: if (!cp)
350: continue;
1.1 deraadt 351: if (cp->free)
352: delchild(cp);
353: else {
354: cp->done = 1;
355: cp->status = status;
356: }
357: }
1.11 deraadt 358: errno = save_errno;
1.1 deraadt 359: }
360:
1.13 millert 361: int wait_status;
1.1 deraadt 362:
363: /*
364: * Wait for a specific child to die.
365: */
366: int
1.29 millert 367: wait_child(pid_t pid)
1.1 deraadt 368: {
1.20 millert 369: struct child *cp;
1.2 deraadt 370: sigset_t nset, oset;
1.32 mpech 371: pid_t rv = 0;
1.9 millert 372:
1.2 deraadt 373: sigemptyset(&nset);
374: sigaddset(&nset, SIGCHLD);
375: sigprocmask(SIG_BLOCK, &nset, &oset);
1.21 millert 376: /*
377: * If we have not already waited on the pid (via sigchild)
378: * wait on it now. Otherwise, use the wait status stashed
379: * by sigchild.
380: */
381: cp = findchild(pid, 1);
382: if (cp == NULL || !cp->done)
383: rv = waitpid(pid, &wait_status, 0);
384: else
385: wait_status = cp->status;
386: if (cp != NULL)
387: delchild(cp);
388: sigprocmask(SIG_SETMASK, &oset, NULL);
389: if (rv == -1 || (WIFEXITED(wait_status) && WEXITSTATUS(wait_status)))
1.20 millert 390: return(-1);
1.21 millert 391: else
392: return(0);
1.1 deraadt 393: }
394:
395: /*
396: * Mark a child as don't care.
397: */
398: void
1.29 millert 399: free_child(pid_t pid)
1.1 deraadt 400: {
1.20 millert 401: struct child *cp;
1.2 deraadt 402: sigset_t nset, oset;
1.17 millert 403:
1.2 deraadt 404: sigemptyset(&nset);
405: sigaddset(&nset, SIGCHLD);
406: sigprocmask(SIG_BLOCK, &nset, &oset);
1.21 millert 407: if ((cp = findchild(pid, 0)) != NULL) {
408: if (cp->done)
409: delchild(cp);
410: else
411: cp->free = 1;
412: }
1.2 deraadt 413: sigprocmask(SIG_SETMASK, &oset, NULL);
1.4 millert 414: }
415:
416: /*
1.22 millert 417: * Lock(1)/unlock(0) mail spool using lockspool(1).
1.4 millert 418: * Returns 1 for success, 0 for failure, -1 for bad usage.
419: */
420: static int
1.29 millert 421: handle_spool_locks(int action)
1.4 millert 422: {
423: static FILE *lockfp = NULL;
424:
425: if (action == 0) {
426: /* Clear the lock */
427: if (lockfp == NULL) {
1.6 millert 428: fputs("handle_spool_locks: no spool lock to remove.\n",
429: stderr);
430: return(-1);
1.4 millert 431: }
1.6 millert 432: (void)Pclose(lockfp);
1.4 millert 433: lockfp = NULL;
434: } else if (action == 1) {
1.27 millert 435: char *cmd;
1.37 deraadt 436: char buf[sizeof(_PATH_LOCKSPOOL) + LOGIN_NAME_MAX + 1];
1.23 millert 437:
438: /* XXX - lockspool requires root for user arg, we do not */
1.27 millert 439: if (uflag) {
440: snprintf(buf, sizeof(buf), "%s %s", _PATH_LOCKSPOOL,
441: myname);
442: cmd = buf;
443: } else
444: cmd = _PATH_LOCKSPOOL;
1.23 millert 445:
1.4 millert 446: /* Create the lock */
1.23 millert 447: lockfp = Popen(cmd, "r");
1.25 millert 448: if (lockfp == NULL)
449: return(0);
450: if (getc(lockfp) != '1') {
451: Pclose(lockfp);
1.4 millert 452: lockfp = NULL;
1.6 millert 453: return(0);
1.4 millert 454: }
455: } else {
1.19 millert 456: (void)fprintf(stderr, "handle_spool_locks: unknown action %d\n",
1.4 millert 457: action);
1.6 millert 458: return(-1);
1.4 millert 459: }
460:
1.6 millert 461: return(1);
1.4 millert 462: }
463:
464: int
1.29 millert 465: spool_lock(void)
1.4 millert 466: {
1.29 millert 467:
1.4 millert 468: return(handle_spool_locks(1));
469: }
470:
471: int
1.29 millert 472: spool_unlock(void)
1.4 millert 473: {
1.29 millert 474:
1.4 millert 475: return(handle_spool_locks(0));
1.1 deraadt 476: }