# 2.11BSD#431 - /usr/src/usr.bin/at/atrun.c

##### Functions

    getname           X
    main              X
    makenowtime       X
    run               X
    sendmailto        X
    should_be_run     X
    updatetime        X

##### Variables

    copyright         X
    errfile           X
    nowtime           X

##### Macros

    ABNORMAL          X
    ATDIR             X
    LASTFILE          X
    MAILER            X
    NORMAL            X
    PASTDIR           X
    TMPDIR            X

       1: /*
       2:  * Copyright (c) 1983 Regents of the University of California.
       3:  * All rights reserved.  The Berkeley software License Agreement
       4:  * specifies the terms and conditions for redistribution.
       5:  */
       6:
       7: #ifndef lint
       8: char copyright[] =
       9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
      10:  All rights reserved.\n";
      11: #endif not lint
      12:
      13: #ifndef lint
      14: static char sccsid[] = "@(#)atrun.c	5.4 (Berkeley) 5/28/86";
      15: #endif not lint
      16:
      17: /*
      18:  *	Synopsis: atrun
      19:  *
      20:  *
      21:  *	Run jobs created by at(1)
      22:  *
      23:  *
      24:  *	Modifications by:	Steve Wall
      25:  *				Computer Systems Research Group
      26:  *				University of California @ Berkeley
      27:  *
      28:  */
      29: # include <stdio.h>
      30: # include <sys/types.h>
      31: # include <sys/dir.h>
      32: # include <sys/file.h>
      33: # include <sys/time.h>
      34: # include <sys/param.h>
      35: #ifdef notdef
      36: # include <sys/quota.h>
      37: #endif
      38: # include <sys/stat.h>
      39: # include <pwd.h>
      40:
      41: # define ATDIR      "/usr/spool/at"     /* spooling area */
      42: # define TMPDIR     "/tmp"          /* area for temporary files */
      43: # define MAILER     "/bin/mail"     /* program to use for sending
      44: 						   mail */
      45: # define NORMAL     0           /* job exited normally */
      46: # define ABNORMAL   1           /* job exited abnormally */
      47: # define PASTDIR    "/usr/spool/at/past"    /* area to run jobs from */
      48: # define LASTFILE   "/usr/spool/at/lasttimedone"    /* update time file */
      49:
      50:
      51: char nowtime[11];           /* time it is right now (yy.ddd.hhmm) */
      52: char errfile[25];           /* file where we redirect errors to */
      53:
      54:
      55: main(argc, argv)
      56: char **argv;
      57: {
      58:
      59:     int i;              /* for loop index */
      60:     int numjobs;            /* number of jobs to be run */
      61:     int should_be_run();        /* should a job be run? */
      62:     struct direct **jobqueue;   /* queue of jobs to be run */
      63:
      64:
      65:     /*
      66: 	 * Move to the spooling area.
      67: 	 */
      68:     chdir(ATDIR);
      69:
      70:     /*
      71: 	 * Create a filename that represents the time it is now. This is used
      72: 	 * to determine if the execution time for a job has arrived.
      73: 	 */
      74:     makenowtime(nowtime);
      75:
      76:     /*
      77: 	 * Create a queue of the jobs that should be run.
      78: 	 */
      79:     if ((numjobs = scandir(".",&jobqueue,should_be_run, 0)) < 0) {
      80:         perror(ATDIR);
      81:         exit(1);
      82:     }
      83:
      84:     /*
      85: 	 * If there are jobs to be run, run them.
      86: 	 */
      87:     if (numjobs > 0) {
      88:         for (i = 0; i < numjobs; ++i) {
      89:             run(jobqueue[i]->d_name);
      90:         }
      91:     }
      92:
      93:     /*
      94: 	 * Record the last update time.
      95: 	 */
      96:     updatetime();
      97:
      98: }
      99:
     100: /*
     101:  * Create a string with the syntax yy.ddd.hhmm that represents the
     102:  * time it is right now. This string is used to determine whether a
     103:  * job should be run.
     104:  */
     105: makenowtime(nowtime)
     106: char *nowtime;
     107: {
     108:     struct tm *now;         /* broken down representation of the
     109: 					   time it is right now */
     110:     struct timeval time;        /* number of seconds since 1/1/70 */
     111:     struct timezone zone;       /* time zone we're in (NOT USED) */
     112:
     113:     /*
     114: 	 * Get the time of day.
     115: 	 */
     116:     if (gettimeofday(&time,&zone) < 0) {
     117:         perror("gettimeofday");
     118:         exit(1);
     119:     }
     120:
     121:     /*
     122: 	 * Get a broken down representation of the time it is right now.
     123: 	 */
     124:     now = localtime(&time.tv_sec);
     125:
     126:     /*
     127: 	 * Create a string to be used in determining whether or not a job
     128: 	 * should be run. The syntax is yy.ddd.hhmm .
     129: 	 */
     130:     sprintf(nowtime,"%d.%03d.%02d%02d",now->tm_year,
     131:                        now->tm_yday,
     132:                        now->tm_hour,
     133:                        now->tm_min);
     134:     return;
     135: }
     136:
     137: /*
     138:  * Run a job.
     139:  */
     140: run(spoolfile)
     141: char *spoolfile;
     142: {
     143:     int i;              /* scratch variable */
     144:     int pid;            /* process id of forked shell */
     145:     int exitstatus;         /* exit status of the job */
     146:     int notifybymail;       /* should we notify the owner of the
     147: 					   job after the job is run? */
     148:     char shell[4];          /* shell to run the job under */
     149:     char *getname();        /* get a uname from using a uid */
     150:     char mailvar[4];        /* send mail variable ("yes" or "no") */
     151:     char runfile[100];      /* file sent to forked shell for exec-
     152: 					   ution */
     153:     char owner[128];        /* owner of job we're going to run */
     154:     char jobname[128];      /* name of job we're going to run */
     155:     char whichshell[100];       /* which shell should we fork off? */
     156:     struct passwd *pwdbuf;      /* password info of the owner of job */
     157:     struct stat errbuf;     /* stats on error file */
     158:     struct stat jobbuf;     /* stats on job file */
     159:     FILE *infile;           /* I/O stream to spoolfile */
     160:
     161:
     162:     /*
     163: 	 * First we fork a child so that the main can run other jobs.
     164: 	 */
     165:     if (pid = fork())
     166:         return;
     167:
     168:     /*
     169: 	 * Open the spoolfile.
     170: 	 */
     171:     if ((infile = fopen(spoolfile,"r")) == NULL) {
     172:         perror(spoolfile);
     173:         exit(1);
     174:     }
     175:
     176:     /*
     177: 	 * Grab the 4-line header out of the spoolfile.
     178: 	 */
     179:     if (
     180:         (fscanf(infile,"# owner: %127s%*[^\n]\n",owner) != 1) ||
     181:         (fscanf(infile,"# jobname: %127s%*[^\n]\n",jobname) != 1) ||
     182:         (fscanf(infile,"# shell: %3s%*[^\n]\n",shell) != 1) ||
     183:         (fscanf(infile,"# notify by mail: %3s%*[^\n]\n",mailvar) != 1)
     184:         ) {
     185:         fprintf(stderr, "%s: bad spool header\n", spoolfile);
     186:         exit(1);
     187:     }
     188:
     189:     /*
     190: 	 * Check to see if we should send mail to the owner.
     191: 	 */
     192:     notifybymail = (strcmp(mailvar, "yes") == 0);
     193:     fclose(infile);
     194:
     195:     /*
     196: 	 * Change the ownership of the spoolfile from "daemon" to the owner
     197: 	 * of the job.
     198: 	 */
     199:     pwdbuf = getpwnam(owner);
     200:     if (pwdbuf == NULL) {
     201:         fprintf(stderr, "%s: could not find owner in passwd file\n",
     202:             spoolfile);
     203:         exit(1);
     204:     }
     205:     if (chown(spoolfile,pwdbuf->pw_uid,pwdbuf->pw_gid) == -1) {
     206:         perror(spoolfile);
     207:         exit(1);
     208:     }
     209:
     210:     /*
     211: 	 * Move the spoolfile to the directory where jobs are run from and
     212: 	 * then move into that directory.
     213: 	 */
     214:     sprintf(runfile,"%s/%s",PASTDIR,spoolfile);
     215:     rename(spoolfile, runfile);
     216:     chdir(PASTDIR);
     217:
     218:     /*
     219: 	 * Create a temporary file where we will redirect errors to.
     220: 	 * Just to make sure we've got a unique file, we'll run an "access"
     221: 	 * check on the file.
     222: 	 */
     223:     for (i = 0; i <= 1000; i += 2) {
     224:         sprintf(errfile,"%s/at.err%d",TMPDIR,(getpid() + i));
     225:
     226:         if (access(errfile, F_OK))
     227:             break;
     228:
     229:         if (i == 1000) {
     230:             fprintf(stderr, "couldn't create errorfile.\n");
     231:             exit(1);
     232:         }
     233:     }
     234:
     235:     /*
     236: 	 * Get the stats of the job being run.
     237: 	 */
     238:     if (stat(runfile, &jobbuf) == -1) {
     239:         perror(runfile);
     240:         exit(1);
     241:     }
     242:
     243:     /*
     244: 	 * Fork another child that will run the job.
     245: 	 */
     246:     if (pid = fork()) {
     247:
     248:         /*
     249: 		 * If the child fails, save the job so that it gets
     250: 		 * rerun the next time "atrun" is executed and then exit.
     251: 		 */
     252:         if (pid == -1) {
     253:             chdir(ATDIR);
     254:             rename(runfile, spoolfile);
     255:             exit(1);
     256:         }
     257:
     258:         /*
     259: 		 * Wait for the child to terminate.
     260: 		 */
     261:         wait((int *)0);
     262:
     263:         /*
     264: 		 * Get the stats of the error file and determine the exit
     265: 		 * status of the child. We assume that if there is anything
     266: 		 * in the error file then the job ran into some errors.
     267: 		 */
     268:         if (stat(errfile,&errbuf) != 0) {
     269:             perror(errfile);
     270:             exit(1);
     271:         }
     272:         exitstatus = ((errbuf.st_size == 0) ? NORMAL : ABNORMAL);
     273:
     274:         /* If errors occurred, then we send mail to the owner
     275: 		 * telling him/her that we ran into trouble.
     276: 		 *
     277: 		 * (NOTE: this could easily be modified so that if any
     278: 		 * errors occurred while running a job, mail is sent regard-
     279: 		 * less of whether the -m flag was set or not.
     280: 		 *
     281: 		 * i.e. rather than:
     282: 		 *
     283: 		 *	"if (notifybymail)" use
     284: 		 * use:
     285: 		 *
     286: 		 *	"if ((exitstatus == ABNORMAL) || (notifybymail))"
     287: 		 *
     288: 		 * It's up to you if you want to implement this.
     289: 		 *
     290: 		 */
     291:         if (exitstatus == ABNORMAL || notifybymail)
     292:             sendmailto(getname(jobbuf.st_uid),jobname,exitstatus);
     293:
     294:         /*
     295: 		 * Remove the errorfile and the jobfile.
     296: 		 */
     297:         if (unlink(errfile) == -1)
     298:             perror(errfile);
     299:         if (unlink(runfile) == -1)
     300:             perror(runfile);
     301:
     302:         exit(0);
     303:     }
     304:
     305:     /*
     306: 	 * HERE'S WHERE WE SET UP AND FORK THE SHELL.
     307: 	 */
     308:
     309:     /*
     310: 	 * Run the job as the owner of the jobfile
     311: 	 */
     312: #ifdef notdef
     313:     /* This is no longer needed with the new, stripped-down quota system */
     314:     quota(Q_SETUID,jobbuf.st_uid,0,0);
     315: #endif
     316:     setgid(jobbuf.st_gid);
     317:     initgroups(getname(jobbuf.st_uid),jobbuf.st_gid);
     318:     setuid(jobbuf.st_uid);
     319:
     320:     /*
     321: 	 * Close all open files so that we can reopen a temporary file
     322: 	 * for stdout and sterr.
     323: 	 */
     324:     for (i = getdtablesize(); --i >= 0;)
     325:         close(i);
     326:
     327:     /*
     328: 	 * Reposition stdin, stdout, and stderr.
     329: 	 *
     330: 	 *	stdin  = /dev/null
     331: 	 *	stout  = /dev/null
     332: 	 *	stderr = /tmp/at.err{pid}
     333: 	 *
     334: 	 */
     335:     open("/dev/null", 0);
     336:     open("/dev/null", 1);
     337:     open(errfile,O_CREAT|O_WRONLY,00644);
     338:
     339:     /*
     340: 	 * Now we fork the shell.
     341: 	 *
     342: 	 * See if the shell is in /bin
     343: 	 */
     344:     sprintf(whichshell,"/bin/%s",shell);
     345:     execl(whichshell,shell,runfile, 0);
     346:
     347:     /*
     348: 	 * If not in /bin, look for the shell in /usr/bin.
     349: 	 */
     350:     sprintf(whichshell,"/usr/bin/%s",shell);
     351:     execl(whichshell,shell,runfile, 0);
     352:
     353:     /*
     354: 	 * If not in /bin, look for the shell in /usr/new.
     355: 	 */
     356:     sprintf(whichshell,"/usr/new/%s",shell);
     357:     execl(whichshell,shell,runfile, 0);
     358:
     359:     /*
     360: 	 * If we don't succeed by now, we're really having troubles,
     361: 	 * so we'll send the owner some mail.
     362: 	 */
     363:     fprintf(stderr, "%s: Can't execl shell\n",shell);
     364:     exit(1);
     365: }
     366:
     367: /*
     368:  * Send mail to the owner of the job.
     369:  */
     370: sendmailto(user,jobname,exitstatus)
     371: char *user;
     372: char *jobname;
     373: int exitstatus;
     374: {
     375:     char ch;            /* scratch variable */
     376:     char mailtouser[100];       /* the process we use to send mail */
     377:     FILE *mailptr;          /* I/O stream to the mail process */
     378:     FILE *errptr;           /* I/O stream to file containing error
     379: 					   messages */
     380:     FILE *popen();          /* initiate I/O to a process */
     381:
     382:
     383:     /*
     384: 	 * Create the full name for the mail process.
     385: 	 */
     386:     sprintf(mailtouser,"%s %s",MAILER, user);
     387:
     388:     /*
     389: 	 * Open a stream to the mail process.
     390: 	 */
     391:     if ((mailptr = popen(mailtouser,"w")) == NULL) {
     392:         perror(MAILER);
     393:         exit(1);
     394:     }
     395:
     396:     /*
     397: 	 * Send the letter. If the job exited normally, just send a
     398: 	 * quick letter notifying the owner that everthing went ok.
     399: 	 */
     400:     if (exitstatus == NORMAL) {
     401:         fprintf(mailptr,"Your job \"%s\" was run without ",jobname);
     402:         fprintf(mailptr,"any errors.\n");
     403:     }
     404:
     405:     /*
     406: 	 * If the job exited abnormally, send a letter notifying the user
     407: 	 * that the job didn't run proberly. Also, send a copy of the errors
     408: 	 * that occurred to the user.
     409: 	 */
     410:     else {
     411:         if (exitstatus == ABNORMAL) {
     412:
     413:             /*
     414: 			 * Write the intro to the letter.
     415: 			 */
     416:             fprintf(mailptr,"\n\nThe job you submitted to at, ");
     417:             fprintf(mailptr,"\"%s\", ",jobname);
     418:             fprintf(mailptr,"exited abnormally.\nA list of the ");
     419:             fprintf(mailptr," errors that occurred follows:\n\n\n");
     420:
     421:             /*
     422: 			 * Open the file containing a log of the errors that
     423: 			 * occurred.
     424: 			 */
     425:             if ((errptr = fopen(errfile,"r")) == NULL) {
     426:                 perror(errfile);
     427:                 exit(1);
     428:             }
     429:
     430:             /*
     431: 			 * Send the copy of the errors to the owner.
     432: 			 */
     433:             fputc('\t',mailptr);
     434:             while ((ch = fgetc(errptr)) != EOF) {
     435:                 fputc(ch,mailptr);
     436:                 if (ch == '\n')
     437:                     fputc('\t',mailptr);
     438:             }
     439:             fclose(errptr);
     440:         }
     441:     }
     442:
     443:     /*
     444: 	 * Sign the letter.
     445: 	 */
     446:     fprintf(mailptr,"\n\n-----------------\n");
     447:     fprintf(mailptr,"The Atrun Program\n");
     448:
     449:     /*
     450: 	 * Close the stream to the mail process.
     451: 	 */
     452:     pclose(mailptr);
     453:     return;
     454: }
     455:
     456: /*
     457:  * Do we want to include a file in the job queue? (used by "scandir")
     458:  * We are looking for files whose "value" (its name) is less than or
     459:  * equal to the time it is right now (represented by "nowtime").
     460:  * We'll only consider files with three dots in their name since these
     461:  * are the only files that represent jobs to be run.
     462:  */
     463: should_be_run(direntry)
     464: struct direct *direntry;
     465: {
     466:     int numdot = 0;         /* number of dots found in a filename */
     467:     char *filename;         /* pointer for scanning a filename */
     468:
     469:
     470:     filename = direntry->d_name;
     471:
     472:     /*
     473: 	 * Count the number of dots found in the directory entry.
     474: 	 */
     475:     while (*filename)
     476:         numdot += (*(filename++) == '.');
     477:
     478:     /*
     479: 	 * If the directory entry doesn't represent a job, just return a 0.
     480: 	 */
     481:     if (numdot != 3)
     482:         return(0);
     483:
     484:     /*
     485: 	 * If a directory entry represents a job, determine if it's time to
     486: 	 * run it.
     487: 	 */
     488:     return(strncmp(direntry->d_name, nowtime,11) <= 0);
     489: }
     490:
     491: /*
     492:  * Record the last time that "atrun" was run.
     493:  */
     494: updatetime()
     495: {
     496:
     497:     struct timeval time;        /* number of seconds since 1/1/70 */
     498:     struct timezone zone;       /* time zone we're in (NOT USED) */
     499:     FILE *lastimefile;      /* file where recored is kept */
     500:
     501:     /*
     502: 	 * Get the time of day.
     503: 	 */
     504:     if (gettimeofday(&time,&zone) < 0) {
     505:         perror("gettimeofday");
     506:         exit(1);
     507:     }
     508:
     509:     /*
     510: 	 * Open the record file.
     511: 	 */
     512:     if ((lastimefile = fopen(LASTFILE, "w")) == NULL) {
     513:         fprintf(stderr, "can't update lastfile: ");
     514:         perror(LASTFILE);
     515:         exit(1);
     516:     }
     517:
     518:     /*
     519: 	 * Record the last update time (in seconds since 1/1/70).
     520: 	 */
     521:     fprintf(lastimefile, "%ld\n", (u_long) time.tv_sec);
     522:
     523:     /*
     524: 	 * Close the record file.
     525: 	 */
     526:     fclose(lastimefile);
     527: }
     528:
     529: /*
     530:  * Get the full login name of a person using his/her user id.
     531:  */
     532: char *
     533: getname(uid)
     534: int uid;
     535: {
     536:     struct passwd *pwdinfo;         /* password info structure */
     537:
     538:
     539:     if ((pwdinfo = getpwuid(uid)) == 0) {
     540:         perror(uid);
     541:         exit(1);
     542:     }
     543:     return(pwdinfo->pw_name);
     544: }

