]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/mail/popen.c
This commit was generated by cvs2svn to compensate for changes in r76726,
[FreeBSD/FreeBSD.git] / usr.bin / mail / popen.c
1 /*
2  * Copyright (c) 1980, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)popen.c     8.1 (Berkeley) 6/6/93";
37 #endif
38 static const char rcsid[] =
39   "$FreeBSD$";
40 #endif /* not lint */
41
42 #include "rcv.h"
43 #include <sys/wait.h>
44 #include <fcntl.h>
45 #include "extern.h"
46
47 #define READ 0
48 #define WRITE 1
49
50 struct fp {
51         FILE *fp;
52         int pipe;
53         int pid;
54         struct fp *link;
55 };
56 static struct fp *fp_head;
57
58 struct child {
59         int pid;
60         char done;
61         char free;
62         int status;
63         struct child *link;
64 };
65 static struct child *child;
66 static struct child *findchild __P((int));
67 static void delchild __P((struct child *));
68 static int file_pid __P((FILE *));
69
70 FILE *
71 Fopen(file, mode)
72         char *file, *mode;
73 {
74         FILE *fp;
75
76         if ((fp = fopen(file, mode)) != NULL) {
77                 register_file(fp, 0, 0);
78                 (void) fcntl(fileno(fp), F_SETFD, 1);
79         }
80         return fp;
81 }
82
83 FILE *
84 Fdopen(fd, mode)
85         int fd;
86         char *mode;
87 {
88         FILE *fp;
89
90         if ((fp = fdopen(fd, mode)) != NULL) {
91                 register_file(fp, 0, 0);
92                 (void) fcntl(fileno(fp), F_SETFD, 1);
93         }
94         return fp;
95 }
96
97 int
98 Fclose(fp)
99         FILE *fp;
100 {
101         unregister_file(fp);
102         return fclose(fp);
103 }
104
105 FILE *
106 Popen(cmd, mode)
107         char *cmd;
108         char *mode;
109 {
110         int p[2];
111         int myside, hisside, fd0, fd1;
112         int pid;
113         FILE *fp;
114
115         if (pipe(p) < 0)
116                 return NULL;
117         (void) fcntl(p[READ], F_SETFD, 1);
118         (void) fcntl(p[WRITE], F_SETFD, 1);
119         if (*mode == 'r') {
120                 myside = p[READ];
121                 fd0 = -1;
122                 hisside = fd1 = p[WRITE];
123         } else {
124                 myside = p[WRITE];
125                 hisside = fd0 = p[READ];
126                 fd1 = -1;
127         }
128         if ((pid = start_command(cmd, 0, fd0, fd1, NOSTR, NOSTR, NOSTR)) < 0) {
129                 close(p[READ]);
130                 close(p[WRITE]);
131                 return NULL;
132         }
133         (void) close(hisside);
134         if ((fp = fdopen(myside, mode)) != NULL)
135                 register_file(fp, 1, pid);
136         return fp;
137 }
138
139 int
140 Pclose(ptr)
141         FILE *ptr;
142 {
143         int i;
144         int omask;
145
146         i = file_pid(ptr);
147         unregister_file(ptr);
148         (void) fclose(ptr);
149         omask = sigblock(sigmask(SIGINT)|sigmask(SIGHUP));
150         i = wait_child(i);
151         sigsetmask(omask);
152         return i;
153 }
154
155 void
156 close_all_files()
157 {
158
159         while (fp_head)
160                 if (fp_head->pipe)
161                         (void) Pclose(fp_head->fp);
162                 else
163                         (void) Fclose(fp_head->fp);
164 }
165
166 void
167 register_file(fp, pipe, pid)
168         FILE *fp;
169         int pipe, pid;
170 {
171         struct fp *fpp;
172
173         if ((fpp = (struct fp *) malloc(sizeof *fpp)) == NULL)
174                 err(1, "Out of memory");
175         fpp->fp = fp;
176         fpp->pipe = pipe;
177         fpp->pid = pid;
178         fpp->link = fp_head;
179         fp_head = fpp;
180 }
181
182 void
183 unregister_file(fp)
184         FILE *fp;
185 {
186         struct fp **pp, *p;
187
188         for (pp = &fp_head; p = *pp; pp = &p->link)
189                 if (p->fp == fp) {
190                         *pp = p->link;
191                         free((char *) p);
192                         return;
193                 }
194         errx(1, "Invalid file pointer");
195         /*NOTREACHED*/
196 }
197
198 int
199 file_pid(fp)
200         FILE *fp;
201 {
202         struct fp *p;
203
204         for (p = fp_head; p; p = p->link)
205                 if (p->fp == fp)
206                         return (p->pid);
207         errx(1, "Invalid file pointer");
208         /*NOTREACHED*/
209 }
210
211 /*
212  * Run a command without a shell, with optional arguments and splicing
213  * of stdin and stdout.  The command name can be a sequence of words.
214  * Signals must be handled by the caller.
215  * "Mask" contains the signals to ignore in the new process.
216  * SIGINT is enabled unless it's in the mask.
217  */
218 /*VARARGS4*/
219 int
220 run_command(cmd, mask, infd, outfd, a0, a1, a2)
221         char *cmd;
222         int mask, infd, outfd;
223         char *a0, *a1, *a2;
224 {
225         int pid;
226
227         if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
228                 return -1;
229         return wait_command(pid);
230 }
231
232 /*VARARGS4*/
233 int
234 start_command(cmd, mask, infd, outfd, a0, a1, a2)
235         char *cmd;
236         int mask, infd, outfd;
237         char *a0, *a1, *a2;
238 {
239         int pid;
240
241         if ((pid = fork()) < 0) {
242                 warn("fork");
243                 return -1;
244         }
245         if (pid == 0) {
246                 char *argv[100];
247                 int i = getrawlist(cmd, argv, sizeof argv / sizeof *argv);
248
249                 if ((argv[i++] = a0) != NOSTR &&
250                     (argv[i++] = a1) != NOSTR &&
251                     (argv[i++] = a2) != NOSTR)
252                         argv[i] = NOSTR;
253                 prepare_child(mask, infd, outfd);
254                 execvp(argv[0], argv);
255                 warn("%s", argv[0]);
256                 _exit(1);
257         }
258         return pid;
259 }
260
261 void
262 prepare_child(mask, infd, outfd)
263         int mask, infd, outfd;
264 {
265         int i;
266
267         /*
268          * All file descriptors other than 0, 1, and 2 are supposed to be
269          * close-on-exec.
270          */
271         if (infd >= 0)
272                 dup2(infd, 0);
273         if (outfd >= 0)
274                 dup2(outfd, 1);
275         for (i = 1; i <= NSIG; i++)
276                 if (mask & sigmask(i))
277                         (void) signal(i, SIG_IGN);
278         if ((mask & sigmask(SIGINT)) == 0)
279                 (void) signal(SIGINT, SIG_DFL);
280         (void) sigsetmask(0);
281 }
282
283 int
284 wait_command(pid)
285         int pid;
286 {
287
288         if (wait_child(pid) < 0) {
289                 printf("Fatal error in process.\n");
290                 return -1;
291         }
292         return 0;
293 }
294
295 static struct child *
296 findchild(pid)
297         int pid;
298 {
299         register struct child **cpp;
300
301         for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
302              cpp = &(*cpp)->link)
303                         ;
304         if (*cpp == NULL) {
305                 *cpp = (struct child *) malloc(sizeof (struct child));
306                 if (*cpp == NULL)
307                         err(1, "Out of memory");
308                 (*cpp)->pid = pid;
309                 (*cpp)->done = (*cpp)->free = 0;
310                 (*cpp)->link = NULL;
311         }
312         return *cpp;
313 }
314
315 static void
316 delchild(cp)
317         register struct child *cp;
318 {
319         register struct child **cpp;
320
321         for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
322                 ;
323         *cpp = cp->link;
324         free((char *) cp);
325 }
326
327 void
328 sigchild(signo)
329         int signo;
330 {
331         int pid;
332         int status;
333         register struct child *cp;
334
335         while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
336                 cp = findchild(pid);
337                 if (cp->free)
338                         delchild(cp);
339                 else {
340                         cp->done = 1;
341                         cp->status = status;
342                 }
343         }
344 }
345
346 int wait_status;
347
348 /*
349  * Wait for a specific child to die.
350  */
351 int
352 wait_child(pid)
353         int pid;
354 {
355         int mask = sigblock(sigmask(SIGCHLD));
356         register struct child *cp = findchild(pid);
357
358         while (!cp->done)
359                 sigpause(mask);
360         wait_status = cp->status;
361         delchild(cp);
362         sigsetmask(mask);
363         return((WIFEXITED(wait_status) && WEXITSTATUS(wait_status)) ? -1 : 0);
364 }
365
366 /*
367  * Mark a child as don't care.
368  */
369 void
370 free_child(pid)
371         int pid;
372 {
373         int mask = sigblock(sigmask(SIGCHLD));
374         register struct child *cp = findchild(pid);
375
376         if (cp->done)
377                 delchild(cp);
378         else
379                 cp->free = 1;
380         sigsetmask(mask);
381 }