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
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
should_be_run defined in line 463; used 2 times
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
nowtime defined in line 51; used 5 times
sccsid defined in line 14; never used

Defined macros

ABNORMAL defined in line 46; used 3 times
ATDIR defined in line 41; used 3 times
LASTFILE defined in line 48; used 2 times
MAILER defined in line 43; used 2 times
NORMAL defined in line 45; used 2 times
PASTDIR defined in line 47; used 2 times
TMPDIR defined in line 42; used 1 times
Last modified: 1987-07-16
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4542
Valid CSS Valid XHTML 1.0 Strict