[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.29

1.29    ! millert     1: /*     $OpenBSD: at.c,v 1.28 2002/05/13 18:43:53 millert Exp $ */
1.1       deraadt     2: /*     $NetBSD: at.c,v 1.4 1995/03/25 18:13:31 glass Exp $     */
                      3:
                      4: /*
1.7       millert     5:  *  at.c : Put file into atrun queue
                      6:  *  Copyright (C) 1993, 1994  Thomas Koenig
1.1       deraadt     7:  *
1.7       millert     8:  *  Atrun & Atq modifications
                      9:  *  Copyright (C) 1993  David Parsons
1.1       deraadt    10:  *
1.29    ! millert    11:  *  Traditional BSD behavior and other significant modifications
        !            12:  *  Copyright (C) 2002  Todd C. Miller
        !            13:  *
1.1       deraadt    14:  * Redistribution and use in source and binary forms, with or without
                     15:  * modification, are permitted provided that the following conditions
                     16:  * are met:
                     17:  * 1. Redistributions of source code must retain the above copyright
                     18:  *    notice, this list of conditions and the following disclaimer.
                     19:  * 2. The name of the author(s) may not be used to endorse or promote
                     20:  *    products derived from this software without specific prior written
                     21:  *    permission.
                     22:  *
                     23:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
                     24:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     25:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1.7       millert    26:  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
1.1       deraadt    27:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
                     28:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     29:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     30:  * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     31:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
                     32:  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     33:  */
                     34:
1.7       millert    35: #include <sys/param.h>
1.1       deraadt    36: #include <sys/stat.h>
1.25      millert    37: #include <sys/time.h>
1.1       deraadt    38: #include <ctype.h>
                     39: #include <dirent.h>
1.29    ! millert    40: #include <err.h>
1.1       deraadt    41: #include <errno.h>
                     42: #include <fcntl.h>
1.29    ! millert    43: #include <locale.h>
1.1       deraadt    44: #include <pwd.h>
                     45: #include <signal.h>
                     46: #include <stddef.h>
                     47: #include <stdio.h>
                     48: #include <stdlib.h>
                     49: #include <string.h>
                     50: #include <time.h>
                     51: #include <unistd.h>
1.7       millert    52: #include <utmp.h>
                     53:
                     54: #if (MAXLOGNAME-1) > UT_NAMESIZE
                     55: #define LOGNAMESIZE UT_NAMESIZE
                     56: #else
                     57: #define LOGNAMESIZE (MAXLOGNAME-1)
                     58: #endif
1.1       deraadt    59:
                     60: #include "at.h"
                     61: #include "panic.h"
                     62: #include "parsetime.h"
1.7       millert    63: #include "perm.h"
1.1       deraadt    64: #include "pathnames.h"
                     65: #define MAIN
                     66: #include "privs.h"
                     67:
                     68: #define ALARMC 10              /* Number of seconds to wait for timeout */
1.29    ! millert    69: #define TIMESIZE 50            /* Size of buffer passed to strftime() */
1.1       deraadt    70:
                     71: #ifndef lint
1.29    ! millert    72: static const char rcsid[] = "$OpenBSD: at.c,v 1.28 2002/05/13 18:43:53 millert Exp $";
1.1       deraadt    73: #endif
                     74:
1.29    ! millert    75: /* Variables to remove from the job's environment. */
1.1       deraadt    76: char *no_export[] =
                     77: {
1.28      millert    78:        "TERM", "TERMCAP", "DISPLAY", "_", "SHELLOPTS", "BASH_VERSINFO",
                     79:        "EUID", "GROUPS", "PPID", "UID", "SSH_AUTH_SOCK", "SSH_AGENT_PID",
1.1       deraadt    80: };
1.7       millert    81:
1.27      millert    82: int program = AT;              /* default program mode */
1.29    ! millert    83: char atfile[PATH_MAX];         /* path to the at spool file */
        !            84: int fcreated;                  /* whether or not we created the file yet */
        !            85: char *atinput = NULL;          /* where to get input from */
1.1       deraadt    86: char atqueue = 0;              /* which queue to examine for jobs (atq) */
1.29    ! millert    87: char vflag = 0;                        /* show completed but unremoved jobs (atq) */
        !            88: char force = 0;                        /* suppress errors (atrm) */
        !            89: char interactive = 0;          /* interactive mode (atrm) */
        !            90: static int send_mail = 0;      /* whether we are sending mail */
1.7       millert    91:
1.21      millert    92: static void sigc(int);
                     93: static void alarmc(int);
                     94: static void writefile(time_t, char);
1.29    ! millert    95: static void list_jobs(int, char **, int, int);
1.25      millert    96: static time_t ttime(const char *);
1.1       deraadt    97:
                     98: static void
1.26      millert    99: sigc(int signo)
1.1       deraadt   100: {
1.7       millert   101:        /* If the user presses ^C, remove the spool file and exit. */
1.1       deraadt   102:        if (fcreated) {
1.26      millert   103:                PRIV_START;
1.7       millert   104:                (void)unlink(atfile);
1.26      millert   105:                PRIV_END;
1.1       deraadt   106:        }
                    107:
1.20      deraadt   108:        _exit(EXIT_FAILURE);
1.1       deraadt   109: }
                    110:
                    111: static void
1.26      millert   112: alarmc(int signo)
1.1       deraadt   113: {
1.20      deraadt   114:        char buf[1024];
                    115:
1.7       millert   116:        /* Time out after some seconds. */
1.25      millert   117:        strlcpy(buf, __progname, sizeof(buf));
1.20      deraadt   118:        strlcat(buf, ": File locking timed out\n", sizeof(buf));
                    119:        write(STDERR_FILENO, buf, strlen(buf));
                    120:        if (fcreated) {
1.26      millert   121:                PRIV_START;
1.20      deraadt   122:                unlink(atfile);
1.26      millert   123:                PRIV_END;
1.20      deraadt   124:        }
                    125:        _exit(EXIT_FAILURE);
1.1       deraadt   126: }
                    127:
1.29    ! millert   128: static int
        !           129: newjob(time_t runtimer, int queue)
        !           130: {
        !           131:        int fd, i;
1.1       deraadt   132:
1.7       millert   133:        /*
1.29    ! millert   134:         * If we have a collision, try shifting the time by up to
        !           135:         * two minutes.  Perhaps it would be better to try different
        !           136:         * queues instead...
1.7       millert   137:         */
1.29    ! millert   138:        for (i = 0; i < 120; i++) {
        !           139:                snprintf(atfile, sizeof(atfile), "%s/%ld.%c",
        !           140:                    _PATH_ATJOBS, (long)runtimer, queue);
        !           141:                fd = open(atfile, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);
        !           142:                if (fd >= 0)
        !           143:                        return (fd);
        !           144:        }
        !           145:        return (-1);
1.1       deraadt   146: }
                    147:
1.29    ! millert   148: /*
        !           149:  * This does most of the work if at or batch are invoked for
        !           150:  * writing a job.
        !           151:  */
1.1       deraadt   152: static void
1.26      millert   153: writefile(time_t runtimer, char queue)
1.1       deraadt   154: {
1.29    ! millert   155:        char *ap, *mailname, *shell;
1.28      millert   156:        char timestr[TIMESIZE];
1.29    ! millert   157:        char path[PATH_MAX];
1.1       deraadt   158:        struct passwd *pass_entry;
1.28      millert   159:        struct tm runtime;
1.1       deraadt   160:        int fdes, lockdes, fd2;
                    161:        FILE *fp, *fpin;
                    162:        struct sigaction act;
                    163:        char **atenv;
                    164:        int ch;
                    165:        mode_t cmask;
1.29    ! millert   166:        extern char **environ;
1.1       deraadt   167:
1.7       millert   168:        (void)setlocale(LC_TIME, "");
                    169:
1.1       deraadt   170:        /*
                    171:         * Install the signal handler for SIGINT; terminate after removing the
                    172:         * spool file if necessary
                    173:         */
1.15      deraadt   174:        memset(&act, 0, sizeof act);
1.1       deraadt   175:        act.sa_handler = sigc;
1.29    ! millert   176:        sigemptyset(&act.sa_mask);
1.1       deraadt   177:        act.sa_flags = 0;
                    178:        sigaction(SIGINT, &act, NULL);
                    179:
1.26      millert   180:        PRIV_START;
1.1       deraadt   181:
1.22      millert   182:        /*
1.29    ! millert   183:         * Lock the jobs dir so we don't have to worry about someone
        !           184:         * else grabbing a file name out from under us.
1.22      millert   185:         * Set an alarm so we don't sleep forever waiting on the lock.
                    186:         * If we don't succeed with ALARMC seconds, something is wrong...
                    187:         */
1.29    ! millert   188:        memset(&act, 0, sizeof act);
1.1       deraadt   189:        act.sa_handler = alarmc;
1.29    ! millert   190:        sigemptyset(&act.sa_mask);
1.1       deraadt   191:        act.sa_flags = 0;
                    192:        sigaction(SIGALRM, &act, NULL);
                    193:        alarm(ALARMC);
1.22      millert   194:        lockdes = open(_PATH_ATJOBS, O_RDONLY|O_EXLOCK, 0);
1.1       deraadt   195:        alarm(0);
                    196:
1.22      millert   197:        if (lockdes < 0)
                    198:                perr("Cannot lock jobs dir");
                    199:
1.1       deraadt   200:        /*
                    201:         * Create the file. The x bit is only going to be set after it has
                    202:         * been completely written out, to make sure it is not executed in
                    203:         * the meantime.  To make sure they do not get deleted, turn off
                    204:         * their r bit.  Yes, this is a kluge.
                    205:         */
                    206:        cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR);
1.29    ! millert   207:        if ((fdes = newjob(runtimer, queue)) == -1)
1.1       deraadt   208:                perr("Cannot create atjob file");
                    209:
                    210:        if ((fd2 = dup(fdes)) < 0)
                    211:                perr("Error in dup() of job file");
                    212:
1.7       millert   213:        if (fchown(fd2, real_uid, real_gid) != 0)
1.1       deraadt   214:                perr("Cannot give away file");
                    215:
1.26      millert   216:        PRIV_END;
1.1       deraadt   217:
                    218:        /*
                    219:         * We've successfully created the file; let's set the flag so it
                    220:         * gets removed in case of an interrupt or error.
                    221:         */
                    222:        fcreated = 1;
                    223:
                    224:        /* Now we can release the lock, so other people can access it */
1.7       millert   225:        (void)close(lockdes);
1.1       deraadt   226:
                    227:        if ((fp = fdopen(fdes, "w")) == NULL)
                    228:                panic("Cannot reopen atjob file");
                    229:
                    230:        /*
1.18      millert   231:         * Get the userid to mail to, first by trying getlogin(), which asks
                    232:         * the kernel, then from $LOGNAME or $USER, finally from getpwuid().
1.1       deraadt   233:         */
                    234:        mailname = getlogin();
1.5       millert   235:        if (mailname == NULL && (mailname = getenv("LOGNAME")) == NULL)
                    236:                mailname = getenv("USER");
1.1       deraadt   237:
1.7       millert   238:        if ((mailname == NULL) || (mailname[0] == '\0') ||
                    239:            (strlen(mailname) > LOGNAMESIZE) || (getpwnam(mailname) == NULL)) {
                    240:                pass_entry = getpwuid(real_uid);
1.1       deraadt   241:                if (pass_entry != NULL)
                    242:                        mailname = pass_entry->pw_name;
                    243:        }
                    244:
1.28      millert   245:        /*
                    246:         * Get the shell to run the job under.  First check $SHELL, falling
                    247:         * back to the user's shell in the password database or, failing
                    248:         * that, /bin/sh.
                    249:         */
                    250:        if ((shell = getenv("SHELL")) == NULL || *shell == '\0') {
                    251:                pass_entry = getpwuid(real_uid);
                    252:                if (pass_entry != NULL && *pass_entry->pw_shell != '\0')
                    253:                        shell = pass_entry->pw_shell;
                    254:                else
                    255:                        shell = _PATH_BSHELL;
                    256:        }
                    257:
1.13      kstailey  258:        if (atinput != NULL) {
1.1       deraadt   259:                fpin = freopen(atinput, "r", stdin);
                    260:                if (fpin == NULL)
                    261:                        perr("Cannot open input file");
                    262:        }
1.7       millert   263:        (void)fprintf(fp, "#!/bin/sh\n# atrun uid=%u gid=%u\n# mail %*s %d\n",
                    264:            real_uid, real_gid, LOGNAMESIZE, mailname, send_mail);
1.1       deraadt   265:
                    266:        /* Write out the umask at the time of invocation */
1.7       millert   267:        (void)fprintf(fp, "umask %o\n", cmask);
1.1       deraadt   268:
                    269:        /*
                    270:         * Write out the environment. Anything that may look like a special
                    271:         * character to the shell is quoted, except for \n, which is done
                    272:         * with a pair of "'s.  Dont't export the no_export list (such as
                    273:         * TERM or DISPLAY) because we don't want these.
                    274:         */
                    275:        for (atenv = environ; *atenv != NULL; atenv++) {
                    276:                int export = 1;
                    277:                char *eqp;
                    278:
                    279:                eqp = strchr(*atenv, '=');
1.19      millert   280:                if (eqp == NULL)
1.1       deraadt   281:                        eqp = *atenv;
                    282:                else {
                    283:                        int i;
                    284:
                    285:                        for (i = 0;i < sizeof(no_export) /
                    286:                            sizeof(no_export[0]); i++) {
                    287:                                export = export
                    288:                                    && (strncmp(*atenv, no_export[i],
                    289:                                        (size_t) (eqp - *atenv)) != 0);
                    290:                        }
                    291:                        eqp++;
                    292:                }
                    293:
                    294:                if (export) {
1.7       millert   295:                        (void)fwrite(*atenv, sizeof(char), eqp - *atenv, fp);
1.1       deraadt   296:                        for (ap = eqp; *ap != '\0'; ap++) {
                    297:                                if (*ap == '\n')
1.7       millert   298:                                        (void)fprintf(fp, "\"\n\"");
1.1       deraadt   299:                                else {
1.7       millert   300:                                        if (!isalnum(*ap)) {
                    301:                                                switch (*ap) {
                    302:                                                case '%': case '/': case '{':
                    303:                                                case '[': case ']': case '=':
                    304:                                                case '}': case '@': case '+':
                    305:                                                case '#': case ',': case '.':
                    306:                                                case ':': case '-': case '_':
                    307:                                                        break;
                    308:                                                default:
                    309:                                                        (void)fputc('\\', fp);
                    310:                                                        break;
                    311:                                                }
                    312:                                        }
                    313:                                        (void)fputc(*ap, fp);
1.1       deraadt   314:                                }
                    315:                        }
1.7       millert   316:                        (void)fputs("; export ", fp);
                    317:                        (void)fwrite(*atenv, sizeof(char), eqp - *atenv - 1, fp);
                    318:                        (void)fputc('\n', fp);
                    319:                }
                    320:        }
                    321:        /*
                    322:         * Cd to the directory at the time and write out all the
                    323:         * commands the user supplies from stdin.
                    324:         */
1.29    ! millert   325:        if ((ap = getcwd(path, sizeof(path))) == NULL)
1.18      millert   326:                perr("Cannot get current working directory");
1.7       millert   327:        (void)fputs("cd ", fp);
1.18      millert   328:        for (; *ap != '\0'; ap++) {
1.7       millert   329:                if (*ap == '\n')
                    330:                        fprintf(fp, "\"\n\"");
                    331:                else {
                    332:                        if (*ap != '/' && !isalnum(*ap))
                    333:                                (void)fputc('\\', fp);
1.1       deraadt   334:
1.7       millert   335:                        (void)fputc(*ap, fp);
1.1       deraadt   336:                }
                    337:        }
                    338:        /*
1.7       millert   339:         * Test cd's exit status: die if the original directory has been
                    340:         * removed, become unreadable or whatever.
1.1       deraadt   341:         */
1.29    ! millert   342:        (void)fprintf(fp, " || {\n\t echo 'Execution directory inaccessible'"
        !           343:            " >&2\n\t exit 1\n}\n");
1.1       deraadt   344:
1.3       millert   345:        if ((ch = getchar()) == EOF)
                    346:                panic("Input error");
                    347:
1.28      millert   348:        /* We want the job to run under the user's shell. */
                    349:        fprintf(fp, "%s << '_END_OF_AT_JOB'\n", shell);
                    350:
1.3       millert   351:        do {
1.7       millert   352:                (void)fputc(ch, fp);
1.3       millert   353:        } while ((ch = getchar()) != EOF);
1.1       deraadt   354:
1.28      millert   355:        (void)fprintf(fp, "\n_END_OF_AT_JOB\n");
1.1       deraadt   356:        if (ferror(fp))
                    357:                panic("Output error");
                    358:
                    359:        if (ferror(stdin))
                    360:                panic("Input error");
                    361:
1.7       millert   362:        (void)fclose(fp);
1.1       deraadt   363:
                    364:        /*
                    365:         * Set the x bit so that we're ready to start executing
                    366:         */
                    367:        if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
                    368:                perr("Cannot give away file");
                    369:
1.7       millert   370:        (void)close(fd2);
1.28      millert   371:
                    372:        runtime = *localtime(&runtimer);
                    373:        strftime(timestr, TIMESIZE, "%a %b %e %T %Y", &runtime);
                    374:        (void)fprintf(stderr, "commands will be executed using %s\n", shell);
1.29    ! millert   375:        (void)fprintf(stderr, "job %s at %s\n", &atfile[sizeof(_PATH_ATJOBS)],
        !           376:            timestr);
        !           377: }
        !           378:
        !           379: /* Sort by creation time. */
        !           380: static int
        !           381: byctime(const void *v1, const void *v2)
        !           382: {
        !           383:        const struct atjob *j1 = *(struct atjob **)v1;
        !           384:        const struct atjob *j2 = *(struct atjob **)v2;
        !           385:
        !           386:        return (j1->ctime - j2->ctime);
        !           387: }
        !           388:
        !           389: /* Sort by job number (and thus execution time). */
        !           390: static int
        !           391: byjobno(const void *v1, const void *v2)
        !           392: {
        !           393:        const struct atjob *j1 = *(struct atjob **)v1;
        !           394:        const struct atjob *j2 = *(struct atjob **)v2;
        !           395:
        !           396:        if (j1->runtimer == j2->runtimer)
        !           397:                return (j1->queue - j2->queue);
        !           398:        return (j1->runtimer - j2->runtimer);
        !           399: }
        !           400:
        !           401: static void
        !           402: print_job(struct atjob *job, int n, struct stat *st, int shortformat)
        !           403: {
        !           404:        struct passwd *pw;
        !           405:        struct tm runtime;
        !           406:        char timestr[TIMESIZE];
        !           407:        static char *ranks[] = {
        !           408:                "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
        !           409:        };
        !           410:
        !           411:        runtime = *localtime(&job->runtimer);
        !           412:        if (shortformat) {
        !           413:                strftime(timestr, TIMESIZE, "%a %b %e %T %Y", &runtime);
        !           414:                (void)printf("%ld.%c\t%s\n", (long)job->runtimer,
        !           415:                    job->queue, timestr);
        !           416:        } else {
        !           417:                pw = getpwuid(st->st_uid);
        !           418:                /* Rank hack shamelessly stolen from lpq */
        !           419:                if (n / 10 == 1)
        !           420:                        printf("%3d%-5s", n,"th");
        !           421:                else
        !           422:                        printf("%3d%-5s", n, ranks[n % 10]);
        !           423:                strftime(timestr, TIMESIZE, "%b %e, %Y %R", &runtime);
        !           424:                (void)printf("%-21.18s%-11.8s%10ld.%c   %c%s\n",
        !           425:                    timestr, pw ? pw->pw_name : "???",
        !           426:                    (long)job->runtimer, job->queue, job->queue,
        !           427:                    (S_IXUSR & st->st_mode) ? "" : " (done)");
        !           428:        }
1.1       deraadt   429: }
                    430:
