7943dd4138a0e314f28a1d8533b2cf6e579f2b01
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
static char sccsid
[] = "@(#)atrun.c 5.6 (Berkeley) %G%";
* Run jobs created by at(1)
* Modifications by: Steve Wall
* Computer Systems Research Group
* University of California @ Berkeley
# define ATDIR "/usr/spool/at" /* spooling area */
# define TMPDIR "/tmp" /* area for temporary files */
# define MAILER "/bin/mail" /* program to use for sending
# define NORMAL 0 /* job exited normally */
# define ABNORMAL 1 /* job exited abnormally */
# define PASTDIR "/usr/spool/at/past" /* area to run jobs from */
# define LASTFILE "/usr/spool/at/lasttimedone" /* update time file */
char nowtime
[11]; /* time it is right now (yy.ddd.hhmm) */
char errfile
[25]; /* file where we redirect errors to */
int i
; /* for loop index */
int numjobs
; /* number of jobs to be run */
int should_be_run(); /* should a job be run? */
struct direct
**jobqueue
; /* queue of jobs to be run */
* Move to the spooling area.
* Create a filename that represents the time it is now. This is used
* to determine if the execution time for a job has arrived.
* Create a queue of the jobs that should be run.
if ((numjobs
= scandir(".",&jobqueue
,should_be_run
, 0)) < 0) {
* If there are jobs to be run, run them.
for (i
= 0; i
< numjobs
; ++i
) {
run(jobqueue
[i
]->d_name
);
* Record the last update time.
* Create a string with the syntax yy.ddd.hhmm that represents the
* time it is right now. This string is used to determine whether a
struct tm
*now
; /* broken down representation of the
struct timeval time
; /* number of seconds since 1/1/70 */
struct timezone zone
; /* time zone we're in (NOT USED) */
if (gettimeofday(&time
,&zone
) < 0) {
* Get a broken down representation of the time it is right now.
now
= localtime(&time
.tv_sec
);
* Create a string to be used in determining whether or not a job
* should be run. The syntax is yy.ddd.hhmm .
sprintf(nowtime
,"%d.%03d.%02d%02d",now
->tm_year
,
int i
; /* scratch variable */
int pid
; /* process id of forked shell */
int exitstatus
; /* exit status of the job */
int notifybymail
; /* should we notify the owner of the
job after the job is run? */
char shell
[4]; /* shell to run the job under */
char *getname(); /* get a uname from using a uid */
char mailvar
[4]; /* send mail variable ("yes" or "no") */
char runfile
[100]; /* file sent to forked shell for exec-
char owner
[128]; /* owner of job we're going to run */
char jobname
[128]; /* name of job we're going to run */
char whichshell
[100]; /* which shell should we fork off? */
struct passwd
*pwdbuf
; /* password info of the owner of job */
struct stat errbuf
; /* stats on error file */
struct stat jobbuf
; /* stats on job file */
FILE *infile
; /* I/O stream to spoolfile */
* First we fork a child so that the main can run other jobs.
if ((infile
= fopen(spoolfile
,"r")) == NULL
) {
(void) unlink(spoolfile
);
* Grab the 4-line header out of the spoolfile.
(fscanf(infile
,"# owner: %127s%*[^\n]\n",owner
) != 1) ||
(fscanf(infile
,"# jobname: %127s%*[^\n]\n",jobname
) != 1) ||
(fscanf(infile
,"# shell: %3s%*[^\n]\n",shell
) != 1) ||
(fscanf(infile
,"# notify by mail: %3s%*[^\n]\n",mailvar
) != 1)
fprintf(stderr
, "%s: bad spool header\n", spoolfile
);
(void) unlink(spoolfile
);
* Check to see if we should send mail to the owner.
notifybymail
= (strcmp(mailvar
, "yes") == 0);
* Change the ownership of the spoolfile from "daemon" to the owner
pwdbuf
= getpwnam(owner
);
fprintf(stderr
, "%s: could not find owner in passwd file\n",
(void) unlink(spoolfile
);
if (chown(spoolfile
,pwdbuf
->pw_uid
,pwdbuf
->pw_gid
) == -1) {
(void) unlink(spoolfile
);
* Move the spoolfile to the directory where jobs are run from and
* then move into that directory.
sprintf(runfile
,"%s/%s",PASTDIR
,spoolfile
);
rename(spoolfile
, runfile
);
* Create a temporary file where we will redirect errors to.
* Just to make sure we've got a unique file, we'll run an "access"
for (i
= 0; i
<= 1000; i
+= 2) {
sprintf(errfile
,"%s/at.err%d",TMPDIR
,(getpid() + i
));
if (access(errfile
, F_OK
))
fprintf(stderr
, "couldn't create errorfile.\n");
* Get the stats of the job being run.
if (stat(runfile
, &jobbuf
) == -1) {
* Fork another child that will run the job.
* If the child fails, save the job so that it gets
* rerun the next time "atrun" is executed and then exit.
rename(runfile
, spoolfile
);
* Wait for the child to terminate.
* Get the stats of the error file and determine the exit
* status of the child. We assume that if there is anything
* in the error file then the job ran into some errors.
if (stat(errfile
,&errbuf
) != 0) {
exitstatus
= ((errbuf
.st_size
== 0) ? NORMAL
: ABNORMAL
);
/* If errors occurred, then we send mail to the owner
* telling him/her that we ran into trouble.
* (NOTE: this could easily be modified so that if any
* errors occurred while running a job, mail is sent regard-
* less of whether the -m flag was set or not.
* "if (notifybymail)" use
* "if ((exitstatus == ABNORMAL) || (notifybymail))"
* It's up to you if you want to implement this.
if (exitstatus
== ABNORMAL
|| notifybymail
)
sendmailto(getname(jobbuf
.st_uid
),jobname
,exitstatus
);
* Remove the errorfile and the jobfile.
if (unlink(errfile
) == -1)
if (unlink(runfile
) == -1)
* HERE'S WHERE WE SET UP AND FORK THE SHELL.
* Run the job as the owner of the jobfile
/* This is no longer needed with the new, stripped-down quota system */
quota(Q_SETUID
,jobbuf
.st_uid
,0,0);
initgroups(getname(jobbuf
.st_uid
),jobbuf
.st_gid
);
* Close all open files so that we can reopen a temporary file
for (i
= getdtablesize(); --i
>= 0;)
* Reposition stdin, stdout, and stderr.
* stderr = /tmp/at.err{pid}
open(errfile
,O_CREAT
|O_WRONLY
,00644);
* See if the shell is in /bin
sprintf(whichshell
,"/bin/%s",shell
);
execl(whichshell
,shell
,runfile
, 0);
* If not in /bin, look for the shell in /usr/bin.
sprintf(whichshell
,"/usr/bin/%s",shell
);
execl(whichshell
,shell
,runfile
, 0);
* If not in /bin, look for the shell in /usr/new.
sprintf(whichshell
,"/usr/new/%s",shell
);
execl(whichshell
,shell
,runfile
, 0);
* If we don't succeed by now, we're really having troubles,
* so we'll send the owner some mail.
fprintf(stderr
, "%s: Can't execl shell\n",shell
);
* Send mail to the owner of the job.
sendmailto(user
,jobname
,exitstatus
)
int ch
; /* scratch variable */
char mailtouser
[100]; /* the process we use to send mail */
FILE *mailptr
; /* I/O stream to the mail process */
FILE *errptr
; /* I/O stream to file containing error
FILE *popen(); /* initiate I/O to a process */
* Create the full name for the mail process.
sprintf(mailtouser
,"%s %s",MAILER
, user
);
* Open a stream to the mail process.
if ((mailptr
= popen(mailtouser
,"w")) == NULL
) {
* Send the letter. If the job exited normally, just send a
* quick letter notifying the owner that everthing went ok.
if (exitstatus
== NORMAL
) {
fprintf(mailptr
,"Your job \"%s\" was run without ",jobname
);
fprintf(mailptr
,"any errors.\n");
* If the job exited abnormally, send a letter notifying the user
* that the job didn't run proberly. Also, send a copy of the errors
* that occurred to the user.
if (exitstatus
== ABNORMAL
) {
* Write the intro to the letter.
fprintf(mailptr
,"\n\nThe job you submitted to at, ");
fprintf(mailptr
,"\"%s\", ",jobname
);
fprintf(mailptr
,"exited abnormally.\nA list of the ");
fprintf(mailptr
," errors that occurred follows:\n\n\n");
* Open the file containing a log of the errors that
if ((errptr
= fopen(errfile
,"r")) == NULL
) {
* Send the copy of the errors to the owner.
while ((ch
= fgetc(errptr
)) != EOF
) {
fprintf(mailptr
,"\n\n-----------------\n");
fprintf(mailptr
,"The Atrun Program\n");
* Close the stream to the mail process.
* Do we want to include a file in the job queue? (used by "scandir")
* We are looking for files whose "value" (its name) is less than or
* equal to the time it is right now (represented by "nowtime").
* We'll only consider files with three dots in their name since these
* are the only files that represent jobs to be run.
int numdot
= 0; /* number of dots found in a filename */
char *filename
; /* pointer for scanning a filename */
filename
= direntry
->d_name
;
* Count the number of dots found in the directory entry.
numdot
+= (*(filename
++) == '.');
* If the directory entry doesn't represent a job, just return a 0.
* If a directory entry represents a job, determine if it's time to
return(strncmp(direntry
->d_name
, nowtime
,11) <= 0);
* Record the last time that "atrun" was run.
struct timeval time
; /* number of seconds since 1/1/70 */
struct timezone zone
; /* time zone we're in (NOT USED) */
FILE *lastimefile
; /* file where recored is kept */
if (gettimeofday(&time
,&zone
) < 0) {
if ((lastimefile
= fopen(LASTFILE
, "w")) == NULL
) {
fprintf(stderr
, "can't update lastfile: ");
* Record the last update time (in seconds since 1/1/70).
fprintf(lastimefile
, "%d\n", (u_long
) time
.tv_sec
);
* Get the full login name of a person using his/her user id.
struct passwd
*pwdinfo
; /* password info structure */
if ((pwdinfo
= getpwuid(uid
)) == 0) {
return(pwdinfo
->pw_name
);