#### Defined functions

getname defined in line 532; used 3 times

- in line 149, 292, 317

main defined in line 55; never used
makenowtime defined in line 105; used 1 times

- in line 74

run defined in line 140; used 1 times

- in line 89

sendmailto defined in line 370; used 1 times

- in line 292

should_be_run defined in line 463; used 2 times

- in line 61, 79

updatetime defined in line 494; used 1 times

- in line 96

#### Defined variables

copyright defined in line 8; never used
errfile defined in line 52; used 9 times

- in line 224-226(2), 268-269(2), 297-298(2), 337, 425-426(2)

nowtime defined in line 51; used 5 times

- in line 74, 105-106(2), 130, 488

sccsid defined in line 14; never used

#### Defined macros

ABNORMAL defined in line 46; used 3 times

- in line 272, 291, 411

ATDIR defined in line 41; used 3 times

- in line 68, 80, 253

LASTFILE defined in line 48; used 2 times

- in line 512-514(2)

MAILER defined in line 43; used 2 times

- in line 386, 392

NORMAL defined in line 45; used 2 times

- in line 272, 400

PASTDIR defined in line 47; used 2 times

- in line 214-216(2)

TMPDIR defined in line 42; used 1 times

- in line 224

Last modified: 1987-07-16 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 1690 |