at.c revision 87208
110154Sache/* 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. 2010154Sache * IN NO EVENT SHALL THE AUTHOR(S) 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 2954158Scharnier#ifndef lint 3054158Scharnierstatic const char rcsid[] = 3154158Scharnier "$FreeBSD: head/usr.bin/at/at.c 87208 2001-12-02 12:26:18Z markm $"; 3254158Scharnier#endif /* not lint */ 3354158Scharnier 34941Snate#define _USE_BSD 1 35941Snate 36941Snate/* System Headers */ 377767Sache 38941Snate#include <sys/types.h> 39941Snate#include <sys/stat.h> 40941Snate#include <sys/wait.h> 4122873Sdavidn#include <sys/param.h> 42941Snate#include <ctype.h> 43941Snate#include <dirent.h> 4454158Scharnier#include <err.h> 45941Snate#include <errno.h> 46941Snate#include <fcntl.h> 47941Snate#include <pwd.h> 48941Snate#include <signal.h> 49941Snate#include <stddef.h> 50941Snate#include <stdio.h> 51941Snate#include <stdlib.h> 52941Snate#include <string.h> 53941Snate#include <time.h> 54941Snate#include <unistd.h> 5522873Sdavidn#include <utmp.h> 567767Sache#ifndef __FreeBSD__ 577767Sache#include <getopt.h> 5811760Sache#else 5911760Sache#include <locale.h> 607767Sache#endif 61941Snate 6223318Sache#if (MAXLOGNAME-1) > UT_NAMESIZE 6322873Sdavidn#define LOGNAMESIZE UT_NAMESIZE 6422873Sdavidn#else 6523318Sache#define LOGNAMESIZE (MAXLOGNAME-1) 6622873Sdavidn#endif 6722873Sdavidn 68941Snate/* Local headers */ 697767Sache 70941Snate#include "at.h" 71941Snate#include "panic.h" 72941Snate#include "parsetime.h" 737767Sache#include "perm.h" 747767Sache 75941Snate#define MAIN 76941Snate#include "privs.h" 77941Snate 78941Snate/* Macros */ 79941Snate 8010154Sache#ifndef ATJOB_DIR 817767Sache#define ATJOB_DIR "/usr/spool/atjobs/" 827767Sache#endif 837767Sache 847767Sache#ifndef LFILE 857767Sache#define LFILE ATJOB_DIR ".lockfile" 867767Sache#endif 877767Sache 887767Sache#ifndef ATJOB_MX 897767Sache#define ATJOB_MX 255 907767Sache#endif 917767Sache 927767Sache#define ALARMC 10 /* Number of seconds to wait for timeout */ 937767Sache 94941Snate#define SIZE 255 95941Snate#define TIMESIZE 50 96941Snate 9710154Sacheenum { ATQ, ATRM, AT, BATCH, CAT }; /* what program we want to run */ 9810154Sache 99941Snate/* File scope variables */ 1007767Sache 10187208Smarkmconst char *no_export[] = 102941Snate{ 1037767Sache "TERM", "TERMCAP", "DISPLAY", "_" 1047767Sache} ; 10546081Simpstatic int send_mail = 0; 106941Snate 107941Snate/* External variables */ 1087767Sache 109941Snateextern char **environ; 110941Snateint fcreated; 1117767Sachechar atfile[] = ATJOB_DIR "12345678901234"; 112941Snate 1137767Sachechar *atinput = (char*)0; /* where to get input from */ 114941Snatechar atqueue = 0; /* which queue to examine for jobs (atq) */ 115941Snatechar atverify = 0; /* verify time instead of queuing job */ 11682722Skrischar *namep; 117941Snate 118941Snate/* Function declarations */ 119941Snate 1207767Sachestatic void sigc(int signo); 1217767Sachestatic void alarmc(int signo); 1227767Sachestatic char *cwdname(void); 1237767Sachestatic void writefile(time_t runtimer, char queue); 1247767Sachestatic void list_jobs(void); 12587208Smarkmstatic long nextjob(void); 1267767Sache 127941Snate/* Signal catching functions */ 128941Snate 12987208Smarkmstatic void sigc(int signo __unused) 130941Snate{ 13110154Sache/* If the user presses ^C, remove the spool file and exit 132941Snate */ 1337767Sache if (fcreated) 1347767Sache { 1357767Sache PRIV_START 1367767Sache unlink(atfile); 1377767Sache PRIV_END 1387767Sache } 139941Snate 14082722Skris _exit(EXIT_FAILURE); 141941Snate} 142941Snate 14387208Smarkmstatic void alarmc(int signo __unused) 144941Snate{ 14582722Skris char buf[1024]; 14682722Skris 14782722Skris /* Time out after some seconds. */ 14882722Skris strlcpy(buf, namep, sizeof(buf)); 14982722Skris strlcat(buf, ": file locking timed out\n", sizeof(buf)); 15082722Skris write(STDERR_FILENO, buf, strlen(buf)); 15182722Skris sigc(0); 152941Snate} 153941Snate 154941Snate/* Local functions */ 155941Snate 1567767Sachestatic char *cwdname(void) 157941Snate{ 158941Snate/* Read in the current directory; the name will be overwritten on 159941Snate * subsequent calls. 160941Snate */ 1617767Sache static char *ptr = NULL; 1627767Sache static size_t size = SIZE; 163941Snate 1647767Sache if (ptr == NULL) 16580294Sobrien if ((ptr = malloc(size)) == NULL) 16680294Sobrien errx(EXIT_FAILURE, "virtual memory exhausted"); 1677767Sache 1687767Sache while (1) 1697767Sache { 170941Snate if (ptr == NULL) 17154158Scharnier panic("out of memory"); 172941Snate 1737767Sache if (getcwd(ptr, size-1) != NULL) 1747767Sache return ptr; 17510154Sache 1767767Sache if (errno != ERANGE) 17754158Scharnier perr("cannot get directory"); 17810154Sache 1797767Sache free (ptr); 1807767Sache size += SIZE; 18180294Sobrien if ((ptr = malloc(size)) == NULL) 18280294Sobrien errx(EXIT_FAILURE, "virtual memory exhausted"); 1837767Sache } 184941Snate} 185941Snate 18610154Sachestatic long 18710154Sachenextjob() 18810154Sache{ 18910154Sache long jobno; 19010154Sache FILE *fid; 19110154Sache 19210154Sache if ((fid = fopen(ATJOB_DIR ".SEQ", "r+")) != (FILE*)0) { 19310154Sache if (fscanf(fid, "%5lx", &jobno) == 1) { 19410154Sache rewind(fid); 19510154Sache jobno = (1+jobno) % 0xfffff; /* 2^20 jobs enough? */ 19610154Sache fprintf(fid, "%05lx\n", jobno); 19710154Sache } 19810154Sache else 19910154Sache jobno = EOF; 20010154Sache fclose(fid); 20110154Sache return jobno; 20210154Sache } 20310154Sache else if ((fid = fopen(ATJOB_DIR ".SEQ", "w")) != (FILE*)0) { 20410154Sache fprintf(fid, "%05lx\n", jobno = 1); 20510154Sache fclose(fid); 20610154Sache return 1; 20710154Sache } 20810154Sache return EOF; 20910154Sache} 21010154Sache 211941Snatestatic void 2127767Sachewritefile(time_t runtimer, char queue) 213941Snate{ 2147767Sache/* This does most of the work if at or batch are invoked for writing a job. 2157767Sache */ 21610154Sache long jobno; 2177767Sache char *ap, *ppos, *mailname; 2187767Sache struct passwd *pass_entry; 2197767Sache struct stat statbuf; 2207767Sache int fdes, lockdes, fd2; 2217767Sache FILE *fp, *fpin; 2227767Sache struct sigaction act; 2237767Sache char **atenv; 2247767Sache int ch; 2257767Sache mode_t cmask; 2267767Sache struct flock lock; 22710154Sache 22811760Sache#ifdef __FreeBSD__ 22911760Sache (void) setlocale(LC_TIME, ""); 23011760Sache#endif 23111760Sache 2327767Sache/* Install the signal handler for SIGINT; terminate after removing the 2337767Sache * spool file if necessary 2347767Sache */ 2357767Sache act.sa_handler = sigc; 2367767Sache sigemptyset(&(act.sa_mask)); 2377767Sache act.sa_flags = 0; 238941Snate 2397767Sache sigaction(SIGINT, &act, NULL); 240941Snate 2417767Sache ppos = atfile + strlen(ATJOB_DIR); 242941Snate 2437767Sache /* Loop over all possible file names for running something at this 2447767Sache * particular time, see if a file is there; the first empty slot at any 2457767Sache * particular time is used. Lock the file LFILE first to make sure 2467767Sache * we're alone when doing this. 2477767Sache */ 248941Snate 2497767Sache PRIV_START 250941Snate 2517767Sache if ((lockdes = open(LFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0) 25254158Scharnier perr("cannot open lockfile " LFILE); 253941Snate 2547767Sache lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 2557767Sache lock.l_len = 0; 256941Snate 2577767Sache act.sa_handler = alarmc; 2587767Sache sigemptyset(&(act.sa_mask)); 2597767Sache act.sa_flags = 0; 260941Snate 2617767Sache /* Set an alarm so a timeout occurs after ALARMC seconds, in case 2627767Sache * something is seriously broken. 2637767Sache */ 2647767Sache sigaction(SIGALRM, &act, NULL); 2657767Sache alarm(ALARMC); 2667767Sache fcntl(lockdes, F_SETLKW, &lock); 2677767Sache alarm(0); 268941Snate 26910154Sache if ((jobno = nextjob()) == EOF) 27054158Scharnier perr("cannot generate job number"); 271941Snate 27210154Sache sprintf(ppos, "%c%5lx%8lx", queue, 27310154Sache jobno, (unsigned long) (runtimer/60)); 274941Snate 27510154Sache for(ap=ppos; *ap != '\0'; ap ++) 27610154Sache if (*ap == ' ') 27710154Sache *ap = '0'; 278941Snate 27910154Sache if (stat(atfile, &statbuf) != 0) 28010154Sache if (errno != ENOENT) 28154158Scharnier perr("cannot access " ATJOB_DIR); 28210154Sache 2837767Sache /* Create the file. The x bit is only going to be set after it has 2847767Sache * been completely written out, to make sure it is not executed in the 2857767Sache * meantime. To make sure they do not get deleted, turn off their r 2867767Sache * bit. Yes, this is a kluge. 2877767Sache */ 2887767Sache cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); 2897767Sache if ((fdes = creat(atfile, O_WRONLY)) == -1) 29054158Scharnier perr("cannot create atjob file"); 291941Snate 2927767Sache if ((fd2 = dup(fdes)) <0) 29354158Scharnier perr("error in dup() of job file"); 294941Snate 2957767Sache if(fchown(fd2, real_uid, real_gid) != 0) 29654158Scharnier perr("cannot give away file"); 297941Snate 2987767Sache PRIV_END 299941Snate 3008112Sache /* We no longer need suid root; now we just need to be able to write 3018112Sache * to the directory, if necessary. 3028112Sache */ 3038112Sache 3048112Sache REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 3058112Sache 30610154Sache /* We've successfully created the file; let's set the flag so it 3077767Sache * gets removed in case of an interrupt or error. 3087767Sache */ 3097767Sache fcreated = 1; 310941Snate 3117767Sache /* Now we can release the lock, so other people can access it 3127767Sache */ 3137767Sache lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 3147767Sache lock.l_len = 0; 3157767Sache fcntl(lockdes, F_SETLKW, &lock); 3167767Sache close(lockdes); 317941Snate 3187767Sache if((fp = fdopen(fdes, "w")) == NULL) 31954158Scharnier panic("cannot reopen atjob file"); 320941Snate 3217767Sache /* Get the userid to mail to, first by trying getlogin(), which reads 3227767Sache * /etc/utmp, then from LOGNAME, finally from getpwuid(). 3237767Sache */ 3247767Sache mailname = getlogin(); 3257767Sache if (mailname == NULL) 3267767Sache mailname = getenv("LOGNAME"); 327941Snate 32810154Sache if ((mailname == NULL) || (mailname[0] == '\0') 32922873Sdavidn || (strlen(mailname) > LOGNAMESIZE) || (getpwnam(mailname)==NULL)) 3307767Sache { 33110154Sache pass_entry = getpwuid(real_uid); 3327767Sache if (pass_entry != NULL) 3337767Sache mailname = pass_entry->pw_name; 3347767Sache } 335941Snate 3367767Sache if (atinput != (char *) NULL) 3377767Sache { 3387767Sache fpin = freopen(atinput, "r", stdin); 3397767Sache if (fpin == NULL) 34054158Scharnier perr("cannot open input file"); 3417767Sache } 34222873Sdavidn fprintf(fp, "#!/bin/sh\n# atrun uid=%ld gid=%ld\n# mail %*s %d\n", 34322873Sdavidn (long) real_uid, (long) real_gid, LOGNAMESIZE, mailname, send_mail); 344941Snate 3457767Sache /* Write out the umask at the time of invocation 3467767Sache */ 3477767Sache fprintf(fp, "umask %lo\n", (unsigned long) cmask); 348941Snate 3497767Sache /* Write out the environment. Anything that may look like a 3507767Sache * special character to the shell is quoted, except for \n, which is 35154158Scharnier * done with a pair of "'s. Don't export the no_export list (such 3527767Sache * as TERM or DISPLAY) because we don't want these. 3537767Sache */ 3547767Sache for (atenv= environ; *atenv != NULL; atenv++) 3557767Sache { 3567767Sache int export = 1; 3577767Sache char *eqp; 358941Snate 3597767Sache eqp = strchr(*atenv, '='); 3607767Sache if (ap == NULL) 3617767Sache eqp = *atenv; 3627767Sache else 3637767Sache { 36487208Smarkm size_t i; 3657767Sache for (i=0; i<sizeof(no_export)/sizeof(no_export[0]); i++) 3667767Sache { 3677767Sache export = export 36810154Sache && (strncmp(*atenv, no_export[i], 3697767Sache (size_t) (eqp-*atenv)) != 0); 3707767Sache } 3717767Sache eqp++; 3727767Sache } 373941Snate 3747767Sache if (export) 3757767Sache { 3767767Sache fwrite(*atenv, sizeof(char), eqp-*atenv, fp); 3777767Sache for(ap = eqp;*ap != '\0'; ap++) 3787767Sache { 3797767Sache if (*ap == '\n') 3807767Sache fprintf(fp, "\"\n\""); 3817767Sache else 3827767Sache { 38310154Sache if (!isalnum(*ap)) { 38410154Sache switch (*ap) { 38510154Sache case '%': case '/': case '{': case '[': 38610154Sache case ']': case '=': case '}': case '@': 38710154Sache case '+': case '#': case ',': case '.': 38810154Sache case ':': case '-': case '_': 38910154Sache break; 39010154Sache default: 39110154Sache fputc('\\', fp); 39210154Sache break; 39310154Sache } 39410154Sache } 3957767Sache fputc(*ap, fp); 396941Snate } 3977767Sache } 3987767Sache fputs("; export ", fp); 3997767Sache fwrite(*atenv, sizeof(char), eqp-*atenv -1, fp); 4007767Sache fputc('\n', fp); 40110154Sache 402941Snate } 40310154Sache } 4047767Sache /* Cd to the directory at the time and write out all the 4057767Sache * commands the user supplies from stdin. 4067767Sache */ 4077767Sache fprintf(fp, "cd "); 4087767Sache for (ap = cwdname(); *ap != '\0'; ap++) 4097767Sache { 4107767Sache if (*ap == '\n') 4117767Sache fprintf(fp, "\"\n\""); 4127767Sache else 4137767Sache { 4147767Sache if (*ap != '/' && !isalnum(*ap)) 4157767Sache fputc('\\', fp); 41610154Sache 4177767Sache fputc(*ap, fp); 4187767Sache } 4197767Sache } 4207767Sache /* Test cd's exit status: die if the original directory has been 4217767Sache * removed, become unreadable or whatever 4227767Sache */ 4237767Sache fprintf(fp, " || {\n\t echo 'Execution directory " 4247767Sache "inaccessible' >&2\n\t exit 1\n}\n"); 425941Snate 4267767Sache while((ch = getchar()) != EOF) 4277767Sache fputc(ch, fp); 428941Snate 4297767Sache fprintf(fp, "\n"); 4307767Sache if (ferror(fp)) 43154158Scharnier panic("output error"); 43210154Sache 4337767Sache if (ferror(stdin)) 43454158Scharnier panic("input error"); 435941Snate 4367767Sache fclose(fp); 437941Snate 4387767Sache /* Set the x bit so that we're ready to start executing 4397767Sache */ 440941Snate 4417767Sache if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0) 44254158Scharnier perr("cannot give away file"); 443941Snate 4447767Sache close(fd2); 44510154Sache fprintf(stderr, "Job %ld will be executed using /bin/sh\n", jobno); 446941Snate} 447941Snate 448941Snatestatic void 449941Snatelist_jobs() 450941Snate{ 45110154Sache /* List all a user's jobs in the queue, by looping through ATJOB_DIR, 4527767Sache * or everybody's if we are root 4537767Sache */ 4547767Sache struct passwd *pw; 4557767Sache DIR *spool; 4567767Sache struct dirent *dirent; 4577767Sache struct stat buf; 4587767Sache struct tm runtime; 4597767Sache unsigned long ctm; 4607767Sache char queue; 46110154Sache long jobno; 4627767Sache time_t runtimer; 4637767Sache char timestr[TIMESIZE]; 4647767Sache int first=1; 46540389Smckay 46640389Smckay#ifdef __FreeBSD__ 46740389Smckay (void) setlocale(LC_TIME, ""); 46840389Smckay#endif 469941Snate 4707767Sache PRIV_START 471941Snate 4727767Sache if (chdir(ATJOB_DIR) != 0) 47354158Scharnier perr("cannot change to " ATJOB_DIR); 474941Snate 4757767Sache if ((spool = opendir(".")) == NULL) 47654158Scharnier perr("cannot open " ATJOB_DIR); 477941Snate 47810154Sache /* Loop over every file in the directory 4797767Sache */ 4807767Sache while((dirent = readdir(spool)) != NULL) { 4817767Sache if (stat(dirent->d_name, &buf) != 0) 48254158Scharnier perr("cannot stat in " ATJOB_DIR); 48310154Sache 4847767Sache /* See it's a regular file and has its x bit turned on and 4857767Sache * is the user's 4867767Sache */ 4877767Sache if (!S_ISREG(buf.st_mode) 4887767Sache || ((buf.st_uid != real_uid) && ! (real_uid == 0)) 4897767Sache || !(S_IXUSR & buf.st_mode || atverify)) 4907767Sache continue; 491941Snate 49210154Sache if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3) 4937767Sache continue; 494941Snate 4957767Sache if (atqueue && (queue != atqueue)) 4967767Sache continue; 497941Snate 4987767Sache runtimer = 60*(time_t) ctm; 4997767Sache runtime = *localtime(&runtimer); 50087208Smarkm strftime(timestr, TIMESIZE, "%+", &runtime); 5017767Sache if (first) { 5027767Sache printf("Date\t\t\tOwner\tQueue\tJob#\n"); 5037767Sache first=0; 5047767Sache } 5057767Sache pw = getpwuid(buf.st_uid); 506941Snate 50710154Sache printf("%s\t%s\t%c%s\t%ld\n", 50810154Sache timestr, 50910154Sache pw ? pw->pw_name : "???", 51010154Sache queue, 51110154Sache (S_IXUSR & buf.st_mode) ? "":"(done)", 51210154Sache jobno); 5137767Sache } 5147767Sache PRIV_END 515941Snate} 516941Snate 517941Snatestatic void 51810154Sacheprocess_jobs(int argc, char **argv, int what) 519941Snate{ 5207767Sache /* Delete every argument (job - ID) given 5217767Sache */ 5227767Sache int i; 5237767Sache struct stat buf; 52410154Sache DIR *spool; 52510154Sache struct dirent *dirent; 52610154Sache unsigned long ctm; 52710154Sache char queue; 52810154Sache long jobno; 529941Snate 5307767Sache PRIV_START 531941Snate 5327767Sache if (chdir(ATJOB_DIR) != 0) 53354158Scharnier perr("cannot change to " ATJOB_DIR); 5348874Srgrimes 53510154Sache if ((spool = opendir(".")) == NULL) 53654158Scharnier perr("cannot open " ATJOB_DIR); 53710154Sache 53810154Sache PRIV_END 53910154Sache 54010154Sache /* Loop over every file in the directory 54110154Sache */ 54210154Sache while((dirent = readdir(spool)) != NULL) { 54310154Sache 54410154Sache PRIV_START 54510154Sache if (stat(dirent->d_name, &buf) != 0) 54654158Scharnier perr("cannot stat in " ATJOB_DIR); 54710154Sache PRIV_END 54810154Sache 54910154Sache if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3) 5507860Sache continue; 55110154Sache 55210154Sache for (i=optind; i < argc; i++) { 55310154Sache if (atoi(argv[i]) == jobno) { 55454158Scharnier if ((buf.st_uid != real_uid) && !(real_uid == 0)) 55554158Scharnier errx(EXIT_FAILURE, "%s: not owner", argv[i]); 55610154Sache switch (what) { 55710154Sache case ATRM: 55810154Sache 55910154Sache PRIV_START 56010154Sache 56110154Sache if (unlink(dirent->d_name) != 0) 56210154Sache perr(dirent->d_name); 56310154Sache 56410154Sache PRIV_END 56510154Sache 56610154Sache break; 56710154Sache 56810154Sache case CAT: 56910154Sache { 57010154Sache FILE *fp; 57110154Sache int ch; 57210154Sache 57310154Sache PRIV_START 57410154Sache 57510154Sache fp = fopen(dirent->d_name,"r"); 57610154Sache 57710154Sache PRIV_END 57810154Sache 57910154Sache if (!fp) { 58054158Scharnier perr("cannot open file"); 58110154Sache } 58210154Sache while((ch = getc(fp)) != EOF) { 58310154Sache putchar(ch); 58410154Sache } 58510154Sache } 58610154Sache break; 58710154Sache 58810154Sache default: 58954158Scharnier errx(EXIT_FAILURE, "internal error, process_jobs = %d", 59054158Scharnier what); 59110154Sache } 59210154Sache } 5937860Sache } 5947767Sache } 5957767Sache} /* delete_jobs */ 596941Snate 597941Snateint 5987767Sachemain(int argc, char **argv) 599941Snate{ 6007767Sache int c; 6017767Sache char queue = DEFAULT_AT_QUEUE; 6027767Sache char queue_set = 0; 6037767Sache char *pgm; 604941Snate 6057767Sache int program = AT; /* our default program */ 60687208Smarkm const char *options = "q:f:mvldbVc";/* default options for at */ 6077767Sache int disp_version = 0; 6087767Sache time_t timer; 609941Snate 6107767Sache RELINQUISH_PRIVS 611941Snate 6127767Sache /* Eat any leading paths 6137767Sache */ 6147767Sache if ((pgm = strrchr(argv[0], '/')) == NULL) 6157767Sache pgm = argv[0]; 6167767Sache else 6177767Sache pgm++; 618941Snate 61982722Skris namep = pgm; 62082722Skris 6217767Sache /* find out what this program is supposed to do 6227767Sache */ 6237767Sache if (strcmp(pgm, "atq") == 0) { 6247767Sache program = ATQ; 6257767Sache options = "q:vV"; 6267767Sache } 6277767Sache else if (strcmp(pgm, "atrm") == 0) { 6287767Sache program = ATRM; 6297767Sache options = "V"; 6307767Sache } 6317767Sache else if (strcmp(pgm, "batch") == 0) { 6327767Sache program = BATCH; 6337767Sache options = "f:q:mvV"; 6347767Sache } 635941Snate 6367767Sache /* process whatever options we can process 6377767Sache */ 6387767Sache opterr=1; 63924360Simp while ((c=getopt(argc, argv, options)) != -1) 6407767Sache switch (c) { 6417767Sache case 'v': /* verify time settings */ 6427767Sache atverify = 1; 6437767Sache break; 644941Snate 6457767Sache case 'm': /* send mail when job is complete */ 6467767Sache send_mail = 1; 6477767Sache break; 648941Snate 6497767Sache case 'f': 6507767Sache atinput = optarg; 6517767Sache break; 65210154Sache 6537767Sache case 'q': /* specify queue */ 6547767Sache if (strlen(optarg) > 1) 6557767Sache usage(); 656941Snate 6577767Sache atqueue = queue = *optarg; 6587767Sache if (!(islower(queue)||isupper(queue))) 6597767Sache usage(); 660941Snate 6617767Sache queue_set = 1; 6627767Sache break; 663941Snate 6647767Sache case 'd': 6657767Sache if (program != AT) 6667767Sache usage(); 667941Snate 6687767Sache program = ATRM; 6697767Sache options = "V"; 6707767Sache break; 671941Snate 6727767Sache case 'l': 6737767Sache if (program != AT) 6747767Sache usage(); 675941Snate 6767767Sache program = ATQ; 6777767Sache options = "q:vV"; 6787767Sache break; 679941Snate 6807767Sache case 'b': 6817767Sache if (program != AT) 6827767Sache usage(); 683941Snate 6847767Sache program = BATCH; 6857767Sache options = "f:q:mvV"; 6867767Sache break; 687941Snate 6887767Sache case 'V': 6897767Sache disp_version = 1; 6907767Sache break; 691941Snate 69210154Sache case 'c': 69310154Sache program = CAT; 69410154Sache options = ""; 69510154Sache break; 69610154Sache 6977767Sache default: 6987767Sache usage(); 6997767Sache break; 7007767Sache } 7017767Sache /* end of options eating 7027767Sache */ 703941Snate 7047767Sache if (disp_version) 70582722Skris fprintf(stderr, "%s version " VERSION "\n" 70682722Skris "Bug reports to: ig25@rz.uni-karlsruhe.de (Thomas Koenig)\n", 70782722Skris namep); 708941Snate 7097767Sache /* select our program 7107767Sache */ 7117767Sache if(!check_permission()) 71254158Scharnier errx(EXIT_FAILURE, "you do not have permission to use this program"); 7137767Sache switch (program) { 7147767Sache case ATQ: 715941Snate 7167767Sache REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 7177767Sache 7187767Sache list_jobs(); 7197767Sache break; 7207767Sache 7217767Sache case ATRM: 7227767Sache 7237767Sache REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 7247767Sache 72510154Sache process_jobs(argc, argv, ATRM); 7267767Sache break; 7277767Sache 72810154Sache case CAT: 72910154Sache 73010154Sache process_jobs(argc, argv, CAT); 73110154Sache break; 73210154Sache 7337767Sache case AT: 7347767Sache timer = parsetime(argc, argv); 7357767Sache if (atverify) 7367767Sache { 7377767Sache struct tm *tm = localtime(&timer); 7387767Sache fprintf(stderr, "%s\n", asctime(tm)); 739941Snate } 7407767Sache writefile(timer, queue); 7417767Sache break; 7427767Sache 7437767Sache case BATCH: 7447767Sache if (queue_set) 7457767Sache queue = toupper(queue); 7467767Sache else 7477767Sache queue = DEFAULT_BATCH_QUEUE; 7487767Sache 7497767Sache if (argc > optind) 7507767Sache timer = parsetime(argc, argv); 7517767Sache else 7527767Sache timer = time(NULL); 75310154Sache 7547767Sache if (atverify) 7557767Sache { 7567767Sache struct tm *tm = localtime(&timer); 7577767Sache fprintf(stderr, "%s\n", asctime(tm)); 7587767Sache } 7597767Sache 7607767Sache writefile(timer, queue); 7617767Sache break; 7627767Sache 7637767Sache default: 76454158Scharnier panic("internal error"); 7657767Sache break; 7667767Sache } 7677767Sache exit(EXIT_SUCCESS); 768941Snate} 769