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