[BACK]Return to at.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / at

Annotation of src/usr.bin/at/at.c, Revision 1.3

1.3     ! millert     1: /*     $OpenBSD: at.c,v 1.2 1996/06/26 05:31:27 deraadt Exp $  */
1.1       deraadt     2: /*     $NetBSD: at.c,v 1.4 1995/03/25 18:13:31 glass Exp $     */
                      3:
                      4: /*
                      5:  * at.c : Put file into atrun queue
                      6:  * Copyright (C) 1993  Thomas Koenig
                      7:  *
                      8:  * Atrun & Atq modifications
                      9:  * Copyright (C) 1993  David Parsons
                     10:  * All rights reserved.
                     11:  *
                     12:  * Redistribution and use in source and binary forms, with or without
                     13:  * modification, are permitted provided that the following conditions
                     14:  * are met:
                     15:  * 1. Redistributions of source code must retain the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer.
                     17:  * 2. The name of the author(s) may not be used to endorse or promote
                     18:  *    products derived from this software without specific prior written
                     19:  *    permission.
                     20:  *
                     21:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
                     22:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     23:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
                     24:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
                     25:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
                     26:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     27:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     28:  * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     29:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
                     30:  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     31:  */
                     32:
                     33: #define _USE_BSD 1
                     34:
                     35: /* System Headers */
                     36: #include <sys/types.h>
                     37: #include <sys/stat.h>
                     38: #include <sys/wait.h>
                     39: #include <ctype.h>
                     40: #include <dirent.h>
                     41: #include <errno.h>
                     42: #include <fcntl.h>
                     43: #include <pwd.h>
                     44: #include <signal.h>
                     45: #include <stddef.h>
                     46: #include <stdio.h>
                     47: #include <stdlib.h>
                     48: #include <string.h>
                     49: #include <time.h>
                     50: #include <unistd.h>
                     51:
                     52: /* Local headers */
                     53: #include "at.h"
                     54: #include "panic.h"
                     55: #include "parsetime.h"
                     56: #include "pathnames.h"
                     57: #define MAIN
                     58: #include "privs.h"
                     59:
                     60: /* Macros */
                     61: #define ALARMC 10              /* Number of seconds to wait for timeout */
                     62:
                     63: #define SIZE 255
                     64: #define TIMESIZE 50
                     65:
                     66: /* File scope variables */
                     67: #ifndef lint
