]> git.saurik.com Git - apple/system_cmds.git/commitdiff
system_cmds-279.6.1.tar.gz mac-os-x-1038 mac-os-x-1039 v279.6.1
authorApple <opensource@apple.com>
Wed, 19 Jan 2005 01:05:26 +0000 (01:05 +0000)
committerApple <opensource@apple.com>
Wed, 19 Jan 2005 01:05:26 +0000 (01:05 +0000)
14 files changed:
at.tproj/Makefile
at.tproj/at.1
at.tproj/at.c
at.tproj/at.h
at.tproj/panic.c
at.tproj/panic.h
at.tproj/parsetime.c
at.tproj/parsetime.h
at.tproj/perm.c
at.tproj/perm.h
at.tproj/privs.h
atrun.tproj/Makefile
atrun.tproj/Makefile.postamble [new file with mode: 0644]
atrun.tproj/atrun.c

index c2335bf4ddbb48d79e5d98b8678dfab70be34af5..2c360246e4b0e5e2b8c10c466cf848b3dd47fa94 100644 (file)
@@ -33,9 +33,11 @@ PROF_LIBS = $(LIBS)
 
 PROJECT_HEADERS = privs.h pathnames.h
 
 
 PROJECT_HEADERS = privs.h pathnames.h
 
+NEXTSTEP_PB_CFLAGS += -D__FBSDID=__RCSID -DDAEMON_UID=1 -DDAEMON_GID=1 \
+       -DDEFAULT_AT_QUEUE=\'a\' -DDEFAULT_BATCH_QUEUE=\'b\' \
+       -DPERM_PATH=\"/var/at/\" -I/System/Library/Frameworks/System.framework/PrivateHeaders
 
 
-
-NEXTSTEP_BUILD_OUTPUT_DIR = /$(USER)/BUILD
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
 
 NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
 WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
 
 NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
 WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
index 47c2591210c27ea181412a4874a38c850bd1374f..381b7eec8b8ddf174542af4d92ca5213ec3ae2c0 100644 (file)
@@ -27,7 +27,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
 .\" (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.1,v 1.3 2003/06/05 17:13:32 eseidel Exp $
+.\"    $Id: at.1,v 1.3.50.2 2005/01/18 22:17:28 asigel Exp $
 .\"
 .Dd December 5, 1993
 .Dt "AT" 1
 .\"
 .Dd December 5, 1993
 .Dt "AT" 1
@@ -210,6 +210,7 @@ Job-creation lock file.
 .Xr crond 8 ,
 .Xr nice 1 ,
 .Xr sh 1 ,
 .Xr crond 8 ,
 .Xr nice 1 ,
 .Xr sh 1 ,
+.Xr compat 5 ,
 .Xr atrun 8
 .Sh AUTHOR
 .Bl -tag
 .Xr atrun 8
 .Sh AUTHOR
 .Bl -tag
index 0caf2d188ec467a7d0a4a23c0ee64f64726c28d8..ed307066cda5430b8ef3ef8cae66ba07461b78d3 100644 (file)
@@ -1,33 +1,9 @@
-/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+/* 
+ *  at.c : Put file into atrun queue
+ *  Copyright (C) 1993, 1994 Thomas Koenig
  *
  *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
- * Reserved.  This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License').  You may not use this file
- * except in compliance with the License.  Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License."
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-/*
- * at.c : Put file into atrun queue
- * Copyright (C) 1993  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
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -41,7 +17,7 @@
  * 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.
  * 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,
+ * IN NO EVENT SHALL THE AUTHOR(S) 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
  * 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
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/at/at.c,v 1.29 2002/07/22 11:32:16 robert Exp $");
+
 #define _USE_BSD 1
 
 /* System Headers */
 #define _USE_BSD 1
 
 /* System Headers */
-#include <sys/types.h>
+
+#include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <sys/wait.h>
 #include <ctype.h>
 #include <dirent.h>
 #include <sys/wait.h>
 #include <ctype.h>
 #include <dirent.h>
+#include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <fcntl.h>
+#ifndef __FreeBSD__
+#include <getopt.h>
+#endif
+#include <glob.h>
+#ifdef __FreeBSD__
+#include <locale.h>
+#endif
 #include <pwd.h>
 #include <signal.h>
 #include <stddef.h>
 #include <pwd.h>
 #include <signal.h>
 #include <stddef.h>
 #include <time.h>
 #include <unistd.h>
 
 #include <time.h>
 #include <unistd.h>
 
+#define COMPAT_MODE(a,b) (0)
+
 /* Local headers */
 /* Local headers */
+
 #include "at.h"
 #include "panic.h"
 #include "parsetime.h"
 #include "pathnames.h"
 #include "at.h"
 #include "panic.h"
 #include "parsetime.h"
 #include "pathnames.h"
+#include "perm.h"
+
 #define MAIN
 #include "privs.h"
 #define MAIN
 #include "privs.h"
-#include "perm.h"
 
 /* Macros */
 
 /* Macros */
-#define ALARMC 10              /* Number of seconds to wait for timeout */
+
+#ifndef ATJOB_DIR 
+#define ATJOB_DIR _PATH_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
 
 
 #define SIZE 255
 #define TIMESIZE 50
 
+enum { ATQ, ATRM, AT, BATCH, CAT };    /* what program we want to run */
+
 /* File scope variables */
 /* File scope variables */
-static char rcsid[] = "$Id: at.c,v 1.1.1.2 2000/01/11 02:10:04 wsanchez Exp $";
-char *no_export[] =
+
+const char *no_export[] =
 {
 {
-       "TERM", "TERMCAP", "DISPLAY", "_"
-};
-static send_mail = 0;
+    "TERM", "TERMCAP", "DISPLAY", "_"
+} ;
+static int send_mail = 0;
 
 /* External variables */
 
 /* External variables */
+
 extern char **environ;
 int fcreated;
 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 */
 char atqueue = 0;              /* which queue to examine for jobs (atq) */
 char atverify = 0;             /* verify time instead of queuing job */
+char *namep;
+int posixly_correct;           /* Behave as per POSIX */
+/* http://www.opengroup.org/onlinepubs/009695399/utilities/at.html */
 
 /* Function declarations */
 
 /* 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(long *, int);
+static long nextjob(void);
+static time_t ttime(const char *arg);
+static int in_job_list(long, long *, int);
+static long *get_job_list(int, char *[], int *);
 
 /* Signal catching functions */
 
 
 /* Signal catching functions */
 
-static void 
-sigc(signo)
-       int signo;
+static void sigc(int signo __unused)
 {
 {
-/* 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 __unused)
 {
 {
-/* Time out after some seconds
- */
-       panic("File locking timed out");
+    char buf[1024];
+
+    /* Time out after some seconds. */
+    strlcpy(buf, namep, sizeof(buf));
+    strlcat(buf, ": file locking timed out\n", sizeof(buf));
+    write(STDERR_FILENO, buf, strlen(buf));
+    sigc(0);
 }
 
 /* Local functions */
 
 }
 
 /* Local functions */
 
-static char *
-cwdname()
+static char *cwdname(void)
 {
 /* Read in the current directory; the name will be overwritten on
  * subsequent calls.
  */
 {
 /* Read in the current directory; the name will be overwritten on
  * subsequent calls.
  */
-       static char *ptr = NULL;
-       static size_t size = SIZE;
-
-       if (ptr == NULL)
-               ptr = (char *) malloc(size);
-
-       while (1) {
-               if (ptr == NULL)
-                       panic("Out of memory");
+    static char *ptr = NULL;
+    static size_t size = SIZE;
 
 
-               if (getcwd(ptr, size - 1) != NULL)
-                       return ptr;
+    if (ptr == NULL)
+       if ((ptr = malloc(size)) == NULL)
+           errx(EXIT_FAILURE, "virtual memory exhausted");
 
 
-               if (errno != ERANGE)
-                       perr("Cannot get directory");
+    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;
+       if ((ptr = malloc(size)) == NULL)
+           errx(EXIT_FAILURE, "virtual memory exhausted");
+    }
+}
 
 
-               free(ptr);
-               size += SIZE;
-               ptr = (char *) malloc(size);
+static long
+nextjob()
+{
+    long jobno;
+    FILE *fid;
+
+    if ((fid = fopen(ATJOB_DIR ".SEQ", "r+")) != (FILE*)0) {
+       if (fscanf(fid, "%5lx", &jobno) == 1) {
+           rewind(fid);
+           jobno = (1+jobno) % 0xfffff;        /* 2^20 jobs enough? */
+           fprintf(fid, "%05lx\n", jobno);
        }
        }
+       else
+           jobno = EOF;
+       fclose(fid);
+       return jobno;
+    }
+    else if ((fid = fopen(ATJOB_DIR ".SEQ", "w")) != (FILE*)0) {
+       fprintf(fid, "%05lx\n", jobno = 1);
+       fclose(fid);
+       return 1;
+    }
+    return EOF;
 }
 
 static void
 }
 
 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;
