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