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