No empty .Rs/.Re
[netbsd-mini2440.git] / usr.bin / mail / popen.c
blobabae9c24fdeb3f62e29c9c1f5085065956d44879
1 /* $NetBSD: popen.c,v 1.24 2007/10/30 02:28:31 christos Exp $ */
3 /*
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)popen.c 8.1 (Berkeley) 6/6/93";
36 #else
37 __RCSID("$NetBSD: popen.c,v 1.24 2007/10/30 02:28:31 christos Exp $");
38 #endif
39 #endif /* not lint */
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <stdarg.h>
44 #include <util.h>
45 #include <sys/wait.h>
47 #include "rcv.h"
48 #include "extern.h"
49 #include "sig.h"
51 #define READ 0
52 #define WRITE 1
54 struct fp {
55 FILE *fp;
56 int pipe;
57 pid_t pid;
58 struct fp *link;
60 static struct fp *fp_head;
62 struct child {
63 pid_t pid;
64 char done;
65 char free;
66 int status;
67 struct child *link;
69 static struct child *child, *child_freelist = NULL;
72 #if 0 /* XXX - debugging stuff. This should go away eventually! */
73 static void
74 show_one_file(FILE *fo, struct fp *fpp)
76 (void)fprintf(fo, ">>> fp: %p, pipe: %d, pid: %d, link: %p\n",
77 fpp->fp, fpp->pipe, fpp->pid, fpp->link);
80 void show_all_files(FILE *fo);
81 __unused
82 PUBLIC void
83 show_all_files(FILE *fo)
85 struct fp *fpp;
87 (void)fprintf(fo, ">> FILES\n");
88 for (fpp = fp_head; fpp; fpp = fpp->link)
89 show_one_file(fo, fpp);
90 (void)fprintf(fo, ">> -------\n");
91 (void)fflush(fo);
93 #endif /* end debugging stuff */
96 static void
97 unregister_file(FILE *fp)
99 struct fp **pp, *p;
101 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
102 if (p->fp == fp) {
103 *pp = p->link;
104 (void)free(p);
105 return;
107 errx(1, "Invalid file pointer");
110 PUBLIC void
111 register_file(FILE *fp, int pipefd, pid_t pid)
113 struct fp *fpp;
115 fpp = emalloc(sizeof(*fpp));
116 fpp->fp = fp;
117 fpp->pipe = pipefd;
118 fpp->pid = pid;
119 fpp->link = fp_head;
120 fp_head = fpp;
123 PUBLIC FILE *
124 Fopen(const char *fn, const char *mode)
126 FILE *fp;
128 if ((fp = fopen(fn, mode)) != NULL) {
129 register_file(fp, 0, 0);
130 (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
132 return fp;
135 PUBLIC FILE *
136 Fdopen(int fd, const char *mode)
138 FILE *fp;
140 if ((fp = fdopen(fd, mode)) != NULL) {
141 register_file(fp, 0, 0);
142 (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
144 return fp;
147 PUBLIC int
148 Fclose(FILE *fp)
151 if (fp == NULL)
152 return 0;
154 unregister_file(fp);
155 return fclose(fp);
158 PUBLIC void
159 prepare_child(sigset_t *nset, int infd, int outfd)
161 int i;
162 sigset_t eset;
165 * All file descriptors other than 0, 1, and 2 are supposed to be
166 * close-on-exec.
168 if (infd > 0) {
169 (void)dup2(infd, 0);
170 } else if (infd != 0) {
171 /* we don't want the child stealing my stdin input */
172 (void)close(0);
173 (void)open(_PATH_DEVNULL, O_RDONLY, 0);
175 if (outfd >= 0 && outfd != 1)
176 (void)dup2(outfd, 1);
178 if (nset != NULL) {
179 for (i = 1; i < NSIG; i++) {
180 if (sigismember(nset, i))
181 (void)signal(i, SIG_IGN);
183 if (!sigismember(nset, SIGINT))
184 (void)signal(SIGINT, SIG_DFL);
185 (void)sigemptyset(&eset);
186 (void)sigprocmask(SIG_SETMASK, &eset, NULL);
191 * Run a command without a shell, with optional arguments and splicing
192 * of stdin (-1 means none) and stdout. The command name can be a sequence
193 * of words.
194 * Signals must be handled by the caller.
195 * "nset" contains the signals to ignore in the new process.
196 * SIGINT is enabled unless it's in "nset".
198 static pid_t
199 start_commandv(const char *cmd, sigset_t *nset, int infd, int outfd,
200 va_list args)
202 pid_t pid;
204 sig_check();
205 if ((pid = fork()) < 0) {
206 warn("fork");
207 return -1;
209 if (pid == 0) {
210 char *argv[100];
211 size_t i;
213 i = getrawlist(cmd, argv, (int)__arraycount(argv));
214 while (i < __arraycount(argv) - 1 &&
215 (argv[i++] = va_arg(args, char *)) != NULL)
216 continue;
217 argv[i] = NULL;
218 prepare_child(nset, infd, outfd);
219 (void)execvp(argv[0], argv);
220 warn("%s", argv[0]);
221 _exit(1);
223 return pid;
226 PUBLIC int
227 start_command(const char *cmd, sigset_t *nset, int infd, int outfd, ...)
229 va_list args;
230 int r;
232 va_start(args, outfd);
233 r = start_commandv(cmd, nset, infd, outfd, args);
234 va_end(args);
235 return r;
238 PUBLIC FILE *
239 Popen(const char *cmd, const char *mode)
241 int p[2];
242 int myside, hisside, fd0, fd1;
243 pid_t pid;
244 sigset_t nset;
245 FILE *fp;
246 char *shellcmd;
248 if (pipe(p) < 0)
249 return NULL;
250 (void)fcntl(p[READ], F_SETFD, FD_CLOEXEC);
251 (void)fcntl(p[WRITE], F_SETFD, FD_CLOEXEC);
252 if (*mode == 'r') {
253 myside = p[READ];
254 hisside = fd0 = fd1 = p[WRITE];
255 } else {
256 myside = p[WRITE];
257 hisside = fd0 = p[READ];
258 fd1 = -1;
260 (void)sigemptyset(&nset);
261 if ((shellcmd = value(ENAME_SHELL)) == NULL)
262 shellcmd = __UNCONST(_PATH_CSHELL);
263 pid = start_command(shellcmd, &nset, fd0, fd1, "-c", cmd, NULL);
264 if (pid < 0) {
265 (void)close(p[READ]);
266 (void)close(p[WRITE]);
267 return NULL;
269 (void)close(hisside);
270 if ((fp = fdopen(myside, mode)) != NULL)
271 register_file(fp, 1, pid);
272 return fp;
275 static struct child *
276 findchild(pid_t pid, int dont_alloc)
278 struct child **cpp;
280 for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
281 cpp = &(*cpp)->link)
282 continue;
283 if (*cpp == NULL) {
284 if (dont_alloc)
285 return NULL;
286 if (child_freelist) {
287 *cpp = child_freelist;
288 child_freelist = (*cpp)->link;
289 } else
290 *cpp = emalloc(sizeof(**cpp));
292 (*cpp)->pid = pid;
293 (*cpp)->done = (*cpp)->free = 0;
294 (*cpp)->link = NULL;
296 return *cpp;
299 static void
300 delchild(struct child *cp)
302 struct child **cpp;
304 for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
305 continue;
306 *cpp = cp->link;
307 cp->link = child_freelist;
308 child_freelist = cp;
312 * Wait for a specific child to die.
314 PUBLIC int
315 wait_child(pid_t pid)
317 struct child *cp;
318 sigset_t nset, oset;
319 pid_t rv = 0;
321 (void)sigemptyset(&nset);
322 (void)sigaddset(&nset, SIGCHLD);
323 (void)sigprocmask(SIG_BLOCK, &nset, &oset);
325 * If we have not already waited on the pid (via sigchild)
326 * wait on it now. Otherwise, use the wait status stashed
327 * by sigchild.
329 cp = findchild(pid, 1);
330 if (cp == NULL || !cp->done)
331 rv = waitpid(pid, &wait_status, 0);
332 else
333 wait_status = cp->status;
334 if (cp != NULL)
335 delchild(cp);
336 (void)sigprocmask(SIG_SETMASK, &oset, NULL);
337 if (rv == -1 || (WIFEXITED(wait_status) && WEXITSTATUS(wait_status)))
338 return -1;
339 else
340 return 0;
343 static pid_t
344 file_pid(FILE *fp)
346 struct fp *p;
348 for (p = fp_head; p; p = p->link)
349 if (p->fp == fp)
350 return p->pid;
351 errx(1, "Invalid file pointer");
352 /*NOTREACHED*/
355 PUBLIC int
356 Pclose(FILE *ptr)
358 int i;
359 sigset_t nset, oset;
361 if (ptr == NULL)
362 return 0;
364 i = file_pid(ptr);
365 unregister_file(ptr);
366 (void)fclose(ptr);
367 (void)sigemptyset(&nset);
368 (void)sigaddset(&nset, SIGINT);
369 (void)sigaddset(&nset, SIGHUP);
370 (void)sigprocmask(SIG_BLOCK, &nset, &oset);
371 i = wait_child(i);
372 (void)sigprocmask(SIG_SETMASK, &oset, NULL);
373 return i;
376 PUBLIC void
377 close_all_files(void)
379 while (fp_head)
380 if (fp_head->pipe)
381 (void)Pclose(fp_head->fp);
382 else
383 (void)Fclose(fp_head->fp);
386 PUBLIC FILE *
387 last_registered_file(int last_pipe)
389 struct fp *fpp;
391 if (last_pipe == 0)
392 return fp_head ? fp_head->fp : NULL;
394 for (fpp = fp_head; fpp; fpp = fpp->link)
395 if (fpp->pipe)
396 return fpp->fp;
397 return NULL;
400 PUBLIC void
401 close_top_files(FILE *fp_stop)
403 while (fp_head && fp_head->fp != fp_stop)
404 if (fp_head->pipe)
405 (void)Pclose(fp_head->fp);
406 else
407 (void)Fclose(fp_head->fp);
410 #ifdef MIME_SUPPORT
411 PUBLIC void
412 flush_files(FILE *fo, int only_pipes)
414 struct fp *fpp;
416 if (fo)
417 (void)fflush(fo);
419 for (fpp = fp_head; fpp; fpp = fpp->link)
420 if (!only_pipes || fpp->pipe)
421 (void)fflush(fpp->fp);
423 (void)fflush(stdout);
425 #endif /* MIME_SUPPORT */
427 static int
428 wait_command(pid_t pid)
431 if (wait_child(pid) < 0) {
432 (void)puts("Fatal error in process.");
433 return -1;
435 return 0;
438 PUBLIC int
439 run_command(const char *cmd, sigset_t *nset, int infd, int outfd, ...)
441 pid_t pid;
442 va_list args;
443 int rval;
445 #ifdef BROKEN_EXEC_TTY_RESTORE
446 struct termios ttybuf;
447 int tcrval;
449 * XXX - grab the tty settings as currently they can get
450 * trashed by emacs-21 when suspending with bash-3.2.25 as the
451 * shell.
453 * 1) from the mail editor, start "emacs -nw" (21.4)
454 * 2) suspend emacs to the shell (bash 3.2.25)
455 * 3) resume emacs
456 * 4) exit emacs back to the mail editor
457 * 5) discover the tty is screwed: the mail editor is no
458 * longer receiving characters
460 * - This occurs on both i386 and amd64.
461 * - This did _NOT_ occur before 4.99.10.
462 * - This does _NOT_ occur if the editor is vi(1) or if the shell
463 * is /bin/sh.
464 * - This _DOES_ happen with the old mail(1) from 2006-01-01 (long
465 * before my changes).
467 * This is the commit that introduced this "feature":
468 * http://mail-index.netbsd.org/source-changes/2007/02/09/0020.html
470 if ((tcrval = tcgetattr(fileno(stdin), &ttybuf)) == -1)
471 warn("tcgetattr");
472 #endif
473 va_start(args, outfd);
474 pid = start_commandv(cmd, nset, infd, outfd, args);
475 va_end(args);
476 if (pid < 0)
477 return -1;
478 rval = wait_command(pid);
479 #ifdef BROKEN_EXEC_TTY_RESTORE
480 if (tcrval != -1 && tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf) == -1)
481 warn("tcsetattr");
482 #endif
483 return rval;
487 /*ARGSUSED*/
488 PUBLIC void
489 sigchild(int signo __unused)
491 pid_t pid;
492 int status;
493 struct child *cp;
494 int save_errno;
496 save_errno = errno;
497 while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
498 cp = findchild(pid, 1); /* async-signal-safe: we don't alloc */
499 if (!cp)
500 continue;
501 if (cp->free)
502 delchild(cp); /* async-signal-safe: list changes */
503 else {
504 cp->done = 1;
505 cp->status = status;
508 errno = save_errno;
512 * Mark a child as don't care.
514 PUBLIC void
515 free_child(pid_t pid)
517 struct child *cp;
518 sigset_t nset, oset;
520 (void)sigemptyset(&nset);
521 (void)sigaddset(&nset, SIGCHLD);
522 (void)sigprocmask(SIG_BLOCK, &nset, &oset);
523 if ((cp = findchild(pid, 0)) != NULL) {
524 if (cp->done)
525 delchild(cp);
526 else
527 cp->free = 1;
529 (void)sigprocmask(SIG_SETMASK, &oset, NULL);