Annotation of src/usr.bin/at/at.c, Revision 1.21
1.21 ! millert 1: /* $OpenBSD: at.c,v 1.20 2001/01/17 19:29:06 deraadt 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: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. The name of the author(s) may not be used to endorse or promote
17: * products derived from this software without specific prior written
18: * permission.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
21: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1.7 millert 23: * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
1.1 deraadt 24: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27: * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30: */
31:
32: /* System Headers */
33: #include <sys/types.h>
1.7 millert 34: #include <sys/param.h>
1.1 deraadt 35: #include <sys/stat.h>
36: #include <sys/wait.h>
37: #include <ctype.h>
38: #include <dirent.h>
39: #include <errno.h>
40: #include <fcntl.h>
41: #include <pwd.h>
42: #include <signal.h>
43: #include <stddef.h>
44: #include <stdio.h>
45: #include <stdlib.h>
46: #include <string.h>
47: #include <time.h>
48: #include <unistd.h>
1.7 millert 49: #include <utmp.h>
50: #include <locale.h>
1.16 mickey 51: #include <err.h>
1.7 millert 52:
53: #if (MAXLOGNAME-1) > UT_NAMESIZE
54: #define LOGNAMESIZE UT_NAMESIZE
55: #else
56: #define LOGNAMESIZE (MAXLOGNAME-1)
57: #endif
1.1 deraadt 58:
59: /* Local headers */
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: /* Macros */
69: #define ALARMC 10 /* Number of seconds to wait for timeout */
70:
71: #define TIMESIZE 50
72:
1.7 millert 73: enum { ATQ, ATRM, AT, BATCH, CAT }; /* what program we want to run */
74:
1.1 deraadt 75: /* File scope variables */
76: #ifndef lint
1.21 ! millert 77: static char rcsid[] = "$OpenBSD: at.c,v 1.20 2001/01/17 19:29:06 deraadt Exp $";
1.1 deraadt 78: #endif
79:
80: char *no_export[] =
81: {
82: "TERM", "TERMCAP", "DISPLAY", "_"
83: };
1.16 mickey 84: static int send_mail = 0;
1.1 deraadt 85:
86: /* External variables */
1.7 millert 87:
1.1 deraadt 88: extern char **environ;
89: int fcreated;
90: char *namep;
91: char atfile[FILENAME_MAX];
92:
1.7 millert 93: char *atinput = (char *)0; /* where to get input from */
1.1 deraadt 94: char atqueue = 0; /* which queue to examine for jobs (atq) */
95: char atverify = 0; /* verify time instead of queuing job */
96:
97: /* Function declarations */
1.7 millert 98:
1.21 ! millert 99: static void sigc(int);
! 100: static void alarmc(int);
! 101: static char *cwdname(void);
! 102: static void writefile(time_t, char);
! 103: static void list_jobs(void);
1.1 deraadt 104:
105: /* Signal catching functions */
106:
107: static void
108: sigc(signo)
109: int signo;
110: {
1.7 millert 111: /* If the user presses ^C, remove the spool file and exit. */
1.1 deraadt 112: if (fcreated) {
113: PRIV_START
1.7 millert 114: (void)unlink(atfile);
1.1 deraadt 115: PRIV_END
116: }
117:
1.20 deraadt 118: _exit(EXIT_FAILURE);
1.1 deraadt 119: }
120:
121: static void
122: alarmc(signo)
123: int signo;
124: {
1.20 deraadt 125: char buf[1024];
126:
1.7 millert 127: /* Time out after some seconds. */
1.20 deraadt 128: strlcpy(buf, namep, sizeof(buf));
129: strlcat(buf, ": File locking timed out\n", sizeof(buf));
130: write(STDERR_FILENO, buf, strlen(buf));
131: if (fcreated) {
132: PRIV_START
133: unlink(atfile);
134: PRIV_END
135: }
136: _exit(EXIT_FAILURE);
1.1 deraadt 137: }
138:
139: /* Local functions */
140:
141: static char *
142: cwdname()
143: {
1.7 millert 144: /*
145: * Read in the current directory; the name will be overwritten on
146: * subsequent calls.
147: */
148: static char path[MAXPATHLEN];
1.1 deraadt 149:
1.7 millert 150: return (getcwd(path, sizeof(path)));
151: }
1.1 deraadt 152:
1.7 millert 153: static int
154: nextjob()
155: {
156: int jobno;
157: FILE *fid;
1.1 deraadt 158:
1.12 kstailey 159: if ((fid = fopen(_PATH_SEQFILE, "r+")) != NULL) {
1.7 millert 160: if (fscanf(fid, "%5x", &jobno) == 1) {
161: (void)rewind(fid);
162: jobno = (1+jobno) % 0xfffff; /* 2^20 jobs enough? */
163: (void)fprintf(fid, "%05x\n", jobno);
164: } else
165: jobno = EOF;
166: (void)fclose(fid);
167: return (jobno);
1.12 kstailey 168: } else if ((fid = fopen(_PATH_SEQFILE, "w")) != NULL) {
1.7 millert 169: (void)fprintf(fid, "%05x\n", jobno = 1);
170: (void)fclose(fid);
171: return (1);
1.1 deraadt 172: }
1.7 millert 173: return (EOF);
1.1 deraadt 174: }
175:
176: static void
177: writefile(runtimer, queue)
178: time_t runtimer;
179: char queue;
180: {
181: /*
182: * This does most of the work if at or batch are invoked for
183: * writing a job.
184: */
1.7 millert 185: int jobno;
1.1 deraadt 186: char *ap, *ppos, *mailname;
187: struct passwd *pass_entry;
188: struct stat statbuf;
189: int fdes, lockdes, fd2;
190: FILE *fp, *fpin;
191: struct sigaction act;
192: char **atenv;
193: int ch;
194: mode_t cmask;
195: struct flock lock;
196:
1.7 millert 197: (void)setlocale(LC_TIME, "");
198:
1.1 deraadt 199: /*
200: * Install the signal handler for SIGINT; terminate after removing the
201: * spool file if necessary
202: */
1.15 deraadt 203: memset(&act, 0, sizeof act);
1.1 deraadt 204: act.sa_handler = sigc;
205: sigemptyset(&(act.sa_mask));
206: act.sa_flags = 0;
207:
208: sigaction(SIGINT, &act, NULL);
209:
1.17 deraadt 210: (void)strlcpy(atfile, _PATH_ATJOBS, sizeof atfile);
1.7 millert 211: ppos = atfile + strlen(atfile);
1.1 deraadt 212:
213: /*
214: * Loop over all possible file names for running something at this
1.7 millert 215: * particular time, see if a file is there; the first empty slot at
216: * any particular time is used. Lock the file _PATH_LOCKFILE first
217: * to make sure we're alone when doing this.
1.1 deraadt 218: */
219:
220: PRIV_START
221:
1.7 millert 222: if ((lockdes = open(_PATH_LOCKFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0)
1.1 deraadt 223: perr2("Cannot open lockfile ", _PATH_LOCKFILE);
224:
225: lock.l_type = F_WRLCK;
226: lock.l_whence = SEEK_SET;
227: lock.l_start = 0;
228: lock.l_len = 0;
229:
230: act.sa_handler = alarmc;
231: sigemptyset(&(act.sa_mask));
232: act.sa_flags = 0;
233:
234: /*
235: * Set an alarm so a timeout occurs after ALARMC seconds, in case
236: * something is seriously broken.
237: */
238: sigaction(SIGALRM, &act, NULL);
239: alarm(ALARMC);
240: fcntl(lockdes, F_SETLKW, &lock);
241: alarm(0);
242:
1.7 millert 243: if ((jobno = nextjob()) == EOF)
244: perr("Cannot generate job number");
245:
1.8 millert 246: (void)snprintf(ppos, sizeof(atfile) - (ppos - atfile),
247: "%c%5x%8x", queue, jobno, (unsigned) (runtimer/60));
1.1 deraadt 248:
1.7 millert 249: for (ap = ppos; *ap != '\0'; ap++)
250: if (*ap == ' ')
251: *ap = '0';
252:
253: if (stat(atfile, &statbuf) != 0)
254: if (errno != ENOENT)
255: perr2("Cannot access ", _PATH_ATJOBS);
1.1 deraadt 256:
257: /*
258: * Create the file. The x bit is only going to be set after it has
259: * been completely written out, to make sure it is not executed in
260: * the meantime. To make sure they do not get deleted, turn off
261: * their r bit. Yes, this is a kluge.
262: */
263: cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR);
1.8 millert 264: if ((fdes = open(atfile, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR)) == -1)
1.1 deraadt 265: perr("Cannot create atjob file");
266:
267: if ((fd2 = dup(fdes)) < 0)
268: perr("Error in dup() of job file");
269:
1.7 millert 270: if (fchown(fd2, real_uid, real_gid) != 0)
1.1 deraadt 271: perr("Cannot give away file");
272:
273: PRIV_END
274:
275: /*
276: * We've successfully created the file; let's set the flag so it
277: * gets removed in case of an interrupt or error.
278: */
279: fcreated = 1;
280:
281: /* Now we can release the lock, so other people can access it */
282: lock.l_type = F_UNLCK;
283: lock.l_whence = SEEK_SET;
284: lock.l_start = 0;
285: lock.l_len = 0;
1.7 millert 286: (void)fcntl(lockdes, F_SETLKW, &lock);
287: (void)close(lockdes);
1.1 deraadt 288:
289: if ((fp = fdopen(fdes, "w")) == NULL)
290: panic("Cannot reopen atjob file");
291:
292: /*
1.18 millert 293: * Get the userid to mail to, first by trying getlogin(), which asks
294: * the kernel, then from $LOGNAME or $USER, finally from getpwuid().
1.1 deraadt 295: */
296: mailname = getlogin();
1.5 millert 297: if (mailname == NULL && (mailname = getenv("LOGNAME")) == NULL)
298: mailname = getenv("USER");
1.1 deraadt 299:
1.7 millert 300: if ((mailname == NULL) || (mailname[0] == '\0') ||
301: (strlen(mailname) > LOGNAMESIZE) || (getpwnam(mailname) == NULL)) {
302: pass_entry = getpwuid(real_uid);
1.1 deraadt 303: if (pass_entry != NULL)
304: mailname = pass_entry->pw_name;
305: }
306:
1.13 kstailey 307: if (atinput != NULL) {
1.1 deraadt 308: fpin = freopen(atinput, "r", stdin);
309: if (fpin == NULL)
310: perr("Cannot open input file");
311: }
1.7 millert 312: (void)fprintf(fp, "#!/bin/sh\n# atrun uid=%u gid=%u\n# mail %*s %d\n",
313: real_uid, real_gid, LOGNAMESIZE, mailname, send_mail);
1.1 deraadt 314:
315: /* Write out the umask at the time of invocation */
1.7 millert 316: (void)fprintf(fp, "umask %o\n", cmask);
1.1 deraadt 317:
318: /*
319: * Write out the environment. Anything that may look like a special
320: * character to the shell is quoted, except for \n, which is done
321: * with a pair of "'s. Dont't export the no_export list (such as
322: * TERM or DISPLAY) because we don't want these.
323: */
324: for (atenv = environ; *atenv != NULL; atenv++) {
325: int export = 1;
326: char *eqp;
327:
328: eqp = strchr(*atenv, '=');
1.19 millert 329: if (eqp == NULL)
1.1 deraadt 330: eqp = *atenv;
331: else {
332: int i;
333:
334: for (i = 0;i < sizeof(no_export) /
335: sizeof(no_export[0]); i++) {
336: export = export
337: && (strncmp(*atenv, no_export[i],
338: (size_t) (eqp - *atenv)) != 0);
339: }
340: eqp++;
341: }
342:
343: if (export) {
1.7 millert 344: (void)fwrite(*atenv, sizeof(char), eqp - *atenv, fp);
1.1 deraadt 345: for (ap = eqp; *ap != '\0'; ap++) {
346: if (*ap == '\n')
1.7 millert 347: (void)fprintf(fp, "\"\n\"");
1.1 deraadt 348: else {
1.7 millert 349: if (!isalnum(*ap)) {
350: switch (*ap) {
351: case '%': case '/': case '{':
352: case '[': case ']': case '=':
353: case '}': case '@': case '+':
354: case '#': case ',': case '.':
355: case ':': case '-': case '_':
356: break;
357: default:
358: (void)fputc('\\', fp);
359: break;
360: }
361: }
362: (void)fputc(*ap, fp);
1.1 deraadt 363: }
364: }
1.7 millert 365: (void)fputs("; export ", fp);
366: (void)fwrite(*atenv, sizeof(char), eqp - *atenv - 1, fp);
367: (void)fputc('\n', fp);
368: }
369: }
370: /*
371: * Cd to the directory at the time and write out all the
372: * commands the user supplies from stdin.
373: */
1.18 millert 374: if ((ap = cwdname()) == NULL)
375: perr("Cannot get current working directory");
1.7 millert 376: (void)fputs("cd ", fp);
1.18 millert 377: for (; *ap != '\0'; ap++) {
1.7 millert 378: if (*ap == '\n')
379: fprintf(fp, "\"\n\"");
380: else {
381: if (*ap != '/' && !isalnum(*ap))
382: (void)fputc('\\', fp);
1.1 deraadt 383:
1.7 millert 384: (void)fputc(*ap, fp);
1.1 deraadt 385: }
386: }
387: /*
1.7 millert 388: * Test cd's exit status: die if the original directory has been
389: * removed, become unreadable or whatever.
1.1 deraadt 390: */
1.7 millert 391: (void)fprintf(fp, " || {\n\t echo 'Execution directory inaccessible' >&2\n\t exit 1\n}\n");
1.1 deraadt 392:
1.3 millert 393: if ((ch = getchar()) == EOF)
394: panic("Input error");
395:
396: do {
1.7 millert 397: (void)fputc(ch, fp);
1.3 millert 398: } while ((ch = getchar()) != EOF);
1.1 deraadt 399:
1.7 millert 400: (void)fprintf(fp, "\n");
1.1 deraadt 401: if (ferror(fp))
402: panic("Output error");
403:
404: if (ferror(stdin))
405: panic("Input error");
406:
1.7 millert 407: (void)fclose(fp);
1.1 deraadt 408:
409: /*
410: * Set the x bit so that we're ready to start executing
411: */
412: if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
413: perr("Cannot give away file");
414:
1.7 millert 415: (void)close(fd2);
416: (void)fprintf(stderr, "Job %d will be executed using /bin/sh\n", jobno);
1.1 deraadt 417: }
418:
419: static void
420: list_jobs()
421: {
422: /*
423: * List all a user's jobs in the queue, by looping through
424: * _PATH_ATJOBS, or everybody's if we are root
425: */
426: struct passwd *pw;
427: DIR *spool;
428: struct dirent *dirent;
429: struct stat buf;
430: struct tm runtime;
431: unsigned long ctm;
432: char queue;
1.7 millert 433: int jobno;
1.1 deraadt 434: time_t runtimer;
435: char timestr[TIMESIZE];
436: int first = 1;
437:
438: PRIV_START
439:
1.7 millert 440: if (chdir(_PATH_ATJOBS) != 0)
1.1 deraadt 441: perr2("Cannot change to ", _PATH_ATJOBS);
442:
443: if ((spool = opendir(".")) == NULL)
444: perr2("Cannot open ", _PATH_ATJOBS);
445:
446: /* Loop over every file in the directory */
447: while ((dirent = readdir(spool)) != NULL) {
448: if (stat(dirent->d_name, &buf) != 0)
449: perr2("Cannot stat in ", _PATH_ATJOBS);
450:
451: /*
452: * See it's a regular file and has its x bit turned on and
453: * is the user's
454: */
455: if (!S_ISREG(buf.st_mode)
456: || ((buf.st_uid != real_uid) && !(real_uid == 0))
457: || !(S_IXUSR & buf.st_mode || atverify))
458: continue;
459:
1.7 millert 460: if (sscanf(dirent->d_name, "%c%5x%8lx", &queue, &jobno, &ctm) != 3)
1.1 deraadt 461: continue;
462:
463: if (atqueue && (queue != atqueue))
464: continue;
465:
466: runtimer = 60 * (time_t) ctm;
467: runtime = *localtime(&runtimer);
468: strftime(timestr, TIMESIZE, "%X %x", &runtime);
469: if (first) {
1.7 millert 470: (void)printf("Date\t\t\tOwner\tQueue\tJob#\n");
1.1 deraadt 471: first = 0;
472: }
473: pw = getpwuid(buf.st_uid);
474:
1.7 millert 475: (void)printf("%s\t%s\t%c%s\t%d\n",
1.1 deraadt 476: timestr,
477: pw ? pw->pw_name : "???",
478: queue,
479: (S_IXUSR & buf.st_mode) ? "" : "(done)",
1.7 millert 480: jobno);
1.1 deraadt 481: }
482: PRIV_END
483: }
484:
485: static void
1.7 millert 486: process_jobs(argc, argv, what)
1.1 deraadt 487: int argc;
488: char **argv;
1.7 millert 489: int what;
1.1 deraadt 490: {
491: /* Delete every argument (job - ID) given */
492: int i;
493: struct stat buf;
1.7 millert 494: DIR *spool;
495: struct dirent *dirent;
496: unsigned long ctm;
497: char queue;
498: int jobno;
1.9 millert 499:
1.1 deraadt 500: PRIV_START
501:
1.7 millert 502: if (chdir(_PATH_ATJOBS) != 0)
1.1 deraadt 503: perr2("Cannot change to ", _PATH_ATJOBS);
504:
1.7 millert 505: if ((spool = opendir(".")) == NULL)
506: perr2("Cannot open ", _PATH_ATJOBS);
507:
508: PRIV_END
509:
510: /* Loop over every file in the directory */
511: while((dirent = readdir(spool)) != NULL) {
512:
513: PRIV_START
514: if (stat(dirent->d_name, &buf) != 0)
515: perr2("Cannot stat in ", _PATH_ATJOBS);
516: PRIV_END
517:
518: if (sscanf(dirent->d_name, "%c%5x%8lx", &queue, &jobno, &ctm) !=3)
519: continue;
520:
521: for (i = optind; i < argc; i++) {
522: if (atoi(argv[i]) == jobno) {
1.16 mickey 523: if ((buf.st_uid != real_uid) && !(real_uid == 0))
524: errx(EXIT_FAILURE,
525: "%s: Not owner\n", argv[i]);
1.7 millert 526: switch (what) {
527: case ATRM:
528: PRIV_START
529:
530: if (unlink(dirent->d_name) != 0)
531: perr(dirent->d_name);
532:
533: PRIV_END
534:
535: break;
536:
537: case CAT:
538: {
539: FILE *fp;
540: int ch;
541:
542: PRIV_START
543:
544: fp = fopen(dirent->d_name, "r");
545:
546: PRIV_END
547:
548: if (!fp)
549: perr("Cannot open file");
550:
551: while((ch = getc(fp)) != EOF)
552: putchar(ch);
553: }
554: break;
555:
556: default:
1.16 mickey 557: errx(EXIT_FAILURE,
558: "Internal error, process_jobs = %d",
1.7 millert 559: what);
560: break;
561: }
562: }
1.1 deraadt 563: }
564: }
565: } /* delete_jobs */
566:
567: /* Global functions */
568:
569: int
570: main(argc, argv)
571: int argc;
572: char **argv;
573: {
574: int c;
1.7 millert 575: char queue = DEFAULT_AT_QUEUE;
576: char queue_set = 0;
1.1 deraadt 577: char *pgm;
578:
579: enum {
1.7 millert 580: ATQ, ATRM, AT, BATCH, CAT
581: }; /* what program we want to run */
582: int program = AT; /* our default program */
583: char *options = "q:f:mvldbVc"; /* default options for at */
584: int disp_version = 0;
1.1 deraadt 585: time_t timer;
586:
587: RELINQUISH_PRIVS
588:
589: /* Eat any leading paths */
590: if ((pgm = strrchr(argv[0], '/')) == NULL)
591: pgm = argv[0];
592: else
593: pgm++;
594:
595: namep = pgm;
596:
597: /* find out what this program is supposed to do */
598: if (strcmp(pgm, "atq") == 0) {
599: program = ATQ;
1.7 millert 600: options = "q:vV";
1.1 deraadt 601: } else if (strcmp(pgm, "atrm") == 0) {
602: program = ATRM;
1.7 millert 603: options = "V";
1.1 deraadt 604: } else if (strcmp(pgm, "batch") == 0) {
605: program = BATCH;
1.7 millert 606: options = "f:q:mvV";
1.1 deraadt 607: }
608:
609: /* process whatever options we can process */
610: opterr = 1;
1.6 millert 611: while ((c = getopt(argc, argv, options)) != -1)
1.1 deraadt 612: switch (c) {
613: case 'v': /* verify time settings */
614: atverify = 1;
615: break;
616:
617: case 'm': /* send mail when job is complete */
618: send_mail = 1;
619: break;
620:
621: case 'f':
622: atinput = optarg;
623: break;
624:
625: case 'q': /* specify queue */
626: if (strlen(optarg) > 1)
627: usage();
628:
629: atqueue = queue = *optarg;
1.7 millert 630: if (!(islower(queue) || isupper(queue)))
1.1 deraadt 631: usage();
1.7 millert 632:
633: queue_set = 1;
634: break;
635:
636: case 'd':
637: if (program != AT)
638: usage();
639:
640: program = ATRM;
641: options = "V";
642: break;
643:
644: case 'l':
645: if (program != AT)
646: usage();
647:
648: program = ATQ;
649: options = "q:vV";
650: break;
651:
652: case 'b':
653: if (program != AT)
654: usage();
655:
656: program = BATCH;
657: options = "f:q:mvV";
658: break;
659:
660: case 'V':
661: disp_version = 1;
662: break;
663:
664: case 'c':
665: program = CAT;
666: options = "";
1.1 deraadt 667: break;
668:
669: default:
670: usage();
671: break;
672: }
673: /* end of options eating */
674:
1.7 millert 675: if (disp_version)
676: (void)fprintf(stderr, "%s version %.1f\n", namep, AT_VERSION);
677:
1.16 mickey 678: if (!check_permission())
679: errx(EXIT_FAILURE, "You do not have permission to use %s.",
680: namep);
1.7 millert 681:
1.1 deraadt 682: /* select our program */
683: switch (program) {
684: case ATQ:
1.10 millert 685: if (optind != argc)
686: usage();
1.1 deraadt 687: list_jobs();
688: break;
689:
690: case ATRM:
1.7 millert 691: case CAT:
1.10 millert 692: if (optind == argc)
693: usage();
1.11 millert 694: process_jobs(argc, argv, program);
1.1 deraadt 695: break;
696:
697: case AT:
698: timer = parsetime(argc, argv);
699: if (atverify) {
700: struct tm *tm = localtime(&timer);
1.7 millert 701: (void)fprintf(stderr, "%s\n", asctime(tm));
1.1 deraadt 702: }
703: writefile(timer, queue);
704: break;
705:
706: case BATCH:
1.7 millert 707: if (queue_set)
708: queue = toupper(queue);
709: else
710: queue = DEFAULT_BATCH_QUEUE;
711:
712: if (argc > optind)
713: timer = parsetime(argc, argv);
714: else
715: timer = time(NULL);
716:
717: if (atverify) {
718: struct tm *tm = localtime(&timer);
719: (void)fprintf(stderr, "%s\n", asctime(tm));
720: }
721:
722: writefile(timer, queue);
1.1 deraadt 723: break;
724:
725: default:
726: panic("Internal error");
727: break;
728: }
729: exit(EXIT_SUCCESS);
730: }