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