1 /* 2 * at.c : Put file into atrun queue 3 * Copyright (C) 1993, 1994 Thomas Koenig 4 * 5 * Atrun & Atq modifications 6 * Copyright (C) 1993 David Parsons 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. The name of the author(s) may not be used to endorse or promote 14 * products derived from this software without specific prior written 15 * permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #define _USE_BSD 1 33 34 /* System Headers */ 35 36 #include <sys/param.h> 37 #include <sys/stat.h> 38 #include <sys/time.h> 39 #include <sys/wait.h> 40 #include <ctype.h> 41 #include <dirent.h> 42 #include <err.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #ifndef __FreeBSD__ 46 #include <getopt.h> 47 #endif 48 #ifdef __FreeBSD__ 49 #include <locale.h> 50 #endif 51 #include <pwd.h> 52 #include <signal.h> 53 #include <stddef.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <time.h> 58 #include <unistd.h> 59 #include <utmp.h> 60 61 #if (MAXLOGNAME-1) > UT_NAMESIZE 62 #define LOGNAMESIZE UT_NAMESIZE 63 #else 64 #define LOGNAMESIZE (MAXLOGNAME-1) 65 #endif 66 67 /* Local headers */ 68 69 #include "at.h" 70 #include "panic.h" 71 #include "parsetime.h" 72 #include "perm.h" 73 74 #define MAIN 75 #include "privs.h" 76 77 /* Macros */ 78 79 #ifndef ATJOB_DIR 80 #define ATJOB_DIR "/usr/spool/atjobs/" 81 #endif 82 83 #ifndef LFILE 84 #define LFILE ATJOB_DIR ".lockfile" 85 #endif 86 87 #ifndef ATJOB_MX 88 #define ATJOB_MX 255 89 #endif 90 91 #define ALARMC 10 /* Number of seconds to wait for timeout */ 92 93 #define SIZE 255 94 #define TIMESIZE 50 95 96 enum { ATQ, ATRM, AT, BATCH, CAT }; /* what program we want to run */ 97 98 /* File scope variables */ 99 100 const char *no_export[] = 101 { 102 "TERM", "TERMCAP", "DISPLAY", "_" 103 } ; 104 static int send_mail = 0; 105 106 /* External variables */ 107 108 extern char **environ; 109 int fcreated; 110 char atfile[] = ATJOB_DIR "12345678901234"; 111 112 char *atinput = (char*)0; /* where to get input from */ 113 char atqueue = 0; /* which queue to examine for jobs (atq) */ 114 char atverify = 0; /* verify time instead of queuing job */ 115 char *namep; 116 117 /* Function declarations */ 118 119 static void sigc(int signo); 120 static void alarmc(int signo); 121 static char *cwdname(void); 122 static void writefile(time_t runtimer, char queue); 123 static void list_jobs(long *, int); 124 static long nextjob(void); 125 static time_t ttime(const char *arg); 126 static int in_job_list(long, long *, int); 127 static long *get_job_list(int, char *[], int *); 128 129 /* Signal catching functions */ 130 131 static void sigc(int signo __unused) 132 { 133 /* If the user presses ^C, remove the spool file and exit 134 */ 135 if (fcreated) 136 { 137 PRIV_START 138 unlink(atfile); 139 PRIV_END 140 } 141 142 _exit(EXIT_FAILURE); 143 } 144 145 static void alarmc(int signo __unused) 146 { 147 char buf[1024]; 148 149 /* Time out after some seconds. */ 150 strlcpy(buf, namep, sizeof(buf)); 151 strlcat(buf, ": file locking timed out\n", sizeof(buf)); 152 write(STDERR_FILENO, buf, strlen(buf)); 153 sigc(0); 154 } 155 156 /* Local functions */ 157 158 static char *cwdname(void) 159 { 160 /* Read in the current directory; the name will be overwritten on 161 * subsequent calls. 162 */ 163 static char *ptr = NULL; 164 static size_t size = SIZE; 165 166 if (ptr == NULL) 167 if ((ptr = malloc(size)) == NULL) 168 errx(EXIT_FAILURE, "virtual memory exhausted"); 169 170 while (1) 171 { 172 if (ptr == NULL) 173 panic("out of memory"); 174 175 if (getcwd(ptr, size-1) != NULL) 176 return ptr; 177 178 if (errno != ERANGE) 179 perr("cannot get directory"); 180 181 free (ptr); 182 size += SIZE; 183 if ((ptr = malloc(size)) == NULL) 184 errx(EXIT_FAILURE, "virtual memory exhausted"); 185 } 186 } 187 188 static long 189 nextjob() 190 { 191 long jobno; 192 FILE *fid; 193 194 if ((fid = fopen(ATJOB_DIR ".SEQ", "r+")) != (FILE*)0) { 195 if (fscanf(fid, "%5lx", &jobno) == 1) { 196 rewind(fid); 197 jobno = (1+jobno) % 0xfffff; /* 2^20 jobs enough? */ 198 fprintf(fid, "%05lx\n", jobno); 199 } 200 else 201 jobno = EOF; 202 fclose(fid); 203 return jobno; 204 } 205 else if ((fid = fopen(ATJOB_DIR ".SEQ", "w")) != (FILE*)0) { 206 fprintf(fid, "%05lx\n", jobno = 1); 207 fclose(fid); 208 return 1; 209 } 210 return EOF; 211 } 212 213 static void 214 writefile(time_t runtimer, char queue) 215 { 216 /* This does most of the work if at or batch are invoked for writing a job. 217 */ 218 long jobno; 219 char *ap, *ppos, *mailname; 220 struct passwd *pass_entry; 221 struct stat statbuf; 222 int fdes, lockdes, fd2; 223 FILE *fp, *fpin; 224 struct sigaction act; 225 char **atenv; 226 int ch; 227 mode_t cmask; 228 struct flock lock; 229 230 #ifdef __FreeBSD__ 231 (void) setlocale(LC_TIME, ""); 232 #endif 233 234 /* Install the signal handler for SIGINT; terminate after removing the 235 * spool file if necessary 236 */ 237 act.sa_handler = sigc; 238 sigemptyset(&(act.sa_mask)); 239 act.sa_flags = 0; 240 241 sigaction(SIGINT, &act, NULL); 242 243 ppos = atfile + strlen(ATJOB_DIR); 244 245 /* Loop over all possible file names for running something at this 246 * particular time, see if a file is there; the first empty slot at any 247 * particular time is used. Lock the file LFILE first to make sure 248 * we're alone when doing this. 249 */ 250 251 PRIV_START 252 253 if ((lockdes = open(LFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0) 254 perr("cannot open lockfile " LFILE); 255 256 lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 257 lock.l_len = 0; 258 259 act.sa_handler = alarmc; 260 sigemptyset(&(act.sa_mask)); 261 act.sa_flags = 0; 262 263 /* Set an alarm so a timeout occurs after ALARMC seconds, in case 264 * something is seriously broken. 265 */ 266 sigaction(SIGALRM, &act, NULL); 267 alarm(ALARMC); 268 fcntl(lockdes, F_SETLKW, &lock); 269 alarm(0); 270 271 if ((jobno = nextjob()) == EOF) 272 perr("cannot generate job number"); 273 274 sprintf(ppos, "%c%5lx%8lx", queue, 275 jobno, (unsigned long) (runtimer/60)); 276 277 for(ap=ppos; *ap != '\0'; ap ++) 278 if (*ap == ' ') 279 *ap = '0'; 280 281 if (stat(atfile, &statbuf) != 0) 282 if (errno != ENOENT) 283 perr("cannot access " ATJOB_DIR); 284 285 /* Create the file. The x bit is only going to be set after it has 286 * been completely written out, to make sure it is not executed in the 287 * meantime. To make sure they do not get deleted, turn off their r 288 * bit. Yes, this is a kluge. 289 */ 290 cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); 291 if ((fdes = creat(atfile, O_WRONLY)) == -1) 292 perr("cannot create atjob file"); 293 294 if ((fd2 = dup(fdes)) <0) 295 perr("error in dup() of job file"); 296 297 if(fchown(fd2, real_uid, real_gid) != 0) 298 perr("cannot give away file"); 299 300 PRIV_END 301 302 /* We no longer need suid root; now we just need to be able to write 303 * to the directory, if necessary. 304 */ 305 306 REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 307 308 /* We've successfully created the file; let's set the flag so it 309 * gets removed in case of an interrupt or error. 310 */ 311 fcreated = 1; 312 313 /* Now we can release the lock, so other people can access it 314 */ 315 lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 316 lock.l_len = 0; 317 fcntl(lockdes, F_SETLKW, &lock); 318 close(lockdes); 319 320 if((fp = fdopen(fdes, "w")) == NULL) 321 panic("cannot reopen atjob file"); 322 323 /* Get the userid to mail to, first by trying getlogin(), which reads 324 * /etc/utmp, then from LOGNAME, finally from getpwuid(). 325 */ 326 mailname = getlogin(); 327 if (mailname == NULL) 328 mailname = getenv("LOGNAME"); 329 330 if ((mailname == NULL) || (mailname[0] == '\0') 331 || (strlen(mailname) > LOGNAMESIZE) || (getpwnam(mailname)==NULL)) 332 { 333 pass_entry = getpwuid(real_uid); 334 if (pass_entry != NULL) 335 mailname = pass_entry->pw_name; 336 } 337 338 if (atinput != (char *) NULL) 339 { 340 fpin = freopen(atinput, "r", stdin); 341 if (fpin == NULL) 342 perr("cannot open input file"); 343 } 344 fprintf(fp, "#!/bin/sh\n# atrun uid=%ld gid=%ld\n# mail %*s %d\n", 345 (long) real_uid, (long) real_gid, LOGNAMESIZE, mailname, send_mail); 346 347 /* Write out the umask at the time of invocation 348 */ 349 fprintf(fp, "umask %lo\n", (unsigned long) cmask); 350 351 /* Write out the environment. Anything that may look like a 352 * special character to the shell is quoted, except for \n, which is 353 * done with a pair of "'s. Don't export the no_export list (such 354 * as TERM or DISPLAY) because we don't want these. 355 */ 356 for (atenv= environ; *atenv != NULL; atenv++) 357 { 358 int export = 1; 359 char *eqp; 360 361 eqp = strchr(*atenv, '='); 362 if (ap == NULL) 363 eqp = *atenv; 364 else 365 { 366 size_t i; 367 for (i=0; i<sizeof(no_export)/sizeof(no_export[0]); i++) 368 { 369 export = export 370 && (strncmp(*atenv, no_export[i], 371 (size_t) (eqp-*atenv)) != 0); 372 } 373 eqp++; 374 } 375 376 if (export) 377 { 378 fwrite(*atenv, sizeof(char), eqp-*atenv, fp); 379 for(ap = eqp;*ap != '\0'; ap++) 380 { 381 if (*ap == '\n') 382 fprintf(fp, "\"\n\""); 383 else 384 { 385 if (!isalnum(*ap)) { 386 switch (*ap) { 387 case '%': case '/': case '{': case '[': 388 case ']': case '=': case '}': case '@': 389 case '+': case '#': case ',': case '.': 390 case ':': case '-': case '_': 391 break; 392 default: 393 fputc('\\', fp); 394 break; 395 } 396 } 397 fputc(*ap, fp); 398 } 399 } 400 fputs("; export ", fp); 401 fwrite(*atenv, sizeof(char), eqp-*atenv -1, fp); 402 fputc('\n', fp); 403 404 } 405 } 406 /* Cd to the directory at the time and write out all the 407 * commands the user supplies from stdin. 408 */ 409 fprintf(fp, "cd "); 410 for (ap = cwdname(); *ap != '\0'; ap++) 411 { 412 if (*ap == '\n') 413 fprintf(fp, "\"\n\""); 414 else 415 { 416 if (*ap != '/' && !isalnum(*ap)) 417 fputc('\\', fp); 418 419 fputc(*ap, fp); 420 } 421 } 422 /* Test cd's exit status: die if the original directory has been 423 * removed, become unreadable or whatever 424 */ 425 fprintf(fp, " || {\n\t echo 'Execution directory " 426 "inaccessible' >&2\n\t exit 1\n}\n"); 427 428 while((ch = getchar()) != EOF) 429 fputc(ch, fp); 430 431 fprintf(fp, "\n"); 432 if (ferror(fp)) 433 panic("output error"); 434 435 if (ferror(stdin)) 436 panic("input error"); 437 438 fclose(fp); 439 440 /* Set the x bit so that we're ready to start executing 441 */ 442 443 if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0) 444 perr("cannot give away file"); 445 446 close(fd2); 447 fprintf(stderr, "Job %ld will be executed using /bin/sh\n", jobno); 448 } 449 450 static int 451 in_job_list(long job, long *joblist, int len) 452 { 453 int i; 454 455 for (i = 0; i < len; i++) 456 if (job == joblist[i]) 457 return 1; 458 459 return 0; 460 } 461 462 static void 463 list_jobs(long *joblist, int len) 464 { 465 /* List all a user's jobs in the queue, by looping through ATJOB_DIR, 466 * or everybody's if we are root 467 */ 468 struct passwd *pw; 469 DIR *spool; 470 struct dirent *dirent; 471 struct stat buf; 472 struct tm runtime; 473 unsigned long ctm; 474 char queue; 475 long jobno; 476 time_t runtimer; 477 char timestr[TIMESIZE]; 478 int first=1; 479 480 #ifdef __FreeBSD__ 481 (void) setlocale(LC_TIME, ""); 482 #endif 483 484 PRIV_START 485 486 if (chdir(ATJOB_DIR) != 0) 487 perr("cannot change to " ATJOB_DIR); 488 489 if ((spool = opendir(".")) == NULL) 490 perr("cannot open " ATJOB_DIR); 491 492 /* Loop over every file in the directory 493 */ 494 while((dirent = readdir(spool)) != NULL) { 495 if (stat(dirent->d_name, &buf) != 0) 496 perr("cannot stat in " ATJOB_DIR); 497 498 /* See it's a regular file and has its x bit turned on and 499 * is the user's 500 */ 501 if (!S_ISREG(buf.st_mode) 502 || ((buf.st_uid != real_uid) && ! (real_uid == 0)) 503 || !(S_IXUSR & buf.st_mode || atverify)) 504 continue; 505 506 if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3) 507 continue; 508 509 /* If jobs are given, only list those jobs */ 510 if (joblist && !in_job_list(jobno, joblist, len)) 511 continue; 512 513 if (atqueue && (queue != atqueue)) 514 continue; 515 516 runtimer = 60*(time_t) ctm; 517 runtime = *localtime(&runtimer); 518 strftime(timestr, TIMESIZE, "%+", &runtime); 519 if (first) { 520 printf("Date\t\t\t\tOwner\t\tQueue\tJob#\n"); 521 first=0; 522 } 523 pw = getpwuid(buf.st_uid); 524 525 printf("%s\t%-16s%c%s\t%ld\n", 526 timestr, 527 pw ? pw->pw_name : "???", 528 queue, 529 (S_IXUSR & buf.st_mode) ? "":"(done)", 530 jobno); 531 } 532 PRIV_END 533 } 534 535 static void 536 process_jobs(int argc, char **argv, int what) 537 { 538 /* Delete every argument (job - ID) given 539 */ 540 int i; 541 struct stat buf; 542 DIR *spool; 543 struct dirent *dirent; 544 unsigned long ctm; 545 char queue; 546 long jobno; 547 548 PRIV_START 549 550 if (chdir(ATJOB_DIR) != 0) 551 perr("cannot change to " ATJOB_DIR); 552 553 if ((spool = opendir(".")) == NULL) 554 perr("cannot open " ATJOB_DIR); 555 556 PRIV_END 557 558 /* Loop over every file in the directory 559 */ 560 while((dirent = readdir(spool)) != NULL) { 561 562 PRIV_START 563 if (stat(dirent->d_name, &buf) != 0) 564 perr("cannot stat in " ATJOB_DIR); 565 PRIV_END 566 567 if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3) 568 continue; 569 570 for (i=optind; i < argc; i++) { 571 if (atoi(argv[i]) == jobno) { 572 if ((buf.st_uid != real_uid) && !(real_uid == 0)) 573 errx(EXIT_FAILURE, "%s: not owner", argv[i]); 574 switch (what) { 575 case ATRM: 576 577 PRIV_START 578 579 if (unlink(dirent->d_name) != 0) 580 perr(dirent->d_name); 581 582 PRIV_END 583 584 break; 585 586 case CAT: 587 { 588 FILE *fp; 589 int ch; 590 591 PRIV_START 592 593 fp = fopen(dirent->d_name,"r"); 594 595 PRIV_END 596 597 if (!fp) { 598 perr("cannot open file"); 599 } 600 while((ch = getc(fp)) != EOF) { 601 putchar(ch); 602 } 603 } 604 break; 605 606 default: 607 errx(EXIT_FAILURE, "internal error, process_jobs = %d", 608 what); 609 } 610 } 611 } 612 } 613 } /* delete_jobs */ 614 615 #define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; 616 617 static time_t 618 ttime(const char *arg) 619 { 620 /* 621 * This is pretty much a copy of stime_arg1() from touch.c. I changed 622 * the return value and the argument list because it's more convenient 623 * (IMO) to do everything in one place. - Joe Halpin 624 */ 625 struct timeval tv[2]; 626 time_t now; 627 struct tm *t; 628 int yearset; 629 char *p; 630 631 if (gettimeofday(&tv[0], NULL)) 632 panic("Cannot get current time"); 633 634 /* Start with the current time. */ 635 now = tv[0].tv_sec; 636 if ((t = localtime(&now)) == NULL) 637 panic("localtime"); 638 /* [[CC]YY]MMDDhhmm[.SS] */ 639 if ((p = strchr(arg, '.')) == NULL) 640 t->tm_sec = 0; /* Seconds defaults to 0. */ 641 else { 642 if (strlen(p + 1) != 2) 643 goto terr; 644 *p++ = '\0'; 645 t->tm_sec = ATOI2(p); 646 } 647 648 yearset = 0; 649 switch(strlen(arg)) { 650 case 12: /* CCYYMMDDhhmm */ 651 t->tm_year = ATOI2(arg); 652 t->tm_year *= 100; 653 yearset = 1; 654 /* FALLTHROUGH */ 655 case 10: /* YYMMDDhhmm */ 656 if (yearset) { 657 yearset = ATOI2(arg); 658 t->tm_year += yearset; 659 } else { 660 yearset = ATOI2(arg); 661 t->tm_year = yearset + 2000; 662 } 663 t->tm_year -= 1900; /* Convert to UNIX time. */ 664 /* FALLTHROUGH */ 665 case 8: /* MMDDhhmm */ 666 t->tm_mon = ATOI2(arg); 667 --t->tm_mon; /* Convert from 01-12 to 00-11 */ 668 t->tm_mday = ATOI2(arg); 669 t->tm_hour = ATOI2(arg); 670 t->tm_min = ATOI2(arg); 671 break; 672 default: 673 goto terr; 674 } 675 676 t->tm_isdst = -1; /* Figure out DST. */ 677 tv[0].tv_sec = tv[1].tv_sec = mktime(t); 678 if (tv[0].tv_sec != -1) 679 return tv[0].tv_sec; 680 else 681 terr: 682 panic( 683 "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 684 } 685 686 static long * 687 get_job_list(int argc, char *argv[], int *joblen) 688 { 689 int i, len; 690 long *joblist; 691 char *ep; 692 693 joblist = NULL; 694 len = argc; 695 if (len > 0) { 696 if ((joblist = malloc(len * sizeof(*joblist))) == NULL) 697 panic("out of memory"); 698 699 for (i = 0; i < argc; i++) { 700 errno = 0; 701 if ((joblist[i] = strtol(argv[i], &ep, 10)) < 0 || 702 ep == argv[i] || *ep != '\0' || errno) 703 panic("invalid job number"); 704 } 705 } 706 707 *joblen = len; 708 return joblist; 709 } 710 711 int 712 main(int argc, char **argv) 713 { 714 int c; 715 char queue = DEFAULT_AT_QUEUE; 716 char queue_set = 0; 717 char *pgm; 718 719 int program = AT; /* our default program */ 720 const char *options = "q:f:t:rmvldbc"; /* default options for at */ 721 time_t timer; 722 long *joblist; 723 int joblen; 724 725 joblist = NULL; 726 joblen = 0; 727 timer = -1; 728 RELINQUISH_PRIVS 729 730 /* Eat any leading paths 731 */ 732 if ((pgm = strrchr(argv[0], '/')) == NULL) 733 pgm = argv[0]; 734 else 735 pgm++; 736 737 namep = pgm; 738 739 /* find out what this program is supposed to do 740 */ 741 if (strcmp(pgm, "atq") == 0) { 742 program = ATQ; 743 options = "q:v"; 744 } 745 else if (strcmp(pgm, "atrm") == 0) { 746 program = ATRM; 747 options = ""; 748 } 749 else if (strcmp(pgm, "batch") == 0) { 750 program = BATCH; 751 options = "f:q:mv"; 752 } 753 754 /* process whatever options we can process 755 */ 756 opterr=1; 757 while ((c=getopt(argc, argv, options)) != -1) 758 switch (c) { 759 case 'v': /* verify time settings */ 760 atverify = 1; 761 break; 762 763 case 'm': /* send mail when job is complete */ 764 send_mail = 1; 765 break; 766 767 case 'f': 768 atinput = optarg; 769 break; 770 771 case 'q': /* specify queue */ 772 if (strlen(optarg) > 1) 773 usage(); 774 775 atqueue = queue = *optarg; 776 if (!(islower(queue)||isupper(queue))) 777 usage(); 778 779 queue_set = 1; 780 break; 781 782 case 'd': 783 warnx("-d is deprecated; use -r instead"); 784 /* fall through to 'r' */ 785 786 case 'r': 787 if (program != AT) 788 usage(); 789 790 program = ATRM; 791 options = ""; 792 break; 793 794 case 't': 795 if (program != AT) 796 usage(); 797 timer = ttime(optarg); 798 break; 799 800 case 'l': 801 if (program != AT) 802 usage(); 803 804 program = ATQ; 805 options = "q:"; 806 break; 807 808 case 'b': 809 if (program != AT) 810 usage(); 811 812 program = BATCH; 813 options = "f:q:mv"; 814 break; 815 816 case 'c': 817 program = CAT; 818 options = ""; 819 break; 820 821 default: 822 usage(); 823 break; 824 } 825 /* end of options eating 826 */ 827 828 /* select our program 829 */ 830 if(!check_permission()) 831 errx(EXIT_FAILURE, "you do not have permission to use this program"); 832 switch (program) { 833 case ATQ: 834 835 REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 836 837 if (queue_set == 0) 838 joblist = get_job_list(argc - optind, argv + optind, &joblen); 839 list_jobs(joblist, joblen); 840 break; 841 842 case ATRM: 843 844 REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 845 846 process_jobs(argc, argv, ATRM); 847 break; 848 849 case CAT: 850 851 process_jobs(argc, argv, CAT); 852 break; 853 854 case AT: 855 /* 856 * If timer is > -1, then the user gave the time with -t. In that 857 * case, it's already been set. If not, set it now. 858 */ 859 if (timer == -1) 860 timer = parsetime(argc, argv); 861 862 if (atverify) 863 { 864 struct tm *tm = localtime(&timer); 865 fprintf(stderr, "%s\n", asctime(tm)); 866 } 867 writefile(timer, queue); 868 break; 869 870 case BATCH: 871 if (queue_set) 872 queue = toupper(queue); 873 else 874 queue = DEFAULT_BATCH_QUEUE; 875 876 if (argc > optind) 877 timer = parsetime(argc, argv); 878 else 879 timer = time(NULL); 880 881 if (atverify) 882 { 883 struct tm *tm = localtime(&timer); 884 fprintf(stderr, "%s\n", asctime(tm)); 885 } 886 887 writefile(timer, queue); 888 break; 889 890 default: 891 panic("internal error"); 892 break; 893 } 894 exit(EXIT_SUCCESS); 895 } 896