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