cron(1m) facility incl at(1) batch(1) crontab(1) part 2 of 4

Donald Lashomb donlash at uncle.uucp
Mon Jan 14 10:01:14 AEST 1991


---- Cut Here and unpack ----
#!/bin/sh
# This is part 02 of a multipart archive
if touch 2>&1 | fgrep '[-amc]' > /dev/null
 then TOUCH=touch
 else TOUCH=true
fi
# ============= at.1 ==============
if test X"$1" != X"-c" -a -f 'at.1'; then
	echo "File already exists: skipping 'at.1'"
else
echo "x - extracting at.1 (Text)"
sed 's/^X//' << 'SHAR_EOF' > at.1 &&
X.TH AT 1 LOCAL
X.SH NAME
Xat, batch, cronjob, crtabj \- run commands at a later time
X.SH SYNOPSIS
X.B at
X[-{n|N} nice] time [{next|this}] [date] [+ increment]
X.br
X.B batch
X[-{n|N} nice]
X.br
X.B cronjob
X[-{n|N} nice] mins hrs mdays mons wdays
X.br
X.B crtabj
X[-{n|N} nice] mins hrs mdays mons wdays command
X.br
X{\fBat\fR|\fBbatch\fR|\fBcronjob\fR|\fBcrtabj\fR}
X-r[ae] {all|jobn ...}
X.br
X{\fBat\fR|\fBbatch\fR|\fBcronjob\fR|\fBcrtabj\fR}
X-x[ae] {all|jobn ...}
X.br
X{\fBat\fR|\fBbatch\fR|\fBcronjob\fR|\fBcrtabj\fR}
X-l[aefu] [all|jobn ...]
X.br
X{\fBat\fR|\fBbatch\fR|\fBcronjob\fR|\fBcrtabj\fR}
X-c
X.P
X.SH DESCRIPTION
X.IR At (1),
X.IR batch (1),
X.IR cronjob (1),
Xand
X.IR crtabj (1)
Xall allow you to enter commands that will be executed at a later time.
XWith
X.I at
Xyou specify the time and enter the commands you want executed through stdin.
XThese comands will be executed (once) when the time comes.
X.I Batch
Xworks like
X.I at
Xexcept the system picks the time.
XYou enter the commands you want executed through stdin,
Xand they will be executed (once) with minimal load on the system.
X.I Cronjob
Xallows you execute commands on a repetitve basis.
XYou enter the commands you want executed through stdin,
Xand the system executes them according to the schedule you provide.
X.I Crtabj
Xalso works repetitively but only executes a single command.
XAny input thru stdin is passed to the command's stdin when it is executed.
X.P
XThis information is saved by the
X.I "cron facility"
Xand assigned a job number which is reported to the user.
XWhen the time comes, the job will be executed by the shell specified
Xfor the user in the
X.B /etc/passwd
Xfile.
XThe job'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
XUsers are permitted to use
X.I at
Xand
X.I batch
Xif their name appears in the file
X.BI /usr/lib/cron/at.allow .
XIf that file does not exist,
Xthe file
X.B /usr/lib/cron/at.deny
Xis checked to determine if the user
Xshould be denied access to them.
XIf neither file exists, only the superuser is allowed to
Xsubmit a job.
XSimilarly, the use of
X.I cronjob
Xand
X.I crtabj
Xis controlled by the files
X.B /usr/lib/cron/cron.allow
Xand
X.BR /usr/lib/cron/cron.deny .
XThe allow/deny files consist of one user name
Xper line.
XIt should be noted that
X.IR at ,
X.IR batch ,
X.IR cronjob ,
Xand
X.I crtabj
Xcheck the real uid, not the effective uid of the user.
X.bp
X.P
X.IR At (1),
X.IR batch (1),
X.IR cronjob (1),
Xand
X.IR crtabj (1)
Xreport the job number and scheduled execution time on standard error.
X.P
XThe user can specify the nice value increment
Xto use when the job is executed by using the
X.B -n
Xoption.
XThe superuser can specify the
X.B -N
Xoption, in which case the
X.I nice
Xparameter is interpreted as negative.
X.P
X.ce
X.B at
X.ce
X.B ==========
XThe
X.I time
Xmay be specified as 1, 2, 3 or 4 digits.
XOne and two digit numbers are taken to be hours,
Xthree or four digits
Xto be hours and minutes.
XThe time may alternately be specified as two numbers
Xseparated by a colon, meaning
X.IR hour : minute .
XA suffix
X.B am
Xor
X.B pm
Xmay be appended;
Xotherwise a 24-hour clock time is understood.
XThe suffix
X.B zulu
Xmay be used to indicate GMT.
XA space between the number and the suffix is OK.
XThe special names
X.BR noon ,
X.BR midnight ,
Xand
X.B now
Xare also recognized.
X.P
XOptionally the word
X.B next
Xor
X.B this
Xcan be used in lieu of
X.I time
Xor in addition to it.
XIf used in place of
X.IR time ,
Xthe current clock time is assumed (ie
X.BR now ).
X.P
XAn optional
X.I date
Xmay be specified as either a day of the week,
Xor a month name followed by a day number (and possibly year number).
XA year number must be 4 digits and is separated from the day number
Xby a comma, a space or, a comma and a space.
XTwo special days,
X.B today
Xand
X.B tomorrow
Xare recognized.
XIf no
X.I date
Xis given,
X.B today
Xis assumed if the given
X.I time
Xis greater than the current time and
X.B tomorrow
Xis assumed if it is less.
XIf the given day of the week is less than the current day of the week,
Xnext week is assumed.
XIf the given month is less than the current month (and no year is
Xgiven), next year is assumed.
XPreceeding the
X.I date
Xwith
X.B next
Xor
X.B this
Xwill override these assumptions.
X.P
XThe optional
X.I increment
Xmust be preceeded by a
X.B +
X(plus sign) and
Xis simply
Xa number
Xsuffixed by one of the following:
X.BR minutes ,
X.BR hours ,
X.BR days ,
X.BR weeks ,
X.BR months ,
Xor
X.BR years .
XSpaces between the plus sign, number and suffix are optional.
X.P
XAdditional comments:
XOnly the first three letters of any word are needed.
XUpper or lower case letters are OK.
XThe following words are also recognized:
X.BR nxt ,
X.BR hrs ,
X.BR wks ,
X.BR mos ,
Xand
X.BR yrs .
XIf you use
X.B next
Xor
X.BR this ,
Xyou must specify a
X.IR date .
XYou can't use a year number,
X.BR today ,
Xor
X.B tomorrow
Xwith
X.B next
Xor
X.BR this .
X.B "Next Wed"
Xmeans the Wednesday of next week.
XLikewise
X.B "this Apr 19"
Xmeans April 19th of this year.
X.bp
X.P
XThus valid
X.I at
Xcommands include:
X.P
X.RS 8
X.nf
Xat 0815am Jan 24
Xat 8:15am Jan 24, 1990
Xat now + 1 day
Xat now tomorrow
Xat 5 pm next Friday
Xat 1700 this Fri +1week
X.fi
X.RE
X.P
X.ce
X.B batch
X.ce
X.B ==========
X.I Batch
Xsubmits a batch job to the facility.
XIt is almost equivalent to
X.BR "at now" .
XThe main difference is that the system only starts execution
Xon one batch job at a time.
XAn additional difference is that
X.I batch
Xdefaults to the maximum nice value if the
X.B -n
Xor
X.B -N
Xoption is not used.
X.P
X.ce
X.B cronjob
X.ce
X.B ==========
XA
X.I cronjob
Xschedule consists of 5 fields:
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 = 1st of every month and also every Sun.
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.P
XYou should note that an asterisk is a special character to the shell,
Xso you should protect it from the shell with quotes.
XYou can enclose the entire schedule in quotes or individual fields,
Xhowever you want.
X.P
XThus valid
X.I cronjob
Xcommands include:
X.P
X.RS 8
X.nf
Xcronjob '30 5 * * Sun'
Xcronjob '5,10,15,20,25,30 * * * *'
Xcronjob 00 7-12,13-17 '* *' mon-FRI
Xcronjob 0 0 1 January \e*
X.fi
X.RE
X.bp
X.P
X.ce
X.B crtabj
X.ce
X.B ==========
X.I Crtabj
Xuses the same scheduling format as
X.IR cronjob .
XIn addition the
X.B command
Xto be executed is stated on the command line.
XI/O redirection can be applied to the
X.B command
Xif it is protected from the shell with quotes.
XLike the scheduling fields, the
X.BR command ,
Xits arguments, and I/O redirection can be separately quoted or
Xthe whole thing enclosed in quotes.
X.P
XValid
X.I crtabj
Xcommand lines include:
X.P
X.RS 8
X.nf
Xcrtabj '30 5 * * Sun echo hello world >/dev/tty0'
Xcrtabj '5,10,15,20,25,30 * * * *' 'who >>online'
Xcrtabj 00 7-17 '* *' 1-5 uudeamon.hr '>/dev/null'
Xcrtabj 0 0 1 January \e* date \e> `tty`
X.fi
X.RE
X.P
XNote:
X.I crtabj
Xis the underlying command for
X.IR crontab (1).
XIt would not usually be called directly by the user.
XSee
X.IR crontab (1)
Xfor more information.
X.SH OPTIONS
X.sp
X.ce
X.B scheduling jobs
X.TP 8
X.BI -n " nice"
XSpecifies the nice value increment to apply to the job when
Xit is executed.
X.TP 8
X.BI -N " nice"
XSpecifies a negative nice value increment to apply to the job
Xwhen it is executed.
XThis option can only be used by the superuser.
X.P
X.ce
X.B "removing jobs (-r)"
X.TP 8
X.B -r
XRemoves jobs.
XYou can specify which jobs to remove by either
X.IR jobn ,
Xwhich is the job number given to you previously by the
X.IR at ,
X.IR batch ,
X.IR cronjob ,
Xor
X.I crtabj
Xcommand;
Xor by using the word
X.BR all ,
Xmeaning all the jobs (of the corresponding type) belonging to you.
XYou can only remove your own jobs unless you are the superuser.
X.TP 8
X.B -a
XAny job type.  Without this option;
X.I at
Xonly looks for AT_JOBs,
X.I batch
Xonly looks for BATCHJobs,
X.I cronjob
Xonly looks for CR_JOBs,
Xand
X.I crtabj
Xonly looks for CR_TAB jobs.
X.TP 8
X.B -e
XEverybody.
XSo,
X.B "at -re all"
Xremoves all the AT_JOBs if you are the superuser.
X.bp
X.P
X.ce
X.B "executing jobs (-x)"
X.TP 8
X.B -x
XExecutes existing jobs.
XThis option allows you to kick off jobs "now",
Xinstead of waiting for their originally scheduled execution time.
XAT_JOBs are run, then removed.
XCR_JOBs and CR_TAB jobs are run and the original schedule resumed.
XThe usefulness of this option with BATCHJobs is nil.
XYou can specify which jobs to execute by either
X.IR jobn ,
Xwhich is the job number given to you previously by the
X.IR at ,
X.IR batch ,
X.IR cronjob ,
Xor
X.I crtabj
Xcommand;
Xor by using the word
X.BR all ,
Xmeaning all the jobs (of the corresponding type) belonging to you.
XYou can only execute your own jobs unless you are the superuser.
X.TP 8
X.B -a
XAny job type.  Without this option;
X.I at
Xonly looks for AT_JOBs,
X.I batch
Xonly looks for BATCHJobs,
X.I cronjob
Xonly looks for CR_JOBs,
Xand
X.I crtabj
Xonly looks for CR_TAB jobs.
X.TP 8
X.B -e
XEverybody.
XSo,
X.B "at -xe all"
Xexecutes all the AT_JOBs if you are the superuser.
X.P
X.ce
X.B "listing jobs (-l)"
X.TP 8
X.B -l
XListing jobs.
XDefaults to one job per line
Xshowing jobnumber, type of job (AT_JOB, BATCHJ, CR_JOB, or CR_TAB),
Xscheduled execution time (next scheduled time for
X.I cronjob
Xand
X.I crtabj
Xjobs),
Xuser's name, user's group, nice value, umask value and ulimit value.
XIf you don't specify any
X.I jobn
Xarguments
Xor if you use the word
X.BR all ,
Xlisting will default to all of the
X.RI ( at ,
X.IR batch ,
X.IR cronjob ,
Xor
X.IR crtabj )
Xjobs that belong to you.
X.TP 8
X.B -a
XAny job type.  Without this option;
X.I at
Xonly looks for AT_JOBs,
X.I batch
Xonly looks for BATCHJobs,
X.I cronjob
Xonly looks for CR_JOBs,
Xand
X.I crtabj
Xonly looks for CR_TAB jobs.
X.TP 8
X.B -e
XEverybody.
XSo,
X.B "at -le all"
Xlists everybody's AT_JOBs.
X.TP 8
X.B -f
XFull listing.  Displays a full multi-line listing of jobs;
Xcomplete with header, environment and user commands.
XOnly your own jobs, unless you are the superuser.
X.TP 8
X.B -u
XUser commands.  This option displays user supplied part of a job
X(ie. that part that was originally enterred through stdin).
XOnly your own jobs, unless you are the superuser.
X.P
X.ce
X.B "clean the log file (-c)"
X.TP 8
X.B -c
XThis option can only be used by the superuser or login cron.
XIt tells the daemon to clean its log file.
X.SH HINTS
XYou should check the scheduled execution time reported to you
Xto make sure that it is what you want.
XYou can press the interrupt key (DEL), to cancel the job
Xwhile you enterring commands, if you do it before you press cntl-D.
X.SH EXAMPLES
XThe
X.IR at ,
X.IR batch ,
Xand
X.I cronjob
Xcommands read from standard input to get the commands to be executed
Xat a later time.
X.IR Sh (1)
Xand
X.IR ksh (1)
Xprovide various ways of specifying standard input.
XWithin your commands, it may be useful to redirect standard output.
XThis sequence can be used at a terminal:
X.sp
X.nf
X.in +1i
Xbatch
Xnroff \fIfilename\fP >\fIoutfile\fP
X<control-d> (hold down control and press d)
X.in -1i
X.sp
X.fi
XThis sequence, which demonstrates redirecting standard
Xerror to a pipe, is useful in a shell procedure (the sequence of
Xoutput redirection specifications is significant):
X.sp
X.nf
X.in +1i
Xbatch <<!
Xnroff \fIfilename\fP 2>&1 >\fIoutfile\fP | mail \fIotherlogin\fP
X!
X.in -1i
X.fi
X.sp
XHad you wanted to mail errors to yourself, the redirection and pipe to
X.I mail
Xwould be unnecessary because
X.I batch
Xautomatically mails stdout and stderr to you.
X.sp
X.fi
XTo have a job reschedule itself, or another job, invoke
X.I at
Xfrom within the shell procedure.
XFirst prepare a
X.I shellfile
Xsimilar to the following:
X.sp
X.nf
X.in +1i
Xecho "Time to go home" > /dev/\fImytty\fR
Xat 5 pm tomorrow < \fIshellfile\fR
X.in -1i
X.sp
XAnd then kick it off by typing:
X.sp
X.in +1i
Xat 5pm today < \fIshellfile\fR
X.sp
X.in -1i
X.fi
XAn even better way is to use
X.I cronjob
Xfrom the terminal like this:
X.sp
X.nf
X.in +1i
Xcronjob '00 17 * * Mon-Fri'
Xecho "Time to go home" > /dev/\fImytty\fR
X<control-d>
X.in -1i
X.fi
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 job is executed.
X.SH CAVEATS
XThe shell which runs the commands saved by
X.IR at ,
X.IR batch ,
Xand
X.I cronjob
Xreads these commands from a file with the shell's stdin
Xconnected to this file.
XIf one of the commands also reads stdin,
Xit could mess up the shell.
XThis problem does not occur with
X.I crtabj
Xwhere the command's stdin is supposed to read it.
X.P
XThe syntax of the command line is probably upwardly compatable with the
Xstandard sysV version of
X.IR at (1),
Xbut I don't guarantee it.
XUnlike the standard sysV version, this version will allow you to do
X.BR "at now" .
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
Xcron(1M), crontab(1), 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
Xprocessing continued (like trying to remove another user's job),
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 too old) and was removed my the daemon.
X.SH AUTHOR
XDonald Lashomb  4/89, 10/90
SHAR_EOF
$TOUCH -am 1018215890 at.1 &&
chmod 0644 at.1 ||
echo "restore of at.1 failed"
set `wc -c at.1`;Wc_c=$1
if test "$Wc_c" != "15142"; then
	echo original size 15142, current size $Wc_c
