125184Sjkh/*	$NetBSD: popen.c,v 1.2 2010/05/06 18:53:17 christos Exp $	*/
225184Sjkh
348662Speter/*
425184Sjkh * Copyright (c) 1988, 1993, 1994
525184Sjkh *	The Regents of the University of California.  All rights reserved.
625184Sjkh *
725184Sjkh * This code is derived from software written by Ken Arnold and
825184Sjkh * published in UNIX Review, Vol. 6, No. 8.
925184Sjkh *
1025184Sjkh * Redistribution and use in source and binary forms, with or without
1125184Sjkh * modification, are permitted provided that the following conditions
1225184Sjkh * are met:
1325184Sjkh * 1. Redistributions of source code must retain the above copyright
1425184Sjkh *    notice, this list of conditions and the following disclaimer.
1525184Sjkh * 2. Redistributions in binary form must reproduce the above copyright
1625184Sjkh *    notice, this list of conditions and the following disclaimer in the
1725184Sjkh *    documentation and/or other materials provided with the distribution.
1825184Sjkh * 3. All advertising materials mentioning features or use of this software
1925184Sjkh *    must display the following acknowledgement:
2025184Sjkh *	This product includes software developed by the University of
2125184Sjkh *	California, Berkeley and its contributors.
2225184Sjkh *
2325184Sjkh * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2425184Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2525184Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2625184Sjkh * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2725184Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2840006Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2940006Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3040006Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3140006Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3240006Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3340006Sphk * SUCH DAMAGE.
3442621Shm *
3542621Shm */
3642621Shm
3742621Shm/* this came out of the ftpd sources; it's been modified to avoid the
3842621Shm * globbing stuff since we don't need it.  also execvp instead of execv.
3942627Sjoerg */
4042627Sjoerg
4142627Sjoerg#include <sys/cdefs.h>
4242627Sjoerg#ifndef lint
4342627Sjoerg#if 0
4442627Sjoergstatic sccsid[] = "@(#)popen.c	8.3 (Berkeley) 4/6/94";
4542627Sjoergstatic char rcsid[] = "Id: popen.c,v 1.6 2003/02/16 04:40:01 vixie Exp";
4642627Sjoerg#else
4742627Sjoerg__RCSID("$NetBSD: popen.c,v 1.2 2010/05/06 18:53:17 christos Exp $");
4842627Sjoerg#endif
4942627Sjoerg#endif /* not lint */
5042627Sjoerg
5142627Sjoerg#include "cron.h"
5242627Sjoerg
5325184Sjkh#define MAX_ARGV	100
5448662Speter#define MAX_GARGV	1000
5548662Speter
5648662Speter/*
5725184Sjkh * Special version of popen which avoids call to shell.  This ensures noone
5833682Sbrian * may create a pipe to a hidden program as a side effect of a list or dir
5948662Speter * command.
6025184Sjkh */
6125184Sjkhstatic PID_T *pids;
6225184Sjkhstatic long fds;
6325184Sjkh
6425184SjkhFILE *
6548662Spetercron_popen(char *program, const char *type, struct passwd *pw) {
6625184Sjkh	char *cp;
6725184Sjkh	FILE *iop;
6825184Sjkh	int argc, pdes[2];
6925184Sjkh	PID_T pid;
7025184Sjkh	char *argv[MAX_ARGV];
7125184Sjkh
7225184Sjkh	if ((*type != 'r' && *type != 'w') || type[1] != '\0')
7325184Sjkh		return (NULL);
7448662Speter
7525184Sjkh	if (!pids) {
7625184Sjkh		size_t len;
7725184Sjkh		if ((fds = sysconf(_SC_OPEN_MAX)) <= 0)
7825184Sjkh			return (NULL);
7925184Sjkh		len = fds * sizeof(*pids);
8025184Sjkh		if ((pids = malloc(len)) == NULL)
8125184Sjkh			return (NULL);
8225184Sjkh		(void)memset(pids, 0, len);
8325184Sjkh	}
8448662Speter	if (pipe(pdes) < 0)
8525184Sjkh		return (NULL);
8648662Speter
8748662Speter	/* break up string into pieces */
8848662Speter	for (argc = 0, cp = program; argc < MAX_ARGV - 1; cp = NULL)
8948662Speter		if (!(argv[argc++] = strtok(cp, " \t\n")))
9025184Sjkh			break;
9129300Sdanny	argv[MAX_ARGV-1] = NULL;
9229300Sdanny
9329300Sdanny	switch (pid = vfork()) {
9429300Sdanny	case -1:			/* error */
9532382Salex		(void)close(pdes[0]);
9632382Salex		(void)close(pdes[1]);
9732382Salex		return (NULL);
9829300Sdanny		/* NOTREACHED */
9929300Sdanny	case 0:				/* child */
10029300Sdanny		if (pw) {
10129300Sdanny			if (setsid() == -1)
10241077Speter				warn("setsid() failed for %s", pw->pw_name);
10329300Sdanny#ifdef LOGIN_CAP
10429300Sdanny			if (setusercontext(0, pw, pw->pw_uid, LOGIN_SETALL) < 0)
10529300Sdanny			{
10629300Sdanny				warn("setusercontext() failed for %s",
10729300Sdanny				    pw->pw_name);
10829300Sdanny				_exit(ERROR_EXIT);
10929300Sdanny			}
11029300Sdanny#else
11129300Sdanny			if (setgid(pw->pw_gid) < 0 ||
11245542Sdes			    initgroups(pw->pw_name, pw->pw_gid) < 0) {
11345542Sdes				warn("unable to set groups for %s",
11445542Sdes				    pw->pw_name);
11545542Sdes				_exit(ERROR_EXIT);
11645542Sdes			}
11745622Sbrian#if (defined(BSD)) && (BSD >= 199103)
11844992Sbrian			if (setlogin(pw->pw_name) < 0) {
11944992Sbrian				warn("setlogin() failed for %s",
12044992Sbrian				    pw->pw_name);
12144992Sbrian				_exit(ERROR_EXIT);
12244992Sbrian			}
12344992Sbrian#endif /* BSD */
12444992Sbrian			if (setuid(pw->pw_uid)) {
12544992Sbrian				warn("unable to set uid for %s", pw->pw_name);
12644992Sbrian				_exit(ERROR_EXIT);
12744992Sbrian			}
12844992Sbrian#endif /* LOGIN_CAP */
12944992Sbrian		}
13029300Sdanny		if (*type == 'r') {
13133337Salex			if (pdes[1] != STDOUT) {
13233337Salex				(void)dup2(pdes[1], STDOUT);
13333149Salex				(void)close(pdes[1]);
13433149Salex			}
13533149Salex			(void)dup2(STDOUT, STDERR);	/* stderr too! */
13633149Salex			(void)close(pdes[0]);
13729300Sdanny		} else {
13825184Sjkh			if (pdes[0] != STDIN) {
13925184Sjkh				(void)dup2(pdes[0], STDIN);
14040006Sphk				(void)close(pdes[0]);
14140006Sphk			}
14240006Sphk			(void)close(pdes[1]);
14340006Sphk		}
14440006Sphk		(void)execvp(argv[0], argv);
14529300Sdanny		_exit(ERROR_EXIT);
14629300Sdanny	}
14725184Sjkh
14825184Sjkh	/* parent; assume fdopen can't fail...  */
14925184Sjkh	if (*type == 'r') {
15025184Sjkh		iop = fdopen(pdes[0], type);
15125184Sjkh		(void)close(pdes[1]);
15225184Sjkh	} else {
15325184Sjkh		iop = fdopen(pdes[1], type);
15425184Sjkh		(void)close(pdes[0]);
15525184Sjkh	}
15625184Sjkh	pids[fileno(iop)] = pid;
15725184Sjkh
15825184Sjkh	return (iop);
15925184Sjkh}
16025184Sjkh
16127218Spstint
16227218Spstcron_pclose(FILE *iop) {
16347755Sbde	int fdes;
16427218Spst	PID_T pid;
16527218Spst	WAIT_T status;
16645096Simp	sigset_t sset, osset;
16745096Simp
16847755Sbde	/*
16947755Sbde	 * pclose returns -1 if stream is not associated with a
17045096Simp	 * `popened' command, or, if already `pclosed'.
17145096Simp	 */
17239267Sjkoshy	if (pids == 0 || pids[fdes = fileno(iop)] == 0)
17339267Sjkoshy		return (-1);
17447755Sbde	(void)fclose(iop);
17539267Sjkoshy	(void)sigemptyset(&sset);
17639267Sjkoshy	(void)sigaddset(&sset, SIGINT);
17725184Sjkh	(void)sigaddset(&sset, SIGQUIT);
17825365Sjkh	(void)sigaddset(&sset, SIGHUP);
17947755Sbde	(void)sigprocmask(SIG_BLOCK, &sset, &osset);
18025184Sjkh	while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR)
18125184Sjkh		continue;
18233439Sguido	(void)sigprocmask(SIG_SETMASK, &osset, NULL);
18333439Sguido	pids[fdes] = 0;
18447755Sbde	if (pid < 0)
18533439Sguido		return (pid);
18633439Sguido	if (WIFEXITED(status))
18733439Sguido		return (WEXITSTATUS(status));
18833439Sguido	else
18947755Sbde		return WTERMSIG(status);
19033439Sguido}
19133439Sguido