xref: /original-bsd/usr.bin/mail/popen.c (revision 860e07fc)
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.18 (Berkeley) 08/23/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 /*
70  * XXX
71  * The old mail code used getdtablesize() to return the max number of
72  * file descriptors.  That's a *real* big number now.  Fake it.
73  */
74 #define	MAX_FILE_DESCRIPTORS		64
75 #define	MAX_FILE_DESCRIPTORS_TO_CLOSE	20
76 FILE *
77 Popen(cmd, mode)
78 	char *cmd;
79 	char *mode;
80 {
81 	int p[2];
82 	int myside, hisside, fd0, fd1;
83 	FILE *fp;
84 
85 	if (pid == 0)
86 		pid = malloc((u_int)sizeof (int) * MAX_FILE_DESCRIPTORS);
87 	if (pipe(p) < 0)
88 		return NULL;
89 	if (*mode == 'r') {
90 		myside = p[READ];
91 		fd0 = -1;
92 		hisside = fd1 = p[WRITE];
93 	} else {
94 		myside = p[WRITE];
95 		hisside = fd0 = p[READ];
96 		fd1 = -1;
97 	}
98 	if ((pid[myside] = start_command(cmd,
99 	    0, fd0, fd1, NOSTR, NOSTR, NOSTR)) < 0) {
100 		close(p[READ]);
101 		close(p[WRITE]);
102 		return NULL;
103 	}
104 	(void) close(hisside);
105 	if ((fp = fdopen(myside, mode)) != NULL)
106 		register_file(fp, 1);
107 	return fp;
108 }
109 
110 int
111 Pclose(ptr)
112 	FILE *ptr;
113 {
114 	int i;
115 	int omask;
116 
117 	i = fileno(ptr);
118 	unregister_file(ptr);
119 	(void) fclose(ptr);
120 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGHUP));
121 	i = wait_child(pid[i]);
122 	sigsetmask(omask);
123 	return i;
124 }
125 
126 void
127 close_all_files()
128 {
129 
130 	while (fp_head)
131 		if (fp_head->pipe)
132 			(void) Pclose(fp_head->fp);
133 		else
134 			(void) Fclose(fp_head->fp);
135 }
136 
137 void
138 register_file(fp, pipe)
139 	FILE *fp;
140 	int pipe;
141 {
142 	struct fp *fpp;
143 
144 	if ((fpp = (struct fp *) malloc(sizeof *fpp)) == NULL)
145 		panic("Out of memory");
146 	fpp->fp = fp;
147 	fpp->pipe = pipe;
148 	fpp->link = fp_head;
149 	fp_head = fpp;
150 }
151 
152 void
153 unregister_file(fp)
154 	FILE *fp;
155 {
156 	struct fp **pp, *p;
157 
158 	for (pp = &fp_head; p = *pp; pp = &p->link)
159 		if (p->fp == fp) {
160 			*pp = p->link;
161 			free((char *) p);
162 			return;
163 		}
164 	/* XXX
165 	 * Ignore this for now; there may still be uncaught
166 	 * duplicate closes.
167 	panic("Invalid file pointer");
168 	*/
169 }
170 
171 /*
172  * Run a command without a shell, with optional arguments and splicing
173  * of stdin and stdout.  The command name can be a sequence of words.
174  * Signals must be handled by the caller.
175  * "Mask" contains the signals to ignore in the new process.
176  * SIGINT is enabled unless it's in the mask.
177  */
178 /*VARARGS4*/
179 int
180 run_command(cmd, mask, infd, outfd, a0, a1, a2)
181 	char *cmd;
182 	int mask, infd, outfd;
183 	char *a0, *a1, *a2;
184 {
185 	int pid;
186 
187 	if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
188 		return -1;
189 	return wait_command(pid);
190 }
191 
192 /*VARARGS4*/
193 int
194 start_command(cmd, mask, infd, outfd, a0, a1, a2)
195 	char *cmd;
196 	int mask, infd, outfd;
197 	char *a0, *a1, *a2;
198 {
199 	int pid;
200 
201 	if ((pid = vfork()) < 0) {
202 		perror("fork");
203 		return -1;
204 	}
205 	if (pid == 0) {
206 		char *argv[100];
207 		int i = getrawlist(cmd, argv, sizeof argv / sizeof *argv);
208 
209 		if ((argv[i++] = a0) != NOSTR &&
210 		    (argv[i++] = a1) != NOSTR &&
211 		    (argv[i++] = a2) != NOSTR)
212 			argv[i] = NOSTR;
213 		prepare_child(mask, infd, outfd);
214 		execvp(argv[0], argv);
215 		perror(argv[0]);
216 		_exit(1);
217 	}
218 	return pid;
219 }
220 
221 void
222 prepare_child(mask, infd, outfd)
223 	int mask, infd, outfd;
224 {
225 	int i;
226 
227 	if (infd >= 0)
228 		dup2(infd, 0);
229 	if (outfd >= 0)
230 		dup2(outfd, 1);
231 	for (i = MAX_FILE_DESCRIPTORS_TO_CLOSE; --i > 2;)
232 		close(i);
233 	for (i = 1; i <= NSIG; i++)
234 		if (mask & sigmask(i))
235 			(void) signal(i, SIG_IGN);
236 	if ((mask & sigmask(SIGINT)) == 0)
237 		(void) signal(SIGINT, SIG_DFL);
238 	(void) sigsetmask(0);
239 }
240 
241 int
242 wait_command(pid)
243 	int pid;
244 {
245 
246 	if (wait_child(pid) < 0) {
247 		printf("Fatal error in process.\n");
248 		return -1;
249 	}
250 	return 0;
251 }
252 
253 static struct child *
254 findchild(pid)
255 	int pid;
256 {
257 	register struct child **cpp;
258 
259 	for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
260 	     cpp = &(*cpp)->link)
261 			;
262 	if (*cpp == NULL) {
263 		*cpp = (struct child *) malloc(sizeof (struct child));
264 		(*cpp)->pid = pid;
265 		(*cpp)->done = (*cpp)->free = 0;
266 		(*cpp)->link = NULL;
267 	}
268 	return *cpp;
269 }
270 
271 static void
272 delchild(cp)
273 	register struct child *cp;
274 {
275 	register struct child **cpp;
276 
277 	for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
278 		;
279 	*cpp = cp->link;
280 	free((char *) cp);
281 }
282 
283 void
284 sigchild(signo)
285 	int signo;
286 {
287 	int pid;
288 	union wait status;
289 	register struct child *cp;
290 
291 	while ((pid =
292 	    wait3((int *)&status, WNOHANG, (struct rusage *)0)) > 0) {
293 		cp = findchild(pid);
294 		if (cp->free)
295 			delchild(cp);
296 		else {
297 			cp->done = 1;
298 			cp->status = status;
299 		}
300 	}
301 }
302 
303 union wait wait_status;
304 
305 /*
306  * Wait for a specific child to die.
307  */
308 int
309 wait_child(pid)
310 	int pid;
311 {
312 	int mask = sigblock(sigmask(SIGCHLD));
313 	register struct child *cp = findchild(pid);
314 
315 	while (!cp->done)
316 		sigpause(mask);
317 	wait_status = cp->status;
318 	delchild(cp);
319 	sigsetmask(mask);
320 	return wait_status.w_status ? -1 : 0;
321 }
322 
323 /*
324  * Mark a child as don't care.
325  */
326 void
327 free_child(pid)
328 	int pid;
329 {
330 	int mask = sigblock(sigmask(SIGCHLD));
331 	register struct child *cp = findchild(pid);
332 
333 	if (cp->done)
334 		delchild(cp);
335 	else
336 		cp->free = 1;
337 	sigsetmask(mask);
338 }
339