popen.c revision 1.26 1 /* $NetBSD: popen.c,v 1.26 2010/01/12 14:43:31 christos Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
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.
18 *
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.
30 */
31
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.26 2010/01/12 14:43:31 christos Exp $");
38 #endif
39 #endif /* not lint */
40
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <stdarg.h>
44 #include <util.h>
45 #include <sys/wait.h>
46
47 #include "rcv.h"
48 #include "extern.h"
49 #include "sig.h"
50
51 #define READ 0
52 #define WRITE 1
53
54 struct fp {
55 FILE *fp;
56 int pipe;
57 pid_t pid;
58 struct fp *link;
59 };
60 static struct fp *fp_head;
61
62 struct child {
63 pid_t pid;
64 char done;
65 char free;
66 int status;
67 struct child *link;
68 };
69 static struct child *child, *child_freelist = NULL;
70 static struct child *findchild(pid_t, int);
71
72
73 #if 0 /* XXX - debugging stuff. This should go away eventually! */
74 static void
75 show_one_file(FILE *fo, struct fp *fpp)
76 {
77 (void)fprintf(fo, ">>> fp: %p, pipe: %d, pid: %d, link: %p\n",
78 fpp->fp, fpp->pipe, fpp->pid, fpp->link);
79 }
80
81 void show_all_files(FILE *fo);
82 __unused
83 PUBLIC void
84 show_all_files(FILE *fo)
85 {
86 struct fp *fpp;
87
88 (void)fprintf(fo, ">> FILES\n");
89 for (fpp = fp_head; fpp; fpp = fpp->link)
90 show_one_file(fo, fpp);
91 (void)fprintf(fo, ">> -------\n");
92 (void)fflush(fo);
93 }
94 #endif /* end debugging stuff */
95
96
97 static void
98 unregister_file(FILE *fp)
99 {
100 struct fp **pp, *p;
101
102 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
103 if (p->fp == fp) {
104 *pp = p->link;
105 (void)free(p);
106 return;
107 }
108 errx(EXIT_FAILURE, "Invalid file pointer");
109 }
110
111 PUBLIC void
112 register_file(FILE *fp, int pipefd, pid_t pid)
113 {
114 struct fp *fpp;
115
116 fpp = emalloc(sizeof(*fpp));
117 fpp->fp = fp;
118 fpp->pipe = pipefd;
119 fpp->pid = pid;
120 fpp->link = fp_head;
121 fp_head = fpp;
122 }
123
124 PUBLIC FILE *
125 Fopen(const char *fn, const char *mode)
126 {
127 FILE *fp;
128
129 if ((fp = fopen(fn, mode)) != NULL) {
130 register_file(fp, 0, 0);
131 (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
132 }
133 return fp;
134 }
135
136 PUBLIC FILE *
137 Fdopen(int fd, const char *mode)
138 {
139 FILE *fp;
140
141 if ((fp = fdopen(fd, mode)) != NULL) {
142 register_file(fp, 0, 0);
143 (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
144 }
145 return fp;
146 }
147
148 PUBLIC int
149 Fclose(FILE *fp)
150 {
151
152 if (fp == NULL)
153 return 0;
154
155 unregister_file(fp);
156 return fclose(fp);
157 }
158
159 PUBLIC void
160 prepare_child(sigset_t *nset, int infd, int outfd)
161 {
162 int i;
163 sigset_t eset;
164
165 /*
166 * All file descriptors other than 0, 1, and 2 are supposed to be
167 * close-on-exec.
168 */
169 if (infd > 0) {
170 (void)dup2(infd, 0);
171 } else if (infd != 0) {
172 /* we don't want the child stealing my stdin input */
173 (void)close(0);
174 (void)open(_PATH_DEVNULL, O_RDONLY, 0);
175 }
176 if (outfd >= 0 && outfd != 1)
177 (void)dup2(outfd, 1);
178
179 if (nset != NULL) {
180 for (i = 1; i < NSIG; i++) {
181 if (sigismember(nset, i))
182 (void)signal(i, SIG_IGN);
183 }
184 if (!sigismember(nset, SIGINT))
185 (void)signal(SIGINT, SIG_DFL);
186 (void)sigemptyset(&eset);
187 (void)sigprocmask(SIG_SETMASK, &eset, NULL);
188 }
189 }
190
191 /*
192 * Run a command without a shell, with optional arguments and splicing
193 * of stdin (-1 means none) and stdout. The command name can be a sequence
194 * of words.
195 * Signals must be handled by the caller.
196 * "nset" contains the signals to ignore in the new process.
197 * SIGINT is enabled unless it's in "nset".
198 */
199 static pid_t
200 start_commandv(const char *cmd, sigset_t *nset, int infd, int outfd,
201 va_list args)
202 {
203 pid_t pid;
204
205 sig_check();
206 if ((pid = fork()) < 0) {
207 warn("fork");
208 return -1;
209 }
210 if (pid == 0) {
211 char *argv[100];
212 size_t i;
213
214 i = getrawlist(cmd, argv, (int)__arraycount(argv));
215 while (i < __arraycount(argv) - 1 &&
216 (argv[i++] = va_arg(args, char *)) != NULL)
217 continue;
218 argv[i] = NULL;
219 prepare_child(nset, infd, outfd);
220 (void)execvp(argv[0], argv);
221 warn("%s", argv[0]);
222 _exit(1);
223 }
224 (void)findchild(pid, 0);
225 return pid;
226 }
227
228 PUBLIC int
229 start_command(const char *cmd, sigset_t *nset, int infd, int outfd, ...)
230 {
231 va_list args;
232 int r;
233
234 va_start(args, outfd);
235 r = start_commandv(cmd, nset, infd, outfd, args);
236 va_end(args);
237 return r;
238 }
239
240 PUBLIC FILE *
241 Popen(const char *cmd, const char *mode)
242 {
243 int p[2];
244 int myside, hisside, fd0, fd1;
245 pid_t pid;
246 sigset_t nset;
247 FILE *fp;
248 char *shellcmd;
249
250 if (pipe(p) < 0)
251 return NULL;
252 (void)fcntl(p[READ], F_SETFD, FD_CLOEXEC);
253 (void)fcntl(p[WRITE], F_SETFD, FD_CLOEXEC);
254 if (*mode == 'r') {
255 myside = p[READ];
256 hisside = fd0 = fd1 = p[WRITE];
257 } else {
258 myside = p[WRITE];
259 hisside = fd0 = p[READ];
260 fd1 = -1;
261 }
262 (void)sigemptyset(&nset);
263 if ((shellcmd = value(ENAME_SHELL)) == NULL)
264 shellcmd = __UNCONST(_PATH_CSHELL);
265 pid = start_command(shellcmd, &nset, fd0, fd1, "-c", cmd, NULL);
266 if (pid < 0) {
267 (void)close(p[READ]);
268 (void)close(p[WRITE]);
269 return NULL;
270 }
271 (void)close(hisside);
272 if ((fp = fdopen(myside, mode)) != NULL)
273 register_file(fp, 1, pid);
274 return fp;
275 }
276
277 static struct child *
278 findchild(pid_t pid, int dont_alloc)
279 {
280 struct child **cpp;
281
282 for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
283 cpp = &(*cpp)->link)
284 continue;
285 if (*cpp == NULL) {
286 if (dont_alloc)
287 return NULL;
288 if (child_freelist) {
289 *cpp = child_freelist;
290 child_freelist = (*cpp)->link;
291 } else
292 *cpp = emalloc(sizeof(**cpp));
293
294 (*cpp)->pid = pid;
295 (*cpp)->done = (*cpp)->free = 0;
296 (*cpp)->link = NULL;
297 }
298 return *cpp;
299 }
300
301 static void
302 delchild(struct child *cp)
303 {
304 struct child **cpp;
305
306 for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
307 continue;
308 *cpp = cp->link;
309 cp->link = child_freelist;
310 child_freelist = cp;
311 }
312
313 /*
314 * Wait for a specific child to die.
315 */
316 PUBLIC int
317 wait_child(pid_t pid)
318 {
319 struct child *cp;
320 sigset_t nset, oset;
321 pid_t rv = 0;
322
323 (void)sigemptyset(&nset);
324 (void)sigaddset(&nset, SIGCHLD);
325 (void)sigprocmask(SIG_BLOCK, &nset, &oset);
326 /*
327 * If we have not already waited on the pid (via sigchild)
328 * wait on it now. Otherwise, use the wait status stashed
329 * by sigchild.
330 */
331 cp = findchild(pid, 1);
332 if (cp == NULL || !cp->done)
333 rv = waitpid(pid, &wait_status, 0);
334 else
335 wait_status = cp->status;
336 if (cp != NULL)
337 delchild(cp);
338 (void)sigprocmask(SIG_SETMASK, &oset, NULL);
339 if (rv == -1 || (WIFEXITED(wait_status) && WEXITSTATUS(wait_status)))
340 return -1;
341 else
342 return 0;
343 }
344
345 static pid_t
346 file_pid(FILE *fp)
347 {
348 struct fp *p;
349
350 for (p = fp_head; p; p = p->link)
351 if (p->fp == fp)
352 return p->pid;
353 errx(EXIT_FAILURE, "Invalid file pointer");
354 /*NOTREACHED*/
355 }
356
357 PUBLIC int
358 Pclose(FILE *ptr)
359 {
360 int i;
361 sigset_t nset, oset;
362
363 if (ptr == NULL)
364 return 0;
365
366 i = file_pid(ptr);
367 unregister_file(ptr);
368 (void)fclose(ptr);
369 (void)sigemptyset(&nset);
370 (void)sigaddset(&nset, SIGINT);
371 (void)sigaddset(&nset, SIGHUP);
372 (void)sigprocmask(SIG_BLOCK, &nset, &oset);
373 i = wait_child(i);
374 (void)sigprocmask(SIG_SETMASK, &oset, NULL);
375 return i;
376 }
377
378 PUBLIC void
379 close_all_files(void)
380 {
381 while (fp_head)
382 if (fp_head->pipe)
383 (void)Pclose(fp_head->fp);
384 else
385 (void)Fclose(fp_head->fp);
386 }
387
388 PUBLIC FILE *
389 last_registered_file(int last_pipe)
390 {
391 struct fp *fpp;
392
393 if (last_pipe == 0)
394 return fp_head ? fp_head->fp : NULL;
395
396 for (fpp = fp_head; fpp; fpp = fpp->link)
397 if (fpp->pipe)
398 return fpp->fp;
399 return NULL;
400 }
401
402 PUBLIC void
403 close_top_files(FILE *fp_stop)
404 {
405 while (fp_head && fp_head->fp != fp_stop)
406 if (fp_head->pipe)
407 (void)Pclose(fp_head->fp);
408 else
409 (void)Fclose(fp_head->fp);
410 }
411
412 #ifdef MIME_SUPPORT
413 PUBLIC void
414 flush_files(FILE *fo, int only_pipes)
415 {
416 struct fp *fpp;
417
418 if (fo)
419 (void)fflush(fo);
420
421 for (fpp = fp_head; fpp; fpp = fpp->link)
422 if (!only_pipes || fpp->pipe)
423 (void)fflush(fpp->fp);
424
425 (void)fflush(stdout);
426 }
427 #endif /* MIME_SUPPORT */
428
429 static int
430 wait_command(pid_t pid)
431 {
432
433 if (wait_child(pid) < 0) {
434 (void)puts("Fatal error in process.");
435 return -1;
436 }
437 return 0;
438 }
439
440 PUBLIC int
441 run_command(const char *cmd, sigset_t *nset, int infd, int outfd, ...)
442 {
443 pid_t pid;
444 va_list args;
445 int rval;
446
447 #ifdef BROKEN_EXEC_TTY_RESTORE
448 struct termios ttybuf;
449 int tcrval;
450 /*
451 * XXX - grab the tty settings as currently they can get
452 * trashed by emacs-21 when suspending with bash-3.2.25 as the
453 * shell.
454 *
455 * 1) from the mail editor, start "emacs -nw" (21.4)
456 * 2) suspend emacs to the shell (bash 3.2.25)
457 * 3) resume emacs
458 * 4) exit emacs back to the mail editor
459 * 5) discover the tty is screwed: the mail editor is no
460 * longer receiving characters
461 *
462 * - This occurs on both i386 and amd64.
463 * - This did _NOT_ occur before 4.99.10.
464 * - This does _NOT_ occur if the editor is vi(1) or if the shell
465 * is /bin/sh.
466 * - This _DOES_ happen with the old mail(1) from 2006-01-01 (long
467 * before my changes).
468 *
469 * This is the commit that introduced this "feature":
470 * http://mail-index.netbsd.org/source-changes/2007/02/09/0020.html
471 */
472 if ((tcrval = tcgetattr(fileno(stdin), &ttybuf)) == -1)
473 warn("tcgetattr");
474 #endif
475 va_start(args, outfd);
476 pid = start_commandv(cmd, nset, infd, outfd, args);
477 va_end(args);
478 if (pid < 0)
479 return -1;
480 rval = wait_command(pid);
481 #ifdef BROKEN_EXEC_TTY_RESTORE
482 if (tcrval != -1 && tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf) == -1)
483 warn("tcsetattr");
484 #endif
485 return rval;
486
487 }
488
489 /*ARGSUSED*/
490 PUBLIC void
491 sigchild(int signo __unused)
492 {
493 pid_t pid;
494 int status;
495 struct child *cp;
496 int save_errno;
497
498 save_errno = errno;
499 while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
500 cp = findchild(pid, 1); /* async-signal-safe: we don't alloc */
501 if (!cp)
502 continue;
503 if (cp->free)
504 delchild(cp); /* async-signal-safe: list changes */
505 else {
506 cp->done = 1;
507 cp->status = status;
508 }
509 }
510 errno = save_errno;
511 }
512
513 /*
514 * Mark a child as don't care.
515 */
516 PUBLIC void
517 free_child(pid_t pid)
518 {
519 struct child *cp;
520 sigset_t nset, oset;
521
522 (void)sigemptyset(&nset);
523 (void)sigaddset(&nset, SIGCHLD);
524 (void)sigprocmask(SIG_BLOCK, &nset, &oset);
525 if ((cp = findchild(pid, 0)) != NULL) {
526 if (cp->done)
527 delchild(cp);
528 else
529 cp->free = 1;
530 }
531 (void)sigprocmask(SIG_SETMASK, &oset, NULL);
532 }
533