+/* This does most of the work if at or batch are invoked for writing a job.
+ */
+    long jobno;
+    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;
+    
+#ifdef __FreeBSD__
+    (void) setlocale(LC_TIME, "");
+#endif
+
+/* 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);
+
+    if ((jobno = nextjob()) == EOF)
+       perr("cannot generate job number");
+
+    sprintf(ppos, "%c%5lx%8lx", queue, 
+           jobno, (unsigned long) (runtimer/60));
+
+    for(ap=ppos; *ap != '\0'; ap ++)
+       if (*ap == ' ')
+           *ap = '0';
+
+    if (stat(atfile, &statbuf) != 0)
+       if (errno != ENOENT)
+           perr("cannot access " ATJOB_DIR);
+
+    /* 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(),
+     * then from LOGNAME, finally from getpwuid().
+     */
+    mailname = getlogin();
+    if (mailname == NULL)
+       mailname = getenv("LOGNAME");
+
+    if ((mailname == NULL) || (mailname[0] == '\0') 
+       || (strlen(mailname) >= MAXLOGNAME) || (getpwnam(mailname)==NULL))
+    {
+       pass_entry = getpwuid(real_uid);
+       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# atrun uid=%ld gid=%ld\n# mail %.*s %d\n",
+       (long) real_uid, (long) real_gid, MAXLOGNAME - 1, 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.  Don'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
+       {
+           size_t 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++;
+       }
 
 
-       sigaction(SIGINT, &act, NULL);
+       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)) {
+                       switch (*ap) {
+                         case '%': case '/': case '{': case '[':
+                         case ']': case '=': case '}': case '@':
+                         case '+': case '#': case ',': case '.':
+                         case ':': case '-': case '_':
+                           break;
+                         default:
+                           fputc('\\', fp);
+                           break;
+                       }
+                   }
+                   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);
+       }
+    }
+    /* 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");
 
 
-       strcpy(atfile, _PATH_ATJOBS);
-       ppos = atfile + strlen(_PATH_ATJOBS);
+    while((ch = getchar()) != EOF)
+       fputc(ch, fp);
 
 
-       /*
-        * 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.
-        */
+    fprintf(fp, "\n");
+    if (ferror(fp))
+       panic("output error");
+       
+    if (ferror(stdin))
+       panic("input error");
 
 
-       PRIV_START
+    fclose(fp);
 
 
-       if ((lockdes = open(_PATH_LOCKFILE, O_WRONLY | O_CREAT, 0600)) < 0)
-               perr2("Cannot open lockfile ", _PATH_LOCKFILE);
+    /* Set the x bit so that we're ready to start executing
+     */
 
 
-       lock.l_type = F_WRLCK;
-       lock.l_whence = SEEK_SET;
-       lock.l_start = 0;
-       lock.l_len = 0;
+    if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
+       perr("cannot give away file");
 
 
-       act.sa_handler = alarmc;
-       sigemptyset(&(act.sa_mask));
-       act.sa_flags = 0;
+    close(fd2);
+    if (posixly_correct) {
+       struct tm runtime;
+       char timestr[TIMESIZE];
+       runtime = *localtime(&runtimer);
+       strftime(timestr, TIMESIZE, "%a %b %e %T %Y", &runtime);
+       fprintf(stderr, "job %ld at %s\n", jobno, timestr);
+    } else
+       fprintf(stderr, "Job %ld will be executed using /bin/sh\n", jobno);
+}
 
 
-       /*
-        * 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 */
+static int 
+in_job_list(long job, long *joblist, int len)
+{
+    int i;
 
 
-       if (i >= AT_MAXJOBS)
-               panic("Too many jobs already");
+    for (i = 0; i < len; i++)
+       if (job == joblist[i])
+           return 1;
 
 
-       /*
-        * 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");
+    return 0;
+}
 
 
-       if ((fd2 = dup(fdes)) < 0)
-               perr("Error in dup() of job file");
+static void
+list_one_job(char *name, long *joblist, int len, int *first)
+{
+    struct stat buf;
+    struct tm runtime;
+    unsigned long ctm;
+    char queue;
+    long jobno;
+    time_t runtimer;
+    char timestr[TIMESIZE];
+
+    if (stat(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))
+       return;
+
+    if(sscanf(name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3)
+       return;
+
+    /* If jobs are given, only list those jobs */
+    if (joblist && !in_job_list(jobno, joblist, len))
+       return;
+
+    if (atqueue && (queue != atqueue))
+        return;
+
+    runtimer = 60*(time_t) ctm;
+    runtime = *localtime(&runtimer);
+    strftime(timestr, TIMESIZE, "%a %b %e %T %Y", &runtime);
+    if (*first) {
+       if (!posixly_correct)
+           printf("Date\t\t\t\tOwner\t\tQueue\tJob#\n");
+       *first=0;
+    }
+    if (posixly_correct)
+       printf("%ld\t%s\n", jobno, timestr);
+    else {
+       struct passwd *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)", 
+              name);
+    }
+}
 
 
-       if (fchown(fd2, real_uid, -1) != 0)
-               perr("Cannot give away file");
+static void
+list_jobs(long *joblist, int len)
+{
+    /* List all a user's jobs in the queue, by looping through ATJOB_DIR, 
+     * or everybody's if we are root
+     */
+    DIR *spool;
+    struct dirent *dirent;
+    int first=1;
+    
+#ifdef __FreeBSD__
+    (void) setlocale(LC_TIME, "");
+#endif
+
+    PRIV_START
+
+    if (chdir(ATJOB_DIR) != 0)
+       perr("cannot change to " ATJOB_DIR);
+
+    if (joblist) {             /* Force order to match POSIX */
+       char jobglob[32];
+       glob_t g;
+       int i;
 
 
-       PRIV_END
+       sprintf(jobglob, "?%05lx*", joblist[0]);
+       g.gl_offs = 0;
+       glob(jobglob, GLOB_DOOFFS, NULL, &g);
+       for (i = 1; i < len; i++) {
+           sprintf(jobglob, "?%05lx*", joblist[i]);
+           glob(jobglob, GLOB_DOOFFS | GLOB_APPEND, NULL, &g);
+       }
+       for (i = 0; i < g.gl_pathc; i++) {
+           list_one_job(g.gl_pathv[i], joblist, len, &first);
+       }
+       globfree(&g);
+    } else {
+       if ((spool = opendir(".")) == NULL)
+           perr("cannot open " ATJOB_DIR);
 
 
-       /*
-        * We no longer need suid root; now we just need to be able to
-        * write to the directory, if necessary.
+       /*      Loop over every file in the directory 
         */
         */
+       while((dirent = readdir(spool)) != NULL) {
+          list_one_job(dirent->d_name, joblist, len, &first);
+       }
+       closedir(spool);
+    }
+    PRIV_END
+}
 
 
-           REDUCE_PRIV(0);
+static void
+process_jobs(int argc, char **argv, int what)
+{
+    /* Delete every argument (job - ID) given
+     */
+    int i;
+    struct stat buf;
+    DIR *spool;
+    struct dirent *dirent;
+    unsigned long ctm;
+    char queue;
+    long jobno;
 
 
-       /*
-        * We've successfully created the file; let's set the flag so it
-        * gets removed in case of an interrupt or error.
-        */
-       fcreated = 1;
+    PRIV_START
 
 
-       /* 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 (chdir(ATJOB_DIR) != 0)
+       perr("cannot change to " ATJOB_DIR);
 
 
-       if ((fp = fdopen(fdes, "w")) == NULL)
-               panic("Cannot reopen atjob file");
+    if ((spool = opendir(".")) == NULL)
+       perr("cannot open " ATJOB_DIR);
 
 
-       /*
-        * 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;
-       }
+    PRIV_END
 
 
-       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);
+    /* Loop over every file in the directory 
+     */
+    while((dirent = readdir(spool)) != NULL) {
 
 
-       /* Write out the umask at the time of invocation */
-       fprintf(fp, "umask %lo\n", (unsigned long) cmask);
+       PRIV_START
+       if (stat(dirent->d_name, &buf) != 0)
+           perr("cannot stat in " ATJOB_DIR);
+       PRIV_END
 
 
-       /*
-        * 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(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3)
+           continue;
 
 
-               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);
+       for (i=optind; i < argc; i++) {
+           if (atoi(argv[i]) == jobno || strcmp(argv[i], dirent->d_name)==0) {
+               if ((buf.st_uid != real_uid) && !(real_uid == 0))
+                   errx(EXIT_FAILURE, "%s: not owner", argv[i]);
+               switch (what) {
+                 case ATRM:
 
 
-               }
-       }
-       /*
-        * Cd to the directory at the time and write out all the commands
-        * the user supplies from stdin.
-        */
-       fprintf(fp, "cd %s\n", cwdname());
+                   PRIV_START
 
 
-       while ((ch = getchar()) != EOF)
-               fputc(ch, fp);
+                   if (unlink(dirent->d_name) != 0)
+                       perr(dirent->d_name);
 
 
-       fprintf(fp, "\n");
-       if (ferror(fp))
-               panic("Output error");
+                   PRIV_END
 
 
-       if (ferror(stdin))
-               panic("Input error");
+                   break;
 
 
-       fclose(fp);
+                 case CAT:
+                   {
+                       FILE *fp;
+                       int ch;
 
 
-       /*
-        * 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");
+                       PRIV_START
 
 
-       close(fd2);
-       fprintf(stderr, "Job %s will be executed using /bin/sh\n", ppos);
-}
+                       fp = fopen(dirent->d_name,"r");
 
 
-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_END
 
 
-       PRIV_START
+                       if (!fp) {
+                           perr("cannot open file");
+                       }
+                       while((ch = getc(fp)) != EOF) {
+                           putchar(ch);
+                       }
+                   }
+                   break;
+
+                 default:
+                   errx(EXIT_FAILURE, "internal error, process_jobs = %d",
+                       what);
+               }
+           }
+       }
+    }
+} /* process_jobs */
 
 
-           if (chdir(_PATH_ATJOBS) != 0)
-               perr2("Cannot change to ", _PATH_ATJOBS);
+#define        ATOI2(ar)       ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
 
 
-       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);
+static time_t
+ttime(const char *arg)
+{
+    /*
+     * This is pretty much a copy of stime_arg1() from touch.c.  I changed
+     * the return value and the argument list because it's more convenient
+     * (IMO) to do everything in one place. - Joe Halpin
+     */
+    struct timeval tv[2];
+    time_t now;
+    struct tm *t;
+    int yearset;
+    char *p;
+    
+    if (gettimeofday(&tv[0], NULL))
+       panic("Cannot get current time");
+    
+    /* Start with the current time. */
+    now = tv[0].tv_sec;
+    if ((t = localtime(&now)) == NULL)
+       panic("localtime");
+    /* [[CC]YY]MMDDhhmm[.SS] */
+    if ((p = strchr(arg, '.')) == NULL)
+       t->tm_sec = 0;          /* Seconds defaults to 0. */
+    else {
+       if (strlen(p + 1) != 2)
+           goto terr;
+       *p++ = '\0';
+       t->tm_sec = ATOI2(p);
+    }
+    
+    yearset = 0;
+    switch(strlen(arg)) {
+    case 12:                   /* CCYYMMDDhhmm */
+       t->tm_year = ATOI2(arg);
+       t->tm_year *= 100;
+       yearset = 1;
+       /* FALLTHROUGH */
+    case 10:                   /* YYMMDDhhmm */
+       if (yearset) {
+           yearset = ATOI2(arg);
+           t->tm_year += yearset;
+       } else {
+           yearset = ATOI2(arg);
+           t->tm_year = yearset + 2000;
        }
        }
-       PRIV_END
+       t->tm_year -= 1900;     /* Convert to UNIX time. */
+       /* FALLTHROUGH */
+    case 8:                            /* MMDDhhmm */
+       t->tm_mon = ATOI2(arg);
+       --t->tm_mon;            /* Convert from 01-12 to 00-11 */
+       t->tm_mday = ATOI2(arg);
+       t->tm_hour = ATOI2(arg);
+       t->tm_min = ATOI2(arg);
+       break;
+    default:
+       goto terr;
+    }
+    
+    t->tm_isdst = -1;          /* Figure out DST. */
+    tv[0].tv_sec = tv[1].tv_sec = mktime(t);
+    if (tv[0].tv_sec != -1)
+       return tv[0].tv_sec;
+    else
+terr:
+       panic(
+          "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
 }
 
 }
 
-static void
-delete_jobs(argc, argv)
-       int argc;
-       char **argv;
+static long *
+get_job_list(int argc, char *argv[], int *joblen)
 {
 {
-       /* 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]);
+    int i, len;
+    long *joblist;
+    char *ep;
+
+    joblist = NULL;
+    len = argc;
+    if (len > 0) {
+       if ((joblist = malloc(len * sizeof(*joblist))) == NULL)
+           panic("out of memory");
+
+       for (i = 0; i < argc; i++) {
+           errno = 0;
+           if ((joblist[i] = strtol(argv[i], &ep, 10)) < 0 ||
+               ep == argv[i] || *ep != '\0' || errno)
+               panic("invalid job number");
        }
        }
-       PRIV_END
-}                              /* delete_jobs */
+    }
 
 
-/* Global functions */
+    *joblen = len;
+    return joblist;
+}
 
 int
 
 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;
+
+    int program = AT;                  /* our default program */
+    const char *options = "q:f:t:rmvldbc"; /* default options for at */
+    time_t timer;
+    long *joblist;
+    int joblen;
+
+    posixly_correct = COMPAT_MODE("bin/at", "Unix2003");
+    joblist = NULL;
+    joblen = 0;
+    timer = -1;
+    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:q:mv";
+    }
+
+    /* process whatever options we can process
+     */
+    opterr=1;
+    while ((c=getopt(argc, argv, options)) != -1)
+       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':
+           warnx("-d is deprecated; use -r instead");
+           /* fall through to 'r' */
+
+       case 'r':
+           if (program != AT)
+               usage();
+
+           program = ATRM;
+           options = "";
+           break;
+
+       case 't':
+           if (program != AT)
+               usage();
+           timer = ttime(optarg);
+           break;
+
+       case 'l':
+           if (program != AT)
+               usage();
+
+           program = ATQ;
+           options = "q:";
+           break;
+
+       case 'b':
+           if (program != AT)
+               usage();
+
+           program = BATCH;
+           options = "f:q:mv";
+           break;
+
+       case 'c':
+           program = CAT;
+           options = "";
+           break;
+
+       default:
+           usage();
+           break;
        }
        }
