diff options
-rw-r--r-- | usr.bin/at/Makefile | 21 | ||||
-rw-r--r-- | usr.bin/at/Makefile.inc | 19 | ||||
-rw-r--r-- | usr.bin/at/at.c | 957 | ||||
-rw-r--r-- | usr.bin/at/at.h | 11 | ||||
-rw-r--r-- | usr.bin/at/at.man | 260 | ||||
-rw-r--r-- | usr.bin/at/panic.c | 37 | ||||
-rw-r--r-- | usr.bin/at/panic.h | 32 | ||||
-rw-r--r-- | usr.bin/at/parsetime.c | 244 | ||||
-rw-r--r-- | usr.bin/at/parsetime.h | 11 | ||||
-rw-r--r-- | usr.bin/at/perm.c | 123 | ||||
-rw-r--r-- | usr.bin/at/perm.h | 26 | ||||
-rw-r--r-- | usr.bin/at/privs.h | 84 |
12 files changed, 1207 insertions, 618 deletions
diff --git a/usr.bin/at/Makefile b/usr.bin/at/Makefile index a2f9b85..e1e3096 100644 --- a/usr.bin/at/Makefile +++ b/usr.bin/at/Makefile @@ -1,7 +1,9 @@ -# $Id: Makefile,v 1.1 1993/12/05 11:35:35 cgd Exp $ +# $Id: Makefile,v 1.1 1994/01/05 01:08:51 nate Exp $ + +.include "${.CURDIR}/Makefile.inc" PROG= at -SRCS= at.c panic.c parsetime.c +SRCS= at.c panic.c parsetime.c perm.c LINKS= ${BINDIR}/at ${BINDIR}/atq \ ${BINDIR}/at ${BINDIR}/atrm \ ${BINDIR}/at ${BINDIR}/batch @@ -11,5 +13,20 @@ MLINKS= at.1 batch.1 \ BINOWN= root BINMODE= 4555 +MANSRC= . +CLEANFILES += ${MAN1} +MANDEPEND = ${MAN1} .include <bsd.prog.mk> + +${MAN1}: at.man + sed -e \ + "s@_ATSPOOL_DIR@$(ATSPOOL_DIR)@g; \ + s@_ATJOB_DIR@$(ATJOB_DIR)@g; \ + s@_DEFAULT_BATCH_QUEUE@$(DEFAULT_BATCH_QUEUE)@g; \ + s@_DEFAULT_AT_QUEUE@$(DEFAULT_AT_QUEUE)@g; \ + s@_LOADAVG_MX@$(LOADAVG_MX)@g; \ + s@_PERM_PATH@$(PERM_PATH)@g; \ + s@_LOCKFILE@$(LOCKFILE)@g" \ + < $? > $@ + diff --git a/usr.bin/at/Makefile.inc b/usr.bin/at/Makefile.inc new file mode 100644 index 0000000..765c947 --- /dev/null +++ b/usr.bin/at/Makefile.inc @@ -0,0 +1,19 @@ +VERSION= 2.7a +LOCKFILE = .lockfile +ATSPOOL_DIR=/var/at/spool +ATJOB_DIR=/var/at/jobs +ATLIB_DIR=/usr/libexec +LOADAVG_MX=1.5 +DAEMON_GID=1 +DAEMON_UID=1 +DEFAULT_BATCH_QUEUE=E +DEFAULT_AT_QUEUE=c +PERM_PATH=/var/at + +CFLAGS += -DATJOB_DIR=\"$(ATJOB_DIR)/\" \ + -DLFILE=\"$(ATJOB_DIR)/$(LOCKFILE)\" \ + -DLOADAVG_MX=$(LOADAVG_MX) -DATSPOOL_DIR=\"$(ATSPOOL_DIR)\" \ + -DVERSION=\"$(VERSION)\" -DDAEMON_UID=$(DAEMON_UID) -DDAEMON_GID=$(DAEMON_GID) \ + -DDEFAULT_BATCH_QUEUE=\'$(DEFAULT_BATCH_QUEUE)\' \ + -DDEFAULT_AT_QUEUE=\'$(DEFAULT_AT_QUEUE)\' -DPERM_PATH=\"$(PERM_PATH)/\" + diff --git a/usr.bin/at/at.c b/usr.bin/at/at.c index 53959e3..f803af5 100644 --- a/usr.bin/at/at.c +++ b/usr.bin/at/at.c @@ -1,10 +1,9 @@ -/* - * at.c : Put file into atrun queue - * Copyright (C) 1993 Thomas Koenig +/* + * at.c : Put file into atrun queue + * Copyright (C) 1993, 1994 Thomas Koenig * - * Atrun & Atq modifications - * Copyright (C) 1993 David Parsons - * All rights reserved. + * Atrun & Atq modifications + * Copyright (C) 1993 David Parsons * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,6 +29,7 @@ #define _USE_BSD 1 /* System Headers */ + #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> @@ -45,518 +45,615 @@ #include <string.h> #include <time.h> #include <unistd.h> +#ifndef __FreeBSD__ +#include <getopt.h> +#endif /* Local headers */ + #include "at.h" #include "panic.h" #include "parsetime.h" -#include "pathnames.h" +#include "perm.h" + #define MAIN #include "privs.h" /* Macros */ -#define ALARMC 10 /* Number of seconds to wait for timeout */ + +#ifndef ATJOB_DIR +#define ATJOB_DIR "/usr/spool/atjobs/" +#endif + +#ifndef LFILE +#define LFILE ATJOB_DIR ".lockfile" +#endif + +#ifndef ATJOB_MX +#define ATJOB_MX 255 +#endif + +#define ALARMC 10 /* Number of seconds to wait for timeout */ #define SIZE 255 #define TIMESIZE 50 /* File scope variables */ -static char rcsid[] = "$Id: at.c,v 1.2 1993/12/06 04:10:42 cgd Exp $"; + +static char rcsid[] = "$Id: at.c,v 1.2 1994/06/08 18:19:43 kernel Exp $"; char *no_export[] = { - "TERM", "TERMCAP", "DISPLAY", "_" -}; + "TERM", "TERMCAP", "DISPLAY", "_" +} ; static send_mail = 0; /* External variables */ + extern char **environ; int fcreated; char *namep; -char atfile[FILENAME_MAX]; +char atfile[] = ATJOB_DIR "12345678901234"; -char *atinput = (char *) 0; /* where to get input from */ +char *atinput = (char*)0; /* where to get input from */ char atqueue = 0; /* which queue to examine for jobs (atq) */ char atverify = 0; /* verify time instead of queuing job */ /* Function declarations */ -static void sigc __P((int signo)); -static void alarmc __P((int signo)); -static char *cwdname __P((void)); -static void writefile __P((time_t runtimer, char queue)); -static void list_jobs __P((void)); + +static void sigc(int signo); +static void alarmc(int signo); +static char *cwdname(void); +static void writefile(time_t runtimer, char queue); +static void list_jobs(void); /* Signal catching functions */ -static void -sigc(signo) - int signo; +static void sigc(int signo) { -/* If the user presses ^C, remove the spool file and exit +/* If the user presses ^C, remove the spool file and exit */ - if (fcreated) { - PRIV_START - unlink(atfile); - PRIV_END - } + if (fcreated) + { + PRIV_START + unlink(atfile); + PRIV_END + } - exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } -static void -alarmc(signo) - int signo; +static void alarmc(int signo) { /* Time out after some seconds */ - panic("File locking timed out"); + panic("File locking timed out"); } /* Local functions */ -static char * -cwdname() +static char *cwdname(void) { /* Read in the current directory; the name will be overwritten on * subsequent calls. */ - static char *ptr = NULL; - static size_t size = SIZE; + static char *ptr = NULL; + static size_t size = SIZE; - if (ptr == NULL) - ptr = (char *) malloc(size); + if (ptr == NULL) + ptr = (char *) mymalloc(size); - while (1) { - if (ptr == NULL) - panic("Out of memory"); - - if (getcwd(ptr, size - 1) != NULL) - return ptr; - - if (errno != ERANGE) - perr("Cannot get directory"); - - free(ptr); - size += SIZE; - ptr = (char *) malloc(size); - } + while (1) + { + if (ptr == NULL) + panic("Out of memory"); + + if (getcwd(ptr, size-1) != NULL) + return ptr; + + if (errno != ERANGE) + perr("Cannot get directory"); + + free (ptr); + size += SIZE; + ptr = (char *) mymalloc(size); + } } static void -writefile(runtimer, queue) - time_t runtimer; - char queue; +writefile(time_t runtimer, char queue) { - /* - * This does most of the work if at or batch are invoked for - * writing a job. - */ - int i; - char *ap, *ppos, *mailname; - struct passwd *pass_entry; - struct stat statbuf; - int fdes, lockdes, fd2; - FILE *fp, *fpin; - struct sigaction act; - char **atenv; - int ch; - mode_t cmask; - struct flock lock; - - /* - * Install the signal handler for SIGINT; terminate after removing the - * spool file if necessary - */ - act.sa_handler = sigc; - sigemptyset(&(act.sa_mask)); - act.sa_flags = 0; - - sigaction(SIGINT, &act, NULL); - - strcpy(atfile, _PATH_ATJOBS); - ppos = atfile + strlen(_PATH_ATJOBS); - - /* - * Loop over all possible file names for running something at this - * particular time, see if a file is there; the first empty slot at - * any particular time is used. Lock the file _PATH_LOCKFILE first - * to make sure we're alone when doing this. - */ - - PRIV_START - - if ((lockdes = open(_PATH_LOCKFILE, O_WRONLY | O_CREAT, 0600)) < 0) - perr2("Cannot open lockfile ", _PATH_LOCKFILE); - - lock.l_type = F_WRLCK; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - - act.sa_handler = alarmc; - sigemptyset(&(act.sa_mask)); - act.sa_flags = 0; - - /* - * Set an alarm so a timeout occurs after ALARMC seconds, in case - * something is seriously broken. - */ - sigaction(SIGALRM, &act, NULL); - alarm(ALARMC); - fcntl(lockdes, F_SETLKW, &lock); - alarm(0); - - for (i = 0; i < AT_MAXJOBS; i++) { - sprintf(ppos, "%c%8lx.%3x", queue, - (unsigned long) (runtimer / 60), i); - for (ap = ppos; *ap != '\0'; ap++) - if (*ap == ' ') - *ap = '0'; - - if (stat(atfile, &statbuf) != 0) { - if (errno == ENOENT) - break; - else - perr2("Cannot access ", _PATH_ATJOBS); - } - } /* for */ - - if (i >= AT_MAXJOBS) - panic("Too many jobs already"); - - /* - * Create the file. The x bit is only going to be set after it has - * been completely written out, to make sure it is not executed in - * the meantime. To make sure they do not get deleted, turn off - * their r bit. Yes, this is a kluge. - */ - cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); - if ((fdes = creat(atfile, O_WRONLY)) == -1) - perr("Cannot create atjob file"); - - if ((fd2 = dup(fdes)) < 0) - perr("Error in dup() of job file"); - - if (fchown(fd2, real_uid, -1) != 0) - perr("Cannot give away file"); - - PRIV_END - - /* - * We no longer need suid root; now we just need to be able to - * write to the directory, if necessary. - */ - - REDUCE_PRIV(0); - - /* - * We've successfully created the file; let's set the flag so it - * gets removed in case of an interrupt or error. - */ - fcreated = 1; - - /* Now we can release the lock, so other people can access it */ - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - fcntl(lockdes, F_SETLKW, &lock); - close(lockdes); - - if ((fp = fdopen(fdes, "w")) == NULL) - panic("Cannot reopen atjob file"); - - /* - * Get the userid to mail to, first by trying getlogin(), which - * reads /etc/utmp, then from LOGNAME, finally from getpwuid(). - */ - mailname = getlogin(); - if (mailname == NULL) - mailname = getenv("LOGNAME"); - - if ((mailname == NULL) || (mailname[0] == '\0') - || (strlen(mailname) > 8)) { - pass_entry = getpwuid(getuid()); - if (pass_entry != NULL) - mailname = pass_entry->pw_name; +/* This does most of the work if at or batch are invoked for writing a job. + */ + int i; + char *ap, *ppos, *mailname; + struct passwd *pass_entry; + struct stat statbuf; + int fdes, lockdes, fd2; + FILE *fp, *fpin; + struct sigaction act; + char **atenv; + int ch; + mode_t cmask; + struct flock lock; + +/* Install the signal handler for SIGINT; terminate after removing the + * spool file if necessary + */ + act.sa_handler = sigc; + sigemptyset(&(act.sa_mask)); + act.sa_flags = 0; + + sigaction(SIGINT, &act, NULL); + + ppos = atfile + strlen(ATJOB_DIR); + + /* Loop over all possible file names for running something at this + * particular time, see if a file is there; the first empty slot at any + * particular time is used. Lock the file LFILE first to make sure + * we're alone when doing this. + */ + + PRIV_START + + if ((lockdes = open(LFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0) + perr("Cannot open lockfile " LFILE); + + lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; + lock.l_len = 0; + + act.sa_handler = alarmc; + sigemptyset(&(act.sa_mask)); + act.sa_flags = 0; + + /* Set an alarm so a timeout occurs after ALARMC seconds, in case + * something is seriously broken. + */ + sigaction(SIGALRM, &act, NULL); + alarm(ALARMC); + fcntl(lockdes, F_SETLKW, &lock); + alarm(0); + + for(i=0; i<ATJOB_MX; i++) + { + sprintf(ppos, "%c%8lx.%2x", queue, + (unsigned long) (runtimer/60), i); + for(ap=ppos; *ap != '\0'; ap ++) + if (*ap == ' ') + *ap = '0'; + + if (stat(atfile, &statbuf) != 0) + { + if (errno == ENOENT) + break; + else + perr("Cannot access " ATJOB_DIR); } - - if (atinput != (char *) NULL) { - fpin = freopen(atinput, "r", stdin); - if (fpin == NULL) - perr("Cannot open input file"); + } /* for */ + + if (i >= ATJOB_MX) + panic("Too many jobs already"); + + /* Create the file. The x bit is only going to be set after it has + * been completely written out, to make sure it is not executed in the + * meantime. To make sure they do not get deleted, turn off their r + * bit. Yes, this is a kluge. + */ + cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); + if ((fdes = creat(atfile, O_WRONLY)) == -1) + perr("Cannot create atjob file"); + + if ((fd2 = dup(fdes)) <0) + perr("Error in dup() of job file"); + + if(fchown(fd2, real_uid, real_gid) != 0) + perr("Cannot give away file"); + + PRIV_END + + /* We no longer need suid root; now we just need to be able to write + * to the directory, if necessary. + */ + + REDUCE_PRIV(DAEMON_UID, DAEMON_GID) + + /* We've successfully created the file; let's set the flag so it + * gets removed in case of an interrupt or error. + */ + fcreated = 1; + + /* Now we can release the lock, so other people can access it + */ + lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; + lock.l_len = 0; + fcntl(lockdes, F_SETLKW, &lock); + close(lockdes); + + if((fp = fdopen(fdes, "w")) == NULL) + panic("Cannot reopen atjob file"); + + /* Get the userid to mail to, first by trying getlogin(), which reads + * /etc/utmp, then from LOGNAME, finally from getpwuid(). + */ + mailname = getlogin(); + if (mailname == NULL) + mailname = getenv("LOGNAME"); + + if ((mailname == NULL) || (mailname[0] == '\0') + || (strlen(mailname) > 8) || (getpwnam(mailname)==NULL)) + { + pass_entry = getpwuid(getuid()); + if (pass_entry != NULL) + mailname = pass_entry->pw_name; + } + + if (atinput != (char *) NULL) + { + fpin = freopen(atinput, "r", stdin); + if (fpin == NULL) + perr("Cannot open input file"); + } + fprintf(fp, "#! /bin/sh\n# mail %8s %d\n", mailname, send_mail); + + /* Write out the umask at the time of invocation + */ + fprintf(fp, "umask %lo\n", (unsigned long) cmask); + + /* Write out the environment. Anything that may look like a + * special character to the shell is quoted, except for \n, which is + * done with a pair of "'s. Dont't export the no_export list (such + * as TERM or DISPLAY) because we don't want these. + */ + for (atenv= environ; *atenv != NULL; atenv++) + { + int export = 1; + char *eqp; + + eqp = strchr(*atenv, '='); + if (ap == NULL) + eqp = *atenv; + else + { + int i; + for (i=0; i<sizeof(no_export)/sizeof(no_export[0]); i++) + { + export = export + && (strncmp(*atenv, no_export[i], + (size_t) (eqp-*atenv)) != 0); + } + eqp++; } - fprintf(fp, "#! /bin/sh\n# mail %8s %d\n", mailname, send_mail); - - /* Write out the umask at the time of invocation */ - fprintf(fp, "umask %lo\n", (unsigned long) cmask); - - /* - * Write out the environment. Anything that may look like a special - * character to the shell is quoted, except for \n, which is done - * with a pair of "'s. Dont't export the no_export list (such as - * TERM or DISPLAY) because we don't want these. - */ - for (atenv = environ; *atenv != NULL; atenv++) { - int export = 1; - char *eqp; - - eqp = strchr(*atenv, '='); - if (ap == NULL) - eqp = *atenv; - else { - int i; - - for (i = 0;i < sizeof(no_export) / - sizeof(no_export[0]); i++) { - export = export - && (strncmp(*atenv, no_export[i], - (size_t) (eqp - *atenv)) != 0); - } - eqp++; - } - - if (export) { - fwrite(*atenv, sizeof(char), eqp - *atenv, fp); - for (ap = eqp; *ap != '\0'; ap++) { - if (*ap == '\n') - fprintf(fp, "\"\n\""); - else { - if (!isalnum(*ap)) - fputc('\\', fp); - - fputc(*ap, fp); - } - } - fputs("; export ", fp); - fwrite(*atenv, sizeof(char), eqp - *atenv - 1, fp); - fputc('\n', fp); + if (export) + { + fwrite(*atenv, sizeof(char), eqp-*atenv, fp); + for(ap = eqp;*ap != '\0'; ap++) + { + if (*ap == '\n') + fprintf(fp, "\"\n\""); + else + { + if (*ap != '/' && !isalnum(*ap)) + fputc('\\', fp); + + fputc(*ap, fp); } + } + fputs("; export ", fp); + fwrite(*atenv, sizeof(char), eqp-*atenv -1, fp); + fputc('\n', fp); + + } + } + /* Cd to the directory at the time and write out all the + * commands the user supplies from stdin. + */ + fprintf(fp, "cd "); + for (ap = cwdname(); *ap != '\0'; ap++) + { + if (*ap == '\n') + fprintf(fp, "\"\n\""); + else + { + if (*ap != '/' && !isalnum(*ap)) + fputc('\\', fp); + + fputc(*ap, fp); } - /* - * Cd to the directory at the time and write out all the commands - * the user supplies from stdin. - */ - fprintf(fp, "cd %s\n", cwdname()); + } + /* Test cd's exit status: die if the original directory has been + * removed, become unreadable or whatever + */ + fprintf(fp, " || {\n\t echo 'Execution directory " + "inaccessible' >&2\n\t exit 1\n}\n"); - while ((ch = getchar()) != EOF) - fputc(ch, fp); + while((ch = getchar()) != EOF) + fputc(ch, fp); - fprintf(fp, "\n"); - if (ferror(fp)) - panic("Output error"); + fprintf(fp, "\n"); + if (ferror(fp)) + panic("Output error"); + + if (ferror(stdin)) + panic("Input error"); - if (ferror(stdin)) - panic("Input error"); + fclose(fp); - fclose(fp); + /* Set the x bit so that we're ready to start executing + */ - /* - * Set the x bit so that we're ready to start executing - */ - if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0) - perr("Cannot give away file"); + if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0) + perr("Cannot give away file"); - close(fd2); - fprintf(stderr, "Job %s will be executed using /bin/sh\n", ppos); + close(fd2); + fprintf(stderr, "Job %s will be executed using /bin/sh\n", ppos); } static void list_jobs() { - /* - * List all a user's jobs in the queue, by looping through - * _PATH_ATJOBS, or everybody's if we are root - */ - struct passwd *pw; - DIR *spool; - struct dirent *dirent; - struct stat buf; - struct tm runtime; - unsigned long ctm; - char queue; - time_t runtimer; - char timestr[TIMESIZE]; - int first = 1; - - PRIV_START - - if (chdir(_PATH_ATJOBS) != 0) - perr2("Cannot change to ", _PATH_ATJOBS); - - if ((spool = opendir(".")) == NULL) - perr2("Cannot open ", _PATH_ATJOBS); - - /* Loop over every file in the directory */ - while ((dirent = readdir(spool)) != NULL) { - if (stat(dirent->d_name, &buf) != 0) - perr2("Cannot stat in ", _PATH_ATJOBS); - - /* - * See it's a regular file and has its x bit turned on and - * is the user's - */ - if (!S_ISREG(buf.st_mode) - || ((buf.st_uid != real_uid) && !(real_uid == 0)) - || !(S_IXUSR & buf.st_mode || atverify)) - continue; - - if (sscanf(dirent->d_name, "%c%8lx", &queue, &ctm) != 2) - continue; - - if (atqueue && (queue != atqueue)) - continue; - - runtimer = 60 * (time_t) ctm; - runtime = *localtime(&runtimer); - strftime(timestr, TIMESIZE, "%X %x", &runtime); - if (first) { - printf("Date\t\t\tOwner\tQueue\tJob#\n"); - first = 0; - } - pw = getpwuid(buf.st_uid); - - printf("%s\t%s\t%c%s\t%s\n", - timestr, - pw ? pw->pw_name : "???", - queue, - (S_IXUSR & buf.st_mode) ? "" : "(done)", - dirent->d_name); + /* List all a user's jobs in the queue, by looping through ATJOB_DIR, + * or everybody's if we are root + */ + struct passwd *pw; + DIR *spool; + struct dirent *dirent; + struct stat buf; + struct tm runtime; + unsigned long ctm; + char queue; + time_t runtimer; + char timestr[TIMESIZE]; + int first=1; + + PRIV_START + + if (chdir(ATJOB_DIR) != 0) + perr("Cannot change to " ATJOB_DIR); + + if ((spool = opendir(".")) == NULL) + perr("Cannot open " ATJOB_DIR); + + /* Loop over every file in the directory + */ + while((dirent = readdir(spool)) != NULL) { + if (stat(dirent->d_name, &buf) != 0) + perr("Cannot stat in " ATJOB_DIR); + + /* See it's a regular file and has its x bit turned on and + * is the user's + */ + if (!S_ISREG(buf.st_mode) + || ((buf.st_uid != real_uid) && ! (real_uid == 0)) + || !(S_IXUSR & buf.st_mode || atverify)) + continue; + + if(sscanf(dirent->d_name, "%c%8lx", &queue, &ctm)!=2) + continue; + + if (atqueue && (queue != atqueue)) + continue; + + runtimer = 60*(time_t) ctm; + runtime = *localtime(&runtimer); + strftime(timestr, TIMESIZE, "%X %x", &runtime); + if (first) { + printf("Date\t\t\tOwner\tQueue\tJob#\n"); + first=0; } - PRIV_END + pw = getpwuid(buf.st_uid); + + printf("%s\t%s\t%c%s\t%s\n", + timestr, + pw ? pw->pw_name : "???", + queue, + (S_IXUSR & buf.st_mode) ? "":"(done)", + dirent->d_name); + } + PRIV_END } static void -delete_jobs(argc, argv) - int argc; - char **argv; +delete_jobs(int argc, char **argv) { - /* Delete every argument (job - ID) given */ - int i; - struct stat buf; - - PRIV_START - - if (chdir(_PATH_ATJOBS) != 0) - perr2("Cannot change to ", _PATH_ATJOBS); - - for (i = optind; i < argc; i++) { - if (stat(argv[i], &buf) != 0) - perr(argv[i]); - if ((buf.st_uid != real_uid) && !(real_uid == 0)) { - fprintf(stderr, "%s: Not owner\n", argv[i]); - exit(EXIT_FAILURE); - } - if (unlink(argv[i]) != 0) - perr(argv[i]); + /* Delete every argument (job - ID) given + */ + int i; + struct stat buf; + + PRIV_START + + if (chdir(ATJOB_DIR) != 0) + perr("Cannot change to " ATJOB_DIR); + + for (i=optind; i < argc; i++) { + if (stat(argv[i], &buf) != 0) + perr(argv[i]); + if ((buf.st_uid != real_uid) && !(real_uid == 0)) { + fprintf(stderr, "%s: Not owner\n", argv[i]); + exit(EXIT_FAILURE); } - PRIV_END -} /* delete_jobs */ + if (unlink(argv[i]) != 0) + perr(argv[i]); + } + PRIV_END +} /* delete_jobs */ /* Global functions */ +void * +mymalloc(size_t n) +{ + void *p; + if ((p=malloc(n))==(void *)0) + { + fprintf(stderr,"Virtual memory exhausted\n"); + exit(EXIT_FAILURE); + } + return p; +} + int -main(argc, argv) - int argc; - char **argv; +main(int argc, char **argv) { - int c; - char queue = 'a'; - char *pgm; - - enum { - ATQ, ATRM, AT, BATCH - }; /* what program we want to run */ - int program = AT; /* our default program */ - char *options = "q:f:mv"; /* default options for at */ - time_t timer; - - RELINQUISH_PRIVS - - /* Eat any leading paths */ - if ((pgm = strrchr(argv[0], '/')) == NULL) - pgm = argv[0]; - else - pgm++; - - namep = pgm; - - /* find out what this program is supposed to do */ - if (strcmp(pgm, "atq") == 0) { - program = ATQ; - options = "q:v"; - } else if (strcmp(pgm, "atrm") == 0) { - program = ATRM; - options = ""; - } else if (strcmp(pgm, "batch") == 0) { - program = BATCH; - options = "f:mv"; - } + int c; + char queue = DEFAULT_AT_QUEUE; + char queue_set = 0; + char *pgm; + + enum { ATQ, ATRM, AT, BATCH }; /* what program we want to run */ + int program = AT; /* our default program */ + char *options = "q:f:mvldbV"; /* default options for at */ + int disp_version = 0; + time_t timer; + + RELINQUISH_PRIVS + + /* Eat any leading paths + */ + if ((pgm = strrchr(argv[0], '/')) == NULL) + pgm = argv[0]; + else + pgm++; + + namep = pgm; + + /* find out what this program is supposed to do + */ + if (strcmp(pgm, "atq") == 0) { + program = ATQ; + options = "q:vV"; + } + else if (strcmp(pgm, "atrm") == 0) { + program = ATRM; + options = "V"; + } + else if (strcmp(pgm, "batch") == 0) { + program = BATCH; + options = "f:q:mvV"; + } + + /* process whatever options we can process + */ + opterr=1; + while ((c=getopt(argc, argv, options)) != EOF) + switch (c) { + case 'v': /* verify time settings */ + atverify = 1; + break; + + case 'm': /* send mail when job is complete */ + send_mail = 1; + break; + + case 'f': + atinput = optarg; + break; + + case 'q': /* specify queue */ + if (strlen(optarg) > 1) + usage(); + + atqueue = queue = *optarg; + if (!(islower(queue)||isupper(queue))) + usage(); + + queue_set = 1; + break; + + case 'd': + if (program != AT) + usage(); + + program = ATRM; + options = "V"; + break; + + case 'l': + if (program != AT) + usage(); + + program = ATQ; + options = "q:vV"; + break; + + case 'b': + if (program != AT) + usage(); + + program = BATCH; + options = "f:q:mvV"; + break; + + case 'V': + disp_version = 1; + break; - /* process whatever options we can process */ - opterr = 1; - while ((c = getopt(argc, argv, options)) != EOF) - switch (c) { - case 'v': /* verify time settings */ - atverify = 1; - break; - - case 'm': /* send mail when job is complete */ - send_mail = 1; - break; - - case 'f': - atinput = optarg; - break; - - case 'q': /* specify queue */ - if (strlen(optarg) > 1) - usage(); - - atqueue = queue = *optarg; - if ((!islower(queue)) || (queue > 'l')) - usage(); - break; - - default: - usage(); - break; - } - /* end of options eating */ + default: + usage(); + break; + } + /* end of options eating + */ + + if (disp_version) + fprintf(stderr, "at version " VERSION "\n" + "Bug reports to: ig25@rz.uni-karlsruhe.de (Thomas Koenig)\n"); + + /* select our program + */ + if(!check_permission()) + { + fprintf(stderr, "You do not have permission to use %s.\n",namep); + exit(EXIT_FAILURE); + } + switch (program) { + case ATQ: - /* select our program */ - switch (program) { - case ATQ: + REDUCE_PRIV(DAEMON_UID, DAEMON_GID) - REDUCE_PRIV(0); + list_jobs(); + break; - list_jobs(); - break; + case ATRM: - case ATRM: + REDUCE_PRIV(DAEMON_UID, DAEMON_GID) - REDUCE_PRIV(0); + delete_jobs(argc, argv); + break; - delete_jobs(argc, argv); - break; + case AT: + timer = parsetime(argc, argv); + if (atverify) + { + struct tm *tm = localtime(&timer); + fprintf(stderr, "%s\n", asctime(tm)); + } + writefile(timer, queue); + break; - case AT: - timer = parsetime(argc, argv); - if (atverify) { - struct tm *tm = localtime(&timer); + case BATCH: + if (queue_set) + queue = toupper(queue); + else + queue = DEFAULT_BATCH_QUEUE; - fprintf(stderr, "%s\n", asctime(tm)); - } - writefile(timer, queue); - break; + if (argc > optind) + timer = parsetime(argc, argv); + else + timer = time(NULL); + + if (atverify) + { + struct tm *tm = localtime(&timer); + fprintf(stderr, "%s\n", asctime(tm)); + } - case BATCH: - writefile(time(NULL), 'b'); - break; + writefile(timer, queue); + break; - default: - panic("Internal error"); - break; - } - exit(EXIT_SUCCESS); + default: + panic("Internal error"); + break; + } + exit(EXIT_SUCCESS); } diff --git a/usr.bin/at/at.h b/usr.bin/at/at.h index 80da0fe..1efd316 100644 --- a/usr.bin/at/at.h +++ b/usr.bin/at/at.h @@ -1,7 +1,6 @@ -/* - * at.h - header for at(1) - * Copyright (c) 1993 by Thomas Koenig - * All rights reserved. +/* + * at.h - header for at(1) + * Copyright (C) 1993 Thomas Koenig * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -22,8 +21,6 @@ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id: at.h,v 1.1 1993/12/05 11:36:45 cgd Exp $ */ extern int fcreated; @@ -31,4 +28,4 @@ extern char *namep; extern char atfile[]; extern char atverify; -#define AT_MAXJOBS 255 /* max jobs outstanding per user */ +void *mymalloc(size_t n); diff --git a/usr.bin/at/at.man b/usr.bin/at/at.man new file mode 100644 index 0000000..21e789c --- /dev/null +++ b/usr.bin/at/at.man @@ -0,0 +1,260 @@ +.\" $Id: at.man,v 1.3 1994/06/16 16:49:46 kernel Exp $ +.Dd April 12, 1995 +.Dt "AT" 1 +.Os "FreeBSD 2.1" +.Sh NAME +.Nm at, batch, atq, atrm +.Nd queue, examine or delete jobs for later execution +.Sh SYNOPSIS +.Nm at +.Op Fl V +.Op Fl q Ar queue +.Op Fl f Ar file +.Op Fl mldbv +.Ar time +.Pp +.Nm atq +.Op Fl V +.Op Fl q Ar queue +.Op Fl v +.Pp +.Nm atrm +.Op Fl V +.Ar job +.Op Ar job ... +.Pp +.Nm batch +.Op Fl V +.Op Fl q Ar queue +.Op Fl f Ar file +.Op Fl mv +.Op Ar time +.Sh DESCRIPTION +.Nm At +and +.Nm batch +read commands from standard input or a specified file which are to +be executed at a later time, using +.Xr sh 1 . +.Bl -tag -width indent +.It Nm at +executes commands at a specified time; +.It Nm atq +lists the user's pending jobs, unless the user is the superuser; in that +case, everybody's jobs are listed; +.It Nm atrm +deletes jobs; +.It Nm batch +executes commands when system load levels permit; in other words, when the load average +drops below _LOADAVG_MX, or the value specified in the invocation of +.Nm atrun . +.El +.Pp +.Nm At +allows some moderately complex +.Ar time +specifications. It accepts times of the form +.Ar HHMM +or +.Ar HH:MM +to run a job at a specific time of day. +(If that time is already past, the next day is assumed.) +You may also specify +.Nm midnight , +.Nm noon , +or +.Nm teatime +(4pm) +and you can have a time-of-day suffixed with +.Nm AM +or +.Nm PM +for running in the morning or the evening. +You can also say what day the job will be run, +by giving a date in the form +.Ar \%month-name day +with an optional +.Ar year , +or giving a date of the form +.Ar MMDDYY +or +.Ar MM/DD/YY +or +.Ar DD.MM.YY . +You can also give times like +.Op Nm now +.Nm + Ar count \%time-units , +where the time-units can be +.Nm minutes , +.Nm hours , +.Nm days , +or +.Nm weeks +and you can tell +.Nm at +to run the job today by suffixing the time with +.Nm today +and to run the job tomorrow by suffixing the time with +.Nm tomorrow. +.Pp +For example, to run a job at 4pm three days from now, you would do +.Nm at 4pm + 3 days , +to run a job at 10:00am on July 31, you would do +.Nm at 10am Jul 31 +and to run a job at 1am tomorrow, you would do +.Nm at 1am tomorrow. +.Pp +For both +.Nm at +and +.Nm batch , +commands are read from standard input or the file specified +with the +.Fl f +option and executed. +The working directory, the environment (except for the variables +.Nm TERM , +.Nm TERMCAP , +.Nm DISPLAY +and +.Nm _ ) +and the +.Ar umask +are retained from the time of invocation. +An +.Nm at +or +.Nm batch +command invoked from a +.Xr su 1 +shell will retain the current userid. +The user will be mailed standard error and standard output from his +commands, if any. Mail will be sent using the command +.Xr sendmail 8 . +If +.Nm at +is executed from a +.Xr su 1 +shell, the owner of the login shell will receive the mail. +.Pp +The superuser may use these commands in any case. +For other users, permission to use at is determined by the files +.Pa _PERM_PATH/at.allow +and +.Pa _PERM_PATH/at.deny . +.Pp +If the file +.Pa _PERM_PATH/at.allow +exists, only usernames mentioned in it are allowed to use +.Nm at . +.Pp +If +.Pa _PERM_PATH/at.allow +does not exist, +.Pa _PERM_PATH/at.deny +is checked, every username not mentioned in it is then allowed +to use +.Nm at . +.Pp +If neither exists, only the superuser is allowed use of +.Nm at . +.Pp +An empty +.Pa _PERM_PATH/at.deny +means that every user is allowed use these commands, this is the +default configuration. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl V +prints the version number to standard error. +.It Fl q Ar queue +uses the specified queue. +A queue designation consists of a single letter; valid queue designations +range from +.Nm a +to +.Nm z . +and +.Nm A +to +.Nm Z . +The +.Nm _DEFAULT_AT_QUEUE +queue is the default for +.Nm at +and the +.Nm _DEFAULT_BATCH_QUEUE +queue for +.Nm batch . +Queues with higher letters run with increased niceness. +If a job is submitted to a queue designated with an uppercase letter, it +is treated as if it had been submitted to batch at that time. +If +.Nm atq +is given a specific queue, it will only show jobs pending in that queue. +.It Fl m +Send mail to the user when the job has completed even if there was no +output. +.It Fl f Ar file +Reads the job from +.Ar file +rather than standard input. +.It Fl l +Is an alias for +.Nm atq. +.It Fl d +Is an alias for +.Nm atrm. +.It Fl b +Is an alias for +.Nm batch. +.It Fl v +For +.Nm atq , +shows completed but not yet deleted jobs in the queue; otherwise +shows the time the job will be executed. +.Sh FILES +.Bl -tag -width _ATJOB_DIR/_LOCKFILE -compact +.It Pa _ATJOB_DIR +Directory containing job files +.It Pa _ATSPOOL_DIR +Directory containing output spool files +.It Pa /var/run/utmp +Login records +.It Pa _PERM_PATH/at.allow +Allow permission control +.It Pa _PERM_PATH/at.deny +Deny permission control +.It Pa _ATJOB_DIR/_LOCKFILE +Job-creation lock file. +.Sh SEE ALSO +.Xr cron 8 , +.Xr nice 1 , +.Xr umask 2 , +.Xr sh 1 , +.Xr sendmail 8 , +.Xr atrun 8 . +.El +.Sh BUGS +.Pp +If the file +.Pa /var/run/utmp +is not available or corrupted, or if the user is not logged on at the +time +.Nm at +is invoked, the mail is sent to the userid found +in the environment variable +.Nm LOGNAME . +If that is undefined or empty, the current userid is assumed. +.Pp +.Nm At +and +.Nm batch +as presently implemented are not suitable when users are competing for +resources. +If this is the case for your site, you might want to consider another +batch system, such as +.Nm nqs . +.Sh AUTHOR +At was mostly written by Thomas Koenig, ig25@rz.uni-karlsruhe.de. +The time parsing routines are by David Parsons, orc@pell.chi.il.us. diff --git a/usr.bin/at/panic.c b/usr.bin/at/panic.c index cd19b5a..9f96f71 100644 --- a/usr.bin/at/panic.c +++ b/usr.bin/at/panic.c @@ -1,7 +1,6 @@ -/* - * panic.c - terminate fast in case of error - * Copyright (c) 1993 by Thomas Koenig - * All rights reserved. +/* + * panic.c - terminate fast in case of error + * Copyright (C) 1993 Thomas Koenig * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -38,28 +37,26 @@ /* File scope variables */ -static char rcsid[] = "$Id: panic.c,v 1.1 1993/12/05 11:36:51 cgd Exp $"; +static char rcsid[] = "$Id: panic.c,v 1.1 1994/05/10 18:23:08 kernel Exp $"; /* External variables */ /* Global functions */ void -panic(a) - char *a; +panic(char *a) { /* Something fatal has happened, print error message and exit. */ - fprintf(stderr, "%s: %s\n", namep, a); + fprintf(stderr,"%s: %s\n",namep,a); if (fcreated) unlink(atfile); - exit(EXIT_FAILURE); + exit (EXIT_FAILURE); } void -perr(a) - char *a; +perr(char *a) { /* Some operating system error; print error message and exit. */ @@ -70,22 +67,14 @@ perr(a) exit(EXIT_FAILURE); } -void -perr2(a, b) - char *a, *b; -{ - fprintf(stderr, "%s", a); - perr(b); -} - void usage(void) { /* Print usage and exit. */ - fprintf(stderr, "Usage: at [-q x] [-f file] [-m] time\n" - " atq [-q x] [-v]\n" - " atrm [-q x] job ...\n" - " batch [-f file] [-m]\n"); - exit(EXIT_FAILURE); + fprintf(stderr, "Usage: at [-V] [-q x] [-f file] [-m] time\n" + " atq [-V] [-q x] [-v]\n" + " atrm [-V] [-q x] job ...\n" + " batch [-V] [-f file] [-m]\n"); + exit(EXIT_FAILURE); } diff --git a/usr.bin/at/panic.h b/usr.bin/at/panic.h index 3b9c2ea..7f82f2d 100644 --- a/usr.bin/at/panic.h +++ b/usr.bin/at/panic.h @@ -1,7 +1,6 @@ -/* - * panic.h - header for at(1) - * Copyright (c) 1993 Thomas Koenig - * All rights reserved. +/* + * panic.h - header for at(1) + * Copyright (C) 1993 Thomas Koenig * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -22,11 +21,24 @@ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id: panic.h,v 1.1 1993/12/05 11:36:58 cgd Exp $ */ -void panic __P((char *a)); -void perr __P((char *a)); -void perr2 __P((char *a, char *b)); -void usage __P((void)); +#ifdef __FreeBSD__ +#define __NORETURN +#endif + +void +#ifdef __GNUC__ +__NORETURN +#endif +panic(char *a); +void +#ifdef __GNUC__ +__NORETURN +#endif +perr(char *a); +void +#ifdef __GNUC__ +__NORETURN +#endif +usage(void); diff --git a/usr.bin/at/parsetime.c b/usr.bin/at/parsetime.c index 30e9043..d73149a 100644 --- a/usr.bin/at/parsetime.c +++ b/usr.bin/at/parsetime.c @@ -1,10 +1,9 @@ /* - * parsetime.c - parse time for at(1) - * Copyright (C) 1993 Thomas Koenig + * parsetime.c - parse time for at(1) + * Copyright (C) 1993, 1994 Thomas Koenig * - * modifications for english-language times - * Copyright (C) 1993 David Parsons - * All rights reserved. + * modifications for english-language times + * Copyright (C) 1993 David Parsons * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,12 +28,14 @@ * at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS * /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]] \ * |NOON | |[TOMORROW] | - * |MIDNIGHT | |NUMBER [SLASH NUMBER [SLASH NUMBER]]| - * \TEATIME / \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ + * |MIDNIGHT | |[DAY OF WEEK] | + * \TEATIME / |NUMBER [SLASH NUMBER [SLASH NUMBER]]| + * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ */ /* System Headers */ + #include <sys/types.h> #include <errno.h> #include <stdio.h> @@ -43,6 +44,9 @@ #include <time.h> #include <unistd.h> #include <ctype.h> +#ifndef __FreeBSD__ +#include <getopt.h> +#endif /* Local headers */ @@ -58,51 +62,60 @@ enum { /* symbols */ MINUTES, HOURS, DAYS, WEEKS, NUMBER, PLUS, DOT, SLASH, ID, JUNK, JAN, FEB, MAR, APR, MAY, JUN, - JUL, AUG, SEP, OCT, NOV, DEC -}; + JUL, AUG, SEP, OCT, NOV, DEC, + SUN, MON, TUE, WED, THU, FRI, SAT + }; -/* - * parse translation table - table driven parsers can be your FRIEND! +/* parse translation table - table driven parsers can be your FRIEND! */ struct { char *name; /* token name */ int value; /* token id */ + int plural; /* is this plural? */ } Specials[] = { - { "midnight", MIDNIGHT }, /* 00:00:00 of today or tomorrow */ - { "noon", NOON }, /* 12:00:00 of today or tomorrow */ - { "teatime", TEATIME }, /* 16:00:00 of today or tomorrow */ - { "am", AM }, /* morning times for 0-12 clock */ - { "pm", PM }, /* evening times for 0-12 clock */ - { "tomorrow", TOMORROW }, /* execute 24 hours from time */ - { "today", TODAY }, /* execute today - don't advance time */ - { "now", NOW }, /* opt prefix for PLUS */ - - { "minute", MINUTES }, /* minutes multiplier */ - { "min", MINUTES }, - { "m", MINUTES }, - { "minutes", MINUTES }, /* (pluralized) */ - { "hour", HOURS }, /* hours ... */ - { "hr", HOURS }, /* abbreviated */ - { "h", HOURS }, - { "hours", HOURS }, /* (pluralized) */ - { "day", DAYS }, /* days ... */ - { "d", DAYS }, - { "days", DAYS }, /* (pluralized) */ - { "week", WEEKS }, /* week ... */ - { "w", WEEKS }, - { "weeks", WEEKS }, /* (pluralized) */ - { "jan", JAN }, - { "feb", FEB }, - { "mar", MAR }, - { "apr", APR }, - { "may", MAY }, - { "jun", JUN }, - { "jul", JUL }, - { "aug", AUG }, - { "sep", SEP }, - { "oct", OCT }, - { "nov", NOV }, - { "dec", DEC } + { "midnight", MIDNIGHT,0 }, /* 00:00:00 of today or tomorrow */ + { "noon", NOON,0 }, /* 12:00:00 of today or tomorrow */ + { "teatime", TEATIME,0 }, /* 16:00:00 of today or tomorrow */ + { "am", AM,0 }, /* morning times for 0-12 clock */ + { "pm", PM,0 }, /* evening times for 0-12 clock */ + { "tomorrow", TOMORROW,0 }, /* execute 24 hours from time */ + { "today", TODAY, 0 }, /* execute today - don't advance time */ + { "now", NOW,0 }, /* opt prefix for PLUS */ + + { "minute", MINUTES,0 }, /* minutes multiplier */ + { "minutes", MINUTES,1 }, /* (pluralized) */ + { "hour", HOURS,0 }, /* hours ... */ + { "hours", HOURS,1 }, /* (pluralized) */ + { "day", DAYS,0 }, /* days ... */ + { "days", DAYS,1 }, /* (pluralized) */ + { "week", WEEKS,0 }, /* week ... */ + { "weeks", WEEKS,1 }, /* (pluralized) */ + { "jan", JAN,0 }, + { "feb", FEB,0 }, + { "mar", MAR,0 }, + { "apr", APR,0 }, + { "may", MAY,0 }, + { "jun", JUN,0 }, + { "jul", JUL,0 }, + { "aug", AUG,0 }, + { "sep", SEP,0 }, + { "oct", OCT,0 }, + { "nov", NOV,0 }, + { "dec", DEC,0 }, + { "sunday", SUN, 0 }, + { "sun", SUN, 0 }, + { "monday", MON, 0 }, + { "mon", MON, 0 }, + { "tuesday", TUE, 0 }, + { "tue", TUE, 0 }, + { "wednesday", WED, 0 }, + { "wed", WED, 0 }, + { "thursday", THU, 0 }, + { "thu", THU, 0 }, + { "friday", FRI, 0 }, + { "fri", FRI, 0 }, + { "saturday", SAT, 0 }, + { "sat", SAT, 0 }, } ; /* File scope variables */ @@ -115,8 +128,9 @@ static int need; /* scanner - need to advance to next argument */ static char *sc_token; /* scanner - token buffer */ static size_t sc_len; /* scanner - lenght of token buffer */ static int sc_tokid; /* scanner - token id */ +static int sc_tokplur; /* scanner - is token plural? */ -static char rcsid[] = "$Id: parsetime.c,v 1.1 1994/01/05 01:09:08 nate Exp $"; +static char rcsid[] = "$Id: parsetime.c,v 1.1 1994/05/10 18:23:08 kernel Exp $"; /* Local functions */ @@ -124,13 +138,13 @@ static char rcsid[] = "$Id: parsetime.c,v 1.1 1994/01/05 01:09:08 nate Exp $"; * parse a token, checking if it's something special to us */ static int -parse_token(arg) - char *arg; +parse_token(char *arg) { int i; for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++) if (strcasecmp(Specials[i].name, arg) == 0) { + sc_tokplur = Specials[i].plural; return sc_tokid = Specials[i].value; } @@ -143,9 +157,7 @@ parse_token(arg) * init_scanner() sets up the scanner to eat arguments */ static void -init_scanner(argc, argv) - int argc; - char **argv; +init_scanner(int argc, char **argv) { scp = argv; scc = argc; @@ -154,9 +166,7 @@ init_scanner(argc, argv) while (--argc > 0) sc_len += strlen(*++argv); - sc_token = (char *) malloc(sc_len); - if (sc_token == NULL) - panic("Insufficient virtual memory"); + sc_token = (char *) mymalloc(sc_len); } /* init_scanner */ /* @@ -170,10 +180,10 @@ token() while (1) { memset(sc_token, 0, sc_len); sc_tokid = EOF; + sc_tokplur = 0; idx = 0; - /* - * if we need to read another argument, walk along the argument list; + /* if we need to read another argument, walk along the argument list; * when we fall off the arglist, we'll just return EOF forever */ if (need) { @@ -184,8 +194,7 @@ token() scc--; need = 0; } - /* - * eat whitespace now - if we walk off the end of the argument, + /* eat whitespace now - if we walk off the end of the argument, * we'll continue, which puts us up at the top of the while loop * to fetch the next argument in */ @@ -196,20 +205,19 @@ token() continue; } - /* - * preserve the first character of the new token + /* preserve the first character of the new token */ sc_token[0] = *sct++; - /* - * then see what it is + /* then see what it is */ if (isdigit(sc_token[0])) { while (isdigit(*sct)) sc_token[++idx] = *sct++; sc_token[++idx] = 0; return sc_tokid = NUMBER; - } else if (isalpha(sc_token[0])) { + } + else if (isalpha(sc_token[0])) { while (isalpha(*sct)) sc_token[++idx] = *sct++; sc_token[++idx] = 0; @@ -219,7 +227,7 @@ token() return sc_tokid = DOT; else if (sc_token[0] == '+') return sc_tokid = PLUS; - else if (*sct == '/') + else if (sc_token[0] == '/') return sc_tokid = SLASH; else return sc_tokid = JUNK; @@ -231,8 +239,7 @@ token() * plonk() gives an appropriate error message if a token is incorrect */ static void -plonk(tok) - int tok; +plonk(int tok) { panic((tok == EOF) ? "incomplete time" : "garbled time"); @@ -243,8 +250,7 @@ plonk(tok) * expect() gets a token and dies most horribly if it's not the token we want */ static void -expect(desired) - int desired; +expect(int desired) { if (token() != desired) plonk(sc_tokid); /* and we die here... */ @@ -257,9 +263,7 @@ expect(desired) * work properly */ static void -dateadd(minutes, tm) - int minutes; - struct tm *tm; +dateadd(int minutes, struct tm *tm) { /* increment days */ @@ -300,14 +304,15 @@ dateadd(minutes, tm) * */ static void -plus(tm) - struct tm *tm; +plus(struct tm *tm) { int delay; + int expectplur; expect(NUMBER); delay = atoi(sc_token); + expectplur = (delay != 1) ? 1 : 0; switch (token()) { case WEEKS: @@ -317,6 +322,8 @@ plus(tm) case HOURS: delay *= 60; case MINUTES: + if (expectplur != sc_tokplur) + fprintf(stderr, "at: pluralization is wrong\n"); dateadd(delay, tm); return; } @@ -329,8 +336,7 @@ plus(tm) * [NUMBER [DOT NUMBER] [AM|PM]] */ static void -tod(tm) - struct tm *tm; +tod(struct tm *tm) { int hour, minute = 0; int tlen; @@ -338,8 +344,7 @@ tod(tm) hour = atoi(sc_token); tlen = strlen(sc_token); - /* - * first pick out the time of day - if it's 4 digits, we assume + /* first pick out the time of day - if it's 4 digits, we assume * a HHMM time, otherwise it's HH DOT MM time */ if (token() == DOT) { @@ -348,15 +353,15 @@ tod(tm) if (minute > 59) panic("garbled time"); token(); - } else if (tlen == 4) { + } + else if (tlen == 4) { minute = hour%100; if (minute > 59) panic("garbeld time"); hour = hour/100; } - /* - * check if an AM or PM specifier was given + /* check if an AM or PM specifier was given */ if (sc_tokid == AM || sc_tokid == PM) { if (hour > 12) @@ -365,16 +370,18 @@ tod(tm) if (sc_tokid == PM) hour += 12; token(); - } else if (hour > 23) + } + else if (hour > 23) panic("garbled time"); - /* - * if we specify an absolute time, we don't want to bump the day even + /* if we specify an absolute time, we don't want to bump the day even * if we've gone past that time - but if we're specifying a time plus * a relative offset, it's okay to bump things */ - if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) + if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) { tm->tm_mday++; + tm->tm_wday++; + } tm->tm_hour = hour; tm->tm_min = minute; @@ -389,9 +396,7 @@ tod(tm) * assign_date() assigns a date, wrapping to next year if needed */ static void -assign_date(tm, mday, mon, year) - struct tm *tm; - long mday, mon, year; +assign_date(struct tm *tm, long mday, long mon, long year) { if (year > 99) { if (year > 1899) @@ -417,15 +422,15 @@ assign_date(tm, mday, mon, year) * * /[<month> NUMBER [NUMBER]] \ * |[TOMORROW] | + * |[DAY OF WEEK] | * |NUMBER [SLASH NUMBER [SLASH NUMBER]]| * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/ */ static void -month(tm) - struct tm *tm; +month(struct tm *tm) { long year= (-1); - long mday, mon; + long mday, wday, mon; int tlen; switch (sc_tokid) { @@ -436,14 +441,14 @@ month(tm) case TOMORROW: /* do something tomorrow */ tm->tm_mday ++; + tm->tm_wday ++; case TODAY: /* force ourselves to stay in today - no further processing */ token(); break; case JAN: case FEB: case MAR: case APR: case MAY: case JUN: case JUL: case AUG: case SEP: case OCT: case NOV: case DEC: - /* - * do month mday [year] + /* do month mday [year] */ mon = (sc_tokid-JAN); expect(NUMBER); @@ -455,9 +460,29 @@ month(tm) assign_date(tm, mday, mon, year); break; + case SUN: case MON: case TUE: + case WED: case THU: case FRI: + case SAT: + /* do a particular day of the week + */ + wday = (sc_tokid-SUN); + + mday = tm->tm_mday; + + /* if this day is < today, then roll to next week + */ + if (wday < tm->tm_wday) + mday += 7 - (tm->tm_wday - wday); + else + mday += (wday - tm->tm_wday); + + tm->tm_wday = wday; + + assign_date(tm, mday, tm->tm_mon, tm->tm_year); + break; + case NUMBER: - /* - * get numeric MMDDYY, mm/dd/yy, or dd.mm.yy + /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy */ tlen = strlen(sc_token); mon = atol(sc_token); @@ -475,25 +500,27 @@ month(tm) token(); } - /* - * flip months and days for european timing + /* flip months and days for european timing */ if (sep == DOT) { int x = mday; mday = mon; mon = x; } - } else if (tlen == 6 || tlen == 8) { + } + else if (tlen == 6 || tlen == 8) { if (tlen == 8) { year = (mon % 10000) - 1900; mon /= 10000; - } else { + } + else { year = mon % 100; mon /= 100; } mday = mon % 100; mon /= 100; - } else + } + else panic("garbled time"); mon--; @@ -509,12 +536,9 @@ month(tm) /* Global functions */ time_t -parsetime(argc, argv) - int argc; - char **argv; +parsetime(int argc, char **argv) { -/* - * Do the argument parsing, die if necessary, and return the time the job +/* Do the argument parsing, die if necessary, and return the time the job * should be run. */ time_t nowtimer, runtimer; @@ -546,8 +570,7 @@ parsetime(argc, argv) month(&runtime); break; - /* - * evil coding for TEATIME|NOON|MIDNIGHT - we've initialised + /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised * hr to zero up above, then fall into this case in such a * way so we add +12 +4 hours to it for teatime, +12 hours * to it for noon, and nothing at all for midnight, then @@ -559,8 +582,10 @@ parsetime(argc, argv) case NOON: hr += 12; case MIDNIGHT: - if (runtime.tm_hour >= hr) + if (runtime.tm_hour >= hr) { runtime.tm_mday++; + runtime.tm_wday++; + } runtime.tm_hour = hr; runtime.tm_min = 0; token(); @@ -571,8 +596,7 @@ parsetime(argc, argv) } /* ugly case statement */ expect(EOF); - /* - * adjust for daylight savings time + /* adjust for daylight savings time */ runtime.tm_isdst = -1; runtimer = mktime(&runtime); diff --git a/usr.bin/at/parsetime.h b/usr.bin/at/parsetime.h index 5df23f6..4696e82 100644 --- a/usr.bin/at/parsetime.h +++ b/usr.bin/at/parsetime.h @@ -1,7 +1,6 @@ -/* - * at.h - header for at(1) - * Copyright (c) 1993 by Thomas Koenig - * All rights reserved. +/* + * at.h - header for at(1) + * Copyright (C) 1993 Thomas Koenig * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -22,8 +21,6 @@ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id: parsetime.h,v 1.1 1993/12/05 11:37:17 cgd Exp $ */ -time_t parsetime __P((int argc, char **argv)); +time_t parsetime(int argc, char **argv); diff --git a/usr.bin/at/perm.c b/usr.bin/at/perm.c new file mode 100644 index 0000000..d2c8d02 --- /dev/null +++ b/usr.bin/at/perm.c @@ -0,0 +1,123 @@ +/* + * perm.c - check user permission for at(1) + * Copyright (C) 1994 Thomas Koenig + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* System Headers */ + +#include <sys/types.h> +#include <errno.h> +#include <pwd.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* Local headers */ + +#include "privs.h" +#include "at.h" + +/* Macros */ + +#define MAXUSERID 10 + +/* Structures and unions */ + + +/* File scope variables */ + +static char rcsid[] = "$Id: perm.c,v 1.1 1994/05/10 18:23:08 kernel Exp $"; + +/* Function declarations */ + +static int check_for_user(FILE *fp,const char *name); + +/* Local functions */ + +static int check_for_user(FILE *fp,const char *name) +{ + char *buffer; + size_t len; + int found = 0; + + len = strlen(name); + buffer = mymalloc(len+2); + + while(fgets(buffer, len+2, fp) != NULL) + { + if ((strncmp(name, buffer, len) == 0) && + (buffer[len] == '\n')) + { + found = 1; + break; + } + } + fclose(fp); + free(buffer); + return found; +} +/* Global functions */ +int check_permission() +{ + FILE *fp; + uid_t uid = geteuid(); + struct passwd *pentry; + + if (uid==0) + return 1; + + if ((pentry = getpwuid(uid)) == NULL) + { + perror("Cannot access user database"); + exit(EXIT_FAILURE); + } + + PRIV_START + + fp=fopen(PERM_PATH "at.allow","r"); + + PRIV_END + + if (fp != NULL) + { + return check_for_user(fp, pentry->pw_name); + } + else + { + + PRIV_START + + fp=fopen(PERM_PATH "at.deny", "r"); + + PRIV_END + + if (fp != NULL) + { + return !check_for_user(fp, pentry->pw_name); + } + perror("at.deny"); + } + return 0; +} diff --git a/usr.bin/at/perm.h b/usr.bin/at/perm.h new file mode 100644 index 0000000..a71e11c --- /dev/null +++ b/usr.bin/at/perm.h @@ -0,0 +1,26 @@ +/* + * perm.h - header for at(1) + * Copyright (C) 1994 Thomas Koenig + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +int check_permission(); diff --git a/usr.bin/at/privs.h b/usr.bin/at/privs.h index e1ad1b4..34fd690 100644 --- a/usr.bin/at/privs.h +++ b/usr.bin/at/privs.h @@ -1,7 +1,6 @@ -/* - * privs.h - header for privileged operations - * Copyright (c) 1993 by Thomas Koenig - * All rights reserved. +/* + * privs.h - header for privileged operations + * Copyright (C) 1993 Thomas Koenig * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -22,31 +21,44 @@ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id: privs.h,v 1.1 1993/12/05 11:37:29 cgd Exp $ */ #ifndef _PRIVS_H #define _PRIVS_H +#ifndef _USE_BSD +#define _USE_BSD 1 +#include <unistd.h> +#undef _USE_BSD +#else #include <unistd.h> +#endif -/* Relinquish privileges temporarily for a setuid program +#ifdef __FreeBSD__ +/* + * setre[ug]id() not change r[ug]id for FreeBSD, but check it incorrectly + * for this program + */ +#define setreuid(r, e) seteuid(e) +#define setregid(r, e) setegid(e) +#endif + +/* Relinquish privileges temporarily for a setuid or setgid program * with the option of getting them back later. This is done by swapping * the real and effective userid BSD style. Call RELINQUISH_PRIVS once * at the beginning of the main program. This will cause all operatons * to be executed with the real userid. When you need the privileges - * of the setuid invocation, call PRIV_START; when you no longer + * of the setuid/setgid invocation, call PRIV_START; when you no longer * need it, call PRIV_END. Note that it is an error to call PRIV_START * and not PRIV_END within the same function. * - * Use RELINQUISH_PRIVS_ROOT(a) if your program started out running + * Use RELINQUISH_PRIVS_ROOT(a,b) if your program started out running * as root, and you want to drop back the effective userid to a * and the effective group id to b, with the option to get them back * later. * * If you no longer need root privileges, but those of some other - * userid/groupid, you can call REDUCE_PRIV(a) when your effective + * userid/groupid, you can call REDUCE_PRIV(a,b) when your effective * is the user's. * * Problems: Do not use return between PRIV_START and PRIV_END; this @@ -65,28 +77,44 @@ extern #endif uid_t real_uid, effective_uid; +#ifndef MAIN +extern +#endif +gid_t real_gid, effective_gid; + #define RELINQUISH_PRIVS { \ - real_uid = getuid(); \ - effective_uid = geteuid(); \ - setreuid(effective_uid,real_uid); \ -} + real_uid = getuid(); \ + effective_uid = geteuid(); \ + real_gid = getgid(); \ + effective_gid = getegid(); \ + setregid(effective_gid, real_gid); \ + setreuid(effective_uid, real_uid); \ + } -#define RELINQUISH_PRIVS_ROOT(a) { \ - real_uid = (a); \ - effective_uid = geteuid(); \ - setreuid(effective_uid,real_uid); \ -} +#define RELINQUISH_PRIVS_ROOT(a,b) { \ + real_uid = (a); \ + effective_uid = geteuid(); \ + real_gid = (b); \ + effective_gid = getegid(); \ + setregid(effective_gid, real_gid); \ + setreuid(effective_uid, real_uid); \ + } -#define PRIV_START { \ - setreuid(real_uid,effective_uid); +#define PRIV_START {\ + setreuid(real_uid, effective_uid); \ + setregid(real_gid, effective_gid); #define PRIV_END \ - setreuid(effective_uid,real_uid); \ -} + setregid(effective_gid, real_gid); \ + setreuid(effective_uid, real_uid); \ + } -#define REDUCE_PRIV(a) { \ - setreuid(real_uid,effective_uid); \ - effective_uid = (a); \ - setreuid(effective_uid,real_uid); \ -} +#define REDUCE_PRIV(a,b) {\ + setreuid(real_uid, effective_uid); \ + setregid(real_gid, effective_gid); \ + effective_uid = (a); \ + effective_gid = (b); \ + setregid(effective_gid, real_gid); \ + setreuid(effective_uid, real_uid); \ + } #endif |