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