+    /* end of options eating
+     */
 
 
-       /* 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 */
+    /* select our program
+     */
+    if(!check_permission())
+       errx(EXIT_FAILURE, "you do not have permission to use this program");
+    switch (program) {
+    case ATQ:
 
 
-       /* 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:
+       REDUCE_PRIV(DAEMON_UID, DAEMON_GID)
 
 
-               REDUCE_PRIV(0);
+       if (queue_set == 0)
+           joblist = get_job_list(argc - optind, argv + optind, &joblen);
+       list_jobs(joblist, joblen);
+       break;
 
 
-               list_jobs();
-               break;
+    case ATRM:
 
 
-       case ATRM:
+       REDUCE_PRIV(DAEMON_UID, DAEMON_GID)
 
 
-               REDUCE_PRIV(0);
+       process_jobs(argc, argv, ATRM);
+       break;
 
 
-               delete_jobs(argc, argv);
-               break;
+    case CAT:
 
 
-       case AT:
-               timer = parsetime(argc, argv);
-               if (atverify) {
-                       struct tm *tm = localtime(&timer);
+       process_jobs(argc, argv, CAT);
+       break;
 
 
-                       fprintf(stderr, "%s\n", asctime(tm));
-               }
-               writefile(timer, queue);
-               break;
+    case AT:
+       /*
+        * If timer is > -1, then the user gave the time with -t.  In that
+        * case, it's already been set. If not, set it now.  
+        */
+       if (timer == -1) 
+           timer = parsetime(argc, argv);
 
 
-       case BATCH:
-               writefile(time(NULL), 'b');
-               break;
+       if (atverify)
+       {
+           struct tm *tm = localtime(&timer);
+           fprintf(stderr, "%s\n", asctime(tm));
+       }
+       writefile(timer, queue);
+       break;
 
 
-       default:
-               panic("Internal error");
-               break;
+    case BATCH:
+       if (queue_set)
+           queue = toupper(queue);
+       else
+           queue = DEFAULT_BATCH_QUEUE;
+
+       if (argc > optind)
+           timer = parsetime(argc, argv);
+       else
+           timer = time(NULL);
+       
+       if (atverify)
+       {
+           struct tm *tm = localtime(&timer);
+           fprintf(stderr, "%s\n", asctime(tm));
        }
        }
-       exit(EXIT_SUCCESS);
+
+        writefile(timer, queue);
+       break;
+
+    default:
+       panic("internal error");
+       break;
+    }
+    exit(EXIT_SUCCESS);
 }
 }
index 117a547883095f5f2d8dd3ec5e298bfa33250d08..ff8008d58dca1f76c8e29935f702e62633d985e0 100644 (file)
@@ -1,30 +1,6 @@
-/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
- * Reserved.  This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License').  You may not use this file
- * except in compliance with the License.  Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License."
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-/*
- * 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
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -38,7 +14,7 @@
  * 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.
  * 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,
+ * IN NO EVENT SHALL THE AUTHOR(S) 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
  * 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
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * (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.1.2 2000/01/11 02:10:05 wsanchez Exp $
+ * $FreeBSD: src/usr.bin/at/at.h,v 1.5 2001/07/24 14:15:51 obrien Exp $
  */
 
 extern int fcreated;
 extern char *namep;
 extern char atfile[];
 extern char atverify;
  */
 
 extern int fcreated;
 extern char *namep;
 extern char atfile[];
 extern char atverify;
-
-#define AT_MAXJOBS     255     /* max jobs outstanding per user */
index 839a54c757d4b757ccf16e9bd9512d7989cd4f57..76831a8a8ebbdc00872f7086a987d07e89833247 100644 (file)
@@ -1,30 +1,6 @@
-/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
- * Reserved.  This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License').  You may not use this file
- * except in compliance with the License.  Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License."
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-/*
- * 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
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -38,7 +14,7 @@
  * 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.
  * 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,
+ * IN NO EVENT SHALL THE AUTHOR(S) 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
  * 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
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/at/panic.c,v 1.17 2002/05/16 00:47:14 tjr Exp $");
+
 /* System Headers */
 
 /* System Headers */
 
+#include <err.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 /* Local headers */
 
 #include "panic.h"
 /* Local headers */
 
 #include "panic.h"
+#include "privs.h"
 #include "at.h"
 
 #include "at.h"
 
-/* File scope variables */
-
-static char rcsid[] = "$Id: panic.c,v 1.1.1.2 2000/01/11 02:10:05 wsanchez Exp $";
-
 /* External variables */
 
 /* Global functions */
 
 /* External variables */
 
 /* Global functions */
 
-#ifdef __APPLE__
-__private_extern__
-#endif
 void
 void
-panic(a)
-       char *a;
+panic(const char *a)
 {
 /* Something fatal has happened, print error message and exit.
  */
 {
 /* Something fatal has happened, print error message and exit.
  */
-       fprintf(stderr, "%s: %s\n", namep, a);
-       if (fcreated)
+       if (fcreated) {
+               PRIV_START
                unlink(atfile);
                unlink(atfile);
+               PRIV_END
+       }
 
 
-       exit(EXIT_FAILURE);
+       errx(EXIT_FAILURE, "%s", a);
 }
 
 void
 }
 
 void
-perr(a)
-       char *a;
+perr(const char *a)
 {
 /* Some operating system error; print error message and exit.
  */
 {
 /* Some operating system error; print error message and exit.
  */
-       perror(a);
-       if (fcreated)
-               unlink(atfile);
+       int serrno = errno;
 
 
-       exit(EXIT_FAILURE);
-}
+       if (fcreated) {
+               PRIV_START
+               unlink(atfile);
+               PRIV_END
+       }
 
 
-void 
-perr2(a, b)
-       char *a, *b;
-{
-       fprintf(stderr, "%s", a);
-       perr(b);
+       errno = serrno;
+       err(EXIT_FAILURE, "%s", a);
 }
 
 void
 usage(void)
 {
 }
 
 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);
+       /* Print usage and exit. */
+    fprintf(stderr, "usage: at [-q x] [-f file] [-m] time\n"
+                   "       at -c job [job ...]\n"
+                   "       at [-f file] -t [[CC]YY]MMDDhhmm[.SS]\n"
+                   "       at -r job [job ...]\n"
+                   "       at -l -q queuename\n"
+                   "       at -l [job ...]\n"
+                   "       atq [-q x] [-v]\n"
+                   "       atrm job [job ...]\n"
+                   "       batch [-f file] [-m]\n");
+    exit(EXIT_FAILURE);
 }
 }
index dda9212a17eed4c837f8874f54b06601ab2074d6..d656d6185b9d2a56cd0af1e02eeaf14cbb847d00 100644 (file)
@@ -1,30 +1,6 @@
-/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
- * Reserved.  This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License').  You may not use this file
- * except in compliance with the License.  Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License."
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-/*
- * 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
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -38,7 +14,7 @@
  * 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.
  * 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,
+ * IN NO EVENT SHALL THE AUTHOR(S) 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
  * 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
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * (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.1.2 2000/01/11 02:10:05 wsanchez Exp $
+ * $FreeBSD: src/usr.bin/at/panic.h,v 1.6 2002/01/13 20:21:08 mike Exp $
  */
 
  */
 
