Annotation of coherent/d/bin/at.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * At takes a list of commands and arranges for them to be executed at
        !             3:  * a specified time.  When the commands are executed, the user id, group
        !             4:  * id, exported shell variables and current directory will all be as they
        !             5:  * were when at was executed.
        !             6:  * The format of the at command is
        !             7:  *     at [-v] [-c command] time [week] [file]
        !             8:  * or
        !             9:  *     at [-v] [-c command] time day_of_week [week] [file]
        !            10:  * or
        !            11:  *     at [-v] [-c command] time month day_of_month [file]
        !            12:  * Here the presence of `week' implies that the command should occur
        !            13:  * on the next week. If the v-flag is specified, then at prints out
        !            14:  * when the command will be executed.  If the c-flag is specified
        !            15:  * then the command come from the string `command'.  If a file is
        !            16:  * specified, then they come from that file.  If none of these is
        !            17:  * done, then standard input is read.
        !            18:  * Note that at should not be set uid or set gid.  Anyone can write
        !            19:  * in the at spooling directory.  Security is maintained because at
        !            20:  * sets the set uid, set gid and owner execute bits.  Without all of
        !            21:  * these being set, atrun will not run the script.
        !            22:  */
        !            23: #include <stdio.h>
        !            24: #include <ctype.h>
        !            25: #include <sys/types.h>
        !            26: #include <time.h>
        !            27: #include <sys/dir.h>
        !            28: #include <signal.h>
        !            29: 
        !            30: 
        !            31: #define        NUL     '\0'
        !            32: #define TRUE   (0 == 0)
        !            33: #define FALSE  (0 != 0)
        !            34: #define        STDOUT  1
        !            35: #define        WEEK    7
        !            36: #define YEAR   365
        !            37: #define        LEAPM   1                       /* leap month */
        !            38: #define        YBASE   1900                    /* Year bias in struct tm */
        !            39: #define        FSTDAY  0                       /* Week day of 1st day of year 0 */
        !            40: #define UNKNOWN        -1
        !            41: #define        MODE    06500                   /* set uid, set gid, owner read-exec */
        !            42: #define        OUTNL   13                      /* "yymmddhhmm.aa" */
        !            43: 
        !            44: 
        !            45: struct tm      td      = {             /* time to do command */
        !            46:                        0,              UNKNOWN,        UNKNOWN,
        !            47:                        UNKNOWN,        UNKNOWN,        UNKNOWN,
        !            48:                        UNKNOWN,        UNKNOWN,        UNKNOWN
        !            49:                };
        !            50: int            wflg    = FALSE,        /* `week' flag */
        !            51:                vflg    = FALSE;        /* verify flag */
        !            52: char           *cstr   = NULL,         /* c-flag command string */
        !            53:                outn[]  = "/usr/spool/at/yymmddhhmm.aa";
        !            54: FILE           *outfp  = NULL;         /* file to place commands into */
        !            55: extern char    **environ;
        !            56: 
        !            57: 
        !            58: main(argc, argv)
        !            59: int    argc;
        !            60: char   *argv[];
        !            61: {
        !            62:        setsigs();
        !            63:        options(argv);
        !            64:        if (vflg)
        !            65:                printf("%s", asctime(&td));
        !            66:        makefile();
        !            67:        copyenv();
        !            68:        copyumask();
        !            69:        copywd();
        !            70:        copycmds();
        !            71:        return (0);
        !            72: }
        !            73: 
        !            74: 
        !            75: /*
        !            76:  * Setsigs simply assures that on an interrupt we will clean up after
        !            77:  * ourselves so as to avoid leaving junk in at's spooling directory.
        !            78:  */
        !            79: setsigs()
        !            80: {
        !            81:        register int    (*fnc)();
        !            82:        int             (*signal())(),
        !            83:                        clean();
        !            84: 
        !            85:        fnc = signal(SIGINT, SIG_IGN);
        !            86:        if (fnc != SIG_IGN)
        !            87:                signal(SIGINT, clean);
        !            88: }
        !            89: 
        !            90: 
        !            91: /*
        !            92:  * Clean is called when an interrupt occurs, or on any error.  Its only
        !            93:  * function is to unlink the partial file in at's spooling directory.
        !            94:  */
        !            95: clean()
        !            96: {
        !            97:        if (outfp != NULL) {
        !            98:                fclose(outfp);
        !            99:                unlink(outn);
        !           100:        }
        !           101:        exit(1);
        !           102: }
        !           103: 
        !           104: 
        !           105: /*
        !           106:  * Makefile makes a file in the correct directory with a name of the
        !           107:  * form
        !           108:  *     yymmddhhmm.xx
        !           109:  * where
        !           110:  *     yy is the year (mod 100)
        !           111:  *     mm is the month
        !           112:  *     dd is the day of the month
        !           113:  *     hh is the hour
        !           114:  * and
        !           115:  *     mm is the minute
        !           116:  * at which the command is to be execute.  The xx is simply to make
        !           117:  * the file name unique.  The name is placed in outn and the stream
        !           118:  * is in outfp.  Also, the mode of the file is execute for owner only.
        !           119:  * An initial line is written on the file to remove itself.
        !           120:  */
        !           121: makefile()
        !           122: {
        !           123:        register char   *cp;
        !           124: 
        !           125:        cp = &outn[(sizeof outn) - OUTNL - 1];
        !           126:        sprintf(cp, "%02d%02d%02d%02d%02d.aa", td.tm_year % 100,
        !           127:                1 + td.tm_mon, td.tm_mday, td.tm_hour, td.tm_min);
        !           128:        cp = &outn[(sizeof outn) - 2];
        !           129:        while (access(outn, 0) == 0)
        !           130:                if (++*cp > 'z') {
        !           131:                        *cp = 'a';
        !           132:                        if (++cp[-1] > 'z')
        !           133:                                die("Too many things to do at the same time");
        !           134:                }
        !           135:        outfp = fopen(outn, "w");
        !           136:        if (outfp == NULL)
        !           137:                die("Can't create %s\n", outn);
        !           138:        chmod(outn, MODE);
        !           139:        fprintf(outfp, "rm -f %s\n", outn);
        !           140: }
        !           141: 
        !           142: 
        !           143: /*
        !           144:  * Copyenv writes onto outfp a pair of lines for each item in the environment.
        !           145:  * These lines are of the form
        !           146:  *     var=value
        !           147:  *     export var
        !           148:  * This will cause all exported variables to be reset when the file
        !           149:  * is executed by the shell.
        !           150:  */
        !           151: copyenv()
        !           152: {
        !           153:        register char   **vp,
        !           154:                        *cp,
        !           155:                        ch;
        !           156:        char            *end;
        !           157: 
        !           158:        for (vp=environ; (cp=*vp) != NULL; ++vp) {
        !           159:                while ((ch=*cp++) != '=' && ch != NUL)
        !           160:                        putc(ch, outfp);
        !           161:                end = cp - 1;
        !           162:                fprintf(outfp, "='");
        !           163:                if (ch != '=')
        !           164:                        --cp;
        !           165:                while ((ch=*cp++) != NUL)
        !           166:                        if (ch == '\'')
        !           167:                                fprintf(outfp, "'\\''");
        !           168:                        else
        !           169:                                putc(ch, outfp);
        !           170:                fprintf(outfp, "'\n");
        !           171:                fprintf(outfp, "export %.*s\n", end-*vp, *vp);
        !           172:        }
        !           173: }
        !           174: 
        !           175: 
        !           176: /*
        !           177:  * Copyumask writes out a line onto the stream outfp of the form
        !           178:  *     umask number
        !           179:  * where number is the current umask.  This will cause the shell
        !           180:  * to execute the commands with the current umask.
        !           181:  */
        !           182: copyumask()
        !           183: {
        !           184:        register int    um;
        !           185: 
        !           186:        um = umask(0777);
        !           187:        umask(um);
        !           188:        fprintf(outfp, "umask %03o\n", um);
        !           189: }
        !           190: 
        !           191: 
        !           192: /*
        !           193:  * Copywd writes a line onto the file outfp of the form
        !           194:  *     cd current working directory
        !           195:  * This will cause the current directory to be reset when the file
        !           196:  * is executed by the shell.
        !           197:  */
        !           198: copywd()
        !           199: {
        !           200:        register int    pid;
        !           201:        int             stat;
        !           202:        static char     *args[] = {
        !           203:                                "pwd",
        !           204:                                NULL
        !           205:                        };
        !           206: 
        !           207:        fprintf(outfp, "cd ");
        !           208:        fflush(outfp);
        !           209:        pid = fork();
        !           210:        if (pid == 0) {
        !           211:                dup2(fileno(outfp), STDOUT);
        !           212:                fclose(outfp);
        !           213:                execv("/bin/pwd", args);
        !           214:                execv("/usr/bin/pwd", args);
        !           215:                exit(1);
        !           216:        }
        !           217:        if (pid < 0)
        !           218:                die("Try again");
        !           219:        wait(&stat);
        !           220:        if (stat != 0)
        !           221:                die("Can't find pwd");
        !           222: }
        !           223: 
        !           224: 
        !           225: /*
        !           226:  * Copycmds simply copies from stdin until EOF.
        !           227:  */
        !           228: copycmds()
        !           229: {
        !           230:        register int    ch;
        !           231: 
        !           232:        if (cstr != NULL)
        !           233:                fprintf(outfp, "%s\n", cstr);
        !           234:        else
        !           235:                while ((ch=getchar()) != EOF)
        !           236:                        putc(ch, outfp);
        !           237: }
        !           238: 
        !           239: 
        !           240: /*
        !           241:  * Options cracks the command line options.
        !           242:  */
        !           243: options(argv)
        !           244: register char  *argv[];
        !           245: {
        !           246:        char    **mopts(),
        !           247:                **getdate();
        !           248: 
        !           249:        argv = mopts(++argv);
        !           250:        if (*argv == NULL)
        !           251:                usage();
        !           252:        gettime(*argv++);
        !           253:        argv = mopts(argv);
        !           254:        argv = getdate(argv);
        !           255:        argv = mopts(argv);
        !           256:        if (*argv != NULL) {
        !           257:                if (cstr != NULL)
        !           258:                        usage();
        !           259:                if (freopen(*argv, "r", stdin) == NULL)
        !           260:                        die("Can't open %s", *argv);
        !           261:                argv = mopts(++argv);
        !           262:        }
        !           263:        if (*argv != NULL)
        !           264:                usage();
        !           265:        cyday();
        !           266: }
        !           267: 
        !           268: 
        !           269: /*
        !           270:  * Mopts cracks the `minus' options.  It returns the first
        !           271:  * unused argument.
        !           272:  */
        !           273: char   **
        !           274: mopts(argv)
        !           275: register char  *argv[];
        !           276: {
        !           277:        register char   *str,
        !           278:                        ch;
        !           279: 
        !           280:        for (str=*argv; str != NULL  &&  *str++ == '-'; str=*++argv)
        !           281:                for (ch=*str++; ch != NUL; ch=*str++)
        !           282:                        switch (ch) {
        !           283:                        case 'c':
        !           284:                                if ((cstr = *++argv) == NULL)
        !           285:                                        usage();
        !           286:                                break;
        !           287:                        case 'v':
        !           288:                                vflg = TRUE;
        !           289:                                break;
        !           290:                        default:
        !           291:                                usage();
        !           292:                        }
        !           293:        return (argv);
        !           294: }
        !           295: 
        !           296: 
        !           297: /*
        !           298:  * Usage give the usage message and dies.
        !           299:  */
        !           300: usage()
        !           301: {
        !           302:        static char     umsg[]  =
        !           303: "usage: at [-v] [-c command] time [month month_day | week_day] [file]";
        !           304: 
        !           305:        die(umsg);
        !           306: }
        !           307: 
        !           308: 
        !           309: /*
        !           310:  * Die prints a message on stderr and exits with status one.
        !           311:  */
        !           312: die(str)
        !           313: char   *str;
        !           314: {
        !           315:        fprintf(stderr, "%r\n", &str);
        !           316:        clean();
        !           317: }
        !           318: 
        !           319: 
        !           320: /*
        !           321:  * Gettime takes the string `str' and sets the td minute and
        !           322:  * hour fields appropriately.  A time is:
        !           323:  *     either
        !           324:  *             0-2 digits (hours)
        !           325:  *     or
        !           326:  *             3-4 digits (hours and minutes)
        !           327:  *     possibly followed by a string starting with
        !           328:  *             a       for A.M.
        !           329:  *             p       for P.M.
        !           330:  *             n       for noon
        !           331:  *             m       for mid-night.
        !           332:  */
        !           333: gettime(str)
        !           334: char   *str;
        !           335: {
        !           336:        register int    ch,
        !           337:                        hour;
        !           338:        int             min;
        !           339:        char            *chp;
        !           340: 
        !           341:        hour = 0;
        !           342:        chp = str;
        !           343:        for (ch=*chp++; isascii(ch) && isdigit(ch); ch = *chp++)
        !           344:                hour = 10*hour + ch - '0';
        !           345:        if (chp - str - 1 <= 2)
        !           346:                hour *= 100;
        !           347:        min = hour % 100;
        !           348:        hour /= 100;
        !           349:        if (hour >= 24 || min >= 60)
        !           350:                usage();
        !           351:        switch (ch) {
        !           352:        case NUL:
        !           353:                break;
        !           354:        case 'a':
        !           355:                if (hour == 12)
        !           356:                        hour = 0;
        !           357:                break;
        !           358:        case 'p':
        !           359:                if (hour < 12)
        !           360:                        hour += 12;
        !           361:                break;
        !           362:        case 'n':
        !           363:                hour = 12;
        !           364:                break;
        !           365:        case 'm':
        !           366:                hour = 0;
        !           367:                break;
        !           368:        default:
        !           369:                usage();
        !           370:        }
        !           371:        td.tm_hour = hour;
        !           372:        td.tm_min = min;
        !           373: }
        !           374: 
        !           375: 
        !           376: /*
        !           377:  * Getdate fills in either the month and month-day or the week-day
        !           378:  * fields of td from the date pointed to by `argv'.  It returns
        !           379:  * a pointer to the next argument.
        !           380:  */
        !           381: char   **
        !           382: getdate(argv)
        !           383: register char  *argv[];
        !           384: {
        !           385:        register int    idx;
        !           386:        static char     *day[]  = {
        !           387:                                "sunday",       "monday",
        !           388:                                "tuesday",      "wednesday",
        !           389:                                "thursday",     "friday",
        !           390:                                "saturday",     NULL
        !           391:                        },
        !           392:                        *month[]        = {
        !           393:                                "january",      "february",
        !           394:                                "march",        "april",        
        !           395:                                "may",          "june", 
        !           396:                                "july",         "august",
        !           397:                                "september",    "october",      
        !           398:                                "november",     "december",     
        !           399:                                NULL
        !           400:                        };
        !           401: 
        !           402:        if (*argv == NULL)
        !           403:                return (argv);
        !           404:        idx = mut(*argv, month);
        !           405:        if (idx != EOF) {
        !           406:                td.tm_mon = idx;
        !           407:                if (*++argv == NULL || (td.tm_mday=atoi(*argv++)) == 0)
        !           408:                        usage();
        !           409:                return (argv);
        !           410:        }
        !           411:        idx = mut(*argv, day);
        !           412:        if (idx != EOF) {
        !           413:                td.tm_wday = idx;
        !           414:                ++argv;
        !           415:        }
        !           416:        if (*argv != NULL && (wflg = strcmp(*argv, "week")==0))
        !           417:                ++argv;
        !           418:        return (argv);
        !           419: }
        !           420: 
        !           421: 
        !           422: /*
        !           423:  * Mut searches the table `tbl' for entrys which may be trunctated to
        !           424:  * `str'.  If there is exactly one such entry, it returns its ordinal.
        !           425:  * Otherwise it returns EOF.  Note that `tbl' should be NULL-terminated.
        !           426:  */
        !           427: mut(str, tbl)
        !           428: register char  *str;
        !           429: char           *tbl[];
        !           430: {
        !           431:        register char   **tp;
        !           432:        register int    len;
        !           433:        int             res;
        !           434: 
        !           435:        tp = tbl;
        !           436:        len = strlen(str);
        !           437:        do {
        !           438:                if (*tp == NULL)
        !           439:                        return (EOF);
        !           440:        } while (strncmp(*tp++, str, len) != 0);
        !           441:        res = tp - tbl - 1;
        !           442:        do {
        !           443:                if (*tp == NULL)
        !           444:                        return (res);
        !           445:        } while (strncmp(*tp++, str, len) != 0);
        !           446:        return (EOF);
        !           447: }
        !           448: 
        !           449: 
        !           450: /*
        !           451:  * Cyday computes the year and year-day on which the command should be
        !           452:  * performed.  It does this on the basis of wflg (which is true iff
        !           453:  * we should delay by a week) and td.
        !           454:  * The result is placed in td.
        !           455:  */
        !           456: cyday()
        !           457: {
        !           458:        register int            late,
        !           459:                                len;
        !           460:        register struct tm      *ct;            /* current time */
        !           461:        time_t                  now;
        !           462: 
        !           463:        time(&now);
        !           464:        ct = localtime(&now);
        !           465:        td.tm_yday = ct->tm_yday;
        !           466:        td.tm_year = ct->tm_year;
        !           467:        if (td.tm_mon != UNKNOWN) {
        !           468:                late = td.tm_mon <= ct->tm_mon;
        !           469:                late &= td.tm_mon < ct->tm_mon
        !           470:                        || td.tm_mday < ct->tm_mday;
        !           471:                if (late)
        !           472:                        ++td.tm_year;
        !           473:                td.tm_yday = mdtoyd(td.tm_year + YBASE, td.tm_mon, td.tm_mday);
        !           474:                td.tm_wday = ydtowd(td.tm_year + YBASE, td.tm_yday);
        !           475:        } else {
        !           476:                late = td.tm_hour <= ct->tm_hour;
        !           477:                late &= td.tm_hour < ct->tm_hour
        !           478:                        || td.tm_min <= ct->tm_min;
        !           479:                if (td.tm_wday != UNKNOWN) {
        !           480:                        td.tm_yday += (td.tm_wday + WEEK - ct->tm_wday) % WEEK;
        !           481:                        late &= td.tm_wday == ct->tm_wday;
        !           482:                        if (late | wflg)
        !           483:                                td.tm_yday += WEEK;
        !           484:                } else {
        !           485:                        td.tm_wday = ct->tm_wday;
        !           486:                        if (wflg)
        !           487:                                td.tm_yday += WEEK;
        !           488:                        else if (late) {
        !           489:                                ++td.tm_yday;
        !           490:                                td.tm_wday = (td.tm_wday+1) % WEEK;
        !           491:                        }
        !           492:                }
        !           493:                len = (isleap(td.tm_year + YBASE)) ? YEAR+1 : YEAR;
        !           494:                if (td.tm_yday >= len) {
        !           495:                        td.tm_yday -= len;
        !           496:                        ++td.tm_year;
        !           497:                }
        !           498:                ydtom(td.tm_year + YBASE, td.tm_yday, &td.tm_mon, &td.tm_mday);
        !           499:        }
        !           500: }
        !           501: 
        !           502: 
        !           503: /*
        !           504:  * Isleap returns TRUE iff the year `year' is a leap year.
        !           505:  */
        !           506: isleap(year)
        !           507: register int   year;
        !           508: {
        !           509:        return (year%4 == 0  &&  (year%100 != 0 || year%400 == 0));
        !           510: }
        !           511: 
        !           512: 
        !           513: /*
        !           514:  * The array mlen is an array of month lengths, indexed by the
        !           515:  * month number (0 - 11).
        !           516:  */
        !           517: static int     mlen[]  = {             /* month lengths */
        !           518:                        31,     28,     31,     30,
        !           519:                        31,     30,     31,     31,
        !           520:                        30,     31,     30,     31
        !           521:                };
        !           522: 
        !           523: 
        !           524: /*
        !           525:  * Mdtoyd returns the year-day for the date with year `year', month
        !           526:  * `month' and month-day `mday'.
        !           527:  */
        !           528: mdtoyd(year, month, mday)
        !           529: register int   month;
        !           530: int            year,
        !           531:                mday;
        !           532: {
        !           533:        register int    res,
        !           534:                        leap;
        !           535: 
        !           536:        res = mday - 1;
        !           537:        if (leap = isleap(year))
        !           538:                ++mlen[LEAPM];
        !           539:        while (--month >= 0)
        !           540:                res += mlen[month];
        !           541:        if (leap)
        !           542:                --mlen[LEAPM];
        !           543:        return (res);
        !           544: }
        !           545: 
        !           546: 
        !           547: /*
        !           548:  * Ydtowd returns the week day for the date with year `year' and
        !           549:  * year-day `yday'.
        !           550:  */
        !           551: ydtowd(year, yday)
        !           552: register int   year;
        !           553: int            yday;
        !           554: {
        !           555:        register int    wday;
        !           556: 
        !           557:        --year;
        !           558:        wday = FSTDAY  +  year * (YEAR%WEEK) + yday%WEEK;
        !           559:        wday += (year+4) / 4;
        !           560:        wday -= (year+100) / 100;
        !           561:        wday += (year+400) / 400;
        !           562:        wday %= WEEK;
        !           563:        return (wday);
        !           564: }
        !           565: 
        !           566: 
        !           567: /*
        !           568:  * Ydtom sets `pmonth' and `pmday' to the month and month-day of the
        !           569:  * date with year `year' and year-day `yday'.
        !           570:  */
        !           571: ydtom(year, yday, pmonth, pmday)
        !           572: int    year,
        !           573:        yday,
        !           574:        *pmonth,
        !           575:        *pmday;
        !           576: {
        !           577:        register int    mday,
        !           578:                        month;
        !           579:        int             leap;
        !           580: 
        !           581:        if (leap = isleap(year))
        !           582:                ++mlen[LEAPM];
        !           583:        mday = yday;
        !           584:        for (month=0; mday >= mlen[month]; ++month)
        !           585:                mday -= mlen[month];
        !           586:        *pmonth = month;
        !           587:        *pmday = mday + 1;
        !           588:        if (leap)
        !           589:                --mlen[LEAPM];
        !           590: }

unix.superglobalmegacorp.com

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