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