Submitted By: Jim Gifford (jim at linuxfromscratch dot org) Date: 2003-09-18 Initial Package Version: 3.1.8 Origin: Rawhide SRC RPM Description: Fixes known issues with the at daemon diff -Naur at-3.1.8.orig/Makefile.in at-3.1.8/Makefile.in --- at-3.1.8.orig/Makefile.in 2002-01-18 06:55:33.000000000 +0000 +++ at-3.1.8/Makefile.in 2003-09-19 01:17:27.000000000 +0000 @@ -51,6 +51,8 @@ OTHERS = parsetime.l parsetime.y +TEST_VERBOSE = 0 + DOCS = Problems Copyright README ChangeLog timespec MISC = COPYING Makefile.in configure acconfig.h install-sh \ @@ -87,11 +89,11 @@ $(CC) -c $(CFLAGS) $(DEFS) $*.c install: all - $(INSTALL) -g root -o root -m 755 -d $(IROOT)$(etcdir) - $(INSTALL) -g root -o root -m 755 -d $(IROOT)$(bindir) - $(INSTALL) -g root -o root -m 755 -d $(IROOT)$(sbindir) - $(INSTALL) -g root -o root -m 755 -d $(IROOT)$(docdir) - $(INSTALL) -g root -o root -m 755 -d $(IROOT)$(atdocdir) + $(INSTALL) -m 755 -d $(IROOT)$(etcdir) + $(INSTALL) -m 755 -d $(IROOT)$(bindir) + $(INSTALL) -m 755 -d $(IROOT)$(sbindir) + $(INSTALL) -m 755 -d $(IROOT)$(docdir) + $(INSTALL) -m 755 -d $(IROOT)$(atdocdir) $(INSTALL) -m 755 -d $(IROOT)$(ATJOB_DIR) $(INSTALL) -g $(DAEMON_GROUPNAME) -o $(DAEMON_USERNAME) -m 755 -d $(IROOT)$(ATSPOOL_DIR) chmod 700 $(IROOT)$(ATJOB_DIR) $(IROOT)$(ATSPOOL_DIR) @@ -99,25 +101,25 @@ touch $(IROOT)$(LFILE) chmod 600 $(IROOT)$(LFILE) chown $(DAEMON_USERNAME):$(DAEMON_GROUPNAME) $(IROOT)$(LFILE) - test -f $(IROOT)$(etcdir)/at.allow || test -f $(IROOT)$(etcdir)/at.deny || $(INSTALL) -o root -m 600 at.deny $(IROOT)$(etcdir)/ - $(INSTALL) -g root -o root -m 4755 -s at $(IROOT)$(bindir) + test -f $(IROOT)$(etcdir)/at.allow || test -f $(IROOT)$(etcdir)/at.deny || $(INSTALL) -m 600 at.deny $(IROOT)$(etcdir)/ + $(INSTALL) -m 4755 -s at $(IROOT)$(bindir) $(LN_S) -f at $(IROOT)$(bindir)/atq $(LN_S) -f at $(IROOT)$(bindir)/atrm - $(INSTALL) -g root -o root -m 755 batch $(IROOT)$(bindir) - $(INSTALL) -d -o root -g root -m 755 $(IROOT)$(man1dir) - $(INSTALL) -d -o root -g root -m 755 $(IROOT)$(man5dir) - $(INSTALL) -d -o root -g root -m 755 $(IROOT)$(man8dir) - $(INSTALL) -g root -o root -m 755 -s atd $(IROOT)$(sbindir) - $(INSTALL) -g root -o root -m 755 atrun $(IROOT)$(sbindir) - $(INSTALL) -g root -o root -m 644 at.1 $(IROOT)$(man1dir)/ + $(INSTALL) -m 755 batch $(IROOT)$(bindir) + $(INSTALL) -d -m 755 $(IROOT)$(man1dir) + $(INSTALL) -d -m 755 $(IROOT)$(man5dir) + $(INSTALL) -d -m 755 $(IROOT)$(man8dir) + $(INSTALL) -m 755 -s atd $(IROOT)$(sbindir) + $(INSTALL) -m 755 atrun $(IROOT)$(sbindir) + $(INSTALL) -m 644 at.1 $(IROOT)$(man1dir)/ cd $(IROOT)$(man1dir) && $(LN_S) -f at.1 atq.1 && $(LN_S) -f at.1 batch.1 && $(LN_S) -f at.1 atrm.1 - $(INSTALL) -g root -o root -m 644 atd.8 $(IROOT)$(man8dir)/ + $(INSTALL) -m 644 atd.8 $(IROOT)$(man8dir)/ sed "s,\$${exec_prefix},$(exec_prefix),g" tmpman - $(INSTALL) -g root -o root -m 644 tmpman $(IROOT)$(man8dir)/atrun.8 + $(INSTALL) -m 644 tmpman $(IROOT)$(man8dir)/atrun.8 rm -f tmpman - $(INSTALL) -g root -o root -m 644 at_allow.5 $(IROOT)$(man5dir)/ + $(INSTALL) -m 644 at_allow.5 $(IROOT)$(man5dir)/ cd $(IROOT)$(man5dir) && $(LN_S) -f at_allow.5 at_deny.5 - $(INSTALL) -g root -o root -m 644 $(DOCS) $(IROOT)$(atdocdir) + $(INSTALL) -m 644 $(DOCS) $(IROOT)$(atdocdir) rm -f $(IROOT)$(mandir)/cat1/at.1* $(IROOT)$(mandir)/cat1/batch.1* \ $(IROOT)$(mandir)/cat1/atq.1* rm -f $(IROOT)$(mandir)/cat1/atd.8* @@ -151,6 +153,9 @@ parsetest: lex.yy.c y.tab.c $(CC) -o parsetest $(CFLAGS) $(DEFS) -DTEST_PARSER -DNEED_YYWRAP lex.yy.c y.tab.c +test: parsetest + PERL_DL_NONLAZY=1 perl -e 'use Test::Harness qw(&runtests $$verbose); $$verbose=$(TEST_VERBOSE); runtests @ARGV;' test.pl + .depend: $(CSRCS) gcc $(CFLAGS) $(DEFS) -MM $(CSRCS) > .depend diff -Naur at-3.1.8.orig/at.1.in at-3.1.8/at.1.in --- at-3.1.8.orig/at.1.in 2002-01-18 07:44:26.000000000 +0000 +++ at-3.1.8/at.1.in 2003-09-19 01:17:35.000000000 +0000 @@ -1,4 +1,4 @@ -.Id $Id: at-3.1.8-fixes-1.patch,v 1.1 2003/10/05 21:39:07 jim Exp $ +.Id $Id: at.1.in,v 1.8 1997/09/28 20:00:25 ig25 Exp .TH AT 1 "Nov 1996" local "Linux Programmer's Manual" .SH NAME at, batch, atq, atrm \- queue, examine or delete jobs for later execution @@ -39,7 +39,10 @@ and .B batch read commands from standard input or a specified file which are to -be executed at a later time, using +be executed at a later time, using the shell set by the user's environment +variable +.BR SHELL +or .BR /bin/sh . .TP 8 .BR at @@ -117,7 +120,7 @@ .B at 1am tomorrow. .PP The exact definition of the time specification can be found in -.IR @prefix@/share/doc/at/timespec . +.IR @prefix@/share/doc/at-@VERSION@/timespec . .PP For both .BR at " and " batch , diff -Naur at-3.1.8.orig/at.c at-3.1.8/at.c --- at-3.1.8.orig/at.c 2002-01-18 04:15:27.000000000 +0000 +++ at-3.1.8/at.c 2003-09-19 01:17:35.000000000 +0000 @@ -199,7 +199,7 @@ fscanf(fid, "%5lx", &jobno); rewind(fid); } else { - fid = fopen(ATJOB_DIR "/.SEQ", "w"); + fid = fopen(LFILE, "w"); if (fid == NULL) return EOF; } @@ -231,6 +231,7 @@ pid_t pid; int istty; int kill_errno; + char *sh, *shell; /* Install the signal handler for SIGINT; terminate after removing the * spool file if necessary @@ -343,8 +344,11 @@ if (fpin == NULL) perr("Cannot open input file %.500s", atinput); } - fprintf(fp, "#!/bin/sh\n# atrun uid=%d gid=%d\n# mail %8s %d\n", - real_uid, real_gid, mailname, send_mail); + sh = (char *) getenv ("SHELL"); + shell = (sh ? sh : "/bin/sh"); + + fprintf(fp, "#!%s\n# atrun uid=%d gid=%d\n# mail %8s %d\n", + shell, real_uid, real_gid, mailname, send_mail); /* Write out the umask at the time of invocation */ @@ -357,24 +361,27 @@ */ for (atenv = environ; *atenv != NULL; atenv++) { int export = 1; - char *eqp; + char *pch; + char * eqp = *atenv; + char * valp = *atenv; - eqp = strchr(*atenv, '='); - if (ap == NULL) - eqp = *atenv; - else { + if ((pch = strchr(*atenv, '=')) != 0) { unsigned int i; + + eqp = pch; + valp = pch + 1; 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++; - } + } else { + continue; /* no '=', so bail on this one */ + } if (export) { - fwrite(*atenv, sizeof(char), eqp - *atenv, fp); - for (ap = eqp; *ap != '\0'; ap++) { + fwrite(*atenv, sizeof(char), valp - *atenv, fp); + for (ap = valp; *ap != '\0'; ap++) { if (*ap == '\n') fprintf(fp, "\"\n\""); else { @@ -405,7 +412,7 @@ } } fputs("; export ", fp); - fwrite(*atenv, sizeof(char), eqp - *atenv - 1, fp); + fwrite(*atenv, sizeof(char), eqp - *atenv, fp); fputc('\n', fp); } @@ -851,10 +858,11 @@ /* POSIX.2 allows the shell specified by the user's SHELL environment variable, the login shell from the user's password database entry, or /bin/sh to be the command interpreter that processes the at-job. - It also alows a warning diagnostic to be printed. Because of the + It also allows a warning diagnostic to be printed. Because of the possible variance, we always output the diagnostic. */ - fprintf(stderr, "warning: commands will be executed using /bin/sh\n"); +/* fprintf(stderr, "warning: commands will be executed using /bin/sh\n"); + */ writefile(timer, queue); break; diff -Naur at-3.1.8.orig/atd.c at-3.1.8/atd.c --- at-3.1.8.orig/atd.c 2002-01-18 04:15:27.000000000 +0000 +++ at-3.1.8/atd.c 2003-09-19 01:17:35.000000000 +0000 @@ -113,7 +113,7 @@ static char rcsid[] = "$Id: at-3.1.8-fixes-1.patch,v 1.1 2003/10/05 21:39:07 jim Exp $"; static double load_avg = LOADAVG_MX; static time_t now; -static time_t last_chg; +//static time_t last_chg; static int nothing_to_do; unsigned int batch_interval; static int run_as_daemon = 0; @@ -188,7 +188,7 @@ #endif static void -run_file(const char *filename, uid_t uid, gid_t gid) +run_file(char *filename, uid_t uid, gid_t gid) { /* Run a file by by spawning off a process which redirects I/O, * spawns a subshell, then waits for it to complete and sends @@ -196,9 +196,9 @@ */ pid_t pid; int fd_out, fd_in; - char mailbuf[9], jobbuf[9]; + char mailbuf[256], jobbuf[9]; char *mailname = NULL; - char *newname; + char newname[256]; FILE *stream; int send_mail = 0; struct stat buf, lbuf; @@ -209,17 +209,24 @@ int ngid; char queue; unsigned long jobno; + char *shell; sscanf(filename, "%c%5lx", &queue, &jobno); sprintf(jobbuf, "%8lu", jobno); - if ((newname = malloc(strlen(filename) + 1)) == NULL) - pabort("Job %8lu : out of virtual memory", jobno); + if( strlen( filename ) >= sizeof( newname ) - 1 ) + pabort("File name too long: %s", filename ); strcpy(newname, filename); + + newname[0] = '!'; + + if( rename( filename, newname ) < 0 ) + perr( "Error renaming job file." ); - newname[0] = '='; + filename[0] = '!'; + newname[0] = '='; /* We try to make a hard link to lock the file. If we fail, then * somebody else has already locked it (a second atd?); log the @@ -234,13 +241,15 @@ } } /* If something goes wrong between here and the unlink() call, - * the job gets restarted as soon as the "=" entry is cleared - * by the main atd loop. - */ + * the job will remain in the "!" queue. + * no point in retrying, and need glaring proof that something went wrong + */ pid = fork(); - if (pid == -1) + if (pid == -1) { + unlink( newname ); perr("Cannot fork"); + } else if (pid != 0) { return; @@ -252,6 +261,7 @@ pentry = getpwuid(uid); if (pentry == NULL) { + unlink( newname ); pabort("Userid %lu not found - aborting job %8lu (%.500s)", (unsigned long) uid, jobno, filename); } @@ -261,34 +271,49 @@ PRIV_END - if (stream == NULL) + if (stream == NULL) { + unlink( newname ); perr("Cannot open input file"); + } - if ((fd_in = dup(fileno(stream))) < 0) + if ((fd_in = dup(fileno(stream))) < 0) { + unlink( newname ); perr("Error duplicating input file descriptor"); + } - if (fstat(fd_in, &buf) == -1) + if (fstat(fd_in, &buf) == -1) { + unlink( newname ); perr("Error in fstat of input file descriptor"); + } - if (lstat(filename, &lbuf) == -1) + if (lstat(filename, &lbuf) == -1) { + unlink( newname ); perr("Error in fstat of input file"); + } - if (S_ISLNK(lbuf.st_mode)) + if (S_ISLNK(lbuf.st_mode)) { + unlink( newname ); perr("Symbolic link encountered in job %8lu (%.500s) - aborting", jobno, filename); + } if ((lbuf.st_dev != buf.st_dev) || (lbuf.st_ino != buf.st_ino) || (lbuf.st_uid != buf.st_uid) || (lbuf.st_gid != buf.st_gid) || - (lbuf.st_size != buf.st_size)) + (lbuf.st_size != buf.st_size)) { + unlink( newname ); perr("Somebody changed files from under us for job %8lu (%.500s) - " "aborting", jobno, filename); + } if (buf.st_nlink > 2) { - perr("Someboy is trying to run a linked script for job %8lu (%.500s)", + unlink( newname ); + perr("Somebody is trying to run a linked script for job %8lu (%.500s)", filename); } - if ((fflags = fcntl(fd_in, F_GETFD)) < 0) + if ((fflags = fcntl(fd_in, F_GETFD)) < 0) { + unlink( newname ); perr("Error in fcntl"); + } fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC); @@ -299,33 +324,48 @@ * NFS and works with local file systems. It's not clear where * the bug is located. -Joey */ - if (fscanf(stream, "#!/bin/sh\n# atrun uid=%d gid=%d\n# mail %8s %d", - &nuid, &ngid, mailbuf, &send_mail) != 4) + if (fscanf(stream, "#!%as\n# atrun uid=%d gid=%d\n# mail %255s %d", + &shell, &nuid, &ngid, mailbuf, &send_mail) != 5) { + unlink( newname ); pabort("File %.500s is in wrong format - aborting", filename); + } + mailbuf[255] = '\0'; - if (mailbuf[0] == '-') + if (mailbuf[0] == '-') { + unlink( newname ); pabort("illegal mail name %.300s in job %8lu (%.300s)", mailbuf, jobno, filename); + } mailname = mailbuf; - if (nuid != uid) + if (nuid != uid) { + unlink( newname ); pabort("Job %8lu (%.500s) - userid %d does not match file uid %d", jobno, filename, nuid, uid); + } - if (ngid != gid) + if (ngid != gid) { + unlink( newname ); pabort("Job %8lu %.500s - groupid %d does not match file gid %d", jobno, filename, ngid, gid); + } /* We are now committed to executing this script. Unlink the * original. */ unlink(filename); + + /* If we bail out from now on, the job gets stuck in "=" + * The main loop should take care of that. + */ fclose(stream); if (chdir(ATSPOOL_DIR) < 0) perr("Cannot chdir to " ATSPOOL_DIR); + + filename[0] = queue; /* Create a file to hold the output of the job we are about to run. * Write the mail header. Complain in case @@ -394,10 +434,15 @@ if (setuid(uid) < 0) perr("Cannot set user id"); + if (SIG_ERR == signal(SIGCHLD, SIG_DFL)) + perr("Cannot reset signal handler to default"); + chdir("/"); - if (execle("/bin/sh", "sh", (char *) NULL, nenvp) != 0) - perr("Exec failed for /bin/sh"); + if (execle(shell, shell, (char *) NULL, nenvp) != 0) + perr("Exec failed for %s", shell); + + free (shell); PRIV_END } @@ -413,19 +458,21 @@ waitpid(pid, (int *) NULL, 0); /* Send mail. Unlink the output file after opening it, so it - * doesn't hang around after the run. + * doesn't hang around after the run (if we are to send mail) */ - stat(filename, &buf); - if (open(filename, O_RDONLY) != STDIN_FILENO) - perr("Open of jobfile failed"); + + if( send_mail != -1 ) { + stat(filename, &buf); + if (open(filename, O_RDONLY) != STDIN_FILENO) + perr("Open of jobfile failed"); - unlink(filename); + unlink(filename); + } /* The job is now finished. We can delete its input file. */ chdir(ATJOB_DIR); unlink(newname); - free(newname); if (((send_mail != -1) && (buf.st_size != size)) || (send_mail == 1)) { @@ -449,7 +496,8 @@ #elif defined(MAILX) execl(MAILX, "mailx", mailname, (char *) NULL); #else -#error "No mail command specified." +/*#error "No mail command specified."*/ + perr("No mail command specified."); #endif perr("Exec failed for mail command"); @@ -458,6 +506,8 @@ exit(EXIT_SUCCESS); } +#define CHECK_INTERVAL_5MIN 300 + static time_t run_loop() { @@ -487,7 +537,7 @@ * atrun. */ - next_job = now + CHECK_INTERVAL; + next_job = now + CHECK_INTERVAL_5MIN; if (next_batch == 0) next_batch = now; @@ -498,11 +548,11 @@ if (stat(".", &buf) == -1) perr("Cannot stat " ATJOB_DIR); - +/* if (nothing_to_do && buf.st_mtime <= last_chg) return next_job; last_chg = buf.st_mtime; - +*/ if ((spool = opendir(".")) == NULL) perr("Cannot read " ATJOB_DIR); @@ -558,6 +608,7 @@ * Let's remove the lockfile and reschedule. */ strncpy(lock_name, dirent->d_name, sizeof(lock_name)); + lock_name[sizeof(lock_name)-1] = '\0'; lock_name[0] = '='; unlink(lock_name); next_job = now; @@ -574,7 +625,7 @@ nothing_to_do = 0; /* There's a job for later. Note its execution time if it's - * the earlierst so far. + * the earliest so far. */ if (run_time > now) { if (next_job > run_time) { @@ -592,6 +643,7 @@ run_batch++; if (strcmp(batch_name, dirent->d_name) > 0) { strncpy(batch_name, dirent->d_name, sizeof(batch_name)); + batch_name[sizeof(batch_name)-1] = '\0'; batch_uid = buf.st_uid; batch_gid = buf.st_gid; batch_queue = queue; @@ -628,7 +680,7 @@ int main(int argc, char *argv[]) { -/* Browse through ATJOB_DIR, checking all the jobfiles wether they should +/* Browse through ATJOB_DIR, checking all the jobfiles whether they should * be executed and or deleted. The queue is coded into the first byte of * the job filename, the date (in minutes since Eon) as a hex number in the * following eight bytes, followed by a dot and a serial number. A file diff -Naur at-3.1.8.orig/atrun.8.in at-3.1.8/atrun.8.in --- at-3.1.8.orig/atrun.8.in 2002-01-18 04:15:27.000000000 +0000 +++ at-3.1.8/atrun.8.in 2003-09-19 01:17:36.000000000 +0000 @@ -11,7 +11,7 @@ .B atrun runs jobs queued by .BR at(1) . -It is a shell script containing invoking +It is a shell script invoking .B @sbindir@/atd with the .I -s diff -Naur at-3.1.8.orig/batch.in at-3.1.8/batch.in --- at-3.1.8.orig/batch.in 1997-03-12 23:57:40.000000000 +0000 +++ at-3.1.8/batch.in 2003-09-19 01:17:25.000000000 +0000 @@ -1,4 +1,42 @@ #! /bin/sh prefix=@prefix@ exec_prefix=@exec_prefix@ -exec @bindir@/at -qb now "$@" + +# Set defaults +OPT_f="" +OPT_m="" +OPT_q="-q b" +OPT_v="" +OPT_V="" +time_date_arg="now" + +# Note that we use `"$@"' to let each command-line parameter expand to a +# separate word. The quotes around `$@' are essential! +# We need TEMP as the `eval set --' would nuke the return value of getopt. +TEMP=`@GETOPT@ 'f:mq:vV' "$@"` + +if [ $? != 0 ] ; then + echo "batch: error parsing command-line arguments" >&2 + exit 1 +fi + +# Note the quotes around `$TEMP': they are essential! +eval set -- "$TEMP" + +while true ; do + case "$1" in + -f) OPT_f="-f $2" ; shift 2 ;; + -m) OPT_m="-m"; shift ;; + -q) OPT_q="-q $2"; shift 2 ;; + -v) OPT_v="-v"; shift ;; + -V) OPT_V="-V"; shift ;; + --) shift ; break ;; + *) echo "batch: internal parsing error" >&2 ; exit 1 ;; + esac +done + +if [ ! -z "$*" ] ; then + time_date_arg="$*" +fi + +exec @bindir@/at $OPT_f $OPT_m $OPT_q $OPT_v $OPT_V $time_date_arg diff -Naur at-3.1.8.orig/configure.in at-3.1.8/configure.in --- at-3.1.8.orig/configure.in 2002-01-18 08:07:02.000000000 +0000 +++ at-3.1.8/configure.in 2003-09-19 01:17:25.000000000 +0000 @@ -90,6 +90,11 @@ MAIL_CMD="$ac_cv_path_SENDMAIL" fi +AC_PATH_PROG(GETOPT, getopt, , $PATH:/bin:/usr/bin:/usr/local/bin ) +if test "$ac_cv_path_GETOPT" != "" ; then +AC_DEFINE_UNQUOTED(GETOPT,"$ac_cv_path_GETOPT") +fi + AC_SUBST(MAIL_CMD) AC_MSG_CHECKING(etcdir) diff -Naur at-3.1.8.orig/parsetime.l at-3.1.8/parsetime.l --- at-3.1.8.orig/parsetime.l 1997-09-28 18:26:30.000000000 +0000 +++ at-3.1.8/parsetime.l 2003-09-19 01:17:26.000000000 +0000 @@ -32,7 +32,6 @@ } while(0) %} -WORD [a-z][a-z0-9]+ %% now { COPY_TOK ; return NOW; } @@ -57,6 +56,7 @@ day(s)? { COPY_TOK ; return DAY; } week(s)? { COPY_TOK ; return WEEK; } month(s)? { COPY_TOK ; return MONTH; } +year(s)? { COPY_TOK ; return YEAR; } jan(uary)? { COPY_TOK ; return JAN; } feb(ruary)? { COPY_TOK ; return FEB; } mar(ch)? { COPY_TOK ; return MAR; } @@ -69,9 +69,16 @@ oct(ober)? { COPY_TOK ; return OCT; } nov(ember)? { COPY_TOK ; return NOV; } dec(ember)? { COPY_TOK ; return DEC; } +utc { COPY_TOK ; return UTC; } +[0-9]{1} { COPY_TOK ; COPY_VAL; return INT1DIGIT; } +[0-9]{2} { COPY_TOK ; COPY_VAL; return INT2DIGIT; } +[0-9]{4} { COPY_TOK ; COPY_VAL; return INT4DIGIT; } +[0-9]{5,8} { COPY_TOK ; COPY_VAL; return INT5_8DIGIT; } [0-9]+ { COPY_TOK ; COPY_VAL; return INT; } +[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{2}([0-9]{2})? { COPY_TOK ; COPY_VAL; return DOTTEDDATE; } +[0-9]{2}([0-9]{2})?-[0-9]{1,2}-[0-9]{1,2} { COPY_TOK ; COPY_VAL; return HYPHENDATE; } +[012]?[0-9][:'h,.][0-9]{2} { COPY_TOK ; COPY_VAL; return HOURMIN; } [ \t\n] ; -{WORD} { COPY_TOK ; COPY_VAL; return WORD; } . { COPY_TOK ; return yytext[0]; } %% diff -Naur at-3.1.8.orig/parsetime.y at-3.1.8/parsetime.y --- at-3.1.8.orig/parsetime.y 2002-01-18 04:15:27.000000000 +0000 +++ at-3.1.8/parsetime.y 2003-09-19 01:17:26.000000000 +0000 @@ -22,6 +22,13 @@ int intval; } +%token DOTTEDDATE +%token HYPHENDATE +%token HOURMIN +%token INT1DIGIT +%token INT2DIGIT +%token INT4DIGIT +%token INT5_8DIGIT %token INT %token NOW %token AM PM @@ -31,48 +38,61 @@ %token NEXT %token MINUTE HOUR DAY WEEK MONTH YEAR %token JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC -%token WORD +%token UTC -%type inc_period -%type inc_number +%type concatenated_date +%type hr24clock_hr_min +%type int1_2digit +%type int2_or_4digit +%type integer +%type inc_dec_period +%type inc_dec_number %type day_of_week %start timespec %% -timespec : date +timespec : spec_base + | spec_base inc_or_dec + { + time_only = 0; + + } + ; + +spec_base : date | time { time_only = 1; } | time date - | time_or_not inc_or_dec - | time_or_not date inc_or_dec - | nowspec - ; + | NOW + ; -nowspec : now - | now inc_or_dec +time : time_base + | time_base timezone_name ; -now : NOW - | TOMORROW - { - add_date(1, DAY); - } - ; - -time_or_not : time - | +time_base : hr24clock_hr_min + { + exectm.tm_min = -1; + exectm.tm_hour = -1; + sscanf($1, "%2d %2d", &exectm.tm_hour, + &exectm.tm_min); + free($1); -time : hr24clock_hr_min - | hr24clock_hr_min timezone_name - | hr24clock_hour time_sep minute - | hr24clock_hour time_sep minute timezone_name - | hr24clock_hour am_pm - | hr24clock_hour am_pm timezone_name - | hr24clock_hour time_sep minute am_pm - | hr24clock_hour time_sep minute am_pm timezone_name - | NOON + if (exectm.tm_min > 60 || exectm.tm_min < 0) { + yyerror("Problem in minutes specification"); + YYERROR; + } + if (exectm.tm_hour > 24 || exectm.tm_hour < 0) { + yyerror("Problem in hours specification"); + YYERROR; + } + } + | time_hour am_pm + | time_hour_min + | time_hour_min am_pm + | NOON { exectm.tm_hour = 12; exectm.tm_min = 0; @@ -81,128 +101,23 @@ { exectm.tm_hour = 0; exectm.tm_min = 0; - add_date(1, DAY); } | TEATIME { exectm.tm_hour = 16; exectm.tm_min = 0; } - ; - -date : month_name day_number - | month_name day_number year_number - | month_name day_number ',' year_number - | day_of_week - { - add_date ((6 + $1 - exectm.tm_wday) %7 + 1, DAY); - } - | TODAY - | TOMORROW - { - add_date(1, DAY); - } - | year_number '-' month_number '-' day_number - | day_number '.' month_number '.' year_number - | day_number '.' month_number - | day_number month_name - | day_number month_name year_number - | month_number '/' day_number '/' year_number - ; - -inc_or_dec : increment - | decrement - -increment : '+' inc_number inc_period - { - add_date($2, $3); - } - | NEXT inc_period - { - add_date(1, $2); - } - | NEXT day_of_week - { - add_date ((6 + $2 - exectm.tm_wday) %7 +1, DAY); - } - ; - -decrement : '-' inc_number inc_period - { - add_date(-$2, $3); - } ; -inc_period : MINUTE { $$ = MINUTE ; } - | HOUR { $$ = HOUR ; } - | DAY { $$ = DAY ; } - | WEEK { $$ = WEEK ; } - | MONTH { $$ = MONTH ; } - | YEAR { $$ = YEAR ; } - ; +hr24clock_hr_min: INT4DIGIT + ; -hr24clock_hr_min: INT +time_hour : int1_2digit { - if (strlen($1) == 4) { - exectm.tm_min = -1; - exectm.tm_hour = -1; - sscanf($1, "%2d %2d", &exectm.tm_hour, - &exectm.tm_min); - } else if (strlen($1) >= 5 && strlen($1) <= 8) { - /* Ok, this is a kluge. I hate design errors... -Joey */ - char shallot[5]; - char *onion; - - onion=$1; - memset (shallot, 0, sizeof (shallot)); - if (strlen($1) == 5 || strlen($1) == 7) { - strncpy (shallot,onion,1); - onion++; - } else { - strncpy (shallot,onion,2); - onion+=2; - } - sscanf(shallot, "%d", &exectm.tm_mon); - - if (exectm.tm_mon < 1 || exectm.tm_mon > 12) { - yyerror("Error in month number"); - YYERROR; - } - exectm.tm_mon--; - - memset (shallot, 0, sizeof (shallot)); - strncpy (shallot,onion,2); - sscanf(shallot, "%d", &exectm.tm_mday); - if (exectm.tm_mday < 0 || exectm.tm_mday > 31) - { - yyerror("Error in day of month"); - YYERROR; - } - - onion+=2; - memset (shallot, 0, sizeof (shallot)); - strncpy (shallot,onion,4); - if ( sscanf(shallot, "%d", &exectm.tm_year) != 1) { - yyerror("Error in year"); - YYERROR; - } - if (exectm.tm_year < 70) { - exectm.tm_year += 100; - } - else if (exectm.tm_year > 1900) { - exectm.tm_year -= 1900; - } - } - else { - sscanf($1, "%d", &exectm.tm_hour); - exectm.tm_min = 0; - } + sscanf($1, "%d", &exectm.tm_hour); + exectm.tm_min = 0; free($1); - if (exectm.tm_min > 60 || exectm.tm_min < 0) { - yyerror("Problem in minutes specification"); - YYERROR; - } if (exectm.tm_hour > 24 || exectm.tm_hour < 0) { yyerror("Problem in hours specification"); YYERROR; @@ -210,29 +125,22 @@ } ; -timezone_name : WORD +time_hour_min : HOURMIN { - if (strcasecmp($1,"utc") == 0) { - isgmt = 1; - } - else { - yyerror("Only UTC timezone is supported"); - YYERROR; - } + exectm.tm_min = -1; + exectm.tm_hour = -1; + sscanf($1, "%d %*c %d", &exectm.tm_hour, + &exectm.tm_min); free($1); - } - ; - -hr24clock_hour : hr24clock_hr_min - ; -minute : INT - { - if (sscanf($1, "%d", &exectm.tm_min) != 1) { - yyerror("Error in minute"); + if (exectm.tm_min > 60 || exectm.tm_min < 0) { + yyerror("Problem in minutes specification"); + YYERROR; + } + if (exectm.tm_hour > 24 || exectm.tm_hour < 0) { + yyerror("Problem in hours specification"); YYERROR; } - free($1); } ; @@ -243,7 +151,7 @@ YYERROR; } else if (exectm.tm_hour == 12) { - exectm.tm_hour = 0; + exectm.tm_hour -=12; } } | PM @@ -258,6 +166,171 @@ } ; +timezone_name : UTC + { + isgmt = 1; + } + ; + +date : month_name day_number + | month_name day_number year_number + | month_name day_number ',' year_number + | day_of_week + { + add_date ((6 + $1 - exectm.tm_wday) %7 + 1, DAY); + } + | TODAY + | TOMORROW + { + add_date(1, DAY); + } + | HYPHENDATE + { + int ynum = -1; + int mnum = -1; + int dnum = -1; + + if (sscanf($1, "%d %*c %d %*c %d", &ynum, &mnum, &dnum) != 3) { + yyerror("Error in hypenated date"); + YYERROR; + } + + if (mnum < 1 || mnum > 12) { + yyerror("Error in month number"); + YYERROR; + } + exectm.tm_mon = mnum -1; + + if (ynum < 70) { + ynum += 100; + } + else if (ynum > 1900) { + ynum -= 1900; + } + exectm.tm_year = ynum ; + + if ( dnum < 0 + || ((mnum == 1 || mnum == 3 || mnum == 5 || + mnum == 7 || mnum == 8 || mnum == 10 || + mnum == 12) && dnum > 31) + || ((mnum == 4 || mnum == 6 || mnum == 9 || + mnum == 11) && dnum > 30) + || (mnum == 2 && dnum > 29 && __isleap(ynum+1900)) + || (mnum == 2 && dnum > 28 && !__isleap(ynum+1900)) + ) + { + yyerror("Error in day of month"); + YYERROR; + } + exectm.tm_mday = dnum; + + free($1); + } + | DOTTEDDATE + { + int ynum = -1; + int mnum = -1; + int dnum = -1; + + if (sscanf($1, "%d %*c %d %*c %d", &dnum, &mnum, &ynum) != 3) { + yyerror("Error in dotted date"); + YYERROR; + } + + if (mnum < 1 || mnum > 12) { + yyerror("Error in month number"); + YYERROR; + } + exectm.tm_mon = mnum -1; + + if (ynum < 70) { + ynum += 100; + } + else if (ynum > 1900) { + ynum -= 1900; + } + exectm.tm_year = ynum ; + + if ( dnum < 0 + || ((mnum == 1 || mnum == 3 || mnum == 5 || + mnum == 7 || mnum == 8 || mnum == 10 || + mnum == 12) && dnum > 31) + || ((mnum == 4 || mnum == 6 || mnum == 9 || + mnum == 11) && dnum > 30) + || (mnum == 2 && dnum > 29 && __isleap(ynum+1900)) + || (mnum == 2 && dnum > 28 && !__isleap(ynum+1900)) + ) + { + yyerror("Error in day of month"); + YYERROR; + } + exectm.tm_mday = dnum; + + free($1); + } + | day_number month_name + | day_number month_name year_number + | month_number '/' day_number '/' year_number + | concatenated_date + { + /* Ok, this is a kluge. I hate design errors... -Joey */ + char shallot[5]; + char *onion; + + onion=$1; + memset (shallot, 0, sizeof (shallot)); + if (strlen($1) == 5 || strlen($1) == 7) { + strncpy (shallot,onion,1); + onion++; + } else { + strncpy (shallot,onion,2); + onion+=2; + } + sscanf(shallot, "%d", &exectm.tm_mon); + + if (exectm.tm_mon < 1 || exectm.tm_mon > 12) { + yyerror("Error in month number"); + YYERROR; + } + exectm.tm_mon--; + + memset (shallot, 0, sizeof (shallot)); + strncpy (shallot,onion,2); + sscanf(shallot, "%d", &exectm.tm_mday); + if (exectm.tm_mday < 0 || exectm.tm_mday > 31) + { + yyerror("Error in day of month"); + YYERROR; + } + + onion+=2; + memset (shallot, 0, sizeof (shallot)); + strncpy (shallot,onion,4); + if ( sscanf(shallot, "%d", &exectm.tm_year) != 1) { + yyerror("Error in year"); + YYERROR; + } + if (exectm.tm_year < 70) { + exectm.tm_year += 100; + } + else if (exectm.tm_year > 1900) { + exectm.tm_year -= 1900; + } + + free ($1); + } + | NEXT inc_dec_period + { + add_date(1, $2); + } + | NEXT day_of_week + { + add_date ((6 + $2 - exectm.tm_wday) %7 +1, DAY); + } + ; + +concatenated_date: INT5_8DIGIT + ; month_name : JAN { exectm.tm_mon = 0; } | FEB { exectm.tm_mon = 1; } @@ -273,7 +346,7 @@ | DEC { exectm.tm_mon =11; } ; -month_number : INT +month_number : int1_2digit { { int mnum = -1; @@ -287,7 +360,9 @@ free($1); } } -day_number : INT + ; + +day_number : int1_2digit { exectm.tm_mday = -1; sscanf($1, "%d", &exectm.tm_mday); @@ -300,7 +375,7 @@ } ; -year_number : INT +year_number : int2_or_4digit { { int ynum; @@ -322,7 +397,6 @@ } ; - day_of_week : SUN { $$ = 0; } | MON { $$ = 1; } | TUE { $$ = 2; } @@ -332,7 +406,23 @@ | SAT { $$ = 6; } ; -inc_number : INT +inc_or_dec : increment + | decrement + ; + +increment : '+' inc_dec_number inc_dec_period + { + add_date($2, $3); + } + ; + +decrement : '-' inc_dec_number inc_dec_period + { + add_date(-$2, $3); + } + ; + +inc_dec_number : integer { if (sscanf($1, "%d", &$$) != 1) { yyerror("Unknown increment"); @@ -342,11 +432,27 @@ } ; -time_sep : ':' - | '\'' - | '.' - | 'h' - | ',' +inc_dec_period : MINUTE { $$ = MINUTE ; } + | HOUR { $$ = HOUR ; } + | DAY { $$ = DAY ; } + | WEEK { $$ = WEEK ; } + | MONTH { $$ = MONTH ; } + | YEAR { $$ = YEAR ; } + ; + +int1_2digit : INT1DIGIT + | INT2DIGIT + ; + +int2_or_4digit : INT2DIGIT + | INT4DIGIT + ; + +integer : INT + | INT1DIGIT + | INT2DIGIT + | INT4DIGIT + | INT5_8DIGIT ; %% @@ -370,10 +476,7 @@ if (exectime == (time_t)-1) return 0; if (isgmt) { - exectime += timezone; - if (daylight) { - exectime -= 3600; - } + exectime -= timezone; } if (time_only && (currtime > exectime)) { exectime += 24*3600; @@ -386,39 +489,29 @@ } #ifdef TEST_PARSER -/* - -Here are some lines to test: - -./parsetest 7AM Mar 24 2000 -./parsetest 7AM Mar 24 00 -./parsetest 7AM 032400 -./parsetest 7AM 03/24/00 -./parsetest 7AM 24.03.00 -./parsetest 7AM Mar 24 - -./parsetest 03242000 -./parsetest noon 03242000 -./parsetest 5:30 -./parsetest 4pm + 3 days -./parsetest 10am Jul 31 - - */ + int main(int argc, char **argv) { + int retval = 1; time_t res; res = parsetime(argc-1, &argv[1]); + if (time_only) { + fprintf(stderr, "time_only = 1\n"); + } if (res > 0) { printf("%s",ctime(&res)); + retval = 0; } else { printf("Ooops...\n"); + retval = 1; } - return 0; + return retval; } #endif + int yyerror(char *s) { if (last_token == NULL) @@ -430,12 +523,36 @@ void add_seconds(struct tm *tm, long numsec) { + struct tm basetm = *tm; time_t timeval; + timeval = mktime(tm); if (timeval == (time_t)-1) timeval = (time_t)0; timeval += numsec; *tm = *localtime(&timeval); + + /* + * Adjust +-1 hour when moving in or out of DST + */ + + if (daylight > 0) /* Only check if DST is used here */ + { + /* Set tm_isdst on &basetm and tm */ + (void) mktime(&basetm); + (void) mktime(tm); + + if (basetm.tm_isdst > 0 && tm->tm_isdst < 1) + { /* DST to no DST */ + timeval += 3600l; + *tm = *localtime(&timeval); + } + else if (basetm.tm_isdst < 1 && tm->tm_isdst > 0) + { /* no DST to DST */ + timeval -= 3600l; + *tm = *localtime(&timeval); + } + } } int @@ -468,6 +585,10 @@ } exectm.tm_mon = newmonth % 12; number += newmonth / 12 ; + + /* Recalculate tm_isdst so we don't get a +-1 hour creep */ + exectm.tm_isdst = -1; + (void) mktime(&exectm); } if (number == 0) { break; @@ -476,6 +597,9 @@ case YEAR: exectm.tm_year += number; + /* Recalculate tm_isdst so we don't get a +-1 hour creep */ + exectm.tm_isdst = -1; + (void) mktime(&exectm); break; default: @@ -483,5 +607,6 @@ fprintf(stderr,"Unexpected case %d\n", period); abort(); } + return 0; } diff -Naur at-3.1.8.orig/timespec at-3.1.8/timespec --- at-3.1.8.orig/timespec 1997-03-12 18:11:05.000000000 +0000 +++ at-3.1.8/timespec 2003-09-19 01:17:26.000000000 +0000 @@ -2,6 +2,13 @@ * Abbreviated version of the yacc grammar used by at(1). */ +%token DOTTEDDATE +%token HYPHENDATE +%token HOURMIN +%token INT1DIGIT +%token INT2DIGIT +%token INT4DIGIT +%token INT5_8DIGIT %token INT %token NOW %token AM PM @@ -11,100 +18,115 @@ %token NEXT %token MINUTE HOUR DAY WEEK MONTH YEAR %token JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC -%token WORD +%token UTC -%type inc_period -%type inc_number +%type concatenated_date +%type hr24clock_hr_min +%type int1_2digit +%type int2_or_4digit +%type integer +%type inc_dec_period +%type inc_dec_number %type day_of_week %start timespec %% -timespec : time - | time date - | time increment - | time date increment - | time decrement - | time date decrement - | nowspec - ; - -nowspec : now - | now increment - | now decrement +timespec : spec_base + | spec_base inc_or_dec ; -now : NOW +spec_base : date + | time + | time date + | NOW ; -time : hr24clock_hr_min - | hr24clock_hr_min timezone_name - | hr24clock_hour time_sep minute - | hr24clock_hour time_sep minute timezone_name - | hr24clock_hour am_pm - | hr24clock_hour am_pm timezone_name - | hr24clock_hour time_sep minute am_pm - | hr24clock_hour time_sep minute am_pm timezone_name - | NOON +time : time_base + | time_base timezone_name + ; + +time_base : hr24clock_hr_min + | time_hour am_pm + | time_hour_min + | time_hour_min am_pm + | NOON | MIDNIGHT | TEATIME - ; + ; + +hr24clock_hr_min: INT4DIGIT + ; + +time_hour : int1_2digit + ; + +time_hour_min : HOURMIN + ; + +am_pm : AM + | PM + ; + +timezone_name : UTC + ; date : month_name day_number + | month_name day_number year_number | month_name day_number ',' year_number | day_of_week | TODAY | TOMORROW - | year_number '-' month_number '-' day_number - | day_number '.' month_number '.' year_number - | day_number '.' month_number + | HYPHENDATE + | DOTTEDDATE | day_number month_name | day_number month_name year_number | month_number '/' day_number '/' year_number - ; - -increment : '+' inc_number inc_period - | NEXT inc_period + | concatenated_date + | NEXT inc_dec_period | NEXT day_of_week ; -decrement : '-' inc_number inc_period +concatenated_date: INT5_8DIGIT ; -inc_period : MINUTE | HOUR | DAY | WEEK | MONTH | YEAR +month_name : JAN | FEB | MAR | APR | MAY | JUN + | JUL | AUG | SEP | OCT | NOV | DEC ; -hr24clock_hr_min: INT +month_number : int1_2digit ; -timezone_name : WORD +day_number : int1_2digit ; -hr24clock_hour : hr24clock_hr_min +year_number : int2_or_4digit ; -minute : INT +day_of_week : SUN | MON | TUE | WED | THU | FRI | SAT ; -am_pm : AM | PM +inc_or_dec : increment | decrement ; -month_name : JAN | FEB | MAR | APR | MAY | JUN | JUL - | AUG | SEP | OCT | NOV | DEC - ; +increment : '+' inc_dec_number inc_dec_period + ; -month_number : INT +decrement : '-' inc_dec_number inc_dec_period ; -day_number : INT + +inc_dec_number : integer ; -year_number : INT +inc_dec_period : MINUTE | HOUR | DAY | WEEK | MONTH | YEAR ; -day_of_week : SUN | MON | TUE | WED | THU | FRI | SAT +int1_2digit : INT1DIGIT | INT2DIGIT ; -inc_number : INT +int2_or_4digit : INT2DIGIT | INT4DIGIT ; -time_sep : ':' | '\'' | '.' | 'h' | ',' +integer : INT | INT1DIGIT | INT2DIGIT | INT4DIGIT | INT5_8DIGIT ; + +%%