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