xref: /original-bsd/lib/libc/gen/popen.c (revision 00695d63)
1 /*
2  * Copyright (c) 1988, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software written by Ken Arnold and
6  * published in UNIX Review, Vol. 6, No. 8.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #if defined(LIBC_SCCS) && !defined(lint)
12 static char sccsid[] = "@(#)popen.c	8.2 (Berkeley) 04/27/95";
13 #endif /* LIBC_SCCS and not lint */
14 
15 #include <sys/param.h>
16 #include <sys/wait.h>
17 
18 #include <signal.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <paths.h>
25 
26 static struct pid {
27 	struct pid *next;
28 	FILE *fp;
29 	pid_t pid;
30 } *pidlist;
31 
32 FILE *
33 popen(command, type)
34 	const char *command, *type;
35 {
36 	struct pid *cur;
37 	FILE *iop;
38 	int pdes[2], pid;
39 
40 	if (*type != 'r' && *type != 'w' || type[1]) {
41 		errno = EINVAL;
42 		return (NULL);
43 	}
44 
45 	if ((cur = malloc(sizeof(struct pid))) == NULL)
46 		return (NULL);
47 
48 	if (pipe(pdes) < 0) {
49 		(void)free(cur);
50 		return (NULL);
51 	}
52 
53 	switch (pid = vfork()) {
54 	case -1:			/* Error. */
55 		(void)close(pdes[0]);
56 		(void)close(pdes[1]);
57 		(void)free(cur);
58 		return (NULL);
59 		/* NOTREACHED */
60 	case 0:				/* Child. */
61 		if (*type == 'r') {
62 			if (pdes[1] != STDOUT_FILENO) {
63 				(void)dup2(pdes[1], STDOUT_FILENO);
64 				(void)close(pdes[1]);
65 			}
66 			(void) close(pdes[0]);
67 		} else {
68 			if (pdes[0] != STDIN_FILENO) {
69 				(void)dup2(pdes[0], STDIN_FILENO);
70 				(void)close(pdes[0]);
71 			}
72 			(void)close(pdes[1]);
73 		}
74 		execl(_PATH_BSHELL, "sh", "-c", command, NULL);
75 		_exit(127);
76 		/* NOTREACHED */
77 	}
78 
79 	/* Parent; assume fdopen can't fail. */
80 	if (*type == 'r') {
81 		iop = fdopen(pdes[0], type);
82 		(void)close(pdes[1]);
83 	} else {
84 		iop = fdopen(pdes[1], type);
85 		(void)close(pdes[0]);
86 	}
87 
88 	/* Link into list of file descriptors. */
89 	cur->fp = iop;
90 	cur->pid =  pid;
91 	cur->next = pidlist;
92 	pidlist = cur;
93 
94 	return (iop);
95 }
96 
97 /*
98  * pclose --
99  *	Pclose returns -1 if stream is not associated with a `popened' command,
100  *	if already `pclosed', or waitpid returns an error.
101  */
102 int
103 pclose(iop)
104 	FILE *iop;
105 {
106 	register struct pid *cur, *last;
107 	int omask;
108 	int pstat;
109 	pid_t pid;
110 
111 	/* Find the appropriate file pointer. */
112 	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
113 		if (cur->fp == iop)
114 			break;
115 	if (cur == NULL)
116 		return (-1);
117 
118 	(void)fclose(iop);
119 
120 	do {
121 		pid = waitpid(cur->pid, &pstat, 0);
122 	} while (pid == -1 && errno == EINTR);
123 
124 	/* Remove the entry from the linked list. */
125 	if (last == NULL)
126 		pidlist = cur->next;
127 	else
128 		last->next = cur->next;
129 	free(cur);
130 
131 	return (pid == -1 ? -1 : pstat);
132 }
133