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