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