1235267Sgabor/*
2330449Seadler * Copyright (c) 1988, 1993, 1994
3330449Seadler *	The Regents of the University of California.  All rights reserved.
4235267Sgabor *
5251245Sgabor * This code is derived from software written by Ken Arnold and
6235267Sgabor * published in UNIX Review, Vol. 6, No. 8.
7235267Sgabor *
8235267Sgabor * Redistribution and use in source and binary forms, with or without
9235267Sgabor * modification, are permitted provided that the following conditions
10235267Sgabor * are met:
11235267Sgabor * 1. Redistributions of source code must retain the above copyright
12235267Sgabor *    notice, this list of conditions and the following disclaimer.
13235267Sgabor * 2. Redistributions in binary form must reproduce the above copyright
14235267Sgabor *    notice, this list of conditions and the following disclaimer in the
15235267Sgabor *    documentation and/or other materials provided with the distribution.
16235267Sgabor * 3. All advertising materials mentioning features or use of this software
17235267Sgabor *    must display the following acknowledgement:
18235267Sgabor *	This product includes software developed by the University of
19235267Sgabor *	California, Berkeley and its contributors.
20235267Sgabor * 4. Neither the name of the University nor the names of its contributors
21235267Sgabor *    may be used to endorse or promote products derived from this software
22235267Sgabor *    without specific prior written permission.
23235267Sgabor *
24235267Sgabor * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25235267Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26235267Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27235267Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28235267Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29235267Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30235267Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31235267Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32235267Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33235267Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34235267Sgabor * SUCH DAMAGE.
35235267Sgabor */
36235267Sgabor
37235267Sgabor#ifndef lint
38235267Sgabor#if 0
39235267Sgaborstatic char sccsid[] = "@(#)popen.c	8.3 (Berkeley) 4/6/94";
40235267Sgabor#endif
41235267Sgabor#endif /* not lint */
42235267Sgabor
43235267Sgabor#include <sys/cdefs.h>
44235267Sgabor__FBSDID("$FreeBSD: src/libexec/ftpd/popen.c,v 1.26 2004/11/18 13:46:29 yar Exp $");
45235267Sgabor
46235267Sgabor#include <sys/types.h>
47235267Sgabor#include <sys/wait.h>
48235267Sgabor#include <netinet/in.h>
49235435Sgabor
50235267Sgabor#include <errno.h>
51235267Sgabor#include <glob.h>
52242430Sgabor#include <signal.h>
53235267Sgabor#include <stdio.h>
54242430Sgabor#include <stdlib.h>
55242430Sgabor#include <string.h>
56242430Sgabor#include <unistd.h>
57235267Sgabor
58235267Sgabor#include "extern.h"
59235267Sgabor#include "pathnames.h"
60235267Sgabor#include <syslog.h>
61235267Sgabor#include <time.h>
62235267Sgabor
63235267Sgabor#define	MAXUSRARGS	100
64235267Sgabor#define	MAXGLOBARGS	1000
65235267Sgabor
66235267Sgabor/*
67235267Sgabor * Special version of popen which avoids call to shell.  This ensures noone
68235267Sgabor * may create a pipe to a hidden program as a side effect of a list or dir
69235267Sgabor * command.
70235267Sgabor */
71235267Sgaborstatic int *pids;
72235267Sgaborstatic int fds;
73235267Sgabor
74235267SgaborFILE *
75235267Sgaborftpd_popen(char *program, char *type)
76235267Sgabor{
77235267Sgabor	char *cp;
78235267Sgabor	FILE *iop;
79235267Sgabor	int argc, gargc, pdes[2], pid;
80235267Sgabor	char **pop, *argv[MAXUSRARGS], *gargv[MAXGLOBARGS];
81235267Sgabor
82235267Sgabor	if (((*type != 'r') && (*type != 'w')) || type[1])
83281123Spfg		return (NULL);
84235267Sgabor
85235267Sgabor	if (!pids) {
86235267Sgabor		if ((fds = getdtablesize()) <= 0)
87235267Sgabor			return (NULL);
88235267Sgabor		if ((pids = calloc(fds, sizeof(int))) == NULL)
89235267Sgabor			return (NULL);
90235267Sgabor	}
91235267Sgabor	if (pipe(pdes) < 0)
92235267Sgabor		return (NULL);
93235267Sgabor
94235267Sgabor	/* break up string into pieces */
95235267Sgabor	for (argc = 0, cp = program; argc < MAXUSRARGS; cp = NULL) {
96235267Sgabor		if (!(argv[argc++] = strtok(cp, " \t\n")))
97235267Sgabor			break;
98281132Spfg	}
99235267Sgabor	argv[argc - 1] = NULL;
100235267Sgabor
101235267Sgabor	/* glob each piece */
102235267Sgabor	gargv[0] = argv[0];
103235267Sgabor	for (gargc = argc = 1; argv[argc] && gargc < (MAXGLOBARGS-1); argc++) {
104235267Sgabor		glob_t gl;
105235267Sgabor		int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
106235267Sgabor
107235267Sgabor		memset(&gl, 0, sizeof(gl));
108281132Spfg		gl.gl_matchc = MAXGLOBARGS;
109235267Sgabor		flags |= GLOB_LIMIT;
110318153Smarius		if (glob(argv[argc], flags, NULL, &gl))
111318153Smarius			gargv[gargc++] = strdup(argv[argc]);
112318153Smarius		else if (gl.gl_pathc > 0) {
113318153Smarius			for (pop = gl.gl_pathv; *pop && gargc < (MAXGLOBARGS-1);
114318153Smarius			     pop++)
115318153Smarius				gargv[gargc++] = strdup(*pop);
116318153Smarius		}
117235267Sgabor		globfree(&gl);
118235267Sgabor	}
119235267Sgabor	gargv[gargc] = NULL;
120235267Sgabor
121235267Sgabor	iop = NULL;
122318153Smarius	fflush(NULL);
123318153Smarius#ifdef BUILTIN_LS
124318153Smarius	pid = (strcmp(gargv[0], _PATH_LS) == 0) ? fork() : vfork();
125318153Smarius#else
126318153Smarius	pid = fork();
127318153Smarius#endif
128318153Smarius	switch(pid) {
129318153Smarius	case -1:			/* error */
130318153Smarius		(void)close(pdes[0]);
131318153Smarius		(void)close(pdes[1]);
132318153Smarius		goto pfree;
133235267Sgabor		/* NOTREACHED */
134235267Sgabor	case 0:				/* child */
135235267Sgabor		if (*type == 'r') {
136235267Sgabor			if (pdes[1] != STDOUT_FILENO) {
137235267Sgabor				dup2(pdes[1], STDOUT_FILENO);
138281132Spfg				(void)close(pdes[1]);
139235267Sgabor			}
140235267Sgabor			dup2(STDOUT_FILENO, STDERR_FILENO); /* stderr too! */
141235267Sgabor			(void)close(pdes[0]);
142318153Smarius		} else {
143235267Sgabor			if (pdes[0] != STDIN_FILENO) {
144235267Sgabor				dup2(pdes[0], STDIN_FILENO);
145235267Sgabor				(void)close(pdes[0]);
146235267Sgabor			}
147235267Sgabor			(void)close(pdes[1]);
148235267Sgabor		}
149235267Sgabor#ifdef BUILTIN_LS
150235267Sgabor		if (strcmp(gargv[0], _PATH_LS) == 0) {
151235267Sgabor			/* Reset getopt for ls_main() */
152235267Sgabor			optreset = optind = optopt = 1;
153235267Sgabor			/* Close syslogging to remove pwd.db missing msgs */
154235267Sgabor			closelog();
155235267Sgabor			/* Trigger to sense new /etc/localtime after chroot */
156235267Sgabor			if (getenv("TZ") == NULL) {
157235267Sgabor				setenv("TZ", "", 0);
158235267Sgabor				tzset();
159235267Sgabor				unsetenv("TZ");
160235267Sgabor				tzset();
161235267Sgabor			}
162235267Sgabor			exit(ls_main(gargc, gargv));
163235267Sgabor		}
164235267Sgabor#endif
165235267Sgabor		execv(gargv[0], gargv);
166235267Sgabor		_exit(1);
167235267Sgabor	}
168235267Sgabor	/* parent; assume fdopen can't fail...  */
169235267Sgabor	if (*type == 'r') {
170235267Sgabor		iop = fdopen(pdes[0], type);
171235267Sgabor		(void)close(pdes[1]);
172235267Sgabor	} else {
173235267Sgabor		iop = fdopen(pdes[1], type);
174235267Sgabor		(void)close(pdes[0]);
175235267Sgabor	}
176318153Smarius	pids[fileno(iop)] = pid;
177235267Sgabor
178318153Smariuspfree:	for (argc = 1; gargv[argc] != NULL; argc++)
179235267Sgabor		free(gargv[argc]);
180235267Sgabor
181235267Sgabor	return (iop);
182235267Sgabor}
183235267Sgabor
184235267Sgaborint
185235267Sgaborftpd_pclose(FILE *iop)
186235267Sgabor{
187235267Sgabor	int fdes, omask, status;
188235267Sgabor	pid_t pid;
189235267Sgabor
190235267Sgabor	/*
191235267Sgabor	 * pclose returns -1 if stream is not associated with a
192235267Sgabor	 * `popened' command, or, if already `pclosed'.
193281132Spfg	 */
194235267Sgabor	if (pids == NULL || pids[fdes = fileno(iop)] == 0)
195235267Sgabor		return (-1);
196235267Sgabor	(void)fclose(iop);
197235267Sgabor	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
198235267Sgabor	while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR)
199235267Sgabor		continue;
200235267Sgabor	(void)sigsetmask(omask);
201235267Sgabor	pids[fdes] = 0;
202235267Sgabor	if (pid < 0)
203235267Sgabor		return (pid);
204281132Spfg	if (WIFEXITED(status))
205235267Sgabor		return (WEXITSTATUS(status));
206235267Sgabor	return (1);
207235267Sgabor}
208235267Sgabor