-void panic     __P((char *a));
-void perr      __P((char *a));
-void perr2     __P((char *a, char *b));
-void usage     __P((void));
+#include <sys/cdefs.h>
+
+void   panic(const char *a) __dead2;
+void   perr(const char *a) __dead2;
+void   usage(void) __dead2;
index 46811fafad424eec4844008a91792082424b156e..4f1b21ef8982b40700ece578f5592e7b8d68cc8e 100644 (file)
@@ -1,33 +1,9 @@
-/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
- * Reserved.  This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License').  You may not use this file
- * except in compliance with the License.  Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License."
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
 /* 
 /* 
- * 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
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -41,7 +17,7 @@
  * 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.
  * 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,
+ * IN NO EVENT SHALL THE AUTHOR(S) 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
  * 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
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  *  at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  *  at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS
+ * DOT ::= ':'|'.'
  *     /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]]             \
  *     |NOON                       | |[TOMORROW]                          |
  *     /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/
  */
 
  */
 
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/at/parsetime.c,v 1.25 2001/12/10 21:13:01 dwmalone Exp $");
+
 /* System Headers */
 
 #include <sys/types.h>
 /* System Headers */
 
 #include <sys/types.h>
+#include <ctype.h>
+#include <err.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#include <tzfile.h>
 #include <unistd.h>
 #include <unistd.h>
-#include <ctype.h>
+#ifndef __FreeBSD__
+#include <getopt.h>
+#endif
 
 /* Local headers */
 
 #include "at.h"
 #include "panic.h"
 
 /* Local headers */
 
 #include "at.h"
 #include "panic.h"
+#include "parsetime.h"
 
 
 /* Structures and unions */
 
 
 /* Structures and unions */
 enum { /* symbols */
     MIDNIGHT, NOON, TEATIME,
     PM, AM, TOMORROW, TODAY, NOW,
 enum { /* symbols */
     MIDNIGHT, NOON, TEATIME,
     PM, AM, TOMORROW, TODAY, NOW,
-    MINUTES, HOURS, DAYS, WEEKS,
-    NUMBER, PLUS, DOT, SLASH, ID, JUNK,
+    MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS,
+    NUMBER, PLUS, DOT, COMMA, SLASH, ID, JUNK,
     JAN, FEB, MAR, APR, MAY, JUN,
     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,
+    UTC
+    };
 
 
-/*
- * parse translation table - table driven parsers can be your FRIEND!
+/* parse translation table - table driven parsers can be your FRIEND!
  */
 struct {
  */
 struct {
-    char *name;        /* token name */
+    const char *name;  /* token name */
     int value; /* token id */
     int value; /* token id */
+    int plural;        /* is this plural? */
 } Specials[] = {
 } 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) */
+    { "month", MONTHS,0 },     /* month ... */
+    { "months", MONTHS,1 },    /* (pluralized) */
+    { "year", YEARS,0 },       /* year ... */
+    { "years", YEARS,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 },
+    { "january", JAN,0 },
+    { "february", FEB,0 },
+    { "march", MAR,0 },
+    { "april", APR,0 },
+    { "may", MAY,0 },
+    { "june", JUN,0 },
+    { "july", JUL,0 },
+    { "august", AUG,0 },
+    { "september", SEP,0 },
+    { "october", OCT,0 },
+    { "november", NOV,0 },
+    { "december", 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 },
+    { "utc", UTC, 0 },
 } ;
 
 /* File scope variables */
 } ;
 
 /* File scope variables */
@@ -136,10 +150,9 @@ static char *sct;  /* scanner - next char pointer in current argument */
 static int need;       /* scanner - need to advance to next argument */
 
 static char *sc_token; /* scanner - token buffer */
 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 size_t sc_len;   /* scanner - length of token buffer */
 static int sc_tokid;   /* scanner - token id */
 static int sc_tokid;   /* scanner - token id */
-
-static char rcsid[] = "$Id: parsetime.c,v 1.1.1.2 2000/01/11 02:10:05 wsanchez Exp $";
+static int sc_tokplur; /* scanner - is token plural? */
 
 /* Local functions */
 
 
 /* Local functions */
 
@@ -147,13 +160,13 @@ static char rcsid[] = "$Id: parsetime.c,v 1.1.1.2 2000/01/11 02:10:05 wsanchez E
  * parse a token, checking if it's something special to us
  */
 static int
  * parse a token, checking if it's something special to us
  */
 static int
-parse_token(arg)
-       char *arg;
+parse_token(char *arg)
 {
 {
-    int i;
+    size_t i;
 
     for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++)
        if (strcasecmp(Specials[i].name, arg) == 0) {
 
     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;
        }
 
            return sc_tokid = Specials[i].value;
        }
 
@@ -166,37 +179,34 @@ parse_token(arg)
  * init_scanner() sets up the scanner to eat arguments
  */
 static void
  * 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;
     need = 1;
     sc_len = 1;
 {
     scp = argv;
     scc = argc;
     need = 1;
     sc_len = 1;
-    while (--argc > 0)
-       sc_len += strlen(*++argv);
+    while (argc-- > 0)
+       sc_len += strlen(*argv++);
 
 
-    sc_token = (char *) malloc(sc_len);
-    if (sc_token == NULL)
-       panic("Insufficient virtual memory");
+    if ((sc_token = malloc(sc_len)) == NULL)
+       errx(EXIT_FAILURE, "virtual memory exhausted");
 } /* init_scanner */
 
 /*
  * token() fetches a token from the input stream
  */
 static int
 } /* init_scanner */
 
 /*
  * token() fetches a token from the input stream
  */
 static int
-token()
+token(void)
 {
     int idx;
 
     while (1) {
        memset(sc_token, 0, sc_len);
        sc_tokid = EOF;
 {
     int idx;
 
     while (1) {
        memset(sc_token, 0, sc_len);
        sc_tokid = EOF;
+       sc_tokplur = 0;
        idx = 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) {
         * when we fall off the arglist, we'll just return EOF forever
         */
        if (need) {
@@ -207,8 +217,7 @@ token()
            scc--;
            need = 0;
        }
            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
         */
         * we'll continue, which puts us up at the top of the while loop
         * to fetch the next argument in
         */
@@ -219,20 +228,19 @@ token()
            continue;
        }
 
            continue;
        }
 
-       /*
-        * preserve the first character of the new token
+       /* preserve the first character of the new token
         */
        sc_token[0] = *sct++;
 
         */
        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;
         */
        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;
            while (isalpha(*sct))
                sc_token[++idx] = *sct++;
            sc_token[++idx] = 0;
@@ -244,6 +252,8 @@ token()
            return sc_tokid = PLUS;
        else if (sc_token[0] == '/')
            return sc_tokid = SLASH;
            return sc_tokid = PLUS;
        else if (sc_token[0] == '/')
            return sc_tokid = SLASH;
+       else if (sc_token[0] == ',')
+           return sc_tokid = COMMA;
        else
            return sc_tokid = JUNK;
     } /* while (1) */
        else
            return sc_tokid = JUNK;
     } /* while (1) */
@@ -254,8 +264,7 @@ token()
  * plonk() gives an appropriate error message if a token is incorrect
  */
 static void
  * 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");
 {
     panic((tok == EOF) ? "incomplete time"
                       : "garbled time");
@@ -266,103 +275,78 @@ plonk(tok)
  * expect() gets a token and dies most horribly if it's not the token we want
  */
 static void
  * 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... */
 } /* expect */
 
 
 {
     if (token() != desired)
        plonk(sc_tokid);        /* and we die here... */
 } /* expect */
 
 
-/*
- * dateadd() adds a number of minutes to a date.  It is extraordinarily
- * stupid regarding day-of-month overflow, and will most likely not
- * work properly
- */
-static void
-dateadd(minutes, tm)
-       int minutes;
-       struct tm *tm;
-{
-    /* increment days */
-
-    while (minutes > 24*60) {
-       minutes -= 24*60;
-       tm->tm_mday++;
-    }
-
-    /* increment hours */
-    while (minutes > 60) {
-       minutes -= 60;
-       tm->tm_hour++;
-       if (tm->tm_hour > 23) {
-           tm->tm_mday++;
-           tm->tm_hour = 0;
-       }
-    }
-
-    /* increment minutes */
-    tm->tm_min += minutes;
-
-    if (tm->tm_min > 59) {
-       tm->tm_hour++;
-       tm->tm_min -= 60;
-
-       if (tm->tm_hour > 23) {
-           tm->tm_mday++;
-           tm->tm_hour = 0;
-       }
-    }
-} /* dateadd */
-
-
 /*
  * plus() parses a now + time
  *
 /*
  * plus() parses a now + time
  *
- *  at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS]
+ *  at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS|MONTHS|YEARS]
  *
  */
  *
  */
+
 static void
 static void
