at.c revision 7860
17767Sache/* 27767Sache * at.c : Put file into atrun queue 37767Sache * Copyright (C) 1993, 1994 Thomas Koenig 4941Snate * 57767Sache * Atrun & Atq modifications 67767Sache * Copyright (C) 1993 David Parsons 7941Snate * 8941Snate * Redistribution and use in source and binary forms, with or without 9941Snate * modification, are permitted provided that the following conditions 10941Snate * are met: 11941Snate * 1. Redistributions of source code must retain the above copyright 12941Snate * notice, this list of conditions and the following disclaimer. 13941Snate * 2. The name of the author(s) may not be used to endorse or promote 14941Snate * products derived from this software without specific prior written 15941Snate * permission. 16941Snate * 17941Snate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 18941Snate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19941Snate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20941Snate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21941Snate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22941Snate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23941Snate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24941Snate * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25941Snate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26941Snate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27941Snate */ 28941Snate 29941Snate#define _USE_BSD 1 30941Snate 31941Snate/* System Headers */ 327767Sache 33941Snate#include <sys/types.h> 34941Snate#include <sys/stat.h> 35941Snate#include <sys/wait.h> 36941Snate#include <ctype.h> 37941Snate#include <dirent.h> 38941Snate#include <errno.h> 39941Snate#include <fcntl.h> 40941Snate#include <pwd.h> 41941Snate#include <signal.h> 42941Snate#include <stddef.h> 43941Snate#include <stdio.h> 44941Snate#include <stdlib.h> 45941Snate#include <string.h> 46941Snate#include <time.h> 47941Snate#include <unistd.h> 487767Sache#ifndef __FreeBSD__ 497767Sache#include <getopt.h> 507767Sache#endif 51941Snate 52941Snate/* Local headers */ 537767Sache 54941Snate#include "at.h" 55941Snate#include "panic.h" 56941Snate#include "parsetime.h" 577767Sache#include "perm.h" 587767Sache 59941Snate#define MAIN 60941Snate#include "privs.h" 61941Snate 62941Snate/* Macros */ 63941Snate 647767Sache#ifndef ATJOB_DIR 657767Sache#define ATJOB_DIR "/usr/spool/atjobs/" 667767Sache#endif 677767Sache 687767Sache#ifndef LFILE 697767Sache#define LFILE ATJOB_DIR ".lockfile" 707767Sache#endif 717767Sache 727767Sache#ifndef ATJOB_MX 737767Sache#define ATJOB_MX 255 747767Sache#endif 757767Sache 767767Sache#define ALARMC 10 /* Number of seconds to wait for timeout */ 777767Sache 78941Snate#define SIZE 255 79941Snate#define TIMESIZE 50 80941Snate 81941Snate/* File scope variables */ 827767Sache 837860Sachestatic char rcsid[] = "$Id: at.c,v 1.2 1995/04/12 02:42:28 ache Exp $"; 84941Snatechar *no_export[] = 85941Snate{ 867767Sache "TERM", "TERMCAP", "DISPLAY", "_" 877767Sache} ; 88941Snatestatic send_mail = 0; 89941Snate 90941Snate/* External variables */ 917767Sache 92941Snateextern char **environ; 93941Snateint fcreated; 94941Snatechar *namep; 957767Sachechar atfile[] = ATJOB_DIR "12345678901234"; 96941Snate 977767Sachechar *atinput = (char*)0; /* where to get input from */ 98941Snatechar atqueue = 0; /* which queue to examine for jobs (atq) */ 99941Snatechar atverify = 0; /* verify time instead of queuing job */ 100941Snate 101941Snate/* Function declarations */ 102941Snate 1037767Sachestatic void sigc(int signo); 1047767Sachestatic void alarmc(int signo); 1057767Sachestatic char *cwdname(void); 1067767Sachestatic void writefile(time_t runtimer, char queue); 1077767Sachestatic void list_jobs(void); 1087767Sache 109941Snate/* Signal catching functions */ 110941Snate 1117767Sachestatic void sigc(int signo) 112941Snate{ 1137767Sache/* If the user presses ^C, remove the spool file and exit 114941Snate */ 1157767Sache if (fcreated) 1167767Sache { 1177767Sache PRIV_START 1187767Sache unlink(atfile); 1197767Sache PRIV_END 1207767Sache } 121941Snate 1227767Sache exit(EXIT_FAILURE); 123941Snate} 124941Snate 1257767Sachestatic void alarmc(int signo) 126941Snate{ 127941Snate/* Time out after some seconds 128941Snate */ 1297767Sache panic("File locking timed out"); 130941Snate} 131941Snate 132941Snate/* Local functions */ 133941Snate 1347767Sachestatic char *cwdname(void) 135941Snate{ 136941Snate/* Read in the current directory; the name will be overwritten on 137941Snate * subsequent calls. 138941Snate */ 1397767Sache static char *ptr = NULL; 1407767Sache static size_t size = SIZE; 141941Snate 1427767Sache if (ptr == NULL) 1437767Sache ptr = (char *) mymalloc(size); 1447767Sache 1457767Sache while (1) 1467767Sache { 147941Snate if (ptr == NULL) 1487767Sache panic("Out of memory"); 149941Snate 1507767Sache if (getcwd(ptr, size-1) != NULL) 1517767Sache return ptr; 1527767Sache 1537767Sache if (errno != ERANGE) 1547767Sache perr("Cannot get directory"); 1557767Sache 1567767Sache free (ptr); 1577767Sache size += SIZE; 1587767Sache ptr = (char *) mymalloc(size); 1597767Sache } 160941Snate} 161941Snate 162941Snatestatic void 1637767Sachewritefile(time_t runtimer, char queue) 164941Snate{ 1657767Sache/* This does most of the work if at or batch are invoked for writing a job. 1667767Sache */ 1677767Sache int i; 1687767Sache char *ap, *ppos, *mailname; 1697767Sache struct passwd *pass_entry; 1707767Sache struct stat statbuf; 1717767Sache int fdes, lockdes, fd2; 1727767Sache FILE *fp, *fpin; 1737767Sache struct sigaction act; 1747767Sache char **atenv; 1757767Sache int ch; 1767767Sache mode_t cmask; 1777767Sache struct flock lock; 1787767Sache 1797767Sache/* Install the signal handler for SIGINT; terminate after removing the 1807767Sache * spool file if necessary 1817767Sache */ 1827767Sache act.sa_handler = sigc; 1837767Sache sigemptyset(&(act.sa_mask)); 1847767Sache act.sa_flags = 0; 185941Snate 1867767Sache sigaction(SIGINT, &act, NULL); 187941Snate 1887767Sache ppos = atfile + strlen(ATJOB_DIR); 189941Snate 1907767Sache /* Loop over all possible file names for running something at this 1917767Sache * particular time, see if a file is there; the first empty slot at any 1927767Sache * particular time is used. Lock the file LFILE first to make sure 1937767Sache * we're alone when doing this. 1947767Sache */ 195941Snate 1967767Sache PRIV_START 197941Snate 1987767Sache if ((lockdes = open(LFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0) 1997767Sache perr("Cannot open lockfile " LFILE); 200941Snate 2017767Sache lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 2027767Sache lock.l_len = 0; 203941Snate 2047767Sache act.sa_handler = alarmc; 2057767Sache sigemptyset(&(act.sa_mask)); 2067767Sache act.sa_flags = 0; 207941Snate 2087767Sache /* Set an alarm so a timeout occurs after ALARMC seconds, in case 2097767Sache * something is seriously broken. 2107767Sache */ 2117767Sache sigaction(SIGALRM, &act, NULL); 2127767Sache alarm(ALARMC); 2137767Sache fcntl(lockdes, F_SETLKW, &lock); 2147767Sache alarm(0); 215941Snate 2167767Sache for(i=0; i<ATJOB_MX; i++) 2177767Sache { 2187767Sache sprintf(ppos, "%c%8lx.%2x", queue, 2197767Sache (unsigned long) (runtimer/60), i); 2207767Sache for(ap=ppos; *ap != '\0'; ap ++) 2217767Sache if (*ap == ' ') 2227767Sache *ap = '0'; 223941Snate 2247767Sache if (stat(atfile, &statbuf) != 0) 2257767Sache { 2267767Sache if (errno == ENOENT) 2277767Sache break; 2287767Sache else 2297767Sache perr("Cannot access " ATJOB_DIR); 2307767Sache } 2317767Sache } /* for */ 232941Snate 2337767Sache if (i >= ATJOB_MX) 2347767Sache panic("Too many jobs already"); 235941Snate 2367767Sache /* Create the file. The x bit is only going to be set after it has 2377767Sache * been completely written out, to make sure it is not executed in the 2387767Sache * meantime. To make sure they do not get deleted, turn off their r 2397767Sache * bit. Yes, this is a kluge. 2407767Sache */ 2417767Sache cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); 2427767Sache if ((fdes = creat(atfile, O_WRONLY)) == -1) 2437767Sache perr("Cannot create atjob file"); 244941Snate 2457767Sache if ((fd2 = dup(fdes)) <0) 2467767Sache perr("Error in dup() of job file"); 247941Snate 2487767Sache if(fchown(fd2, real_uid, real_gid) != 0) 2497767Sache perr("Cannot give away file"); 250941Snate 2517767Sache PRIV_END 252941Snate 2537767Sache /* We've successfully created the file; let's set the flag so it 2547767Sache * gets removed in case of an interrupt or error. 2557767Sache */ 2567767Sache fcreated = 1; 257941Snate 2587767Sache /* Now we can release the lock, so other people can access it 2597767Sache */ 2607767Sache lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 2617767Sache lock.l_len = 0; 2627767Sache fcntl(lockdes, F_SETLKW, &lock); 2637767Sache close(lockdes); 264941Snate 2657767Sache if((fp = fdopen(fdes, "w")) == NULL) 2667767Sache panic("Cannot reopen atjob file"); 267941Snate 2687767Sache /* Get the userid to mail to, first by trying getlogin(), which reads 2697767Sache * /etc/utmp, then from LOGNAME, finally from getpwuid(). 2707767Sache */ 2717767Sache mailname = getlogin(); 2727767Sache if (mailname == NULL) 2737767Sache mailname = getenv("LOGNAME"); 274941Snate 2757767Sache if ((mailname == NULL) || (mailname[0] == '\0') 2767767Sache || (strlen(mailname) > 8) || (getpwnam(mailname)==NULL)) 2777767Sache { 2787767Sache pass_entry = getpwuid(getuid()); 2797767Sache if (pass_entry != NULL) 2807767Sache mailname = pass_entry->pw_name; 2817767Sache } 282941Snate 2837767Sache if (atinput != (char *) NULL) 2847767Sache { 2857767Sache fpin = freopen(atinput, "r", stdin); 2867767Sache if (fpin == NULL) 2877767Sache perr("Cannot open input file"); 2887767Sache } 2897767Sache fprintf(fp, "#! /bin/sh\n# mail %8s %d\n", mailname, send_mail); 290941Snate 2917767Sache /* Write out the umask at the time of invocation 2927767Sache */ 2937767Sache fprintf(fp, "umask %lo\n", (unsigned long) cmask); 294941Snate 2957767Sache /* Write out the environment. Anything that may look like a 2967767Sache * special character to the shell is quoted, except for \n, which is 2977767Sache * done with a pair of "'s. Dont't export the no_export list (such 2987767Sache * as TERM or DISPLAY) because we don't want these. 2997767Sache */ 3007767Sache for (atenv= environ; *atenv != NULL; atenv++) 3017767Sache { 3027767Sache int export = 1; 3037767Sache char *eqp; 304941Snate 3057767Sache eqp = strchr(*atenv, '='); 3067767Sache if (ap == NULL) 3077767Sache eqp = *atenv; 3087767Sache else 3097767Sache { 3107767Sache int i; 3117767Sache for (i=0; i<sizeof(no_export)/sizeof(no_export[0]); i++) 3127767Sache { 3137767Sache export = export 3147767Sache && (strncmp(*atenv, no_export[i], 3157767Sache (size_t) (eqp-*atenv)) != 0); 3167767Sache } 3177767Sache eqp++; 3187767Sache } 319941Snate 3207767Sache if (export) 3217767Sache { 3227767Sache fwrite(*atenv, sizeof(char), eqp-*atenv, fp); 3237767Sache for(ap = eqp;*ap != '\0'; ap++) 3247767Sache { 3257767Sache if (*ap == '\n') 3267767Sache fprintf(fp, "\"\n\""); 3277767Sache else 3287767Sache { 3297767Sache if (*ap != '/' && !isalnum(*ap)) 3307767Sache fputc('\\', fp); 3317767Sache 3327767Sache fputc(*ap, fp); 333941Snate } 3347767Sache } 3357767Sache fputs("; export ", fp); 3367767Sache fwrite(*atenv, sizeof(char), eqp-*atenv -1, fp); 3377767Sache fputc('\n', fp); 3387767Sache 339941Snate } 3407767Sache } 3417767Sache /* Cd to the directory at the time and write out all the 3427767Sache * commands the user supplies from stdin. 3437767Sache */ 3447767Sache fprintf(fp, "cd "); 3457767Sache for (ap = cwdname(); *ap != '\0'; ap++) 3467767Sache { 3477767Sache if (*ap == '\n') 3487767Sache fprintf(fp, "\"\n\""); 3497767Sache else 3507767Sache { 3517767Sache if (*ap != '/' && !isalnum(*ap)) 3527767Sache fputc('\\', fp); 3537767Sache 3547767Sache fputc(*ap, fp); 3557767Sache } 3567767Sache } 3577767Sache /* Test cd's exit status: die if the original directory has been 3587767Sache * removed, become unreadable or whatever 3597767Sache */ 3607767Sache fprintf(fp, " || {\n\t echo 'Execution directory " 3617767Sache "inaccessible' >&2\n\t exit 1\n}\n"); 362941Snate 3637767Sache while((ch = getchar()) != EOF) 3647767Sache fputc(ch, fp); 365941Snate 3667767Sache fprintf(fp, "\n"); 3677767Sache if (ferror(fp)) 3687767Sache panic("Output error"); 3697767Sache 3707767Sache if (ferror(stdin)) 3717767Sache panic("Input error"); 372941Snate 3737767Sache fclose(fp); 374941Snate 3757767Sache /* Set the x bit so that we're ready to start executing 3767767Sache */ 377941Snate 3787767Sache if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0) 3797767Sache perr("Cannot give away file"); 380941Snate 3817767Sache close(fd2); 3827767Sache fprintf(stderr, "Job %s will be executed using /bin/sh\n", ppos); 383941Snate} 384941Snate 385941Snatestatic void 386941Snatelist_jobs() 387941Snate{ 3887767Sache /* List all a user's jobs in the queue, by looping through ATJOB_DIR, 3897767Sache * or everybody's if we are root 3907767Sache */ 3917767Sache struct passwd *pw; 3927767Sache DIR *spool; 3937767Sache struct dirent *dirent; 3947767Sache struct stat buf; 3957767Sache struct tm runtime; 3967767Sache unsigned long ctm; 3977767Sache char queue; 3987767Sache time_t runtimer; 3997767Sache char timestr[TIMESIZE]; 4007767Sache int first=1; 401941Snate 4027767Sache PRIV_START 403941Snate 4047767Sache if (chdir(ATJOB_DIR) != 0) 4057767Sache perr("Cannot change to " ATJOB_DIR); 406941Snate 4077767Sache if ((spool = opendir(".")) == NULL) 4087767Sache perr("Cannot open " ATJOB_DIR); 409941Snate 4107767Sache /* Loop over every file in the directory 4117767Sache */ 4127767Sache while((dirent = readdir(spool)) != NULL) { 4137767Sache if (stat(dirent->d_name, &buf) != 0) 4147767Sache perr("Cannot stat in " ATJOB_DIR); 4157767Sache 4167767Sache /* See it's a regular file and has its x bit turned on and 4177767Sache * is the user's 4187767Sache */ 4197767Sache if (!S_ISREG(buf.st_mode) 4207767Sache || ((buf.st_uid != real_uid) && ! (real_uid == 0)) 4217767Sache || !(S_IXUSR & buf.st_mode || atverify)) 4227767Sache continue; 423941Snate 4247767Sache if(sscanf(dirent->d_name, "%c%8lx", &queue, &ctm)!=2) 4257767Sache continue; 426941Snate 4277767Sache if (atqueue && (queue != atqueue)) 4287767Sache continue; 429941Snate 4307767Sache runtimer = 60*(time_t) ctm; 4317767Sache runtime = *localtime(&runtimer); 4327767Sache strftime(timestr, TIMESIZE, "%X %x", &runtime); 4337767Sache if (first) { 4347767Sache printf("Date\t\t\tOwner\tQueue\tJob#\n"); 4357767Sache first=0; 4367767Sache } 4377767Sache pw = getpwuid(buf.st_uid); 438941Snate 4397767Sache printf("%s\t%s\t%c%s\t%s\n", 4407767Sache timestr, 4417767Sache pw ? pw->pw_name : "???", 4427767Sache queue, 4437767Sache (S_IXUSR & buf.st_mode) ? "":"(done)", 4447767Sache dirent->d_name); 4457767Sache } 4467767Sache PRIV_END 447941Snate} 448941Snate 449941Snatestatic void 4507767Sachedelete_jobs(int argc, char **argv) 451941Snate{ 4527767Sache /* Delete every argument (job - ID) given 4537767Sache */ 4547767Sache int i; 4557767Sache struct stat buf; 456941Snate 4577767Sache PRIV_START 458941Snate 4597767Sache if (chdir(ATJOB_DIR) != 0) 4607767Sache perr("Cannot change to " ATJOB_DIR); 4617767Sache 4627767Sache for (i=optind; i < argc; i++) { 4637860Sache if (stat(argv[i], &buf) != 0) { 4647767Sache perr(argv[i]); 4657860Sache continue; 4667860Sache } 4677767Sache if ((buf.st_uid != real_uid) && !(real_uid == 0)) { 4687767Sache fprintf(stderr, "%s: Not owner\n", argv[i]); 4697860Sache continue; 470941Snate } 4717767Sache if (unlink(argv[i]) != 0) 4727767Sache perr(argv[i]); 4737767Sache } 4747767Sache PRIV_END 4757767Sache} /* delete_jobs */ 476941Snate 477941Snate/* Global functions */ 478941Snate 4797767Sachevoid * 4807767Sachemymalloc(size_t n) 4817767Sache{ 4827767Sache void *p; 4837767Sache if ((p=malloc(n))==(void *)0) 4847767Sache { 4857767Sache fprintf(stderr,"Virtual memory exhausted\n"); 4867767Sache exit(EXIT_FAILURE); 4877767Sache } 4887767Sache return p; 4897767Sache} 4907767Sache 491941Snateint 4927767Sachemain(int argc, char **argv) 493941Snate{ 4947767Sache int c; 4957767Sache char queue = DEFAULT_AT_QUEUE; 4967767Sache char queue_set = 0; 4977767Sache char *pgm; 498941Snate 4997767Sache enum { ATQ, ATRM, AT, BATCH }; /* what program we want to run */ 5007767Sache int program = AT; /* our default program */ 5017767Sache char *options = "q:f:mvldbV"; /* default options for at */ 5027767Sache int disp_version = 0; 5037767Sache time_t timer; 504941Snate 5057767Sache RELINQUISH_PRIVS 506941Snate 5077767Sache /* Eat any leading paths 5087767Sache */ 5097767Sache if ((pgm = strrchr(argv[0], '/')) == NULL) 5107767Sache pgm = argv[0]; 5117767Sache else 5127767Sache pgm++; 513941Snate 5147767Sache namep = pgm; 515941Snate 5167767Sache /* find out what this program is supposed to do 5177767Sache */ 5187767Sache if (strcmp(pgm, "atq") == 0) { 5197767Sache program = ATQ; 5207767Sache options = "q:vV"; 5217767Sache } 5227767Sache else if (strcmp(pgm, "atrm") == 0) { 5237767Sache program = ATRM; 5247767Sache options = "V"; 5257767Sache } 5267767Sache else if (strcmp(pgm, "batch") == 0) { 5277767Sache program = BATCH; 5287767Sache options = "f:q:mvV"; 5297767Sache } 530941Snate 5317767Sache /* process whatever options we can process 5327767Sache */ 5337767Sache opterr=1; 5347767Sache while ((c=getopt(argc, argv, options)) != EOF) 5357767Sache switch (c) { 5367767Sache case 'v': /* verify time settings */ 5377767Sache atverify = 1; 5387767Sache break; 539941Snate 5407767Sache case 'm': /* send mail when job is complete */ 5417767Sache send_mail = 1; 5427767Sache break; 543941Snate 5447767Sache case 'f': 5457767Sache atinput = optarg; 5467767Sache break; 5477767Sache 5487767Sache case 'q': /* specify queue */ 5497767Sache if (strlen(optarg) > 1) 5507767Sache usage(); 551941Snate 5527767Sache atqueue = queue = *optarg; 5537767Sache if (!(islower(queue)||isupper(queue))) 5547767Sache usage(); 555941Snate 5567767Sache queue_set = 1; 5577767Sache break; 558941Snate 5597767Sache case 'd': 5607767Sache if (program != AT) 5617767Sache usage(); 562941Snate 5637767Sache program = ATRM; 5647767Sache options = "V"; 5657767Sache break; 566941Snate 5677767Sache case 'l': 5687767Sache if (program != AT) 5697767Sache usage(); 570941Snate 5717767Sache program = ATQ; 5727767Sache options = "q:vV"; 5737767Sache break; 574941Snate 5757767Sache case 'b': 5767767Sache if (program != AT) 5777767Sache usage(); 578941Snate 5797767Sache program = BATCH; 5807767Sache options = "f:q:mvV"; 5817767Sache break; 582941Snate 5837767Sache case 'V': 5847767Sache disp_version = 1; 5857767Sache break; 586941Snate 5877767Sache default: 5887767Sache usage(); 5897767Sache break; 5907767Sache } 5917767Sache /* end of options eating 5927767Sache */ 593941Snate 5947767Sache if (disp_version) 5957767Sache fprintf(stderr, "at version " VERSION "\n" 5967767Sache "Bug reports to: ig25@rz.uni-karlsruhe.de (Thomas Koenig)\n"); 597941Snate 5987767Sache /* select our program 5997767Sache */ 6007767Sache if(!check_permission()) 6017767Sache { 6027767Sache fprintf(stderr, "You do not have permission to use %s.\n",namep); 6037767Sache exit(EXIT_FAILURE); 6047767Sache } 6057767Sache switch (program) { 6067767Sache case ATQ: 607941Snate 6087767Sache REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 6097767Sache 6107767Sache list_jobs(); 6117767Sache break; 6127767Sache 6137767Sache case ATRM: 6147767Sache 6157767Sache REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 6167767Sache 6177767Sache delete_jobs(argc, argv); 6187767Sache break; 6197767Sache 6207767Sache case AT: 6217767Sache timer = parsetime(argc, argv); 6227767Sache if (atverify) 6237767Sache { 6247767Sache struct tm *tm = localtime(&timer); 6257767Sache fprintf(stderr, "%s\n", asctime(tm)); 626941Snate } 6277767Sache writefile(timer, queue); 6287767Sache break; 6297767Sache 6307767Sache case BATCH: 6317767Sache if (queue_set) 6327767Sache queue = toupper(queue); 6337767Sache else 6347767Sache queue = DEFAULT_BATCH_QUEUE; 6357767Sache 6367767Sache if (argc > optind) 6377767Sache timer = parsetime(argc, argv); 6387767Sache else 6397767Sache timer = time(NULL); 6407767Sache 6417767Sache if (atverify) 6427767Sache { 6437767Sache struct tm *tm = localtime(&timer); 6447767Sache fprintf(stderr, "%s\n", asctime(tm)); 6457767Sache } 6467767Sache 6477767Sache writefile(timer, queue); 6487767Sache break; 6497767Sache 6507767Sache default: 6517767Sache panic("Internal error"); 6527767Sache break; 6537767Sache } 6547767Sache exit(EXIT_SUCCESS); 655941Snate} 656