File:  [DragonFly] / src / usr.sbin / cron / cron / popen.c
Revision 1.4: download - view: text, annotated - select for diffs
Wed Mar 10 18:27:26 2004 UTC (10 years, 7 months ago) by dillon
Branches: MAIN
CVS tags: HEAD, DragonFly_Snap29Sep2004, DragonFly_Snap13Sep2004, DragonFly_1_0_REL, DragonFly_1_0_RC1, DragonFly_1_0A_REL
ANSIfication, remove 'register' and 'auto' use, convert K&R procedure decls.
Fix one bug where getc() was being called on a FILE returned by fdopen()
before the a FILE was checked for NULL.

Submitted-by: Chris Pressey <cpressey@catseye.mine.nu>

    1: /*
    2:  * Copyright (c) 1988 The Regents of the University of California.
    3:  * All rights reserved.
    4:  *
    5:  * This code is derived from software written by Ken Arnold and
    6:  * published in UNIX Review, Vol. 6, No. 8.
    7:  *
    8:  * Redistribution and use in source and binary forms are permitted
    9:  * provided that the above copyright notice and this paragraph are
   10:  * duplicated in all such forms and that any documentation,
   11:  * advertising materials, and other materials related to such
   12:  * distribution and use acknowledge that the software was developed
   13:  * by the University of California, Berkeley.  The name of the
   14:  * University may not be used to endorse or promote products derived
   15:  * from this software without specific prior written permission.
   16:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
   17:  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
   18:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
   19:  *
   20:  * @(#)popen.c	5.7 (Berkeley) 2/14/89
   21:  * $FreeBSD: src/usr.sbin/cron/cron/popen.c,v 1.7.2.3 2000/12/11 01:03:31 obrien Exp $
   22:  * $DragonFly: src/usr.sbin/cron/cron/popen.c,v 1.4 2004/03/10 18:27:26 dillon Exp $
   23:  */
   24: 
   25: /* this came out of the ftpd sources; it's been modified to avoid the
   26:  * globbing stuff since we don't need it.  also execvp instead of execv.
   27:  */
   28: 
   29: #include "cron.h"
   30: #include <sys/signal.h>
   31: #include <fcntl.h>
   32: #include <paths.h>
   33: #if defined(SYSLOG)
   34: # include <syslog.h>
   35: #endif
   36: #if defined(LOGIN_CAP)
   37: # include <login_cap.h>
   38: #endif
   39: 
   40: 
   41: #define MAX_ARGS 100
   42: #define WANT_GLOBBING 0
   43: 
   44: /*
   45:  * Special version of popen which avoids call to shell.  This insures noone
   46:  * may create a pipe to a hidden program as a side effect of a list or dir
   47:  * command.
   48:  */
   49: static PID_T *pids;
   50: static int fds;
   51: 
   52: FILE *
   53: cron_popen(char *program, char *type, entry *e)
   54: {
   55: 	char *cp;
   56: 	FILE *iop;
   57: 	int argc, pdes[2];
   58: 	PID_T pid;
   59: 	char *usernm;
   60: 	char *argv[MAX_ARGS + 1];
   61: # if defined(LOGIN_CAP)
   62: 	struct passwd	*pwd;
   63: 	login_cap_t *lc;
   64: # endif
   65: #if WANT_GLOBBING
   66: 	char **pop, *vv[2];
   67: 	int gargc;
   68: 	char *gargv[1000];
   69: 	extern char **glob(), **copyblk();
   70: #endif
   71: 
   72: 	if ((*type != 'r' && *type != 'w') || type[1])
   73: 		return(NULL);
   74: 
   75: 	if (!pids) {
   76: 		if ((fds = getdtablesize()) <= 0)
   77: 			return(NULL);
   78: 		if (!(pids = (PID_T *)malloc((u_int)(fds * sizeof(PID_T)))))
   79: 			return(NULL);
   80: 		bzero((char *)pids, fds * sizeof(PID_T));
   81: 	}
   82: 	if (pipe(pdes) < 0)
   83: 		return(NULL);
   84: 
   85: 	/* break up string into pieces */
   86: 	for (argc = 0, cp = program; argc < MAX_ARGS; cp = NULL)
   87: 		if (!(argv[argc++] = strtok(cp, " \t\n")))
   88: 			break;
   89: 	argv[MAX_ARGS] = NULL;
   90: 
   91: #if WANT_GLOBBING
   92: 	/* glob each piece */
   93: 	gargv[0] = argv[0];
   94: 	for (gargc = argc = 1; argv[argc]; argc++) {
   95: 		if (!(pop = glob(argv[argc]))) {	/* globbing failed */
   96: 			vv[0] = argv[argc];
   97: 			vv[1] = NULL;
   98: 			pop = copyblk(vv);
   99: 		}
  100: 		argv[argc] = (char *)pop;		/* save to free later */
  101: 		while (*pop && gargc < 1000)
  102: 			gargv[gargc++] = *pop++;
  103: 	}
  104: 	gargv[gargc] = NULL;
  105: #endif
  106: 
  107: 	iop = NULL;
  108: 	switch(pid = vfork()) {
  109: 	case -1:			/* error */
  110: 		(void)close(pdes[0]);
  111: 		(void)close(pdes[1]);
  112: 		goto pfree;
  113: 		/* NOTREACHED */
  114: 	case 0:				/* child */
  115: 		if (e != NULL) {
  116: #ifdef SYSLOG
  117: 			closelog();
  118: #endif
  119: 
  120: 			/* get new pgrp, void tty, etc.
  121: 			 */
  122: 			(void) setsid();
  123: 		}
  124: 		if (*type == 'r') {
  125: 			/* Do not share our parent's stdin */
  126: 			(void)close(0);
  127: 			(void)open(_PATH_DEVNULL, O_RDWR);
  128: 			if (pdes[1] != 1) {
  129: 				dup2(pdes[1], 1);
  130: 				dup2(pdes[1], 2);	/* stderr, too! */
  131: 				(void)close(pdes[1]);
  132: 			}
  133: 			(void)close(pdes[0]);
  134: 		} else {
  135: 			if (pdes[0] != 0) {
  136: 				dup2(pdes[0], 0);
  137: 				(void)close(pdes[0]);
  138: 			}
  139: 			/* Hack: stdout gets revoked */
  140: 			(void)close(1);
  141: 			(void)open(_PATH_DEVNULL, O_RDWR);
  142: 			(void)close(2);
  143: 			(void)open(_PATH_DEVNULL, O_RDWR);
  144: 			(void)close(pdes[1]);
  145: 		}
  146: # if defined(LOGIN_CAP)
  147: 		if (e != NULL) {
  148: 			/* Set user's entire context, but skip the environment
  149: 			 * as cron provides a separate interface for this
  150: 			 */
  151: 			usernm = env_get("LOGNAME", e->envp);
  152: 			if ((pwd = getpwnam(usernm)) == NULL)
  153: 				pwd = getpwuid(e->uid);
  154: 			lc = NULL;
  155: 			if (pwd != NULL) {
  156: 				pwd->pw_gid = e->gid;
  157: 				if (e->class != NULL)
  158: 					lc = login_getclass(e->class);
  159: 			}
  160: 			if (pwd &&
  161: 			    setusercontext(lc, pwd, e->uid,
  162: 				    LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV)) == 0)
  163: 				(void) endpwent();
  164: 			else {
  165: 				/* fall back to the old method */
  166: 				(void) endpwent();
  167: # endif
  168: 				/* set our directory, uid and gid.  Set gid first,
  169: 				 * since once we set uid, we've lost root privledges.
  170: 				 */
  171: 				setgid(e->gid);
  172: # if defined(BSD)
  173: 				initgroups(usernm, e->gid);
  174: # endif
  175: 				setlogin(usernm);
  176: 				setuid(e->uid);         /* we aren't root after this..*/
  177: #if defined(LOGIN_CAP)
  178: 			}
  179: 			if (lc != NULL)
  180: 				login_close(lc);
  181: #endif
  182: 			chdir(env_get("HOME", e->envp));
  183: 		}
  184: #if WANT_GLOBBING
  185: 		execvp(gargv[0], gargv);
  186: #else
  187: 		execvp(argv[0], argv);
  188: #endif
  189: 		_exit(1);
  190: 	}
  191: 	/* parent; assume fdopen can't fail...  */
  192: 	if (*type == 'r') {
  193: 		iop = fdopen(pdes[0], type);
  194: 		(void)close(pdes[1]);
  195: 	} else {
  196: 		iop = fdopen(pdes[1], type);
  197: 		(void)close(pdes[0]);
  198: 	}
  199: 	pids[fileno(iop)] = pid;
  200: 
  201: pfree:
  202: #if WANT_GLOBBING
  203: 	for (argc = 1; argv[argc] != NULL; argc++) {
  204: /*		blkfree((char **)argv[argc]);	*/
  205: 		free((char *)argv[argc]);
  206: 	}
  207: #endif
  208: 	return(iop);
  209: }
  210: 
  211: int
  212: cron_pclose(FILE *iop)
  213: {
  214: 	int fdes;
  215: 	int omask;
  216: 	WAIT_T stat_loc;
  217: 	PID_T pid;
  218: 
  219: 	/*
  220: 	 * pclose returns -1 if stream is not associated with a
  221: 	 * `popened' command, or, if already `pclosed'.
  222: 	 */
  223: 	if (pids == 0 || pids[fdes = fileno(iop)] == 0)
  224: 		return(-1);
  225: 	(void)fclose(iop);
  226: 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
  227: 	while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1)
  228: 		;
  229: 	(void)sigsetmask(omask);
  230: 	pids[fdes] = 0;
  231: 	return (pid == -1 ? -1 : WEXITSTATUS(stat_loc));
  232: }