1 /* popen.c: A "safe" pipe open routine.
2 
3 %%% portions-copyright-cmetz-96
4 Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5 Reserved. The Inner Net License Version 2 applies to these portions of
6 the software.
7 You should have received a copy of the license with this software. If
8 you didn't get a copy, you may request one from <license@inner.net>.
9 
10 Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11 McDonald, All Rights Reserved. All Rights under this copyright are assigned
12 to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13 License Agreement applies to this software.
14 
15 	History:
16 
17 	Modified by cmetz for OPIE 2.31. Merged in some 4.4BSD-Lite fixes.
18 	Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al.
19                 Removed useless string. ifdef around some headers.
20         Modified at NRL for OPIE 2.1. Optimized for only one pipe at a time.
21                 Added minimal version of sigprocmask(). Moved some pid_t
22 		dancing to the config headers.
23 	Modified at NRL for OPIE 2.0.
24 	Originally from BSD.
25 
26 $FreeBSD: stable/10/contrib/opie/popen.c 117501 2003-07-13 05:59:50Z kris $
27 */
28 /*
29  * Copyright (c) 1988, 1993, 1994
30  *     The Regents of the University of California.  All rights reserved.
31  *
32  * This code is derived from software written by Ken Arnold and
33  * published in UNIX Review, Vol. 6, No. 8.
34  *
35  * Redistribution and use in source and binary forms, with or without
36  * modification, are permitted provided that the following conditions
37  * are met:
38  * 1. Redistributions of source code must retain the above copyright
39  *    notice, this list of conditions and the following disclaimer.
40  * 2. Redistributions in binary form must reproduce the above copyright
41  *    notice, this list of conditions and the following disclaimer in the
42  *    documentation and/or other materials provided with the distribution.
43  * 3. All advertising materials mentioning features or use of this software
44  *    must display the following acknowledgement:
45  *      This product includes software developed by the University of
46  *      California, Berkeley and its contributors.
47  * 4. Neither the name of the University nor the names of its contributors
48  *    may be used to endorse or promote products derived from this software
49  *    without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61  * SUCH DAMAGE.
62  *
63  */
64 
65 #include "opie_cfg.h"
66 
67 #include <sys/types.h>
68 #include <sys/wait.h>
69 #if HAVE_SIGNAL_H
70 #include <signal.h>
71 #endif /* HAVE_SIGNAL_H */
72 #if HAVE_SYS_SIGNAL_H
73 #include <sys/signal.h>
74 #endif /* HAVE_SYS_SIGNAL_H */
75 #if HAVE_UNISTD_H
76 #include <unistd.h>
77 #endif /* HAVE_UNISTD_H */
78 #include <stdio.h>
79 #if HAVE_STDLIB_H
80 #include <stdlib.h>
81 #endif /* HAVE_STDLIB_H */
82 #if HAVE_STRING_H
83 #include <string.h>
84 #endif /* HAVE_STRING_H */
85 
86 #include "opie.h"
87 
88 #define MAXUSRARGS	100
89 #define MAXGLOBARGS	1000
90 
91 char **ftpglob __P((register char *));
92 char **copyblk __P((char **));
93 VOIDRET blkfree __P((char **));
94 
95 /*
96  * Special version of popen which avoids call to shell.  This ensures noone
97  * may create a pipe to a hidden program as a side effect of a list or dir
98  * command.
99  */
100 static pid_t child_pid = -1;
101 static int pipe_fd;
102 
103 extern char **environ;
104 
105 FILE *ftpd_popen FUNCTION((program, type), char *program AND char *type)
106 {
107   char *cp;
108   FILE *iop;
109   int argc, gargc, pdes[2];
110   char **pop, *argv[MAXUSRARGS], *gargv[MAXGLOBARGS], *vv[2];
111 
112   if ((*type != 'r' && *type != 'w') || type[1])
113     return (NULL);
114 
115   if (pipe(pdes) < 0)
116     return (NULL);
117 
118   /* break up string into pieces */
119   for (argc = 0, cp = program; argc < MAXUSRARGS-1; cp = NULL) {
120     if (!(argv[argc++] = strtok(cp, " \t\n")))
121       break;
122   }
123   argv[argc - 1] = NULL;
124 
125   /* glob each piece */
126   gargv[0] = argv[0];
127   for (gargc = argc = 1; argv[argc] && gargc < (MAXGLOBARGS-1); argc++) {
128     if (!(pop = (char **) ftpglob(argv[argc]))) {
129       /* globbing failed */
130       vv[0] = argv[argc];
131       vv[1] = NULL;
132       pop = (char **) copyblk(vv);
133     }
134     argv[argc] = (char *) pop;	/* save to free later */
135     while (*pop && gargc < MAXGLOBARGS-1)
136       gargv[gargc++] = *pop++;
137   }
138   gargv[gargc] = NULL;
139 
140   iop = NULL;
141   switch (child_pid = fork()) {
142   case -1:	/* error */
143     close(pdes[0]);
144     close(pdes[1]);
145     goto pfree;
146     /* NOTREACHED */
147   case 0:	/* child */
148     if (*type == 'r') {
149       if (pdes[1] != 1) {
150 	dup2(pdes[1], 1);
151 	dup2(pdes[1], 2);	/* stderr, too! */
152 	close(pdes[1]);
153       }
154       close(pdes[0]);
155     } else {
156       if (pdes[0] != 0) {
157 	dup2(pdes[0], 0);
158 	close(pdes[0]);
159       }
160       close(pdes[1]);
161     }
162     environ = NULL;
163     execv(gargv[0], gargv);
164     _exit(1);
165   }
166 
167   /* parent; assume fdopen can't fail...  */
168   if (*type == 'r') {
169     iop = fdopen(pipe_fd = pdes[0], type);
170     close(pdes[1]);
171   } else {
172     iop = fdopen(pipe_fd = pdes[1], type);
173     close(pdes[0]);
174   }
175 
176 pfree: for (argc = 1; argv[argc] != NULL; argc++) {
177     blkfree((char **) argv[argc]);
178     free((char *) argv[argc]);
179   }
180   return (iop);
181 }
182 
183 int ftpd_pclose FUNCTION((iop), FILE *iop)
184 {
185   int status;
186   pid_t pid;
187   sigset_t omask, mask;
188 
189   sigemptyset(&mask);
190   sigaddset(&mask, SIGINT);
191   sigaddset(&mask, SIGQUIT);
192   sigaddset(&mask, SIGHUP);
193 
194   /* pclose returns -1 if stream is not associated with a `popened' command,
195      or, if already `pclosed'. */
196   if ((child_pid < 0) || (fileno(iop) != pipe_fd))
197     return (-1);
198 
199   fclose(iop);
200   sigprocmask(SIG_BLOCK, &mask, &omask);
201 
202   while ((pid = wait(&status)) != child_pid && (pid != -1));
203   sigprocmask(SIG_SETMASK, &omask, NULL);
204 
205   child_pid = -1;
206   pipe_fd = -1;
207 
208 #if defined(WEXITSTATUS) && defined(WIFEXITED)
209   if ((pid > 0) && WIFEXITED(status))
210     return WEXITSTATUS(status);
211 
212   return -1;
213 #else /* defined(WEXITSTATUS) && defined(WIFEXITED) */
214   return (pid == -1 ? -1 : status.w_status);
215 #endif /* defined(WEXITSTATUS) && defined(WIFEXITED) */
216 }
217