Home | History | Annotate | Line # | Download | only in at
at.c revision 1.1
      1  1.1  cgd /*
      2  1.1  cgd  * at.c : Put file into atrun queue
      3  1.1  cgd  * Copyright (C) 1993  Thomas Koenig
      4  1.1  cgd  *
      5  1.1  cgd  * Atrun & Atq modifications
      6  1.1  cgd  * Copyright (C) 1993  David Parsons
      7  1.1  cgd  * All rights reserved.
      8  1.1  cgd  *
      9  1.1  cgd  * Redistribution and use in source and binary forms, with or without
     10  1.1  cgd  * modification, are permitted provided that the following conditions
     11  1.1  cgd  * are met:
     12  1.1  cgd  * 1. Redistributions of source code must retain the above copyright
     13  1.1  cgd  *    notice, this list of conditions and the following disclaimer.
     14  1.1  cgd  * 2. The name of the author(s) may not be used to endorse or promote
     15  1.1  cgd  *    products derived from this software without specific prior written
     16  1.1  cgd  *    permission.
     17  1.1  cgd  *
     18  1.1  cgd  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
     19  1.1  cgd  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     20  1.1  cgd  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     21  1.1  cgd  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     22  1.1  cgd  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     23  1.1  cgd  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24  1.1  cgd  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25  1.1  cgd  * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  1.1  cgd  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  1.1  cgd  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  1.1  cgd  */
     29  1.1  cgd 
     30  1.1  cgd #define _USE_BSD 1
     31  1.1  cgd 
     32  1.1  cgd /* System Headers */
     33  1.1  cgd #include <sys/types.h>
     34  1.1  cgd #include <sys/stat.h>
     35  1.1  cgd #include <sys/wait.h>
     36  1.1  cgd #include <ctype.h>
     37  1.1  cgd #include <dirent.h>
     38  1.1  cgd #include <errno.h>
     39  1.1  cgd #include <fcntl.h>
     40  1.1  cgd #include <pwd.h>
     41  1.1  cgd #include <signal.h>
     42  1.1  cgd #include <stddef.h>
     43  1.1  cgd #include <stdio.h>
     44  1.1  cgd #include <stdlib.h>
     45  1.1  cgd #include <string.h>
     46  1.1  cgd #include <time.h>
     47  1.1  cgd #include <unistd.h>
     48  1.1  cgd 
     49  1.1  cgd /* Local headers */
     50  1.1  cgd #include "at.h"
     51  1.1  cgd #include "panic.h"
     52  1.1  cgd #include "parsetime.h"
     53  1.1  cgd #include "pathnames.h"
     54  1.1  cgd #define MAIN
     55  1.1  cgd #include "privs.h"
     56  1.1  cgd 
     57  1.1  cgd /* Macros */
     58  1.1  cgd #define ALARMC 10		/* Number of seconds to wait for timeout */
     59  1.1  cgd 
     60  1.1  cgd #define SIZE 255
     61  1.1  cgd #define TIMESIZE 50
     62  1.1  cgd 
     63  1.1  cgd /* File scope variables */
     64  1.1  cgd static char rcsid[] = "$Id: at.c,v 1.1 1993/12/05 11:36:38 cgd Exp $";
     65  1.1  cgd char *no_export[] =
     66  1.1  cgd {
     67  1.1  cgd 	"TERM", "TERMCAP", "DISPLAY", "_"
     68  1.1  cgd };
     69  1.1  cgd static send_mail = 0;
     70  1.1  cgd 
     71  1.1  cgd /* External variables */
     72  1.1  cgd extern char **environ;
     73  1.1  cgd int fcreated;
     74  1.1  cgd char *namep;
     75  1.1  cgd char atfile[FILENAME_MAX];
     76  1.1  cgd 
     77  1.1  cgd char *atinput = (char *) 0;	/* where to get input from */
     78  1.1  cgd char atqueue = 0;		/* which queue to examine for jobs (atq) */
     79  1.1  cgd char atverify = 0;		/* verify time instead of queuing job */
     80  1.1  cgd 
     81  1.1  cgd /* Function declarations */
     82  1.1  cgd static void sigc	__P((int signo));
     83  1.1  cgd static void alarmc	__P((int signo));
     84  1.1  cgd static char *cwdname	__P((void));
     85  1.1  cgd static void writefile	__P((time_t runtimer, char queue));
     86  1.1  cgd static void list_jobs	__P((void));
     87  1.1  cgd 
     88  1.1  cgd /* Signal catching functions */
     89  1.1  cgd 
     90  1.1  cgd static void
     91  1.1  cgd sigc(signo)
     92  1.1  cgd 	int signo;
     93  1.1  cgd {
     94  1.1  cgd /* If the user presses ^C, remove the spool file and exit
     95  1.1  cgd  */
     96  1.1  cgd 	if (fcreated) {
     97  1.1  cgd 		PRIV_START
     98  1.1  cgd 		unlink(atfile);
     99  1.1  cgd 		PRIV_END
    100  1.1  cgd 	}
    101  1.1  cgd 
    102  1.1  cgd 	exit(EXIT_FAILURE);
    103  1.1  cgd }
    104  1.1  cgd 
    105  1.1  cgd static void
    106  1.1  cgd alarmc(signo)
    107  1.1  cgd 	int signo;
    108  1.1  cgd {
    109  1.1  cgd /* Time out after some seconds
    110  1.1  cgd  */
    111  1.1  cgd 	panic("File locking timed out");
    112  1.1  cgd }
    113  1.1  cgd 
    114  1.1  cgd /* Local functions */
    115  1.1  cgd 
    116  1.1  cgd static char *
    117  1.1  cgd cwdname()
    118  1.1  cgd {
    119  1.1  cgd /* Read in the current directory; the name will be overwritten on
    120  1.1  cgd  * subsequent calls.
    121  1.1  cgd  */
    122  1.1  cgd 	static char *ptr = NULL;
    123  1.1  cgd 	static size_t size = SIZE;
    124  1.1  cgd 
    125  1.1  cgd 	if (ptr == NULL)
    126  1.1  cgd 		ptr = (char *) malloc(size);
    127  1.1  cgd 
    128  1.1  cgd 	while (1) {
    129  1.1  cgd 		if (ptr == NULL)
    130  1.1  cgd 			panic("Out of memory");
    131  1.1  cgd 
    132  1.1  cgd 		if (getcwd(ptr, size - 1) != NULL)
    133  1.1  cgd 			return ptr;
    134  1.1  cgd 
    135  1.1  cgd 		if (errno != ERANGE)
    136  1.1  cgd 			perr("Cannot get directory");
    137  1.1  cgd 
    138  1.1  cgd 		free(ptr);
    139  1.1  cgd 		size += SIZE;
    140  1.1  cgd 		ptr = (char *) malloc(size);
    141  1.1  cgd 	}
    142  1.1  cgd }
    143  1.1  cgd 
    144  1.1  cgd static void
    145  1.1  cgd writefile(runtimer, queue)
    146  1.1  cgd 	time_t runtimer;
    147  1.1  cgd 	char queue;
    148  1.1  cgd {
    149  1.1  cgd 	/*
    150  1.1  cgd 	 * This does most of the work if at or batch are invoked for
    151  1.1  cgd 	 * writing a job.
    152  1.1  cgd 	 */
    153  1.1  cgd 	int i;
    154  1.1  cgd 	char *ap, *ppos, *mailname;
    155  1.1  cgd 	struct passwd *pass_entry;
    156  1.1  cgd 	struct stat statbuf;
    157  1.1  cgd 	int fdes, lockdes, fd2;
    158  1.1  cgd 	FILE *fp, *fpin;
    159  1.1  cgd 	struct sigaction act;
    160  1.1  cgd 	char **atenv;
    161  1.1  cgd 	int ch;
    162  1.1  cgd 	mode_t cmask;
    163  1.1  cgd 	struct flock lock;
    164  1.1  cgd 
    165  1.1  cgd 	/*
    166  1.1  cgd 	 * Install the signal handler for SIGINT; terminate after removing the
    167  1.1  cgd 	 * spool file if necessary
    168  1.1  cgd 	 */
    169  1.1  cgd 	act.sa_handler = sigc;
    170  1.1  cgd 	sigemptyset(&(act.sa_mask));
    171  1.1  cgd 	act.sa_flags = 0;
    172  1.1  cgd 
    173  1.1  cgd 	sigaction(SIGINT, &act, NULL);
    174  1.1  cgd 
    175  1.1  cgd 	strcpy(atfile, _PATH_ATJOBS);
    176  1.1  cgd 	ppos = atfile + strlen(_PATH_ATJOBS);
    177  1.1  cgd 
    178  1.1  cgd 	/*
    179  1.1  cgd 	 * Loop over all possible file names for running something at this
    180  1.1  cgd 	 *  particular time, see if a file is there; the first empty slot at
    181  1.1  cgd 	 *  any particular time is used.  Lock the file _PATH_LOCKFILE first
    182  1.1  cgd 	 *  to make sure we're alone when doing this.
    183  1.1  cgd 	 */
    184  1.1  cgd 
    185  1.1  cgd 	PRIV_START
    186  1.1  cgd 
    187  1.1  cgd 	    if ((lockdes = open(_PATH_LOCKFILE, O_WRONLY | O_CREAT)) < 0)
    188  1.1  cgd 		perr2("Cannot open lockfile ", _PATH_LOCKFILE);
    189  1.1  cgd 
    190  1.1  cgd 	act.sa_handler = alarmc;
    191  1.1  cgd 	sigemptyset(&(act.sa_mask));
    192  1.1  cgd 	act.sa_flags = 0;
    193  1.1  cgd 
    194  1.1  cgd 	/*
    195  1.1  cgd 	 * Set an alarm so a timeout occurs after ALARMC seconds, in case
    196  1.1  cgd 	 * something is seriously broken.
    197  1.1  cgd 	 */
    198  1.1  cgd 	sigaction(SIGALRM, &act, NULL);
    199  1.1  cgd 	alarm(ALARMC);
    200  1.1  cgd 	fcntl(lockdes, F_SETLKW, &lock);
    201  1.1  cgd 	alarm(0);
    202  1.1  cgd 
    203  1.1  cgd 	for (i = 0; i < AT_MAXJOBS; i++) {
    204  1.1  cgd 		sprintf(ppos, "%c%8lx.%3x", queue,
    205  1.1  cgd 		    (unsigned long) (runtimer / 60), i);
    206  1.1  cgd 		for (ap = ppos; *ap != '\0'; ap++)
    207  1.1  cgd 			if (*ap == ' ')
    208  1.1  cgd 				*ap = '0';
    209  1.1  cgd 
    210  1.1  cgd 		if (stat(atfile, &statbuf) != 0) {
    211  1.1  cgd 			if (errno == ENOENT)
    212  1.1  cgd 				break;
    213  1.1  cgd 			else
    214  1.1  cgd 				perr2("Cannot access ", _PATH_ATJOBS);
    215  1.1  cgd 		}
    216  1.1  cgd 	}			/* for */
    217  1.1  cgd 
    218  1.1  cgd 	if (i >= AT_MAXJOBS)
    219  1.1  cgd 		panic("Too many jobs already");
    220  1.1  cgd 
    221  1.1  cgd 	/*
    222  1.1  cgd 	 * Create the file. The x bit is only going to be set after it has
    223  1.1  cgd 	 * been completely written out, to make sure it is not executed in
    224  1.1  cgd 	 * the meantime.  To make sure they do not get deleted, turn off
    225  1.1  cgd 	 * their r bit.  Yes, this is a kluge.
    226  1.1  cgd 	 */
    227  1.1  cgd 	cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR);
    228  1.1  cgd 	if ((fdes = creat(atfile, O_WRONLY)) == -1)
    229  1.1  cgd 		perr("Cannot create atjob file");
    230  1.1  cgd 
    231  1.1  cgd 	if ((fd2 = dup(fdes)) < 0)
    232  1.1  cgd 		perr("Error in dup() of job file");
    233  1.1  cgd 
    234  1.1  cgd 	if (fchown(fd2, real_uid, -1) != 0)
    235  1.1  cgd 		perr("Cannot give away file");
    236  1.1  cgd 
    237  1.1  cgd 	PRIV_END
    238  1.1  cgd 
    239  1.1  cgd 	/*
    240  1.1  cgd 	 * We no longer need suid root; now we just need to be able to
    241  1.1  cgd 	 * write to the directory, if necessary.
    242  1.1  cgd 	 */
    243  1.1  cgd 
    244  1.1  cgd 	    REDUCE_PRIV(0);
    245  1.1  cgd 
    246  1.1  cgd 	/*
    247  1.1  cgd 	 * We've successfully created the file; let's set the flag so it
    248  1.1  cgd 	 * gets removed in case of an interrupt or error.
    249  1.1  cgd 	 */
    250  1.1  cgd 	fcreated = 1;
    251  1.1  cgd 
    252  1.1  cgd 	/* Now we can release the lock, so other people can access it */
    253  1.1  cgd 	lock.l_type = F_UNLCK;
    254  1.1  cgd 	lock.l_whence = SEEK_SET;
    255  1.1  cgd 	lock.l_start = 0;
    256  1.1  cgd 	lock.l_len = 0;
    257  1.1  cgd 	fcntl(lockdes, F_SETLKW, &lock);
    258  1.1  cgd 	close(lockdes);
    259  1.1  cgd 
    260  1.1  cgd 	if ((fp = fdopen(fdes, "w")) == NULL)
    261  1.1  cgd 		panic("Cannot reopen atjob file");
    262  1.1  cgd 
    263  1.1  cgd 	/*
    264  1.1  cgd 	 * Get the userid to mail to, first by trying getlogin(), which
    265  1.1  cgd 	 * reads /etc/utmp, then from LOGNAME, finally from getpwuid().
    266  1.1  cgd 	 */
    267  1.1  cgd 	mailname = getlogin();
    268  1.1  cgd 	if (mailname == NULL)
    269  1.1  cgd 		mailname = getenv("LOGNAME");
    270  1.1  cgd 
    271  1.1  cgd 	if ((mailname == NULL) || (mailname[0] == '\0')
    272  1.1  cgd 	    || (strlen(mailname) > 8)) {
    273  1.1  cgd 		pass_entry = getpwuid(getuid());
    274  1.1  cgd 		if (pass_entry != NULL)
    275  1.1  cgd 			mailname = pass_entry->pw_name;
    276  1.1  cgd 	}
    277  1.1  cgd 
    278  1.1  cgd 	if (atinput != (char *) NULL) {
    279  1.1  cgd 		fpin = freopen(atinput, "r", stdin);
    280  1.1  cgd 		if (fpin == NULL)
    281  1.1  cgd 			perr("Cannot open input file");
    282  1.1  cgd 	}
    283  1.1  cgd 	fprintf(fp, "#! /bin/sh\n# mail %8s %d\n", mailname, send_mail);
    284  1.1  cgd 
    285  1.1  cgd 	/* Write out the umask at the time of invocation */
    286  1.1  cgd 	fprintf(fp, "umask %lo\n", (unsigned long) cmask);
    287  1.1  cgd 
    288  1.1  cgd 	/*
    289  1.1  cgd 	 * Write out the environment. Anything that may look like a special
    290  1.1  cgd 	 * character to the shell is quoted, except for \n, which is done
    291  1.1  cgd 	 * with a pair of "'s.  Dont't export the no_export list (such as
    292  1.1  cgd 	 * TERM or DISPLAY) because we don't want these.
    293  1.1  cgd 	 */
    294  1.1  cgd 	for (atenv = environ; *atenv != NULL; atenv++) {
    295  1.1  cgd 		int export = 1;
    296  1.1  cgd 		char *eqp;
    297  1.1  cgd 
    298  1.1  cgd 		eqp = strchr(*atenv, '=');
    299  1.1  cgd 		if (ap == NULL)
    300  1.1  cgd 			eqp = *atenv;
    301  1.1  cgd 		else {
    302  1.1  cgd 			int i;
    303  1.1  cgd 
    304  1.1  cgd 			for (i = 0;i < sizeof(no_export) /
    305  1.1  cgd 			    sizeof(no_export[0]); i++) {
    306  1.1  cgd 				export = export
    307  1.1  cgd 				    && (strncmp(*atenv, no_export[i],
    308  1.1  cgd 					(size_t) (eqp - *atenv)) != 0);
    309  1.1  cgd 			}
    310  1.1  cgd 			eqp++;
    311  1.1  cgd 		}
    312  1.1  cgd 
    313  1.1  cgd 		if (export) {
    314  1.1  cgd 			fwrite(*atenv, sizeof(char), eqp - *atenv, fp);
    315  1.1  cgd 			for (ap = eqp; *ap != '\0'; ap++) {
    316  1.1  cgd 				if (*ap == '\n')
    317  1.1  cgd 					fprintf(fp, "\"\n\"");
    318  1.1  cgd 				else {
    319  1.1  cgd 					if (!isalnum(*ap))
    320  1.1  cgd 						fputc('\\', fp);
    321  1.1  cgd 
    322  1.1  cgd 					fputc(*ap, fp);
    323  1.1  cgd 				}
    324  1.1  cgd 			}
    325  1.1  cgd 			fputs("; export ", fp);
    326  1.1  cgd 			fwrite(*atenv, sizeof(char), eqp - *atenv - 1, fp);
    327  1.1  cgd 			fputc('\n', fp);
    328  1.1  cgd 
    329  1.1  cgd 		}
    330  1.1  cgd 	}
    331  1.1  cgd 	/*
    332  1.1  cgd 	 * Cd to the directory at the time and write out all the commands
    333  1.1  cgd 	 * the user supplies from stdin.
    334  1.1  cgd 	 */
    335  1.1  cgd 	fprintf(fp, "cd %s\n", cwdname());
    336  1.1  cgd 
    337  1.1  cgd 	while ((ch = getchar()) != EOF)
    338  1.1  cgd 		fputc(ch, fp);
    339  1.1  cgd 
    340  1.1  cgd 	fprintf(fp, "\n");
    341  1.1  cgd 	if (ferror(fp))
    342  1.1  cgd 		panic("Output error");
    343  1.1  cgd 
    344  1.1  cgd 	if (ferror(stdin))
    345  1.1  cgd 		panic("Input error");
    346  1.1  cgd 
    347  1.1  cgd 	fclose(fp);
    348  1.1  cgd 
    349  1.1  cgd 	/*
    350  1.1  cgd 	 * Set the x bit so that we're ready to start executing
    351  1.1  cgd 	 */
    352  1.1  cgd 	if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
    353  1.1  cgd 		perr("Cannot give away file");
    354  1.1  cgd 
    355  1.1  cgd 	close(fd2);
    356  1.1  cgd 	fprintf(stderr, "Job %s will be executed using /bin/sh\n", ppos);
    357  1.1  cgd }
    358  1.1  cgd 
    359  1.1  cgd static void
    360  1.1  cgd list_jobs()
    361  1.1  cgd {
    362  1.1  cgd 	/*
    363  1.1  cgd 	 * List all a user's jobs in the queue, by looping through
    364  1.1  cgd 	 * _PATH_ATJOBS, or everybody's if we are root
    365  1.1  cgd 	 */
    366  1.1  cgd 	struct passwd *pw;
    367  1.1  cgd 	DIR *spool;
    368  1.1  cgd 	struct dirent *dirent;
    369  1.1  cgd 	struct stat buf;
    370  1.1  cgd 	struct tm runtime;
    371  1.1  cgd 	unsigned long ctm;
    372  1.1  cgd 	char queue;
    373  1.1  cgd 	time_t runtimer;
    374  1.1  cgd 	char timestr[TIMESIZE];
    375  1.1  cgd 	int first = 1;
    376  1.1  cgd 
    377  1.1  cgd 	PRIV_START
    378  1.1  cgd 
    379  1.1  cgd 	    if (chdir(_PATH_ATJOBS) != 0)
    380  1.1  cgd 		perr2("Cannot change to ", _PATH_ATJOBS);
    381  1.1  cgd 
    382  1.1  cgd 	if ((spool = opendir(".")) == NULL)
    383  1.1  cgd 		perr2("Cannot open ", _PATH_ATJOBS);
    384  1.1  cgd 
    385  1.1  cgd 	/* Loop over every file in the directory */
    386  1.1  cgd 	while ((dirent = readdir(spool)) != NULL) {
    387  1.1  cgd 		if (stat(dirent->d_name, &buf) != 0)
    388  1.1  cgd 			perr2("Cannot stat in ", _PATH_ATJOBS);
    389  1.1  cgd 
    390  1.1  cgd 		/*
    391  1.1  cgd 		 * See it's a regular file and has its x bit turned on and
    392  1.1  cgd 		 * is the user's
    393  1.1  cgd 		 */
    394  1.1  cgd 		if (!S_ISREG(buf.st_mode)
    395  1.1  cgd 		    || ((buf.st_uid != real_uid) && !(real_uid == 0))
    396  1.1  cgd 		    || !(S_IXUSR & buf.st_mode || atverify))
    397  1.1  cgd 			continue;
    398  1.1  cgd 
    399  1.1  cgd 		if (sscanf(dirent->d_name, "%c%8lx", &queue, &ctm) != 2)
    400  1.1  cgd 			continue;
    401  1.1  cgd 
    402  1.1  cgd 		if (atqueue && (queue != atqueue))
    403  1.1  cgd 			continue;
    404  1.1  cgd 
    405  1.1  cgd 		runtimer = 60 * (time_t) ctm;
    406  1.1  cgd 		runtime = *localtime(&runtimer);
    407  1.1  cgd 		strftime(timestr, TIMESIZE, "%X %x", &runtime);
    408  1.1  cgd 		if (first) {
    409  1.1  cgd 			printf("Date\t\t\tOwner\tQueue\tJob#\n");
    410  1.1  cgd 			first = 0;
    411  1.1  cgd 		}
    412  1.1  cgd 		pw = getpwuid(buf.st_uid);
    413  1.1  cgd 
    414  1.1  cgd 		printf("%s\t%s\t%c%s\t%s\n",
    415  1.1  cgd 		    timestr,
    416  1.1  cgd 		    pw ? pw->pw_name : "???",
    417  1.1  cgd 		    queue,
    418  1.1  cgd 		    (S_IXUSR & buf.st_mode) ? "" : "(done)",
    419  1.1  cgd 		    dirent->d_name);
    420  1.1  cgd 	}
    421  1.1  cgd 	PRIV_END
    422  1.1  cgd }
    423  1.1  cgd 
    424  1.1  cgd static void
    425  1.1  cgd delete_jobs(argc, argv)
    426  1.1  cgd 	int argc;
    427  1.1  cgd 	char **argv;
    428  1.1  cgd {
    429  1.1  cgd 	/* Delete every argument (job - ID) given */
    430  1.1  cgd 	int i;
    431  1.1  cgd 	struct stat buf;
    432  1.1  cgd 
    433  1.1  cgd 	PRIV_START
    434  1.1  cgd 
    435  1.1  cgd 	    if (chdir(_PATH_ATJOBS) != 0)
    436  1.1  cgd 		perr2("Cannot change to ", _PATH_ATJOBS);
    437  1.1  cgd 
    438  1.1  cgd 	for (i = optind; i < argc; i++) {
    439  1.1  cgd 		if (stat(argv[i], &buf) != 0)
    440  1.1  cgd 			perr(argv[i]);
    441  1.1  cgd 		if ((buf.st_uid != real_uid) && !(real_uid == 0)) {
    442  1.1  cgd 			fprintf(stderr, "%s: Not owner\n", argv[i]);
    443  1.1  cgd 			exit(EXIT_FAILURE);
    444  1.1  cgd 		}
    445  1.1  cgd 		if (unlink(argv[i]) != 0)
    446  1.1  cgd 			perr(argv[i]);
    447  1.1  cgd 	}
    448  1.1  cgd 	PRIV_END
    449  1.1  cgd }				/* delete_jobs */
    450  1.1  cgd 
    451  1.1  cgd /* Global functions */
    452  1.1  cgd 
    453  1.1  cgd int
    454  1.1  cgd main(argc, argv)
    455  1.1  cgd 	int argc;
    456  1.1  cgd 	char **argv;
    457  1.1  cgd {
    458  1.1  cgd 	int c;
    459  1.1  cgd 	char queue = 'a';
    460  1.1  cgd 	char *pgm;
    461  1.1  cgd 
    462  1.1  cgd 	enum {
    463  1.1  cgd 		ATQ, ATRM, AT, BATCH
    464  1.1  cgd 	};			/* what program we want to run */
    465  1.1  cgd 	int program = AT;	/* our default program */
    466  1.1  cgd 	char *options = "q:f:mv";	/* default options for at */
    467  1.1  cgd 	time_t timer;
    468  1.1  cgd 
    469  1.1  cgd 	RELINQUISH_PRIVS
    470  1.1  cgd 
    471  1.1  cgd 	/* Eat any leading paths */
    472  1.1  cgd 	if ((pgm = strrchr(argv[0], '/')) == NULL)
    473  1.1  cgd 		pgm = argv[0];
    474  1.1  cgd 	else
    475  1.1  cgd 		pgm++;
    476  1.1  cgd 
    477  1.1  cgd 	namep = pgm;
    478  1.1  cgd 
    479  1.1  cgd 	/* find out what this program is supposed to do */
    480  1.1  cgd 	if (strcmp(pgm, "atq") == 0) {
    481  1.1  cgd 		program = ATQ;
    482  1.1  cgd 		options = "q:v";
    483  1.1  cgd 	} else if (strcmp(pgm, "atrm") == 0) {
    484  1.1  cgd 		program = ATRM;
    485  1.1  cgd 		options = "";
    486  1.1  cgd 	} else if (strcmp(pgm, "batch") == 0) {
    487  1.1  cgd 		program = BATCH;
    488  1.1  cgd 		options = "f:mv";
    489  1.1  cgd 	}
    490  1.1  cgd 
    491  1.1  cgd 	/* process whatever options we can process */
    492  1.1  cgd 	opterr = 1;
    493  1.1  cgd 	while ((c = getopt(argc, argv, options)) != EOF)
    494  1.1  cgd 		switch (c) {
    495  1.1  cgd 		case 'v':	/* verify time settings */
    496  1.1  cgd 			atverify = 1;
    497  1.1  cgd 			break;
    498  1.1  cgd 
    499  1.1  cgd 		case 'm':	/* send mail when job is complete */
    500  1.1  cgd 			send_mail = 1;
    501  1.1  cgd 			break;
    502  1.1  cgd 
    503  1.1  cgd 		case 'f':
    504  1.1  cgd 			atinput = optarg;
    505  1.1  cgd 			break;
    506  1.1  cgd 
    507  1.1  cgd 		case 'q':	/* specify queue */
    508  1.1  cgd 			if (strlen(optarg) > 1)
    509  1.1  cgd 				usage();
    510  1.1  cgd 
    511  1.1  cgd 			atqueue = queue = *optarg;
    512  1.1  cgd 			if ((!islower(queue)) || (queue > 'l'))
    513  1.1  cgd 				usage();
    514  1.1  cgd 			break;
    515  1.1  cgd 
    516  1.1  cgd 		default:
    517  1.1  cgd 			usage();
    518  1.1  cgd 			break;
    519  1.1  cgd 		}
    520  1.1  cgd 	/* end of options eating */
    521  1.1  cgd 
    522  1.1  cgd 	/* select our program */
    523  1.1  cgd 	switch (program) {
    524  1.1  cgd 	case ATQ:
    525  1.1  cgd 
    526  1.1  cgd 		REDUCE_PRIV(0);
    527  1.1  cgd 
    528  1.1  cgd 		list_jobs();
    529  1.1  cgd 		break;
    530  1.1  cgd 
    531  1.1  cgd 	case ATRM:
    532  1.1  cgd 
    533  1.1  cgd 		REDUCE_PRIV(0);
    534  1.1  cgd 
    535  1.1  cgd 		delete_jobs(argc, argv);
    536  1.1  cgd 		break;
    537  1.1  cgd 
    538  1.1  cgd 	case AT:
    539  1.1  cgd 		timer = parsetime(argc, argv);
    540  1.1  cgd 		if (atverify) {
    541  1.1  cgd 			struct tm *tm = localtime(&timer);
    542  1.1  cgd 
    543  1.1  cgd 			fprintf(stderr, "%s\n", asctime(tm));
    544  1.1  cgd 		}
    545  1.1  cgd 		writefile(timer, queue);
    546  1.1  cgd 		break;
    547  1.1  cgd 
    548  1.1  cgd 	case BATCH:
    549  1.1  cgd 		writefile(time(NULL), 'b');
    550  1.1  cgd 		break;
    551  1.1  cgd 
    552  1.1  cgd 	default:
    553  1.1  cgd 		panic("Internal error");
    554  1.1  cgd 		break;
    555  1.1  cgd 	}
    556  1.1  cgd 	exit(EXIT_SUCCESS);
    557  1.1  cgd }
    558