at.c revision 96701
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 2987230Smarkm#include <sys/cdefs.h> 3087230Smarkm__FBSDID("$FreeBSD: head/usr.bin/at/at.c 96701 2002-05-16 00:47:14Z tjr $"); 3187230Smarkm 32941Snate#define _USE_BSD 1 33941Snate 34941Snate/* System Headers */ 357767Sache 3691220Sbde#include <sys/param.h> 37941Snate#include <sys/stat.h> 3891220Sbde#include <sys/time.h> 39941Snate#include <sys/wait.h> 40941Snate#include <ctype.h> 41941Snate#include <dirent.h> 4254158Scharnier#include <err.h> 43941Snate#include <errno.h> 44941Snate#include <fcntl.h> 4591220Sbde#ifndef __FreeBSD__ 4691220Sbde#include <getopt.h> 4791220Sbde#endif 4891220Sbde#ifdef __FreeBSD__ 4991220Sbde#include <locale.h> 5091220Sbde#endif 51941Snate#include <pwd.h> 52941Snate#include <signal.h> 53941Snate#include <stddef.h> 54941Snate#include <stdio.h> 55941Snate#include <stdlib.h> 56941Snate#include <string.h> 57941Snate#include <time.h> 58941Snate#include <unistd.h> 5922873Sdavidn#include <utmp.h> 60941Snate 6123318Sache#if (MAXLOGNAME-1) > UT_NAMESIZE 6222873Sdavidn#define LOGNAMESIZE UT_NAMESIZE 6322873Sdavidn#else 6423318Sache#define LOGNAMESIZE (MAXLOGNAME-1) 6522873Sdavidn#endif 6622873Sdavidn 67941Snate/* Local headers */ 687767Sache 69941Snate#include "at.h" 70941Snate#include "panic.h" 71941Snate#include "parsetime.h" 727767Sache#include "perm.h" 737767Sache 74941Snate#define MAIN 75941Snate#include "privs.h" 76941Snate 77941Snate/* Macros */ 78941Snate 7910154Sache#ifndef ATJOB_DIR 807767Sache#define ATJOB_DIR "/usr/spool/atjobs/" 817767Sache#endif 827767Sache 837767Sache#ifndef LFILE 847767Sache#define LFILE ATJOB_DIR ".lockfile" 857767Sache#endif 867767Sache 877767Sache#ifndef ATJOB_MX 887767Sache#define ATJOB_MX 255 897767Sache#endif 907767Sache 917767Sache#define ALARMC 10 /* Number of seconds to wait for timeout */ 927767Sache 93941Snate#define SIZE 255 94941Snate#define TIMESIZE 50 95941Snate 9610154Sacheenum { ATQ, ATRM, AT, BATCH, CAT }; /* what program we want to run */ 9710154Sache 98941Snate/* File scope variables */ 997767Sache 10087208Smarkmconst char *no_export[] = 101941Snate{ 1027767Sache "TERM", "TERMCAP", "DISPLAY", "_" 1037767Sache} ; 10446081Simpstatic int send_mail = 0; 105941Snate 106941Snate/* External variables */ 1077767Sache 108941Snateextern char **environ; 109941Snateint fcreated; 1107767Sachechar atfile[] = ATJOB_DIR "12345678901234"; 111941Snate 1127767Sachechar *atinput = (char*)0; /* where to get input from */ 113941Snatechar atqueue = 0; /* which queue to examine for jobs (atq) */ 114941Snatechar atverify = 0; /* verify time instead of queuing job */ 11582722Skrischar *namep; 116941Snate 117941Snate/* Function declarations */ 118941Snate 1197767Sachestatic void sigc(int signo); 1207767Sachestatic void alarmc(int signo); 1217767Sachestatic char *cwdname(void); 1227767Sachestatic void writefile(time_t runtimer, char queue); 12396701Stjrstatic void list_jobs(long *, int); 12487208Smarkmstatic long nextjob(void); 12589315Smikestatic time_t ttime(const char *arg); 12696701Stjrstatic int in_job_list(long, long *, int); 12796701Stjrstatic long *get_job_list(int, char *[], int *); 1287767Sache 129941Snate/* Signal catching functions */ 130941Snate 13187208Smarkmstatic void sigc(int signo __unused) 132941Snate{ 13310154Sache/* If the user presses ^C, remove the spool file and exit 134941Snate */ 1357767Sache if (fcreated) 1367767Sache { 1377767Sache PRIV_START 1387767Sache unlink(atfile); 1397767Sache PRIV_END 1407767Sache } 141941Snate 14282722Skris _exit(EXIT_FAILURE); 143941Snate} 144941Snate 14587208Smarkmstatic void alarmc(int signo __unused) 146941Snate{ 14782722Skris char buf[1024]; 14882722Skris 14982722Skris /* Time out after some seconds. */ 15082722Skris strlcpy(buf, namep, sizeof(buf)); 15182722Skris strlcat(buf, ": file locking timed out\n", sizeof(buf)); 15282722Skris write(STDERR_FILENO, buf, strlen(buf)); 15382722Skris sigc(0); 154941Snate} 155941Snate 156941Snate/* Local functions */ 157941Snate 1587767Sachestatic char *cwdname(void) 159941Snate{ 160941Snate/* Read in the current directory; the name will be overwritten on 161941Snate * subsequent calls. 162941Snate */ 1637767Sache static char *ptr = NULL; 1647767Sache static size_t size = SIZE; 165941Snate 1667767Sache if (ptr == NULL) 16780294Sobrien if ((ptr = malloc(size)) == NULL) 16880294Sobrien errx(EXIT_FAILURE, "virtual memory exhausted"); 1697767Sache 1707767Sache while (1) 1717767Sache { 172941Snate if (ptr == NULL) 17354158Scharnier panic("out of memory"); 174941Snate 1757767Sache if (getcwd(ptr, size-1) != NULL) 1767767Sache return ptr; 17710154Sache 1787767Sache if (errno != ERANGE) 17954158Scharnier perr("cannot get directory"); 18010154Sache 1817767Sache free (ptr); 1827767Sache size += SIZE; 18380294Sobrien if ((ptr = malloc(size)) == NULL) 18480294Sobrien errx(EXIT_FAILURE, "virtual memory exhausted"); 1857767Sache } 186941Snate} 187941Snate 18810154Sachestatic long 18910154Sachenextjob() 19010154Sache{ 19110154Sache long jobno; 19210154Sache FILE *fid; 19310154Sache 19410154Sache if ((fid = fopen(ATJOB_DIR ".SEQ", "r+")) != (FILE*)0) { 19510154Sache if (fscanf(fid, "%5lx", &jobno) == 1) { 19610154Sache rewind(fid); 19710154Sache jobno = (1+jobno) % 0xfffff; /* 2^20 jobs enough? */ 19810154Sache fprintf(fid, "%05lx\n", jobno); 19910154Sache } 20010154Sache else 20110154Sache jobno = EOF; 20210154Sache fclose(fid); 20310154Sache return jobno; 20410154Sache } 20510154Sache else if ((fid = fopen(ATJOB_DIR ".SEQ", "w")) != (FILE*)0) { 20610154Sache fprintf(fid, "%05lx\n", jobno = 1); 20710154Sache fclose(fid); 20810154Sache return 1; 20910154Sache } 21010154Sache return EOF; 21110154Sache} 21210154Sache 213941Snatestatic void 2147767Sachewritefile(time_t runtimer, char queue) 215941Snate{ 2167767Sache/* This does most of the work if at or batch are invoked for writing a job. 2177767Sache */ 21810154Sache long jobno; 2197767Sache char *ap, *ppos, *mailname; 2207767Sache struct passwd *pass_entry; 2217767Sache struct stat statbuf; 2227767Sache int fdes, lockdes, fd2; 2237767Sache FILE *fp, *fpin; 2247767Sache struct sigaction act; 2257767Sache char **atenv; 2267767Sache int ch; 2277767Sache mode_t cmask; 2287767Sache struct flock lock; 22910154Sache 23011760Sache#ifdef __FreeBSD__ 23111760Sache (void) setlocale(LC_TIME, ""); 23211760Sache#endif 23311760Sache 2347767Sache/* Install the signal handler for SIGINT; terminate after removing the 2357767Sache * spool file if necessary 2367767Sache */ 2377767Sache act.sa_handler = sigc; 2387767Sache sigemptyset(&(act.sa_mask)); 2397767Sache act.sa_flags = 0; 240941Snate 2417767Sache sigaction(SIGINT, &act, NULL); 242941Snate 2437767Sache ppos = atfile + strlen(ATJOB_DIR); 244941Snate 2457767Sache /* Loop over all possible file names for running something at this 2467767Sache * particular time, see if a file is there; the first empty slot at any 2477767Sache * particular time is used. Lock the file LFILE first to make sure 2487767Sache * we're alone when doing this. 2497767Sache */ 250941Snate 2517767Sache PRIV_START 252941Snate 2537767Sache if ((lockdes = open(LFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0) 25454158Scharnier perr("cannot open lockfile " LFILE); 255941Snate 2567767Sache lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 2577767Sache lock.l_len = 0; 258941Snate 2597767Sache act.sa_handler = alarmc; 2607767Sache sigemptyset(&(act.sa_mask)); 2617767Sache act.sa_flags = 0; 262941Snate 2637767Sache /* Set an alarm so a timeout occurs after ALARMC seconds, in case 2647767Sache * something is seriously broken. 2657767Sache */ 2667767Sache sigaction(SIGALRM, &act, NULL); 2677767Sache alarm(ALARMC); 2687767Sache fcntl(lockdes, F_SETLKW, &lock); 2697767Sache alarm(0); 270941Snate 27110154Sache if ((jobno = nextjob()) == EOF) 27254158Scharnier perr("cannot generate job number"); 273941Snate 27410154Sache sprintf(ppos, "%c%5lx%8lx", queue, 27510154Sache jobno, (unsigned long) (runtimer/60)); 276941Snate 27710154Sache for(ap=ppos; *ap != '\0'; ap ++) 27810154Sache if (*ap == ' ') 27910154Sache *ap = '0'; 280941Snate 28110154Sache if (stat(atfile, &statbuf) != 0) 28210154Sache if (errno != ENOENT) 28354158Scharnier perr("cannot access " ATJOB_DIR); 28410154Sache 2857767Sache /* Create the file. The x bit is only going to be set after it has 2867767Sache * been completely written out, to make sure it is not executed in the 2877767Sache * meantime. To make sure they do not get deleted, turn off their r 2887767Sache * bit. Yes, this is a kluge. 2897767Sache */ 2907767Sache cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); 2917767Sache if ((fdes = creat(atfile, O_WRONLY)) == -1) 29254158Scharnier perr("cannot create atjob file"); 293941Snate 2947767Sache if ((fd2 = dup(fdes)) <0) 29554158Scharnier perr("error in dup() of job file"); 296941Snate 2977767Sache if(fchown(fd2, real_uid, real_gid) != 0) 29854158Scharnier perr("cannot give away file"); 299941Snate 3007767Sache PRIV_END 301941Snate 3028112Sache /* We no longer need suid root; now we just need to be able to write 3038112Sache * to the directory, if necessary. 3048112Sache */ 3058112Sache 3068112Sache REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 3078112Sache 30810154Sache /* We've successfully created the file; let's set the flag so it 3097767Sache * gets removed in case of an interrupt or error. 3107767Sache */ 3117767Sache fcreated = 1; 312941Snate 3137767Sache /* Now we can release the lock, so other people can access it 3147767Sache */ 3157767Sache lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 3167767Sache lock.l_len = 0; 3177767Sache fcntl(lockdes, F_SETLKW, &lock); 3187767Sache close(lockdes); 319941Snate 3207767Sache if((fp = fdopen(fdes, "w")) == NULL) 32154158Scharnier panic("cannot reopen atjob file"); 322941Snate 3237767Sache /* Get the userid to mail to, first by trying getlogin(), which reads 3247767Sache * /etc/utmp, then from LOGNAME, finally from getpwuid(). 3257767Sache */ 3267767Sache mailname = getlogin(); 3277767Sache if (mailname == NULL) 3287767Sache mailname = getenv("LOGNAME"); 329941Snate 33010154Sache if ((mailname == NULL) || (mailname[0] == '\0') 33122873Sdavidn || (strlen(mailname) > LOGNAMESIZE) || (getpwnam(mailname)==NULL)) 3327767Sache { 33310154Sache pass_entry = getpwuid(real_uid); 3347767Sache if (pass_entry != NULL) 3357767Sache mailname = pass_entry->pw_name; 3367767Sache } 337941Snate 3387767Sache if (atinput != (char *) NULL) 3397767Sache { 3407767Sache fpin = freopen(atinput, "r", stdin); 3417767Sache if (fpin == NULL) 34254158Scharnier perr("cannot open input file"); 3437767Sache } 34422873Sdavidn fprintf(fp, "#!/bin/sh\n# atrun uid=%ld gid=%ld\n# mail %*s %d\n", 34522873Sdavidn (long) real_uid, (long) real_gid, LOGNAMESIZE, mailname, send_mail); 346941Snate 3477767Sache /* Write out the umask at the time of invocation 3487767Sache */ 3497767Sache fprintf(fp, "umask %lo\n", (unsigned long) cmask); 350941Snate 3517767Sache /* Write out the environment. Anything that may look like a 3527767Sache * special character to the shell is quoted, except for \n, which is 35354158Scharnier * done with a pair of "'s. Don't export the no_export list (such 3547767Sache * as TERM or DISPLAY) because we don't want these. 3557767Sache */ 3567767Sache for (atenv= environ; *atenv != NULL; atenv++) 3577767Sache { 3587767Sache int export = 1; 3597767Sache char *eqp; 360941Snate 3617767Sache eqp = strchr(*atenv, '='); 3627767Sache if (ap == NULL) 3637767Sache eqp = *atenv; 3647767Sache else 3657767Sache { 36687208Smarkm size_t i; 3677767Sache for (i=0; i<sizeof(no_export)/sizeof(no_export[0]); i++) 3687767Sache { 3697767Sache export = export 37010154Sache && (strncmp(*atenv, no_export[i], 3717767Sache (size_t) (eqp-*atenv)) != 0); 3727767Sache } 3737767Sache eqp++; 3747767Sache } 375941Snate 3767767Sache if (export) 3777767Sache { 3787767Sache fwrite(*atenv, sizeof(char), eqp-*atenv, fp); 3797767Sache for(ap = eqp;*ap != '\0'; ap++) 3807767Sache { 3817767Sache if (*ap == '\n') 3827767Sache fprintf(fp, "\"\n\""); 3837767Sache else 3847767Sache { 38510154Sache if (!isalnum(*ap)) { 38610154Sache switch (*ap) { 38710154Sache case '%': case '/': case '{': case '[': 38810154Sache case ']': case '=': case '}': case '@': 38910154Sache case '+': case '#': case ',': case '.': 39010154Sache case ':': case '-': case '_': 39110154Sache break; 39210154Sache default: 39310154Sache fputc('\\', fp); 39410154Sache break; 39510154Sache } 39610154Sache } 3977767Sache fputc(*ap, fp); 398941Snate } 3997767Sache } 4007767Sache fputs("; export ", fp); 4017767Sache fwrite(*atenv, sizeof(char), eqp-*atenv -1, fp); 4027767Sache fputc('\n', fp); 40310154Sache 404941Snate } 40510154Sache } 4067767Sache /* Cd to the directory at the time and write out all the 4077767Sache * commands the user supplies from stdin. 4087767Sache */ 4097767Sache fprintf(fp, "cd "); 4107767Sache for (ap = cwdname(); *ap != '\0'; ap++) 4117767Sache { 4127767Sache if (*ap == '\n') 4137767Sache fprintf(fp, "\"\n\""); 4147767Sache else 4157767Sache { 4167767Sache if (*ap != '/' && !isalnum(*ap)) 4177767Sache fputc('\\', fp); 41810154Sache 4197767Sache fputc(*ap, fp); 4207767Sache } 4217767Sache } 4227767Sache /* Test cd's exit status: die if the original directory has been 4237767Sache * removed, become unreadable or whatever 4247767Sache */ 4257767Sache fprintf(fp, " || {\n\t echo 'Execution directory " 4267767Sache "inaccessible' >&2\n\t exit 1\n}\n"); 427941Snate 4287767Sache while((ch = getchar()) != EOF) 4297767Sache fputc(ch, fp); 430941Snate 4317767Sache fprintf(fp, "\n"); 4327767Sache if (ferror(fp)) 43354158Scharnier panic("output error"); 43410154Sache 4357767Sache if (ferror(stdin)) 43654158Scharnier panic("input error"); 437941Snate 4387767Sache fclose(fp); 439941Snate 4407767Sache /* Set the x bit so that we're ready to start executing 4417767Sache */ 442941Snate 4437767Sache if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0) 44454158Scharnier perr("cannot give away file"); 445941Snate 4467767Sache close(fd2); 44710154Sache fprintf(stderr, "Job %ld will be executed using /bin/sh\n", jobno); 448941Snate} 449941Snate 45096701Stjrstatic int 45196701Stjrin_job_list(long job, long *joblist, int len) 45296701Stjr{ 45396701Stjr int i; 45496701Stjr 45596701Stjr for (i = 0; i < len; i++) 45696701Stjr if (job == joblist[i]) 45796701Stjr return 1; 45896701Stjr 45996701Stjr return 0; 46096701Stjr} 46196701Stjr 462941Snatestatic void 46396701Stjrlist_jobs(long *joblist, int len) 464941Snate{ 46510154Sache /* List all a user's jobs in the queue, by looping through ATJOB_DIR, 4667767Sache * or everybody's if we are root 4677767Sache */ 4687767Sache struct passwd *pw; 4697767Sache DIR *spool; 4707767Sache struct dirent *dirent; 4717767Sache struct stat buf; 4727767Sache struct tm runtime; 4737767Sache unsigned long ctm; 4747767Sache char queue; 47510154Sache long jobno; 4767767Sache time_t runtimer; 4777767Sache char timestr[TIMESIZE]; 4787767Sache int first=1; 47940389Smckay 48040389Smckay#ifdef __FreeBSD__ 48140389Smckay (void) setlocale(LC_TIME, ""); 48240389Smckay#endif 483941Snate 4847767Sache PRIV_START 485941Snate 4867767Sache if (chdir(ATJOB_DIR) != 0) 48754158Scharnier perr("cannot change to " ATJOB_DIR); 488941Snate 4897767Sache if ((spool = opendir(".")) == NULL) 49054158Scharnier perr("cannot open " ATJOB_DIR); 491941Snate 49210154Sache /* Loop over every file in the directory 4937767Sache */ 4947767Sache while((dirent = readdir(spool)) != NULL) { 4957767Sache if (stat(dirent->d_name, &buf) != 0) 49654158Scharnier perr("cannot stat in " ATJOB_DIR); 49710154Sache 4987767Sache /* See it's a regular file and has its x bit turned on and 4997767Sache * is the user's 5007767Sache */ 5017767Sache if (!S_ISREG(buf.st_mode) 5027767Sache || ((buf.st_uid != real_uid) && ! (real_uid == 0)) 5037767Sache || !(S_IXUSR & buf.st_mode || atverify)) 5047767Sache continue; 505941Snate 50610154Sache if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3) 5077767Sache continue; 508941Snate 50996701Stjr /* If jobs are given, only list those jobs */ 51096701Stjr if (joblist && !in_job_list(jobno, joblist, len)) 51196701Stjr continue; 51296701Stjr 5137767Sache if (atqueue && (queue != atqueue)) 5147767Sache continue; 515941Snate 5167767Sache runtimer = 60*(time_t) ctm; 5177767Sache runtime = *localtime(&runtimer); 51887208Smarkm strftime(timestr, TIMESIZE, "%+", &runtime); 5197767Sache if (first) { 52096216Skuriyama printf("Date\t\t\t\tOwner\t\tQueue\tJob#\n"); 5217767Sache first=0; 5227767Sache } 5237767Sache pw = getpwuid(buf.st_uid); 524941Snate 52596216Skuriyama printf("%s\t%-16s%c%s\t%ld\n", 52610154Sache timestr, 52710154Sache pw ? pw->pw_name : "???", 52810154Sache queue, 52910154Sache (S_IXUSR & buf.st_mode) ? "":"(done)", 53010154Sache jobno); 5317767Sache } 5327767Sache PRIV_END 533941Snate} 534941Snate 535941Snatestatic void 53610154Sacheprocess_jobs(int argc, char **argv, int what) 537941Snate{ 5387767Sache /* Delete every argument (job - ID) given 5397767Sache */ 5407767Sache int i; 5417767Sache struct stat buf; 54210154Sache DIR *spool; 54310154Sache struct dirent *dirent; 54410154Sache unsigned long ctm; 54510154Sache char queue; 54610154Sache long jobno; 547941Snate 5487767Sache PRIV_START 549941Snate 5507767Sache if (chdir(ATJOB_DIR) != 0) 55154158Scharnier perr("cannot change to " ATJOB_DIR); 5528874Srgrimes 55310154Sache if ((spool = opendir(".")) == NULL) 55454158Scharnier perr("cannot open " ATJOB_DIR); 55510154Sache 55610154Sache PRIV_END 55710154Sache 55810154Sache /* Loop over every file in the directory 55910154Sache */ 56010154Sache while((dirent = readdir(spool)) != NULL) { 56110154Sache 56210154Sache PRIV_START 56310154Sache if (stat(dirent->d_name, &buf) != 0) 56454158Scharnier perr("cannot stat in " ATJOB_DIR); 56510154Sache PRIV_END 56610154Sache 56710154Sache if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3) 5687860Sache continue; 56910154Sache 57010154Sache for (i=optind; i < argc; i++) { 57110154Sache if (atoi(argv[i]) == jobno) { 57254158Scharnier if ((buf.st_uid != real_uid) && !(real_uid == 0)) 57354158Scharnier errx(EXIT_FAILURE, "%s: not owner", argv[i]); 57410154Sache switch (what) { 57510154Sache case ATRM: 57610154Sache 57710154Sache PRIV_START 57810154Sache 57910154Sache if (unlink(dirent->d_name) != 0) 58010154Sache perr(dirent->d_name); 58110154Sache 58210154Sache PRIV_END 58310154Sache 58410154Sache break; 58510154Sache 58610154Sache case CAT: 58710154Sache { 58810154Sache FILE *fp; 58910154Sache int ch; 59010154Sache 59110154Sache PRIV_START 59210154Sache 59310154Sache fp = fopen(dirent->d_name,"r"); 59410154Sache 59510154Sache PRIV_END 59610154Sache 59710154Sache if (!fp) { 59854158Scharnier perr("cannot open file"); 59910154Sache } 60010154Sache while((ch = getc(fp)) != EOF) { 60110154Sache putchar(ch); 60210154Sache } 60310154Sache } 60410154Sache break; 60510154Sache 60610154Sache default: 60754158Scharnier errx(EXIT_FAILURE, "internal error, process_jobs = %d", 60854158Scharnier what); 60910154Sache } 61010154Sache } 6117860Sache } 6127767Sache } 6137767Sache} /* delete_jobs */ 614941Snate 61589315Smike#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; 61689315Smike 61789315Smikestatic time_t 61889315Smikettime(const char *arg) 61989315Smike{ 62089315Smike /* 62189315Smike * This is pretty much a copy of stime_arg1() from touch.c. I changed 62289315Smike * the return value and the argument list because it's more convenient 62389315Smike * (IMO) to do everything in one place. - Joe Halpin 62489315Smike */ 62589315Smike struct timeval tv[2]; 62689315Smike time_t now; 62789315Smike struct tm *t; 62889315Smike int yearset; 62989315Smike char *p; 63089315Smike 63189315Smike if (gettimeofday(&tv[0], NULL)) 63289315Smike panic("Cannot get current time"); 63389315Smike 63489315Smike /* Start with the current time. */ 63589315Smike now = tv[0].tv_sec; 63689315Smike if ((t = localtime(&now)) == NULL) 63789315Smike panic("localtime"); 63889315Smike /* [[CC]YY]MMDDhhmm[.SS] */ 63989315Smike if ((p = strchr(arg, '.')) == NULL) 64089315Smike t->tm_sec = 0; /* Seconds defaults to 0. */ 64189315Smike else { 64289315Smike if (strlen(p + 1) != 2) 64389315Smike goto terr; 64489315Smike *p++ = '\0'; 64589315Smike t->tm_sec = ATOI2(p); 64689315Smike } 64789315Smike 64889315Smike yearset = 0; 64989315Smike switch(strlen(arg)) { 65089315Smike case 12: /* CCYYMMDDhhmm */ 65189315Smike t->tm_year = ATOI2(arg); 65289315Smike t->tm_year *= 100; 65389315Smike yearset = 1; 65489315Smike /* FALLTHROUGH */ 65589315Smike case 10: /* YYMMDDhhmm */ 65689315Smike if (yearset) { 65789315Smike yearset = ATOI2(arg); 65889315Smike t->tm_year += yearset; 65989315Smike } else { 66089315Smike yearset = ATOI2(arg); 66189315Smike t->tm_year = yearset + 2000; 66289315Smike } 66389315Smike t->tm_year -= 1900; /* Convert to UNIX time. */ 66489315Smike /* FALLTHROUGH */ 66589315Smike case 8: /* MMDDhhmm */ 66689315Smike t->tm_mon = ATOI2(arg); 66789315Smike --t->tm_mon; /* Convert from 01-12 to 00-11 */ 66889315Smike t->tm_mday = ATOI2(arg); 66989315Smike t->tm_hour = ATOI2(arg); 67089315Smike t->tm_min = ATOI2(arg); 67189315Smike break; 67289315Smike default: 67389315Smike goto terr; 67489315Smike } 67589315Smike 67689315Smike t->tm_isdst = -1; /* Figure out DST. */ 67789315Smike tv[0].tv_sec = tv[1].tv_sec = mktime(t); 67889315Smike if (tv[0].tv_sec != -1) 67989315Smike return tv[0].tv_sec; 68089315Smike else 68189315Smiketerr: 68289315Smike panic( 68389315Smike "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 68489315Smike} 68589315Smike 68696701Stjrstatic long * 68796701Stjrget_job_list(int argc, char *argv[], int *joblen) 68896701Stjr{ 68996701Stjr int i, len; 69096701Stjr long *joblist; 69196701Stjr char *ep; 69296701Stjr 69396701Stjr joblist = NULL; 69496701Stjr len = argc; 69596701Stjr if (len > 0) { 69696701Stjr if ((joblist = malloc(len * sizeof(*joblist))) == NULL) 69796701Stjr panic("out of memory"); 69896701Stjr 69996701Stjr for (i = 0; i < argc; i++) { 70096701Stjr errno = 0; 70196701Stjr if ((joblist[i] = strtol(argv[i], &ep, 10)) < 0 || 70296701Stjr ep == argv[i] || *ep != '\0' || errno) 70396701Stjr panic("invalid job number"); 70496701Stjr } 70596701Stjr } 70696701Stjr 70796701Stjr *joblen = len; 70896701Stjr return joblist; 70996701Stjr} 71096701Stjr 711941Snateint 7127767Sachemain(int argc, char **argv) 713941Snate{ 7147767Sache int c; 7157767Sache char queue = DEFAULT_AT_QUEUE; 7167767Sache char queue_set = 0; 7177767Sache char *pgm; 718941Snate 7197767Sache int program = AT; /* our default program */ 72089633Smike const char *options = "q:f:t:rmvldbc"; /* default options for at */ 7217767Sache time_t timer; 72296701Stjr long *joblist; 72396701Stjr int joblen; 724941Snate 72596701Stjr joblist = NULL; 72696701Stjr joblen = 0; 72789315Smike timer = -1; 7287767Sache RELINQUISH_PRIVS 729941Snate 7307767Sache /* Eat any leading paths 7317767Sache */ 7327767Sache if ((pgm = strrchr(argv[0], '/')) == NULL) 7337767Sache pgm = argv[0]; 7347767Sache else 7357767Sache pgm++; 736941Snate 73782722Skris namep = pgm; 73882722Skris 7397767Sache /* find out what this program is supposed to do 7407767Sache */ 7417767Sache if (strcmp(pgm, "atq") == 0) { 7427767Sache program = ATQ; 74389633Smike options = "q:v"; 7447767Sache } 7457767Sache else if (strcmp(pgm, "atrm") == 0) { 7467767Sache program = ATRM; 74789633Smike options = ""; 7487767Sache } 7497767Sache else if (strcmp(pgm, "batch") == 0) { 7507767Sache program = BATCH; 75189633Smike options = "f:q:mv"; 7527767Sache } 753941Snate 7547767Sache /* process whatever options we can process 7557767Sache */ 7567767Sache opterr=1; 75724360Simp while ((c=getopt(argc, argv, options)) != -1) 7587767Sache switch (c) { 7597767Sache case 'v': /* verify time settings */ 7607767Sache atverify = 1; 7617767Sache break; 762941Snate 7637767Sache case 'm': /* send mail when job is complete */ 7647767Sache send_mail = 1; 7657767Sache break; 766941Snate 7677767Sache case 'f': 7687767Sache atinput = optarg; 7697767Sache break; 77010154Sache 7717767Sache case 'q': /* specify queue */ 7727767Sache if (strlen(optarg) > 1) 7737767Sache usage(); 774941Snate 7757767Sache atqueue = queue = *optarg; 7767767Sache if (!(islower(queue)||isupper(queue))) 7777767Sache usage(); 778941Snate 7797767Sache queue_set = 1; 7807767Sache break; 781941Snate 7827767Sache case 'd': 78389315Smike warnx("-d is deprecated; use -r instead"); 78489315Smike /* fall through to 'r' */ 78589315Smike 78689315Smike case 'r': 7877767Sache if (program != AT) 7887767Sache usage(); 789941Snate 7907767Sache program = ATRM; 79189633Smike options = ""; 7927767Sache break; 793941Snate 79489315Smike case 't': 79589315Smike if (program != AT) 79689315Smike usage(); 79789315Smike timer = ttime(optarg); 79889315Smike break; 79989315Smike 8007767Sache case 'l': 8017767Sache if (program != AT) 8027767Sache usage(); 803941Snate 8047767Sache program = ATQ; 80596701Stjr options = "q:"; 8067767Sache break; 807941Snate 8087767Sache case 'b': 8097767Sache if (program != AT) 8107767Sache usage(); 811941Snate 8127767Sache program = BATCH; 81389633Smike options = "f:q:mv"; 8147767Sache break; 815941Snate 81610154Sache case 'c': 81710154Sache program = CAT; 81810154Sache options = ""; 81910154Sache break; 82010154Sache 8217767Sache default: 8227767Sache usage(); 8237767Sache break; 8247767Sache } 8257767Sache /* end of options eating 8267767Sache */ 827941Snate 8287767Sache /* select our program 8297767Sache */ 8307767Sache if(!check_permission()) 83154158Scharnier errx(EXIT_FAILURE, "you do not have permission to use this program"); 8327767Sache switch (program) { 8337767Sache case ATQ: 834941Snate 8357767Sache REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 8367767Sache 83796701Stjr if (queue_set == 0) 83896701Stjr joblist = get_job_list(argc - optind, argv + optind, &joblen); 83996701Stjr list_jobs(joblist, joblen); 8407767Sache break; 8417767Sache 8427767Sache case ATRM: 8437767Sache 8447767Sache REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 8457767Sache 84610154Sache process_jobs(argc, argv, ATRM); 8477767Sache break; 8487767Sache 84910154Sache case CAT: 85010154Sache 85110154Sache process_jobs(argc, argv, CAT); 85210154Sache break; 85310154Sache 8547767Sache case AT: 85589315Smike /* 85689315Smike * If timer is > -1, then the user gave the time with -t. In that 85789315Smike * case, it's already been set. If not, set it now. 85889315Smike */ 85989315Smike if (timer == -1) 86089315Smike timer = parsetime(argc, argv); 86189315Smike 8627767Sache if (atverify) 8637767Sache { 8647767Sache struct tm *tm = localtime(&timer); 8657767Sache fprintf(stderr, "%s\n", asctime(tm)); 866941Snate } 8677767Sache writefile(timer, queue); 8687767Sache break; 8697767Sache 8707767Sache case BATCH: 8717767Sache if (queue_set) 8727767Sache queue = toupper(queue); 8737767Sache else 8747767Sache queue = DEFAULT_BATCH_QUEUE; 8757767Sache 8767767Sache if (argc > optind) 8777767Sache timer = parsetime(argc, argv); 8787767Sache else 8797767Sache timer = time(NULL); 88010154Sache 8817767Sache if (atverify) 8827767Sache { 8837767Sache struct tm *tm = localtime(&timer); 8847767Sache fprintf(stderr, "%s\n", asctime(tm)); 8857767Sache } 8867767Sache 8877767Sache writefile(timer, queue); 8887767Sache break; 8897767Sache 8907767Sache default: 89154158Scharnier panic("internal error"); 8927767Sache break; 8937767Sache } 8947767Sache exit(EXIT_SUCCESS); 895941Snate} 896