xref: /original-bsd/usr.bin/mail/popen.c (revision 403c148d)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)popen.c	5.17 (Berkeley) 06/26/92";
10 #endif /* not lint */
11 
12 #include "rcv.h"
13 #include <sys/wait.h>
14 #include "extern.h"
15 
16 #define READ 0
17 #define WRITE 1
18 static int *pid;
19 
20 struct fp {
21 	FILE *fp;
22 	int pipe;
23 	struct fp *link;
24 };
25 static struct fp *fp_head;
26 
27 struct child {
28 	int pid;
29 	char done;
30 	char free;
31 	union wait status;
32 	struct child *link;
33 };
34 static struct child *child;
35 static struct child *findchild __P((int));
36 static void delchild __P((struct child *));
37 
38 FILE *
39 Fopen(file, mode)
40 	char *file, *mode;
41 {
42 	FILE *fp;
43 
44 	if ((fp = fopen(file, mode)) != NULL)
45 		register_file(fp, 0);
46 	return fp;
47 }
48 
49 FILE *
50 Fdopen(fd, mode)
51 	int fd;
52 	char *mode;
53 {
54 	FILE *fp;
55 
56 	if ((fp = fdopen(fd, mode)) != NULL)
57 		register_file(fp, 0);
58 	return fp;
59 }
60 
61 int
62 Fclose(fp)
63 	FILE *fp;
64 {
65 	unregister_file(fp);
66 	return fclose(fp);
67 }
68 
69 FILE *
70 Popen(cmd, mode)
71 	char *cmd;
72 	char *mode;
73 {
74 	int p[2];
75 	int myside, hisside, fd0, fd1;
76 	FILE *fp;
77 
78 	if (pid == 0)
79 		pid = (int *) malloc((unsigned) sizeof (int) * getdtablesize());
80 	if (pipe(p) < 0)
81 		return NULL;
82 	if (*mode == 'r') {
83 		myside = p[READ];
84 		fd0 = -1;
85 		hisside = fd1 = p[WRITE];
86 	} else {
87 		myside = p[WRITE];
88 		hisside = fd0 = p[READ];
89 		fd1 = -1;
90 	}
91 	if ((pid[myside] = start_command(cmd,
92 	    0, fd0, fd1, NOSTR, NOSTR, NOSTR)) < 0) {
93 		close(p[READ]);
94 		close(p[WRITE]);
95 		return NULL;
96 	}
97 	(void) close(hisside);
98 	if ((fp = fdopen(myside, mode)) != NULL)
99 		register_file(fp, 1);
100 	return fp;
101 }
102 
103 int
104 Pclose(ptr)
105 	FILE *ptr;
106 {
107 	int i;
108 	int omask;
109 
110 	i = fileno(ptr);
111 	unregister_file(ptr);
112 	(void) fclose(ptr);
113 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGHUP));
114 	i = wait_child(pid[i]);
115 	sigsetmask(omask);
116 	return i;
117 }
118 
119 void
120 close_all_files()
121 {
122 
123 	while (fp_head)
124 		if (fp_head->pipe)
125 			(void) Pclose(fp_head->fp);
126 		else
127 			(void) Fclose(fp_head->fp);
128 }
129 
130 void
131 register_file(fp, pipe)
132 	FILE *fp;
133 	int pipe;
134 {
135 	struct fp *fpp;
136 
137 	if ((fpp = (struct fp *) malloc(sizeof *fpp)) == NULL)
138 		panic("Out of memory");
139 	fpp->fp = fp;
140 	fpp->pipe = pipe;
141 	fpp->link = fp_head;
142 	fp_head = fpp;
143 }
144 
145 void
146 unregister_file(fp)
147 	FILE *fp;
148 {
149 	struct fp **pp, *p;
150 
151 	for (pp = &fp_head; p = *pp; pp = &p->link)
152 		if (p->fp == fp) {
153 			*pp = p->link;
154 			free((char *) p);
155 			return;
156 		}
157 	/* XXX
158 	 * Ignore this for now; there may still be uncaught
159 	 * duplicate closes.
160 	panic("Invalid file pointer");
161 	*/
162 }
163 
164 /*
165  * Run a command without a shell, with optional arguments and splicing
166  * of stdin and stdout.  The command name can be a sequence of words.
167  * Signals must be handled by the caller.
168  * "Mask" contains the signals to ignore in the new process.
169  * SIGINT is enabled unless it's in the mask.
170  */
171 /*VARARGS4*/
172 int
173 run_command(cmd, mask, infd, outfd, a0, a1, a2)
174 	char *cmd;
175 	int mask, infd, outfd;
176 	char *a0, *a1, *a2;
177 {
178 	int pid;
179 
180 	if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
181 		return -1;
182 	return wait_command(pid);
183 }
184 
185 /*VARARGS4*/
186 int
187 start_command(cmd, mask, infd, outfd, a0, a1, a2)
188 	char *cmd;
189 	int mask, infd, outfd;
190 	char *a0, *a1, *a2;
191 {
192 	int pid;
193 
194 	if ((pid = vfork()) < 0) {
195 		perror("fork");
196 		return -1;
197 	}
198 	if (pid == 0) {
199 		char *argv[100];
200 		int i = getrawlist(cmd, argv, sizeof argv / sizeof *argv);
201 
202 		if ((argv[i++] = a0) != NOSTR &&
203 		    (argv[i++] = a1) != NOSTR &&
204 		    (argv[i++] = a2) != NOSTR)
205 			argv[i] = NOSTR;
206 		prepare_child(mask, infd, outfd);
207 		execvp(argv[0], argv);
208 		perror(argv[0]);
209 		_exit(1);
210 	}
211 	return pid;
212 }
213 
214 void
215 prepare_child(mask, infd, outfd)
216 	int mask, infd, outfd;
217 {
218 	int i;
219 
220 	if (infd >= 0)
221 		dup2(infd, 0);
222 	if (outfd >= 0)
223 		dup2(outfd, 1);
224 	for (i = getdtablesize(); --i > 2;)
225 		close(i);
226 	for (i = 1; i <= NSIG; i++)
227 		if (mask & sigmask(i))
228 			(void) signal(i, SIG_IGN);
229 	if ((mask & sigmask(SIGINT)) == 0)
230 		(void) signal(SIGINT, SIG_DFL);
231 	(void) sigsetmask(0);
232 }
233 
234 int
235 wait_command(pid)
236 	int pid;
237 {
238 
239 	if (wait_child(pid) < 0) {
240 		printf("Fatal error in process.\n");
241 		return -1;
242 	}
243 	return 0;
244 }
245 
246 static struct child *
247 findchild(pid)
248 	int pid;
249 {
250 	register struct child **cpp;
251 
252 	for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
253 	     cpp = &(*cpp)->link)
254 			;
255 	if (*cpp == NULL) {
256 		*cpp = (struct child *) malloc(sizeof (struct child));
257 		(*cpp)->pid = pid;
258 		(*cpp)->done = (*cpp)->free = 0;
259 		(*cpp)->link = NULL;
260 	}
261 	return *cpp;
262 }
263 
264 static void
265 delchild(cp)
266 	register struct child *cp;
267 {
268 	register struct child **cpp;
269 
270 	for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
271 		;
272 	*cpp = cp->link;
273 	free((char *) cp);
274 }
275 
276 void
277 sigchild(signo)
278 	int signo;
279 {
280 	int pid;
281 	union wait status;
282 	register struct child *cp;
283 
284 	while ((pid =
285 	    wait3((int *)&status, WNOHANG, (struct rusage *)0)) > 0) {
286 		cp = findchild(pid);
287 		if (cp->free)
288 			delchild(cp);
289 		else {
290 			cp->done = 1;
291 			cp->status = status;
292 		}
293 	}
294 }
295 
296 union wait wait_status;
297 
298 /*
299  * Wait for a specific child to die.
300  */
301 int
302 wait_child(pid)
303 	int pid;
304 {
305 	int mask = sigblock(sigmask(SIGCHLD));
306 	register struct child *cp = findchild(pid);
307 
308 	while (!cp->done)
309 		sigpause(mask);
310 	wait_status = cp->status;
311 	delchild(cp);
312 	sigsetmask(mask);
313 	return wait_status.w_status ? -1 : 0;
314 }
315 
316 /*
317  * Mark a child as don't care.
318  */
319 void
320 free_child(pid)
321 	int pid;
322 {
323 	int mask = sigblock(sigmask(SIGCHLD));
324 	register struct child *cp = findchild(pid);
325 
326 	if (cp->done)
327 		delchild(cp);
328 	else
329 		cp->free = 1;
330 	sigsetmask(mask);
331 }
332