|
|
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.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, "%d\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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.