fi
fi
# ============= at.c ==============
if test X"$1" != X"-c" -a -f 'at.c'; then
	echo "File already exists: skipping 'at.c'"
else
echo "x - extracting at.c (Text)"
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] mins hrs mdays mons wdays cmd	<<1>>
X *
X *            {at|batch|cronjob|crtabj} -r[ae] {all|jobn ...}		<<2>>
X *            {at|batch|cronjob|crtabj} -x[ae] {all|jobn ...}		<<3>>
X *            {at|batch|cronjob|crtabj} -l[aefu] [all|jobn ...]		<<4>>
X *            {at|batch|cronjob|crtabj} -c				<<5>>
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>> -x executes queued 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 * <<4>> -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 *	all	all your jobs (or everybody's with -e) [default]
X *	jobn	specify which job to list, default all
X *
X * <<5>> -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	(c) copyright October 16, 1990 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 <pwd.h>
X#include <grp.h>
X#include <errno.h>
X#include <sys/dir.h>
X#include "cron.h"
X#include "job.h"
X
X#define BADSIG		((int (*)()) -1)
X
Xextern char		**environ;
Xextern int		errno;
Xextern void		exit();
Xextern void		perror();
Xextern long		ulimit();
Xextern long		time();
Xextern char		*strrchr();
Xextern int		(*signal())();
Xextern int		getopt();
Xextern char		*optarg;
Xextern int		optind;
Xextern char		*getenv();
Xextern struct passwd	*getpwnam();
Xextern struct passwd	*getpwuid();
Xextern struct group	*getgrgid();
Xextern char		*ctime();
Xextern long		strtol();
X
Xextern long		parsetime();		/* link with parsetime.o  */
Xextern SCHED		*parsesched();		/* link with parsesched.o */
Xextern long		resched();		/* link with resched.o    */
Xextern char		*jtypes[];		/* link with job.o        */
Xextern int		openfifo();		/* \                      */
Xextern int		rdfifo();		/*  > link with fifo.o    */
Xextern int		wrfifo();		/* /                      */
Xextern int		realuid;		/* \                      */
Xextern struct passwd	*userpw;		/*  \ link with login.o   */
Xextern void		deny();			/*  /                     */
Xextern void		login();		/* /                      */
Xextern char		*getwd();		/* link with getwd.o      */
Xextern void		opendir();		/* \                      */
Xextern void		closedir();		/*  > link with dir.o     */
Xextern char		*ls();			/* /                      */
X
X
X/* global variables ------------------------------------------- */
X
XJOB		jjj = {0};			/* default job buffer     */
X
X
X/* static variables ------------------------------------------- */
X
Xstatic char		*myname;
Xstatic int		exval = 0;
Xstatic int		wrjbflg = 0;
Xstatic int		unlkflg = 0;
Xstatic int		jnum,fnam;
Xstatic char		fname[DIRSIZ+1] = "";
Xstatic char
Xcopyright[] = "at(1), batch(1), cronjob(1), crtabj(1) - (c)1989,1990 D.Lashomb";
X
X
X/* global routines -------------------------------------------- */
X
Xvoid fatal(str)
X	char *str;
X	{
X	int oerrno = errno;
X
X	fprintf(stderr,"%s: can't ",myname);
X	errno = oerrno;
X	perror(str);
X	if(wrjbflg) { wrjbflg=0; wrfifo(jnum); }
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
X
X/* static routines -------------------------------------------- */
X
Xstatic void 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 ] mins hrs mdays mons wdays\n\
X       crtabj [ -{n|N} nice ] mins hrs mdays mons wdays command\n\
X       { at | batch | cronjob | crtabj } -r[ae] { all | jobn ... }\n\
X       { at | batch | cronjob | crtabj } -x[ae] { all | jobn ... }\n\
X       { at | batch | cronjob | crtabj } -l[aefu] [ all | jobn ... ]\n\
X       { at | batch | cronjob | crtabj } -c\n\
X",stderr);
X	exit(2);
X	}
X
Xstatic int 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); }
X	if(unlkflg) unlink(fname);
X	exit(1);
X	/*NOTREACHED*/
X	}
X
Xstatic void error(str)
X	char *str;
X	{
X	fprintf(stderr,"can't %s\n",str);
X	exval = 1;
X	}
X
X/* fclose job file - convienence routine ---------------------- */
X
Xstatic void 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
Xstatic void addjob(msg,jtime,nicv,sched,cmd)
X	int	msg;
X	long	jtime;
X	int	nicv;
X	SCHED	*sched;
X	char	*cmd;
X	{
X	register char	**envp;
X	register int	envc,envz;
X	register char	*p;
X	register int	c;
X	int	jfd;
X	FILE	*jfp;
X	char	*shell;
X	char	wd[PATHSIZ];
X
X/* Get job slot ----------------------------------------------- */
X
X	if(rdfifo(jnum) == 0)			/* sets jjj.jnum */
X		crash("get job slot, try later");
X	wrjbflg = 1;
X
X/* Build job struct ------------------------------------------- */
X
X	/*	I do the time schedule part of the job struct first,
X	 *	so I can display the time on stderr quickly.  This
X	 *	makes it *appear* more responsive to the user.
X	 */
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	fprintf(stderr,"%d\t%s",jjj.jobn,ctime(&jjj.time));
X
X	jjj.link = NOMAGIC;		/* \                         */
X	jjj.envc = 0;			/*  > mark file not complete */
X	jjj.envz = 0;			/* /                         */
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) == NULL)
X		fatal("get current working dir");
X	if(cmd == NULL) cmd = "";
X
X/* create job file and write partially completed header ------- */
X
X	if(chdir(ATSPOOL) != 0)
X		fatal("cd to spool dir");
X	sprintf(fname,"%d",jjj.jobn);		/* fname[] > ascii 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
X/* write environment, etc strings to the job file ------------- */
X
X	/*
X	 * count number of char ptrs and bytes in environment
X	 * including shell, working dir, and crtabj command
X	 * and write this stuff to the job file
X	 */
X	envc = 3;		/* at least 4 char ptrs: shell, wd, cmd, NULL */
X	envz = 3;		/* at least 3 '\0' bytes for them             */
X	envp = environ;
X	while(++envc,((p = *envp++) != NULL))
X		while(++envz,((c=putc(*p++,jfp)) != '\0'))
X			if(c == EOF)
X				fatal("write job file");
X	jjj.envc = envc;
X	jjj.envz = envz+strlen(shell)+strlen(wd)+strlen(cmd);
X
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	while((c=getchar()) != EOF)
X		if(putc(c,jfp) == EOF)
X			fatal("write job file");
X	if(!feof(stdin))
X		fatal("read stdin");
X
X/* mark job done with MAGIC, (re)write completed header ------- */
X
X	jjj.link = MAGIC;
X	rewind(jfp);
X	if(fwrite((char *)&jjj,JOBSIZ,1,jfp) != 1)
X		fatal("write job file");
X	closejobfile(jfp);
X	
X/* send message to the daemon thru the fifo ------------------- */
X
X	if((jtime < (time((long *)0)-CATCHUP-GRAINULARITY)) && (msg == AT_JOB))
X		crash("do it, too late");
X
X	if(wrfifo(fnam) == 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
Xstatic int rmvjob(msg,fnm,e,a)
X	int	msg;
X	char	*fnm;
X	int	e;
X	int	a;
X	{
X	FILE	*jfp;
X	int	jno;
X	char	*p;
X
X	jno = (int)strtol(fnm,&p,10);
X	if((p == fnm) || (*p != '\0')) {
X		return(-6);
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 = jno;			/* important */
X	jjj.msg = REMOVE;		/* important */
X	jjj.uid = realuid;		/* important */
X	wrfifo(fnam);
X	closejobfile(jfp);
X	return(0);
X	}
X
X/* ============================================================ */
X/*                   SEND EXECUTE MSG TO DAEMON                 */
X/* ------------------------------------------------------------ */
X
Xstatic int execjob(msg,fnm,e,a)
X	int	msg;
X	char	*fnm;
X	int	e;
X	int	a;
X	{
X	FILE	*jfp;
X	int	jno;
X	char	*p;
X
X	jno = (int)strtol(fnm,&p,10);
X	if((p == fnm) || (*p != '\0')) {
X		return(-6);
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
X	jjj.link = MAGIC;
X	jjj.jobn = jno;			/* important */
X	jjj.msg = EXECUT;		/* important */
X	jjj.uid = realuid;		/* important */
X	wrfifo(fnam);
X	closejobfile(jfp);
X	return(0);
X	}
X
X/* ============================================================ */
X/*                          LIST JOBS                           */
X/* ------------------------------------------------------------ */
X
Xstatic void printlist(label,array,start,end,offset)
X	char		*label;
X	char		array[];
X	int		start;
X	register int	end;
X	int		offset;
X	{
X	register 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
Xstatic int 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	register int	c,m,x,z;
X	char		line[LINESIZ];
X	char		*p;
X
X	(void)strtol(fnm,&p,10);
X	if((p == fnm) || (*p != '\0')) {
X		return(-6);
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			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		/*
X		 *	x flags all mdays
X		 *	z flags all wdays
X		 *	c general index
X		 */
X		x = z = 0;
X		for(c=1;c<32;++c) x |= jjj.mday[c];
X		for(c=0;c<7;++c) z |= jjj.wday[c];
X
X		printf("\n<<schedule>>\n");
X		printlist("minutes= ",jjj.min,0,60,0);
X		printlist("hours=   ",jjj.hour,0,24,0);
X		if((x == 0) && (z != 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((z == 0) && (x != 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
Xstatic void cleanlog()
X	{
X	jjj.link = MAGIC;
X	jjj.jobn = 0;
X	jjj.msg = CL_LOG;		/* important */
X	jjj.uid = realuid;		/* important */
X	wrfifo(fnam);
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,xflag;
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		usage();
X		}
X
X/* Setup and check if user allowed to use this program -------- */
X
X	login(allowfile,denyfile);
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=xflag=0;
X
X/* Process command line --------------------------------------- */
X
X/*	optind = 0;					ld(1) does this */
X	while((c=getopt(argc,argv,"N:n:aceflrux")) != EOF) {
X		switch(c) {
X		case 'N':
X			if(Nflag||nflag||rflag||eflag||
X			   lflag||fflag||aflag||cflag||uflag||xflag) 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||xflag) 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||xflag) usage();
X			rflag = 1; break;
X		case 'x':
X			if(Nflag||nflag||rflag||
X			   lflag||fflag||cflag||uflag||xflag) usage();
X			xflag = 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||xflag) usage();
X			lflag = 1; break;
X		case 'f':
X			if(Nflag||nflag||rflag||fflag||cflag||xflag) usage();
X			fflag = 1; break;
X		case 'u':
X			if(Nflag||nflag||rflag||uflag||cflag||xflag) 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||xflag) usage();
X			cflag = 1; break;
X		default:
X			usage();
X			}
X		}
X
X/* {at|batch|cronjob|crtabj} -c ------------------------- <<5>> */
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||xflag)) {
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					break;
X				case -6:
X					error("- non-numeric fname, get help");
X					}
X				}
X			closedir();
X			exit(exval);
X			}
X
X		if(realuid == 0) eflag = 1;
X		while(optind < argc) {
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("- wrong job type");
X				break;
X			case -4:
X				error("- permission denied");
X				break;
X			case -6:
X				error("- non-numeric jobn");
X				}
X			}
X		exit(exval);
X		}
X
X/* {at|batch|cronjob|crtabj} -x[ae] {all|jobn ...} ------ <<3>> */
X
X	if(xflag) {
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(execjob(arg0,p,eflag,aflag)) {
X				case -1:
X					error("open job file");
X					break;
X				case -2:
X					error("read job file");
X					break;
X				case -6:
X					error("- non-numeric fname, get help");
X					}
X				}
X			closedir();
X			exit(exval);
X			}
X
X		if(realuid == 0) eflag = 1;
X		while(optind < argc) {
X			switch(execjob(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("- wrong job type");
X				break;
X			case -4:
X				error("- permission denied");
X				break;
X			case -6:
X				error("- non-numeric jobn");
X				}
X			}
X		exit(exval);
X		}
X
X/* {at|batch|cronjob|crtabj} -l[aefu] [all|jobn ...] ---- <<4>> */
X
X	if(lflag) {
X		c = 0;
X		if(optind == argc)
X			c = 1;
X		else
X			if(strcmp(argv[optind],"all") == 0) {
X				if(optind != (argc-1)) usage();
X				c = 1;
X				}
X		if(c) {
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					break;
X				case -6:
X					error("- non-numeric fname, get help");
X					}
X				}
X			closedir();
X			exit(exval);
X			}
X
X		eflag = 1;
X		while(optind < argc) {
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("- wrong job type");
X				break;
X			case -5:
X				error("- permission denied");
X				break;
X			case -6:
X				error("- non-numeric jobn");
X				}
X			}
X		exit(exval);
X		}
X
X	/* not valid option combination */
X	usage();
X
X	/*NOTREACHED*/
X	}
SHAR_EOF
$TOUCH -am 1025125590 at.c &&
chmod 0644 at.c ||
echo "restore of at.c failed"
set `wc -c at.c`;Wc_c=$1
if test "$Wc_c" != "22541"; then
	echo original size 22541, current size $Wc_c
