register FILE	*mail;
FILE		*mail;
child_process(e, u)	entry	*e;	user	*u;{	int		stdin_pipe[2], stdout_pipe[2];	register char	*input_data;	char		*usernm, *mailto;	int		children = 0;	Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd))	/* mark ourselves as different to PS command watchers by upshifting	 * our program name.  This has no effect on some kernels.	 */	/*local*/{		register char	*pch;		for (pch = ProgramName;  *pch;  pch++)			*pch = MkUpper(*pch);	}	/* discover some useful and important environment settings	 */	usernm = env_get("LOGNAME", e->envp);	mailto = env_get("MAILTO", e->envp);#ifdef USE_SIGCHLD	/* our parent is watching for our death by catching SIGCHLD.  we	 * do not care to watch for our children's deaths this way -- we	 * use wait() explictly.  so we have to disable the signal (which	 * was inherited from the parent).	 */	(void) signal(SIGCHLD, SIG_IGN);#else	/* on system-V systems, we are ignoring SIGCLD.  we have to stop	 * ignoring it now or the wait() in cron_pclose() won't work.	 * because of this, we have to wait() for our children here, as well.	 */	(void) signal(SIGCLD, SIG_DFL);#endif /*BSD*/	/* create some pipes to talk to our future child	 */	pipe(stdin_pipe);	/* child's stdin */	pipe(stdout_pipe);	/* child's stdout */		/* since we are a forked process, we can diddle the command string	 * we were passed -- nobody else is going to use it again, right?	 *	 * if a % is present in the command, previous characters are the	 * command, and subsequent characters are the additional input to	 * the command.  Subsequent %'s will be transformed into newlines,	 * but that happens later.	 */	/*local*/{		register int escaped = FALSE;		register int ch;		for (input_data = e->cmd;  ch = *input_data;  input_data++) {			if (escaped) {				escaped = FALSE;				continue;			}			if (ch == '\\') {				escaped = TRUE;				continue;			}			if (ch == '%') {				*input_data++ = '\0';				break;			}		}	}	/* fork again, this time so we can exec the user's command.	 */	switch (vfork()) {	case -1:		log_it("CRON",getpid(),"error","can't vfork");		exit(ERROR_EXIT);		/*NOTREACHED*/	case 0:		Debug(DPROC, ("[%d] grandchild process Vfork()'ed\n",			      getpid()))		/* write a log message.  we've waited this long to do it		 * because it was not until now that we knew the PID that		 * the actual user command shell was going to get and the		 * PID is part of the log message.		 */		/*local*/{			char *x = mkprints((u_char *)e->cmd, strlen(e->cmd));			log_it(usernm, getpid(), "CMD", x);			free(x);		}		/* that's the last thing we'll log.  close the log files.		 */#ifdef SYSLOG		closelog();#endif		/* get new pgrp, void tty, etc.		 */		(void) setsid();		/* close the pipe ends that we won't use.  this doesn't affect		 * the parent, who has to read and write them; it keeps the		 * kernel from recording us as a potential client TWICE --		 * which would keep it from sending SIGPIPE in otherwise		 * appropriate circumstances.		 */		close(stdin_pipe[WRITE_PIPE]);		close(stdout_pipe[READ_PIPE]);		/* grandchild process.  make std{in,out} be the ends of		 * pipes opened by our daddy; make stderr go to stdout.		 */		close(STDIN);	dup2(stdin_pipe[READ_PIPE], STDIN);		close(STDOUT);	dup2(stdout_pipe[WRITE_PIPE], STDOUT);		close(STDERR);	dup2(STDOUT, STDERR);		/* close the pipes we just dup'ed.  The resources will remain.		 */		close(stdin_pipe[READ_PIPE]);		close(stdout_pipe[WRITE_PIPE]);		/* set our login universe.  Do this in the grandchild		 * so that the child can invoke /usr/lib/sendmail		 * without surprises.		 */		do_univ(u);		/* set our directory, uid and gid.  Set gid first, since once		 * we set uid, we've lost root privledges.		 */		setgid(e->gid);# if defined(BSD)		initgroups(env_get("LOGNAME", e->envp), e->gid);# endif		setlogin(usernm);		setuid(e->uid);		/* we aren't root after this... */		chdir(env_get("HOME", e->envp));		/* exec the command.		 */		{			char	*shell = env_get("SHELL", e->envp);# if DEBUGGING			if (DebugFlags & DTEST) {				fprintf(stderr,				"debug DTEST is on, not exec'ing command.\n");				fprintf(stderr,				"\tcmd='%s' shell='%s'\n", e->cmd, shell);				_exit(OK_EXIT);			}# endif /*DEBUGGING*/			execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);			fprintf(stderr, "execl: couldn't exec `%s'\n", shell);			perror("execl");			_exit(ERROR_EXIT);		}		break;	default:		/* parent process */		break;	}	children++;	/* middle process, child of original cron, parent of process running	 * the user's command.	 */	Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid()))	/* close the ends of the pipe that will only be referenced in the	 * grandchild process...	 */	close(stdin_pipe[READ_PIPE]);	close(stdout_pipe[WRITE_PIPE]);	/*	 * write, to the pipe connected to child's stdin, any input specified	 * after a % in the crontab entry.  while we copy, convert any	 * additional %'s to newlines.  when done, if some characters were	 * written and the last one wasn't a newline, write a newline.	 *	 * Note that if the input data won't fit into one pipe buffer (2K	 * or 4K on most BSD systems), and the child doesn't read its stdin,	 * we would block here.  thus we must fork again.	 */	if (*input_data && fork() == 0) {		register FILE	*out = fdopen(stdin_pipe[WRITE_PIPE], "w");		register int	need_newline = FALSE;		register int	escaped = FALSE;		register int	ch;		Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid()))		/* close the pipe we don't use, since we inherited it and		 * are part of its reference count now.		 */		close(stdout_pipe[READ_PIPE]);		/* translation:		 *	\% -> %		 *	%  -> \n		 *	\x -> \x	for all x != %		 */		while (ch = *input_data++) {			if (escaped) {				if (ch != '%')					putc('\\', out);			} else {				if (ch == '%')					ch = '\n';			}			if (!(escaped = (ch == '\\'))) {				putc(ch, out);				need_newline = (ch != '\n');			}		}		if (escaped)			putc('\\', out);		if (need_newline)			putc('\n', out);		/* close the pipe, causing an EOF condition.  fclose causes		 * stdin_pipe[WRITE_PIPE] to be closed, too.		 */		fclose(out);		Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid()))		exit(0);	}	/* close the pipe to the grandkiddie's stdin, since its wicked uncle	 * ernie back there has it open and will close it when he's done.	 */	close(stdin_pipe[WRITE_PIPE]);	children++;	/*	 * read output from the grandchild.  it's stderr has been redirected to	 * it's stdout, which has been redirected to our pipe.  if there is any	 * output, we'll be mailing it to the user whose crontab this is...	 * when the grandchild exits, we'll get EOF.	 */	Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid()))	/*local*/{		register FILE	*in = fdopen(stdout_pipe[READ_PIPE], "r");		register int	ch = getc(in);		if (ch != EOF) {			register FILE	*mail;			register int	bytes = 1;			int		status = 0;			Debug(DPROC|DEXT,				("[%d] got data (%x:%c) from grandchild\n",					getpid(), ch, ch))			/* get name of recipient.  this is MAILTO if set to a			 * valid local username; USER otherwise.			 */			if (mailto) {				/* MAILTO was present in the environment				 */				if (!*mailto) {					/* ... but it's empty. set to NULL					 */					mailto = NULL;				}			} else {				/* MAILTO not present, set to USER.				 */				mailto = usernm;			}					/* if we are supposed to be mailing, MAILTO will			 * be non-NULL.  only in this case should we set			 * up the mail command and subjects and stuff...			 */			if (mailto) {				register char	**env;				auto char	mailcmd[MAX_COMMAND];				auto char	hostname[MAXHOSTNAMELEN];				(void) gethostname(hostname, MAXHOSTNAMELEN);				(void) snprintf(mailcmd, sizeof(mailcmd),				    MAILARGS, MAILCMD);				if (!(mail = cron_popen(mailcmd, "w"))) {					perror(MAILCMD);					(void) _exit(ERROR_EXIT);				}				fprintf(mail, "From: root (Cron Daemon)\n");				fprintf(mail, "To: %s\n", mailto);				fprintf(mail, "Subject: Cron <%s@%s> %s\n",					usernm, first_word(hostname, "."),					e->cmd);# if defined(MAIL_DATE)				fprintf(mail, "Date: %s\n",					arpadate(&StartTime));# endif /* MAIL_DATE */				for (env = e->envp;  *env;  env++)					fprintf(mail, "X-Cron-Env: <%s>\n",						*env);				fprintf(mail, "\n");				/* this was the first char from the pipe				 */				putc(ch, mail);			}			/* we have to read the input pipe no matter whether			 * we mail or not, but obviously we only write to			 * mail pipe if we ARE mailing.			 */			while (EOF != (ch = getc(in))) {				bytes++;				if (mailto)					putc(ch, mail);			}			/* only close pipe if we opened it -- i.e., we're			 * mailing...			 */			if (mailto) {				Debug(DPROC, ("[%d] closing pipe to mail\n",					getpid()))				/* Note: the pclose will probably see				 * the termination of the grandchild				 * in addition to the mail process, since				 * it (the grandchild) is likely to exit				 * after closing its stdout.				 */				status = cron_pclose(mail);			}			/* if there was output and we could not mail it,			 * log the facts so the poor user can figure out			 * what's going on.			 */			if (mailto && status) {				char buf[MAX_TEMPSTR];				sprintf(buf,			"mailed %d byte%s of output but got status 0x%04x\n",					bytes, (bytes==1)?"":"s",					status);				log_it(usernm, getpid(), "MAIL", buf);			}		} /*if data from grandchild*/		Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))		fclose(in);	/* also closes stdout_pipe[READ_PIPE] */	}	/* wait for children to die.	 */	for (;  children > 0;  children--)	{		WAIT_T		waiter;		PID_T		pid;		Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n",			getpid(), children))		pid = wait(&waiter);		if (pid < OK) {			Debug(DPROC, ("[%d] no more grandchildren--mail written?\n",				getpid()))			break;		}		Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x",			getpid(), pid, WEXITSTATUS(waiter)))		if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))			Debug(DPROC, (", dumped core"))		Debug(DPROC, ("\n"))	}}