1.29    ! millert   431: /*
        !           432:  * List all of a user's jobs in the queue, by looping through
        !           433:  * _PATH_ATJOBS, or all jobs if we are root.  If argc is > 0, argv
        !           434:  * contains the list of users whose jobs shall be displayed. By
        !           435:  * default, the list is sorted by execution date and queue.  If
        !           436:  * csort is non-zero jobs will be sorted by creation/submission date.
        !           437:  */
1.1       deraadt   438: static void
1.29    ! millert   439: list_jobs(int argc, char **argv, int count_only, int csort)
1.1       deraadt   440: {
                    441:        struct passwd *pw;
                    442:        struct dirent *dirent;
1.29    ! millert   443:        struct atjob **atjobs, *job;
        !           444:        struct stat stbuf;
1.1       deraadt   445:        time_t runtimer;
1.29    ! millert   446:        uid_t *uids;
        !           447:        long l;
        !           448:        char queue, *ep;
        !           449:        DIR *spool;
        !           450:        int i, shortformat, numjobs, maxjobs;
        !           451:
        !           452:        if (argc) {
        !           453:                if ((uids = malloc(sizeof(uid_t) * argc)) == NULL)
        !           454:                        err(EXIT_FAILURE, "malloc");
        !           455:
        !           456:                for (i = 0; i < argc; i++) {
        !           457:                        if ((pw = getpwnam(argv[i])) == NULL)
        !           458:                                errx(EXIT_FAILURE,
        !           459:                                    "%s: invalid user name", argv[i]);
        !           460:                        if (pw->pw_uid != real_uid && real_uid != 0)
        !           461:                                errx(EXIT_FAILURE, "Only the superuser may "
        !           462:                                    "display other users' jobs");
        !           463:                        uids[i] = pw->pw_uid;
        !           464:                }
        !           465:        } else
        !           466:                uids = NULL;
        !           467:
        !           468:        shortformat = strcmp(__progname, "at") == 0;
1.1       deraadt   469:
1.26      millert   470:        PRIV_START;
1.1       deraadt   471:
1.7       millert   472:        if (chdir(_PATH_ATJOBS) != 0)
1.1       deraadt   473:                perr2("Cannot change to ", _PATH_ATJOBS);
                    474:
                    475:        if ((spool = opendir(".")) == NULL)
                    476:                perr2("Cannot open ", _PATH_ATJOBS);
                    477:
1.29    ! millert   478:        PRIV_END;
        !           479:
        !           480:        if (fstat(dirfd(spool), &stbuf) != 0)
        !           481:                perr2("Cannot stat ", _PATH_ATJOBS);
        !           482:
        !           483:        /*
        !           484:         * The directory's link count should give us a good idea
        !           485:         * of how many files are in it.  Fudge things a little just
        !           486:         * in case someone adds a job or two.
        !           487:         */
        !           488:        numjobs = 0;
        !           489:        maxjobs = stbuf.st_nlink + 4;
        !           490:        atjobs = (struct atjob **)malloc(maxjobs * sizeof(struct atjob *));
        !           491:        if (atjobs == NULL)
        !           492:                err(EXIT_FAILURE, "malloc");
        !           493:
        !           494:        /* Loop over every file in the directory. */
1.1       deraadt   495:        while ((dirent = readdir(spool)) != NULL) {
1.29    ! millert   496:                PRIV_START;
        !           497:
        !           498:                if (stat(dirent->d_name, &stbuf) != 0)
1.1       deraadt   499:                        perr2("Cannot stat in ", _PATH_ATJOBS);
                    500:
1.29    ! millert   501:                PRIV_END;
        !           502:
1.1       deraadt   503:                /*
                    504:                 * See it's a regular file and has its x bit turned on and
                    505:                 * is the user's
                    506:                 */
1.29    ! millert   507:                if (!S_ISREG(stbuf.st_mode)
        !           508:                    || ((stbuf.st_uid != real_uid) && !(real_uid == 0))
        !           509:                    || !(S_IXUSR & stbuf.st_mode || vflag))
1.1       deraadt   510:                        continue;
                    511:
1.29    ! millert   512:                l = strtol(dirent->d_name, &ep, 10);
        !           513:                if (*ep != '.' || !isalpha(*(ep + 1)) || *(ep + 2) != '\0' ||
        !           514:                    l < 0 || l >= INT_MAX)
1.1       deraadt   515:                        continue;
1.29    ! millert   516:                runtimer = (time_t)l;
        !           517:                queue = *(ep + 1);
1.1       deraadt   518:
                    519:                if (atqueue && (queue != atqueue))
                    520:                        continue;
                    521:
1.29    ! millert   522:                /* Check against specified user(s). */
        !           523:                if (argc) {
        !           524:                        for (i = 0; i < argc; i++) {
        !           525:                                if (uids[0] == stbuf.st_uid)
        !           526:                                        break;
        !           527:                        }
        !           528:                        if (i == argc)
        !           529:                                continue;       /* user doesn't match */
        !           530:                }
        !           531:
        !           532:                if (count_only) {
        !           533:                        numjobs++;
        !           534:                        continue;
        !           535:                }
        !           536:
        !           537:                job = (struct atjob *)malloc(sizeof(struct atjob));
        !           538:                if (job == NULL)
        !           539:                        err(EXIT_FAILURE, "malloc");
        !           540:                job->runtimer = runtimer;
        !           541:                job->ctime = stbuf.st_ctime;
        !           542:                job->queue = queue;
        !           543:                if (numjobs == maxjobs) {
        !           544:                    maxjobs *= 2;
        !           545:                    atjobs = realloc(atjobs, maxjobs * sizeof(struct atjob *));
        !           546:                    if (atjobs == NULL)
        !           547:                        err(EXIT_FAILURE, "realloc");
        !           548:                }
        !           549:                atjobs[numjobs++] = job;
        !           550:        }
        !           551:        free(uids);
        !           552:
        !           553:        if (count_only || numjobs == 0) {
        !           554:                if (numjobs == 0 && !shortformat)
        !           555:                        fprintf(stderr, "no files in queue.\n");
        !           556:                else if (count_only)
        !           557:                        printf("%d\n", numjobs);
        !           558:                free(atjobs);
        !           559:                return;
        !           560:        }
        !           561:
        !           562:        /* Sort by job run time or by job creation time. */
        !           563:        qsort(atjobs, numjobs, sizeof(struct atjob *),
        !           564:            csort ? byctime : byjobno);
        !           565:
        !           566:        if (!shortformat)
        !           567:                (void)puts(" Rank     Execution Date     Owner          "
        !           568:                    "Job       Queue");
        !           569:
        !           570:        for (i = 0; i < numjobs; i++) {
        !           571:                print_job(atjobs[i], i + 1, &stbuf, shortformat);
        !           572:                free(atjobs[i]);
1.1       deraadt   573:        }
1.29    ! millert   574:        free(atjobs);
        !           575: }
        !           576:
        !           577: static int
        !           578: rmok(int job)
        !           579: {
        !           580:        int ch, junk;
        !           581:
        !           582:        printf("%d: remove it? ", job);
        !           583:        ch = getchar();
        !           584:        while ((junk = getchar()) != EOF && junk != '\n')
        !           585:                ;
        !           586:        return (ch == 'y' || ch == 'Y');
1.1       deraadt   587: }
                    588:
