v23i030: A cron/crontab replacement, Part03/03

Rich Salz rsalz at bbn.com
Wed Oct 10 23:54:04 AEST 1990


Submitted-by: Paul A Vixie <vixie at vixie.sf.ca.us>
Posting-number: Volume 23, Issue 30
Archive-name: vixie-cron/part03

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 3)."
# Contents:  do_command.c misc.c
# Wrapped by vixie at volition.pa.dec.com on Wed Jul 18 00:32:49 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'do_command.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'do_command.c'\"
else
echo shar: Extracting \"'do_command.c'\" \(14961 characters\)
sed "s/^X//" >'do_command.c' <<'END_OF_FILE'
X#if !defined(lint) && !defined(LINT)
static char rcsid[] = "$Header: do_command.c,v 2.1 90/07/18 00:23:38 vixie Exp $";
X#endif
X
X/* $Source: /jove_u3/vixie/src/cron/RCS/do_command.c,v $
X * $Revision: 2.1 $
X * $Log:	do_command.c,v $
X * Revision 2.1  90/07/18  00:23:38  vixie
X * Baseline for 4.4BSD release
X * 
X * Revision 2.0  88/12/10  04:57:44  vixie
X * V2 Beta
X * 
X * Revision 1.5  88/11/29  13:06:06  vixie
X * seems to work on Ultrix 3.0 FT1
X * 
X * Revision 1.4  87/05/02  17:33:35  paul
X * baseline for mod.sources release
X * 
X * Revision 1.3  87/04/09  00:03:58  paul
X * improved data hiding, locality of declaration/references
X * fixed a rs at mirror bug by redesigning the mailto stuff completely
X * 
X * Revision 1.2  87/03/19  12:46:24  paul
X * implemented suggestions from rs at mirror (Rich $alz):
X *    MAILTO="" means no mail should be sent
X *    various fixes of bugs or lint complaints
X *    put a To: line in the mail message
X * 
X * Revision 1.1  87/01/26  23:47:00  paul
X * Initial revision
X */
X
X/* Copyright 1988,1990 by Paul Vixie
X * All rights reserved
X *
X * Distribute freely, except: don't remove my name from the source or
X * documentation (don't take credit for my work), mark your changes (don't
X * get me blamed for your possible bugs), don't alter or remove this
X * notice.  May be sold if buildable source is provided to buyer.  No
X * warrantee of any kind, express or implied, is included with this
X * software; use at your own risk, responsibility for damages (if any) to
X * anyone resulting from the use of this software rests entirely with the
X * user.
X *
X * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
X * I'll try to keep a version up to date.  I can be reached as follows:
X * Paul Vixie, 329 Noe Street, San Francisco, CA, 94114, (415) 864-7013,
X * paul at vixie.sf.ca.us || {hoptoad,pacbell,decwrl,crash}!vixie!paul
X */
X
X
X#include "cron.h"
X#include <signal.h>
X#include <pwd.h>
X#if defined(BSD)
X# include <sys/wait.h>
X#endif /*BSD*/
X#if defined(sequent)
X# include <strings.h>
X# include <sys/universe.h>
X#endif
X
X
void
do_command(cmd, u)
X	char	*cmd;
X	user	*u;
X{
X	extern int	fork(), _exit();
X	extern void	child_process(), log_it();
X	extern char	*env_get();
X
X	Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n",
X		getpid(), cmd, env_get(USERENV, u->envp), u->uid, u->gid))
X
X	/* fork to become asynchronous -- parent process is done immediately,
X	 * and continues to run the normal cron code, which means return to
X	 * tick().  the child and grandchild don't leave this function, alive.
X	 *
X	 * vfork() is unsuitable, since we have much to do, and the parent
X	 * needs to be able to run off and fork other processes.
X	 */
X	switch (fork())
X	{
X	case -1:
X		log_it("CROND",getpid(),"error","can't fork");
X		break;
X	case 0:
X		/* child process */
X		child_process(cmd, u);
X		Debug(DPROC, ("[%d] child process done, exiting\n", getpid()))
X		_exit(OK_EXIT);
X		break;
X	}
X	Debug(DPROC, ("[%d] main process returning to work\n", getpid()))
X}
X
X
static void
child_process(cmd, u)
X	char	*cmd;
X	user	*u;
X{
X	extern struct passwd	*getpwnam();
X	extern void	sigpipe_func(), be_different(), log_it();
X	extern int	VFORK();
X	extern char	*index(), *env_get();
X
X	auto int	stdin_pipe[2], stdout_pipe[2];
X	register char	*input_data, *usernm, *mailto;
X	auto int	children = 0;
X#if defined(sequent)
X	extern void	do_univ();
X#endif
X
X	Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), cmd))
X
X	/* mark ourselves as different to PS command watchers by upshifting
X	 * our program name.  This has no effect on some kernels.
X	 */
X	{
X		register char	*pch;
X
X		for (pch = ProgramName;  *pch;  pch++)
X			*pch = MkUpper(*pch);
X	}
X
X	/* discover some useful and important environment settings
X	 */
X	usernm = env_get(USERENV, u->envp);
X	mailto = env_get("MAILTO", u->envp);
X
X#if defined(BSD)
X	/* our parent is watching for our death by catching SIGCHLD.  we
X	 * do not care to watch for our children's deaths this way -- we
X	 * use wait() explictly.  so we have to disable the signal (which
X	 * was inherited from the parent).
X	 *
X	 * this isn't needed for system V, since our parent is already
X	 * SIG_IGN on SIGCLD -- which, hopefully, will cause children to
X	 * simply vanish when they die.
X	 */
X	(void) signal(SIGCHLD, SIG_IGN);
X#endif /*BSD*/
X
X	/* create some pipes to talk to our future child
X	 */
X	pipe(stdin_pipe);	/* child's stdin */
X	pipe(stdout_pipe);	/* child's stdout */
X	
X	/* since we are a forked process, we can diddle the command string
X	 * we were passed -- nobody else is going to use it again, right?
X	 *
X	 * if a % is present in the command, previous characters are the
X	 * command, and subsequent characters are the additional input to
X	 * the command.  Subsequent %'s will be transformed into newlines,
X	 * but that happens later.
X	 */
X	if (NULL == (input_data = index(cmd, '%')))
X	{
X		/* no %.  point input_data at a null string.
X		 */
X		input_data = "";
X	}
X	else
X	{
X		/* % found.  replace with a null (remember, we're a forked
X		 * process and the string won't be reused), and increment
X		 * input_data to point at the following character.
X		 */
X		*input_data++ = '\0';
X	}
X
X	/* fork again, this time so we can exec the user's command.  Vfork()
X	 * is okay this time, since we are going to exec() pretty quickly.
X	 * I'm assuming that closing pipe ends &whatnot will not affect our
X	 * suspended pseudo-parent/alter-ego.
X	 */
X	if (VFORK() == 0)
X	{
X		Debug(DPROC, ("[%d] grandchild process VFORK()'ed\n", getpid()))
X
X		/* write a log message.  we've waited this long to do it
X		 * because it was not until now that we knew the PID that
X		 * the actual user command shell was going to get and the
X		 * PID is part of the log message.
X		 */
X#ifdef LOG_FILE
X		{
X			extern char *mkprints();
X			char *x = mkprints(cmd, strlen(cmd));
X
X			log_it(usernm, getpid(), "CMD", x);
X			free(x);
X		}
X#endif
X
X		/* get new pgrp, void tty, etc.
X		 */
X		be_different();
X
X		/* close the pipe ends that we won't use.  this doesn't affect
X		 * the parent, who has to read and write them; it keeps the
X		 * kernel from recording us as a potential client TWICE --
X		 * which would keep it from sending SIGPIPE in otherwise
X		 * appropriate circumstances.
X		 */
X		close(stdin_pipe[WRITE_PIPE]);
X		close(stdout_pipe[READ_PIPE]);
X
X		/* grandchild process.  make std{in,out} be the ends of
X		 * pipes opened by our daddy; make stderr go to stdout.
X		 */
X		close(STDIN);	dup2(stdin_pipe[READ_PIPE], STDIN);
X		close(STDOUT);	dup2(stdout_pipe[WRITE_PIPE], STDOUT);
X		close(STDERR);	dup2(STDOUT, STDERR);
X
X		/* close the pipes we just dup'ed.  The resources will remain,
X		 * since they've been dup'ed... :-)...
X		 */
X		close(stdin_pipe[READ_PIPE]);
X		close(stdout_pipe[WRITE_PIPE]);
X
X# if defined(sequent)
X		/* set our login universe.  Do this in the grandchild
X		 * so that the child can invoke /usr/lib/sendmail
X		 * without surprises.
X		 */
X		do_univ(u);
X# endif
X
X		/* set our directory, uid and gid.  Set gid first, since once
X		 * we set uid, we've lost root privledges.  (oops!)
X		 */
X		setgid(u->gid);
X# if defined(BSD)
X		initgroups(env_get(USERENV, u->envp), u->gid);
X# endif
X		setuid(u->uid);		/* you aren't root after this... */
X		chdir(env_get("HOME", u->envp));
X
X		/* exec the command.
X		 */
X		{
X			char	*shell = env_get("SHELL", u->envp);
X
X# if DEBUGGING
X			if (DebugFlags & DTEST) {
X				fprintf(stderr,
X				"debug DTEST is on, not exec'ing command.\n");
X				fprintf(stderr,
X				"\tcmd='%s' shell='%s'\n", cmd, shell);
X				_exit(OK_EXIT);
X			}
X# endif /*DEBUGGING*/
X			/* normally you can't put debugging stuff here because
X			 * it gets mailed with the command output.
X			 */
X			/*
X			Debug(DPROC, ("[%d] execle('%s', '%s', -c, '%s')\n",
X					getpid(), shell, shell, cmd))
X			 */
X
X# ifdef bad_idea
X			/* files writable by non-owner are a no-no
X			 */
X			{
X				struct stat sb;
X
X				if (0 != stat(cmd, &sb)) {
X					fputs("crond: stat(2): ", stderr);
X					perror(cmd);
X					_exit(ERROR_EXIT);
X				} else if (sb.st_mode & 022) {
X					fprintf(stderr,
X					"crond: %s writable by nonowner\n",
X						cmd);
X					_exit(ERROR_EXIT);
X				} else if (sb.st_uid & 022) {
X					fprintf(stderr,
X					"crond: %s owned by uid %d\n",
X						cmd, sb.st_uid);
X					_exit(ERROR_EXIT);
X				}
X			}
X# endif /*bad_idea*/
X
X			execle(shell, shell, "-c", cmd, (char *)0, u->envp);
X			fprintf(stderr, "execl: couldn't exec `%s'\n", shell);
X			perror("execl");
X			_exit(ERROR_EXIT);
X		}
X	}
X
X	children++;
X
X	/* middle process, child of original cron, parent of process running
X	 * the user's command.
X	 */
X
X	Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid()))
X
X	/* close the ends of the pipe that will only be referenced in the
X	 * grandchild process...
X	 */
X	close(stdin_pipe[READ_PIPE]);
X	close(stdout_pipe[WRITE_PIPE]);
X
X	/*
X	 * write, to the pipe connected to child's stdin, any input specified
X	 * after a % in the crontab entry.  while we copy, convert any
X	 * additional %'s to newlines.  when done, if some characters were
X	 * written and the last one wasn't a newline, write a newline.
X	 *
X	 * Note that if the input data won't fit into one pipe buffer (2K
X	 * or 4K on most BSD systems), and the child doesn't read its stdin,
X	 * we would block here.  the solution, of course, is to fork again.
X	 */
X
X	if (*input_data && fork() == 0) {
X		register FILE	*out = fdopen(stdin_pipe[WRITE_PIPE], "w");
X		register int	need_newline = FALSE;
X		register int	escaped = FALSE;
X		register int	ch;
X
X		Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid()))
X
X		/* close the pipe we don't use, since we inherited it and
X		 * are part of its reference count now.
X		 */
X		close(stdout_pipe[READ_PIPE]);
X
X		/* translation:
X		 *	\% -> %
X		 *	%  -> \n
X		 *	\x -> \x	for all x != %
X		 */
X		while (ch = *input_data++)
X		{
X			if (escaped) {
X				if (ch != '%')
X					putc('\\', out);
X			} else {
X				if (ch == '%')
X					ch = '\n';
X			}
X
X			if (!(escaped = (ch == '\\'))) {
X				putc(ch, out);
X				need_newline = (ch != '\n');
X			}
X		}
X		if (escaped)
X			putc('\\', out);
X		if (need_newline)
X			putc('\n', out);
X
X		/* close the pipe, causing an EOF condition.  fclose causes
X		 * stdin_pipe[WRITE_PIPE] to be closed, too.
X		 */
X		fclose(out);
X
X		Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid()))
X		exit(0);
X	}
X
X	/* close the pipe to the grandkiddie's stdin, since its wicked uncle
X	 * ernie back there has it open and will close it when he's done.
X	 */
X	close(stdin_pipe[WRITE_PIPE]);
X
X	children++;
X
X	/*
X	 * read output from the grandchild.  it's stderr has been redirected to
X	 * it's stdout, which has been redirected to our pipe.  if there is any
X	 * output, we'll be mailing it to the user whose crontab this is...
X	 * when the grandchild exits, we'll get EOF.
X	 */
X
X	Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid()))
X
X	{
X		register FILE	*in = fdopen(stdout_pipe[READ_PIPE], "r");
X		register int	ch = getc(in);
X
X		if (ch != EOF)
X		{
X			register FILE	*mail;
X			register int	bytes = 1;
X			union wait	status;
X
X			Debug(DPROC|DEXT,
X				("[%d] got data (%x:%c) from grandchild\n",
X					getpid(), ch, ch))
X
X			/* get name of recipient.  this is MAILTO if set to a
X			 * valid local username; USER otherwise.
X			 */
X			if (mailto)
X			{
X				/* MAILTO was present in the environment
X				 */
X				if (!*mailto)
X				{
X					/* ... but it's empty. set to NULL
X					 */
X					mailto = NULL;
X				}
X			}
X			else
X			{
X				/* MAILTO not present, set to USER.
X				 */
X				mailto = usernm;
X			}
X		
X			/* if we are supposed to be mailing, MAILTO will
X			 * be non-NULL.  only in this case should we set
X			 * up the mail command and subjects and stuff...
X			 */
X
X			if (mailto)
X			{
X				extern FILE	*popen();
X				extern char	*sprintf(), *print_cmd();
X				register char	**env;
X				auto char	mailcmd[MAX_COMMAND];
X				auto char	hostname[MAXHOSTNAMELEN];
X
X				(void) gethostname(hostname, MAXHOSTNAMELEN);
X				(void) sprintf(mailcmd, MAILCMD, mailto);
X				if (!(mail = popen(mailcmd, "w")))
X				{
X					perror(MAILCMD);
X					(void) _exit(ERROR_EXIT);
X				}
X				fprintf(mail, "From: root (Cron Daemon)\n");
X				fprintf(mail, "To: %s\n", mailto);
X				fprintf(mail,
X				"Subject: cron for %s@%s said this\n",
X					usernm, first_word(hostname, ".")
X				);
X				fprintf(mail, "Date: %s", ctime(&TargetTime));
X				fprintf(mail, "X-Cron-Cmd: <%s>\n", cmd);
X				for (env = u->envp;  *env;  env++)
X					fprintf(mail, "X-Cron-Env: <%s>\n",
X						*env);
X				fprintf(mail, "\n");
X
X				/* this was the first char from the pipe
X				 */
X				putc(ch, mail);
X			}
X
X			/* we have to read the input pipe no matter whether
X			 * we mail or not, but obviously we only write to
X			 * mail pipe if we ARE mailing.
X			 */
X
X			while (EOF != (ch = getc(in)))
X			{
X				bytes++;
X				if (mailto)
X					putc(ch, mail);
X			}
X
X			/* only close pipe if we opened it -- i.e., we're
X			 * mailing...
X			 */
X
X			if (mailto) {
X				Debug(DPROC, ("[%d] closing pipe to mail\n",
X					getpid()))
X				/* Note: the pclose will probably see
X				 * the termination of the grandchild
X				 * in addition to the mail process, since
X				 * it (the grandchild) is likely to exit
X				 * after closing its stdout.
X				 */
X				status.w_status = pclose(mail);
X			}
X
X			/* if there was output and we could not mail it,
X			 * log the facts so the poor user can figure out
X			 * what's going on.
X			 */
X			if (mailto && status.w_status) {
X				char buf[MAX_TEMPSTR];
X
X				sprintf(buf,
X			"mailed %d byte%s of output but got status 0x%04x\n",
X					bytes, (bytes==1)?"":"s",
X					status.w_status);
X				log_it(usernm, getpid(), "MAIL", buf);
X			}
X
X		} /*if data from grandchild*/
X
X		Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))
X
X		fclose(in);	/* also closes stdout_pipe[READ_PIPE] */
X	}
X
X#if defined(BSD)
X	/* wait for children to die.
X	 */
X	for (;  children > 0;  children--)
X	{
X		int		pid;
X		union wait	waiter;
X
X		Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n",
X			getpid(), children))
X		pid = wait(&waiter);
X		if (pid < OK) {
X			Debug(DPROC, ("[%d] no more grandchildren--mail written?\n",
X				getpid()))
X			break;
X		}
X		Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x",
X			getpid(), pid, waiter.w_status))
X		if (waiter.w_coredump)
X			Debug(DPROC, (", dumped core"))
X		Debug(DPROC, ("\n"))
X	}
X#endif /*BSD*/
X}
X
X
X#if defined(sequent)
X/* Dynix (Sequent) hack to put the user associated with
X * the passed user structure into the ATT universe if
X * necessary.  We have to dig the gecos info out of
X * the user's password entry to see if the magic
X * "universe(att)" string is present.  If we do change
X * the universe, also set "LOGNAME".
X */
X
void
do_univ(u)
X	user	*u;
X{
X	struct	passwd	*p;
X	char	*s;
X	int	i;
X	char	envstr[MAX_ENVSTR], **env_set();
X
X	p = getpwuid(u->uid);
X	(void) endpwent();
X
X	if (p == NULL)
X		return;
X
X	s = p->pw_gecos;
X
X	for (i = 0; i < 4; i++)
X	{
X		if ((s = index(s, ',')) == NULL)
X			return;
X		s++;
X	}
X	if (strcmp(s, "universe(att)"))
X		return;
X
X	(void) sprintf(envstr, "LOGNAME=%s", p->pw_name);
X	u->envp = env_set(u->envp, envstr);
X
X	(void) universe(U_ATT);
X}
X#endif
END_OF_FILE
if test 14961 -ne `wc -c <'do_command.c'`; then
    echo shar: \"'do_command.c'\" unpacked with wrong size!