-plus(tm)
-       struct tm *tm;
+plus(struct tm *tm)
 {
     int delay;
 {
     int delay;
+    int expectplur;
 
     expect(NUMBER);
 
     delay = atoi(sc_token);
 
     expect(NUMBER);
 
     delay = atoi(sc_token);
+    expectplur = (delay != 1) ? 1 : 0;
 
     switch (token()) {
 
     switch (token()) {
+    case YEARS:
+           tm->tm_year += delay;
+           break;
+    case MONTHS:
+           tm->tm_mon += delay;
+           break;
     case WEEKS:
            delay *= 7;
     case DAYS:
     case WEEKS:
            delay *= 7;
     case DAYS:
-           delay *= 24;
+           tm->tm_mday += delay;
+           break;
     case HOURS:
     case HOURS:
-           delay *= 60;
+           tm->tm_hour += delay;
+           break;
     case MINUTES:
     case MINUTES:
-           dateadd(delay, tm);
-           return;
+           tm->tm_min += delay;
+           break;
+    default:
+           plonk(sc_tokid);
+           break;
     }
     }
-    plonk(sc_tokid);
+
+    if (expectplur != sc_tokplur)
+       warnx("pluralization is wrong");
+
+    tm->tm_isdst = -1;
+    if (mktime(tm) < 0)
+       plonk(sc_tokid);
+
 } /* plus */
 
 
 /*
  * tod() computes the time of day
 } /* plus */
 
 
 /*
  * tod() computes the time of day
- *     [NUMBER [DOT NUMBER] [AM|PM]]
+ *     [NUMBER [DOT NUMBER] [AM|PM]] [UTC]
  */
 static void
  */
 static void
-tod(tm)
-       struct tm *tm;
+tod(struct tm *tm)
 {
     int hour, minute = 0;
 {
     int hour, minute = 0;
-    int tlen;
+    size_t tlen;
 
     hour = atoi(sc_token);
     tlen = strlen(sc_token);
 
 
     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) {
      * a HHMM time, otherwise it's HH DOT MM time
      */
     if (token() == DOT) {
@@ -371,33 +355,58 @@ tod(tm)
        if (minute > 59)
            panic("garbled time");
        token();
        if (minute > 59)
            panic("garbled time");
        token();
-    } else if (tlen == 4) {
+    }
+    else if (tlen == 4) {
        minute = hour%100;
        if (minute > 59)
        minute = hour%100;
        if (minute > 59)
-           panic("garbeld time");
+           panic("garbled time");
        hour = hour/100;
     }
 
        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) {
+    switch (sc_tokid) {
+    case AM:
+    case PM:
        if (hour > 12)
            panic("garbled time");
 
        if (hour > 12)
            panic("garbled time");
 
-       if (sc_tokid == PM)
-           hour += 12;
-       token();
-    } else if (hour > 23)
-       panic("garbled time");
+       if (sc_tokid == PM) {
+           if (hour != 12)     /* 12:xx PM is 12:xx, not 24:xx */
+               hour += 12;
+       } else {
+           if (hour == 12)     /* 12:xx AM is 00:xx, not 12:xx */
+               hour = 0;
+       }
+       if (UTC != token())
+            break;             /* else fallthrough */
+
+    case UTC:
+        hour += tm->tm_gmtoff/(60*60);
+         while (hour < 0)
+            hour += 24;
+        minute += (tm->tm_gmtoff/60);
+        while (minute < 0)
+            minute += 60;
+        tm->tm_gmtoff = 0;
+        token();
+        break;
+    default:
+       if (hour > 23)
+           panic("garbled time");
+       break;
+    }
 
 
-    /*
-     * 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 we've gone past that time - but if we're specifying a time plus
      * a relative offset, it's okay to bump things
+     * If minutes are the same assume tomorrow was meant
      */
      */
-    if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour)
+    if ((sc_tokid == EOF || sc_tokid == PLUS) && 
+       ((tm->tm_hour > hour) || ((tm->tm_hour == hour) && (tm->tm_min >= minute)))) {
        tm->tm_mday++;
        tm->tm_mday++;
+       tm->tm_wday++;
+    }
 
     tm->tm_hour = hour;
     tm->tm_min = minute;
 
     tm->tm_hour = hour;
     tm->tm_min = minute;
@@ -412,15 +421,30 @@ tod(tm)
  * assign_date() assigns a date, wrapping to next year if needed
  */
 static void
  * 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)
-           year -= 1900;
-       else
-           panic("garbled time");
+   /*
+    * Convert year into tm_year format (year - 1900).
+    * We may be given the year in 2 digit, 4 digit, or tm_year format.
+    */
+    if (year != -1) {
+       if (year >= TM_YEAR_BASE)
+               year -= TM_YEAR_BASE;   /* convert from 4 digit year */
+       else if (year < 100) {
+               /* convert from 2 digit year */
+               struct tm *lt;
+               time_t now;
+
+               time(&now);
+               lt = localtime(&now);
+
+               /* Convert to tm_year assuming current century */
+               year += (lt->tm_year / 100) * 100;
+
+               if (year == lt->tm_year - 1) year++;
+               else if (year < lt->tm_year)
+                       year += 100;    /* must be in next century */
+       }
     }
 
     if (year < 0 &&
     }
 
     if (year < 0 &&
@@ -440,15 +464,15 @@ assign_date(tm, mday, mon, year)
  *
  *  /[<month> NUMBER [NUMBER]]           \
  *  |[TOMORROW]                          |
  *
  *  /[<month> NUMBER [NUMBER]]           \
  *  |[TOMORROW]                          |
+ *  |[DAY OF WEEK]                       |
  *  |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
  *  \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
  */
 static void
  *  |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 year= (-1);
-    long mday, mon;
+    long mday = 0, wday, mon;
     int tlen;
 
     switch (sc_tokid) {
     int tlen;
 
     switch (sc_tokid) {
@@ -459,28 +483,52 @@ month(tm)
     case TOMORROW:
            /* do something tomorrow */
            tm->tm_mday ++;
     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:
     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);
            mday = atol(sc_token);
             */
            mon = (sc_tokid-JAN);
            expect(NUMBER);
            mday = atol(sc_token);
-           if (token() == NUMBER) {
-               year = atol(sc_token);
-               token();
+           if (token() == COMMA) {
+               if (token() == NUMBER) {
+                   year = atol(sc_token);
+                   token();
+               }
            }
            assign_date(tm, mday, mon, year);
            }
            assign_date(tm, mday, mon, year);
+           if (sc_tokid == PLUS)
+                   plus(tm);
+           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:
            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);
             */
            tlen = strlen(sc_token);
            mon = atol(sc_token);
@@ -498,25 +546,27 @@ month(tm)
                    token();
                }
 
                    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;
                }
                 */
                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) {
                if (tlen == 8) {
-                   year = (mon % 10000) - 1900;
+                   year = (mon % 10000) - TM_YEAR_BASE;
                    mon /= 10000;
                    mon /= 10000;
-               } else {
+               }
+               else {
                    year = mon % 100;
                    mon /= 100;
                }
                mday = mon % 100;
                mon /= 100;
                    year = mon % 100;
                    mon /= 100;
                }
                mday = mon % 100;
                mon /= 100;
-           } else
+           }
+           else
                panic("garbled time");
 
            mon--;
                panic("garbled time");
 
            mon--;
@@ -532,12 +582,9 @@ month(tm)
 /* Global functions */
 
 time_t
 /* 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;
  * should be run.
  */
     time_t nowtimer, runtimer;
@@ -558,7 +605,11 @@ parsetime(argc, argv)
     init_scanner(argc-optind, argv+optind);
 
     switch (token()) {
     init_scanner(argc-optind, argv+optind);
 
     switch (token()) {
-    case NOW:  /* now is optional prefix for PLUS tree */
+    case NOW:  
+           if (scc < 1) {
+               return nowtimer;
+           }
+           /* now is optional prefix for PLUS tree */
            expect(PLUS);
     case PLUS:
            plus(&runtime);
            expect(PLUS);
     case PLUS:
            plus(&runtime);
@@ -569,8 +620,7 @@ parsetime(argc, argv)
            month(&runtime);
            break;
 
            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
             * 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
@@ -582,20 +632,21 @@ parsetime(argc, argv)
     case NOON:
            hr += 12;
     case MIDNIGHT:
     case NOON:
            hr += 12;
     case MIDNIGHT:
-           if (runtime.tm_hour >= hr)
+           if (runtime.tm_hour >= hr) {
                runtime.tm_mday++;
                runtime.tm_mday++;
+               runtime.tm_wday++;
+           }
            runtime.tm_hour = hr;
            runtime.tm_min = 0;
            token();
            runtime.tm_hour = hr;
            runtime.tm_min = 0;
            token();
-           /* fall through to month setting */
+           /* FALLTHROUGH to month setting */
     default:
            month(&runtime);
            break;
     } /* ugly case statement */
     expect(EOF);
 
     default:
            month(&runtime);
            break;
     } /* ugly case statement */
     expect(EOF);
 
-    /*
-     * adjust for daylight savings time
+    /* adjust for daylight savings time
      */
     runtime.tm_isdst = -1;
     runtimer = mktime(&runtime);
      */
     runtime.tm_isdst = -1;
     runtimer = mktime(&runtime);
@@ -608,7 +659,7 @@ parsetime(argc, argv)
        panic("garbled time");
 
     if (nowtimer > runtimer)
        panic("garbled time");
 
     if (nowtimer > runtimer)
-       panic("Trying to travel back in time");
+       panic("trying to travel back in time");
 
     return runtimer;
 } /* parsetime */
 
     return runtimer;
 } /* parsetime */
index c2e792bf7906d8afd34a639ed0cf06880bf34c6b..30c3f207aa3d3916b43689ba86aeea22d13e23b1 100644 (file)
@@ -1,30 +1,6 @@
-/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
- * Reserved.  This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License').  You may not use this file
- * except in compliance with the License.  Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License."
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-/*
- * 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
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * 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.
  * 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,
+ * IN NO EVENT SHALL THE AUTHOR(S) 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.
  * 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.
- *
- *     $Id: parsetime.h,v 1.1.1.2 2000/01/11 02:10:05 wsanchez Exp $
  */
 
  */
 
-time_t parsetime       __P((int argc, char **argv));
+time_t parsetime(int argc, char **argv);
index 0a5ba303a56c602f2374669ef7533d37dabdfd53..872ce44dbae7b4525d7b3678173a61646a3e8f67 100644 (file)
@@ -1,26 +1,3 @@
-/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
- * Reserved.  This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License').  You may not use this file
- * except in compliance with the License.  Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License."
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
 /* 
  *  perm.c - check user permission for at(1)
  *  Copyright (C) 1994  Thomas Koenig
 /* 
  *  perm.c - check user permission for at(1)
  *  Copyright (C) 1994  Thomas Koenig
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/at/perm.c,v 1.13 2001/12/10 21:13:01 dwmalone Exp $");
+
 /* System Headers */
 
 #include <sys/types.h>
 /* System Headers */
 
 #include <sys/types.h>
+#include <err.h>
 #include <errno.h>
 #include <pwd.h>
 #include <stddef.h>
 #include <errno.h>
 #include <pwd.h>
 #include <stddef.h>
@@ -59,9 +40,9 @@
 
 /* Local headers */
 
 
 /* Local headers */
 
-#include "privs.h"
 #include "at.h"
 #include "at.h"
-#include "pathnames.h"
+#include "perm.h"
+#include "privs.h"
 
 /* Macros */
 
 
 /* Macros */
 
 
 /* Structures and unions */
 
 
 /* Structures and unions */
 
-/* File scope variables */
-
-static char rcsid[] = "$Id: perm.c,v 1.1.1.2 2000/01/11 02:10:05 wsanchez Exp $";
-
 /* Function declarations */
 
 static int check_for_user(FILE *fp,const char *name);
 /* Function declarations */
 
 static int check_for_user(FILE *fp,const char *name);
@@ -86,10 +63,8 @@ static int check_for_user(FILE *fp,const char *name)
     int found = 0;
 
     len = strlen(name);
     int found = 0;
 
     len = strlen(name);
-    if ((buffer = malloc(sizeof (char) * (len+2))) == NULL) {
-       fprintf(stderr, "malloc error!");
-       exit(EXIT_FAILURE);
-    }
+    if ((buffer = malloc(len+2)) == NULL)
+       errx(EXIT_FAILURE, "virtual memory exhausted");
 
     while(fgets(buffer, len+2, fp) != NULL)
     {
 
     while(fgets(buffer, len+2, fp) != NULL)
     {
@@ -105,7 +80,7 @@ static int check_for_user(FILE *fp,const char *name)
     return found;
 }
 /* Global functions */
     return found;
 }
 /* Global functions */
