xref: /original-bsd/usr.bin/mail/popen.c (revision 2301fdfb)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)popen.c	5.13 (Berkeley) 07/08/88";
20 #endif /* not lint */
21 
22 #include "rcv.h"
23 #include <sys/signal.h>
24 #include <sys/wait.h>
25 
26 #define READ 0
27 #define WRITE 1
28 static int *pid;
29 
30 FILE *
31 Popen(cmd, mode)
32 	char *cmd;
33 	char *mode;
34 {
35 	int p[2];
36 	int myside, hisside, fd0, fd1;
37 
38 	if (pid == 0)
39 		pid = (int *) malloc((unsigned) sizeof (int) * getdtablesize());
40 	if (pipe(p) < 0)
41 		return NULL;
42 	if (*mode == 'r') {
43 		myside = p[READ];
44 		fd0 = -1;
45 		hisside = fd1 = p[WRITE];
46 	} else {
47 		myside = p[WRITE];
48 		hisside = fd0 = p[READ];
49 		fd1 = -1;
50 	}
51 	if ((pid[myside] = start_command(cmd, 0, fd0, fd1, NOSTR)) < 0) {
52 		close(p[READ]);
53 		close(p[WRITE]);
54 		return NULL;
55 	}
56 	close(hisside);
57 	return fdopen(myside, mode);
58 }
59 
60 Pclose(ptr)
61 	FILE *ptr;
62 {
63 	int i;
64 	int omask;
65 
66 	i = fileno(ptr);
67 	fclose(ptr);
68 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGHUP));
69 	i = wait_child(pid[i]);
70 	sigsetmask(omask);
71 	return i;
72 }
73 
74 /*
75  * Run a command without a shell, with optional arguments and splicing
76  * of stdin and stdout.  The command name can be a sequence of words.
77  * Signals must be handled by the caller.
78  * "Mask" contains the signals to ignore in the new process.
79  * SIGINT is enabled unless it's in the mask.
80  */
81 /*VARARGS4*/
82 run_command(cmd, mask, infd, outfd, a0, a1, a2)
83 	char *cmd;
84 	int mask, infd, outfd;
85 	char *a0, *a1, *a2;
86 {
87 	int pid;
88 
89 	if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
90 		return -1;
91 	return wait_command(pid);
92 }
93 
94 /*VARARGS4*/
95 start_command(cmd, mask, infd, outfd, a0, a1, a2)
96 	char *cmd;
97 	int mask, infd, outfd;
98 	char *a0, *a1, *a2;
99 {
100 	int pid;
101 
102 	if ((pid = vfork()) < 0) {
103 		perror("fork");
104 		return -1;
105 	}
106 	if (pid == 0) {
107 		char *argv[100];
108 		int i = getrawlist(cmd, argv, sizeof argv / sizeof *argv);
109 
110 		if ((argv[i++] = a0) != NOSTR &&
111 		    (argv[i++] = a1) != NOSTR &&
112 		    (argv[i++] = a2) != NOSTR)
113 			argv[i] = NOSTR;
114 		prepare_child(mask, infd, outfd);
115 		execvp(argv[0], argv);
116 		perror(argv[0]);
117 		_exit(1);
118 	}
119 	return pid;
120 }
121 
122 prepare_child(mask, infd, outfd)
123 	int mask, infd, outfd;
124 {
125 	int i;
126 
127 	if (infd >= 0)
128 		dup2(infd, 0);
129 	if (outfd >= 0)
130 		dup2(outfd, 1);
131 	for (i = getdtablesize(); --i > 2;)
132 		close(i);
133 	for (i = 1; i <= NSIG; i++)
134 		if (mask & sigmask(i))
135 			(void) signal(i, SIG_IGN);
136 	if ((mask & sigmask(SIGINT)) == 0)
137 		(void) signal(SIGINT, SIG_DFL);
138 	(void) sigsetmask(0);
139 }
140 
141 wait_command(pid)
142 	int pid;
143 {
144 
145 	if (wait_child(pid) < 0) {
146 		printf("Fatal error in process.\n");
147 		return -1;
148 	}
149 	return 0;
150 }
151 
152 struct child {
153 	int pid;
154 	char done;
155 	char free;
156 	union wait status;
157 	struct child *link;
158 };
159 static struct child *child;
160 
161 struct child *
162 findchild(pid)
163 	int pid;
164 {
165 	register struct child **cpp;
166 
167 	for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
168 	     cpp = &(*cpp)->link)
169 			;
170 	if (*cpp == NULL) {
171 		*cpp = (struct child *) malloc(sizeof (struct child));
172 		(*cpp)->pid = pid;
173 		(*cpp)->done = (*cpp)->free = 0;
174 		(*cpp)->link = NULL;
175 	}
176 	return *cpp;
177 }
178 
179 delchild(cp)
180 	register struct child *cp;
181 {
182 	register struct child **cpp;
183 
184 	for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
185 		;
186 	*cpp = cp->link;
187 	free((char *) cp);
188 }
189 
190 sigchild()
191 {
192 	int pid;
193 	union wait status;
194 	register struct child *cp;
195 
196 	while ((pid = wait3(&status, WNOHANG, (struct timeval *)0)) > 0) {
197 		cp = findchild(pid);
198 		if (cp->free)
199 			delchild(cp);
200 		else {
201 			cp->done = 1;
202 			cp->status = status;
203 		}
204 	}
205 }
206 
207 union wait wait_status;
208 
209 /*
210  * Wait for a specific child to die.
211  */
212 wait_child(pid)
213 	int pid;
214 {
215 	int mask = sigblock(sigmask(SIGCHLD));
216 	register struct child *cp = findchild(pid);
217 
218 	while (!cp->done)
219 		sigpause(mask);
220 	wait_status = cp->status;
221 	delchild(cp);
222 	sigsetmask(mask);
223 	return wait_status.w_status ? -1 : 0;
224 }
225 
226 /*
227  * Mark a child as don't care.
228  */
229 free_child(pid)
230 	int pid;
231 {
232 	int mask = sigblock(sigmask(SIGCHLD));
233 	register struct child *cp = findchild(pid);
234 
235 	if (cp->done)
236 		delchild(cp);
237 	else
238 		cp->free = 1;
239 	sigsetmask(mask);
240 }
241