1/* popen.c: A "safe" pipe open routine.
2
3%%% portions-copyright-cmetz-96
4Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5Reserved. The Inner Net License Version 2 applies to these portions of
6the software.
7You should have received a copy of the license with this software. If
8you didn't get a copy, you may request one from <license@inner.net>.
9
10Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11McDonald, All Rights Reserved. All Rights under this copyright are assigned
12to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13License 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$
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
91char **ftpglob __P((register char *));
92char **copyblk __P((char **));
93VOIDRET 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 */
100static pid_t child_pid = -1;
101static int pipe_fd;
102
103extern char **environ;
104
105FILE *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
176pfree: for (argc = 1; argv[argc] != NULL; argc++) {
177    blkfree((char **) argv[argc]);
178    free((char *) argv[argc]);
179  }
180  return (iop);
181}
182
183int 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