-int check_permission()
+int check_permission(void)
 {
     FILE *fp;
     uid_t uid = geteuid();
 {
     FILE *fp;
     uid_t uid = geteuid();
@@ -115,14 +90,11 @@ int check_permission()
        return 1;
 
     if ((pentry = getpwuid(uid)) == NULL)
        return 1;
 
     if ((pentry = getpwuid(uid)) == NULL)
-    {
-       perror("Cannot access user database");
-       exit(EXIT_FAILURE);
-    }
+       err(EXIT_FAILURE, "cannot access user database");
 
     PRIV_START
 
 
     PRIV_START
 
-    fp=fopen(_PATH_AT "at.allow","r");
+    fp=fopen(PERM_PATH "at.allow","r");
 
     PRIV_END
 
 
     PRIV_END
 
@@ -130,12 +102,12 @@ int check_permission()
     {
        return check_for_user(fp, pentry->pw_name);
     }
     {
        return check_for_user(fp, pentry->pw_name);
     }
-    else
+    else if (errno == ENOENT)
     {
 
        PRIV_START
 
     {
 
        PRIV_START
 
-       fp=fopen(_PATH_AT "at.deny", "r");
+       fp=fopen(PERM_PATH "at.deny", "r");
 
        PRIV_END
 
 
        PRIV_END
 
@@ -143,7 +115,10 @@ int check_permission()
        {
            return !check_for_user(fp, pentry->pw_name);
        }
        {
            return !check_for_user(fp, pentry->pw_name);
        }
-       perror("at.deny");
+       else if (errno != ENOENT)
+           warn("at.deny");
     }
     }
+    else
+       warn("at.allow");
     return 0;
 }
     return 0;
 }
index 8d14b37206342fe0671bfb63f19cedf55e43a324..92d0e31f10d7cbba6d887d7de0094e433d73286c 100644 (file)
@@ -1,26 +1,3 @@
-/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
- * Reserved.  This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License').  You may not use this file
- * except in compliance with the License.  Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License."
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
 /* 
  *  perm.h -  header for at(1)
  *  Copyright (C) 1994  Thomas Koenig
 /* 
  *  perm.h -  header for at(1)
  *  Copyright (C) 1994  Thomas Koenig
@@ -44,6 +21,8 @@
  * 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.
  * 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.
+ *
+ * $FreeBSD: src/usr.bin/at/perm.h,v 1.4 2001/12/02 12:26:18 markm Exp $
  */
 
  */
 
-int check_permission();
+int check_permission(void);
index ae37877e3be4a12dc09ff3e19855879ec2d8e6cd..9c5e39d88279e867707e45ee14eb755b91465198 100644 (file)
@@ -1,30 +1,6 @@
-/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
- * Reserved.  This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License').  You may not use this file
- * except in compliance with the License.  Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License."
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-/*
- * 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
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -38,7 +14,7 @@
  * 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.
  * 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,
+ * IN NO EVENT SHALL THE AUTHOR(S) 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
  * 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
@@ -46,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * (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.1.2 2000/01/11 02:10:05 wsanchez Exp $
+ * $FreeBSD: src/usr.bin/at/privs.h,v 1.8 2001/09/04 16:15:51 ru Exp $
  */
 
 #ifndef _PRIVS_H
  */
 
 #ifndef _PRIVS_H
 
 #include <unistd.h>
 
 
 #include <unistd.h>
 
-/* Relinquish privileges temporarily for a setuid 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
+/* Relinquish privileges temporarily for a setuid or setgid program
+ * with the option of getting them back later.  This is done by
+ * utilizing POSIX saved user and group IDs.  Call RELINQUISH_PRIVS once
+ * at the beginning of the main program.  This will cause all operations
  * to be executed with the real userid.  When you need the privileges
  * 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.
  *
  * 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
  * 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, 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
  * is the user's.
  *
  * Problems: Do not use return between PRIV_START and PRIV_END; this
@@ -79,7 +55,7 @@
  * It is NOT safe to call exec(), system() or popen() with a user-
  * supplied program (i.e. without carefully checking PATH and any
  * library load paths) with relinquished privileges; the called program
  * It is NOT safe to call exec(), system() or popen() with a user-
  * supplied program (i.e. without carefully checking PATH and any
  * library load paths) with relinquished privileges; the called program
- * can aquire them just as easily.  Set both effective and real userid
+ * can acquire them just as easily.  Set both effective and real userid
  * to the real userid before calling any of them.
  */
 
  * to the real userid before calling any of them.
  */
 
@@ -88,28 +64,47 @@ extern
 #endif
 uid_t real_uid, effective_uid;
 
 #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(); \
 #define RELINQUISH_PRIVS { \
        real_uid = getuid(); \
        effective_uid = geteuid(); \
+       real_gid = getgid(); \
+       effective_gid = getegid(); \
+       setegid(real_gid); \
        seteuid(real_uid); \
 }
 
        seteuid(real_uid); \
 }
 
-#define RELINQUISH_PRIVS_ROOT(a) { \
+#define RELINQUISH_PRIVS_ROOT(a, b) { \
        real_uid = (a); \
        effective_uid = geteuid(); \
        real_uid = (a); \
        effective_uid = geteuid(); \
+       real_gid = (b); \
+       effective_gid = getegid(); \
+       setegid(real_gid); \
        seteuid(real_uid); \
 }
 
 #define PRIV_START { \
        seteuid(real_uid); \
 }
 
 #define PRIV_START { \
-       seteuid(effective_uid);
+       if (seteuid(0)<0) perr("cannot regain privs"); \
+       if (setegid(effective_gid)<0) perr("cannot reset gid"); \
+       if (seteuid(effective_uid)<0) perr("cannot reset uid"); \
+}
 
 
-#define PRIV_END \
-       seteuid(real_uid); \
+#define PRIV_END { \
+       if (seteuid(0)<0) perr("cannot regain privs"); \
+       if (setegid(real_gid)<0) perr("cannot reset gid"); \
+       if (seteuid(real_uid)<0) perr("cannot reset uid"); \
 }
 
 }
 
-#define REDUCE_PRIV(a) { \
-       seteuid(effective_uid); \
-       real_uid = effective_uid = (a); \
-       setuid(real_uid); \
+#define REDUCE_PRIV(a, b) { \
+       PRIV_START \
+       effective_uid = (a); \
+       effective_gid = (b); \
+       setregid((gid_t)-1, effective_gid); \
+       setreuid((uid_t)-1, effective_uid); \
+       PRIV_END \
 }
 #endif
 }
 #endif
index 8150fd193cd336d043f70628297c4f20f39e4067..b2bb44b28e057f559efa1faa27d1a8577e8ee9c2 100644 (file)
@@ -16,7 +16,9 @@ HFILES = atrun.h
 
 CFILES = atrun.c
 
 
 CFILES = atrun.c
 
-OTHERSRCS = Makefile.preamble Makefile Makefile.dist atrun.8
+OTHERSRCS = Makefile.preamble Makefile Makefile.dist atrun.8 Makefile.postamble
+
+NEXTSTEP_PB_CFLAGS += -DDAEMON_UID=1 -DDAEMON_GID=1
 
 
 MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
 
 
 MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
@@ -30,7 +32,7 @@ DEBUG_LIBS = $(LIBS)
 PROF_LIBS = $(LIBS)
 
 
 PROF_LIBS = $(LIBS)
 
 
-HEADER_PATHS = -I../at
+HEADER_PATHS = -I../at.tproj
 
 
 NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
 
 
 NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
diff --git a/atrun.tproj/Makefile.postamble b/atrun.tproj/Makefile.postamble
new file mode 100644 (file)
index 0000000..dd90965
--- /dev/null
@@ -0,0 +1,3 @@
+after_install:
+       mkdir -p $(DSTROOT)/usr/share/man/man8
+       install -c -m 444 atrun.8 $(DSTROOT)/usr/share/man/man8
index f9e31bd21c8ceaa87a00b398b0e614ec231ffa51..9d52ffa888ecfc55b845f75b41bdeab1b1c1b7ab 100644 (file)
@@ -1,30 +1,6 @@
-/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
- * Reserved.  This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License').  You may not use this file
- * except in compliance with the License.  Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License."
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-/*
- * atrun.c - run jobs queued by at; run with root privileges.
- * Copyright (c) 1993 by Thomas Koenig
- * All rights reserved.
+/* 
+ *  atrun.c - run jobs queued by at; run with root privileges.
+ *  Copyright (C) 1993, 1994 Thomas Koenig
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -38,7 +14,7 @@
  * 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.
  * 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,
+ * IN NO EVENT SHALL THE AUTHOR(S) 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
  * 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
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifndef lint
+static const char rcsid[] =
+  "$FreeBSD: src/libexec/atrun/atrun.c,v 1.18 2004/07/11 17:37:32 stefanf Exp $";
+#endif /* not lint */
+
 /* System Headers */
 
 #include <sys/fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 /* System Headers */
 
 #include <sys/fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
+#include <sys/param.h>
+#include <ctype.h>
 #include <dirent.h>
 #include <dirent.h>
-#include <errno.h>
+#include <err.h>
+#include <grp.h>
 #include <pwd.h>
 #include <signal.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <pwd.h>
 #include <signal.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <syslog.h>
 #include <time.h>
 #include <unistd.h>
 #include <time.h>
 #include <unistd.h>
-#include <syslog.h>
-
+#include <utmp.h>
+#if 1
 #include <paths.h>
 #include <paths.h>
+#else
+#include <getopt.h>
+#endif
+
+#if (MAXLOGNAME-1) > UT_NAMESIZE
+#define LOGNAMESIZE UT_NAMESIZE
+#else
+#define LOGNAMESIZE (MAXLOGNAME-1)
+#endif
 
 /* Local headers */
 
 #define MAIN
 #include "privs.h"
 #include "pathnames.h"
 
 /* Local headers */
 
 #define MAIN
 #include "privs.h"
 #include "pathnames.h"
