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