cron for unix-pc file 2 of 3

Donald Lashomb donlash at uncle.UUCP
Fri Dec 29 14:14:35 AEST 1989


For all who requested my cron program:

here's part 2 of 3

-Don		donlash at uncle.UUCP

#! /bin/sh"
# -------------------- CUT HERE ----------------------"
#! /bin/sh"
# This is a shar file.  To unbundle it, remove everything"
# above including the 'CUT HERE' line using your editor."
# Then type 'sh thisfile'.  Your shell will unbundle it."
echo "extracting at.c (  20174 chars)"
sed 's/^X//' <<'SHAR_EOF' >at.c
X/*  at(1), batch(1), cronjob(1) and crtabj(1) command program  */
X
X/* SYNOPSIS:  at [-{n|N} nice] time [date] [+ increment]	<<1>>
X *            batch [-{n|N} nice]				<<1>>
X *            cronjob [-{n|N} nice] mins hrs mdays mons wdays	<<1>>
X *            crtabj [-{n|N} nice] schedule command		<<1>>
X *
X *            {at|batch|cronjob|crtabj} -r[ae] {all|jobn ...}	<<2>>
X *            {at|batch|cronjob|crtabj} -l[aefu] [jobn ...]	<<3>>
X *            {at|batch|cronjob|crtabj} -c			<<4>>
X *
X * <<1>> schedules new jobs:
X *	-n nice	specifiy nice value for job
X *	-N nice	negative nice value, su only (eg -N 20)
X *	time date increment - see parsetime.c
X *	schedule = mins hrs ... - see parsesched.c
X *
X * <<2>> -r removes jobs:
X *	-a	any jobtype (ie AT_JOB, BATCHJ, CR_JOB or CR_TAB)
X *	-e	everybody's, su only (no -e is just your jobs)
X *	all	all your jobs (or everybody's with -e for su)
X *	jobn	specify which jobs to remove by number (any for su)
X *
X * <<3>> -l list jobs:
X *	-a	any jobtype (ie AT_JOB, BATCHJ, CR_JOB or CR_TAB)
X *	-e	everybody's jobs
X *	-f	full listing incl job contents (-ef for su only)
X *	-u	user supplied part of job (-eu for su only)
X *	jobn	specify which job to list, default all
X *
X * <<4>> -c clean log:
X *	only the superuser or login cron
X *
X */
X
X/* ------------------------- NOTICE -----------------------------
X
X   at(1) batch(1) cronjob(1) crtabj(1) crontab(1) cron(1M)
X	(c) copyright April 10, 1989 by Donald Lashomb
X
X   This program is free.  Use it, modify it, copy it, give a copy
X   to a friend; I simply request the following provisions be observed:
X
X   1. My name as original author and this notice remain intact.
X   2. This program (or any modification of it) is not to be sold
X      for profit.
X   3. If this program is included in commercial products, there be
X      no charge for it.
X   4. This program must be distributed with source code.  Compiled-
X      only or binary-only distribution of this program is not allowed.
X      The administrator of any system that uses this program must have
X      full access to the source code.
X   5. If you enhance this program, discover a bug, have any comments
X      about it (or flames) please let me know.
X   
X   		Donald Lashomb
X   		Main Street
X   		Cranberry Lake, NY 12927
X
X   -------------------------------------------------------------- */
X
X#include <stdio.h>
X#include <fcntl.h>
X#include <string.h>
X#include <memory.h>
X#include <ctype.h>
X#include <sys/signal.h>
X#include <sys/dir.h>
X#include <pwd.h>
X#include <grp.h>
X#include <errno.h>
X#include "cron.h"
X#include "job.h"
X
Xextern char	**environ;
Xextern int	errno;
Xvoid		exit();
Xvoid		perror();
Xlong		ulimit();
Xlong		time();
Xchar		*strrchr();
Xint		(*signal())();
Xint		getopt();
Xextern char	*optarg;
Xextern int	optind;
Xchar		*getenv();
Xstruct passwd	*getpwnam();
Xstruct passwd	*getpwuid();
Xstruct group	*getgrgid();
Xchar		*ctime();
X
Xlong		parsetime();		/* link with parsetime.o  */
XSCHED		*parsesched();		/* link with parsesched.o */
Xlong		resched();		/* link with resched.o    */
Xextern char	jdone[];		/* \                      */
Xextern char	*jtypes[];		/* / link with job.o      */
Xint		openfifo();		/* \                      */
Xint		rdfifo();		/*  > link with fifo.o    */
Xint		wrfifo();		/* /                      */
Xstruct passwd	*login();		/* \                      */
Xint		allowed();		/* / link with allow.o    */
X
X#define BADSIG		((int (*)()) -1)
X
Xstruct dirctry {
X	ino_t	inode;
X	char	name[DIRSIZ+1];
X	};
X
X/* global variables ------------------------------------------- */
X
XJOB		jjj = {0};		/* default job buffer             */
X
Xchar		*myname;
Xint		realuid;		/* so don't have to getuid() alot */
Xstruct passwd	*userpw;		/* --> static area, be careful    */
Xstruct passwd	*cronpw;		/* satisfy linker with fifo.o     */
Xint		exval = 0;
Xint		wrjbflg = 0;
Xint		unlkflg = 0;
Xint		jnum,fnam;
Xchar		fname[DIRSIZ+1] = "";
XFILE		*dir;
Xstruct dirctry	dirln;
X
Xstatic char
Xcopyright[] = "at(1), batch(1), cronjob(1), crtabj(1) - (c)1989 D.Lashomb";
X
X
X/* misc routines ---------------------------------------------- */
X
Xvoid usage()
X	{
X	fputs("\
Xusage: at [ -{n|N} nice ] time [{ next | this }] [ date ] [ + increment ]\n\
X       batch [ -{n|N} nice ]\n\
X       cronjob [ -{n|N} nice ] minutes hours mdays months wdays\n\
X       crtabj [ -{n|N} nice ] schedule command\n\
X       { at | batch | cronjob | crtabj } -r[ae] { all | jobn ... }\n\
X       { at | batch | cronjob | crtabj } -l[aefu] [ jobn ... ]\n\
X       { at | batch | cronjob | crtabj } -c\n\
X",stderr);
X	exit(2);
X	}
X
Xvoid fatal(str)
X	char *str;
X	{
X	fprintf(stderr,"%s: can't ",myname);
X	perror(str);
X	if(wrjbflg) { wrjbflg=0; wrfifo(jnum,&jjj); }
X	if(unlkflg) unlink(fname);
X	exit(1);
X	}
X
Xvoid crash(str)
X	char *str;
X	{
X	errno = 0;
X	fatal(str);
X	}
X
Xint intrpt()
X	{
X	if((
X	signal(SIGHUP,SIG_IGN) == BADSIG) || (
X	signal(SIGINT,SIG_IGN) == BADSIG) || (
X	signal(SIGTERM,SIG_IGN) == BADSIG)
X	)
X		fatal("catch signal");
X
X	if(wrjbflg) { wrjbflg=0; wrfifo(jnum,&jjj); }
X	if(unlkflg) unlink(fname);
X	exit(1);
X	/*NOTREACHED*/
X	}
X
Xvoid deny()
X	{
X	fputs("permission denied\n",stderr);
X	exit(2);
X	}
X
Xvoid error(str)
X	char *str;
X	{
X	fprintf(stderr,"can't %s\n",str);
X	exval = 1;
X	}
X
X/* get current working directory ------------------------------ */
X/*    don't use stdio's getcwd() because of security problem    */
X/*    also you now see the reason for size+2 requirement        */
X
Xchar *getwd(buf,size)
X	char *buf;
X	int size;
X	{
X	int pfd[2];
X	int rv[1];
X
X	buf[size-1] = '\0';	/* mark end, see if overflows */
X	if(pipe(pfd) != 0)
X		return(NULL);
X	switch(fork()) {
X	case 0:
X		if((
X		close(1)	== 0)	&& (
X		dup(pfd[1])	== 1)	&& (
X		close(pfd[0])	== 0)	&& (
X		close(pfd[1])	== 0)
X		)
X		execl("/bin/pwd","pwd",NULL);
X		exit(errno);
X	case -1:
X		return(NULL);
X		}
X	if((
X	read(pfd[0],buf,(unsigned)(size-1))	> 0)		&& (
X	buf[size-1]				== '\0')	&& (
X	wait(rv)				!= -1)		&& (
X	(*rv & 0xffff)				== 0)		&& (
X	close(pfd[0])				== 0)		&& (
X	close(pfd[1])				== 0)
X	) {
X		buf[strcspn(buf,"\n")] = '\0';		/* strip \n */
X		return(buf);
X		}
X	close(pfd[0]);
X	close(pfd[1]);
X	wait((int *)0);
X	return(NULL);
X	}
X
X/* open, list and close (current) directory ------------------- */
X
Xvoid opendir()
X	{
X	if((dir=fopen(".","r")) == NULL)
X		fatal("open spool dir");
X	}
X
Xvoid closedir()
X	{
X	if(fclose(dir) != 0)
X		fatal("close spool dir");
X	}
X
Xchar *ls()
X	{
X	do {
X		if(fread(&dirln,sizeof(struct direct),1,dir) != 1) {
X			if(!feof(dir))
X				fatal("read spool dir");
X			return(NULL);
X			}
X		}
X		while((dirln.inode == 0) || (*dirln.name == '.'));
X	dirln.name[DIRSIZ] = '\0';
X	return(dirln.name);
X	}
X
X
X/* fclose job file - convienence routine ---------------------- */
X
Xvoid closejobfile(jfp)
X	FILE	*jfp;
X	{
X	if(fclose(jfp) != 0)
X		fatal("close job file");
X	}
X
X
X/* ============================================================ */
X/*                  SEND NEW JOB TO THE DAEMON                  */
X/* ------------------------------------------------------------ */
X
Xvoid addjob(msg,jtime,nicv,sched,cmd)
X	int	msg;
X	long	jtime;
X	int	nicv;
X	SCHED	*sched;
X	char	*cmd;
X	{
X	char	**envp;
X	int	envc,envz;
X	int	jfd;
X	FILE	*jfp;
X	char	*p;
X	int	c;
X	char	*shell;
X	char	wd[PATHSIZ+2];
X
X/* Get job slot ----------------------------------------------- */
X
X	if(rdfifo(jnum,&jjj) == 0)
X		crash("get job slot, try later");
X	wrjbflg = 1;
X
X/* Build job struct ------------------------------------------- */
X
X	jjj.link = MAGIC;
X	jjj.msg = msg;
X	jjj.uid = realuid;
X	jjj.gid = getgid();
X	jjj.nice = nicv;
X	jjj.umask = umask(UMASK);
X	jjj.ulimit = ulimit(1,0L);
X
X	shell = userpw->pw_shell;
X	if(getwd(wd,PATHSIZ+2) == NULL)
X		fatal("get current working dir");
X	if(cmd == NULL) cmd = "";
X
X	/*
X	 * count number of char ptrs and bytes in environment
X	 * including shell, working dir, and crtabj command
X	 */
X	envc = 3;		/* at least 3 char ptrs: shell, wd, cmd */
X	envz = 3;		/* at least 3 '\0' bytes for them       */
X	envp = environ;
X	while(++envc,((p = *envp++) != NULL))
X		while(++envz,(*p++ != '\0')) ;
X	jjj.envc = envc;
X	jjj.envz = envz+strlen(shell)+strlen(wd)+strlen(cmd);
X
X	jjj.time = jtime;
X	if((msg == CR_JOB) || (msg == CR_TAB)) {
X		memcpy(jjj.min,sched->min,60);
X		memcpy(jjj.hour,sched->hour,24);
X		memcpy(jjj.mday,sched->mday,32);
X		memcpy(jjj.mon,sched->mon,12);
X		memcpy(jjj.wday,sched->wday,7);
X		if((jjj.time = resched(jtime)) < 0L)
X			crash("- scheduling error");
X		}
X
X/* create job file and write header --------------------------- */
X
X	if(chdir(ATSPOOL) != 0)
X		fatal("cd to spool dir");
X	sprintf(fname,"%d",jjj.jobn);
X	unlkflg = 1;
X	if(((jfd=open(fname,O_WRONLY|O_CREAT|O_EXCL,J_PERM)) == -1) || 
X	   ((jfp=fdopen(jfd,"w")) == NULL))
X		fatal("open job file");
X	if(fwrite((char *)&jjj,JOBSIZ,1,jfp) != 1)
X		fatal("write job file");
X	envp = environ;
X	while((p = *envp++) != NULL)
X		while((c=putc(*p++,jfp)) != '\0')
X			if(c == EOF)
X				fatal("write job file");
X	if((
X	fputs(shell,jfp)	== EOF)	|| (
X	putc('\0',jfp)		== EOF)	|| (
X	fputs(wd,jfp)		== EOF)	|| (
X	putc('\0',jfp)		== EOF)	|| (
X	fputs(cmd,jfp)		== EOF)	|| (
X	putc('\0',jfp)		== EOF)
X	)
X		fatal("write job file");
X
X/* write user supplied part of job file ----------------------- */
X
X	fprintf(stderr,"%d\t%s",jjj.jobn,ctime(&jjj.time));
X	while((c=getchar()) != EOF)
X		if(putc(c,jfp) == EOF)
X			fatal("write job file");
X	if(!feof(stdin))
X		fatal("read stdin");
X	if(msg != CR_TAB)
X		if((putc('\n',jfp) == EOF) || (fputs(jdone,jfp) == EOF))
X			fatal("write job file");
X	closejobfile(jfp);
X	
X/* write job struct to daemon --------------------------------- */
X
X	if((jtime < (time((long *)0)-CATCHUP-GRAINULARITY)) && (msg == AT_JOB))
X		crash("do it, too late");
X
X	if(wrfifo(fnam,&jjj) == 0)
X		fatal("write FNAM fifo");
X	wrjbflg = 0;
X	if(isatty(0))
X		fprintf(stderr,"%d\t%s",jjj.jobn,ctime(&jjj.time));
X	unlkflg = 0;
X	}
X
X/* ============================================================ */
X/*          UNLINK FILE AND SEND REMOVE MSG TO DAEMON           */
X/* ------------------------------------------------------------ */
X
Xint rmvjob(msg,fnm,e,a)
X	int	msg;
X	char	*fnm;
X	int	e;
X	int	a;
X	{
X	FILE	*jfp;
X
X	if((jfp=fopen(fnm,"r")) == NULL) {
X		return(-1);
X		}
X	if(fread((char *)&jjj,JOBSIZ,1,jfp) != 1) {
X		closejobfile(jfp);
X		return(-2);
X		}
X	if((msg != jjj.msg) && (!a)) {
X		closejobfile(jfp);
X		return(-3);
X		}
X	if((realuid != jjj.uid) && (!e)) {
X		closejobfile(jfp);
X		return(-4);
X		}
X	unlink(fnm);
X
X	jjj.link = MAGIC;
X	jjj.jobn = atoi(fnm);		/* important */
X	jjj.msg = REMOVE;		/* important */
X	jjj.uid = realuid;		/* important */
X	wrfifo(fnam,&jjj);
X	closejobfile(jfp);
X	return(0);
X	}
X
X/* ============================================================ */
X/*                          LIST JOBS                           */
X/* ------------------------------------------------------------ */
X
Xvoid printlist(label,array,start,end,offset)
X	char	*label;
X	char	array[];
X	int	start;
X	int	end;
X	int	offset;
X	{
X	int	i,j;
X	int	comma;
X
X	comma = 0;
X	printf(label);
X	for(i=start;i<end;++i) {
X		if(array[i] != '\0') {
X			for(j=i+1;j<end;++j) {
X				if(array[j] == '\0') break;
X				}
X			if((j-i) == (end-start)) {
X				printf("*\n");
X				return;
X				}
X			--j;
X			if(comma) putchar(',');
X			switch(j-i) {
X			case 0:
X				printf("%d",i+offset);
X				break;
X			case 1:
X				printf("%d,%d",i+offset,j+offset);
X				break;
X			default:
X				printf("%d-%d",i+offset,j+offset);
X				}
X			comma = 1;
X			i = j;
X			}
X		}
X	putchar('\n');
X	}
X
Xint printjob(msg,fnm,e,f,a,u)
X	int	msg;
X	char	*fnm;
X	int	e,f,a,u;
X	{
X	FILE	*jfp;
X	char	*tim;
X	struct passwd *pw;
X	struct group *gr;
X	int	c,m,x,z;
X	int	i,mall,wall;
X	char	line[LINESIZ];
X
X	if((jfp=fopen(fnm,"r")) == NULL) {
X		return(-1);
X		}
X	if(fread((char *)&jjj,JOBSIZ,1,jfp) != 1) {
X		closejobfile(jfp);
X		return(-2);
X		}
X
X	m=jjj.msg;
X	if((m < MINMSG) || (m > MAXMSG)) m=MAXMSG+1;
X
X	if((msg != m) && (!a)) {
X		closejobfile(jfp);
X		return(-3);
X		}
X	if((realuid != jjj.uid) && (!e)) {
X		closejobfile(jfp);
X		return(-4);
X		}
X	if((m == CR_JOB) || (m == CR_TAB))
X		jjj.time = resched(time((long *)0));
X	if(jjj.time < 0L)
X		tim = "invalid time";
X	else {
X		tim = ctime(&jjj.time);
X		tim[strcspn(tim,"\n")] = '\0';	/* strip \n */
X		}
X	pw = getpwuid(jjj.uid);
X	gr = getgrgid(jjj.gid);
X
X	if(!(f||u)) {
X		printf("%d %s %s %s %s %d %#o %d\n",
X			jjj.jobn,
X			jtypes[m],
X			tim,
X			pw->pw_name,
X			gr->gr_name,
X			jjj.nice,
X			jjj.umask,
X			jjj.ulimit
X			);
X		closejobfile(jfp);
X		return(0);
X		}
X
X	/* f or u */
X
X	if((realuid != jjj.uid) && (realuid != 0)) {
X		closejobfile(jfp);
X		return(-5);
X		}
X
X	if(u) {
X		for(z=0;z<jjj.envz;++z) {
X			if(getc(jfp) == EOF)
X				fatal("read job file");
X			}
X		while(fgets(line,LINESIZ,jfp) != NULL)
X			if(strcmp(line,jdone) != 0)
X				fputs(line,stdout);
X		if(!feof(jfp))
X			fatal("read job file");
X		closejobfile(jfp);
X		return(0);
X		}
X
X	/* f */
X
X	printf("jobn=   %d\n",jjj.jobn);
X	printf("type=   %s\n",jtypes[m]);
X	printf("time=   %s\n",tim);
X	printf("uid=    %d %s\n",jjj.uid,pw->pw_name);
X	printf("gid=    %d %s\n",jjj.gid,gr->gr_name);
X	printf("nice=   %d\n",jjj.nice);
X	printf("umask=  %#o\n",jjj.umask);
X	printf("ulimit= %d\n",jjj.ulimit);
X
X	if((m == CR_JOB) || (m == CR_TAB)) {
X		mall = wall = 0;
X		for(i=1;i<32;++i) mall |= jjj.mday[i];
X		for(i=0;i<7;++i) wall |= jjj.wday[i];
X
X		printf("\n<<schedule>>\n");
X		printlist("minutes= ",jjj.min,0,60,0);
X		printlist("hours=   ",jjj.hour,0,24,0);
X		if((mall == 0) && (wall != 0))
X			printf("mo-days= *\n");
X		else
X			printlist("mo-days= ",jjj.mday,1,32,0);
X		printlist("months=  ",jjj.mon,0,12,1);
X		if((wall == 0) && (mall != 0))
X			printf("wk-days= *\n");
X		else
X			printlist("wk-days= ",jjj.wday,0,7,0);
X		}
X
X	printf("\n<<environment>>\n");
X	for(x=2,z=0;z<jjj.envz;++z) {
X		switch(c=getc(jfp)) {
X		case EOF:
X			fatal("read job file");
X		case '\0':
X			if(++x == jjj.envc-2)
X				printf("\n\n<<shell>>");
X			else if(x == jjj.envc-1)
X				printf("\n\n<<working dir>>");
X			else if(x == jjj.envc)
X				printf("\n\n<<command>>");
X			c = '\n';
X		default:
X			putchar(c);
X			}
X		}
X	printf("\n<<user stdin>>\n");
X	while((c=getc(jfp)) != EOF)
X		putchar(c);
X
X	if(!feof(jfp))
X		fatal("read job file");
X	closejobfile(jfp);
X	return(0);
X	}
X
X
X/* ============================================================ */
X/*                        CLEAN LOG FILE                        */
X/* ------------------------------------------------------------ */
X
Xvoid cleanlog()
X	{
X	jjj.link = MAGIC;
X	jjj.jobn = 0;
X	jjj.msg = CL_LOG;		/* important */
X	jjj.uid = realuid;		/* important */
X	wrfifo(fnam,&jjj);
X	}
X
X
X/* ============================================================ */
X/*                            MAIN()                            */
X/* ------------------------------------------------------------ */
X
Xmain(argc,argv)
X	int	argc;
X	char	**argv;
X	{
X	int	arg0;
X	char	*allowfile,*denyfile;
X	char	*p;
X	int	c;
X	int	nicv;
X	int	Nflag,nflag,rflag,eflag,lflag,fflag,aflag,cflag,uflag;
X	long	jtime,now;
X	SCHED	*sched;
X
X/* Check arg0 ------------------------------------------------- */
X
X	myname = argv[0];			/*****************************/
X	if((p=strrchr(myname,'/')) != NULL)	/* shell doesn't always make */
X		myname = p+1;			/*  argv[0] == basename !!!  */
X						/*****************************/
X	if(strcmp(myname,"at") == 0) {
X		arg0 = AT_JOB;
X		allowfile = ATALLOW;
X		denyfile = ATDENY;
X		nicv = 0;
X		}
X	else if (strcmp(myname,"batch") == 0) {
X		arg0 = BATCHJ;
X		allowfile = ATALLOW;
X		denyfile = ATDENY;
X		nicv = MAXNICE;
X		}
X	else if	(strcmp(myname,"cronjob") == 0) {
X		arg0 = CR_JOB;
X		allowfile = CRALLOW;
X		denyfile = CRDENY;
X		nicv = 0;
X		}
X	else if	(strcmp(myname,"crtabj") == 0) {
X		arg0 = CR_TAB;
X		allowfile = CRALLOW;
X		denyfile = CRDENY;
X		nicv = 0;
X		}
X	else {
X		fprintf(stderr,"%s - not recognized by at(1)\n",myname);
X		exit(2);
X		}
X
X/* Setup and check if user allowed to use this program -------- */
X
X	realuid = getuid();
X	userpw = login();
X
X	if(userpw == NULL) deny();
X	if(!allowed(userpw->pw_name,allowfile,denyfile)) deny();
X
X	if((
X	signal(SIGHUP,intrpt) == BADSIG) || (
X	signal(SIGINT,intrpt) == BADSIG) || (
X	signal(SIGTERM,intrpt) == BADSIG)
X	)
X		fatal("catch signal");
X
X	jnum = openfifo(JNUM);		/* closed by exit() */
X	fnam = openfifo(FNAM);		/* closed by exit() */
X
X	Nflag=nflag=rflag=eflag=lflag=fflag=aflag=cflag=uflag=0;
X
X/* Process command line --------------------------------------- */
X
X/*	optind = 0;					ld(1) does this */
X	while((c=getopt(argc,argv,"N:n:aceflru")) != EOF) {
X		switch(c) {
X		case 'N':
X			if(Nflag||nflag||rflag||eflag||
X			   lflag||fflag||aflag||cflag||uflag) usage();
X			if(realuid != 0) deny();
X			if(!isdigit(*optarg)) usage();
X			nicv = -(atoi(optarg));
X			Nflag = 1;
X			break;
X		case 'n':
X			if(Nflag||nflag||rflag||eflag||
X			   lflag||fflag||aflag||cflag||uflag) usage();
X			if(!isdigit(*optarg)) usage();
X			nicv = atoi(optarg);
X			nflag = 1;
X			break;
X		case 'r':
X			if(Nflag||nflag||rflag||
X			   lflag||fflag||cflag||uflag) usage();
X			rflag = 1; break;
X		case 'e':
X			if(Nflag||nflag||eflag||cflag) usage();
X			eflag = 1; break;
X		case 'l':
X			if(Nflag||nflag||rflag||lflag||cflag) usage();
X			lflag = 1; break;
X		case 'f':
X			if(Nflag||nflag||rflag||fflag||cflag) usage();
X			fflag = 1; break;
X		case 'u':
X			if(Nflag||nflag||rflag||uflag||cflag) usage();
X			uflag = 1; break;
X		case 'a':
X			if(Nflag||nflag||aflag||cflag) usage();
X			aflag = 1; break;
X		case 'c':
X			if(Nflag||nflag||rflag||eflag||
X			   lflag||fflag||aflag||cflag||uflag) usage();
X			cflag = 1; break;
X		default:
X			usage();
X			}
X		}
X
X/* {at|batch|cronjob|crtabj} -c ------------------------- <<4>> */
X
Xif(cflag) {
X	if((realuid != 0) &&
X	   (strcmp(userpw->pw_name,"cron") != 0))
X		deny();
X	cleanlog();
X	exit(exval);
X	}
X
X/* at [-{n|N} nice] time [date] [+ increment] ----------- <<1>> */
X/* batch [-{n|N} nice] ---------------------------------- <<1>> */
X/* cronjob [-{n|N} nice] mins hrs mdays mons wdays ------ <<1>> */
X/* crtabj [-{n|N} nice] schedule command ---------------- <<1>> */
X
Xif(!(rflag||eflag||lflag||fflag||aflag||uflag)) {
X	now = time((long *)0);
X	switch(arg0) {
X	case AT_JOB:
X		if((jtime = parsetime(argc,argv)) == -1L) usage();
X		addjob(AT_JOB,jtime,nicv,(SCHED *)NULL,NULL);
X		break;
X	case BATCHJ:
X		if(optind != argc) usage();
X		addjob(BATCHJ,now,nicv,(SCHED *)NULL,NULL);
X		break;
X	case CR_JOB:
X		if((sched = parsesched(argc,argv,&p)) == NULL) usage();
X		if(*p != '\0') usage();
X		addjob(CR_JOB,now,nicv,sched,NULL);
X		break;
X	case CR_TAB:
X		if((sched = parsesched(argc,argv,&p)) == NULL) usage();
X		if(*p == '\0') usage();
X		*(strrchr(p,' ')) = '\0';		/* strip last space */
X		addjob(CR_TAB,now,nicv,sched,p);
X		}
X	exit(exval);
X	}
X
X/* for all the following, cd to spool dir --------------------- */
X
X	if(chdir(ATSPOOL) != 0)
X		fatal("cd to spool dir");
X
X/* {at|batch|cronjob|crtabj} -r[ae] {all|jobn ...} ------ <<2>> */
X
X	if(rflag) {
X		if(optind == argc) usage();
X		if((eflag) && (realuid != 0)) deny();
X		if(strcmp(argv[optind],"all") == 0) {
X			if(optind != (argc-1)) usage();
X
X			opendir();
X			while((p=ls()) != NULL)
X				switch(rmvjob(arg0,p,eflag,aflag)) {
X				case -1:
X					error("open job file");
X					break;
X				case -2:
X					error("read job file");
X				}
X			closedir();
X			exit(exval);
X			}
X
X		if(realuid == 0) eflag = 1;
X		while(optind < argc) {
X			if(!isdigit(*argv[optind])) usage();
X			switch(rmvjob(arg0,argv[optind++],eflag,aflag)) {
X			case -1:
X				error("open job file");
X				break;
X			case -2:
X				error("read job file");
X				break;
X			case -3:
X				error("- not valid jobn");
X				break;
X			case -4:
X				error("- permission denied");
X				}
X			}
X		exit(exval);
X		}
X
X/* {at|batch|cronjob|crtabj} -l[aefu] [jobn ...] -------- <<3>> */
X
X	if(lflag) {
X		if(optind == argc) {
X			opendir();
X			while(p=ls())
X				switch(printjob(arg0,p,
X					eflag,fflag,aflag,uflag)) {
X				case -1:
X					error("open job file");
X					break;
X				case -2:
X					error("read job file");
X					}
X			closedir();
X			exit(exval);
X			}
X		eflag = 1;
X		while(optind < argc) {
X			if(!isdigit(*argv[optind])) usage();
X			switch(printjob(arg0,argv[optind++],
X				eflag,fflag,aflag,uflag)) {
X			case -1:
X				error("open job file");
X				break;
X			case -2:
X				error("read job file");
X				break;
X			case -3:
X				error("- not valid jobn");
X				break;
X			case -5:
X				error("- permission denied");
X				}
X			}
X		exit(exval);
X		}
X
X	/* not valid option combination */
X	usage();
X
X	/*NOTREACHED*/
X	}
SHAR_EOF
if test   20174 -ne `wc -c <at.c`
then
	echo "at.c unpacked with wrong size"