-#include "atrun.h"
+/* Macros */
+
+#ifndef ATJOB_DIR
+#define ATJOB_DIR _PATH_ATJOBS
+#endif
+
+#ifndef ATSPOOL_DIR
+#define ATSPOOL_DIR _PATH_ATSPOOL
+#endif
+
+#ifndef LOADAVG_MX
+#define LOADAVG_MX 1.5
+#endif
 
 /* File scope variables */
 
 
 /* File scope variables */
 
-static char *namep;
-static char rcsid[] = "$Id: atrun.c,v 1.1.1.2 2000/01/11 02:10:06 wsanchez Exp $";
+static int debug = 0;
+
+void perr(const char *a);
+static void usage(void);
 
 /* Local functions */
 
 /* Local functions */
-static void
-perr(a)
-       const char *a;
+static int
+write_string(int fd, const char* a)
 {
 {
-       syslog(LOG_ERR, "%s: %m", a);
-       exit(EXIT_FAILURE);
+    return write(fd, a, strlen(a));
 }
 
 }
 
-static int
-write_string(fd, a)
-       int fd;
-       const char *a;
+#undef DEBUG_FORK
+#ifdef DEBUG_FORK
+static pid_t
+myfork(void)
 {
 {
-       return write(fd, a, strlen(a));
+       pid_t res;
+       res = fork();
+       if (res == 0)
+           kill(getpid(),SIGSTOP);
+       return res;
 }
 
 }
 
+#define fork myfork
+#endif
+
 static void
 static void
-run_file(filename, uid, gid)
-       const char *filename;
-       uid_t uid;
-       gid_t gid;
+run_file(const 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 spawns another
-        * process to send mail to the user.
-        */
-       pid_t pid;
-       int fd_out, fd_in;
-       int queue;
-       char mailbuf[9];
-       char *mailname = NULL;
-       FILE *stream;
-       int send_mail = 0;
-       struct stat buf;
-       off_t size;
-       struct passwd *pentry;
-       int fflags;
-
-       pid = fork();
-       if (pid == -1)
-               perr("Cannot fork");
-       else if (pid > 0)
-               return;
-
-       /*
-        * Let's see who we mail to.  Hopefully, we can read it from the
-        * command file; if not, send it to the owner, or, failing that, to
-        * root.
-        */
+/* Run a file by spawning off a process which redirects I/O,
+ * spawns a subshell, then waits for it to complete and sends
+ * mail to the user.
+ */
+    pid_t pid;
+    int fd_out, fd_in;
+    int queue;
+    char mailbuf[LOGNAMESIZE + 1], fmt[49];
+    char *mailname = NULL;
+    FILE *stream;
+    int send_mail = 0;
+    struct stat buf, lbuf;
+    off_t size;
+    struct passwd *pentry;
+    int fflags;
+    long nuid;
+    long ngid;
+
+
+    PRIV_START
+
+    if (chmod(filename, S_IRUSR) != 0)
+    {
+       perr("cannot change file permissions");
+    }
+
+    PRIV_END
+
+    pid = fork();
+    if (pid == -1)
+       perr("cannot fork");
+    
+    else if (pid != 0)
+       return;
+
+    /* Let's see who we mail to.  Hopefully, we can read it from
+     * the command file; if not, send it to the owner, or, failing that,
+     * to root.
+     */
+
+    pentry = getpwuid(uid);
+    if (pentry == NULL)
+    {
+       syslog(LOG_ERR,"Userid %lu not found - aborting job %s",
+              (unsigned long) uid, filename);
+        exit(EXIT_FAILURE);
+    }
+    PRIV_START
+
+    stream=fopen(filename, "r");
+
+    PRIV_END
+
+#ifdef __FreeBSD__
+    if (pentry->pw_expire && time(NULL) >= pentry->pw_expire)
+    {
+       syslog(LOG_ERR, "Userid %lu is expired - aborting job %s",
+               (unsigned long) uid, filename);
+       exit(EXIT_FAILURE);
+    }
+#endif
 
 
-       PRIV_START
+    if (stream == NULL)
+       perr("cannot open input file");
 
 
-           stream = fopen(filename, "r");
+    if ((fd_in = dup(fileno(stream))) <0)
+       perr("error duplicating input file descriptor");
 
 
-       PRIV_END
+    if (fstat(fd_in, &buf) == -1)
+       perr("error in fstat of input file descriptor");
 
 
-       if (stream == NULL)
-               perr("Cannot open input file");
+    if (lstat(filename, &lbuf) == -1)
+       perr("error in fstat of input file");
 
 
-       if ((fd_in = dup(fileno(stream))) < 0)
-               perr("Error duplicating input file descriptor");
+    if (S_ISLNK(lbuf.st_mode)) {
+       syslog(LOG_ERR,"Symbolic link encountered in job %s - aborting",
+               filename);
+       exit(EXIT_FAILURE);
+    }
+    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)) {
+       syslog(LOG_ERR,"Somebody changed files from under us for job %s - "
+       "aborting",filename);
+       exit(EXIT_FAILURE);
+    }
+    if (buf.st_nlink > 1) {
+       syslog(LOG_ERR,"Someboy is trying to run a linked script for job %s",
+               filename);
+       exit(EXIT_FAILURE);
+    }
+    if ((fflags = fcntl(fd_in, F_GETFD)) <0)
+       perr("error in fcntl");
 
 
-       if ((fflags = fcntl(fd_in, F_GETFD)) < 0)
-               perr("Error in fcntl");
+    fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC);
 
 
-       fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC);
+    snprintf(fmt, sizeof(fmt),
+       "#!/bin/sh\n# atrun uid=%%ld gid=%%ld\n# mail %%%ds %%d",
+                          LOGNAMESIZE);
+    if (fscanf(stream, fmt, &nuid, &ngid, mailbuf, &send_mail) != 4) {
+       syslog(LOG_ERR,"File %s is in wrong format - aborting", filename);
+       exit(EXIT_FAILURE);
+    }
+    if (mailbuf[0] == '-') {
+       syslog(LOG_ERR,"illegal mail name %s in %s",mailbuf,filename);
+       exit(EXIT_FAILURE);
+    }
+    mailname = mailbuf;
+    if (nuid != uid) {
+       syslog(LOG_ERR,"Job %s - userid %ld does not match file uid %lu",
+               filename, nuid, (unsigned long)uid);
+       exit(EXIT_FAILURE);
+    }
+    if (ngid != gid) {
+       syslog(LOG_ERR,"Job %s - groupid %ld does not match file gid %lu",
+               filename, ngid, (unsigned long)gid);
+       exit(EXIT_FAILURE);
+    }
+    fclose(stream);
+    if (chdir(ATSPOOL_DIR) < 0)
+       perr("cannot chdir to " ATSPOOL_DIR);
+    
+    /* Create a file to hold the output of the job we are about to run.
+     * Write the mail header.
+     */    
+    if((fd_out=open(filename,
+               O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) < 0)
+       perr("cannot create output file");
+
+    write_string(fd_out, "Subject: Output from your job ");
+    write_string(fd_out, filename);
+    write_string(fd_out, "\n\n");
+    fstat(fd_out, &buf);
+    size = buf.st_size;
+
+    close(STDIN_FILENO);
+    close(STDOUT_FILENO);
+    close(STDERR_FILENO);
+    pid = fork();
+    if (pid < 0)
+       perr("error in fork");
+
+    else if (pid == 0)
+    {
+       char *nul = NULL;
+       char **nenvp = &nul;
+
+       /* Set up things for the child; we want standard input from the input file,
+        * and standard output and error sent to our output file.
+        */
 
 
-       if (fscanf(stream, "#! /bin/sh\n# mail %8s %d", mailbuf, &send_mail) == 2) {
-               mailname = mailbuf;
-       } else {
-               pentry = getpwuid(uid);
-               if (pentry == NULL)
-                       mailname = "root";
-               else
-                       mailname = pentry->pw_name;
-       }
-       fclose(stream);
-       if (chdir(_PATH_ATSPOOL) < 0)
-               perr("Cannot chdir to " _PATH_ATSPOOL);
+       if (lseek(fd_in, (off_t) 0, SEEK_SET) < 0)
+           perr("error in lseek");
 
 
-       /*
-        * Create a file to hold the output of the job we are  about to
-        * run. Write the mail header.
-        */
-       if ((fd_out = open(filename,
-                   O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) < 0)
-               perr("Cannot create output file");
+       if (dup(fd_in) != STDIN_FILENO)
+           perr("error in I/O redirection");
+
+       if (dup(fd_out) != STDOUT_FILENO)
+           perr("error in I/O redirection");
+
+       if (dup(fd_out) != STDERR_FILENO)
+           perr("error in I/O redirection");
 
 
-       write_string(fd_out, "Subject: Output from your job ");
-       write_string(fd_out, filename);
-       write_string(fd_out, "\n\n");
-       fstat(fd_out, &buf);
-       size = buf.st_size;
+       close(fd_in);
+       close(fd_out);
+       if (chdir(ATJOB_DIR) < 0)
+           perr("cannot chdir to " ATJOB_DIR);
 
 
-       close(STDIN_FILENO);
-       close(STDOUT_FILENO);
-       close(STDERR_FILENO);
+       queue = *filename;
 
 
-       pid = fork();
-       if (pid < 0)
-               perr("Error in fork");
-       else if (pid == 0) {
-               char *nul = NULL;
-               char **nenvp = &nul;
+       PRIV_START
 
 
-               /*
-                * Set up things for the child; we want standard input from
-                * the input file, and standard output and error sent to
-                * our output file.
-                */
+        nice(tolower(queue) - 'a');
+       
+       if (initgroups(pentry->pw_name,pentry->pw_gid))
+           perr("cannot delete saved userids");
 
 
-               if (lseek(fd_in, (off_t) 0, SEEK_SET) < 0)
-                       perr("Error in lseek");
+       if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0)
+           perr("cannot change group");
 
 
-               if (dup(fd_in) != STDIN_FILENO)
-                       perr("Error in I/O redirection");
+       if (setlogin(pentry->pw_name))
+           perr("cannot set login name");
 
 
-               if (dup(fd_out) != STDOUT_FILENO)
-                       perr("Error in I/O redirection");
+       if (setuid(uid) < 0 || seteuid(uid) < 0)
+           perr("cannot set user id");
 
 
-               if (dup(fd_out) != STDERR_FILENO)
-                       perr("Error in I/O redirection");
+       if (chdir(pentry->pw_dir))
+               chdir("/");
 
 
-               close(fd_in);
-               close(fd_out);
-               if (chdir(_PATH_ATJOBS) < 0)
-                       perr("Cannot chdir to " _PATH_ATJOBS);
+       if(execle("/bin/sh","sh",(char *) NULL, nenvp) != 0)
+           perr("exec failed for /bin/sh");
 
 
-               queue = *filename;
+       PRIV_END
+    }
+    /* We're the parent.  Let's wait.
+     */
+    close(fd_in);
+    close(fd_out);
+    waitpid(pid, (int *) NULL, 0);
+
+    /* Send mail.  Unlink the output file first, so it is deleted after
+     * the run.
+     */
+    stat(filename, &buf);
+    if (open(filename, O_RDONLY) != STDIN_FILENO)
+        perr("open of jobfile failed");
+
+    unlink(filename);
+    if ((buf.st_size != size) || send_mail)
+    {    
+       PRIV_START
 
 
-               PRIV_START
+       if (initgroups(pentry->pw_name,pentry->pw_gid))
+           perr("cannot delete saved userids");
 
 
-                   if (queue > 'b')
-                       nice(queue - 'b');
+       if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0)
+           perr("cannot change group");
 
 
-               if (setgid(gid) < 0)
-                       perr("Cannot change group");
+       if (setlogin(pentry->pw_name))
+           perr("cannot set login name");
 
 
-               if (setuid(uid) < 0)
-                       perr("Cannot set user id");
+       if (setuid(uid) < 0 || seteuid(uid) < 0)
+           perr("cannot set user id");
 
 
+       if (chdir(pentry->pw_dir))
                chdir("/");
 
                chdir("/");
 
-               if (execle("/bin/sh", "sh", (char *) NULL, nenvp) != 0)
-                       perr("Exec failed");
+#if 1
+       execl(_PATH_SENDMAIL, "sendmail", "-F", "Atrun Service",
+                       "-odi", "-oem",
+                       mailname, (char *) NULL);
+#else
+        execl(MAIL_CMD, MAIL_CMD, mailname, (char *) NULL);
+#endif
+           perr("exec failed for mail command");
 
 
-               PRIV_END
-       }
-       /* We're the parent.  Let's wait. */
-       close(fd_in);
-       close(fd_out);
-       waitpid(pid, (int *) NULL, 0);
-
-       stat(filename, &buf);
-       if ((buf.st_size != size) || send_mail) {
-               /* Fork off a child for sending mail */
-               pid = fork();
-               if (pid < 0)
-                       perr("Fork failed");
-               else if (pid == 0) {
-                       if (open(filename, O_RDONLY) != STDIN_FILENO)
-                               perr("Cannot reopen output file");
-
-                       execl(_PATH_SENDMAIL, _PATH_SENDMAIL, mailname,
-                           (char *) NULL);
-                       perr("Exec failed");
-               }
-               waitpid(pid, (int *) NULL, 0);
-       }
-       unlink(filename);
-       exit(EXIT_SUCCESS);
+       PRIV_END
+    }
+    exit(EXIT_SUCCESS);
 }
 
 /* Global functions */
 
 }
 
 /* Global functions */
 
