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