fi
echo "extracting cron.1 (   7585 chars)"
sed 's/^X//' <<'SHAR_EOF' >cron.1
X.TH CRON 1M LOCAL
X.SH NAME
Xcron \- clock daemon
X.SH SYNOPSIS
X.B /etc/daemons/cron
X[ catchup [ maxkids ]]
X.SH DESCRIPTION
XThe
X.I cron
Xdaemon executes commands at scheduled dates and times.
XBecause the
X.I cron
Xdaemon never exits, it should only be called once.
XThis is best done by running it from the
X.B /etc/rc
Xfile during the initialization process.
X.IR Cron ;
Xtogether with
X.IR at (1),
X.IR batch (1),
X.IR cronjob (1),
X.IR crtabj (1)
Xand
X.IR crontab (1);
Ximplements the
X.I "cron facility"
Xand gives you the ability to execute commands at a later time.
X.P
XUser commands are submitted to this daemon in the form of a
X.BR job .
XA
X.B job
Xconsists of a file containing vital information such as
Xa jobnumber, schedule time, user's uid, environment and the
Xactual commands that the user wants performed.
XThe daemon provides the jobnumber;
Xthe
X.IR at (1),
X.IR batch (1),
X.IR cronjob (1)
Xor
X.IR crtabj (1)
X.RI [ crontab (1)]
Xcommand provides the schedule time, uid, environment
Xand other vital information;
Xand, of course, the user provides the actual commands to be executed.
XJob files are kept in the
X.B atjobs
Xspool directory.
X.P
XWhen the time comes for a job to be executed, the daemon:
X.TP
X\(bu
Xforks off a process to run it
X.TP
X\(bu
Xrestores the user's environment
X.TP
X\(bu
Xconnects the standard input to the job file
X.TP
X\(bu
Xarranges for the standard output and standard error output to
Xbe mailed to the user
X.TP
X\(bu
Xfires up the user's shell to execute the commands
X.TP
X\(bu
Xand removes or reschedules the job.
X.P
XThe shell environment variables, current working directory,
Xumask and ulimit in effect when the job was created are preserved.
XOpen file descriptors, traps and priority are lost.
XException: the
X.B TZ
Xenvironment variable, see below.
X.P
XThe daemon keeps an in-memory list of pending jobs.
XWhen the daemon is started, it checks the spool directory to see
Xif there are any existing jobs, and rebuilds its in-memory list.
XThe jobnumber is the same as the job's filename.
XAny free jobnumbers are written into the JNUMBS fifo,
Xso the
X.IR at (1),
X.IR batch (1),
X.IR cronjob (1)
Xand
X.IR crtabj (1)
X.RI [ crontab (1)]
Xcommands can get them.
XThe daemon then goes into an endless sleep/wake cycle.
XEvery time it wakes up; it checks the FNAMES fifo to see if the
X.IR at (1),
X.IR batch (1),
X.IR cronjob (1)
Xor
X.IR crtabj (1)
X.RI [ crontab (1)]
Xcommands have submitted any new jobs,
Xchecks its in-memory list,
Xand runs any jobs that are due.
X.P
XThe daemon tries pretty hard to do the bidding of the users,
Xbut there are limits to everything!
X.P
XJobs may not execute exactly when the user has scheduled them
Xfor a number of reasons.
XThe biggest reason is because of the sleep/wake cycle of the
Xdaemon itself.
XThe length of this cycle is determined by the
X.I GRAINULARITY
Xparameter which is compiled into the daemon (usually 1 minute).
XThe daemon only looks backwards in time.
XIf it wakes up even one second before a job is scheduled,
Xit doesn't consider that job until the next cycle.
X.P
XThe
X.I CATCHUP
Xparameter is used to determine how "stale" a job can be
Xand still be executed.
XA default value is set when the daemon is compiled.
XThe default can be overridden by defining a variable,
X.BR CATCHUP ,
Xin the daemon's environment when it is started.
XThis, in turn, can be overridden by using the
X.I catchup
Xargument when starting the daemon.
XIn all cases the value is the number of seconds older than "now"
Xthat a job can be and still be considerred for execution.
XThis is useful for running jobs that are already in the
Xspool directory when the daemon starts up.
XThe action taken by the daemon for jobs older than the
X.I CATCHUP
Xparameter, depends on the type of job:
XAT_JOBs are removed.
XBATCHJobs are not affected.
XCR_JOBs and CR_TAB are rescheduled.
X.P
XThe maximum number of jobs that the daemon will run each cycle
Xis controlled by the
X.I MAXKIDS
Xparameter.
XA default value is set when the daemon is compiled.
XThe default can be overridden by defining a variable,
X.BR MAXKIDS ,
Xin the daemon's environment when it is started.
XThis, in turn, can be overridden by using the
X.I maxkids
Xargument when starting the daemon.
XIf you set this parameter too high,
Xthe kernal will impose a limit
Xbecause it will run out of processes.
XIn any case, undone jobs are pushed ahead to the next cycle.
X.P
X.IR Batch (1)
Xjobs are handled slightly differently.
XThe daemon only considers execution of, at most, one BATCHJob per cycle.
XWhereas all the other jobs that are due are possible candidates for execution.
X.bp
X.P
XIt is still possible, after the daemon has forked a process
Xto run a job, for the system to fail to execute it because the job's
Xowner is at his/her process limit.
XThe process will sleep for a few seconds and try up to
X.I MAXTRIES
Xnumber of times to execute the job.
XThis limit is compiled into the daemon.
X.SH NOTES
XThe user must have the
X.B LOGNAME
Xenvironment variable set, and it must match uid,
Xor the job will not be executed.
X.P
XThe daemon is usually compiled to include code which sets the
X.B TZ
Xenvironment variable.  If the user's job has
X.B TZ
Xdefined, then the daemon's value will replace it.
XThis kluges around a problem with daylight savings time
Xcalculation in the kernal and stardard library.
X.P
XThis daemon does not wakeup "on the minute" like some other programs.
XRather it wakes up at even intervals from when it is started.
XSo if you have other things happening "on the minute",
Xyou could start this daemon on the 30-second mark;
Xmaking the system load more even.
X.P
XThe
X.IR crontab (1)
Xcommand saves a copy of the user's crontab file in the
X.B crontabs
Xdirectory.
XWhen the daemon is started, for every CR_TAB job,
Xit checks to see if a corresponding crontab exists.
XIf not, the CR_TAB job is removed.
XThe daemon makes no other use of the crontab files.
X.P
XAn additional /usr/lib/crontab
Xfile is provided for the superuser by the
X.IR smgr (1M)
Xprogram in the UNIX-PC.
XIt has nothing to do with this program.
X.P
XAT_JOB, BATCHJ, and CR_JOB jobs are executed using
X.I shell
X.BR -s ,
Xso the job file is in essence a shell script that is fed into
Xthe shell's stdin.
XCR_TAB jobs are executed with
X.I shell
X.B -c
X.IR command ,
Xso the job file is fed into the command's stdin.
X.P
XWhen the daemon starts-up, all job files are checked for consistency.
XAny bad jobs are removed.
XA much more limitted consistency check is performed
Xwhen the job is executed.
X.bp
X.SH FILES
X.nf
X/usr/lib/cron             main cron directory
X/usr/lib/cron/at.allow    list of allowed users \e at
X/usr/lib/cron/at.deny     list of denied users  / batch
X/usr/lib/cron/cron.allow  list of allowed users \e cronjob
X/usr/lib/cron/cron.deny   list of denied users  / crontab
X/usr/spool/cron/atjobs    spool area for jobs
X/usr/spool/cron/crontabs  save area for crontabs
X/usr/local/bin/at         user command
X/usr/local/bin/batch      user command
X/usr/local/bin/cronjob    user command
X/usr/local/bin/crtabj     user command
X/usr/local/bin/crontab    user command
X/etc/daemons/cron         queues and executes jobs
X/usr/lib/cron/log         log of all daemon's actions
X/usr/lib/cron/FNAMES      FIFO from user to daemon
X/usr/lib/cron/JNUMBS      FIFO from daemon to user
X.fi
X.SH SEE ALSO
Xat(1), crontab(1), init(1M), ksh(1), mail(1), nice(1), ps(1), sh(1).
X.SH DIAGNOSTICS
XAll actions are recorded in the log file.
X.SH WARNINGS
XThis facility can only be used by logins that use the standard
XBourne shell,
X.IR sh (1)
Xor the Korn shell,
X.IR ksh (1).
X.SH BUGS
XThe owner of a job is not informed if the job can't be executed
X(eg too old) and was removed my the daemon.
X.SH AUTHOR
XDonald Lashomb  4/89
SHAR_EOF
if test    7585 -ne `wc -c <cron.1`
then
	echo "cron.1 unpacked with wrong size"
fi
echo "extracting cron.h (   3780 chars)"
sed 's/^X//' <<'SHAR_EOF' >cron.h
X/*  header file for cron(1M) facility  */
X
X/* ------------------------- NOTICE -----------------------------
X
X   at(1) batch(1) cronjob(1) crtabj(1) crontab(1) cron(1M)
X	(c) copyright April 10, 1989 by Donald Lashomb
X
X   This program is free.  Use it, modify it, copy it, give a copy
X   to a friend; I simply request the following provisions be observed:
X
X   1. My name as original author and this notice remain intact.
X   2. This program (or any modification of it) is not to be sold
X      for profit.
X   3. If this program is included in commercial products, there be
X      no charge for it.
X   4. This program must be distributed with source code.  Compiled-
X      only or binary-only distribution of this program is not allowed.
X      The administrator of any system that uses this program must have
X      full access to the source code.
X   5. If you enhance this program, discover a bug, have any comments
X      about it (or flames) please let me know.
X   
X   		Donald Lashomb
X   		Main Street
X   		Cranberry Lake, NY 12927
X
X   -------------------------------------------------------------- */
X
X#define NUMJOBS		400
X#define GRAINULARITY	((unsigned)60)
X#define CATCHUP		0
X#define MAXKIDS		10
X#define MAXTRIES	5
X#define LINESIZ		256
X#define PATHSIZ		254
X
X#if LINESIZ<PATHSIZ
X#define LINESIZ		PATHSIZ
X#endif
X
X#define ZEROSECS
X#define TZOVRIDE
X#define SET_LOGNAME
X
X/* --------------------------------------------------------------
X   FIFOs:  daemon ----JNUM----> user
X	   daemon <---FNAM----- user
X   -------------------------------------------------------------- */
X
X#ifndef DEBUG
X
X#define ATSPOOL		"/usr/spool/cron/atjobs"
X#define CRSPOOL		"/usr/spool/cron/crontabs"
X#define	JNUM		"/usr/lib/cron/JNUMBS"
X#define FNAM		"/usr/lib/cron/FNAMES"
X#define LOG		"/usr/lib/cron/log"
X#define ATDENY		"/usr/lib/cron/at.deny"
X#define ATALLOW		"/usr/lib/cron/at.allow"
X#define CRDENY		"/usr/lib/cron/cron.deny"
X#define CRALLOW		"/usr/lib/cron/cron.allow"
X
X#else
X
X#define ATSPOOL		"/u/install/Filecabinet/src/cron/spool/atjobs"
X#define CRSPOOL		"/u/install/Filecabinet/src/cron/spool/crontabs"
X#define	JNUM		"/u/install/Filecabinet/src/cron/lib/JNUMBS"
X#define FNAM		"/u/install/Filecabinet/src/cron/lib/FNAMES"
X#define LOG		"/u/install/Filecabinet/src/cron/lib/log"
X#define ATDENY		"/u/install/Filecabinet/src/cron/lib/at.deny"
X#define ATALLOW		"/u/install/Filecabinet/src/cron/lib/at.allow"
X#define CRDENY		"/u/install/Filecabinet/src/cron/lib/cron.deny"
X#define CRALLOW		"/u/install/Filecabinet/src/cron/lib/cron.allow"
X
X#endif
X
X/* --------------------------------------------------------------
X   File permissions, owners, groups:
X
X   /				root	root	drwxr-xr-x
X   /etc				root	sys	drwxr-xr-x
X   /etc/daemons			root	sys	drwxr-x---
X   /etc/daemons/cron		root	bin	-rwx------	note1
X   /usr				root	users	drwxr-xr-x
X   /usr/local			root	users	drwxr-xr-x
X   /usr/local/bin		bin	bin	drwxr-xr-x
X   /usr/local/bin/at		cron	bin	-rws--x--x	\
X   /usr/local/bin/batch		cron	bin	-rws--x--x	 \ link
X   /usr/local/bin/cronjob	cron	bin	-rws--x--x	 /
X   /usr/local/bin/crtabj	cron	bin	-rws--x--x	/
X   /usr/local/bin/crontab	root	bin	-rws--x--x	note2
X   /usr/lib			root	bin	drwxr-xr-x
X   /usr/lib/cron		cron	bin	drwx------
X   /usr/lib/cron/*		cron	other	-rw-------
X   /usr/spool			root	bin	drwxr-xr-x
X   /usr/spool/cron		cron	bin	drwx------
X   /usr/spool/cron/atjobs	cron	bin	drwx------
X   /usr/spool/cron/atjobs/*	cron	<<any>>	-rw-------
X   /usr/spool/cron/crontabs/*	root	<<any>>	-rw-------
X
X   note1: fireup the daemon once from /etc/rc, must be uid=0(root)
X   note2: must be SUID root because of bug in setuid() system call
X
X   -------------------------------------------------------------- */
X
X#define UMASK		077
X#define J_PERM		0600
X#define T_PERM		0600
X#define FIFO_PERM	0600
X#define LOG_PERM	0600
X
SHAR_EOF
if test    3780 -ne `wc -c <cron.h`
then
	echo "cron.h unpacked with wrong size"
fi
echo "extracting crontab.1 (   6586 chars)"
sed 's/^X//' <<'SHAR_EOF' >crontab.1
X.TH CRONTAB 1 LOCAL
X.SH NAME
Xcrontab \- user crontab file
X.SH SYNOPSIS
X.B crontab
X[file] 
X.br
X.B crontab
X-r
X.br
X.B crontab
X-l
X.SH DESCRIPTION
X.I Crontab
Xcopies the specified file, or standard input if no file is specified,
Xinto a directory that holds all user crontab files.
XA user crontab file
Xspecifies commands to be executed at specific dates and times by the
X.IR cron (1M)
Xdaemon.
X.I Crontab
Xgenerates CR_TAB jobs in the daemon for each command to be executed.
X.P
XUsers are permitted to use
X.I crontab
Xif their names appear in the file
X.B /usr/lib/cron/cron.allow.
XIf that file does not exist,
Xthe file
X.B /usr/lib/cron/cron.deny
Xis checked to determine if the user
Xshould be denied access to
X.I crontab.
XIf neither file exists, only root is allowed to
Xsubmit a job.
XThe allow/deny files consist of one user name
Xper line.
X.P
XA crontab file consists of lines of six fields each.
XThe fields are separated by spaces or tabs.
XThe first five specify the schedule:
X.P
X.RS 8
X.nf
X\fBmins\fR  - minutes    (0...59)
X\fBhrs\fR   - hours      (0...23)
X\fBmdays\fR - month days (1...31)
X\fBmons\fR  - months     (1...12) or (January...December)
X\fBwdays\fR - week days  (0...6)  or (Sunday...Saturday)
X.fi
X.RE
X.P
XEach of these is a pattern that may be an asterisk
X.RB ( * ),
Xmeaning all valid values;
Xor a list of elements separated by commas
X.RB ( , ).
XAn element is a number, name, or two elements separated by a dash
X.RB ( - ),
Xmeaning an inclusive range.
XOnly the first three letters of any name are needed.
XUpper or lower case letters are OK.
XNote that there are two fields which specify days
X(day of the month and day of the week).
XIf both are specified, both are adhered to; in this way:
X.P
X.RS 8
X.nf
X0 0 1 * 0 = first of every month and every Sunday.
X0 0 1 * * = only the first of every month.
X0 0 * * 0 = only on Sundays.
X0 0 * * * = every day, of course.
X.fi
X.RE
X.bp
X.P
XThe sixth field of a line in a crontab file
Xis the action that is performed at the specified times.
XPercent characters
X.RB ( % )
Xin this field (unless escaped by \fB\e\fP)
Xare translated to a new-line characters.
X(\e\e is translated to a single \e).
XThe first part of the action field (up to a % or end of line)
Xis a command that is executed by the user's shell
Xas specified in the
X.B /etc/passwd
Xfile.
XThe rest of the action field is made available to the
Xcommand as standard input.
XThe command's
Xstandard output and standard error output are
Xmailed to the user unless they are redirected elsewhere.
XThe shell environment variables, current directory,
Xumask, and ulimit are retained when the commands
Xare executed.
XOpen file descriptors, traps, and priority are lost.
X.P
XBlank lines in the crontab file are ignored.
XLines that start with a sharp
X.RB ( # )
Xcharacter are comments, except for the special
X.I "nice pragma"
Xindicator.
X.P
XThe special forms,
X.BI #-n digits
Xand
X.BI #-N digits
Xare used to set a nice value increment for the commands.
XOnce set, the nice value increment remains in effect for the rest
Xof the file until set again to a different value.
XThe form
X.BI #-N digits
Xspecifies a negative nice value increment
Xand can only used by the superuser.
X.SH OPTIONS
X.TP 8
X.B -r
XRemoves your crontab file from the crontab directory and
Xremoves all your CR_TAB jobs from the
X.IR cron (1M)
Xdaemon.
X.TP 8
X.B -l
XLists the crontab file for the invoking user.
X.SH EXAMPLE
X.RS 8
X.nf
X.sp
X#        ----- uucpadm crontab -----
X.sp
X#-n19
X56 * * * *   /usr/lib/uucp/uudemon.hr >/dev/null
X00 4 * * *   /usr/lib/uucp/uudemon.day >/dev/null
X#-n0
X30 5 * * Sun /usr/lib/uucp/uudemon.wk >/dev/null
X.fi
X.RE
X.SH HINT
XWhile enterring a crontab file "by hand" through stdin,
Xyou can press the interrupt key (DEL) to cancel it,
Xif you do it before you press cntl-D.
X.bp
X.SH NOTES
XThe user must have the
X.B LOGNAME
Xenvironment variable set properly or access will be denied.
XIn the case of the superuser,
X.B LOGNAME=root
Xwill (usually) be put into the environment if need be.
X.P
XThe
X.B TZ
Xenvironment variable, if set in the user's environment,
Xwill (usually) be replaced by the daemon's value when the
Xcommands are executed.
X.SH CAVEATS
XThe syntax of the command line and crontab file are upwardly compatable
Xwith the standard sysV version of
X.IR crontab (1).
XThe environment in which the commands run is different than
Xstandard sysV in the following ways:
X.P
XIn standard sysV the shell is invoked from your 
X.B HOME 
Xdirectory with an 
X.BR arg0 " of " sh.
X.I Cron
Xsupplies a default environment for every shell, defining
X.BR HOME ", " LOGNAME ", " SHELL(=/bin/sh) ,
Xand 
X.BR PATH(=:/bin:/usr/bin:/usr/lbin) .
X.P
XThis version restores the environment as it existed when
X.IR crontab (1)
Xwas called.
XIf you are in your HOME directory when you call
X.IR crontab (1),
Xthen it is almost the same.
XAnother difference is that this version is compatable with
X.IR ksh (1).
XIf that is your login shell, then it will be used to execute the commands.
X.P
XThis program calls
X.IR crtabj (1)
Xto communicate with the daemon - see
X.IR at (1)
Xmanual page.
X.SH FILES
X.nf
X/usr/lib/cron             main cron directory
X/usr/lib/cron/at.allow    list of allowed users \e at
X/usr/lib/cron/at.deny     list of denied users  / batch
X/usr/lib/cron/cron.allow  list of allowed users \e cronjob
X/usr/lib/cron/cron.deny   list of denied users  / crontab
X/usr/spool/cron/atjobs    spool area for jobs
X/usr/spool/cron/crontabs  save area for crontabs
X/usr/local/bin/at         user command
X/usr/local/bin/batch      user command
X/usr/local/bin/cronjob    user command
X/usr/local/bin/crtabj     user command
X/usr/local/bin/crontab    user command
X/etc/daemons/cron         queues and executes jobs
X/usr/lib/cron/log         log of all daemon's actions
X/usr/lib/cron/FNAMES      FIFO from user to daemon
X/usr/lib/cron/JNUMBS      FIFO from daemon to user
X.fi
X.SH SEE ALSO
Xat(1), cron(1M), kill(1), ksh(1), mail(1), nice(1), ps(1), sh(1).
X.SH DIAGNOSTICS
XReports various syntax errors and times that are out of range.
XExit value of 0 is returned if all OK, 1 if an error occurs but
Xsome processing occurred,
Xor 2 if processing can't even get going.
X.SH WARNINGS
XThis facility can only be used by logins that use the standard
XBourne shell,
X.IR sh (1)
Xor the Korn shell,
X.IR ksh (1).
X.SH BUGS
XThe -n or -N nice value increment is not applied to the user's
Xnice value when the job is executed, rather it applies to the
Xdaemon's value.  This is not worth fixing:
Xthe daemon and users usually have the same nice value (20).
X.P
XThe owner of a job is not informed if the job can't be executed
X(eg corrupted) and was removed my the daemon.
X.SH AUTHOR
XDonald Lashomb  4/89
SHAR_EOF
if test    6586 -ne `wc -c <crontab.1`
then
	echo "crontab.1 unpacked with wrong size"
fi
echo "extracting crontab.c (   8772 chars)"
sed 's/^X//' <<'SHAR_EOF' >crontab.c
X/*   crontab(1) user command  D.Lashomb    */
X
X/* SYNOPSIS: crontab [ file ]				<<1>>
X *           crontab -r					<<2>>
X *           crontab -l					<<3>>
X *
X * <<1>> make a new crontab file and jobs:
X *	[file]	stdin otherwise
X *
X *	syntax of a crontab file:
X *
X *	any line that starts with a # is a comment
X *	#-{n|N}nice = sets nice value, no spaces allowed
X *	<schedule> <action>
X *		<schedule> = mins hrs ... same as crtabj
X *		<action> = <command>[<cmd's stdin>]
X *			<cmd's stdin> = uses % to mark newlines
X *
X * <<2>> remove crontab file and jobs
X * <<3>> list crontab file to stdout
X *
X */
X
X/* ------------------------- NOTICE -----------------------------
X
X   at(1) batch(1) cronjob(1) crtabj(1) crontab(1) cron(1M)
X	(c) copyright April 10, 1989 by Donald Lashomb
X
X   This program is free.  Use it, modify it, copy it, give a copy
X   to a friend; I simply request the following provisions be observed:
X
X   1. My name as original author and this notice remain intact.
X   2. This program (or any modification of it) is not to be sold
X      for profit.
X   3. If this program is included in commercial products, there be
X      no charge for it.
X   4. This program must be distributed with source code.  Compiled-
X      only or binary-only distribution of this program is not allowed.
X      The administrator of any system that uses this program must have
X      full access to the source code.
X   5. If you enhance this program, discover a bug, have any comments
X      about it (or flames) please let me know.
X   
X   		Donald Lashomb
X   		Main Street
X   		Cranberry Lake, NY 12927
X
X   -------------------------------------------------------------- */
X
X/*************************************
X * security note:
X * Don't ever call system(), popen(), getcwd(), execlp(), or execvp()
X * when euid != real uid.  These functions actually call the shell to
X * do their work and, therefore, are subject to PATH and IFS tricks.
X *************************************/
X
X#include <stdio.h>
X#include <string.h>
X#include <ctype.h>
X#include <pwd.h>
X#include <sys/signal.h>
X#include <errno.h>
X#include "cron.h"
X
Xvoid		exit();
Xextern int	errno;
Xvoid		perror();
Xint		getopt();
Xextern int	optind;
Xstruct passwd	*getpwnam();
Xchar		*getenv();
X
Xint		(*signal())();
X#define BADSIG	((int (*)()) -1)
X
Xstruct passwd	*login();		/* \                   */
Xint		allowed();		/* / link with allow.o */
X
X
X/* global variables ------------------------------------------- */
X
Xint		realuid;
Xstruct passwd	*userpw;
Xchar		line[LINESIZ];
Xchar		*lin;
Xchar		fname[PATHSIZ+2];
Xchar		nicopt[LINESIZ] = "-n0";
Xchar		command[LINESIZ];
Xint		unlkflg=0;
Xint		pid=0;			/* pid of crtabj */
X
Xstatic char copyright[] = "crontab(1) - (c)1989 D.Lashomb";
X
X/* misc routines ---------------------------------------------- */
X
Xvoid usage()
X	{
X	fputs("\
Xusage: crontab [ file ]\n\
X       crontab -r\n\
X       crontab -l\n\
X",stderr);
X	exit(2);
X	}
X
Xvoid fatal(str)
X	char *str;
X	{
X	fputs("crontab: can't ",stderr);
X	perror(str);
X	if(unlkflg) {
X		if(realuid == geteuid())
X			system("crontab -r");
X		else
X			unlink(fname);
X		}
X	if(pid > 0) kill(pid,SIGTERM);
X	exit(1);
X	}
X
Xvoid crash(str)
X	char *str;
X	{
X	errno = 0;
X	fatal(str);
X	}
X
Xint intrpt()
X	{
X	if((
X	signal(SIGHUP,SIG_IGN) == BADSIG) || (
X	signal(SIGINT,SIG_IGN) == BADSIG) || (
X	signal(SIGTERM,SIG_IGN) == BADSIG)
X	)
X		fatal("catch signal");
X
X	if(unlkflg) {
X		if(realuid == geteuid())
X			system("crontab -r");
X		else
X			unlink(fname);
X		}
X	if(pid > 0) kill(pid,SIGTERM);
X	exit(2);
X	/*NOTREACHED*/
X	}
X
Xvoid deny()
X	{
X	fputs("permission denied\n",stderr);
X	exit(2);
X	}
X
Xvoid markstring(str)
X	char	**str;
X	{
X	while(isspace(*lin)) ++lin;
X	if(*lin == '\0') crash("- syntax error");
X	*str = lin;
X	while(!isspace(*lin)) ++lin;
X	*lin++ = '\0';
X	}
X
Xvoid getcommand()
X	{
X	char	*p;
X	int	c,esc;
X
X	while(isspace(*lin)) ++lin;
X	if(*lin == '\0')
X		crash("- syntax error");
X
X	p = command;
X	esc = 0;
X	while((c = *lin++) != '\0') {
X		if(esc) {
X			switch(c) {
X			case '%':
X			case '\\':
X				*p++ = c; break;
X			case '\n':
X				*p++ = '\\'; *p = '\0'; return;
X			default:
X				*p++ = '\\'; *p++ = c;
X				}
X			esc = 0;
X			}
X		else {
X			switch(c) {
X			case '%':
X				*p = '\0'; return;
X			case '\\':
X				esc = 1; break;
X			case '\n':
X				*p = '\0'; return;
X			default:
X				*p++ = c;
X				}
X			}
X		}
X	*p = '\0';
X	--lin;
X	}
X
Xvoid pputc(c,pfp)
X	int	c;
X	FILE	*pfp;
X	{
X	if(putc(c,pfp) == EOF)
X		fatal("write crtabj");
X	}
X
X/* ============================================================ */
X/*                            MAIN()                            */
X/* ------------------------------------------------------------ */
X
Xmain(argc,argv)
X	int	argc;
X	char	**argv;
X	{
X	char	*p;
X	FILE	*fp,*fp0,*pfp;
X	int	pfd[2];
X	int	lflag,rflag;
X	char	*mm,*hh,*DD,*MM,*ww;
X	int	c;
X	int	esc;
X	int	retv;
X
X
X/* Setup and check if user allowed to use this program -------- */
X
X	realuid = getuid();
X	userpw = login();
X
X	if(userpw == NULL) deny();
X	if(!allowed(userpw->pw_name,CRALLOW,CRDENY)) deny();
X
X	if((
X	signal(SIGHUP,intrpt) == BADSIG) || (
X	signal(SIGINT,intrpt) == BADSIG) || (
X	signal(SIGTERM,intrpt) == BADSIG)
X	)
X		fatal("catch signal");
X
X/* Process command line --------------------------------------- */
X
X	if(argc > 2) usage();
X	lflag=rflag=0;
X/*	optind = 0;					ld(1) does this */
X	while((c=getopt(argc,argv,"lr")) != EOF) {
X		switch(c) {
X		case 'l':
X			if(lflag||rflag) usage();
X			lflag = 1; break;
X		case 'r':
X			if(lflag||rflag) usage();
X			rflag = 1; break;
X		default:
X			usage();
X			}
X		}
X
X	sprintf(fname,"%s/%d",CRSPOOL,realuid);
X
X/* delete crontab --------------------------------------------- */
X
X	if(rflag) {
X		if((unlink(fname) != 0) && (errno != ENOENT))
X			fatal("unlink crontab");
X		if(setuid(realuid) != 0)
X			fatal("get real uid");
X		if(system("crtabj -r all") != 0)
X			exit(1);
X		exit(0);
X		}
X
X/* read crontab file ------------------------------------------ */
X
X	if(lflag) {
X		if((fp=fopen(fname,"r")) == NULL)
X			fatal("open crontab");
X		while(fgets(line,LINESIZ,fp) != NULL)
X			if(fputs(line,stdout) == EOF)
X				fatal("write stdout");
X		if(!feof(fp))
X			fatal("read crontab");
X		if(fclose(fp) != 0)
X			fatal("close crontab");
X		exit(0);
X		}
X
X/* make new crontab file and jobs ----------------------------- */
X
X	/* test if already a crontab file */
X
X	if((fp=fopen(fname,"r")) != NULL) {
X		fputs("crontab: you already have a crontab\n",stderr);
X		if(fclose(fp) != 0)
X			fatal("close crontab");
X		exit(2);
X		}
X
X	/* creat crontab file and open input file */
X
X	unlkflg=1;
X	if((fp=fopen(fname,"w")) == NULL)
X		fatal("open crontab");
X	if(chmod(fname,T_PERM) != 0)
X		fatal("chmod crontab");
X	if(setuid(realuid) != 0)
X		fatal("get real uid");
X	if(argc == 1)
X		fp0 = stdin;
X	else
X		if((fp0=fopen(argv[optind],"r")) == NULL)
X			fatal("open input file");
X
X	/* process input file, write crontab file */
X
X	while(fgets(line,LINESIZ,fp0) != NULL) {
X		if(fputs(line,fp) == EOF)
X			fatal("write crontab");
X
X		lin = line;
X		while(isspace(*lin)) ++lin;
X		switch(*lin) {
X
X		case '\0':
X			continue;
X
X		case '#':
X			if((strncmp(lin,"#-n",3) == 0) ||
X			   (strncmp(lin,"#-N",3) == 0)) {
X				++lin;
X				p = nicopt;
X				while(!isspace(*lin)) *p++ = *lin++;
X				*p = '\0';
X				}
X			continue;
X
X		default:
X			/* call crtabj */
X
X			markstring(&mm);
X			markstring(&hh);
X			markstring(&DD);
X			markstring(&MM);
X			markstring(&ww);
X			getcommand();
X
X/*********************
X * can't use popen() because:
X * #1 shell interprets wild card char.s like *
X * #2 doesn't return exit value of crtabj
X *********************/
X
X			if(pipe(pfd) != 0)
X				fatal("open pipe to crtabj");
X			switch(pid=fork()) {
X			case 0:
X				if((
X				close(0)	== 0) && (
X				dup(pfd[0])	== 0) && (
X				close(pfd[0])	== 0) && (
X				close(pfd[1])	== 0)
X				)
X					execlp("crtabj","crtabj",
X						nicopt,mm,hh,DD,MM,ww,command);
X				exit(5);
X			case -1:
X				fatal("fork crtabj");
X			default:
X				if(close(pfd[0]) != 0)
X					fatal("close pipe");
X				}
X
X			if((pfp=fdopen(pfd[1],"w")) == NULL)
X				fatal("pipe to crtabj");
X
X			/* pipe rest of line to it */
X
X			esc = 0;
X			while((c = *lin++) != '\0') {
X				if(esc) {
X					switch(c) {
X					case '%':
X						pputc('%',pfp); break;
X					case '\\':
X						pputc('\\',pfp); break;
X					default:
X						pputc('\\',pfp); pputc(c,pfp);
X						}
X					esc = 0;
X					}
X				else {
X					switch(c) {
X					case '%':
X						pputc('\n',pfp); break;
X					case '\\':
X						esc = 1; break;
X					default:
X						pputc(c,pfp);
X						}
X					}
X				}
X			if(fclose(pfp) != 0)
X				fatal("close pipe to crtabj");
X			if(wait(&retv) != pid)
X				fatal("wait");
X			pid = 0;
X			if((retv & 0xffff) != 0)
X				crash("- syntax error");
X			}
X			/* end switch */
X		}
X		/* end while */
X
X	if(!feof(fp0))
X		fatal("read input file");
X	if(fclose(fp) != 0)
X		fatal("close crontab");
X	if(fclose(fp0) != 0)
X		fatal("close input file");
X	exit(0);
X	/*NOTREACHED*/
X	}
SHAR_EOF
if test    8772 -ne `wc -c <crontab.c`
then
	echo "crontab.c unpacked with wrong size"
fi



More information about the Comp.sys.att mailing list