1331722Seadler/*
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
301590Srgrimes#ifndef lint
3174769Smikeh#if 0
321590Srgrimesstatic char sccsid[] = "@(#)popen.c	8.1 (Berkeley) 6/6/93";
3374769Smikeh#endif
341590Srgrimes#endif /* not lint */
3599112Sobrien#include <sys/cdefs.h>
3699112Sobrien__FBSDID("$FreeBSD: stable/11/usr.bin/mail/popen.c 336953 2018-07-31 00:37:25Z markj $");
371590Srgrimes
381590Srgrimes#include "rcv.h"
391590Srgrimes#include <sys/wait.h>
401590Srgrimes#include <fcntl.h>
411590Srgrimes#include "extern.h"
421590Srgrimes
431590Srgrimes#define READ 0
441590Srgrimes#define WRITE 1
451590Srgrimes
461590Srgrimesstruct fp {
4777274Smikeh	FILE	*fp;
4877274Smikeh	int	pipe;
49336953Smarkj	int	pid;
5077274Smikeh	struct	fp *link;
511590Srgrimes};
521590Srgrimesstatic struct fp *fp_head;
531590Srgrimes
541590Srgrimesstruct child {
55336953Smarkj	int	pid;
5677274Smikeh	char	done;
5777274Smikeh	char	free;
5877274Smikeh	int	status;
5977274Smikeh	struct	child *link;
601590Srgrimes};
61336953Smarkjstatic struct child *child;
62336953Smarkjstatic struct child *findchild(int);
6392921Simpstatic void delchild(struct child *);
64336953Smarkjstatic int file_pid(FILE *);
651590Srgrimes
661590SrgrimesFILE *
67216564ScharnierFopen(const char *path, const char *mode)
681590Srgrimes{
691590Srgrimes	FILE *fp;
701590Srgrimes
7177274Smikeh	if ((fp = fopen(path, mode)) != NULL) {
721590Srgrimes		register_file(fp, 0, 0);
7377274Smikeh		(void)fcntl(fileno(fp), F_SETFD, 1);
741590Srgrimes	}
7577274Smikeh	return (fp);
761590Srgrimes}
771590Srgrimes
781590SrgrimesFILE *
79216564ScharnierFdopen(int fd, const char *mode)
801590Srgrimes{
811590Srgrimes	FILE *fp;
821590Srgrimes
831590Srgrimes	if ((fp = fdopen(fd, mode)) != NULL) {
841590Srgrimes		register_file(fp, 0, 0);
8577274Smikeh		(void)fcntl(fileno(fp), F_SETFD, 1);
861590Srgrimes	}
8777274Smikeh	return (fp);
881590Srgrimes}
891590Srgrimes
901590Srgrimesint
91216564ScharnierFclose(FILE *fp)
921590Srgrimes{
931590Srgrimes	unregister_file(fp);
9477274Smikeh	return (fclose(fp));
951590Srgrimes}
961590Srgrimes
971590SrgrimesFILE *
98216564ScharnierPopen(char *cmd, const char *mode)
991590Srgrimes{
1001590Srgrimes	int p[2];
1011590Srgrimes	int myside, hisside, fd0, fd1;
102336953Smarkj	int pid;
10388150Smikeh	sigset_t nset;
1041590Srgrimes	FILE *fp;
1051590Srgrimes
1061590Srgrimes	if (pipe(p) < 0)
10777274Smikeh		return (NULL);
10877274Smikeh	(void)fcntl(p[READ], F_SETFD, 1);
10977274Smikeh	(void)fcntl(p[WRITE], F_SETFD, 1);
1101590Srgrimes	if (*mode == 'r') {
1111590Srgrimes		myside = p[READ];
112336953Smarkj		fd0 = -1;
113336953Smarkj		hisside = fd1 = p[WRITE];
1141590Srgrimes	} else {
1151590Srgrimes		myside = p[WRITE];
1161590Srgrimes		hisside = fd0 = p[READ];
1171590Srgrimes		fd1 = -1;
1181590Srgrimes	}
11988150Smikeh	(void)sigemptyset(&nset);
120336953Smarkj	if ((pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL)) < 0) {
12177274Smikeh		(void)close(p[READ]);
12277274Smikeh		(void)close(p[WRITE]);
12377274Smikeh		return (NULL);
1241590Srgrimes	}
12577274Smikeh	(void)close(hisside);
1261590Srgrimes	if ((fp = fdopen(myside, mode)) != NULL)
1271590Srgrimes		register_file(fp, 1, pid);
12877274Smikeh	return (fp);
1291590Srgrimes}
1301590Srgrimes
1311590Srgrimesint
132216564ScharnierPclose(FILE *ptr)
1331590Srgrimes{
1341590Srgrimes	int i;
13588150Smikeh	sigset_t nset, oset;
1361590Srgrimes
1371590Srgrimes	i = file_pid(ptr);
1381590Srgrimes	unregister_file(ptr);
13977274Smikeh	(void)fclose(ptr);
14088150Smikeh	(void)sigemptyset(&nset);
14188150Smikeh	(void)sigaddset(&nset, SIGINT);
14288150Smikeh	(void)sigaddset(&nset, SIGHUP);
14388150Smikeh	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
1441590Srgrimes	i = wait_child(i);
14588150Smikeh	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
14677274Smikeh	return (i);
1471590Srgrimes}
1481590Srgrimes
1491590Srgrimesvoid
150216564Scharnierclose_all_files(void)
1511590Srgrimes{
1521590Srgrimes
15377274Smikeh	while (fp_head != NULL)
1541590Srgrimes		if (fp_head->pipe)
15577274Smikeh			(void)Pclose(fp_head->fp);
1561590Srgrimes		else
15777274Smikeh			(void)Fclose(fp_head->fp);
1581590Srgrimes}
1591590Srgrimes
1601590Srgrimesvoid
161336953Smarkjregister_file(FILE *fp, int pipe, int pid)
1621590Srgrimes{
1631590Srgrimes	struct fp *fpp;
1641590Srgrimes
16577274Smikeh	if ((fpp = malloc(sizeof(*fpp))) == NULL)
16674769Smikeh		err(1, "Out of memory");
1671590Srgrimes	fpp->fp = fp;
1681590Srgrimes	fpp->pipe = pipe;
1691590Srgrimes	fpp->pid = pid;
1701590Srgrimes	fpp->link = fp_head;
1711590Srgrimes	fp_head = fpp;
1721590Srgrimes}
1731590Srgrimes
1741590Srgrimesvoid
175216564Scharnierunregister_file(FILE *fp)
1761590Srgrimes{
1771590Srgrimes	struct fp **pp, *p;
1781590Srgrimes
17977274Smikeh	for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
1801590Srgrimes		if (p->fp == fp) {
1811590Srgrimes			*pp = p->link;
18277274Smikeh			(void)free(p);
1831590Srgrimes			return;
1841590Srgrimes		}
18574769Smikeh	errx(1, "Invalid file pointer");
18674769Smikeh	/*NOTREACHED*/
1871590Srgrimes}
1881590Srgrimes
189336953Smarkjint
190216564Scharnierfile_pid(FILE *fp)
1911590Srgrimes{
1921590Srgrimes	struct fp *p;
1931590Srgrimes
19477274Smikeh	for (p = fp_head; p != NULL; p = p->link)
1951590Srgrimes		if (p->fp == fp)
1961590Srgrimes			return (p->pid);
19774769Smikeh	errx(1, "Invalid file pointer");
1981590Srgrimes	/*NOTREACHED*/
1991590Srgrimes}
2001590Srgrimes
2011590Srgrimes/*
2021590Srgrimes * Run a command without a shell, with optional arguments and splicing
203336953Smarkj * of stdin and stdout.  The command name can be a sequence of words.
2041590Srgrimes * Signals must be handled by the caller.
205336953Smarkj * "Mask" contains the signals to ignore in the new process.
206336953Smarkj * SIGINT is enabled unless it's in the mask.
2071590Srgrimes */
208336953Smarkj/*VARARGS4*/
209336953Smarkjint
210336953Smarkjrun_command(char *cmd, sigset_t *mask, int infd, int outfd, char *a0,
211336953Smarkj	char *a1, char *a2)
2121590Srgrimes{
213336953Smarkj	int pid;
2141590Srgrimes
215336953Smarkj	if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
216336953Smarkj		return (-1);
217336953Smarkj	return (wait_command(pid));
218336953Smarkj}
219336953Smarkj
220336953Smarkj/*VARARGS4*/
221336953Smarkjint
222336953Smarkjstart_command(char *cmd, sigset_t *mask, int infd, int outfd, char *a0,
223336953Smarkj	char *a1, char *a2)
224336953Smarkj{
225336953Smarkj	int pid;
226336953Smarkj
22740188Sbde	if ((pid = fork()) < 0) {
22874769Smikeh		warn("fork");
22977274Smikeh		return (-1);
2301590Srgrimes	}
2311590Srgrimes	if (pid == 0) {
2321590Srgrimes		char *argv[100];
23377274Smikeh		int i = getrawlist(cmd, argv, sizeof(argv) / sizeof(*argv));
2341590Srgrimes
235336953Smarkj		if ((argv[i++] = a0) != NULL &&
236336953Smarkj		    (argv[i++] = a1) != NULL &&
237336953Smarkj		    (argv[i++] = a2) != NULL)
238336953Smarkj			argv[i] = NULL;
239336953Smarkj		prepare_child(mask, infd, outfd);
2401590Srgrimes		execvp(argv[0], argv);
24174769Smikeh		warn("%s", argv[0]);
2421590Srgrimes		_exit(1);
2431590Srgrimes	}
24477274Smikeh	return (pid);
2451590Srgrimes}
2461590Srgrimes
2471590Srgrimesvoid
248216564Scharnierprepare_child(sigset_t *nset, int infd, int outfd)
2491590Srgrimes{
2501590Srgrimes	int i;
25188150Smikeh	sigset_t eset;
2521590Srgrimes
2531590Srgrimes	/*
2541590Srgrimes	 * All file descriptors other than 0, 1, and 2 are supposed to be
2551590Srgrimes	 * close-on-exec.
2561590Srgrimes	 */
2571590Srgrimes	if (infd >= 0)
2581590Srgrimes		dup2(infd, 0);
2591590Srgrimes	if (outfd >= 0)
2601590Srgrimes		dup2(outfd, 1);
26188150Smikeh	for (i = 1; i < NSIG; i++)
26288150Smikeh		if (nset != NULL && sigismember(nset, i))
26377274Smikeh			(void)signal(i, SIG_IGN);
26488150Smikeh	if (nset == NULL || !sigismember(nset, SIGINT))
26577274Smikeh		(void)signal(SIGINT, SIG_DFL);
26688150Smikeh	(void)sigemptyset(&eset);
26788150Smikeh	(void)sigprocmask(SIG_SETMASK, &eset, NULL);
2681590Srgrimes}
2691590Srgrimes
2701590Srgrimesint
271336953Smarkjwait_command(int pid)
2721590Srgrimes{
2731590Srgrimes
2741590Srgrimes	if (wait_child(pid) < 0) {
2751590Srgrimes		printf("Fatal error in process.\n");
27677274Smikeh		return (-1);
2771590Srgrimes	}
27877274Smikeh	return (0);
2791590Srgrimes}
2801590Srgrimes
2811590Srgrimesstatic struct child *
282336953Smarkjfindchild(int pid)
2831590Srgrimes{
28477274Smikeh	struct child **cpp;
2851590Srgrimes
2861590Srgrimes	for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
28777274Smikeh	    cpp = &(*cpp)->link)
2881590Srgrimes			;
2891590Srgrimes	if (*cpp == NULL) {
290336953Smarkj		*cpp = malloc(sizeof(struct child));
291336953Smarkj		if (*cpp == NULL)
292336953Smarkj			err(1, "Out of memory");
2931590Srgrimes		(*cpp)->pid = pid;
2941590Srgrimes		(*cpp)->done = (*cpp)->free = 0;
2951590Srgrimes		(*cpp)->link = NULL;
2961590Srgrimes	}
29777274Smikeh	return (*cpp);
2981590Srgrimes}
2991590Srgrimes
3001590Srgrimesstatic void
301216564Scharnierdelchild(struct child *cp)
3021590Srgrimes{
30377274Smikeh	struct child **cpp;
3041590Srgrimes
3051590Srgrimes	for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
3061590Srgrimes		;
3071590Srgrimes	*cpp = cp->link;
308336953Smarkj	(void)free(cp);
3091590Srgrimes}
3101590Srgrimes
31177274Smikeh/*ARGSUSED*/
3121590Srgrimesvoid
313216564Scharniersigchild(int signo __unused)
3141590Srgrimes{
315336953Smarkj	int pid;
31674769Smikeh	int status;
31777274Smikeh	struct child *cp;
3181590Srgrimes
319252679Skevlo	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
320336953Smarkj		cp = findchild(pid);
3211590Srgrimes		if (cp->free)
3221590Srgrimes			delchild(cp);
3231590Srgrimes		else {
3241590Srgrimes			cp->done = 1;
3251590Srgrimes			cp->status = status;
3261590Srgrimes		}
3271590Srgrimes	}
3281590Srgrimes}
3291590Srgrimes
33074769Smikehint wait_status;
3311590Srgrimes
3321590Srgrimes/*
3331590Srgrimes * Wait for a specific child to die.
3341590Srgrimes */
3351590Srgrimesint
336336953Smarkjwait_child(int pid)
3371590Srgrimes{
338336953Smarkj	sigset_t nset, oset;
339335693Seadler	struct child *cp;
3401590Srgrimes
34188150Smikeh	(void)sigemptyset(&nset);
34288150Smikeh	(void)sigaddset(&nset, SIGCHLD);
343336953Smarkj	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
344336953Smarkj
345336953Smarkj	cp = findchild(pid);
346336953Smarkj
347336953Smarkj	while (!cp->done)
348336953Smarkj		(void)sigsuspend(&oset);
349336953Smarkj	wait_status = cp->status;
350336953Smarkj	delchild(cp);
35188150Smikeh	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
352336953Smarkj	return ((WIFEXITED(wait_status) && WEXITSTATUS(wait_status)) ? -1 : 0);
3531590Srgrimes}
3541590Srgrimes
3551590Srgrimes/*
3561590Srgrimes * Mark a child as don't care.
3571590Srgrimes */
3581590Srgrimesvoid
359336953Smarkjfree_child(int pid)
3601590Srgrimes{
36188150Smikeh	sigset_t nset, oset;
362336953Smarkj	struct child *cp = findchild(pid);
3631590Srgrimes
36488150Smikeh	(void)sigemptyset(&nset);
36588150Smikeh	(void)sigaddset(&nset, SIGCHLD);
366336953Smarkj	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
367336953Smarkj
368336953Smarkj	if (cp->done)
369336953Smarkj		delchild(cp);
370336953Smarkj	else
371336953Smarkj		cp->free = 1;
37288150Smikeh	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
3731590Srgrimes}
374