Annotation of coherent/d/bin/at.c, revision 1.1.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.