1.29    ! millert   589: /*
        !           590:  * Loop through all jobs in _PATH_ATJOBS and display or delete ones
        !           591:  * that match argv (may be job or username), or all if argc == 0.
        !           592:  * Only the superuser may display/delete other people's jobs.
        !           593:  */
1.28      millert   594: static int
1.26      millert   595: process_jobs(int argc, char **argv, int what)
1.1       deraadt   596: {
1.29    ! millert   597:        struct stat stbuf;
        !           598:        struct dirent *dirent;
        !           599:        struct passwd *pw;
        !           600:        time_t runtimer;
        !           601:        uid_t *uids;
        !           602:        char **jobs, *ep, queue;
        !           603:        long l;
        !           604:        FILE *fp;
1.7       millert   605:        DIR *spool;
1.29    ! millert   606:        int job_matches, jobs_len, uids_len;
        !           607:        int error, i, ch;
1.9       millert   608:
1.26      millert   609:        PRIV_START;
1.1       deraadt   610:
1.7       millert   611:        if (chdir(_PATH_ATJOBS) != 0)
1.1       deraadt   612:                perr2("Cannot change to ", _PATH_ATJOBS);
                    613:
1.7       millert   614:        if ((spool = opendir(".")) == NULL)
                    615:                perr2("Cannot open ", _PATH_ATJOBS);
                    616:
1.26      millert   617:        PRIV_END;
1.7       millert   618:
1.29    ! millert   619:        /* Convert argv into a list of jobs and uids. */
        !           620:        jobs = NULL;
        !           621:        uids = NULL;
        !           622:        jobs_len = uids_len = 0;
        !           623:        if (argc > 0) {
        !           624:                if ((jobs = malloc(sizeof(char *) * argc)) == NULL ||
        !           625:                    (uids = malloc(sizeof(uid_t) * argc)) == NULL)
        !           626:                        err(EXIT_FAILURE, "malloc");
        !           627:
        !           628:                for (i = 0; i < argc; i++) {
        !           629:                        l = strtol(argv[i], &ep, 10);
        !           630:                        if (*ep == '.' && isalpha(*(ep + 1)) &&
        !           631:                            *(ep + 2) == '\0' && l > 0 && l < INT_MAX)
        !           632:                                jobs[jobs_len++] = argv[i];
        !           633:                        else if ((pw = getpwnam(argv[i])) != NULL) {
        !           634:                                if (real_uid != pw->pw_uid && real_uid != 0)
        !           635:                                        errx(EXIT_FAILURE,
        !           636:                                            "Only the superuser may %s"
        !           637:                                            " other users' jobs", what == ATRM
        !           638:                                            ? "remove" : "print");
        !           639:                                uids[uids_len++] = pw->pw_uid;
        !           640:                        } else
        !           641:                                errx(EXIT_FAILURE,
        !           642:                                    "%s: invalid user name", argv[i]);
        !           643:                }
        !           644:        }
        !           645:
1.7       millert   646:        /* Loop over every file in the directory */
1.28      millert   647:        while ((dirent = readdir(spool)) != NULL) {
1.7       millert   648:
1.26      millert   649:                PRIV_START;
1.29    ! millert   650:                if (stat(dirent->d_name, &stbuf) != 0)
1.7       millert   651:                        perr2("Cannot stat in ", _PATH_ATJOBS);
1.26      millert   652:                PRIV_END;
1.7       millert   653:
1.29    ! millert   654:                if (stbuf.st_uid != real_uid && real_uid != 0)
1.7       millert   655:                        continue;
                    656:
1.29    ! millert   657:                l = strtol(dirent->d_name, &ep, 10);
        !           658:                if (*ep != '.' || !isalpha(*(ep + 1)) || *(ep + 2) != '\0' ||
        !           659:                    l < 0 || l >= INT_MAX)
        !           660:                        continue;
        !           661:                runtimer = (time_t)l;
        !           662:                queue = *(ep + 1);
1.7       millert   663:
1.29    ! millert   664:                /* Check runtimer against argv; argc==0 means do all. */
        !           665:                job_matches = (argc == 0) ? 1 : 0;
        !           666:                if (!job_matches) {
        !           667:                        for (i = 0; i < jobs_len; i++) {
        !           668:                                if (strcmp(dirent->d_name, jobs[i]) == 0) {
        !           669:                                        jobs[i] = NULL;
        !           670:                                        job_matches = 1;
        !           671:                                        break;
        !           672:                                }
        !           673:                        }
        !           674:                }
        !           675:                if (!job_matches) {
        !           676:                        for (i = 0; i < uids_len; i++) {
        !           677:                                if (uids[i] == stbuf.st_uid) {
        !           678:                                        job_matches = 1;
        !           679:                                        break;
        !           680:                                }
        !           681:                        }
        !           682:                }
        !           683:
        !           684:                if (job_matches) {
        !           685:                        switch (what) {
        !           686:                        case ATRM:
        !           687:                                PRIV_START;
        !           688:
        !           689:                                if (!interactive ||
        !           690:                                    (interactive && rmok(runtimer))) {
1.7       millert   691:                                        if (unlink(dirent->d_name) != 0)
                    692:                                                perr(dirent->d_name);
1.29    ! millert   693:                                        if (!force && !interactive)
        !           694:                                                fprintf(stderr,
        !           695:                                                    "%s removed\n",
        !           696:                                                    dirent->d_name);
        !           697:                                }
1.7       millert   698:
1.29    ! millert   699:                                PRIV_END;
1.7       millert   700:
1.29    ! millert   701:                                break;
1.7       millert   702:
1.29    ! millert   703:                        case CAT:
        !           704:                                PRIV_START;
1.7       millert   705:
1.29    ! millert   706:                                fp = fopen(dirent->d_name, "r");
1.7       millert   707:
1.29    ! millert   708:                                PRIV_END;
1.7       millert   709:
1.29    ! millert   710:                                if (!fp)
        !           711:                                        perr("Cannot open file");
1.7       millert   712:
1.29    ! millert   713:                                while ((ch = getc(fp)) != EOF)
        !           714:                                        putchar(ch);
1.7       millert   715:
1.29    ! millert   716:                                break;
1.7       millert   717:
1.29    ! millert   718:                        default:
        !           719:                                errx(EXIT_FAILURE,
        !           720:                                    "Internal error, process_jobs = %d",
        !           721:                                    what);
        !           722:                                break;
1.7       millert   723:                        }
1.1       deraadt   724:                }
                    725:        }
1.29    ! millert   726:        for (error = 0, i = 0; i < jobs_len; i++) {
        !           727:                if (jobs[i] != NULL) {
        !           728:                        if (!force)
        !           729:                                warnx("%s: no such job", jobs[i]);
1.28      millert   730:                        error++;
                    731:                }
                    732:        }
1.29    ! millert   733:        free(jobs);
        !           734:        free(uids);
        !           735:
        !           736:        return (error);
1.28      millert   737: }
1.1       deraadt   738:
1.25      millert   739: #define        ATOI2(s)        ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
                    740:
