12311Sjkh/*
22311Sjkh * Copyright (c) 1988 The Regents of the University of California.
32311Sjkh * All rights reserved.
42311Sjkh *
52311Sjkh * This code is derived from software written by Ken Arnold and
62311Sjkh * published in UNIX Review, Vol. 6, No. 8.
72311Sjkh *
82311Sjkh * Redistribution and use in source and binary forms are permitted
92311Sjkh * provided that the above copyright notice and this paragraph are
102311Sjkh * duplicated in all such forms and that any documentation,
112311Sjkh * advertising materials, and other materials related to such
122311Sjkh * distribution and use acknowledge that the software was developed
132311Sjkh * by the University of California, Berkeley.  The name of the
142311Sjkh * University may not be used to endorse or promote products derived
152311Sjkh * from this software without specific prior written permission.
162311Sjkh * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
172311Sjkh * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
182311Sjkh * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
192311Sjkh *
202311Sjkh */
212311Sjkh
222311Sjkh/* this came out of the ftpd sources; it's been modified to avoid the
232311Sjkh * globbing stuff since we don't need it.  also execvp instead of execv.
242311Sjkh */
252311Sjkh
262311Sjkh#ifndef lint
2729452Scharnier#if 0
282311Sjkhstatic char sccsid[] = "@(#)popen.c	5.7 (Berkeley) 2/14/89";
2929452Scharnier#endif
3029452Scharnierstatic const char rcsid[] =
3150479Speter  "$FreeBSD$";
322311Sjkh#endif /* not lint */
332311Sjkh
342311Sjkh#include "cron.h"
352311Sjkh#include <sys/signal.h>
3645369Speter#include <fcntl.h>
3769793Sobrien#include <paths.h>
3862367Sache#if defined(SYSLOG)
3962367Sache# include <syslog.h>
4062367Sache#endif
4162367Sache#if defined(LOGIN_CAP)
4262367Sache# include <login_cap.h>
4362367Sache#endif
442311Sjkh
452311Sjkh
4620573Spst#define MAX_ARGS 100
472311Sjkh#define WANT_GLOBBING 0
482311Sjkh
492311Sjkh/*
502311Sjkh * Special version of popen which avoids call to shell.  This insures noone
512311Sjkh * may create a pipe to a hidden program as a side effect of a list or dir
522311Sjkh * command.
532311Sjkh */
542311Sjkhstatic PID_T *pids;
552311Sjkhstatic int fds;
562311Sjkh
572311SjkhFILE *
5862367Sachecron_popen(program, type, e)
592311Sjkh	char *program, *type;
6062367Sache	entry *e;
612311Sjkh{
622311Sjkh	register char *cp;
632311Sjkh	FILE *iop;
642311Sjkh	int argc, pdes[2];
652311Sjkh	PID_T pid;
6662367Sache	char *usernm;
6720573Spst	char *argv[MAX_ARGS + 1];
6862367Sache# if defined(LOGIN_CAP)
6962367Sache	struct passwd	*pwd;
7062367Sache	login_cap_t *lc;
7162367Sache# endif
722311Sjkh#if WANT_GLOBBING
732311Sjkh	char **pop, *vv[2];
742311Sjkh	int gargc;
752311Sjkh	char *gargv[1000];
762311Sjkh	extern char **glob(), **copyblk();
772311Sjkh#endif
782311Sjkh
7929452Scharnier	if ((*type != 'r' && *type != 'w') || type[1])
802311Sjkh		return(NULL);
812311Sjkh
822311Sjkh	if (!pids) {
832311Sjkh		if ((fds = getdtablesize()) <= 0)
842311Sjkh			return(NULL);
852311Sjkh		if (!(pids = (PID_T *)malloc((u_int)(fds * sizeof(PID_T)))))
862311Sjkh			return(NULL);
872311Sjkh		bzero((char *)pids, fds * sizeof(PID_T));
882311Sjkh	}
892311Sjkh	if (pipe(pdes) < 0)
902311Sjkh		return(NULL);
912311Sjkh
922311Sjkh	/* break up string into pieces */
9320573Spst	for (argc = 0, cp = program; argc < MAX_ARGS; cp = NULL)
942311Sjkh		if (!(argv[argc++] = strtok(cp, " \t\n")))
952311Sjkh			break;
9662365Sache	argv[MAX_ARGS] = NULL;
972311Sjkh
982311Sjkh#if WANT_GLOBBING
992311Sjkh	/* glob each piece */
1002311Sjkh	gargv[0] = argv[0];
1012311Sjkh	for (gargc = argc = 1; argv[argc]; argc++) {
1022311Sjkh		if (!(pop = glob(argv[argc]))) {	/* globbing failed */
1032311Sjkh			vv[0] = argv[argc];
1042311Sjkh			vv[1] = NULL;
1052311Sjkh			pop = copyblk(vv);
1062311Sjkh		}
1072311Sjkh		argv[argc] = (char *)pop;		/* save to free later */
1082311Sjkh		while (*pop && gargc < 1000)
1092311Sjkh			gargv[gargc++] = *pop++;
1102311Sjkh	}
1112311Sjkh	gargv[gargc] = NULL;
1122311Sjkh#endif
1132311Sjkh
1142311Sjkh	iop = NULL;
1152311Sjkh	switch(pid = vfork()) {
1162311Sjkh	case -1:			/* error */
1172311Sjkh		(void)close(pdes[0]);
1182311Sjkh		(void)close(pdes[1]);
1192311Sjkh		goto pfree;
1202311Sjkh		/* NOTREACHED */
1212311Sjkh	case 0:				/* child */
12262367Sache		if (e != NULL) {
12362367Sache#ifdef SYSLOG
12462367Sache			closelog();
12562367Sache#endif
12662367Sache
12762367Sache			/* get new pgrp, void tty, etc.
12862367Sache			 */
12962367Sache			(void) setsid();
13062367Sache		}
1312311Sjkh		if (*type == 'r') {
13245369Speter			/* Do not share our parent's stdin */
13345369Speter			(void)close(0);
13469793Sobrien			(void)open(_PATH_DEVNULL, O_RDWR);
1352311Sjkh			if (pdes[1] != 1) {
1362311Sjkh				dup2(pdes[1], 1);
1372311Sjkh				dup2(pdes[1], 2);	/* stderr, too! */
1382311Sjkh				(void)close(pdes[1]);
1392311Sjkh			}
1402311Sjkh			(void)close(pdes[0]);
1412311Sjkh		} else {
1422311Sjkh			if (pdes[0] != 0) {
1432311Sjkh				dup2(pdes[0], 0);
1442311Sjkh				(void)close(pdes[0]);
1452311Sjkh			}
14645369Speter			/* Hack: stdout gets revoked */
14745369Speter			(void)close(1);
14869793Sobrien			(void)open(_PATH_DEVNULL, O_RDWR);
14945369Speter			(void)close(2);
15069793Sobrien			(void)open(_PATH_DEVNULL, O_RDWR);
1512311Sjkh			(void)close(pdes[1]);
1522311Sjkh		}
15362367Sache		if (e != NULL) {
15462367Sache			/* Set user's entire context, but skip the environment
15562367Sache			 * as cron provides a separate interface for this
15662367Sache			 */
15762367Sache			usernm = env_get("LOGNAME", e->envp);
15890276Sbbraun# if defined(LOGIN_CAP)
15962367Sache			if ((pwd = getpwnam(usernm)) == NULL)
16062367Sache				pwd = getpwuid(e->uid);
16162367Sache			lc = NULL;
16262367Sache			if (pwd != NULL) {
16362367Sache				pwd->pw_gid = e->gid;
16462367Sache				if (e->class != NULL)
16562367Sache					lc = login_getclass(e->class);
16662367Sache			}
16762367Sache			if (pwd &&
16862367Sache			    setusercontext(lc, pwd, e->uid,
16962367Sache				    LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV)) == 0)
17062367Sache				(void) endpwent();
17162367Sache			else {
17262367Sache				/* fall back to the old method */
17362367Sache				(void) endpwent();
17462367Sache# endif
175159523Smaxim				/*
176159523Smaxim				 * Set our directory, uid and gid.  Set gid
177159523Smaxim				 * first since once we set uid, we've lost
178159527Smaxim				 * root privileges.
17962367Sache				 */
180159142Smaxim				if (setgid(e->gid) != 0)
181159142Smaxim					_exit(ERROR_EXIT);
18262367Sache# if defined(BSD)
183159142Smaxim				if (initgroups(usernm, e->gid) != 0)
184159142Smaxim					_exit(ERROR_EXIT);
18562367Sache# endif
186159142Smaxim				if (setlogin(usernm) != 0)
187159142Smaxim					_exit(ERROR_EXIT);
188159142Smaxim				if (setuid(e->uid) != 0)
189159142Smaxim					_exit(ERROR_EXIT);
190159142Smaxim				/* we aren't root after this..*/
19162367Sache#if defined(LOGIN_CAP)
19262367Sache			}
19362376Sache			if (lc != NULL)
19462376Sache				login_close(lc);
19562367Sache#endif
19662367Sache			chdir(env_get("HOME", e->envp));
19762367Sache		}
1982311Sjkh#if WANT_GLOBBING
1992311Sjkh		execvp(gargv[0], gargv);
2002311Sjkh#else
2012311Sjkh		execvp(argv[0], argv);
2022311Sjkh#endif
2032311Sjkh		_exit(1);
2042311Sjkh	}
2052311Sjkh	/* parent; assume fdopen can't fail...  */
2062311Sjkh	if (*type == 'r') {
2072311Sjkh		iop = fdopen(pdes[0], type);
2082311Sjkh		(void)close(pdes[1]);
2092311Sjkh	} else {
2102311Sjkh		iop = fdopen(pdes[1], type);
2112311Sjkh		(void)close(pdes[0]);
2122311Sjkh	}
2132311Sjkh	pids[fileno(iop)] = pid;
2142311Sjkh
2152311Sjkhpfree:
2162311Sjkh#if WANT_GLOBBING
2172311Sjkh	for (argc = 1; argv[argc] != NULL; argc++) {
2182311Sjkh/*		blkfree((char **)argv[argc]);	*/
2192311Sjkh		free((char *)argv[argc]);
2202311Sjkh	}
2212311Sjkh#endif
2222311Sjkh	return(iop);
2232311Sjkh}
2242311Sjkh
2252311Sjkhint
2262311Sjkhcron_pclose(iop)
2272311Sjkh	FILE *iop;
2282311Sjkh{
2292311Sjkh	register int fdes;
2302311Sjkh	int omask;
2312311Sjkh	WAIT_T stat_loc;
2322311Sjkh	PID_T pid;
2332311Sjkh
2342311Sjkh	/*
2352311Sjkh	 * pclose returns -1 if stream is not associated with a
2362311Sjkh	 * `popened' command, or, if already `pclosed'.
2372311Sjkh	 */
2382311Sjkh	if (pids == 0 || pids[fdes = fileno(iop)] == 0)
2392311Sjkh		return(-1);
2402311Sjkh	(void)fclose(iop);
2412311Sjkh	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
2422311Sjkh	while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1)
2432311Sjkh		;
2442311Sjkh	(void)sigsetmask(omask);
2452311Sjkh	pids[fdes] = 0;
2462311Sjkh	return (pid == -1 ? -1 : WEXITSTATUS(stat_loc));
2472311Sjkh}
248