1.3     ! millert    68: static char rcsid[] = "$OpenBSD: at.c,v 1.2 1996/06/26 05:31:27 deraadt Exp $";
1.1       deraadt    69: #endif
                     70:
                     71: char *no_export[] =
                     72: {
                     73:        "TERM", "TERMCAP", "DISPLAY", "_"
                     74: };
                     75: static send_mail = 0;
                     76:
                     77: /* External variables */
                     78: extern char **environ;
                     79: int fcreated;
                     80: char *namep;
                     81: char atfile[FILENAME_MAX];
                     82:
                     83: char *atinput = (char *) 0;    /* where to get input from */
                     84: char atqueue = 0;              /* which queue to examine for jobs (atq) */
                     85: char atverify = 0;             /* verify time instead of queuing job */
                     86:
                     87: /* Function declarations */
                     88: static void sigc       __P((int signo));
                     89: static void alarmc     __P((int signo));
                     90: static char *cwdname   __P((void));
                     91: static void writefile  __P((time_t runtimer, char queue));
                     92: static void list_jobs  __P((void));
                     93:
                     94: /* Signal catching functions */
                     95:
                     96: static void
                     97: sigc(signo)
                     98:        int signo;
                     99: {
                    100: /* If the user presses ^C, remove the spool file and exit
                    101:  */
                    102:        if (fcreated) {
                    103:                PRIV_START
                    104:                unlink(atfile);
                    105:                PRIV_END
                    106:        }
                    107:
                    108:        exit(EXIT_FAILURE);
                    109: }
                    110:
                    111: static void
                    112: alarmc(signo)
                    113:        int signo;
                    114: {
                    115: /* Time out after some seconds
                    116:  */
                    117:        panic("File locking timed out");
                    118: }
                    119:
                    120: /* Local functions */
                    121:
                    122: static char *
                    123: cwdname()
                    124: {
                    125: /* Read in the current directory; the name will be overwritten on
                    126:  * subsequent calls.
                    127:  */
                    128:        static char *ptr = NULL;
                    129:        static size_t size = SIZE;
                    130:
                    131:        if (ptr == NULL)
                    132:                ptr = (char *) malloc(size);
                    133:
                    134:        while (1) {
                    135:                if (ptr == NULL)
                    136:                        panic("Out of memory");
                    137:
                    138:                if (getcwd(ptr, size - 1) != NULL)
                    139:                        return ptr;
                    140:
                    141:                if (errno != ERANGE)
                    142:                        perr("Cannot get directory");
                    143:
                    144:                free(ptr);
                    145:                size += SIZE;
                    146:                ptr = (char *) malloc(size);
                    147:        }
                    148: }
                    149:
                    150: static void
                    151: writefile(runtimer, queue)
                    152:        time_t runtimer;
                    153:        char queue;
                    154: {
                    155:        /*
                    156:         * This does most of the work if at or batch are invoked for
                    157:         * writing a job.
                    158:         */
                    159:        int i;
                    160:        char *ap, *ppos, *mailname;
                    161:        struct passwd *pass_entry;
                    162:        struct stat statbuf;
                    163:        int fdes, lockdes, fd2;
                    164:        FILE *fp, *fpin;
                    165:        struct sigaction act;
                    166:        char **atenv;
                    167:        int ch;
                    168:        mode_t cmask;
                    169:        struct flock lock;
                    170:
                    171:        /*
                    172:         * Install the signal handler for SIGINT; terminate after removing the
                    173:         * spool file if necessary
                    174:         */
                    175:        act.sa_handler = sigc;
                    176:        sigemptyset(&(act.sa_mask));
                    177:        act.sa_flags = 0;
                    178:
                    179:        sigaction(SIGINT, &act, NULL);
                    180:
                    181:        strcpy(atfile, _PATH_ATJOBS);
                    182:        ppos = atfile + strlen(_PATH_ATJOBS);
                    183:
                    184:        /*
                    185:         * Loop over all possible file names for running something at this
                    186:         *  particular time, see if a file is there; the first empty slot at
                    187:         *  any particular time is used.  Lock the file _PATH_LOCKFILE first
                    188:         *  to make sure we're alone when doing this.
                    189:         */
                    190:
                    191:        PRIV_START
                    192:
                    193:        if ((lockdes = open(_PATH_LOCKFILE, O_WRONLY | O_CREAT, 0600)) < 0)
                    194:                perr2("Cannot open lockfile ", _PATH_LOCKFILE);
                    195:
                    196:        lock.l_type = F_WRLCK;
                    197:        lock.l_whence = SEEK_SET;
                    198:        lock.l_start = 0;
                    199:        lock.l_len = 0;
                    200:
                    201:        act.sa_handler = alarmc;
                    202:        sigemptyset(&(act.sa_mask));
                    203:        act.sa_flags = 0;
                    204:
                    205:        /*
                    206:         * Set an alarm so a timeout occurs after ALARMC seconds, in case
                    207:         * something is seriously broken.
                    208:         */
                    209:        sigaction(SIGALRM, &act, NULL);
                    210:        alarm(ALARMC);
                    211:        fcntl(lockdes, F_SETLKW, &lock);
                    212:        alarm(0);
                    213:
                    214:        for (i = 0; i < AT_MAXJOBS; i++) {
                    215:                sprintf(ppos, "%c%8lx.%3x", queue,
                    216:                    (unsigned long) (runtimer / 60), i);
                    217:                for (ap = ppos; *ap != '\0'; ap++)
                    218:                        if (*ap == ' ')
                    219:                                *ap = '0';
                    220:
                    221:                if (stat(atfile, &statbuf) != 0) {
                    222:                        if (errno == ENOENT)
                    223:                                break;
                    224:                        else
                    225:                                perr2("Cannot access ", _PATH_ATJOBS);
                    226:                }
                    227:        }                       /* for */
                    228:
                    229:        if (i >= AT_MAXJOBS)
                    230:                panic("Too many jobs already");
                    231:
                    232:        /*
                    233:         * Create the file. The x bit is only going to be set after it has
                    234:         * been completely written out, to make sure it is not executed in
                    235:         * the meantime.  To make sure they do not get deleted, turn off
                    236:         * their r bit.  Yes, this is a kluge.
                    237:         */
                    238:        cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR);
                    239:        if ((fdes = creat(atfile, O_WRONLY)) == -1)
                    240:                perr("Cannot create atjob file");
                    241:
                    242:        if ((fd2 = dup(fdes)) < 0)
                    243:                perr("Error in dup() of job file");
                    244:
                    245:        if (fchown(fd2, real_uid, -1) != 0)
                    246:                perr("Cannot give away file");
                    247:
                    248:        PRIV_END
                    249:
                    250:        /*
                    251:         * We've successfully created the file; let's set the flag so it
                    252:         * gets removed in case of an interrupt or error.
                    253:         */
                    254:        fcreated = 1;
                    255:
                    256:        /* Now we can release the lock, so other people can access it */
                    257:        lock.l_type = F_UNLCK;
                    258:        lock.l_whence = SEEK_SET;
                    259:        lock.l_start = 0;
                    260:        lock.l_len = 0;
                    261:        fcntl(lockdes, F_SETLKW, &lock);
                    262:        close(lockdes);
                    263:
                    264:        if ((fp = fdopen(fdes, "w")) == NULL)
                    265:                panic("Cannot reopen atjob file");
                    266:
                    267:        /*
                    268:         * Get the userid to mail to, first by trying getlogin(), which
                    269:         * reads /etc/utmp, then from LOGNAME, finally from getpwuid().
                    270:         */
                    271:        mailname = getlogin();
                    272:        if (mailname == NULL)
                    273:                mailname = getenv("LOGNAME");
                    274:
                    275:        if ((mailname == NULL) || (mailname[0] == '\0')
                    276:            || (strlen(mailname) > 8)) {
                    277:                pass_entry = getpwuid(getuid());
                    278:                if (pass_entry != NULL)
                    279:                        mailname = pass_entry->pw_name;
                    280:        }
                    281:
                    282:        if (atinput != (char *) NULL) {
                    283:                fpin = freopen(atinput, "r", stdin);
                    284:                if (fpin == NULL)
                    285:                        perr("Cannot open input file");
                    286:        }
                    287:        fprintf(fp, "#! /bin/sh\n# mail %8s %d\n", mailname, send_mail);
                    288:
                    289:        /* Write out the umask at the time of invocation */
                    290:        fprintf(fp, "umask %lo\n", (unsigned long) cmask);
                    291:
                    292:        /*
                    293:         * Write out the environment. Anything that may look like a special
                    294:         * character to the shell is quoted, except for \n, which is done
                    295:         * with a pair of "'s.  Dont't export the no_export list (such as
                    296:         * TERM or DISPLAY) because we don't want these.
                    297:         */
                    298:        for (atenv = environ; *atenv != NULL; atenv++) {
                    299:                int export = 1;
                    300:                char *eqp;
                    301:
                    302:                eqp = strchr(*atenv, '=');
                    303:                if (ap == NULL)
                    304:                        eqp = *atenv;
                    305:                else {
                    306:                        int i;
                    307:
                    308:                        for (i = 0;i < sizeof(no_export) /
                    309:                            sizeof(no_export[0]); i++) {
                    310:                                export = export
                    311:                                    && (strncmp(*atenv, no_export[i],
                    312:                                        (size_t) (eqp - *atenv)) != 0);
                    313:                        }
                    314:                        eqp++;
                    315:                }
                    316:
                    317:                if (export) {
                    318:                        fwrite(*atenv, sizeof(char), eqp - *atenv, fp);
                    319:                        for (ap = eqp; *ap != '\0'; ap++) {
                    320:                                if (*ap == '\n')
                    321:                                        fprintf(fp, "\"\n\"");
                    322:                                else {
                    323:                                        if (!isalnum(*ap))
                    324:                                                fputc('\\', fp);
                    325:
                    326:                                        fputc(*ap, fp);
                    327:                                }
                    328:                        }
                    329:                        fputs("; export ", fp);
                    330:                        fwrite(*atenv, sizeof(char), eqp - *atenv - 1, fp);
                    331:                        fputc('\n', fp);
                    332:
                    333:                }
                    334:        }
                    335:        /*
                    336:         * Cd to the directory at the time and write out all the commands
                    337:         * the user supplies from stdin.
                    338:         */
                    339:        fprintf(fp, "cd %s\n", cwdname());
                    340:
