625589cfda1129c914d2c8d5d7d347d5e1952381
[dragonfly.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  * @(#)popen.c  8.1 (Berkeley) 6/6/93
34  * $FreeBSD: src/usr.bin/mail/popen.c,v 1.2.6.3 2003/01/06 05:46:03 mikeh Exp $
35  * $DragonFly: src/usr.bin/mail/popen.c,v 1.4 2004/09/08 03:01:11 joerg Exp $
36  */
37
38 #include "rcv.h"
39 #include <sys/wait.h>
40 #include <fcntl.h>
41 #include "extern.h"
42
43 #define READ 0
44 #define WRITE 1
45
46 struct fp {
47         FILE    *fp;
48         int     pipe;
49         int     pid;
50         struct  fp *link;
51 };
52 static struct fp *fp_head;
53
54 struct child {
55         int     pid;
56         char    done;
57         char    free;
58         int     status;
59         struct  child *link;
60 };
61 static struct child *child;
62 static struct child *findchild(int);
63 static void delchild(struct child *);
64 static int file_pid(FILE *);
65
66 FILE *
67 Fopen(const char *path, const char *mode)
68 {
69         FILE *fp;
70
71         if ((fp = fopen(path, mode)) != NULL) {
72                 register_file(fp, 0, 0);
73                 fcntl(fileno(fp), F_SETFD, 1);
74         }
75         return (fp);
76 }
77
78 FILE *
79 Fdopen(int fd, const char *mode)
80 {
81         FILE *fp;
82
83         if ((fp = fdopen(fd, mode)) != NULL) {
84                 register_file(fp, 0, 0);
85                 fcntl(fileno(fp), F_SETFD, 1);
86         }
87         return (fp);
88 }
89
90 int
91 Fclose(FILE *fp)
92 {
93         unregister_file(fp);
94         return (fclose(fp));
95 }
96
97 FILE *
98 Popen(char *cmd, const char *mode)
99 {
100         int p[2];
101         int myside, hisside, fd0, fd1;
102         int pid;
103         sigset_t nset;
104         FILE *fp;
105
106         if (pipe(p) < 0)
107                 return (NULL);
108         fcntl(p[READ], F_SETFD, 1);
109         fcntl(p[WRITE], F_SETFD, 1);
110         if (*mode == 'r') {
111                 myside = p[READ];
112                 fd0 = -1;
113                 hisside = fd1 = p[WRITE];
114         } else {
115                 myside = p[WRITE];
116                 hisside = fd0 = p[READ];
117                 fd1 = -1;
118         }
119         sigemptyset(&nset);
120         if ((pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL)) < 0) {
121                 close(p[READ]);
122                 close(p[WRITE]);
123                 return (NULL);
124         }
125         close(hisside);
126         if ((fp = fdopen(myside, mode)) != NULL)
127                 register_file(fp, 1, pid);
128         return (fp);
129 }
130
131 int
132 Pclose(FILE *ptr)
133 {
134         int i;
135         sigset_t nset, oset;
136
137         i = file_pid(ptr);
138         unregister_file(ptr);
139         fclose(ptr);
140         sigemptyset(&nset);
141         sigaddset(&nset, SIGINT);
142         sigaddset(&nset, SIGHUP);
143         sigprocmask(SIG_BLOCK, &nset, &oset);
144         i = wait_child(i);
145         sigprocmask(SIG_SETMASK, &oset, NULL);
146         return (i);
147 }
148
149 void
150 close_all_files(void)
151 {
152
153         while (fp_head != NULL)
154                 if (fp_head->pipe)
155                         (void)Pclose(fp_head->fp);
156                 else
157                         (void)Fclose(fp_head->fp);
158 }
159
160 void
161 register_file(FILE *fp, int pipe, int pid)
162 {
163         struct fp *fpp;
164
165         if ((fpp = malloc(sizeof(*fpp))) == NULL)
166                 err(1, "Out of memory");
167         fpp->fp = fp;
168         fpp->pipe = pipe;
169         fpp->pid = pid;
170         fpp->link = fp_head;
171         fp_head = fpp;
172 }
173
174 void
175 unregister_file(FILE *fp)
176 {
177         struct fp **pp, *p;
178
179         for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
180                 if (p->fp == fp) {
181                         *pp = p->link;
182                         (void)free(p);
183                         return;
184                 }
185         errx(1, "Invalid file pointer");
186         /*NOTREACHED*/
187 }
188
189 int
190 file_pid(FILE *fp)
191 {
192         struct fp *p;
193
194         for (p = fp_head; p != NULL; p = p->link)
195                 if (p->fp == fp)
196                         return (p->pid);
197         errx(1, "Invalid file pointer");
198         /*NOTREACHED*/
199 }
200
201 /*
202  * Run a command without a shell, with optional arguments and splicing
203  * of stdin and stdout.  The command name can be a sequence of words.
204  * Signals must be handled by the caller.
205  * "Mask" contains the signals to ignore in the new process.
206  * SIGINT is enabled unless it's in the mask.
207  */
208 /*VARARGS4*/
209 int
210 run_command(char *cmd, sigset_t *mask, int infd, int outfd, char *a0, char *a1,
211             char *a2)
212 {
213         int pid;
214
215         if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
216                 return (-1);
217         return (wait_command(pid));
218 }
219
220 /*VARARGS4*/
221 int
222 start_command(char *cmd, sigset_t *mask, int infd, int outfd, char *a0,
223               char *a1, char *a2)
224 {
225         int pid;
226
227         if ((pid = fork()) < 0) {
228                 warn("fork");
229                 return (-1);
230         }
231         if (pid == 0) {
232                 char *argv[100];
233                 int i = getrawlist(cmd, argv, sizeof(argv) / sizeof(*argv));
234
235                 if ((argv[i++] = a0) != NULL &&
236                     (argv[i++] = a1) != NULL &&
237                     (argv[i++] = a2) != NULL)
238                         argv[i] = NULL;
239                 prepare_child(mask, infd, outfd);
240                 execvp(argv[0], argv);
241                 warn("%s", argv[0]);
242                 _exit(1);
243         }
244         return (pid);
245 }
246
247 void
248 prepare_child(sigset_t *nset, int infd, int outfd)
249 {
250         int i;
251         sigset_t eset;
252
253         /*
254          * All file descriptors other than 0, 1, and 2 are supposed to be
255          * close-on-exec.
256          */
257         if (infd >= 0)
258                 dup2(infd, 0);
259         if (outfd >= 0)
260                 dup2(outfd, 1);
261         for (i = 1; i < NSIG; i++)
262                 if (nset != NULL && sigismember(nset, i))
263                         (void)signal(i, SIG_IGN);
264         if (nset == NULL || !sigismember(nset, SIGINT))
265                 (void)signal(SIGINT, SIG_DFL);
266         sigemptyset(&eset);
267         sigprocmask(SIG_SETMASK, &eset, NULL);
268 }
269
270 int
271 wait_command(int pid)
272 {
273         if (wait_child(pid) < 0) {
274                 printf("Fatal error in process.\n");
275                 return (-1);
276         }
277         return (0);
278 }
279
280 static struct child *
281 findchild(int pid)
282 {
283         struct child **cpp;
284
285         for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
286             cpp = &(*cpp)->link)
287                         ;
288         if (*cpp == NULL) {
289                 *cpp = malloc(sizeof(struct child));
290                 if (*cpp == NULL)
291                         err(1, "Out of memory");
292                 (*cpp)->pid = pid;
293                 (*cpp)->done = (*cpp)->free = 0;
294                 (*cpp)->link = NULL;
295         }
296         return (*cpp);
297 }
298
299 static void
300 delchild(struct child *cp)
301 {
302         struct child **cpp;
303
304         for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
305                 ;
306         *cpp = cp->link;
307         free(cp);
308 }
309
310 /*ARGSUSED*/
311 void
312 sigchild(int signo)
313 {
314         int pid;
315         int status;
316         struct child *cp;
317
318         while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
319                 cp = findchild(pid);
320                 if (cp->free)
321                         delchild(cp);
322                 else {
323                         cp->done = 1;
324                         cp->status = status;
325                 }
326         }
327 }
328
329 int wait_status;
330
331 /*
332  * Wait for a specific child to die.
333  */
334 int
335 wait_child(int pid)
336 {
337         sigset_t nset, oset;
338         struct child *cp = findchild(pid);
339
340         sigemptyset(&nset);
341         sigaddset(&nset, SIGCHLD);
342         sigprocmask(SIG_BLOCK, &nset, &oset);   
343
344         while (!cp->done)
345                 (void)sigsuspend(&oset);
346         wait_status = cp->status;
347         delchild(cp);
348         sigprocmask(SIG_SETMASK, &oset, NULL);
349         return ((WIFEXITED(wait_status) && WEXITSTATUS(wait_status)) ? -1 : 0);
350 }
351
352 /*
353  * Mark a child as don't care.
354  */
355 void
356 free_child(int pid)
357 {
358         sigset_t nset, oset;
359         struct child *cp = findchild(pid);
360
361         sigemptyset(&nset);
362         sigaddset(&nset, SIGCHLD);
363         sigprocmask(SIG_BLOCK, &nset, &oset);   
364
365         if (cp->done)
366                 delchild(cp);
367         else
368                 cp->free = 1;
369         sigprocmask(SIG_SETMASK, &oset, NULL);
370 }