Annotation of 43BSDTahoe/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.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: }

unix.superglobalmegacorp.com

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