4.4BSD snapshot (revision 8.1); add 1993 to copyright
[unix-history] / usr / src / usr.bin / mail / popen.c
... / ...
CommitLineData
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
9static char sccsid[] = "@(#)popen.c 8.1 (Berkeley) %G%";
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
20struct fp {
21 FILE *fp;
22 int pipe;
23 int pid;
24 struct fp *link;
25};
26static struct fp *fp_head;
27
28struct child {
29 int pid;
30 char done;
31 char free;
32 union wait status;
33 struct child *link;
34};
35static struct child *child;
36static struct child *findchild __P((int));
37static void delchild __P((struct child *));
38
39FILE *
40Fopen(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
52FILE *
53Fdopen(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
66int
67Fclose(fp)
68 FILE *fp;
69{
70 unregister_file(fp);
71 return fclose(fp);
72}
73
74FILE *
75Popen(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
108int
109Pclose(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
124void
125close_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
135void
136register_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
151void
152unregister_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
166file_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*/
186int
187run_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*/
200int
201start_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
228void
229prepare_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
250int
251wait_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
262static struct child *
263findchild(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
280static void
281delchild(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
292void
293sigchild(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
312union wait wait_status;
313
314/*
315 * Wait for a specific child to die.
316 */
317int
318wait_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 */
335void
336free_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}