1.3     ! millert   341:        if ((ch = getchar()) == EOF)
        !           342:                panic("Input error");
        !           343:
        !           344:        do {
1.1       deraadt   345:                fputc(ch, fp);
1.3     ! millert   346:        } while ((ch = getchar()) != EOF);
1.1       deraadt   347:
                    348:        fprintf(fp, "\n");
                    349:        if (ferror(fp))
                    350:                panic("Output error");
                    351:
                    352:        if (ferror(stdin))
                    353:                panic("Input error");
                    354:
                    355:        fclose(fp);
                    356:
                    357:        /*
                    358:         * Set the x bit so that we're ready to start executing
                    359:         */
                    360:        if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
                    361:                perr("Cannot give away file");
                    362:
                    363:        close(fd2);
                    364:        fprintf(stderr, "Job %s will be executed using /bin/sh\n", ppos);
                    365: }
                    366:
                    367: static void
                    368: list_jobs()
                    369: {
                    370:        /*
                    371:         * List all a user's jobs in the queue, by looping through
                    372:         * _PATH_ATJOBS, or everybody's if we are root
                    373:         */
                    374:        struct passwd *pw;
                    375:        DIR *spool;
                    376:        struct dirent *dirent;
                    377:        struct stat buf;
                    378:        struct tm runtime;
                    379:        unsigned long ctm;
                    380:        char queue;
                    381:        time_t runtimer;
                    382:        char timestr[TIMESIZE];
                    383:        int first = 1;
                    384:
                    385:        PRIV_START
                    386:
                    387:            if (chdir(_PATH_ATJOBS) != 0)
                    388:                perr2("Cannot change to ", _PATH_ATJOBS);
                    389:
                    390:        if ((spool = opendir(".")) == NULL)
                    391:                perr2("Cannot open ", _PATH_ATJOBS);
                    392:
                    393:        /* Loop over every file in the directory */
                    394:        while ((dirent = readdir(spool)) != NULL) {
                    395:                if (stat(dirent->d_name, &buf) != 0)
                    396:                        perr2("Cannot stat in ", _PATH_ATJOBS);
                    397:
                    398:                /*
                    399:                 * See it's a regular file and has its x bit turned on and
                    400:                 * is the user's
                    401:                 */
                    402:                if (!S_ISREG(buf.st_mode)
                    403:                    || ((buf.st_uid != real_uid) && !(real_uid == 0))
                    404:                    || !(S_IXUSR & buf.st_mode || atverify))
                    405:                        continue;
                    406:
                    407:                if (sscanf(dirent->d_name, "%c%8lx", &queue, &ctm) != 2)
                    408:                        continue;
                    409:
                    410:                if (atqueue && (queue != atqueue))
                    411:                        continue;
                    412:
                    413:                runtimer = 60 * (time_t) ctm;
                    414:                runtime = *localtime(&runtimer);
                    415:                strftime(timestr, TIMESIZE, "%X %x", &runtime);
                    416:                if (first) {
                    417:                        printf("Date\t\t\tOwner\tQueue\tJob#\n");
                    418:                        first = 0;
                    419:                }
                    420:                pw = getpwuid(buf.st_uid);
                    421:
                    422:                printf("%s\t%s\t%c%s\t%s\n",
                    423:                    timestr,
                    424:                    pw ? pw->pw_name : "???",
                    425:                    queue,
                    426:                    (S_IXUSR & buf.st_mode) ? "" : "(done)",
                    427:                    dirent->d_name);
                    428:        }
                    429:        PRIV_END
                    430: }
                    431:
                    432: static void
                    433: delete_jobs(argc, argv)
                    434:        int argc;
                    435:        char **argv;
                    436: {
                    437:        /* Delete every argument (job - ID) given */
                    438:        int i;
                    439:        struct stat buf;
                    440:
                    441:        PRIV_START
                    442:
                    443:            if (chdir(_PATH_ATJOBS) != 0)
                    444:                perr2("Cannot change to ", _PATH_ATJOBS);
                    445:
                    446:        for (i = optind; i < argc; i++) {
                    447:                if (stat(argv[i], &buf) != 0)
                    448:                        perr(argv[i]);
                    449:                if ((buf.st_uid != real_uid) && !(real_uid == 0)) {
                    450:                        fprintf(stderr, "%s: Not owner\n", argv[i]);
                    451:                        exit(EXIT_FAILURE);
                    452:                }
                    453:                if (unlink(argv[i]) != 0)
                    454:                        perr(argv[i]);
                    455:        }
                    456:        PRIV_END
                    457: }                              /* delete_jobs */
                    458:
                    459: /* Global functions */
                    460:
                    461: int
                    462: main(argc, argv)
                    463:        int argc;
                    464:        char **argv;
                    465: {
                    466:        int c;
                    467:        char queue = 'a';
                    468:        char *pgm;
                    469:
                    470:        enum {
                    471:                ATQ, ATRM, AT, BATCH
                    472:        };                      /* what program we want to run */
                    473:        int program = AT;       /* our default program */
                    474:        char *options = "q:f:mv";       /* default options for at */
                    475:        time_t timer;
                    476:
                    477:        RELINQUISH_PRIVS
                    478:
                    479:        /* Eat any leading paths */
                    480:        if ((pgm = strrchr(argv[0], '/')) == NULL)
                    481:                pgm = argv[0];
                    482:        else
                    483:                pgm++;
                    484:
                    485:        namep = pgm;
                    486:
                    487:        /* find out what this program is supposed to do */
                    488:        if (strcmp(pgm, "atq") == 0) {
                    489:                program = ATQ;
                    490:                options = "q:v";
                    491:        } else if (strcmp(pgm, "atrm") == 0) {
                    492:                program = ATRM;
                    493:                options = "";
                    494:        } else if (strcmp(pgm, "batch") == 0) {
                    495:                program = BATCH;
                    496:                options = "f:mv";
                    497:        }
                    498:
                    499:        /* process whatever options we can process */
                    500:        opterr = 1;
                    501:        while ((c = getopt(argc, argv, options)) != EOF)
                    502:                switch (c) {
                    503:                case 'v':       /* verify time settings */
                    504:                        atverify = 1;
                    505:                        break;
                    506:
                    507:                case 'm':       /* send mail when job is complete */
                    508:                        send_mail = 1;
                    509:                        break;
                    510:
                    511:                case 'f':
                    512:                        atinput = optarg;
                    513:                        break;
                    514:
                    515:                case 'q':       /* specify queue */
                    516:                        if (strlen(optarg) > 1)
                    517:                                usage();
                    518:
                    519:                        atqueue = queue = *optarg;
                    520:                        if ((!islower(queue)) || (queue > 'l'))
                    521:                                usage();
                    522:                        break;
                    523:
                    524:                default:
                    525:                        usage();
                    526:                        break;
                    527:                }
                    528:        /* end of options eating */
                    529:
                    530:        /* select our program */
                    531:        switch (program) {
                    532:        case ATQ:
                    533:                list_jobs();
                    534:                break;
                    535:
                    536:        case ATRM:
                    537:                delete_jobs(argc, argv);
                    538:                break;
                    539:
                    540:        case AT:
                    541:                timer = parsetime(argc, argv);
                    542:                if (atverify) {
                    543:                        struct tm *tm = localtime(&timer);
                    544:
                    545:                        fprintf(stderr, "%s\n", asctime(tm));
                    546:                }
                    547:                writefile(timer, queue);
                    548:                break;
                    549:
                    550:        case BATCH:
                    551:                writefile(time(NULL), 'b');
                    552:                break;
                    553:
                    554:        default:
                    555:                panic("Internal error");
                    556:                break;
                    557:        }
                    558:        exit(EXIT_SUCCESS);
                    559: }