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