xref: /dragonfly/usr.bin/mail/popen.c (revision dc71b7ab81c4f5270d3668e1625d94a58895fa7a)
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. 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 *
Fopen(const char * path,const char * mode)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 *
Fdopen(int fd,const char * mode)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
Fclose(FILE * fp)87 Fclose(FILE *fp)
88 {
89           unregister_file(fp);
90           return (fclose(fp));
91 }
92 
93 FILE *
Popen(char * cmd,const char * mode)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
Pclose(FILE * ptr)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
close_all_files(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
register_file(FILE * fp,int pipe,int pid)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
unregister_file(FILE * fp)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
file_pid(FILE * fp)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
run_command(char * cmd,sigset_t * mask,int infd,int outfd,char * a0,char * a1,char * a2)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
start_command(char * cmd,sigset_t * mask,int infd,int outfd,char * a0,char * a1,char * a2)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
prepare_child(sigset_t * nset,int infd,int outfd)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
wait_command(int pid)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 *
findchild(int pid)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
delchild(struct child * cp)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
sigchild(int signo)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
wait_child(int pid)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
free_child(int pid)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 }
367