fi
fi
# ============= convertjob.c ==============
if test X"$1" != X"-c" -a -f 'convertjob.c'; then
	echo "File already exists: skipping 'convertjob.c'"
else
echo "x - extracting convertjob.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > convertjob.c &&
X/* convertjob.c - Oct 8, 1990 D.Lashomb
X *
X * SYNOPSIS
X *	convertjob jobn >temp
X *
X * DESCRIPTION
X *	This is a throwaway program to convert old style cron facility
X *	jobs to the new style.
X *
X *	Pre-version-4.0 cron facility jobs used a magic number of zero
X *	and marked most jobs with an extra newline and a "# job done\n".
X *	Version 4.0 of the cron facility marks its jobs with a magic
X *	number of MAGIC (== 'c''r''o''n') and does away with the extra
X *	newline and the "# job done\n".
X *
X *	The purpose of this program is to make it easier for users of
X *	older versions of my cron facility to upgrade to the new version.
X *	Before firing up the new facility, you would convert the old
X *	jobs:
X *		$ su
X *		# ATSPOOL=/usr/spool/cron/atjobs
X *		# cd $ATSPOOL
X *		# for i in *
X *		> do
X *		>	<<wherever>>/convertjob $i >/tmp/convj.temp
X *		>	cat /tmp/convj.temp >$i	# preserve own,grp,mode
X *		> done
X *		# rm /tmp/convj.temp
X *
X * CAVEATS
X *	Because this is a throwaway program, the error checking is nil.
X */
X
X#include <stdio.h>
X#include <string.h>
X#include "cron.h"
X#include "job.h"
X
XJOB	jjj;
XFILE	*fp;
Xchar	line1[LINESIZ] = "";
Xchar	line2[LINESIZ] = "";
Xchar	line3[LINESIZ] = "";
Xint	l1flg = 0;
Xint	l2flg = 0;
Xint	l3flg = 0;
X
Xint getline()
X	{
X	if(l2flg) {
X		strcpy(line3,line2);
X		l3flg = 1;
X		}
X	if(l1flg) {
X		strcpy(line2,line1);
X		l2flg = 1;
X		}
X	if(fgets(line1,LINESIZ,fp) == NULL) {
X		l1flg = 0;
X		return(0);
X		}
X	l1flg = 1;
X	return(1);
X	}
X
Xvoid putline()
X	{
X	if(l3flg) {
X		l3flg = 0;
X		fputs(line3,stdout);
X		}
X	else if(l2flg) {
X		l2flg = 0;
X		fputs(line2,stdout);
X		}
X	else if(l1flg) {
X		l1flg = 0;
X		fputs(line1,stdout);
X		}
X	}
X
Xint isdone()
X	{
X	if((strcmp(line2,"\n") == 0) &&
X	   (strcmp(line1,"# job done\n") == 0))
X		return(1);
X	if((strcmp(line3,"\n") == 0) &&
X	   (strcmp(line2,"# job done\n") == 0))
X		return(1);
X	return(0);
X	}
X
Xmain(argc,argv)
X	int	argc;
X	char	**argv;
X	{
X
X	fp = fopen(argv[1],"r");
X
X	/* convert the header */
X	fread(&jjj,JOBSIZ,1,fp);
X	jjj.link = MAGIC;
X	fwrite(&jjj,JOBSIZ,1,stdout);
X
X	/* copy the environment */
X	while((jjj.envz)--)
X		fputc(fgetc(fp),stdout);
X
X	/* copy user input up to the "# job done\n" */
X	if(getline() && getline())
X		while(getline()) putline();
X	if(!isdone()) {
X		putline();
X		putline();
X		}
X
X	fclose(fp);
X	}
SHAR_EOF
$TOUCH -am 1008173890 convertjob.c &&
chmod 0644 convertjob.c ||
echo "restore of convertjob.c failed"
set `wc -c convertjob.c`;Wc_c=$1
if test "$Wc_c" != "2275"; then
	echo original size 2275, current size $Wc_c