1.29    ! millert   741: /*
        !           742:  * This is pretty much a copy of stime_arg1() from touch.c.
        !           743:  */
1.25      millert   744: static time_t
                    745: ttime(const char *arg)
                    746: {
                    747:        struct timeval tv[2];
                    748:        time_t now;
                    749:        struct tm *t;
                    750:        int yearset;
                    751:        char *p;
                    752:
                    753:        if (gettimeofday(&tv[0], NULL))
                    754:                panic("Cannot get current time");
                    755:
                    756:        /* Start with the current time. */
                    757:        now = tv[0].tv_sec;
                    758:        if ((t = localtime(&now)) == NULL)
                    759:                panic("localtime");
                    760:        /* [[CC]YY]MMDDhhmm[.SS] */
                    761:        if ((p = strchr(arg, '.')) == NULL)
                    762:                t->tm_sec = 0;          /* Seconds defaults to 0. */
                    763:        else {
                    764:                if (strlen(p + 1) != 2)
                    765:                        goto terr;
                    766:                *p++ = '\0';
                    767:                t->tm_sec = ATOI2(p);
                    768:        }
                    769:
                    770:        yearset = 0;
                    771:        switch(strlen(arg)) {
                    772:        case 12:                        /* CCYYMMDDhhmm */
                    773:                t->tm_year = ATOI2(arg);
                    774:                t->tm_year *= 100;
                    775:                yearset = 1;
                    776:                /* FALLTHROUGH */
                    777:        case 10:                        /* YYMMDDhhmm */
                    778:                if (yearset) {
                    779:                        yearset = ATOI2(arg);
                    780:                        t->tm_year += yearset;
                    781:                } else {
                    782:                        yearset = ATOI2(arg);
                    783:                        t->tm_year = yearset + 2000;
                    784:                }
                    785:                t->tm_year -= 1900;     /* Convert to UNIX time. */
                    786:                /* FALLTHROUGH */
                    787:        case 8:                         /* MMDDhhmm */
                    788:                t->tm_mon = ATOI2(arg);
                    789:                --t->tm_mon;            /* Convert from 01-12 to 00-11 */
                    790:                t->tm_mday = ATOI2(arg);
                    791:                t->tm_hour = ATOI2(arg);
                    792:                t->tm_min = ATOI2(arg);
                    793:                break;
                    794:        default:
                    795:                goto terr;
                    796:        }
                    797:
                    798:        t->tm_isdst = -1;               /* Figure out DST. */
                    799:        tv[0].tv_sec = tv[1].tv_sec = mktime(t);
                    800:        if (tv[0].tv_sec != -1)
                    801:                return (tv[0].tv_sec);
                    802:        else
                    803:     terr:
                    804:                panic("out of range or illegal time specification: "
                    805:                    "[[CC]YY]MMDDhhmm[.SS]");
                    806: }
                    807:
