Annotation of 43BSDReno/usr.bin/at/atrun/atrun.c, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.