fi
# end of 'do_command.c'
fi
if test -f 'misc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'misc.c'\"
else
echo shar: Extracting \"'misc.c'\" \(13932 characters\)
sed "s/^X//" >'misc.c' <<'END_OF_FILE'
X#if !defined(lint) && !defined(LINT)
static char rcsid[] = "$Header: misc.c,v 2.1 90/07/18 00:24:33 vixie Exp $";
X#endif
X
X/* vix 26jan87 [RCS has the rest of the log]
X * vix 15jan87 [added TIOCNOTTY, thanks csg at pyramid]
X * vix 30dec86 [written]
X */
X
X/* Copyright 1988,1990 by Paul Vixie
X * All rights reserved
X *
X * Distribute freely, except: don't remove my name from the source or
X * documentation (don't take credit for my work), mark your changes (don't
X * get me blamed for your possible bugs), don't alter or remove this
X * notice.  May be sold if buildable source is provided to buyer.  No
X * warrantee of any kind, express or implied, is included with this
X * software; use at your own risk, responsibility for damages (if any) to
X * anyone resulting from the use of this software rests entirely with the
X * user.
X *
X * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
X * I'll try to keep a version up to date.  I can be reached as follows:
X * Paul Vixie, 329 Noe Street, San Francisco, CA, 94114, (415) 864-7013,
X * paul at vixie.sf.ca.us || {hoptoad,pacbell,decwrl,crash}!vixie!paul
X */
X
X
X#include "cron.h"
X#include <sys/time.h>
X#include <sys/resource.h>
X#include <sys/ioctl.h>
X#include <sys/file.h>
X#include <errno.h>
X#if defined(ATT)
X# include <fcntl.h>
X#endif
X
X
void log_it(), be_different(), acquire_daemonlock();
X
X
char *
savestr(str)
X	char	*str;
X{
X	extern	int	strlen();
X	extern	char	*malloc(), *strcpy();
X	/**/	char	*temp;
X
X	temp = malloc((unsigned) (strlen(str) + 1));
X	(void) strcpy(temp, str);
X	return temp;
X}
X
X
int
nocase_strcmp(left, right)
X	char	*left;
X	char	*right;
X{
X	while (*left && (MkLower(*left) == MkLower(*right)))
X	{
X		left++;
X		right++;
X	}
X	return MkLower(*left) - MkLower(*right);
X}
X
X
int
strcmp_until(left, right, until)
X	char	*left;
X	char	*right;
X	char	until;
X{
X	register int	diff;
X
X	Debug(DPARS|DEXT, ("strcmp_until(%s,%s,%c) ... ", left, right, until))
X
X	while (*left && *left != until && *left == *right)
X	{
X		left++;
X		right++;
X	}
X
X	if (	(*left=='\0' || *left == until) 
X	    &&	(*right=='\0' || *right == until)
X	   )
X		diff = 0;
X	else
X		diff = *left - *right;
X
X	Debug(DPARS|DEXT, ("%d\n", diff))
X
X	return diff;
X}
X
X
X/* strdtb(s) - delete trailing blanks in string 's' and return new length
X */
int
strdtb(s)
X	register char	*s;
X{
X	register char	*x = s;
X
X	/* scan forward to the null
X	 */
X	while (*x)
X		x++;
X
X	/* scan backward to either the first character before the string,
X	 * or the last non-blank in the string, whichever comes first.
X	 */
X	do	{x--;}
X	while (x >= s && isspace(*x));
X
X	/* one character beyond where we stopped above is where the null
X	 * goes.
X	 */
X	*++x = '\0';
X
X	/* the difference between the position of the null character and
X	 * the position of the first character of the string is the length.
X	 */
X	return x - s;
X}
X
X
int
set_debug_flags(flags)
X	char	*flags;
X{
X	/* debug flags are of the form    flag[,flag ...]
X	 *
X	 * if an error occurs, print a message to stdout and return FALSE.
X	 * otherwise return TRUE after setting ERROR_FLAGS.
X	 */
X
X#if !DEBUGGING
X
X	printf("this program was compiled without debugging enabled\n");
X	return FALSE;
X
X#else /* DEBUGGING */
X
X	char	*pc = flags;
X
X	DebugFlags = 0;
X
X	while (*pc)
X	{
X		char	**test;
X		int	mask;
X
X		/* try to find debug flag name in our list.
X		 */
X		for (	test = DebugFlagNames, mask = 1;
X			*test && strcmp_until(*test, pc, ',');
X			test++, mask <<= 1
X		    )
X			;
X
X		if (!*test)
X		{
X			fprintf(stderr,
X				"unrecognized debug flag <%s> <%s>\n",
X				flags, pc);
X			return FALSE;
X		}
X
X		DebugFlags |= mask;
X
X		/* skip to the next flag
X		 */
X		while (*pc && *pc != ',')
X			pc++;
X		if (*pc == ',')
X			pc++;
X	}
X
X	if (DebugFlags)
X	{
X		int	flag;
X
X		fprintf(stderr, "debug flags enabled:");
X
X		for (flag = 0;  DebugFlagNames[flag];  flag++)
X			if (DebugFlags & (1 << flag))
X				fprintf(stderr, " %s", DebugFlagNames[flag]);
X		fprintf(stderr, "\n");
X	}
X
X	return TRUE;
X
X#endif /* DEBUGGING */
X}
X
X
X#if defined(BSD)
void
set_cron_uid()
X{
X	int	seteuid();
X
X	if (seteuid(ROOT_UID) < OK)
X	{
X		perror("seteuid");
X		exit(ERROR_EXIT);
X	}
X}
X#endif
X
X#if defined(ATT)
void
set_cron_uid()
X{
X	int	setuid();
X
X	if (setuid(ROOT_UID) < OK)
X	{
X		perror("setuid");
X		exit(ERROR_EXIT);
X	}
X}
X#endif
X
void
set_cron_cwd()
X{
X	extern int	errno;
X	struct stat	sb;
X
X	/* first check for CRONDIR ("/var/cron" or some such)
X	 */
X	if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
X		perror(CRONDIR);
X		if (OK == mkdir(CRONDIR, 0700)) {
X			fprintf(stderr, "%s: created\n", CRONDIR);
X			stat(CRONDIR, &sb);
X		} else {
X			fprintf(stderr, "%s: ", CRONDIR);
X			perror("mkdir");
X			exit(ERROR_EXIT);
X		}
X	}
X	if (!(sb.st_mode & S_IFDIR)) {
X		fprintf(stderr, "'%s' is not a directory, bailing out.\n",
X			CRONDIR);
X		exit(ERROR_EXIT);
X	}
X	if (chdir(CRONDIR) < OK) {
X		fprintf(stderr, "cannot chdir(%s), bailing out.\n", CRONDIR);
X		perror(CRONDIR);
X		exit(ERROR_EXIT);
X	}
X
X	/* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such)
X	 */
X	if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) {
X		perror(SPOOL_DIR);
X		if (OK == mkdir(SPOOL_DIR, 0700)) {
X			fprintf(stderr, "%s: created\n", SPOOL_DIR);
X			stat(SPOOL_DIR, &sb);
X		} else {
X			fprintf(stderr, "%s: ", SPOOL_DIR);
X			perror("mkdir");
X			exit(ERROR_EXIT);
X		}
X	}
X	if (!(sb.st_mode & S_IFDIR)) {
X		fprintf(stderr, "'%s' is not a directory, bailing out.\n",
X			SPOOL_DIR);
X		exit(ERROR_EXIT);
X	}
X}
X
X
X#if defined(BSD)
void
be_different()
X{
X	/* release the control terminal:
X	 *  get new pgrp (name after our PID)
X	 *  do an IOCTL to void tty association
X	 */
X
X	extern int	getpid(), setpgrp(), open(), ioctl(), close();
X	auto int	fd;
X
X	(void) setpgrp(0, getpid());
X
X	if ((fd = open("/dev/tty", 2)) >= 0)
X	{
X		(void) ioctl(fd, TIOCNOTTY, (char*)0);
X		(void) close(fd);
X	}
X}
X#endif /*BSD*/
X
X#if defined(ATT)
void
be_different()
X{
X	/* not being a system V wiz, I don't know if this is what you have
X	 * to do to release your control terminal.  what I want to accomplish
X	 * is to keep this process from getting any signals from the tty.
X	 *
X	 * some system V person should let me know if this works... (vixie)
X	 */
X	int	setpgrp(), close(), open();
X
X	(void) setpgrp();
X
X	(void) close(STDIN);	(void) open("/dev/null", 0);
X	(void) close(STDOUT);	(void) open("/dev/null", 1);
X	(void) close(STDERR);	(void) open("/dev/null", 2);
X}
X#endif /*ATT*/
X
X
X/* acquire_daemonlock() - write our PID into /etc/crond.pid, unless
X *	another daemon is already running, which we detect here.
X */
void
acquire_daemonlock()
X{
X	int	fd = open(PIDFILE, O_RDWR|O_CREAT, 0644);
X	FILE	*fp = fdopen(fd, "r+");
X	int	pid = getpid(), otherpid;
X	char	buf[MAX_TEMPSTR];
X
X	if (fd < 0 || fp == NULL) {
X		sprintf(buf, "can't open or create %s, errno %d", PIDFILE, errno);
X		log_it("CROND", pid, "DEATH", buf);
X		exit(ERROR_EXIT);
X	}
X
X	if (flock(fd, LOCK_EX|LOCK_NB) < OK) {
X		int save_errno = errno;
X
X		fscanf(fp, "%d", &otherpid);
X		sprintf(buf, "can't lock %s, otherpid may be %d, errno %d",
X			PIDFILE, otherpid, save_errno);
X		log_it("CROND", pid, "DEATH", buf);
X		exit(ERROR_EXIT);
X	}
X
X	rewind(fp);
X	fprintf(fp, "%d\n", pid);
X	fflush(fp);
X	ftruncate(fd, ftell(fp));
X
X	/* abandon fd and fp even though the file is open. we need to
X	 * keep it open and locked, but we don't need the handles elsewhere.
X	 */
X}
X
X/* get_char(file) : like getc() but increment LineNumber on newlines
X */
int
get_char(file)
X	FILE	*file;
X{
X	int	ch;
X
X	ch = getc(file);
X	if (ch == '\n')
X		Set_LineNum(LineNumber + 1)
X	return ch;
X}
X
X
X/* unget_char(ch, file) : like ungetc but do LineNumber processing
X */
void
unget_char(ch, file)
X	int	ch;
X	FILE	*file;
X{
X	ungetc(ch, file);
X	if (ch == '\n')
X		Set_LineNum(LineNumber - 1)
X}
X
X
X/* get_string(str, max, file, termstr) : like fgets() but
X *		(1) has terminator string which should include \n
X *		(2) will always leave room for the null
X *		(3) uses get_char() so LineNumber will be accurate
X *		(4) returns EOF or terminating character, whichever
X */
int
get_string(string, size, file, terms)
X	char	*string;
X	int	size;
X	FILE	*file;
X	char	*terms;
X{
X	int	ch;
X	char	*index();
X
X	while (EOF != (ch = get_char(file)) && !index(terms, ch))
X		if (size > 1)
X		{
X			*string++ = (char) ch;
X			size--;
X		}
X
X	if (size > 0)
X		*string = '\0';
X
X	return ch;
X}
X
X
X/* skip_comments(file) : read past comment (if any)
X */
void
skip_comments(file)
X	FILE	*file;
X{
X	int	ch;
X
X	while (EOF != (ch = get_char(file)))
X	{
X		/* ch is now the first character of a line.
X		 */
X
X		while (ch == ' ' || ch == '\t')
X			ch = get_char(file);
X
X		if (ch == EOF)
X			break;
X
X		/* ch is now the first non-blank character of a line.
X		 */
X
X		if (ch != '\n' && ch != '#')
X			break;
X
X		/* ch must be a newline or comment as first non-blank
X		 * character on a line.
X		 */
X
X		while (ch != '\n' && ch != EOF)
X			ch = get_char(file);
X
X		/* ch is now the newline of a line which we're going to
X		 * ignore.
X		 */
X	}
X	unget_char(ch, file);
X}
X
X/* int in_file(char *string, FILE *file)
X *	return TRUE if one of the lines in file matches string exactly,
X *	FALSE otherwise.
X */
int
in_file(string, file)
X	char *string;
X	FILE *file;
X{
X	char line[MAX_TEMPSTR];
X
X	/* let's be persnickety today.
X	 */
X	if (!file) {
X		if (!string)
X			string = "0x0";
X		fprintf(stderr,
X			"in_file(\"%s\", 0x%x): called with NULL file--botch",
X			string, file);
X		exit(ERROR_EXIT);
X	}
X
X	rewind(file);
X	while (fgets(line, MAX_TEMPSTR, file)) {
X		if (line[0] != '\0')
X			line[strlen(line)-1] = '\0';
X		if (0 == strcmp(line, string))
X			return TRUE;
X	}
X	return FALSE;
X}
X
X
X/* int allowed(char *username)
X *	returns TRUE if (ALLOW_FILE exists and user is listed)
X *	or (DENY_FILE exists and user is NOT listed)
X *	or (neither file exists but user=="root" so it's okay)
X */
int
allowed(username)
X	char *username;
X{
X	static int	init = FALSE;
X	static FILE	*allow, *deny;
X
X	if (!init) {
X		init = TRUE;
X#if defined(ALLOW_FILE) && defined(DENY_FILE)
X		allow = fopen(ALLOW_FILE, "r");
X		deny = fopen(DENY_FILE, "r");
X		Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny))
X#else
X		allow = NULL;
X		deny = NULL;
X#endif
X	}
X
X	if (allow)
X		return (in_file(username, allow));
X	if (deny)
X		return (!in_file(username, deny));
X
X#if defined(ALLOW_ONLY_ROOT)
X	return (strcmp(username, ROOT_USER) == 0);
X#else
X	return TRUE;
X#endif
X}
X
X
X#if defined(LOG_FILE) || defined(SYSLOG)
void
log_it(username, pid, event, detail)
X	char	*username;
X	int	pid;
X	char	*event;
X	char	*detail;
X{
X#if defined(LOG_FILE)
X	extern struct tm	*localtime();
X	extern long		time();
X	extern char		*malloc();
X	auto char		*msg;
X	auto long		now = time((long *) 0);
X	register struct tm	*t = localtime(&now);
X	static int		log_fd = -1;
X#endif /*LOG_FILE*/
X
X#if defined(SYSLOG)
X	static int		syslog_open = 0;
X#endif
X
X
X#if defined(LOG_FILE)
X	/* we assume that MAX_TEMPSTR will hold the date, time, &punctuation.
X	 */
X	msg = malloc(strlen(username)
X	      + strlen(event)
X	      + strlen(detail)
X	      + MAX_TEMPSTR);
X
X	if (log_fd < OK) {
X		log_fd = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
X		if (log_fd < OK) {
X			fprintf(stderr, "%s: can't open log file\n", ProgramName);
X			perror(LOG_FILE);
X		}
X	}
X
X	/* we have to sprintf() it because fprintf() doesn't always write
X	 * everything out in one chunk and this has to be atomically appended
X	 * to the log file.
X	 */
X	sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n",
X		username,
X		t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, pid,
X		event, detail);
X
X	/* we have to run strlen() because sprintf() returns (char*) on BSD
X	 */
X	if (log_fd < OK || write(log_fd, msg, strlen(msg)) < OK) {
X		fprintf(stderr, "%s: can't write to log file\n", ProgramName);
X		if (log_fd >= OK)
X			perror(LOG_FILE);
X		write(STDERR, msg, strlen(msg));
X	}
X
X	/* I suppose we could use alloca()...
X	 */
X	free(msg);
X#endif /*LOG_FILE*/
X
X#if defined(SYSLOG)
X	if (!syslog_open) {
X		/* we don't use LOG_PID since the pid passed to us by
X		 * our client may not be our own.  therefore we want to
X		 * print the pid ourselves.
X		 */
X# ifdef LOG_CRON
X		openlog(ProgramName, 0, LOG_CRON);
X# else
X# ifdef LOG_DAEMON
X		openlog(ProgramName, 0, LOG_DAEMON);
X# else
X		openlog(ProgramName, 0);
X# endif /*LOG_DAEMON*/
X# endif /*LOG_CRON*/
X		syslog_open = TRUE;		/* assume openlog success */
X	}
X
X	syslog(LOG_INFO, "(%s %d) %s (%s)\n",
X		username, pid, event, detail);
X
X#endif /*SYSLOG*/
X
X	if (DebugFlags) {
X		fprintf(stderr, "log_it: (%s %d) %s (%s)",
X			username, pid, event, detail);
X	}
X}
X#endif /*LOG_FILE || SYSLOG */
X
X
X/* two warnings:
X *	(1) this routine is fairly slow
X *	(2) it returns a pointer to static storage
X */
char *
first_word(s, t)
X	local char *s;	/* string we want the first word of */
X	local char *t;	/* terminators, implicitly including \0 */
X{
X	static char retbuf[2][MAX_TEMPSTR + 1];	/* sure wish I had GC */
X	static int retsel = 0;
X	local char *rb, *rp;
X	extern char *index();
X
X	/* select a return buffer */
X	retsel = 1-retsel;
X	rb = &retbuf[retsel][0];
X	rp = rb;
X
X	/* skip any leading terminators */
X	while (*s && (NULL != index(t, *s))) {s++;}
X
X	/* copy until next terminator or full buffer */
X	while (*s && (NULL == index(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
X		*rp++ = *s++;
X	}
X
X	/* finish the return-string and return it */
X	*rp = '\0';
X	return rb;
X}
X
X
X/* warning:
X *	heavily ascii-dependent.
X */
X
void
mkprint(dst, src, len)
X	register char *dst;
X	register unsigned char *src;
X	register int len;
X{
X	while (len-- > 0)
X	{
X		register unsigned char ch = *src++;
X
X		if (ch < ' ') {			/* control character */
X			*dst++ = '^';
X			*dst++ = ch + '@';
X		} else if (ch < 0177) {		/* printable */
X			*dst++ = ch;
X		} else if (ch == 0177) {	/* delete/rubout */
X			*dst++ = '^';
X			*dst++ = '?';
X		} else {			/* parity character */
X			sprintf(dst, "\\%03o", ch);
X			dst += 4;
X		}
X	}
X	*dst = NULL;
X}
X
X
X/* warning:
X *	returns a pointer to malloc'd storage, you must call free yourself.
X */
X
char *
mkprints(src, len)
X	register unsigned char *src;
X	register unsigned int len;
X{
X	extern char *malloc();
X	register char *dst = malloc(len*4 + 1);
X
X	mkprint(dst, src, len);
X
X	return dst;
X}
END_OF_FILE
if test 13932 -ne `wc -c <'misc.c'`; then
    echo shar: \"'misc.c'\" unpacked with wrong size!
fi
# end of 'misc.c'
fi
echo shar: End of archive 3 \(of 3\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.



More information about the Comp.sources.unix mailing list