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$"); 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> 59941Snate 60941Snate/* Local headers */ 617767Sache 62941Snate#include "at.h" 63941Snate#include "panic.h" 64941Snate#include "parsetime.h" 657767Sache#include "perm.h" 66227269Sed 67227269Sed#define MAIN 68941Snate#include "privs.h" 69941Snate 70941Snate/* Macros */ 71941Snate 7210154Sache#ifndef ATJOB_DIR 737767Sache#define ATJOB_DIR "/usr/spool/atjobs/" 747767Sache#endif 757767Sache 767767Sache#ifndef LFILE 777767Sache#define LFILE ATJOB_DIR ".lockfile" 787767Sache#endif 797767Sache 807767Sache#ifndef ATJOB_MX 817767Sache#define ATJOB_MX 255 827767Sache#endif 837767Sache 847767Sache#define ALARMC 10 /* Number of seconds to wait for timeout */ 857767Sache 86941Snate#define SIZE 255 87941Snate#define TIMESIZE 50 88941Snate 8910154Sacheenum { ATQ, ATRM, AT, BATCH, CAT }; /* what program we want to run */ 9010154Sache 91941Snate/* File scope variables */ 927767Sache 93227233Sedstatic const char *no_export[] = { 947767Sache "TERM", "TERMCAP", "DISPLAY", "_" 95227233Sed}; 9646081Simpstatic int send_mail = 0; 97227233Sedstatic char *atinput = NULL; /* where to get input from */ 98227233Sedstatic char atqueue = 0; /* which queue to examine for jobs (atq) */ 99941Snate 100941Snate/* External variables */ 1017767Sache 102941Snateextern char **environ; 103941Snateint fcreated; 1047767Sachechar atfile[] = ATJOB_DIR "12345678901234"; 105941Snatechar atverify = 0; /* verify time instead of queuing job */ 10682722Skrischar *namep; 107941Snate 108941Snate/* Function declarations */ 109941Snate 1107767Sachestatic void sigc(int signo); 1117767Sachestatic void alarmc(int signo); 1127767Sachestatic char *cwdname(void); 1137767Sachestatic void writefile(time_t runtimer, char queue); 11496701Stjrstatic void list_jobs(long *, int); 11587208Smarkmstatic long nextjob(void); 11689315Smikestatic time_t ttime(const char *arg); 11796701Stjrstatic int in_job_list(long, long *, int); 11896701Stjrstatic long *get_job_list(int, char *[], int *); 1197767Sache 120941Snate/* Signal catching functions */ 121941Snate 12287208Smarkmstatic void sigc(int signo __unused) 123941Snate{ 12410154Sache/* If the user presses ^C, remove the spool file and exit 125941Snate */ 1267767Sache if (fcreated) 1277767Sache { 1287767Sache PRIV_START 1297767Sache unlink(atfile); 1307767Sache PRIV_END 1317767Sache } 132941Snate 13382722Skris _exit(EXIT_FAILURE); 134941Snate} 135941Snate 13687208Smarkmstatic void alarmc(int signo __unused) 137941Snate{ 13882722Skris char buf[1024]; 13982722Skris 14082722Skris /* Time out after some seconds. */ 14182722Skris strlcpy(buf, namep, sizeof(buf)); 14282722Skris strlcat(buf, ": file locking timed out\n", sizeof(buf)); 14382722Skris write(STDERR_FILENO, buf, strlen(buf)); 14482722Skris sigc(0); 145941Snate} 146941Snate 147941Snate/* Local functions */ 148941Snate 1497767Sachestatic char *cwdname(void) 150941Snate{ 151941Snate/* Read in the current directory; the name will be overwritten on 152941Snate * subsequent calls. 153941Snate */ 1547767Sache static char *ptr = NULL; 1557767Sache static size_t size = SIZE; 156941Snate 1577767Sache if (ptr == NULL) 15880294Sobrien if ((ptr = malloc(size)) == NULL) 15980294Sobrien errx(EXIT_FAILURE, "virtual memory exhausted"); 1607767Sache 1617767Sache while (1) 1627767Sache { 163941Snate if (ptr == NULL) 16454158Scharnier panic("out of memory"); 165941Snate 1667767Sache if (getcwd(ptr, size-1) != NULL) 1677767Sache return ptr; 16810154Sache 1697767Sache if (errno != ERANGE) 17054158Scharnier perr("cannot get directory"); 17110154Sache 1727767Sache free (ptr); 1737767Sache size += SIZE; 17480294Sobrien if ((ptr = malloc(size)) == NULL) 17580294Sobrien errx(EXIT_FAILURE, "virtual memory exhausted"); 1767767Sache } 177941Snate} 178941Snate 17910154Sachestatic long 180201382Sednextjob(void) 18110154Sache{ 18210154Sache long jobno; 18310154Sache FILE *fid; 18410154Sache 185172261Skevlo if ((fid = fopen(ATJOB_DIR ".SEQ", "r+")) != NULL) { 18610154Sache if (fscanf(fid, "%5lx", &jobno) == 1) { 18710154Sache rewind(fid); 18810154Sache jobno = (1+jobno) % 0xfffff; /* 2^20 jobs enough? */ 18910154Sache fprintf(fid, "%05lx\n", jobno); 19010154Sache } 19110154Sache else 19210154Sache jobno = EOF; 19310154Sache fclose(fid); 19410154Sache return jobno; 19510154Sache } 196172261Skevlo else if ((fid = fopen(ATJOB_DIR ".SEQ", "w")) != NULL) { 19710154Sache fprintf(fid, "%05lx\n", jobno = 1); 19810154Sache fclose(fid); 19910154Sache return 1; 20010154Sache } 20110154Sache return EOF; 20210154Sache} 20310154Sache 204941Snatestatic void 2057767Sachewritefile(time_t runtimer, char queue) 206941Snate{ 2077767Sache/* This does most of the work if at or batch are invoked for writing a job. 2087767Sache */ 20910154Sache long jobno; 2107767Sache char *ap, *ppos, *mailname; 2117767Sache struct passwd *pass_entry; 2127767Sache struct stat statbuf; 2137767Sache int fdes, lockdes, fd2; 2147767Sache FILE *fp, *fpin; 2157767Sache struct sigaction act; 2167767Sache char **atenv; 2177767Sache int ch; 2187767Sache mode_t cmask; 2197767Sache struct flock lock; 22010154Sache 22111760Sache#ifdef __FreeBSD__ 22211760Sache (void) setlocale(LC_TIME, ""); 22311760Sache#endif 22411760Sache 2257767Sache/* Install the signal handler for SIGINT; terminate after removing the 2267767Sache * spool file if necessary 2277767Sache */ 2287767Sache act.sa_handler = sigc; 2297767Sache sigemptyset(&(act.sa_mask)); 2307767Sache act.sa_flags = 0; 231941Snate 2327767Sache sigaction(SIGINT, &act, NULL); 233941Snate 2347767Sache ppos = atfile + strlen(ATJOB_DIR); 235941Snate 2367767Sache /* Loop over all possible file names for running something at this 2377767Sache * particular time, see if a file is there; the first empty slot at any 2387767Sache * particular time is used. Lock the file LFILE first to make sure 2397767Sache * we're alone when doing this. 2407767Sache */ 241941Snate 2427767Sache PRIV_START 243941Snate 2447767Sache if ((lockdes = open(LFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0) 24554158Scharnier perr("cannot open lockfile " LFILE); 246941Snate 2477767Sache lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 2487767Sache lock.l_len = 0; 249941Snate 2507767Sache act.sa_handler = alarmc; 2517767Sache sigemptyset(&(act.sa_mask)); 2527767Sache act.sa_flags = 0; 253941Snate 2547767Sache /* Set an alarm so a timeout occurs after ALARMC seconds, in case 2557767Sache * something is seriously broken. 2567767Sache */ 2577767Sache sigaction(SIGALRM, &act, NULL); 2587767Sache alarm(ALARMC); 2597767Sache fcntl(lockdes, F_SETLKW, &lock); 2607767Sache alarm(0); 261941Snate 26210154Sache if ((jobno = nextjob()) == EOF) 26354158Scharnier perr("cannot generate job number"); 264941Snate 26510154Sache sprintf(ppos, "%c%5lx%8lx", queue, 26610154Sache jobno, (unsigned long) (runtimer/60)); 267941Snate 26810154Sache for(ap=ppos; *ap != '\0'; ap ++) 26910154Sache if (*ap == ' ') 27010154Sache *ap = '0'; 271941Snate 27210154Sache if (stat(atfile, &statbuf) != 0) 27310154Sache if (errno != ENOENT) 27454158Scharnier perr("cannot access " ATJOB_DIR); 27510154Sache 2767767Sache /* Create the file. The x bit is only going to be set after it has 2777767Sache * been completely written out, to make sure it is not executed in the 2787767Sache * meantime. To make sure they do not get deleted, turn off their r 2797767Sache * bit. Yes, this is a kluge. 2807767Sache */ 2817767Sache cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); 2827767Sache if ((fdes = creat(atfile, O_WRONLY)) == -1) 28354158Scharnier perr("cannot create atjob file"); 284941Snate 2857767Sache if ((fd2 = dup(fdes)) <0) 28654158Scharnier perr("error in dup() of job file"); 287941Snate 2887767Sache if(fchown(fd2, real_uid, real_gid) != 0) 28954158Scharnier perr("cannot give away file"); 290941Snate 2917767Sache PRIV_END 292941Snate 2938112Sache /* We no longer need suid root; now we just need to be able to write 2948112Sache * to the directory, if necessary. 2958112Sache */ 2968112Sache 2978112Sache REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 2988112Sache 29910154Sache /* We've successfully created the file; let's set the flag so it 3007767Sache * gets removed in case of an interrupt or error. 3017767Sache */ 3027767Sache fcreated = 1; 303941Snate 3047767Sache /* Now we can release the lock, so other people can access it 3057767Sache */ 3067767Sache lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 3077767Sache lock.l_len = 0; 3087767Sache fcntl(lockdes, F_SETLKW, &lock); 3097767Sache close(lockdes); 310941Snate 3117767Sache if((fp = fdopen(fdes, "w")) == NULL) 31254158Scharnier panic("cannot reopen atjob file"); 313941Snate 314100494Srobert /* Get the userid to mail to, first by trying getlogin(), 315100494Srobert * then from LOGNAME, finally from getpwuid(). 3167767Sache */ 3177767Sache mailname = getlogin(); 3187767Sache if (mailname == NULL) 3197767Sache mailname = getenv("LOGNAME"); 320941Snate 32110154Sache if ((mailname == NULL) || (mailname[0] == '\0') 322100494Srobert || (strlen(mailname) >= MAXLOGNAME) || (getpwnam(mailname)==NULL)) 3237767Sache { 32410154Sache pass_entry = getpwuid(real_uid); 3257767Sache if (pass_entry != NULL) 3267767Sache mailname = pass_entry->pw_name; 3277767Sache } 328941Snate 3297767Sache if (atinput != (char *) NULL) 3307767Sache { 3317767Sache fpin = freopen(atinput, "r", stdin); 3327767Sache if (fpin == NULL) 33354158Scharnier perr("cannot open input file"); 3347767Sache } 33522873Sdavidn fprintf(fp, "#!/bin/sh\n# atrun uid=%ld gid=%ld\n# mail %*s %d\n", 336100494Srobert (long) real_uid, (long) real_gid, MAXLOGNAME - 1, mailname, 337100494Srobert send_mail); 338941Snate 3397767Sache /* Write out the umask at the time of invocation 3407767Sache */ 3417767Sache fprintf(fp, "umask %lo\n", (unsigned long) cmask); 342941Snate 3437767Sache /* Write out the environment. Anything that may look like a 3447767Sache * special character to the shell is quoted, except for \n, which is 34554158Scharnier * done with a pair of "'s. Don't export the no_export list (such 3467767Sache * as TERM or DISPLAY) because we don't want these. 3477767Sache */ 3487767Sache for (atenv= environ; *atenv != NULL; atenv++) 3497767Sache { 3507767Sache int export = 1; 3517767Sache char *eqp; 352941Snate 3537767Sache eqp = strchr(*atenv, '='); 3547767Sache if (ap == NULL) 3557767Sache eqp = *atenv; 3567767Sache else 3577767Sache { 35887208Smarkm size_t i; 3597767Sache for (i=0; i<sizeof(no_export)/sizeof(no_export[0]); i++) 3607767Sache { 3617767Sache export = export 36210154Sache && (strncmp(*atenv, no_export[i], 3637767Sache (size_t) (eqp-*atenv)) != 0); 3647767Sache } 3657767Sache eqp++; 3667767Sache } 367941Snate 3687767Sache if (export) 3697767Sache { 370272438Sdelphij (void)fputs("export ", fp); 3717767Sache fwrite(*atenv, sizeof(char), eqp-*atenv, fp); 3727767Sache for(ap = eqp;*ap != '\0'; ap++) 3737767Sache { 3747767Sache if (*ap == '\n') 3757767Sache fprintf(fp, "\"\n\""); 3767767Sache else 3777767Sache { 37810154Sache if (!isalnum(*ap)) { 37910154Sache switch (*ap) { 38010154Sache case '%': case '/': case '{': case '[': 38110154Sache case ']': case '=': case '}': case '@': 38210154Sache case '+': case '#': case ',': case '.': 38310154Sache case ':': case '-': case '_': 38410154Sache break; 38510154Sache default: 38610154Sache fputc('\\', fp); 38710154Sache break; 38810154Sache } 38910154Sache } 3907767Sache fputc(*ap, fp); 391941Snate } 3927767Sache } 3937767Sache fputc('\n', fp); 39410154Sache 395941Snate } 39610154Sache } 3977767Sache /* Cd to the directory at the time and write out all the 3987767Sache * commands the user supplies from stdin. 3997767Sache */ 4007767Sache fprintf(fp, "cd "); 4017767Sache for (ap = cwdname(); *ap != '\0'; ap++) 4027767Sache { 4037767Sache if (*ap == '\n') 4047767Sache fprintf(fp, "\"\n\""); 4057767Sache else 4067767Sache { 4077767Sache if (*ap != '/' && !isalnum(*ap)) 4087767Sache fputc('\\', fp); 40910154Sache 4107767Sache fputc(*ap, fp); 4117767Sache } 4127767Sache } 4137767Sache /* Test cd's exit status: die if the original directory has been 4147767Sache * removed, become unreadable or whatever 4157767Sache */ 4167767Sache fprintf(fp, " || {\n\t echo 'Execution directory " 4177767Sache "inaccessible' >&2\n\t exit 1\n}\n"); 418941Snate 4197767Sache while((ch = getchar()) != EOF) 4207767Sache fputc(ch, fp); 421941Snate 4227767Sache fprintf(fp, "\n"); 4237767Sache if (ferror(fp)) 42454158Scharnier panic("output error"); 42510154Sache 4267767Sache if (ferror(stdin)) 42754158Scharnier panic("input error"); 428941Snate 4297767Sache fclose(fp); 430941Snate 4317767Sache /* Set the x bit so that we're ready to start executing 4327767Sache */ 433941Snate 4347767Sache if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0) 43554158Scharnier perr("cannot give away file"); 436941Snate 4377767Sache close(fd2); 43810154Sache fprintf(stderr, "Job %ld will be executed using /bin/sh\n", jobno); 439941Snate} 440941Snate 44196701Stjrstatic int 44296701Stjrin_job_list(long job, long *joblist, int len) 44396701Stjr{ 44496701Stjr int i; 44596701Stjr 44696701Stjr for (i = 0; i < len; i++) 44796701Stjr if (job == joblist[i]) 44896701Stjr return 1; 44996701Stjr 45096701Stjr return 0; 45196701Stjr} 45296701Stjr 453941Snatestatic void 45496701Stjrlist_jobs(long *joblist, int len) 455941Snate{ 45610154Sache /* List all a user's jobs in the queue, by looping through ATJOB_DIR, 4577767Sache * or everybody's if we are root 4587767Sache */ 4597767Sache struct passwd *pw; 4607767Sache DIR *spool; 4617767Sache struct dirent *dirent; 4627767Sache struct stat buf; 4637767Sache struct tm runtime; 4647767Sache unsigned long ctm; 4657767Sache char queue; 46610154Sache long jobno; 4677767Sache time_t runtimer; 4687767Sache char timestr[TIMESIZE]; 4697767Sache int first=1; 47040389Smckay 47140389Smckay#ifdef __FreeBSD__ 47240389Smckay (void) setlocale(LC_TIME, ""); 47340389Smckay#endif 474941Snate 4757767Sache PRIV_START 476941Snate 4777767Sache if (chdir(ATJOB_DIR) != 0) 47854158Scharnier perr("cannot change to " ATJOB_DIR); 479941Snate 4807767Sache if ((spool = opendir(".")) == NULL) 48154158Scharnier perr("cannot open " ATJOB_DIR); 482941Snate 48310154Sache /* Loop over every file in the directory 4847767Sache */ 4857767Sache while((dirent = readdir(spool)) != NULL) { 4867767Sache if (stat(dirent->d_name, &buf) != 0) 48754158Scharnier perr("cannot stat in " ATJOB_DIR); 48810154Sache 4897767Sache /* See it's a regular file and has its x bit turned on and 4907767Sache * is the user's 4917767Sache */ 4927767Sache if (!S_ISREG(buf.st_mode) 4937767Sache || ((buf.st_uid != real_uid) && ! (real_uid == 0)) 4947767Sache || !(S_IXUSR & buf.st_mode || atverify)) 4957767Sache continue; 496941Snate 49710154Sache if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3) 4987767Sache continue; 499941Snate 50096701Stjr /* If jobs are given, only list those jobs */ 50196701Stjr if (joblist && !in_job_list(jobno, joblist, len)) 50296701Stjr continue; 50396701Stjr 5047767Sache if (atqueue && (queue != atqueue)) 5057767Sache continue; 506941Snate 5077767Sache runtimer = 60*(time_t) ctm; 5087767Sache runtime = *localtime(&runtimer); 50987208Smarkm strftime(timestr, TIMESIZE, "%+", &runtime); 5107767Sache if (first) { 51196216Skuriyama printf("Date\t\t\t\tOwner\t\tQueue\tJob#\n"); 5127767Sache first=0; 5137767Sache } 5147767Sache pw = getpwuid(buf.st_uid); 515941Snate 51696216Skuriyama printf("%s\t%-16s%c%s\t%ld\n", 51710154Sache timestr, 51810154Sache pw ? pw->pw_name : "???", 51910154Sache queue, 52010154Sache (S_IXUSR & buf.st_mode) ? "":"(done)", 52110154Sache jobno); 5227767Sache } 5237767Sache PRIV_END 524215518Skevlo closedir(spool); 525941Snate} 526941Snate 527941Snatestatic void 52810154Sacheprocess_jobs(int argc, char **argv, int what) 529941Snate{ 5307767Sache /* Delete every argument (job - ID) given 5317767Sache */ 5327767Sache int i; 533249406Sgahr int rc; 534249406Sgahr int nofJobs; 535249406Sgahr int nofDone; 536249406Sgahr int statErrno; 5377767Sache struct stat buf; 53810154Sache DIR *spool; 53910154Sache struct dirent *dirent; 54010154Sache unsigned long ctm; 54110154Sache char queue; 54210154Sache long jobno; 543941Snate 544249406Sgahr nofJobs = argc - optind; 545249406Sgahr nofDone = 0; 546249406Sgahr 5477767Sache PRIV_START 548941Snate 5497767Sache if (chdir(ATJOB_DIR) != 0) 55054158Scharnier perr("cannot change to " ATJOB_DIR); 5518874Srgrimes 55210154Sache if ((spool = opendir(".")) == NULL) 55354158Scharnier perr("cannot open " ATJOB_DIR); 55410154Sache 55510154Sache PRIV_END 55610154Sache 55710154Sache /* Loop over every file in the directory 55810154Sache */ 55910154Sache while((dirent = readdir(spool)) != NULL) { 56010154Sache 56110154Sache PRIV_START 562249406Sgahr rc = stat(dirent->d_name, &buf); 563249406Sgahr statErrno = errno; 56410154Sache PRIV_END 565249406Sgahr /* There's a race condition between readdir above and stat here: 566249406Sgahr * another atrm process could have removed the file from the spool 567249406Sgahr * directory under our nose. If this happens, stat will set errno to 568249406Sgahr * ENOENT, which we shouldn't treat as fatal. 569249406Sgahr */ 570249406Sgahr if (rc != 0) { 571249406Sgahr if (statErrno == ENOENT) 572249406Sgahr continue; 573249406Sgahr else 574249406Sgahr perr("cannot stat in " ATJOB_DIR); 575249406Sgahr } 57610154Sache 57710154Sache if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3) 5787860Sache continue; 57910154Sache 58010154Sache for (i=optind; i < argc; i++) { 58110154Sache if (atoi(argv[i]) == jobno) { 58254158Scharnier if ((buf.st_uid != real_uid) && !(real_uid == 0)) 58354158Scharnier errx(EXIT_FAILURE, "%s: not owner", argv[i]); 58410154Sache switch (what) { 58510154Sache case ATRM: 58610154Sache 58710154Sache PRIV_START 58810154Sache 58910154Sache if (unlink(dirent->d_name) != 0) 59010154Sache perr(dirent->d_name); 59110154Sache 59210154Sache PRIV_END 59310154Sache 59410154Sache break; 59510154Sache 59610154Sache case CAT: 59710154Sache { 59810154Sache FILE *fp; 59910154Sache int ch; 60010154Sache 60110154Sache PRIV_START 60210154Sache 60310154Sache fp = fopen(dirent->d_name,"r"); 60410154Sache 60510154Sache PRIV_END 60610154Sache 60710154Sache if (!fp) { 60854158Scharnier perr("cannot open file"); 60910154Sache } 61010154Sache while((ch = getc(fp)) != EOF) { 61110154Sache putchar(ch); 61210154Sache } 613215518Skevlo fclose(fp); 61410154Sache } 61510154Sache break; 61610154Sache 61710154Sache default: 61854158Scharnier errx(EXIT_FAILURE, "internal error, process_jobs = %d", 61954158Scharnier what); 62010154Sache } 621249406Sgahr 622249406Sgahr /* All arguments have been processed 623249406Sgahr */ 624249406Sgahr if (++nofDone == nofJobs) 625249406Sgahr goto end; 62610154Sache } 6277860Sache } 6287767Sache } 629249406Sgahrend: 630215518Skevlo closedir(spool); 6317767Sache} /* delete_jobs */ 632941Snate 63389315Smike#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; 63489315Smike 63589315Smikestatic time_t 63689315Smikettime(const char *arg) 63789315Smike{ 63889315Smike /* 63989315Smike * This is pretty much a copy of stime_arg1() from touch.c. I changed 64089315Smike * the return value and the argument list because it's more convenient 64189315Smike * (IMO) to do everything in one place. - Joe Halpin 64289315Smike */ 64389315Smike struct timeval tv[2]; 64489315Smike time_t now; 64589315Smike struct tm *t; 64689315Smike int yearset; 64789315Smike char *p; 64889315Smike 64989315Smike if (gettimeofday(&tv[0], NULL)) 65089315Smike panic("Cannot get current time"); 65189315Smike 65289315Smike /* Start with the current time. */ 65389315Smike now = tv[0].tv_sec; 65489315Smike if ((t = localtime(&now)) == NULL) 65589315Smike panic("localtime"); 65689315Smike /* [[CC]YY]MMDDhhmm[.SS] */ 65789315Smike if ((p = strchr(arg, '.')) == NULL) 65889315Smike t->tm_sec = 0; /* Seconds defaults to 0. */ 65989315Smike else { 66089315Smike if (strlen(p + 1) != 2) 66189315Smike goto terr; 66289315Smike *p++ = '\0'; 66389315Smike t->tm_sec = ATOI2(p); 66489315Smike } 66589315Smike 66689315Smike yearset = 0; 66789315Smike switch(strlen(arg)) { 66889315Smike case 12: /* CCYYMMDDhhmm */ 66989315Smike t->tm_year = ATOI2(arg); 67089315Smike t->tm_year *= 100; 67189315Smike yearset = 1; 67289315Smike /* FALLTHROUGH */ 67389315Smike case 10: /* YYMMDDhhmm */ 67489315Smike if (yearset) { 67589315Smike yearset = ATOI2(arg); 67689315Smike t->tm_year += yearset; 67789315Smike } else { 67889315Smike yearset = ATOI2(arg); 67989315Smike t->tm_year = yearset + 2000; 68089315Smike } 68189315Smike t->tm_year -= 1900; /* Convert to UNIX time. */ 68289315Smike /* FALLTHROUGH */ 68389315Smike case 8: /* MMDDhhmm */ 68489315Smike t->tm_mon = ATOI2(arg); 68589315Smike --t->tm_mon; /* Convert from 01-12 to 00-11 */ 68689315Smike t->tm_mday = ATOI2(arg); 68789315Smike t->tm_hour = ATOI2(arg); 68889315Smike t->tm_min = ATOI2(arg); 68989315Smike break; 69089315Smike default: 69189315Smike goto terr; 69289315Smike } 69389315Smike 69489315Smike t->tm_isdst = -1; /* Figure out DST. */ 69589315Smike tv[0].tv_sec = tv[1].tv_sec = mktime(t); 69689315Smike if (tv[0].tv_sec != -1) 69789315Smike return tv[0].tv_sec; 69889315Smike else 69989315Smiketerr: 70089315Smike panic( 70189315Smike "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 70289315Smike} 70389315Smike 70496701Stjrstatic long * 70596701Stjrget_job_list(int argc, char *argv[], int *joblen) 70696701Stjr{ 70796701Stjr int i, len; 70896701Stjr long *joblist; 70996701Stjr char *ep; 71096701Stjr 71196701Stjr joblist = NULL; 71296701Stjr len = argc; 71396701Stjr if (len > 0) { 71496701Stjr if ((joblist = malloc(len * sizeof(*joblist))) == NULL) 71596701Stjr panic("out of memory"); 71696701Stjr 71796701Stjr for (i = 0; i < argc; i++) { 71896701Stjr errno = 0; 71996701Stjr if ((joblist[i] = strtol(argv[i], &ep, 10)) < 0 || 72096701Stjr ep == argv[i] || *ep != '\0' || errno) 72196701Stjr panic("invalid job number"); 72296701Stjr } 72396701Stjr } 72496701Stjr 72596701Stjr *joblen = len; 72696701Stjr return joblist; 72796701Stjr} 72896701Stjr 729941Snateint 7307767Sachemain(int argc, char **argv) 731941Snate{ 7327767Sache int c; 7337767Sache char queue = DEFAULT_AT_QUEUE; 7347767Sache char queue_set = 0; 7357767Sache char *pgm; 736941Snate 7377767Sache int program = AT; /* our default program */ 73889633Smike const char *options = "q:f:t:rmvldbc"; /* default options for at */ 7397767Sache time_t timer; 74096701Stjr long *joblist; 74196701Stjr int joblen; 742941Snate 74396701Stjr joblist = NULL; 74496701Stjr joblen = 0; 74589315Smike timer = -1; 7467767Sache RELINQUISH_PRIVS 747941Snate 7487767Sache /* Eat any leading paths 7497767Sache */ 7507767Sache if ((pgm = strrchr(argv[0], '/')) == NULL) 7517767Sache pgm = argv[0]; 7527767Sache else 7537767Sache pgm++; 754941Snate 75582722Skris namep = pgm; 75682722Skris 7577767Sache /* find out what this program is supposed to do 7587767Sache */ 7597767Sache if (strcmp(pgm, "atq") == 0) { 7607767Sache program = ATQ; 76189633Smike options = "q:v"; 7627767Sache } 7637767Sache else if (strcmp(pgm, "atrm") == 0) { 7647767Sache program = ATRM; 76589633Smike options = ""; 7667767Sache } 7677767Sache else if (strcmp(pgm, "batch") == 0) { 7687767Sache program = BATCH; 76989633Smike options = "f:q:mv"; 7707767Sache } 771941Snate 7727767Sache /* process whatever options we can process 7737767Sache */ 7747767Sache opterr=1; 77524360Simp while ((c=getopt(argc, argv, options)) != -1) 7767767Sache switch (c) { 7777767Sache case 'v': /* verify time settings */ 7787767Sache atverify = 1; 7797767Sache break; 780941Snate 7817767Sache case 'm': /* send mail when job is complete */ 7827767Sache send_mail = 1; 7837767Sache break; 784941Snate 7857767Sache case 'f': 7867767Sache atinput = optarg; 7877767Sache break; 78810154Sache 7897767Sache case 'q': /* specify queue */ 7907767Sache if (strlen(optarg) > 1) 7917767Sache usage(); 792941Snate 7937767Sache atqueue = queue = *optarg; 7947767Sache if (!(islower(queue)||isupper(queue))) 7957767Sache usage(); 796941Snate 7977767Sache queue_set = 1; 7987767Sache break; 799941Snate 8007767Sache case 'd': 80189315Smike warnx("-d is deprecated; use -r instead"); 80289315Smike /* fall through to 'r' */ 80389315Smike 80489315Smike case 'r': 8057767Sache if (program != AT) 8067767Sache usage(); 807941Snate 8087767Sache program = ATRM; 80989633Smike options = ""; 8107767Sache break; 811941Snate 81289315Smike case 't': 81389315Smike if (program != AT) 81489315Smike usage(); 81589315Smike timer = ttime(optarg); 81689315Smike break; 81789315Smike 8187767Sache case 'l': 8197767Sache if (program != AT) 8207767Sache usage(); 821941Snate 8227767Sache program = ATQ; 82396701Stjr options = "q:"; 8247767Sache break; 825941Snate 8267767Sache case 'b': 8277767Sache if (program != AT) 8287767Sache usage(); 829941Snate 8307767Sache program = BATCH; 83189633Smike options = "f:q:mv"; 8327767Sache break; 833941Snate 83410154Sache case 'c': 83510154Sache program = CAT; 83610154Sache options = ""; 83710154Sache break; 83810154Sache 8397767Sache default: 8407767Sache usage(); 8417767Sache break; 8427767Sache } 8437767Sache /* end of options eating 8447767Sache */ 845941Snate 8467767Sache /* select our program 8477767Sache */ 8487767Sache if(!check_permission()) 84954158Scharnier errx(EXIT_FAILURE, "you do not have permission to use this program"); 8507767Sache switch (program) { 8517767Sache case ATQ: 852941Snate 8537767Sache REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 8547767Sache 85596701Stjr if (queue_set == 0) 85696701Stjr joblist = get_job_list(argc - optind, argv + optind, &joblen); 85796701Stjr list_jobs(joblist, joblen); 8587767Sache break; 8597767Sache 8607767Sache case ATRM: 8617767Sache 8627767Sache REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 8637767Sache 86410154Sache process_jobs(argc, argv, ATRM); 8657767Sache break; 8667767Sache 86710154Sache case CAT: 86810154Sache 86910154Sache process_jobs(argc, argv, CAT); 87010154Sache break; 87110154Sache 8727767Sache case AT: 87389315Smike /* 87489315Smike * If timer is > -1, then the user gave the time with -t. In that 87589315Smike * case, it's already been set. If not, set it now. 87689315Smike */ 87789315Smike if (timer == -1) 87889315Smike timer = parsetime(argc, argv); 87989315Smike 8807767Sache if (atverify) 8817767Sache { 8827767Sache struct tm *tm = localtime(&timer); 8837767Sache fprintf(stderr, "%s\n", asctime(tm)); 884941Snate } 8857767Sache writefile(timer, queue); 8867767Sache break; 8877767Sache 8887767Sache case BATCH: 8897767Sache if (queue_set) 8907767Sache queue = toupper(queue); 8917767Sache else 8927767Sache queue = DEFAULT_BATCH_QUEUE; 8937767Sache 8947767Sache if (argc > optind) 8957767Sache timer = parsetime(argc, argv); 8967767Sache else 8977767Sache timer = time(NULL); 89810154Sache 8997767Sache if (atverify) 9007767Sache { 9017767Sache struct tm *tm = localtime(&timer); 9027767Sache fprintf(stderr, "%s\n", asctime(tm)); 9037767Sache } 9047767Sache 9057767Sache writefile(timer, queue); 9067767Sache break; 9077767Sache 9087767Sache default: 90954158Scharnier panic("internal error"); 9107767Sache break; 9117767Sache } 9127767Sache exit(EXIT_SUCCESS); 913941Snate} 914