1.1       deraadt   808: int
1.26      millert   809: main(int argc, char **argv)
1.1       deraadt   810: {
1.29    ! millert   811:        time_t timer = -1;
1.7       millert   812:        char queue = DEFAULT_AT_QUEUE;
                    813:        char queue_set = 0;
1.25      millert   814:        char *options = "q:f:t:bcdlmrv";        /* default options for at */
1.29    ! millert   815:        int ch;
        !           816:        int aflag = 0;
        !           817:        int cflag = 0;
        !           818:        int nflag = 0;
1.1       deraadt   819:
1.26      millert   820:        RELINQUISH_PRIVS;
1.1       deraadt   821:
                    822:        /* find out what this program is supposed to do */
1.25      millert   823:        if (strcmp(__progname, "atq") == 0) {
1.1       deraadt   824:                program = ATQ;
1.29    ! millert   825:                options = "cnvq:";
1.25      millert   826:        } else if (strcmp(__progname, "atrm") == 0) {
1.1       deraadt   827:                program = ATRM;
1.29    ! millert   828:                options = "afi";
1.25      millert   829:        } else if (strcmp(__progname, "batch") == 0) {
1.1       deraadt   830:                program = BATCH;
1.24      millert   831:                options = "f:q:mv";
1.1       deraadt   832:        }
                    833:
                    834:        /* process whatever options we can process */
1.29    ! millert   835:        while ((ch = getopt(argc, argv, options)) != -1) {
        !           836:                switch (ch) {
        !           837:                case 'a':
        !           838:                        aflag = 1;
        !           839:                        break;
        !           840:
        !           841:                case 'i':
        !           842:                        interactive = 1;
        !           843:                        force = 0;
        !           844:                        break;
        !           845:
        !           846:                case 'v':       /* show completed but unremoved jobs */
        !           847:                        /*
        !           848:                         * This option is only useful when we are invoked
        !           849:                         * as atq but we accept (and ignore) this flag in
        !           850:                         * the other programs for backwards compatibility.
        !           851:                         */
        !           852:                        vflag = 1;
1.1       deraadt   853:                        break;
                    854:
                    855:                case 'm':       /* send mail when job is complete */
                    856:                        send_mail = 1;
                    857:                        break;
                    858:
                    859:                case 'f':
1.29    ! millert   860:                        if (program == ATRM) {
        !           861:                                force = 1;
        !           862:                                interactive = 0;
        !           863:                        } else
        !           864:                                atinput = optarg;
1.1       deraadt   865:                        break;
                    866:
                    867:                case 'q':       /* specify queue */
                    868:                        if (strlen(optarg) > 1)
                    869:                                usage();
                    870:
                    871:                        atqueue = queue = *optarg;
1.7       millert   872:                        if (!(islower(queue) || isupper(queue)))
1.1       deraadt   873:                                usage();
1.7       millert   874:
                    875:                        queue_set = 1;
                    876:                        break;
                    877:
1.25      millert   878:                case 'd':               /* for backwards compatibility */
                    879:                case 'r':
1.7       millert   880:                        program = ATRM;
1.24      millert   881:                        options = "";
1.7       millert   882:                        break;
                    883:
1.25      millert   884:                case 't':
                    885:                        timer = ttime(optarg);
                    886:                        break;
                    887:
1.7       millert   888:                case 'l':
                    889:                        program = ATQ;
1.29    ! millert   890:                        options = "cnvq:";
1.7       millert   891:                        break;
                    892:
                    893:                case 'b':
                    894:                        program = BATCH;
1.24      millert   895:                        options = "f:q:mv";
1.7       millert   896:                        break;
                    897:
                    898:                case 'c':
1.29    ! millert   899:                        if (program == ATQ) {
        !           900:                                cflag = 1;
        !           901:                        } else {
        !           902:                                program = CAT;
        !           903:                                options = "";
        !           904:                        }
        !           905:                        break;
        !           906:
        !           907:                case 'n':
        !           908:                        nflag = 1;
1.1       deraadt   909:                        break;
                    910:
                    911:                default:
                    912:                        usage();
                    913:                        break;
                    914:                }
1.29    ! millert   915:        }
        !           916:        argc -= optind;
        !           917:        argv += optind;
1.7       millert   918:
1.16      mickey    919:        if (!check_permission())
                    920:                errx(EXIT_FAILURE, "You do not have permission to use %s.",
1.25      millert   921:                     __progname);
1.7       millert   922:
1.1       deraadt   923:        /* select our program */
                    924:        switch (program) {
                    925:        case ATQ:
1.29    ! millert   926:                list_jobs(argc, argv, nflag, cflag);
1.1       deraadt   927:                break;
                    928:
                    929:        case ATRM:
1.7       millert   930:        case CAT:
1.29    ! millert   931:                if ((aflag && argc) || (!aflag && !argc))
1.10      millert   932:                        usage();
1.28      millert   933:                exit(process_jobs(argc, argv, program));
1.1       deraadt   934:                break;
                    935:
                    936:        case AT:
1.25      millert   937:                /* Time may have been specified via the -t flag. */
1.29    ! millert   938:                if (timer == -1)
1.25      millert   939:                        timer = parsetime(argc, argv);
1.1       deraadt   940:                writefile(timer, queue);
                    941:                break;
                    942:
                    943:        case BATCH:
1.7       millert   944:                if (queue_set)
                    945:                        queue = toupper(queue);
                    946:                else
                    947:                        queue = DEFAULT_BATCH_QUEUE;
                    948:
1.29    ! millert   949:                if (argc > 0)
1.7       millert   950:                        timer = parsetime(argc, argv);
                    951:                else
                    952:                        timer = time(NULL);
                    953:
                    954:                writefile(timer, queue);
1.1       deraadt   955:                break;
                    956:
                    957:        default:
                    958:                panic("Internal error");
                    959:                break;
                    960:        }
                    961:        exit(EXIT_SUCCESS);
                    962: }