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