Annotation of 43BSD/usr.bin/at/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.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: }

unix.superglobalmegacorp.com

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