xref: /netbsd-src/usr.bin/mail/popen.c (revision 5942983d76e2690e7d72e25a1d4df2d1a3096495)
1*5942983dSchristos /*	$NetBSD: popen.c,v 1.27 2012/04/29 23:50:22 christos Exp $	*/
288b833a7Schristos 
361f28255Scgd /*
42cb5542fSderaadt  * Copyright (c) 1980, 1993
52cb5542fSderaadt  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
1589aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
327c81c8f3Slukem #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
3488b833a7Schristos #if 0
3588b833a7Schristos static char sccsid[] = "@(#)popen.c	8.1 (Berkeley) 6/6/93";
3688b833a7Schristos #else
37*5942983dSchristos __RCSID("$NetBSD: popen.c,v 1.27 2012/04/29 23:50:22 christos Exp $");
3888b833a7Schristos #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
41c1587f4dSchristos #include <errno.h>
428207b28aSchristos #include <fcntl.h>
43c1587f4dSchristos #include <stdarg.h>
448207b28aSchristos #include <util.h>
458207b28aSchristos #include <sys/wait.h>
468207b28aSchristos 
478207b28aSchristos #include "rcv.h"
482cb5542fSderaadt #include "extern.h"
49ca13337dSchristos #include "sig.h"
5061f28255Scgd 
5161f28255Scgd #define READ 0
5261f28255Scgd #define WRITE 1
5361f28255Scgd 
5461f28255Scgd struct fp {
5561f28255Scgd 	FILE *fp;
5661f28255Scgd 	int pipe;
57c1587f4dSchristos 	pid_t pid;
5861f28255Scgd 	struct fp *link;
5961f28255Scgd };
6061f28255Scgd static struct fp *fp_head;
6161f28255Scgd 
622cb5542fSderaadt struct child {
63c1587f4dSchristos 	pid_t pid;
642cb5542fSderaadt 	char done;
652cb5542fSderaadt 	char free;
6628807559Schristos 	int status;
672cb5542fSderaadt 	struct child *link;
682cb5542fSderaadt };
69c1587f4dSchristos static struct child *child, *child_freelist = NULL;
70533d2abbSchristos static struct child *findchild(pid_t, int);
71c1587f4dSchristos 
722cb5542fSderaadt 
738207b28aSchristos #if 0	/* XXX - debugging stuff.  This should go away eventually! */
748207b28aSchristos static void
758207b28aSchristos show_one_file(FILE *fo, struct fp *fpp)
768207b28aSchristos {
778207b28aSchristos 	(void)fprintf(fo, ">>> fp: %p,  pipe: %d,  pid: %d,  link: %p\n",
788207b28aSchristos 	    fpp->fp, fpp->pipe, fpp->pid, fpp->link);
798207b28aSchristos }
808207b28aSchristos 
818207b28aSchristos void show_all_files(FILE *fo);
828207b28aSchristos __unused
83f3098750Schristos PUBLIC void
848207b28aSchristos show_all_files(FILE *fo)
858207b28aSchristos {
868207b28aSchristos 	struct fp *fpp;
878207b28aSchristos 
888207b28aSchristos 	(void)fprintf(fo, ">> FILES\n");
898207b28aSchristos 	for (fpp = fp_head; fpp; fpp = fpp->link)
908207b28aSchristos 		show_one_file(fo, fpp);
918207b28aSchristos 	(void)fprintf(fo, ">> -------\n");
928207b28aSchristos 	(void)fflush(fo);
938207b28aSchristos }
94f3098750Schristos #endif /* end debugging stuff */
958207b28aSchristos 
968207b28aSchristos 
97f3098750Schristos static void
unregister_file(FILE * fp)98b127ccccSwiz unregister_file(FILE *fp)
9961f28255Scgd {
10061f28255Scgd 	struct fp **pp, *p;
10161f28255Scgd 
10288b833a7Schristos 	for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
10361f28255Scgd 		if (p->fp == fp) {
10461f28255Scgd 			*pp = p->link;
105c1587f4dSchristos 			(void)free(p);
10661f28255Scgd 			return;
10761f28255Scgd 		}
108533d2abbSchristos 	errx(EXIT_FAILURE, "Invalid file pointer");
1092cb5542fSderaadt }
1102cb5542fSderaadt 
111f3098750Schristos PUBLIC void
register_file(FILE * fp,int pipefd,pid_t pid)112f3098750Schristos register_file(FILE *fp, int pipefd, pid_t pid)
1132cb5542fSderaadt {
114f3098750Schristos 	struct fp *fpp;
1152cb5542fSderaadt 
1162283d346Schristos 	fpp = emalloc(sizeof(*fpp));
117f3098750Schristos 	fpp->fp = fp;
118f3098750Schristos 	fpp->pipe = pipefd;
119f3098750Schristos 	fpp->pid = pid;
120f3098750Schristos 	fpp->link = fp_head;
121f3098750Schristos 	fp_head = fpp;
122f3098750Schristos }
123f3098750Schristos 
124f3098750Schristos PUBLIC FILE *
Fopen(const char * fn,const char * mode)125f3098750Schristos Fopen(const char *fn, const char *mode)
126f3098750Schristos {
127f3098750Schristos 	FILE *fp;
128f3098750Schristos 
129*5942983dSchristos 	if ((fp = fopen(fn, mode)) != NULL)
130f3098750Schristos 		register_file(fp, 0, 0);
131f3098750Schristos 	return fp;
132f3098750Schristos }
133f3098750Schristos 
134f3098750Schristos PUBLIC FILE *
Fdopen(int fd,const char * mode)135f3098750Schristos Fdopen(int fd, const char *mode)
136f3098750Schristos {
137f3098750Schristos 	FILE *fp;
138f3098750Schristos 
139*5942983dSchristos 	if ((fp = fdopen(fd, mode)) != NULL)
140f3098750Schristos 		register_file(fp, 0, 0);
141f3098750Schristos 	return fp;
142f3098750Schristos }
143f3098750Schristos 
144f3098750Schristos PUBLIC int
Fclose(FILE * fp)145f3098750Schristos Fclose(FILE *fp)
146f3098750Schristos {
147ca13337dSchristos 
148ca13337dSchristos 	if (fp == NULL)
149ca13337dSchristos 		return 0;
150ca13337dSchristos 
151f3098750Schristos 	unregister_file(fp);
152f3098750Schristos 	return fclose(fp);
153f3098750Schristos }
154f3098750Schristos 
155f3098750Schristos PUBLIC void
prepare_child(sigset_t * nset,int infd,int outfd)156f3098750Schristos prepare_child(sigset_t *nset, int infd, int outfd)
157f3098750Schristos {
158f3098750Schristos 	int i;
159f3098750Schristos 	sigset_t eset;
160f3098750Schristos 
161f3098750Schristos 	/*
162f3098750Schristos 	 * All file descriptors other than 0, 1, and 2 are supposed to be
163f3098750Schristos 	 * close-on-exec.
164f3098750Schristos 	 */
165f3098750Schristos 	if (infd > 0) {
166f3098750Schristos 		(void)dup2(infd, 0);
167f3098750Schristos 	} else if (infd != 0) {
168f3098750Schristos 		/* we don't want the child stealing my stdin input */
169f3098750Schristos 		(void)close(0);
170f3098750Schristos 		(void)open(_PATH_DEVNULL, O_RDONLY, 0);
171f3098750Schristos 	}
172f3098750Schristos 	if (outfd >= 0 && outfd != 1)
173f3098750Schristos 		(void)dup2(outfd, 1);
174ca13337dSchristos 
175f3098750Schristos 	if (nset != NULL) {
176ca13337dSchristos 		for (i = 1; i < NSIG; i++) {
177f3098750Schristos 			if (sigismember(nset, i))
178f3098750Schristos 				(void)signal(i, SIG_IGN);
179f3098750Schristos 		}
180ca13337dSchristos 		if (!sigismember(nset, SIGINT))
181f3098750Schristos 			(void)signal(SIGINT, SIG_DFL);
182f3098750Schristos 		(void)sigemptyset(&eset);
183f3098750Schristos 		(void)sigprocmask(SIG_SETMASK, &eset, NULL);
18461f28255Scgd 	}
185ca13337dSchristos }
18661f28255Scgd 
18761f28255Scgd /*
18861f28255Scgd  * Run a command without a shell, with optional arguments and splicing
189c1587f4dSchristos  * of stdin (-1 means none) and stdout.  The command name can be a sequence
190c1587f4dSchristos  * of words.
19161f28255Scgd  * Signals must be handled by the caller.
192c1587f4dSchristos  * "nset" contains the signals to ignore in the new process.
193c1587f4dSchristos  * SIGINT is enabled unless it's in "nset".
19461f28255Scgd  */
195c1587f4dSchristos static pid_t
start_commandv(const char * cmd,sigset_t * nset,int infd,int outfd,va_list args)196ece0fd5cSchristos start_commandv(const char *cmd, sigset_t *nset, int infd, int outfd,
197ece0fd5cSchristos     va_list args)
19861f28255Scgd {
199c1587f4dSchristos 	pid_t pid;
20061f28255Scgd 
201ca13337dSchristos 	sig_check();
202c1587f4dSchristos 	if ((pid = fork()) < 0) {
203ae38aa87Swiz 		warn("fork");
20461f28255Scgd 		return -1;
20561f28255Scgd 	}
20661f28255Scgd 	if (pid == 0) {
20761f28255Scgd 		char *argv[100];
208ca13337dSchristos 		size_t i;
20961f28255Scgd 
210ca13337dSchristos 		i = getrawlist(cmd, argv, (int)__arraycount(argv));
211ca13337dSchristos 		while (i < __arraycount(argv) - 1 &&
212ca13337dSchristos 		    (argv[i++] = va_arg(args, char *)) != NULL)
213ca286310Schristos 			continue;
214ab850155Swiz 		argv[i] = NULL;
215c1587f4dSchristos 		prepare_child(nset, infd, outfd);
216ca286310Schristos 		(void)execvp(argv[0], argv);
217ae38aa87Swiz 		warn("%s", argv[0]);
21861f28255Scgd 		_exit(1);
21961f28255Scgd 	}
220533d2abbSchristos 	(void)findchild(pid, 0);
22161f28255Scgd 	return pid;
22261f28255Scgd }
22361f28255Scgd 
224f3098750Schristos PUBLIC int
start_command(const char * cmd,sigset_t * nset,int infd,int outfd,...)225ece0fd5cSchristos start_command(const char *cmd, sigset_t *nset, int infd, int outfd, ...)
226c1587f4dSchristos {
227c1587f4dSchristos 	va_list args;
228c1587f4dSchristos 	int r;
229c1587f4dSchristos 
230c1587f4dSchristos 	va_start(args, outfd);
231c1587f4dSchristos 	r = start_commandv(cmd, nset, infd, outfd, args);
232c1587f4dSchristos 	va_end(args);
233c1587f4dSchristos 	return r;
234c1587f4dSchristos }
235c1587f4dSchristos 
236f3098750Schristos PUBLIC FILE *
Popen(const char * cmd,const char * mode)237f3098750Schristos Popen(const char *cmd, const char *mode)
23861f28255Scgd {
239f3098750Schristos 	int p[2];
240f3098750Schristos 	int myside, hisside, fd0, fd1;
241f3098750Schristos 	pid_t pid;
242f3098750Schristos 	sigset_t nset;
243f3098750Schristos 	FILE *fp;
244f3098750Schristos 	char *shellcmd;
24561f28255Scgd 
246*5942983dSchristos 	if (pipe2(p, O_CLOEXEC) < 0)
247f3098750Schristos 		return NULL;
248f3098750Schristos 	if (*mode == 'r') {
249f3098750Schristos 		myside = p[READ];
250f3098750Schristos 		hisside = fd0 = fd1 = p[WRITE];
251f3098750Schristos 	} else {
252f3098750Schristos 		myside = p[WRITE];
253f3098750Schristos 		hisside = fd0 = p[READ];
254f3098750Schristos 		fd1 = -1;
255c1587f4dSchristos 	}
256f3098750Schristos 	(void)sigemptyset(&nset);
257f3098750Schristos 	if ((shellcmd = value(ENAME_SHELL)) == NULL)
258f3098750Schristos 		shellcmd = __UNCONST(_PATH_CSHELL);
259f3098750Schristos 	pid = start_command(shellcmd, &nset, fd0, fd1, "-c", cmd, NULL);
260f3098750Schristos 	if (pid < 0) {
261f3098750Schristos 		(void)close(p[READ]);
262f3098750Schristos 		(void)close(p[WRITE]);
263f3098750Schristos 		return NULL;
264c1587f4dSchristos 	}
265f3098750Schristos 	(void)close(hisside);
266f3098750Schristos 	if ((fp = fdopen(myside, mode)) != NULL)
267f3098750Schristos 		register_file(fp, 1, pid);
268f3098750Schristos 	return fp;
26961f28255Scgd }
27061f28255Scgd 
2712cb5542fSderaadt static struct child *
findchild(pid_t pid,int dont_alloc)272c1587f4dSchristos findchild(pid_t pid, int dont_alloc)
27361f28255Scgd {
2747c81c8f3Slukem 	struct child **cpp;
27561f28255Scgd 
27661f28255Scgd 	for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
27761f28255Scgd 	     cpp = &(*cpp)->link)
2788207b28aSchristos 			continue;
27961f28255Scgd 	if (*cpp == NULL) {
280c1587f4dSchristos 		if (dont_alloc)
281c1587f4dSchristos 			return NULL;
282c1587f4dSchristos 		if (child_freelist) {
283c1587f4dSchristos 			*cpp = child_freelist;
284c1587f4dSchristos 			child_freelist = (*cpp)->link;
2858207b28aSchristos 		} else
286349d9781Schristos 			*cpp = emalloc(sizeof(**cpp));
2878207b28aSchristos 
28861f28255Scgd 		(*cpp)->pid = pid;
28961f28255Scgd 		(*cpp)->done = (*cpp)->free = 0;
29061f28255Scgd 		(*cpp)->link = NULL;
29161f28255Scgd 	}
29261f28255Scgd 	return *cpp;
29361f28255Scgd }
29461f28255Scgd 
2952cb5542fSderaadt static void
delchild(struct child * cp)296b127ccccSwiz delchild(struct child *cp)
29761f28255Scgd {
2987c81c8f3Slukem 	struct child **cpp;
29961f28255Scgd 
30061f28255Scgd 	for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
3018207b28aSchristos 		continue;
30261f28255Scgd 	*cpp = cp->link;
303c1587f4dSchristos 	cp->link = child_freelist;
304c1587f4dSchristos 	child_freelist = cp;
30561f28255Scgd }
30661f28255Scgd 
30761f28255Scgd /*
30861f28255Scgd  * Wait for a specific child to die.
30961f28255Scgd  */
310f3098750Schristos PUBLIC int
wait_child(pid_t pid)311c1587f4dSchristos wait_child(pid_t pid)
31261f28255Scgd {
31302b57cf8Sperry 	struct child *cp;
314c1587f4dSchristos 	sigset_t nset, oset;
315c1587f4dSchristos 	pid_t rv = 0;
316c1587f4dSchristos 
317ca286310Schristos 	(void)sigemptyset(&nset);
318ca286310Schristos 	(void)sigaddset(&nset, SIGCHLD);
319ca286310Schristos 	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
320c1587f4dSchristos 	/*
321c1587f4dSchristos 	 * If we have not already waited on the pid (via sigchild)
322c1587f4dSchristos 	 * wait on it now.  Otherwise, use the wait status stashed
323c1587f4dSchristos 	 * by sigchild.
324c1587f4dSchristos 	 */
325c1587f4dSchristos 	cp = findchild(pid, 1);
326c1587f4dSchristos 	if (cp == NULL || !cp->done)
327c1587f4dSchristos 		rv = waitpid(pid, &wait_status, 0);
328c1587f4dSchristos 	else
32961f28255Scgd 		wait_status = cp->status;
330c1587f4dSchristos 	if (cp != NULL)
33161f28255Scgd 		delchild(cp);
332ca286310Schristos 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
333c1587f4dSchristos 	if (rv == -1 || (WIFEXITED(wait_status) && WEXITSTATUS(wait_status)))
334c1587f4dSchristos 		return -1;
335c1587f4dSchristos 	else
336c1587f4dSchristos 		return 0;
33761f28255Scgd }
33861f28255Scgd 
339f3098750Schristos static pid_t
file_pid(FILE * fp)340f3098750Schristos file_pid(FILE *fp)
341f3098750Schristos {
342f3098750Schristos 	struct fp *p;
343f3098750Schristos 
344f3098750Schristos 	for (p = fp_head; p; p = p->link)
345f3098750Schristos 		if (p->fp == fp)
346f3098750Schristos 			return p->pid;
347533d2abbSchristos 	errx(EXIT_FAILURE, "Invalid file pointer");
348f3098750Schristos 	/*NOTREACHED*/
349f3098750Schristos }
350f3098750Schristos 
351f3098750Schristos PUBLIC int
Pclose(FILE * ptr)352f3098750Schristos Pclose(FILE *ptr)
353f3098750Schristos {
354f3098750Schristos 	int i;
355f3098750Schristos 	sigset_t nset, oset;
356f3098750Schristos 
357ca13337dSchristos 	if (ptr == NULL)
358ca13337dSchristos 		return 0;
359ca13337dSchristos 
360f3098750Schristos 	i = file_pid(ptr);
361f3098750Schristos 	unregister_file(ptr);
362f3098750Schristos 	(void)fclose(ptr);
363f3098750Schristos 	(void)sigemptyset(&nset);
364f3098750Schristos 	(void)sigaddset(&nset, SIGINT);
365f3098750Schristos 	(void)sigaddset(&nset, SIGHUP);
366f3098750Schristos 	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
367f3098750Schristos 	i = wait_child(i);
368f3098750Schristos 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
369f3098750Schristos 	return i;
370f3098750Schristos }
371f3098750Schristos 
372f3098750Schristos PUBLIC void
close_all_files(void)373f3098750Schristos close_all_files(void)
374f3098750Schristos {
375f3098750Schristos 	while (fp_head)
376f3098750Schristos 		if (fp_head->pipe)
377f3098750Schristos 			(void)Pclose(fp_head->fp);
378f3098750Schristos 		else
379f3098750Schristos 			(void)Fclose(fp_head->fp);
380f3098750Schristos }
381f3098750Schristos 
382f3098750Schristos PUBLIC FILE *
last_registered_file(int last_pipe)383f3098750Schristos last_registered_file(int last_pipe)
384f3098750Schristos {
385f3098750Schristos 	struct fp *fpp;
386f3098750Schristos 
387f3098750Schristos 	if (last_pipe == 0)
388f3098750Schristos 		return fp_head ? fp_head->fp : NULL;
389f3098750Schristos 
390f3098750Schristos 	for (fpp = fp_head; fpp; fpp = fpp->link)
391f3098750Schristos 		if (fpp->pipe)
392f3098750Schristos 			return fpp->fp;
393f3098750Schristos 	return NULL;
394f3098750Schristos }
395f3098750Schristos 
396f3098750Schristos PUBLIC void
close_top_files(FILE * fp_stop)397f3098750Schristos close_top_files(FILE *fp_stop)
398f3098750Schristos {
399f3098750Schristos 	while (fp_head && fp_head->fp != fp_stop)
400f3098750Schristos 		if (fp_head->pipe)
401f3098750Schristos 			(void)Pclose(fp_head->fp);
402f3098750Schristos 		else
403f3098750Schristos 			(void)Fclose(fp_head->fp);
404f3098750Schristos }
405f3098750Schristos 
406f3098750Schristos #ifdef MIME_SUPPORT
407f3098750Schristos PUBLIC void
flush_files(FILE * fo,int only_pipes)408f3098750Schristos flush_files(FILE *fo, int only_pipes)
409f3098750Schristos {
410f3098750Schristos 	struct fp *fpp;
411f3098750Schristos 
412f3098750Schristos 	if (fo)
413f3098750Schristos 		(void)fflush(fo);
414f3098750Schristos 
415f3098750Schristos 	for (fpp = fp_head; fpp; fpp = fpp->link)
416f3098750Schristos 		if (!only_pipes || fpp->pipe)
417f3098750Schristos 			(void)fflush(fpp->fp);
418f3098750Schristos 
419f3098750Schristos 	(void)fflush(stdout);
420f3098750Schristos }
421f3098750Schristos #endif /* MIME_SUPPORT */
422f3098750Schristos 
423f3098750Schristos static int
wait_command(pid_t pid)424f3098750Schristos wait_command(pid_t pid)
425f3098750Schristos {
426f3098750Schristos 
427f3098750Schristos 	if (wait_child(pid) < 0) {
428f3098750Schristos 		(void)puts("Fatal error in process.");
429f3098750Schristos 		return -1;
430f3098750Schristos 	}
431f3098750Schristos 	return 0;
432f3098750Schristos }
433f3098750Schristos 
434f3098750Schristos PUBLIC int
run_command(const char * cmd,sigset_t * nset,int infd,int outfd,...)435f3098750Schristos run_command(const char *cmd, sigset_t *nset, int infd, int outfd, ...)
436f3098750Schristos {
437f3098750Schristos 	pid_t pid;
438f3098750Schristos 	va_list args;
439ca13337dSchristos 	int rval;
440f3098750Schristos 
441ca13337dSchristos #ifdef BROKEN_EXEC_TTY_RESTORE
442ca13337dSchristos 	struct termios ttybuf;
443ca13337dSchristos 	int tcrval;
444ca13337dSchristos 	/*
445ca13337dSchristos 	 * XXX - grab the tty settings as currently they can get
446ca13337dSchristos 	 * trashed by emacs-21 when suspending with bash-3.2.25 as the
447ca13337dSchristos 	 * shell.
448ca13337dSchristos 	 *
449ca13337dSchristos 	 * 1) from the mail editor, start "emacs -nw" (21.4)
450ca13337dSchristos 	 * 2) suspend emacs to the shell (bash 3.2.25)
451ca13337dSchristos 	 * 3) resume emacs
452ca13337dSchristos 	 * 4) exit emacs back to the mail editor
453ca13337dSchristos 	 * 5) discover the tty is screwed: the mail editor is no
454ca13337dSchristos 	 *    longer receiving characters
455ca13337dSchristos 	 *
456ca13337dSchristos 	 * - This occurs on both i386 and amd64.
457ca13337dSchristos 	 * - This did _NOT_ occur before 4.99.10.
458ca13337dSchristos 	 * - This does _NOT_ occur if the editor is vi(1) or if the shell
459ca13337dSchristos 	 *   is /bin/sh.
460ca13337dSchristos 	 * - This _DOES_ happen with the old mail(1) from 2006-01-01 (long
461ca13337dSchristos 	 *   before my changes).
462ca13337dSchristos 	 *
463ca13337dSchristos 	 * This is the commit that introduced this "feature":
464ca13337dSchristos 	 * http://mail-index.netbsd.org/source-changes/2007/02/09/0020.html
465ca13337dSchristos 	 */
466ca13337dSchristos 	if ((tcrval = tcgetattr(fileno(stdin), &ttybuf)) == -1)
467ca13337dSchristos 		warn("tcgetattr");
468ca13337dSchristos #endif
469f3098750Schristos 	va_start(args, outfd);
470f3098750Schristos 	pid = start_commandv(cmd, nset, infd, outfd, args);
471f3098750Schristos 	va_end(args);
472f3098750Schristos 	if (pid < 0)
473f3098750Schristos 		return -1;
474ca13337dSchristos 	rval = wait_command(pid);
475ca13337dSchristos #ifdef BROKEN_EXEC_TTY_RESTORE
476ca13337dSchristos 	if (tcrval != -1 && tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf) == -1)
477ca13337dSchristos 		warn("tcsetattr");
478ca13337dSchristos #endif
479ca13337dSchristos 	return rval;
480ca13337dSchristos 
481f3098750Schristos }
482f3098750Schristos 
483f3098750Schristos /*ARGSUSED*/
484f3098750Schristos PUBLIC void
sigchild(int signo __unused)485f3098750Schristos sigchild(int signo __unused)
486f3098750Schristos {
487f3098750Schristos 	pid_t pid;
488f3098750Schristos 	int status;
489f3098750Schristos 	struct child *cp;
490ca13337dSchristos 	int save_errno;
491f3098750Schristos 
492ca13337dSchristos 	save_errno = errno;
493ca13337dSchristos 	while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
494ca13337dSchristos 		cp = findchild(pid, 1);	/* async-signal-safe: we don't alloc */
495f3098750Schristos 		if (!cp)
496f3098750Schristos 			continue;
497f3098750Schristos 		if (cp->free)
498ca13337dSchristos 			delchild(cp);	/* async-signal-safe: list changes */
499f3098750Schristos 		else {
500f3098750Schristos 			cp->done = 1;
501f3098750Schristos 			cp->status = status;
502f3098750Schristos 		}
503f3098750Schristos 	}
504f3098750Schristos 	errno = save_errno;
505f3098750Schristos }
506f3098750Schristos 
50761f28255Scgd /*
50861f28255Scgd  * Mark a child as don't care.
50961f28255Scgd  */
510f3098750Schristos PUBLIC void
free_child(pid_t pid)511c1587f4dSchristos free_child(pid_t pid)
51261f28255Scgd {
51302b57cf8Sperry 	struct child *cp;
514c1587f4dSchristos 	sigset_t nset, oset;
515c1587f4dSchristos 
516ca286310Schristos 	(void)sigemptyset(&nset);
517ca286310Schristos 	(void)sigaddset(&nset, SIGCHLD);
518ca286310Schristos 	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
519c1587f4dSchristos 	if ((cp = findchild(pid, 0)) != NULL) {
52061f28255Scgd 		if (cp->done)
52161f28255Scgd 			delchild(cp);
52261f28255Scgd 		else
52361f28255Scgd 			cp->free = 1;
524c1587f4dSchristos 	}
525ca286310Schristos 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
52661f28255Scgd }
527