+/* Needed in gloadavg.c */
+void
+perr(const char *a)
+{
+    if (debug)
+    {
+       warn("%s", a);
+    }
+    else
+       syslog(LOG_ERR, "%s: %m", a);
+
+    exit(EXIT_FAILURE);
+}
+
 int
 int
-main(argc, argv)
-       int argc;
-       char *argv[];
+main(int argc, char *argv[])
 {
 {
-       /*
-        * Browse through  _PATH_ATJOBS, checking all the jobfiles wether
-        * 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 which has not been executed
-        * yet is denoted by its execute - bit set.  For those files which
-        * are to be executed, run_file() is called, which forks off a
-        * child which takes care of I/O redirection, forks off another
-        * child for execution and yet another one, optionally, for sending
-        * mail.  Files which already have run are removed during the
-        * next invocation.
+/* Browse through  ATJOB_DIR, checking all the jobfiles wether 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
+ * which has not been executed yet is denoted by its execute - bit set.
+ * For those files which are to be executed, run_file() is called, which forks
+ * off a child which takes care of I/O redirection, forks off another child
+ * for execution and yet another one, optionally, for sending mail.
+ * Files which already have run are removed during the next invocation.
+ */
+    DIR *spool;
+    struct dirent *dirent;
+    struct stat buf;
+    unsigned long ctm;
+    unsigned long jobno;
+    char queue;
+    time_t now, run_time;
+    char batch_name[] = "Z2345678901234";
+    uid_t batch_uid;
+    gid_t batch_gid;
+    int c;
+    int run_batch;
+    double load_avg = LOADAVG_MX, la;
+
+/* We don't need root privileges all the time; running under uid and gid daemon
+ * is fine.
+ */
+
+    RELINQUISH_PRIVS_ROOT(DAEMON_UID, DAEMON_GID)
+
+    openlog("atrun", LOG_PID, LOG_CRON);
+
+    opterr = 0;
+    while((c=getopt(argc, argv, "dl:"))!= -1)
+    {
+       switch (c)
+       {
+       case 'l': 
+           if (sscanf(optarg, "%lf", &load_avg) != 1)
+               perr("garbled option -l");
+           if (load_avg <= 0.)
+               load_avg = LOADAVG_MX;
+           break;
+
+       case 'd':
+           debug ++;
+           break;
+
+       case '?':
+       default:
+           usage();
+       }
+    }
+
+    if (chdir(ATJOB_DIR) != 0)
+       perr("cannot change to " ATJOB_DIR);
+
+    /* Main loop. Open spool directory for reading and look over all the
+     * files in there. If the filename indicates that the job should be run
+     * and the x bit is set, fork off a child which sets its user and group
+     * id to that of the files and exec a /bin/sh which executes the shell
+     * script. Unlink older files if they should no longer be run.  For
+     * deletion, their r bit has to be turned on.
+     *
+     * Also, pick the oldest batch job to run, at most one per invocation of
+     * atrun.
+     */
+    if ((spool = opendir(".")) == NULL)
+       perr("cannot read " ATJOB_DIR);
+
+    now = time(NULL);
+    run_batch = 0;
+    batch_uid = (uid_t) -1;
+    batch_gid = (gid_t) -1;
+
+    while ((dirent = readdir(spool)) != NULL) {
+       if (stat(dirent->d_name,&buf) != 0)
+           perr("cannot stat in " ATJOB_DIR);
+
+       /* We don't want directories
         */
         */
-       DIR *spool;
-       struct dirent *dirent;
-       struct stat buf;
-       int older;
-       unsigned long ctm;
-       char queue;
-
-       /*
-        * We don't need root privileges all the time; running under uid
-        * and gid daemon is fine.
+       if (!S_ISREG(buf.st_mode)) 
+           continue;
+
+       if (sscanf(dirent->d_name,"%c%5lx%8lx",&queue,&jobno,&ctm) != 3)
+           continue;
+
+       run_time = (time_t) ctm*60;
+
+       if ((S_IXUSR & buf.st_mode) && (run_time <=now)) {
+           if (isupper(queue) && (strcmp(batch_name,dirent->d_name) > 0)) {
+               run_batch = 1;
+               strncpy(batch_name, dirent->d_name, sizeof(batch_name));
+               batch_uid = buf.st_uid;
+               batch_gid = buf.st_gid;
+           }
+       
+       /* The file is executable and old enough
         */
         */
-
-       RELINQUISH_PRIVS_ROOT(0) /* it's setuid root */
-       openlog("atrun", LOG_PID, LOG_CRON);
-
-       namep = argv[0];
-       if (chdir(_PATH_ATJOBS) != 0)
-               perr("Cannot change to " _PATH_ATJOBS);
-
-       /*
-        * Main loop. Open spool directory for reading and look over all
-        * the files in there. If the filename indicates that the job
-        * should be run and the x bit is set, fork off a child which sets
-        * its user and group id to that of the files and exec a /bin/sh
-        * which executes the shell script. Unlink older files if they
-        * should no longer be run.  For deletion, their r bit has to be
-        * turned on.
+           if (islower(queue))
+               run_file(dirent->d_name, buf.st_uid, buf.st_gid);
+       }
+       /*  Delete older files
         */
         */
-       if ((spool = opendir(".")) == NULL)
-               perr("Cannot read " _PATH_ATJOBS);
-
-       while ((dirent = readdir(spool)) != NULL) {
-               double la;
-
-               if (stat(dirent->d_name, &buf) != 0)
-                       perr("Cannot stat in " _PATH_ATJOBS);
-
-               /* We don't want directories */
-               if (!S_ISREG(buf.st_mode))
-                       continue;
-
-               if (sscanf(dirent->d_name, "%c%8lx", &queue, &ctm) != 2)
-                       continue;
-
-               if ((queue == 'b') && ((getloadavg(&la, 1) != 1) ||
-                   (la > ATRUN_MAXLOAD)))
-                       continue;
-
-               older = (time_t) ctm *60 <= time(NULL);
-
-               /* The file is executable and old enough */
-               if (older && (S_IXUSR & buf.st_mode)) {
-                       /*
-                        * Now we know we want to run the file, we can turn
-                        * off the execute bit
-                        */
-
-                       PRIV_START
-
-                           if (chmod(dirent->d_name, S_IRUSR) != 0)
-                               perr("Cannot change file permissions");
+       if ((run_time < now) && !(S_IXUSR & buf.st_mode) && (S_IRUSR & buf.st_mode))
+           unlink(dirent->d_name);
+    }
+    /* run the single batch file, if any
+    */
+    if (run_batch && (getloadavg(&la, 1) == 1) && la < load_avg)
+       run_file(batch_name, batch_uid, batch_gid);
+
+    closelog();
+    exit(EXIT_SUCCESS);
+}
 
 
-                       PRIV_END
+static void
+usage(void)
+{
+    if (debug)
+       fprintf(stderr, "usage: atrun [-l load_avg] [-d]\n");
+    else
+       syslog(LOG_ERR, "usage: atrun [-l load_avg] [-d]"); 
 
 
-                       run_file(dirent->d_name, buf.st_uid, buf.st_gid);
-               }
-               /* Delete older files */
-               if (older && !(S_IXUSR & buf.st_mode) &&
-                   (S_IRUSR & buf.st_mode))
-                       unlink(dirent->d_name);
-       }
-       closelog();
-       exit(EXIT_SUCCESS);
+    exit(EXIT_FAILURE);
 }
 }