xref: /original-bsd/lib/libc/gen/popen.c (revision 94e7bb75)
1 /*
2  * Copyright (c) 1988 The Regents of the University of California.
3  * 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	5.17 (Berkeley) 04/14/92";
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(program, type)
34 	const char *program;
35 	const char *type;
36 {
37 	struct pid *cur;
38 	FILE *iop;
39 	int pdes[2], pid;
40 
41 	if (*type != 'r' && *type != 'w' || type[1])
42 		return (NULL);
43 
44 	if ((cur = malloc(sizeof(struct pid))) == NULL)
45 		return (NULL);
46 
47 	if (pipe(pdes) < 0) {
48 		(void)free(cur);
49 		return (NULL);
50 	}
51 
52 	switch (pid = vfork()) {
53 	case -1:			/* Error. */
54 		(void)close(pdes[0]);
55 		(void)close(pdes[1]);
56 		(void)free(cur);
57 		return (NULL);
58 		/* NOTREACHED */
59 	case 0:				/* Child. */
60 		if (*type == 'r') {
61 			if (pdes[1] != STDOUT_FILENO) {
62 				(void)dup2(pdes[1], STDOUT_FILENO);
63 				(void)close(pdes[1]);
64 			}
65 			(void) close(pdes[0]);
66 		} else {
67 			if (pdes[0] != STDIN_FILENO) {
68 				(void)dup2(pdes[0], STDIN_FILENO);
69 				(void)close(pdes[0]);
70 			}
71 			(void)close(pdes[1]);
72 		}
73 		execl(_PATH_BSHELL, "sh", "-c", program, NULL);
74 		_exit(127);
75 		/* NOTREACHED */
76 	}
77 
78 	/* Parent; assume fdopen can't fail. */
79 	if (*type == 'r') {
80 		iop = fdopen(pdes[0], type);
81 		(void)close(pdes[1]);
82 	} else {
83 		iop = fdopen(pdes[1], type);
84 		(void)close(pdes[0]);
85 	}
86 
87 	/* Link into list of file descriptors. */
88 	cur->fp = iop;
89 	cur->pid =  pid;
90 	cur->next = pidlist;
91 	pidlist = cur;
92 
93 	return (iop);
94 }
95 
96 /*
97  * pclose --
98  *	Pclose returns -1 if stream is not associated with a `popened' command,
99  *	if already `pclosed', or waitpid returns an error.
100  */
101 int
102 pclose(iop)
103 	FILE *iop;
104 {
105 	register struct pid *cur, *last;
106 	int omask;
107 	union wait pstat;
108 	pid_t pid;
109 
110 	(void)fclose(iop);
111 
112 	/* Find the appropriate file pointer. */
113 	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
114 		if (cur->fp == iop)
115 			break;
116 	if (cur == NULL)
117 		return (-1);
118 
119 	/* Get the status of the process. */
120 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
121 	do {
122 		pid = waitpid(cur->pid, (int *) &pstat, 0);
123 	} while (pid == -1 && errno == EINTR);
124 	(void)sigsetmask(omask);
125 
126 	/* Remove the entry from the linked list. */
127 	if (last == NULL)
128 		pidlist = cur->next;
129 	else
130 		last->next = cur->next;
131 	free(cur);
132 
133 	return (pid == -1 ? -1 : pstat.w_status);
134 }
135