xref: /original-bsd/libexec/ftpd/popen.c (revision c3e32dec)
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 
12 #ifndef lint
13 static char sccsid[] = "@(#)popen.c	8.1 (Berkeley) 06/04/93";
14 #endif /* not lint */
15 
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 
19 #include <signal.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "extern.h"
25 
26 /*
27  * Special version of popen which avoids call to shell.  This insures noone
28  * may create a pipe to a hidden program as a side effect of a list or dir
29  * command.
30  */
31 static int *pids;
32 static int fds;
33 
34 FILE *
35 ftpd_popen(program, type)
36 	char *program, *type;
37 {
38 	register char *cp;
39 	FILE *iop;
40 	int argc, gargc, pdes[2], pid;
41 	char **pop, *argv[100], *gargv[1000], *vv[2];
42 
43 	if (*type != 'r' && *type != 'w' || type[1])
44 		return(NULL);
45 
46 	if (!pids) {
47 		if ((fds = getdtablesize()) <= 0)
48 			return(NULL);
49 		if ((pids = (int *)malloc((u_int)(fds * sizeof(int)))) == NULL)
50 			return(NULL);
51 		memset(pids, 0, fds * sizeof(int));
52 	}
53 	if (pipe(pdes) < 0)
54 		return(NULL);
55 
56 	/* break up string into pieces */
57 	for (argc = 0, cp = program;; cp = NULL)
58 		if (!(argv[argc++] = strtok(cp, " \t\n")))
59 			break;
60 
61 	/* glob each piece */
62 	gargv[0] = argv[0];
63 	for (gargc = argc = 1; argv[argc]; argc++) {
64 		if (!(pop = ftpglob(argv[argc]))) {	/* globbing failed */
65 			vv[0] = argv[argc];
66 			vv[1] = NULL;
67 			pop = copyblk(vv);
68 		}
69 		argv[argc] = (char *)pop;		/* save to free later */
70 		while (*pop && gargc < 1000)
71 			gargv[gargc++] = *pop++;
72 	}
73 	gargv[gargc] = NULL;
74 
75 	iop = NULL;
76 	switch(pid = vfork()) {
77 	case -1:			/* error */
78 		(void)close(pdes[0]);
79 		(void)close(pdes[1]);
80 		goto pfree;
81 		/* NOTREACHED */
82 	case 0:				/* child */
83 		if (*type == 'r') {
84 			if (pdes[1] != 1) {
85 				dup2(pdes[1], 1);
86 				dup2(pdes[1], 2);	/* stderr, too! */
87 				(void)close(pdes[1]);
88 			}
89 			(void)close(pdes[0]);
90 		} else {
91 			if (pdes[0] != 0) {
92 				dup2(pdes[0], 0);
93 				(void)close(pdes[0]);
94 			}
95 			(void)close(pdes[1]);
96 		}
97 		execv(gargv[0], gargv);
98 		_exit(1);
99 	}
100 	/* parent; assume fdopen can't fail...  */
101 	if (*type == 'r') {
102 		iop = fdopen(pdes[0], type);
103 		(void)close(pdes[1]);
104 	} else {
105 		iop = fdopen(pdes[1], type);
106 		(void)close(pdes[0]);
107 	}
108 	pids[fileno(iop)] = pid;
109 
110 pfree:	for (argc = 1; argv[argc] != NULL; argc++) {
111 		blkfree((char **)argv[argc]);
112 		free((char *)argv[argc]);
113 	}
114 	return(iop);
115 }
116 
117 int
118 ftpd_pclose(iop)
119 	FILE *iop;
120 {
121 	register int fdes;
122 	int omask;
123 	union wait stat_loc;
124 	int pid;
125 
126 	/*
127 	 * pclose returns -1 if stream is not associated with a
128 	 * `popened' command, or, if already `pclosed'.
129 	 */
130 	if (pids == 0 || pids[fdes = fileno(iop)] == 0)
131 		return(-1);
132 	(void)fclose(iop);
133 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
134 	while ((pid = wait((int *)&stat_loc)) != pids[fdes] && pid != -1);
135 	(void)sigsetmask(omask);
136 	pids[fdes] = 0;
137 	return(pid == -1 ? -1 : stat_loc.w_status);
138 }
139