118334Speter/* 218334Speter * at.c : Put file into atrun queue 318334Speter * Copyright (C) 1993, 1994 Thomas Koenig 418334Speter * 518334Speter * Atrun & Atq modifications 618334Speter * Copyright (C) 1993 David Parsons 718334Speter * 818334Speter * Redistribution and use in source and binary forms, with or without 918334Speter * modification, are permitted provided that the following conditions 1018334Speter * are met: 1118334Speter * 1. Redistributions of source code must retain the above copyright 1218334Speter * notice, this list of conditions and the following disclaimer. 1318334Speter * 2. The name of the author(s) may not be used to endorse or promote 1418334Speter * products derived from this software without specific prior written 1518334Speter * permission. 1618334Speter * 1718334Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 1818334Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1918334Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2018334Speter * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 2118334Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2218334Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2318334Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2418334Speter * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2518334Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2618334Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2718334Speter */ 2818334Speter 2918334Speter#include <sys/cdefs.h> 3018334Speter__FBSDID("$FreeBSD$"); 3118334Speter 3218334Speter#define _USE_BSD 1 3318334Speter 3418334Speter/* System Headers */ 3518334Speter 3618334Speter#include <sys/param.h> 3718334Speter#include <sys/stat.h> 3818334Speter#include <sys/time.h> 3918334Speter#include <sys/wait.h> 4018334Speter#include <ctype.h> 4118334Speter#include <dirent.h> 4218334Speter#include <err.h> 4318334Speter#include <errno.h> 4418334Speter#include <fcntl.h> 4518334Speter#ifndef __FreeBSD__ 4618334Speter#include <getopt.h> 4718334Speter#endif 4818334Speter#ifdef __FreeBSD__ 4918334Speter#include <locale.h> 5018334Speter#endif 5118334Speter#include <pwd.h> 5218334Speter#include <signal.h> 5318334Speter#include <stddef.h> 5418334Speter#include <stdio.h> 5518334Speter#include <stdlib.h> 5618334Speter#include <string.h> 5718334Speter#include <time.h> 5818334Speter#include <unistd.h> 5918334Speter 6018334Speter/* Local headers */ 6118334Speter 6218334Speter#include "at.h" 6318334Speter#include "panic.h" 6418334Speter#include "parsetime.h" 6518334Speter#include "perm.h" 6618334Speter 6718334Speter#define MAIN 6818334Speter#include "privs.h" 6918334Speter 7018334Speter/* Macros */ 7118334Speter 7218334Speter#ifndef ATJOB_DIR 7318334Speter#define ATJOB_DIR "/usr/spool/atjobs/" 7418334Speter#endif 7518334Speter 7618334Speter#ifndef LFILE 7718334Speter#define LFILE ATJOB_DIR ".lockfile" 7818334Speter#endif 7918334Speter 8018334Speter#ifndef ATJOB_MX 8118334Speter#define ATJOB_MX 255 8218334Speter#endif 8318334Speter 8418334Speter#define ALARMC 10 /* Number of seconds to wait for timeout */ 8518334Speter 8618334Speter#define SIZE 255 8718334Speter#define TIMESIZE 50 8818334Speter 8918334Speterenum { ATQ, ATRM, AT, BATCH, CAT }; /* what program we want to run */ 9018334Speter 9118334Speter/* File scope variables */ 9218334Speter 9318334Speterstatic const char *no_export[] = { 9418334Speter "TERM", "TERMCAP", "DISPLAY", "_" 9518334Speter}; 9618334Speterstatic int send_mail = 0; 9718334Speterstatic char *atinput = NULL; /* where to get input from */ 9818334Speterstatic char atqueue = 0; /* which queue to examine for jobs (atq) */ 9918334Speter 10018334Speter/* External variables */ 10118334Speter 10218334Speterextern char **environ; 10318334Speterint fcreated; 10418334Speterchar atfile[] = ATJOB_DIR "12345678901234"; 10518334Speterchar atverify = 0; /* verify time instead of queuing job */ 10618334Speterchar *namep; 10718334Speter 10818334Speter/* Function declarations */ 10918334Speter 11018334Speterstatic void sigc(int signo); 11118334Speterstatic void alarmc(int signo); 11218334Speterstatic char *cwdname(void); 11318334Speterstatic void writefile(time_t runtimer, char queue); 11418334Speterstatic void list_jobs(long *, int); 11518334Speterstatic long nextjob(void); 11618334Speterstatic time_t ttime(const char *arg); 11718334Speterstatic int in_job_list(long, long *, int); 11818334Speterstatic long *get_job_list(int, char *[], int *); 11918334Speter 12018334Speter/* Signal catching functions */ 12118334Speter 12218334Speterstatic void sigc(int signo __unused) 12318334Speter{ 12418334Speter/* If the user presses ^C, remove the spool file and exit 12518334Speter */ 12618334Speter if (fcreated) 12718334Speter { 12818334Speter PRIV_START 12918334Speter unlink(atfile); 13018334Speter PRIV_END 13118334Speter } 13218334Speter 13318334Speter _exit(EXIT_FAILURE); 13418334Speter} 13518334Speter 13618334Speterstatic void alarmc(int signo __unused) 13718334Speter{ 13818334Speter char buf[1024]; 13918334Speter 14018334Speter /* Time out after some seconds. */ 14118334Speter strlcpy(buf, namep, sizeof(buf)); 14218334Speter strlcat(buf, ": file locking timed out\n", sizeof(buf)); 14318334Speter write(STDERR_FILENO, buf, strlen(buf)); 14418334Speter sigc(0); 14518334Speter} 14618334Speter 14718334Speter/* Local functions */ 14818334Speter 14918334Speterstatic char *cwdname(void) 15018334Speter{ 15118334Speter/* Read in the current directory; the name will be overwritten on 15218334Speter * subsequent calls. 15318334Speter */ 15418334Speter static char *ptr = NULL; 15518334Speter static size_t size = SIZE; 15618334Speter 15718334Speter if (ptr == NULL) 15818334Speter if ((ptr = malloc(size)) == NULL) 15918334Speter errx(EXIT_FAILURE, "virtual memory exhausted"); 16018334Speter 16118334Speter while (1) 16218334Speter { 16318334Speter if (ptr == NULL) 16418334Speter panic("out of memory"); 16518334Speter 16618334Speter if (getcwd(ptr, size-1) != NULL) 16718334Speter return ptr; 16818334Speter 16918334Speter if (errno != ERANGE) 17018334Speter perr("cannot get directory"); 17118334Speter 17218334Speter free (ptr); 17318334Speter size += SIZE; 17418334Speter if ((ptr = malloc(size)) == NULL) 17518334Speter errx(EXIT_FAILURE, "virtual memory exhausted"); 17618334Speter } 17718334Speter} 17818334Speter 17918334Speterstatic long 18018334Speternextjob(void) 18118334Speter{ 18218334Speter long jobno; 18318334Speter FILE *fid; 18418334Speter 18518334Speter if ((fid = fopen(ATJOB_DIR ".SEQ", "r+")) != NULL) { 18618334Speter if (fscanf(fid, "%5lx", &jobno) == 1) { 18718334Speter rewind(fid); 18818334Speter jobno = (1+jobno) % 0xfffff; /* 2^20 jobs enough? */ 18918334Speter fprintf(fid, "%05lx\n", jobno); 19018334Speter } 19118334Speter else 19218334Speter jobno = EOF; 19318334Speter fclose(fid); 19418334Speter return jobno; 19518334Speter } 19618334Speter else if ((fid = fopen(ATJOB_DIR ".SEQ", "w")) != NULL) { 19718334Speter fprintf(fid, "%05lx\n", jobno = 1); 19818334Speter fclose(fid); 19918334Speter return 1; 20018334Speter } 20118334Speter return EOF; 20218334Speter} 20318334Speter 20418334Speterstatic void 20518334Speterwritefile(time_t runtimer, char queue) 20618334Speter{ 20718334Speter/* This does most of the work if at or batch are invoked for writing a job. 20818334Speter */ 20918334Speter long jobno; 21018334Speter char *ap, *ppos, *mailname; 21118334Speter struct passwd *pass_entry; 21218334Speter struct stat statbuf; 21318334Speter int fdes, lockdes, fd2; 21418334Speter FILE *fp, *fpin; 21518334Speter struct sigaction act; 21618334Speter char **atenv; 21718334Speter int ch; 21818334Speter mode_t cmask; 21918334Speter struct flock lock; 22018334Speter 22118334Speter#ifdef __FreeBSD__ 22218334Speter (void) setlocale(LC_TIME, ""); 22318334Speter#endif 22418334Speter 22518334Speter/* Install the signal handler for SIGINT; terminate after removing the 22618334Speter * spool file if necessary 22718334Speter */ 22818334Speter act.sa_handler = sigc; 22918334Speter sigemptyset(&(act.sa_mask)); 23018334Speter act.sa_flags = 0; 23118334Speter 23218334Speter sigaction(SIGINT, &act, NULL); 23318334Speter 23418334Speter ppos = atfile + strlen(ATJOB_DIR); 23518334Speter 23618334Speter /* Loop over all possible file names for running something at this 23718334Speter * particular time, see if a file is there; the first empty slot at any 23818334Speter * particular time is used. Lock the file LFILE first to make sure 23918334Speter * we're alone when doing this. 24018334Speter */ 24118334Speter 24218334Speter PRIV_START 24318334Speter 24418334Speter if ((lockdes = open(LFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0) 24518334Speter perr("cannot open lockfile " LFILE); 24618334Speter 24718334Speter lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 24818334Speter lock.l_len = 0; 24918334Speter 25018334Speter act.sa_handler = alarmc; 25118334Speter sigemptyset(&(act.sa_mask)); 25218334Speter act.sa_flags = 0; 25318334Speter 25418334Speter /* Set an alarm so a timeout occurs after ALARMC seconds, in case 25518334Speter * something is seriously broken. 25618334Speter */ 25718334Speter sigaction(SIGALRM, &act, NULL); 25818334Speter alarm(ALARMC); 25918334Speter fcntl(lockdes, F_SETLKW, &lock); 26018334Speter alarm(0); 26118334Speter 26218334Speter if ((jobno = nextjob()) == EOF) 26318334Speter perr("cannot generate job number"); 26418334Speter 26518334Speter sprintf(ppos, "%c%5lx%8lx", queue, 26618334Speter jobno, (unsigned long) (runtimer/60)); 26718334Speter 26818334Speter for(ap=ppos; *ap != '\0'; ap ++) 26918334Speter if (*ap == ' ') 27018334Speter *ap = '0'; 27118334Speter 27218334Speter if (stat(atfile, &statbuf) != 0) 27318334Speter if (errno != ENOENT) 27418334Speter perr("cannot access " ATJOB_DIR); 27518334Speter 27618334Speter /* Create the file. The x bit is only going to be set after it has 27718334Speter * been completely written out, to make sure it is not executed in the 27818334Speter * meantime. To make sure they do not get deleted, turn off their r 27918334Speter * bit. Yes, this is a kluge. 28018334Speter */ 28118334Speter cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); 28218334Speter if ((fdes = creat(atfile, O_WRONLY)) == -1) 28318334Speter perr("cannot create atjob file"); 28418334Speter 28518334Speter if ((fd2 = dup(fdes)) <0) 28618334Speter perr("error in dup() of job file"); 28718334Speter 28818334Speter if(fchown(fd2, real_uid, real_gid) != 0) 28918334Speter perr("cannot give away file"); 29018334Speter 29118334Speter PRIV_END 29218334Speter 29318334Speter /* We no longer need suid root; now we just need to be able to write 29418334Speter * to the directory, if necessary. 29518334Speter */ 29618334Speter 29718334Speter REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 29818334Speter 29918334Speter /* We've successfully created the file; let's set the flag so it 30018334Speter * gets removed in case of an interrupt or error. 30118334Speter */ 30218334Speter fcreated = 1; 30318334Speter 30418334Speter /* Now we can release the lock, so other people can access it 30518334Speter */ 30618334Speter lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 30718334Speter lock.l_len = 0; 30818334Speter fcntl(lockdes, F_SETLKW, &lock); 30918334Speter close(lockdes); 31018334Speter 31118334Speter if((fp = fdopen(fdes, "w")) == NULL) 31218334Speter panic("cannot reopen atjob file"); 31318334Speter 31418334Speter /* Get the userid to mail to, first by trying getlogin(), 31518334Speter * then from LOGNAME, finally from getpwuid(). 31618334Speter */ 31718334Speter mailname = getlogin(); 31818334Speter if (mailname == NULL) 31918334Speter mailname = getenv("LOGNAME"); 32018334Speter 32118334Speter if ((mailname == NULL) || (mailname[0] == '\0') 32218334Speter || (strlen(mailname) >= MAXLOGNAME) || (getpwnam(mailname)==NULL)) 32318334Speter { 32418334Speter pass_entry = getpwuid(real_uid); 32518334Speter if (pass_entry != NULL) 32618334Speter mailname = pass_entry->pw_name; 32718334Speter } 32818334Speter 32918334Speter if (atinput != (char *) NULL) 33018334Speter { 33118334Speter fpin = freopen(atinput, "r", stdin); 33218334Speter if (fpin == NULL) 33318334Speter perr("cannot open input file"); 33418334Speter } 33518334Speter fprintf(fp, "#!/bin/sh\n# atrun uid=%ld gid=%ld\n# mail %*s %d\n", 33618334Speter (long) real_uid, (long) real_gid, MAXLOGNAME - 1, mailname, 33718334Speter send_mail); 33818334Speter 33918334Speter /* Write out the umask at the time of invocation 34018334Speter */ 34118334Speter fprintf(fp, "umask %lo\n", (unsigned long) cmask); 34218334Speter 34318334Speter /* Write out the environment. Anything that may look like a 34418334Speter * special character to the shell is quoted, except for \n, which is 34518334Speter * done with a pair of "'s. Don't export the no_export list (such 34618334Speter * as TERM or DISPLAY) because we don't want these. 34718334Speter */ 34818334Speter for (atenv= environ; *atenv != NULL; atenv++) 34918334Speter { 35018334Speter int export = 1; 35118334Speter char *eqp; 35218334Speter 35318334Speter eqp = strchr(*atenv, '='); 35418334Speter if (ap == NULL) 35518334Speter eqp = *atenv; 35618334Speter else 35718334Speter { 35818334Speter size_t i; 35918334Speter for (i=0; i<sizeof(no_export)/sizeof(no_export[0]); i++) 36018334Speter { 36118334Speter export = export 36218334Speter && (strncmp(*atenv, no_export[i], 36318334Speter (size_t) (eqp-*atenv)) != 0); 36418334Speter } 36518334Speter eqp++; 36618334Speter } 36718334Speter 36818334Speter if (export) 36918334Speter { 37018334Speter (void)fputs("export ", fp); 37118334Speter fwrite(*atenv, sizeof(char), eqp-*atenv, fp); 37218334Speter for(ap = eqp;*ap != '\0'; ap++) 37318334Speter { 37418334Speter if (*ap == '\n') 37518334Speter fprintf(fp, "\"\n\""); 37618334Speter else 37718334Speter { 37818334Speter if (!isalnum(*ap)) { 37918334Speter switch (*ap) { 38018334Speter case '%': case '/': case '{': case '[': 38118334Speter case ']': case '=': case '}': case '@': 38218334Speter case '+': case '#': case ',': case '.': 38318334Speter case ':': case '-': case '_': 38418334Speter break; 38518334Speter default: 38618334Speter fputc('\\', fp); 38718334Speter break; 38818334Speter } 38918334Speter } 39018334Speter fputc(*ap, fp); 39118334Speter } 39218334Speter } 39318334Speter fputc('\n', fp); 39418334Speter 39518334Speter } 39618334Speter } 39718334Speter /* Cd to the directory at the time and write out all the 39818334Speter * commands the user supplies from stdin. 39918334Speter */ 40018334Speter fprintf(fp, "cd "); 40118334Speter for (ap = cwdname(); *ap != '\0'; ap++) 40218334Speter { 40318334Speter if (*ap == '\n') 40418334Speter fprintf(fp, "\"\n\""); 40518334Speter else 40618334Speter { 40718334Speter if (*ap != '/' && !isalnum(*ap)) 40818334Speter fputc('\\', fp); 40918334Speter 41018334Speter fputc(*ap, fp); 41118334Speter } 41218334Speter } 41318334Speter /* Test cd's exit status: die if the original directory has been 41418334Speter * removed, become unreadable or whatever 41518334Speter */ 41618334Speter fprintf(fp, " || {\n\t echo 'Execution directory " 41718334Speter "inaccessible' >&2\n\t exit 1\n}\n"); 41818334Speter 41918334Speter while((ch = getchar()) != EOF) 42018334Speter fputc(ch, fp); 42118334Speter 42218334Speter fprintf(fp, "\n"); 42318334Speter if (ferror(fp)) 42418334Speter panic("output error"); 42518334Speter 42618334Speter if (ferror(stdin)) 42718334Speter panic("input error"); 42818334Speter 42918334Speter fclose(fp); 43018334Speter 43118334Speter /* Set the x bit so that we're ready to start executing 43218334Speter */ 43318334Speter 43418334Speter if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0) 43518334Speter perr("cannot give away file"); 43618334Speter 43718334Speter close(fd2); 43818334Speter fprintf(stderr, "Job %ld will be executed using /bin/sh\n", jobno); 43918334Speter} 44018334Speter 44118334Speterstatic int 44218334Speterin_job_list(long job, long *joblist, int len) 44318334Speter{ 44418334Speter int i; 44518334Speter 44618334Speter for (i = 0; i < len; i++) 44718334Speter if (job == joblist[i]) 44818334Speter return 1; 44918334Speter 45018334Speter return 0; 45118334Speter} 45218334Speter 45318334Speterstatic void 45418334Speterlist_jobs(long *joblist, int len) 45518334Speter{ 45618334Speter /* List all a user's jobs in the queue, by looping through ATJOB_DIR, 45718334Speter * or everybody's if we are root 45818334Speter */ 45918334Speter struct passwd *pw; 46018334Speter DIR *spool; 46118334Speter struct dirent *dirent; 46218334Speter struct stat buf; 46318334Speter struct tm runtime; 46418334Speter unsigned long ctm; 46518334Speter char queue; 46618334Speter long jobno; 46718334Speter time_t runtimer; 46818334Speter char timestr[TIMESIZE]; 46918334Speter int first=1; 47018334Speter 47118334Speter#ifdef __FreeBSD__ 47218334Speter (void) setlocale(LC_TIME, ""); 47318334Speter#endif 47418334Speter 47518334Speter PRIV_START 47618334Speter 47718334Speter if (chdir(ATJOB_DIR) != 0) 47818334Speter perr("cannot change to " ATJOB_DIR); 47918334Speter 48018334Speter if ((spool = opendir(".")) == NULL) 48118334Speter perr("cannot open " ATJOB_DIR); 48218334Speter 48318334Speter /* Loop over every file in the directory 48418334Speter */ 48518334Speter while((dirent = readdir(spool)) != NULL) { 48618334Speter if (stat(dirent->d_name, &buf) != 0) 48718334Speter perr("cannot stat in " ATJOB_DIR); 48818334Speter 48918334Speter /* See it's a regular file and has its x bit turned on and 49018334Speter * is the user's 49118334Speter */ 49218334Speter if (!S_ISREG(buf.st_mode) 49318334Speter || ((buf.st_uid != real_uid) && ! (real_uid == 0)) 49418334Speter || !(S_IXUSR & buf.st_mode || atverify)) 49518334Speter continue; 49618334Speter 49718334Speter if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3) 49818334Speter continue; 49918334Speter 50018334Speter /* If jobs are given, only list those jobs */ 50118334Speter if (joblist && !in_job_list(jobno, joblist, len)) 50218334Speter continue; 50318334Speter 50418334Speter if (atqueue && (queue != atqueue)) 50518334Speter continue; 50618334Speter 50718334Speter runtimer = 60*(time_t) ctm; 50818334Speter runtime = *localtime(&runtimer); 50918334Speter strftime(timestr, TIMESIZE, "%+", &runtime); 51018334Speter if (first) { 51118334Speter printf("Date\t\t\t\tOwner\t\tQueue\tJob#\n"); 51218334Speter first=0; 51318334Speter } 51418334Speter pw = getpwuid(buf.st_uid); 51518334Speter 51618334Speter printf("%s\t%-16s%c%s\t%ld\n", 51718334Speter timestr, 51818334Speter pw ? pw->pw_name : "???", 51918334Speter queue, 52018334Speter (S_IXUSR & buf.st_mode) ? "":"(done)", 52118334Speter jobno); 52218334Speter } 52318334Speter PRIV_END 52418334Speter closedir(spool); 52518334Speter} 52618334Speter 52718334Speterstatic void 52818334Speterprocess_jobs(int argc, char **argv, int what) 52918334Speter{ 53018334Speter /* Delete every argument (job - ID) given 53118334Speter */ 53218334Speter int i; 53318334Speter int rc; 53418334Speter int nofJobs; 53518334Speter int nofDone; 53618334Speter int statErrno; 53718334Speter struct stat buf; 53818334Speter DIR *spool; 53918334Speter struct dirent *dirent; 54018334Speter unsigned long ctm; 54118334Speter char queue; 54218334Speter long jobno; 54318334Speter 54418334Speter nofJobs = argc - optind; 54518334Speter nofDone = 0; 54618334Speter 54718334Speter PRIV_START 54818334Speter 54918334Speter if (chdir(ATJOB_DIR) != 0) 55018334Speter perr("cannot change to " ATJOB_DIR); 55118334Speter 55218334Speter if ((spool = opendir(".")) == NULL) 55318334Speter perr("cannot open " ATJOB_DIR); 55418334Speter 55518334Speter PRIV_END 55618334Speter 55718334Speter /* Loop over every file in the directory 55818334Speter */ 55918334Speter while((dirent = readdir(spool)) != NULL) { 56018334Speter 56118334Speter PRIV_START 56218334Speter rc = stat(dirent->d_name, &buf); 56318334Speter statErrno = errno; 56418334Speter PRIV_END 56518334Speter /* There's a race condition between readdir above and stat here: 56618334Speter * another atrm process could have removed the file from the spool 56718334Speter * directory under our nose. If this happens, stat will set errno to 56818334Speter * ENOENT, which we shouldn't treat as fatal. 56918334Speter */ 57018334Speter if (rc != 0) { 57118334Speter if (statErrno == ENOENT) 57218334Speter continue; 57318334Speter else 57418334Speter perr("cannot stat in " ATJOB_DIR); 57518334Speter } 57618334Speter 57718334Speter if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3) 57818334Speter continue; 57918334Speter 58018334Speter for (i=optind; i < argc; i++) { 58118334Speter if (atoi(argv[i]) == jobno) { 58218334Speter if ((buf.st_uid != real_uid) && !(real_uid == 0)) 58318334Speter errx(EXIT_FAILURE, "%s: not owner", argv[i]); 58418334Speter switch (what) { 58518334Speter case ATRM: 58618334Speter 58718334Speter PRIV_START 58818334Speter 58918334Speter if (unlink(dirent->d_name) != 0) 59018334Speter perr(dirent->d_name); 59118334Speter 59218334Speter PRIV_END 59318334Speter 59418334Speter break; 59518334Speter 59618334Speter case CAT: 59718334Speter { 59818334Speter FILE *fp; 59918334Speter int ch; 60018334Speter 60118334Speter PRIV_START 60218334Speter 60318334Speter fp = fopen(dirent->d_name,"r"); 60418334Speter 60518334Speter PRIV_END 60618334Speter 60718334Speter if (!fp) { 60818334Speter perr("cannot open file"); 60918334Speter } 61018334Speter while((ch = getc(fp)) != EOF) { 61118334Speter putchar(ch); 61218334Speter } 61318334Speter fclose(fp); 61418334Speter } 61518334Speter break; 61618334Speter 61718334Speter default: 61818334Speter errx(EXIT_FAILURE, "internal error, process_jobs = %d", 61918334Speter what); 62018334Speter } 62118334Speter 62218334Speter /* All arguments have been processed 62318334Speter */ 62418334Speter if (++nofDone == nofJobs) 62518334Speter goto end; 62618334Speter } 62718334Speter } 62818334Speter } 62918334Speterend: 63018334Speter closedir(spool); 63118334Speter} /* delete_jobs */ 63218334Speter 63318334Speter#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; 63418334Speter 63518334Speterstatic time_t 63618334Speterttime(const char *arg) 63718334Speter{ 63818334Speter /* 63918334Speter * This is pretty much a copy of stime_arg1() from touch.c. I changed 64018334Speter * the return value and the argument list because it's more convenient 64118334Speter * (IMO) to do everything in one place. - Joe Halpin 64218334Speter */ 64318334Speter struct timeval tv[2]; 64418334Speter time_t now; 64518334Speter struct tm *t; 64618334Speter int yearset; 64718334Speter char *p; 64818334Speter 64918334Speter if (gettimeofday(&tv[0], NULL)) 65018334Speter panic("Cannot get current time"); 65118334Speter 65218334Speter /* Start with the current time. */ 65318334Speter now = tv[0].tv_sec; 65418334Speter if ((t = localtime(&now)) == NULL) 65518334Speter panic("localtime"); 65618334Speter /* [[CC]YY]MMDDhhmm[.SS] */ 65718334Speter if ((p = strchr(arg, '.')) == NULL) 65818334Speter t->tm_sec = 0; /* Seconds defaults to 0. */ 65918334Speter else { 66018334Speter if (strlen(p + 1) != 2) 66118334Speter goto terr; 66218334Speter *p++ = '\0'; 66318334Speter t->tm_sec = ATOI2(p); 66418334Speter } 66518334Speter 66618334Speter yearset = 0; 66718334Speter switch(strlen(arg)) { 66818334Speter case 12: /* CCYYMMDDhhmm */ 66918334Speter t->tm_year = ATOI2(arg); 67018334Speter t->tm_year *= 100; 67118334Speter yearset = 1; 67218334Speter /* FALLTHROUGH */ 67318334Speter case 10: /* YYMMDDhhmm */ 67418334Speter if (yearset) { 67518334Speter yearset = ATOI2(arg); 67618334Speter t->tm_year += yearset; 67718334Speter } else { 67818334Speter yearset = ATOI2(arg); 67918334Speter t->tm_year = yearset + 2000; 68018334Speter } 68118334Speter t->tm_year -= 1900; /* Convert to UNIX time. */ 68218334Speter /* FALLTHROUGH */ 68318334Speter case 8: /* MMDDhhmm */ 68418334Speter t->tm_mon = ATOI2(arg); 68518334Speter --t->tm_mon; /* Convert from 01-12 to 00-11 */ 68618334Speter t->tm_mday = ATOI2(arg); 68718334Speter t->tm_hour = ATOI2(arg); 68818334Speter t->tm_min = ATOI2(arg); 68918334Speter break; 69018334Speter default: 69118334Speter goto terr; 69218334Speter } 69318334Speter 69418334Speter t->tm_isdst = -1; /* Figure out DST. */ 69518334Speter tv[0].tv_sec = tv[1].tv_sec = mktime(t); 69618334Speter if (tv[0].tv_sec != -1) 69718334Speter return tv[0].tv_sec; 69818334Speter else 69918334Speterterr: 70018334Speter panic( 70118334Speter "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 70218334Speter} 70318334Speter 70418334Speterstatic long * 70518334Speterget_job_list(int argc, char *argv[], int *joblen) 70618334Speter{ 70718334Speter int i, len; 70818334Speter long *joblist; 70918334Speter char *ep; 71018334Speter 71118334Speter joblist = NULL; 71218334Speter len = argc; 71318334Speter if (len > 0) { 71418334Speter if ((joblist = malloc(len * sizeof(*joblist))) == NULL) 71518334Speter panic("out of memory"); 71618334Speter 71718334Speter for (i = 0; i < argc; i++) { 71818334Speter errno = 0; 71918334Speter if ((joblist[i] = strtol(argv[i], &ep, 10)) < 0 || 72018334Speter ep == argv[i] || *ep != '\0' || errno) 72118334Speter panic("invalid job number"); 72218334Speter } 72318334Speter } 72418334Speter 72518334Speter *joblen = len; 72618334Speter return joblist; 72718334Speter} 72818334Speter 72918334Speterint 73018334Spetermain(int argc, char **argv) 73118334Speter{ 73218334Speter int c; 73318334Speter char queue = DEFAULT_AT_QUEUE; 73418334Speter char queue_set = 0; 73518334Speter char *pgm; 73618334Speter 73718334Speter int program = AT; /* our default program */ 73818334Speter const char *options = "q:f:t:rmvldbc"; /* default options for at */ 73918334Speter time_t timer; 74018334Speter long *joblist; 74118334Speter int joblen; 74218334Speter 74318334Speter joblist = NULL; 74418334Speter joblen = 0; 74518334Speter timer = -1; 74618334Speter RELINQUISH_PRIVS 74718334Speter 74818334Speter /* Eat any leading paths 74918334Speter */ 75018334Speter if ((pgm = strrchr(argv[0], '/')) == NULL) 75118334Speter pgm = argv[0]; 75218334Speter else 75318334Speter pgm++; 75418334Speter 75518334Speter namep = pgm; 75618334Speter 75718334Speter /* find out what this program is supposed to do 75818334Speter */ 75918334Speter if (strcmp(pgm, "atq") == 0) { 76018334Speter program = ATQ; 76118334Speter options = "q:v"; 76218334Speter } 76318334Speter else if (strcmp(pgm, "atrm") == 0) { 76418334Speter program = ATRM; 76518334Speter options = ""; 76618334Speter } 76718334Speter else if (strcmp(pgm, "batch") == 0) { 76818334Speter program = BATCH; 76918334Speter options = "f:q:mv"; 77018334Speter } 77118334Speter 77218334Speter /* process whatever options we can process 77318334Speter */ 77418334Speter opterr=1; 77518334Speter while ((c=getopt(argc, argv, options)) != -1) 77618334Speter switch (c) { 77718334Speter case 'v': /* verify time settings */ 77818334Speter atverify = 1; 77918334Speter break; 78018334Speter 78118334Speter case 'm': /* send mail when job is complete */ 78218334Speter send_mail = 1; 78318334Speter break; 78418334Speter 78518334Speter case 'f': 78618334Speter atinput = optarg; 78718334Speter break; 78818334Speter 78918334Speter case 'q': /* specify queue */ 79018334Speter if (strlen(optarg) > 1) 79118334Speter usage(); 79218334Speter 79318334Speter atqueue = queue = *optarg; 79418334Speter if (!(islower(queue)||isupper(queue))) 79518334Speter usage(); 79618334Speter 79718334Speter queue_set = 1; 79818334Speter break; 79918334Speter 80018334Speter case 'd': 80118334Speter warnx("-d is deprecated; use -r instead"); 80218334Speter /* fall through to 'r' */ 80318334Speter 80418334Speter case 'r': 80518334Speter if (program != AT) 80618334Speter usage(); 80718334Speter 80818334Speter program = ATRM; 80918334Speter options = ""; 81018334Speter break; 81118334Speter 81218334Speter case 't': 81318334Speter if (program != AT) 81418334Speter usage(); 81518334Speter timer = ttime(optarg); 81618334Speter break; 81718334Speter 81818334Speter case 'l': 81918334Speter if (program != AT) 82018334Speter usage(); 82118334Speter 82218334Speter program = ATQ; 82318334Speter options = "q:"; 82418334Speter break; 82518334Speter 82618334Speter case 'b': 82718334Speter if (program != AT) 82818334Speter usage(); 82918334Speter 83018334Speter program = BATCH; 83118334Speter options = "f:q:mv"; 83218334Speter break; 83318334Speter 83418334Speter case 'c': 83518334Speter program = CAT; 83618334Speter options = ""; 83718334Speter break; 83818334Speter 83918334Speter default: 84018334Speter usage(); 84118334Speter break; 84218334Speter } 84318334Speter /* end of options eating 84418334Speter */ 84518334Speter 84618334Speter /* select our program 84718334Speter */ 84818334Speter if(!check_permission()) 84918334Speter errx(EXIT_FAILURE, "you do not have permission to use this program"); 85018334Speter switch (program) { 85118334Speter case ATQ: 85218334Speter 85318334Speter REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 85418334Speter 85518334Speter if (queue_set == 0) 85618334Speter joblist = get_job_list(argc - optind, argv + optind, &joblen); 85718334Speter list_jobs(joblist, joblen); 85818334Speter break; 85918334Speter 86018334Speter case ATRM: 86118334Speter 86218334Speter REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 86318334Speter 86418334Speter process_jobs(argc, argv, ATRM); 86518334Speter break; 86618334Speter 86718334Speter case CAT: 86818334Speter 86918334Speter process_jobs(argc, argv, CAT); 87018334Speter break; 87118334Speter 87218334Speter case AT: 87318334Speter /* 87418334Speter * If timer is > -1, then the user gave the time with -t. In that 87518334Speter * case, it's already been set. If not, set it now. 87618334Speter */ 87718334Speter if (timer == -1) 87818334Speter timer = parsetime(argc, argv); 87918334Speter 88018334Speter if (atverify) 88118334Speter { 88218334Speter struct tm *tm = localtime(&timer); 88318334Speter fprintf(stderr, "%s\n", asctime(tm)); 88418334Speter } 88518334Speter writefile(timer, queue); 88618334Speter break; 88718334Speter 88818334Speter case BATCH: 88918334Speter if (queue_set) 89018334Speter queue = toupper(queue); 89118334Speter else 89218334Speter queue = DEFAULT_BATCH_QUEUE; 89318334Speter 89418334Speter if (argc > optind) 89518334Speter timer = parsetime(argc, argv); 89618334Speter else 89718334Speter timer = time(NULL); 89818334Speter 89918334Speter if (atverify) 90018334Speter { 90118334Speter struct tm *tm = localtime(&timer); 90218334Speter fprintf(stderr, "%s\n", asctime(tm)); 90318334Speter } 90418334Speter 90518334Speter writefile(timer, queue); 90618334Speter break; 90718334Speter 90818334Speter default: 90918334Speter panic("internal error"); 91018334Speter break; 91118334Speter } 91218334Speter exit(EXIT_SUCCESS); 91318334Speter} 91418334Speter