fi
fi
# ============= cron.1 ==============
if test X"$1" != X"-c" -a -f 'cron.1'; then
	echo "File already exists: skipping 'cron.1'"
else
echo "x - extracting cron.1 (Text)"
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 jobs 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
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.
XThe daemon makes no 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.
XThe consistency check is also 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, 10/90
SHAR_EOF
$TOUCH -am 1018215890 cron.1 &&
chmod 0644 cron.1 ||
echo "restore of cron.1 failed"
set `wc -c cron.1`;Wc_c=$1
if test "$Wc_c" != "7418"; then
	echo original size 7418, current size $Wc_c
fi
fi
# ============= cron.h ==============
if test X"$1" != X"-c" -a -f 'cron.h'; then
	echo "File already exists: skipping 'cron.h'"
else
echo "x - extracting cron.h (Text)"
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	(c) copyright October 16, 1990 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		256
X
X/*
X *	PATHSIZ and LINESIZ should both be reasonably big
X *	enough.  Messages are sprintf'ed and crontab files
X *	are fgets'ed into LINESIZ'ed buffers.  PATHSIZ
X *	should be big enough to hold the longest pathname
X *	to any of this facility's files.  LINESIZ should
X *	never be less than PATHSIZ either.
X */
X
X#define ZEROSECS
X#define TZOVRIDE
X#define SET_LOGNAME
X
X#define MAXLATENESS	(10*GRAINULARITY)
X#define SECS		60
X
X/*	MAXLATENESS and SECS are used to reset the daemon's
X *	idea of the time if the system clock changes.
X *
X *	MAXLATENESS should be >= GRAINULARITY.
X *
X *	If GRAINULARITY is < 60
X *	then define SECS = GRAINULARITY
X *	else define SECS = 60
X *
X *	note- can't use preprocessor to enforce these because
X *	      of (unsigned) cast in define of GRAINULARITY
X */
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
$TOUCH -am 1018163090 cron.h &&
chmod 0644 cron.h ||
echo "restore of cron.h failed"
set `wc -c cron.h`;Wc_c=$1
if test "$Wc_c" != "4499"; then
	echo original size 4499, current size $Wc_c
fi
fi
echo "End of part 2, continue with part 3"
exit 0



More information about the Alt.sources mailing list