home *** CD-ROM | disk | FTP | other *** search
- From: keith@sequoia.execu.com (Keith Pyle)
- Newsgroups: alt.sources
- Subject: Keith's at package, part 2 of 2
- Message-ID: <32217@sequoia.execu.com>
- Date: 14 Feb 91 04:46:51 GMT
-
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 2 (of 2)."
- # Contents: doc/at.1 src/at.c src/atq.c src/atrun.c src/date.y
- # Wrapped by keith@lime on Wed Feb 13 22:33:50 1991
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'doc/at.1' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'doc/at.1'\"
- else
- echo shar: Extracting \"'doc/at.1'\" \(7841 characters\)
- sed "s/^X//" >'doc/at.1' <<'END_OF_FILE'
- X.TH "AT" 1L "7 November 1990" "Execucom" "LOCAL USER COMMANDS"
- X.SH NAME
- Xat \- execute commands at a specified time
- X.SH SYNOPSIS
- X.LP
- X.B at
- X[
- X.B \-m
- X] [
- X.B \-cks
- X]
- X.I time
- X[
- X.I date
- X] [
- X.B \+increment
- X] [
- X.I script
- X]
- X.SH DESCRIPTION
- X.B at
- Xis a utility which accepts user commands to be executed at a specified later
- Xtime. The execution time can be given as a time only, as a time and date,
- Xor as either of these with an increment added. If a
- X.I script
- Xis specified, this file will be read to obtain the commands that will be
- Xexecuted. Otherwise,
- X.B at
- Xwill read standard input for the commands to be queued. If standard input
- Xis a terminal device,
- X.B at
- Xwill prompt for input with the string
- X.LP
- X.RS
- Xat>
- X.RE
- X.LP
- XThe shell used to process the commands will be determined as follows: (1) if
- Xa command line argument is used to specify a shell, that shell will be used,
- X(2) if the commands come from a
- X.I script
- Xand that script specifies a shell on its first line, e.g., #!/bin/sh, the
- Xindicated shell will be used, or (3) the shell specified in the current
- XSHELL environment variable will be used.
- X.LP
- XIf the queued job generates output on either standard output or standard
- Xerror, the output will be mailed to the user who submitted the job. If no
- Xoutput is generated or if it is redirected in the commands, there will be
- Xno mail to the user unless either (1) the commands terminated with an
- Xerror status, or (2) the user requests confirmation of the run through the
- X.B \-m
- Xoption.
- X.LP
- XThe user's environment variables (except TERM), current directory, and
- Xumask are copied to the queued job when it is created. If
- X.I script
- Xis specified, its contents are copied to the queued job. Thus, subsequent
- Xchanges to
- X.I script
- Xor its deletion will not affect the
- X.B at
- Xjob.
- X.LP
- XThe format of
- X.I time
- Xis quite flexible. It may consist of one or two digits to indicate hours only.
- XIf three or four digits are given, they are taken to indicate hours and minutes.
- XAn optional colon between the hours and minutes is allowed. The hours
- Xspecification is assumed to be a 24 hour clock reference unless an am or pm
- Xsuffix is present. The time zone may be included; if not, the current time
- Xzone is assumed. The special times
- X.B now, noon,
- Xand
- X.B midnight
- Xare also recognized. Case is not significant for any of the character fields.
- X.LP
- XThe
- X.I date
- Xis optional and may be any of the following forms: (1) month name and day,
- X(2) month name, day, comma, and year, (3) day and month name, (4) day, month
- Xname, and year, (5) month number, slash, and day, (6) month number,
- Xslash, day, slash, and year, or (7) a day of the week reference.
- XThe month name may be abbreviated to the first
- Xthree letters in the forms using it.
- XIn those forms where the year is not specified,
- Xthe current year is used. If a two digit year is specified, 1900 is added to
- Xit. The special dates today and tomorrow can be used in place of the
- X.I date
- Xfield. If no
- X.I date
- Xis specified, the current date is used.
- X.LP
- XThe optional
- X.I +increment
- Xor
- X.I -increment
- Xconsists of a simple number suffixed by one of the following: minute, hour,
- Xday, week, month, or year. The plural form of any of these keywords will also
- Xwork. The following abbreviations are also valid: min, m, hr, h, d, wk, w,
- Xmon, yr, and y.
- X.LP
- XIf the
- X.I date
- Xspecification is omitted, the time will be interpreted as the next occurrence
- Xof that time. For instance, if it is 11 AM and the command
- X.LP
- X.RS
- Xat 10 am
- X.RE
- X.LP
- Xis given, the at job will be scheduled for 10 AM on the following day.
- X.LP
- XThe following are example of allowable time/date specifications for the
- Xcurrent time assuming it is 1:00 AM on August 24, 1990:
- X.LP
- X.RS
- X1
- X.br
- X100
- X.br
- X1:00
- X.br
- X0100
- X.br
- X1:00 am
- X.br
- X1:00 aug 24
- X.br
- X1:00 24 aug
- X.br
- X0100 8/24
- X.br
- X01 8/24/90
- X.RE
- X.LP
- XThe use of the day of week reference consists of a time and the name of a
- Xday of the week. The day name may be preceded by an ordinal reference (e.g.,
- Xthird). For example,
- X.LP
- X.RS
- X1 pm friday
- X.br
- X1300 second friday
- X.RE
- X.LP
- XThe special ordinal value
- X.B this
- Xis provided, however, it has no influence on the computation since
- X"this Friday" is synonymous with simply "Friday".
- X.B next
- Xis synonymous
- Xwith the ordinal
- X.B first
- Xand both imply the next occurence of the specified day.
- XThus, both "first Friday"
- Xand "next Friday" will be
- Xthe first Friday after today. If today were Friday, either of these would
- Xrefer to the day one week from now.
- X.LP
- XWhen the job is entered into the
- X.B at
- Xqueue, a unique job number and the scheduled time will be displayed on
- Xstandard output.
- X.LP
- XThere may be restrictions on
- X.B at
- Xusage depending upon the policies of the installation. These restrictions
- Xare accomplished through one of two files:
- X.B at\.allow
- Xor
- X.B at\.deny.
- XIf
- X.B at\.allow
- Xexists, only those users listed in it may use the
- X.B at
- Xprograms. If it does not exist, but
- X.B at\.deny
- Xdoes exist, all users except those listed may submit jobs. If
- X.B at\.deny
- Xis empty, all users are allowed to submit jobs. If neither file exists,
- Xonly the super-user may use
- X.B at.
- XEntries in either file consist of one user name per line.
- X.LP
- XThe execution time specified is the earliest that the job will be run.
- XAll jobs are run via the
- X.BR atrun (8)
- Xcommand, which is typically invoked periodically by
- X.BR cron (8).
- XThe exact time of execution will depend upon the frequency of execution
- Xof
- X.BR atrun (8).
- X.SH OPTIONS
- X.TP
- X.B \-c
- XUse the C shell to execute the specified commands.
- X.TP
- X.B \-k
- XUse the Korn shell to execute the specified commands.
- X.TP
- X.B \-m
- XSend mail to the user who submitted the job on completion, even if no
- Xoutput is generated and no error occurs.
- X.TP
- X.B \-s
- XUse the Bourne shell to execute the specified commands.
- X.SH EXAMPLES
- X(1) To delete a .forward file on June 29 at 10:38 in the morning (e.g., to
- Xterminate vacation processing), the sequence could be
- X.sp
- X.RS
- X.B "at 10:38 am June 29"
- X.br
- Xat>
- X.B "rm .forward"
- X.br
- Xat>
- X.B "CTRL-D"
- X.RE
- X.sp
- XThere is no limit on the number of commands that can be entered.
- XNote that CTRL-D is the control-D character (hold down control and press D).
- X.LP
- X(2) To execute the script
- X.B cleanup
- Xat 3:00 AM on this coming Friday, the
- X.B at
- Xcommand could be
- X.sp
- X.RS
- X.B "at 3:00 AM Friday cleanup"
- X.RE
- X.LP
- X(3) To perform the same script at the same time, but with notification of
- Xits completion, the following command could be used.
- X.sp
- X.RS
- X.B "at -m 0300 fri cleanup"
- X.RE
- X.LP
- X(4) If the
- X.B cleanup
- Xscript were written for the C shell and the
- X.B at
- Xjob is being submitted while running a different shell (e.g., the Bourne
- Xshell), it would be necessary to force
- X.B at
- Xto run the commands in the script using the C shell.
- X.sp
- X.RS
- X.B "at -c -m 3 Fri cleanup"
- X.RE
- X.sp
- XNote that the time specifications shown in the preceding three examples
- Xwill all result in the same execution time.
- X.SH AUTHOR
- XKeith Pyle
- X.SH FILES
- X.ta 2.5i
- X\fB/usr/spool/at\fR spool directory
- X.br
- X\fB/usr/spool/at/at.allow\fR allowed users
- X.br
- X\fB/usr/spool/at/at.deny\fR prohibited users
- X.br
- X\fB/usr/spool/at/.seq\fR job sequence number
- X.SH "SEE ALSO"
- X.BR atcat (1L),
- X.BR atq (1L),
- X.BR atrm (1L),
- X.BR atrun (8)
- X.SH DIAGNOSTICS
- XThe exit status is 0 when a job is correctly queued. For file access failures
- Xand other execution problems, the exit status is set to 1.
- XAn exit status of 2 is set for syntax errors.
- X.SH BUGS
- X.LP
- XIf a job does not complete due to a system failure, it will not be requeued.
- XHowever, it would be possible to affect this function by modification of the
- Xappropriate boot file (e.g., rc.local on a BSD system). Assuming that the
- X.B at
- Xqueue directory is /usr/spool/at and the working directory is
- X/usr/spool/at/past, the following shell
- Xcommand could be used:
- X.sp
- X.RS
- X.ft B
- Xfind /usr/spool/at/past -name '[1-9]*'
- X.if n \{ \\
- X.br
- X.ti +0.5i
- X\}
- X-exec mv {} /usr/spool/at \\;
- X.ft R
- X.RE
- X.LP
- XThe next execution of
- X.BR atrun (8)
- Xwill restart these jobs.
- END_OF_FILE
- if test 7841 -ne `wc -c <'doc/at.1'`; then
- echo shar: \"'doc/at.1'\" unpacked with wrong size!
- fi
- # end of 'doc/at.1'
- fi
- if test -f 'src/at.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/at.c'\"
- else
- echo shar: Extracting \"'src/at.c'\" \(8859 characters\)
- sed "s/^X//" >'src/at.c' <<'END_OF_FILE'
- X/* at --- a member of the kat family (Keith's at package) */
- X
- X/* at(1) queues jobs for later execution by atrun. It is similar in */
- X/* function to the at(1) supplied with most BSD systems, but with additional */
- X/* capabilities in date parsing, shell selection, and confirmation of the */
- X/* run. */
- X
- X/* Copyright (c) 1990, W. Keith Pyle, Austin, Texas */
- X
- X#include "at.h"
- X
- Xchar at_file[MAXPATHLEN];
- Xchar at_tmp[MAXPATHLEN];
- Xchar buffer[MAXLINE];
- Xchar *verstring = PATCHLEVEL;
- X
- Xint jobno;
- X
- Xchar *atname();
- Xchar *ctime();
- Xchar *rindex();
- Xtime_t parse_date();
- Xvoid quit();
- X
- Xextern char *optarg;
- X
- Xextern int opterr;
- Xextern int optind;
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xmain(argc, argv, envp)
- X
- Xint argc;
- Xchar *argv[];
- Xchar *envp[];
- X
- X{
- X char *lastarg;
- X char owner[32];
- X char script[128];
- X char shell[33];
- X
- X int flag;
- X int mail;
- X int mask;
- X
- X time_t at_time;
- X time_t now_time;
- X
- X FILE *fp_at_file;
- X FILE *fp_script;
- X
- X if (argc == 1)
- X usage();
- X
- X /* Some initialization */
- X
- X mail = FALSE;
- X shell[0] = '\0';
- X
- X /* Get the user name */
- X
- X whoami(owner);
- X
- X /* Check for allowed at usage (just use two handy arrays for the names) */
- X
- X (void)sprintf(at_tmp, "%s/%s", ATDIR, ALLOW_FILE);
- X (void)sprintf(at_file, "%s/%s", ATDIR, DENY_FILE);
- X
- X if (!permit(owner, at_tmp, at_file)) {
- X
- X (void)fprintf(stderr, "you are not authorized to use at. Sorry.\n");
- X exit(1);
- X }
- X
- X /* Parse any option flags */
- X
- X while ((flag = getopt(argc, argv, "ckms")) != EOF) {
- X
- X switch (flag) {
- X
- X case 'c':
- X
- X (void)strcpy(shell, C_SHELL);
- X break;
- X
- X case 'k':
- X
- X (void)strcpy(shell, KORN_SHELL);
- X break;
- X
- X case 'm':
- X
- X mail = TRUE;
- X break;
- X
- X case 's':
- X
- X (void)strcpy(shell, BOURNE_SHELL);
- X break;
- X
- X default:
- X
- X usage();
- X }
- X }
- X
- X /* Try to parse all of the remaining arguments as a time specification. */
- X /* If this fails and there at least three arguments, see if the last */
- X /* argument is a file name. */
- X
- X lastarg = argv[argc - 1];
- X (void)time(&now_time);
- X now_time -= now_time % 60;
- X
- X if ((at_time = parse_args(argc - optind, argv + optind)) != -1) {
- X
- X fp_script = stdin;
- X (void)strcpy(script, "stdin");
- X }
- X
- X else if ((argc - optind) > 1 &&
- X (at_time = parse_args(argc - optind - 1, argv + optind)) != -1) {
- X
- X char *ptr;
- X
- X if (access(lastarg, F_OK) != 0) {
- X
- X (void)fprintf(stderr, "at: %s does not exist\n", lastarg);
- X exit(1);
- X }
- X
- X if ((ptr = rindex(lastarg, '/')) == NULL)
- X ptr = lastarg;
- X else
- X ptr++;
- X
- X (void)strncpy(script, ptr, 127);
- X
- X if ((fp_script = fopen(lastarg, "r")) == NULL) {
- X
- X perror(lastarg);
- X exit(1);
- X }
- X }
- X
- X else {
- X
- X (void)fprintf(stderr, "bad date/time specification\n");
- X exit(2);
- X }
- X
- X /* Check the 'at time' against the current time */
- X
- X if (at_time < now_time) {
- X
- X (void)fprintf(stderr, "too late\n");
- X exit(1);
- X }
- X
- X /* Build file names for the temporary file and the 'at file' */
- X
- X (void)sprintf(at_tmp, "%s/_at%d", ATDIR, getpid());
- X jobno = atseq();
- X (void)sprintf(at_file, "%s/%s", ATDIR, atname(at_time, jobno));
- X
- X /* Set up signal handlers */
- X
- X signal_init();
- X
- X /* Open the temporary file */
- X
- X if ((fp_at_file = fopen(at_tmp, "w")) == NULL) {
- X
- X perror("can't create a job for you");
- X exit(1);
- X }
- X
- X /* If the shell hasn't been specified, determine what should be used */
- X
- X if (shell[0] == '\0') {
- X
- X /* If the commands are from stdin, use the default shell; */
- X /* otherwise, look at the script to see if a shell is specified */
- X
- X if (fp_script == stdin || which_shell(fp_script, shell))
- X (void)strcpy(shell, "$SHELL");
- X }
- X
- X /* Put the identification comments in the job file, copy the current */
- X /* environment, and the shell command */
- X
- X comments(owner, script, mail, fp_at_file);
- X dumpenv(envp, fp_at_file);
- X shell_start(shell, fp_at_file);
- X
- X /* Copy commands from the appropriate source to the job file */
- X
- X if (copy_commands(fp_script, fp_at_file) == 0) {
- X
- X (void)fprintf(stderr, "nothing specified\n");
- X quit();
- X /* NOTREACHED */
- X }
- X
- X /* Change the ownership of the job file to the real user */
- X
- X if (chown(at_tmp, getuid(), getgid()) < 0) {
- X
- X perror("can't change the owner/group of your job to you");
- X exit(1);
- X }
- X
- X /* Set the mode of the file according to the owner's preference */
- X /* (but allow owner read/write access in any case) */
- X
- X mask = umask(0);
- X (void)umask(mask);
- X
- X if (chmod(at_tmp, (mask ^ 0777) | 0600) < 0) {
- X
- X perror("can't change the mode of your job");
- X exit(1);
- X }
- X
- X /* Close the temporary job file and rename it to the 'at name' */
- X
- X (void)fclose(fp_at_file);
- X
- X if (rename(at_tmp, at_file) < 0) {
- X
- X perror(at_tmp);
- X (void)unlink(at_tmp);
- X exit(1);
- X }
- X
- X /* Say, "Nice user, good user..." */
- X
- X (void)printf("job %d at %s", jobno, ctime(&at_time));
- X
- X exit(0);
- X /* NOTREACHED */
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xvoid
- Xquit()
- X
- X{
- X (void)unlink(at_tmp);
- X (void)unlink(at_file);
- X exit(1);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xcomments(owner, script, mail, fp_at_file)
- X
- Xchar *owner;
- Xchar *script;
- Xint mail;
- XFILE *fp_at_file;
- X
- X{
- X (void)fprintf(fp_at_file, "# at job\n");
- X (void)fprintf(fp_at_file, "# owner: %.127s\n", owner);
- X (void)fprintf(fp_at_file, "# jobname: %.127s\n", script);
- X (void)fprintf(fp_at_file, "# shell: sh\n");
- X (void)fprintf(fp_at_file, "# notify by mail: %s\n", mail ? "yes" : "no");
- X (void)fprintf(fp_at_file, "\n");
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xcopy_commands(fp_script, fp_at_file)
- X
- XFILE *fp_script;
- XFILE *fp_at_file;
- X
- X{
- X int ncmd;
- X
- X /* If a script has been specified or if stdin has been redirected, */
- X /* read and copy the script */
- X
- X if (fp_script != stdin || isatty(0) == 0) {
- X
- X for (ncmd = 0 ; fgets(buffer, sizeof(buffer), fp_script) ; ncmd++)
- X fputs(buffer, fp_at_file);
- X }
- X
- X /* Otherwise, prompt for each line and copy it */
- X
- X else {
- X
- X for (ncmd = 0 ; (void)printf("at> "), fgets(buffer, sizeof(buffer),
- X stdin) ; ncmd++)
- X fputs(buffer, fp_at_file);
- X
- X (void)printf("<EOT>\n");
- X }
- X
- X return(ncmd);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- X#if defined(GENERIC_SYSV)
- X
- X/* A dummy routine to keep certain System V machines happy since parse_date */
- X/* wants to use it and we want to use the stock version of parse_date */
- X
- Xftime(dummy)
- X
- Xstruct timeb *dummy;
- X
- X{
- X}
- X
- X#endif
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xparse_args(nargs, argv)
- X
- Xint nargs;
- Xchar *argv[];
- X
- X{
- X int i;
- X
- X struct timeb *now;
- X
- X time_t at_time;
- X
- X buffer[0] = '\0';
- X
- X /* Build up a string of the specified arguments */
- X
- X for (i = 0 ; i < nargs ; i++) {
- X
- X (void)strcat(buffer, argv[i]);
- X (void)strcat(buffer, " ");
- X }
- X
- X /* Now try to parse it as a time specification */
- X
- X now = NULL;
- X at_time = parse_date(buffer, now);
- X
- X /* Drop any seconds in the specification */
- X
- X if (at_time > 0)
- X at_time -= at_time % 60;
- X
- X return(at_time);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xshell_start(shell, fp_at_file)
- X
- Xchar *shell;
- XFILE *fp_at_file;
- X
- X{
- X char path[MAXPATHLEN];
- X int mask;
- X
- X (void)fprintf(fp_at_file,
- X "%s << '...use the rest of this file for input'\n", shell);
- X
- X#ifdef HAS_GETCWD
- X if (getcwd(path, MAXPATHLEN) == (char *)NULL) {
- X#else
- X if (getwd(path) == (char *)NULL) {
- X#endif
- X
- X (void)fprintf(stderr, "%s\n", path);
- X exit(1);
- X }
- X
- X (void)fprintf(fp_at_file, "cd %s\n", path);
- X mask = umask(0);
- X umask(mask);
- X (void)fprintf(fp_at_file, "umask %o\n", mask);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xsignal_init()
- X
- X{
- X (void)signal(SIGHUP, quit);
- X (void)signal(SIGQUIT, quit);
- X (void)signal(SIGINT, quit);
- X (void)signal(SIGTERM, quit);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xusage()
- X
- X{
- X (void)printf("usage: at [-c|k|s] [-m] time [date] [script]\n");
- X exit(2);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xwhich_shell(fp_script, shell)
- X
- XFILE *fp_script;
- Xchar *shell;
- X
- X{
- X char *ptr;
- X
- X int length;
- X
- X /* Determine if a shell is specified in the script */
- X
- X /* If we get EOF, just use the current shell */
- X
- X if (fgets(buffer, sizeof(buffer), fp_script) == NULL)
- X return(1);
- X
- X /* Reposition to the beginning of the file */
- X
- X if (fseek(fp_script, 0L, 0) < 0) {
- X
- X perror("seek on input script failed");
- X exit(1);
- X }
- X
- X /* If we don't get a cookie, forget it */
- X
- X if (buffer[0] != '#' || buffer[1] != '!')
- X return(1);
- X
- X /* Skip leading spaces and drop the newline */
- X
- X for (ptr = buffer + 2 ; *ptr == ' ' ; ptr++)
- X ;
- X
- X length = strlen(ptr);
- X *(ptr + --length) = '\0';
- X
- X /* Is this too long? */
- X
- X if (length > 32) {
- X
- X (void)fprintf(stderr,
- X "shell specifier (%s) in script is too long - using default\n",
- X ptr);
- X return(1);
- X }
- X
- X (void)strcpy(shell, ptr);
- X return(0);
- X}
- END_OF_FILE
- if test 8859 -ne `wc -c <'src/at.c'`; then
- echo shar: \"'src/at.c'\" unpacked with wrong size!
- fi
- # end of 'src/at.c'
- fi
- if test -f 'src/atq.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/atq.c'\"
- else
- echo shar: Extracting \"'src/atq.c'\" \(6069 characters\)
- sed "s/^X//" >'src/atq.c' <<'END_OF_FILE'
- X/* atq --- a member of the kat family (Keith's at package) */
- X
- X/* atq(1) lists all or part of the queue of jobs submitted via at(1). */
- X
- X/* Copyright (c) 1990, W. Keith Pyle, Austin, Texas */
- X
- X#include "at.h"
- X
- Xchar *verstring = PATCHLEVEL;
- X
- Xchar *disp_date();
- Xchar *nth();
- Xint comp_ctime();
- Xint comp_start();
- X
- Xextern char *optarg;
- Xextern char *sys_errlist[];
- X
- Xextern int optind;
- Xextern int opterr;
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xmain(argc, argv)
- X
- Xint argc;
- Xchar *argv[];
- X
- X{
- X char jobname[32]; /* Jobname from queued jobfile */
- X char owner[16]; /* Owner of queued job */
- X
- X int count_only; /* TRUE for display count only */
- X int flag; /* The getopt found flag */
- X int i; /* An index */
- X int n_jobs; /* The number of jobs to be listed */
- X int qsize; /* Size of queue data structure */
- X
- X int (*sort_func)(); /* Pointer to function for sort use */
- X
- X struct dirent *entry; /* Pointer to directory entry */
- X struct queue *atq; /* The queue list data structure ptr */
- X register struct queue *qptr; /* A working queue list pointer */
- X
- X DIR *dp; /* Descriptor for queue directory */
- X
- X /* Check for allowed at usage */
- X
- X whoami(owner);
- X
- X /* Change to the at directory and check permissions */
- X
- X if (chdir(ATDIR) < 0) {
- X
- X perror("can't change directory to the at directory");
- X exit(1);
- X }
- X
- X if (!permit(owner, ALLOW_FILE, DENY_FILE)) {
- X
- X fprintf(stderr, "you are not authorized to use at. Sorry.\n");
- X exit(1);
- X }
- X
- X /* Some initialization */
- X
- X count_only = FALSE;
- X qsize = 0;
- X sort_func = comp_start;
- X
- X /* Parse any arguments */
- X
- X while ((flag = getopt(argc, argv, "cn")) != EOF) {
- X
- X switch (flag) {
- X
- X case 'c':
- X
- X sort_func = comp_ctime;
- X break;
- X
- X case 'n':
- X
- X count_only = TRUE;
- X break;
- X
- X default:
- X
- X fprintf(stderr, "usage: atq [-c|n] [username]\n");
- X exit(2);
- X }
- X }
- X
- X /* Try to open the at job directory */
- X
- X if ((dp = opendir(".")) == NULL) {
- X
- X perror("can't read the at directory");
- X exit(1);
- X }
- X
- X /* Process all of the directory entries which have names of the proper */
- X /* format for a queued at job */
- X
- X while ((entry = readdir(dp)) != NULL) {
- X
- X int jobno;
- X
- X time_t tod;
- X
- X struct stat statbuf;
- X
- X /* Is the name of the proper format? */
- X
- X if (sscanf(entry->d_name, "%d.%d", &tod, &jobno) != 2)
- X continue;
- X
- X if (stat(entry->d_name, &statbuf) < 0) {
- X
- X perror(entry->d_name);
- X continue;
- X }
- X
- X /* If there are names in the argument list, do any of them */
- X /* match the owner of this jobfile? */
- X
- X if (optind < argc) {
- X
- X int found;
- X
- X struct passwd *pw;
- X
- X if ((pw = getpwuid(statbuf.st_uid)) == NULL) {
- X
- X perror(entry->d_name);
- X continue;
- X }
- X
- X found = FALSE;
- X
- X for (i = optind ; i < argc ; i++) {
- X
- X if (strcmp(argv[i], pw->pw_name) == 0) {
- X
- X found = TRUE;
- X break;
- X }
- X }
- X
- X if (!found)
- X continue;
- X }
- X
- X /* This entry should be listed. Make sure there is space */
- X /* in the queue data structure to store it */
- X
- X if ((n_jobs + 1) > qsize) {
- X
- X if ((qsize = qalloc(&atq, qsize)) < 0) {
- X
- X fprintf(stderr, "can't allocate space for queue data\n");
- X exit(1);
- X }
- X }
- X
- X /* Save the info */
- X
- X atq[n_jobs].qu_start = tod;
- X strcpy(atq[n_jobs].qu_name, entry->d_name);
- X atq[n_jobs].qu_jobno = jobno;
- X atq[n_jobs].qu_ctime = statbuf.st_ctime;
- X n_jobs++;
- X }
- X
- X closedir(dp);
- X
- X /* Do we do a count only? */
- X
- X if (count_only) {
- X
- X printf("%d\n", n_jobs);
- X exit(0);
- X }
- X
- X /* Were there any jobs? */
- X
- X if (n_jobs == 0) {
- X
- X printf("no files in queue\n");
- X exit(0);
- X }
- X
- X /* If more than one job, sort them */
- X
- X else if (n_jobs > 1)
- X qsort(atq, n_jobs, sizeof(struct queue), sort_func);
- X
- X /* Print the info on the selected queued entries */
- X
- X printf(" Rank Execution Date Owner Job # Job Name\n");
- X
- X for (i = 1, qptr = atq ; i <= n_jobs ; i++, qptr++) {
- X
- X if (get_ident(qptr->qu_name, owner, jobname) < 0)
- X continue;
- X
- X printf("%3d%s %s %-10s%5d %s\n", i, nth(i),
- X disp_date(qptr->qu_start), owner, qptr->qu_jobno, jobname);
- X }
- X
- X exit(0);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xcomp_ctime(str1, str2)
- X
- Xstruct queue *str1;
- Xstruct queue *str2;
- X
- X{
- X /* Compare the creation times of the two entries */
- X
- X return(str1->qu_ctime - str2->qu_ctime);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xcomp_start(str1, str2)
- X
- Xstruct queue *str1;
- Xstruct queue *str2;
- X
- X{
- X /* Compare the start times of the two entries */
- X
- X return(str1->qu_start - str2->qu_start);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xchar *
- Xdisp_date(tod)
- X
- Xtime_t tod;
- X
- X{
- X static char datebuf[20];
- X static char *mon[] = {
- X "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
- X };
- X
- X struct tm *tm;
- X
- X tm = localtime(&tod);
- X sprintf(datebuf, "%s %2d, %4d %02d:%02d", mon[tm->tm_mon], tm->tm_mday,
- X tm->tm_year + 1900, tm->tm_hour, tm->tm_min);
- X
- X return(datebuf);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xget_ident(jobfile, owner, jobname)
- X
- Xchar *jobfile;
- Xchar *owner;
- Xchar *jobname;
- X
- X{
- X char line[MAXLINE];
- X
- X int found_job;
- X int found_owner;
- X
- X FILE *fp;
- X
- X /* Open the jobfile */
- X
- X if ((fp = fopen(jobfile, "r")) == NULL) {
- X
- X printf("atq: Can't open job file %s: %s\n",
- X jobfile, sys_errlist[errno]);
- X return(-1);
- X }
- X
- X /* Look for the owner and jobname lines of the jobfile */
- X
- X found_job = found_owner = FALSE;
- X *owner = *jobname = '\0';
- X
- X while (fgets(line, sizeof(line), fp)) {
- X
- X if (sscanf(line, "# owner: %10s", owner) == 1)
- X found_owner = TRUE;
- X
- X else if (sscanf(line, "# jobname: %27s%*[^\n]", jobname) == 1)
- X found_job = TRUE;
- X
- X if (found_owner && found_job)
- X break;
- X }
- X
- X fclose(fp);
- X return(0);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xchar *
- Xnth(n)
- X
- Xint n;
- X
- X{
- X switch (n) {
- X
- X case 1:
- X
- X return("st");
- X
- X case 2:
- X
- X return("nd");
- X
- X case 3:
- X
- X return("rd");
- X
- X default:
- X
- X return("th");
- X }
- X}
- END_OF_FILE
- if test 6069 -ne `wc -c <'src/atq.c'`; then
- echo shar: \"'src/atq.c'\" unpacked with wrong size!
- fi
- # end of 'src/atq.c'
- fi
- if test -f 'src/atrun.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/atrun.c'\"
- else
- echo shar: Extracting \"'src/atrun.c'\" \(17097 characters\)
- sed "s/^X//" >'src/atrun.c' <<'END_OF_FILE'
- X/* atrun --- a member of the kat family (Keith's at package) */
- X
- X/* atrun(8) controls execution of jobs in the at(1) queue. Provision is */
- X/* made to mail output generated during the execution to the submitting */
- X/* user and to control the number of concurrent at processes. atrun(8) */
- X/* is typically run by cron periodically (e.g., every 15 minutes). */
- X
- X/* Copyright (c) 1990, W. Keith Pyle, Austin, Texas */
- X
- X#include "at.h"
- X
- Xchar *verstring = PATCHLEVEL;
- X
- X#include <syslog.h>
- X#ifdef HAS_TERMIO
- X#include <termio.h>
- X#else
- X#include <sgtty.h>
- X#endif
- X
- X#define LOCKFILE "atrun.lock"
- X
- Xchar *progname;
- X
- X#ifdef DEBUG
- X
- X/* These options define logging behavior for a debugging version only */
- X
- Xint debug_level;
- Xextern char *optarg;
- X
- X#ifdef NOSYSLOG
- X#define syslog fprintf
- X#define LOGDEST logfile
- X#define LOGFILE "./atrun.debug"
- XFILE *logfile;
- X#endif
- X
- X#else
- X
- X/* These options apply to logging in the production version */
- X
- X#ifdef HAS_SYSLOGFACILITY
- X#define LOGDEST LOG_ERR | LOG_DAEMON
- X#else
- X#define LOGDEST LOG_ERR
- X#endif
- X
- X#endif /* DEBUG */
- X
- Xchar *rindex();
- X
- Xint comp_start();
- X
- Xextern char *sys_errlist[];
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xmain(argc, argv)
- X
- Xint argc;
- Xchar *argv[];
- X
- X{
- X char lockfile[MAXPATHLEN];
- X char logname[32];
- X char *ptr;
- X
- X int flag;
- X int n_jobs;
- X int tod;
- X
- X struct queue *atq;
- X
- X FILE *fp;
- X
- X /* Make sure only root runs this */
- X
- X if (geteuid() != 0) {
- X
- X fprintf(stderr, "permission denied\n");
- X exit(1);
- X }
- X
- X /* Make sure we are allowed to run: check for lock file */
- X
- X sprintf(lockfile, "%s/%s", ATDIR, LOCKFILE);
- X
- X if (access(lockfile, F_OK) == 0) {
- X
- X fprintf(stderr, "atrun.lock exists: cannot execute\n");
- X exit(1);
- X }
- X
- X /* Convert to a daemon */
- X
- X daemonize();
- X
- X /* Set up syslog options */
- X
- X#ifndef NOSYSLOG
- X if ((ptr = rindex(argv[0], '/')) != NULL)
- X ptr++;
- X else
- X ptr = argv[0];
- X
- X strcpy(logname, ptr);
- X
- X#ifdef HAS_SYSLOGFACILITY
- X openlog(logname, LOG_CONS, LOG_DAEMON);
- X#else
- X openlog(logname, 0);
- X#endif
- X#else
- X if ((logfile = fopen(LOGFILE, "a")) == NULL) {
- X
- X perror(LOGFILE);
- X exit(1);
- X }
- X
- X setbuf(logfile, NULL);
- X time(&tod);
- X fprintf(logfile, "atrun started at %s", ctime(&tod));
- X#endif
- X
- X /* Make our name stand out for ps(1) */
- X
- X progname = argv[0];
- X
- X for (ptr = argv[0] ; *ptr ; ptr++)
- X *ptr = islower(*ptr) ? toupper(*ptr) : *ptr;
- X
- X#ifdef DEBUG
- X /* In the DEBUG version, check for a debug level argument */
- X
- X while ((flag = getopt(argc, argv, "x")) != EOF) {
- X
- X switch (flag) {
- X
- X case 'x':
- X
- X debug_level = TRUE;
- X break;
- X
- X default:
- X
- X syslog(LOGDEST, "invalid argument: %c\n", flag);
- X exit(1);
- X }
- X }
- X#endif
- X
- X /* Change to the at job directory */
- X
- X if (chdir(ATDIR) < 0) {
- X
- X syslog(LOGDEST, "can't chdir to %s: %s\n", ATDIR, sys_errlist[errno]);
- X exit(1);
- X }
- X
- X /* Make sure we can update the last done file */
- X
- X if ((fp = fopen("lasttimedone", "w")) == NULL) {
- X
- X syslog(LOGDEST, "can't open 'lasttimedone': %s\n", sys_errlist[errno]);
- X exit(1);
- X }
- X
- X /* Select the job files to be run */
- X
- X n_jobs = select_jobs(&atq);
- X
- X /* Update the last done file */
- X
- X time(&tod);
- X fprintf(fp, "%d (%.19s)\n", tod, ctime(&tod));
- X fclose(fp);
- X
- X /* Now run the selected jobs */
- X
- X if (n_jobs != 0)
- X run_all(n_jobs, atq);
- X
- X exit(0);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- X/* Compare function for use with sort: compare job start times */
- X
- Xcomp_start(ptr1, ptr2)
- X
- Xstruct queue *ptr1;
- Xstruct queue *ptr2;
- X
- X{
- X return(ptr1->qu_start - ptr2->qu_start);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- X/* Determine if the user requested mail confirming the completion */
- X/* of the job */
- X
- Xconfirmation(mail_uid, jobfile, jobno)
- X
- Xint mail_uid;
- Xchar *jobfile;
- Xint jobno;
- X
- X{
- X char buffer[MAXLINE];
- X
- X int confirm;
- X
- X FILE *jobfile_fp;
- X
- X confirm = FALSE;
- X
- X /* Open the job file and look for the notify line */
- X
- X if ((jobfile_fp = fopen(jobfile, "r")) != NULL) {
- X
- X while (fgets(buffer, sizeof(buffer), jobfile_fp)) {
- X
- X if (strcmp(buffer, "# notify by mail: yes\n") == 0) {
- X
- X confirm = TRUE;
- X break;
- X }
- X }
- X
- X fclose(jobfile_fp);
- X }
- X
- X /* Did we find a comfirm line? */
- X
- X if (confirm) {
- X
- X /* Oh, yes, we did! */
- X
- X open_mail(mail_uid);
- X sprintf(buffer, "Your \"at\" job \"%d\" completed.\n", jobno);
- X write(1, buffer, strlen(buffer));
- X }
- X
- X return(confirm);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- X/* Send generated output to user via mail */
- X
- Xcopy_output(jobfile, jobno, outfile)
- X
- Xchar *jobfile;
- Xint jobno;
- Xchar *outfile;
- X
- X{
- X char buffer [8192];
- X
- X int bytes;
- X int outfile_fd;
- X
- X sprintf(buffer,
- X "Your \"at\" job \"%d\" generated the following output:\n\n",
- X jobno);
- X write(1, buffer, strlen(buffer));
- X
- X /* Open the generated output file and write it to sendmail */
- X
- X if ((outfile_fd = open(outfile, O_RDONLY)) >= 0) {
- X
- X while ((bytes = read(outfile_fd, buffer, sizeof(buffer))) > 0)
- X write(1, buffer, bytes);
- X
- X close(outfile_fd);
- X }
- X
- X else
- X syslog(LOGDEST,
- X "couldn't open output %s from jobfile %s for reading: %s\n",
- X outfile, jobfile, sys_errlist[errno]);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- X/* Convert the master process to a daemon */
- X
- Xdaemonize()
- X
- X{
- X int fd;
- X int pid;
- X
- X /* Ignore the terminal stop signals */
- X
- X (void) signal(SIGTTOU, SIG_IGN);
- X (void) signal(SIGTTIN, SIG_IGN);
- X (void) signal(SIGTSTP, SIG_IGN);
- X
- X /* Fork to a child process, parent terminates */
- X
- X if ((pid = fork()) < 0) {
- X
- X syslog(LOGDEST, "could not fork to create daemon\n");
- X exit(1);
- X }
- X
- X else if (pid > 0)
- X exit(0);
- X
- X /* Disassociate ourselves from the old process group */
- X
- X if (setpgrp(0, getpid()) == -1) {
- X
- X syslog(LOGDEST, "couldn't set process group in master atrun\n");
- X exit(1);
- X }
- X
- X#ifdef HAS_TIOCNOTTY
- X /* Lose the controlling terminal */
- X
- X if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
- X
- X (void) ioctl(fd, TIOCNOTTY, (char *) NULL);
- X (void) close(fd);
- X }
- X#endif
- X
- X /* Make stdin, stdout, and stderr worthless but keep them as placeholders */
- X /* since we may need to do forks and use them in the child */
- X
- X close(0);
- X close(1);
- X
- X if (open("/dev/null", O_RDONLY) != 0) {
- X
- X syslog(LOGDEST, "couldn't reopen stdin as /dev/null");
- X exit(1);
- X }
- X
- X if (open("/dev/null", O_WRONLY) != 1) {
- X
- X syslog(LOGDEST, "couldn't reopen stdout as /dev/null");
- X exit(1);
- X }
- X
- X close(2);
- X dup(1);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- X/* Create a sendmail process to mail output to the at job owner */
- X
- Xopen_mail(uid)
- X
- Xuid_t uid;
- X
- X{
- X int mail_pipe[2];
- X int pid;
- X
- X struct passwd *pw;
- X
- X /* Determine the user name for this job */
- X
- X if ((pw = getpwuid(uid)) == NULL) {
- X
- X syslog(LOGDEST, "can't find passwd entry for uid %d: %s\n",
- X uid, sys_errlist[errno]);
- X return;
- X }
- X
- X#ifdef DEBUG
- X if (debug_level)
- X syslog(LOGDEST, "opening mail for %s in slave atrun (pid=%d)\n",
- X pw->pw_name, getpid());
- X#endif
- X
- X if (pipe(mail_pipe) < 0) {
- X
- X syslog(LOGDEST, "can't create pipe for mail: %s\n", sys_errlist[errno]);
- X return;
- X }
- X
- X if ((pid = fork()) < 0) {
- X
- X syslog(LOGDEST, "can't fork for mail: %s\n", sys_errlist[errno]);
- X return;
- X }
- X
- X else if (pid > 0) {
- X
- X char buffer[256];
- X
- X /* Parent (slave atrun): make stdout go to sendmail and write header */
- X
- X close(1);
- X dup(mail_pipe[1]);
- X close(mail_pipe[0]);
- X close(mail_pipe[1]);
- X
- X#ifdef USE_SENDMAIL
- X sprintf(buffer, "To: %s\nSubject: Output from \"at\" job\n\n",
- X pw->pw_name);
- X write(1, buffer, strlen(buffer));
- X#endif
- X
- X return;
- X }
- X
- X else {
- X
- X /* Child: convert pipe to stdin and exec to sendmail */
- X
- X close(0);
- X dup(mail_pipe[0]);
- X close(mail_pipe[0]);
- X close(mail_pipe[1]);
- X
- X#ifdef USE_SENDMAIL
- X execl("/usr/lib/sendmail", "sendmail", "-t", (char *)NULL);
- X#endif
- X
- X#ifdef USE_UCBMAIL
- X execl("/usr/ucb/Mail", "Mail", "-s", "Output from \"at\" job",
- X pw->pw_name, (char *)NULL);
- X#endif
- X
- X#ifdef USE_BINMAIL
- X execl("/bin/mail", "mail", pw->pw_name, (char *)NULL);
- X#endif
- X
- X syslog(LOGDEST, "exec to mailer failed: %s\n", sys_errlist[errno]);
- X exit(1);
- X }
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- X/* If a slave atrun fails, this will try to move the jobfile back to the */
- X/* normal queue directory */
- X
- Xrecover(jobfile)
- X
- Xchar *jobfile;
- X
- X{
- X char newname[MAXPATHLEN];
- X
- X#ifdef DEBUG
- X if (debug_level)
- X syslog(LOGDEST,
- X "attempting recovery of jobfile %s in slave atrun (pid=%d)\n",
- X jobfile, getpid());
- X#endif
- X
- X sprintf(newname, "%s/%s", ATDIR, jobfile);
- X
- X if (rename(jobfile, newname) < 0)
- X syslog(LOGDEST,
- X "couldn't recover jobfile %s from the past directory: %s\n",
- X jobfile, sys_errlist[errno]);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- X/* Create slave atrun processes for each job in queue. Manage total */
- X/* number running so as not to exceed configured limit. */
- X
- Xrun_all(n_jobs, atq)
- X
- Xint n_jobs;
- Xstruct queue *atq;
- X
- X{
- X int i;
- X int n_slave;
- X int status;
- X
- X register struct queue *qptr;
- X
- X /* cd to the working directory */
- X
- X if (chdir(PASTDIR) < 0) {
- X
- X syslog(LOGDEST, "can't chdir to past: %s\n", sys_errlist[errno]);
- X exit(1);
- X }
- X
- X /* Sort the entries in order of starting time */
- X
- X if (n_jobs > 1)
- X qsort(atq, n_jobs, sizeof(struct queue), comp_start);
- X
- X /* Run them */
- X
- X n_slave = 0;
- X
- X for (i = 0 , qptr = atq ; i < n_jobs ; i++, qptr++) {
- X
- X#if MAXATPROC
- X /* Check number of slaves running */
- X
- X if (n_slave >= MAXATPROC) {
- X
- X /* At configured limit; wait for one to finish */
- X
- X#ifdef DEBUG
- X if (debug_level)
- X syslog(LOGDEST,
- X "MAXATPROC hit; waiting for slave to finish...\n");
- X#endif
- X
- X if (wait(&status) == -1)
- X syslog(LOGDEST,
- X "shouldn't happen: wait reports no slaves running\n");
- X
- X n_slave--;
- X }
- X#endif /* MAXATPROC */
- X
- X /* Run the next queue entry */
- X
- X run_one(qptr);
- X n_slave++;
- X }
- X
- X /* Wait for any remaining slaves to finish */
- X
- X while (wait(&status) != -1)
- X ;
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- X/* Manage creation and execution of one queue entry */
- X
- Xrun_one(qptr)
- X
- Xstruct queue *qptr;
- X
- X{
- X char *ptr;
- X char *jobfile;
- X char outfile[32];
- X
- X int jobno;
- X int pid;
- X int mail_pipe[2];
- X int outfile_fd;
- X
- X struct stat statbuf;
- X
- X jobfile = qptr->qu_name;
- X jobno = qptr->qu_jobno;
- X
- X /* Create a slave atrun; return if master process */
- X
- X if ((pid = fork()) < 0) {
- X
- X syslog(LOGDEST, "can't create fork slave atrun for jobfile %s: %s\n",
- X jobfile, sys_errlist[errno]);
- X recover(jobfile);
- X return;
- X }
- X
- X else if (pid > 0)
- X return;
- X
- X#ifdef DEBUG
- X if (debug_level)
- X syslog(LOGDEST, "slave atrun (pid=%d) created for jobfile %s\n",
- X getpid(), jobfile);
- X#endif
- X
- X /* We're now a slave atrun responsible for only one jobfile. */
- X /* Fix our name so ps(1) shows it a little differently than the master. */
- X
- X if ((ptr = rindex(progname, '/')) != NULL)
- X ptr += 2;
- X else
- X ptr = progname + 1;
- X
- X for ( ; *ptr ; ptr++)
- X *ptr = isupper(*ptr) ? tolower(*ptr) : *ptr;
- X
- X /* Create a file to receive the stdout and stderr from the at job. */
- X
- X strcpy(outfile, "atoutXXXXXX");
- X
- X if (mktemp(outfile) == NULL) {
- X
- X syslog(LOGDEST,
- X "can't create tmp file for output from jobfile %s: %s\n",
- X jobfile, sys_errlist[errno]);
- X recover(jobfile);
- X exit(1);
- X }
- X
- X if ((outfile_fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC,
- X 0600)) < 0) {
- X
- X syslog(LOGDEST,
- X "can't open tmp file for output from jobfile %s: %s\n",
- X jobfile, sys_errlist[errno]);
- X recover(jobfile);
- X exit(1);
- X }
- X
- X#ifdef DEBUG
- X syslog(LOGDEST,
- X "tmp file %s created for jobfile %s in slave atrun (pid=%d)\n",
- X outfile, jobfile, getpid());
- X#endif
- X
- X /* Find out who the owner of the job should be */
- X
- X if (stat(jobfile, &statbuf) < 0) {
- X
- X syslog(LOGDEST, "couldn't stat jobfile %s: %s\n", jobfile,
- X sys_errlist[errno]);
- X recover(jobfile);
- X exit(1);
- X }
- X
- X /* Fork to create child for running at job */
- X
- X if ((pid = fork()) < 0) {
- X
- X syslog(LOGDEST, "can't fork from slave atrun for jobfile %s: %s\n",
- X jobfile, sys_errlist[errno]);
- X recover(jobfile);
- X exit(1);
- X }
- X
- X else if (pid == 0) {
- X
- X /* Child: close stdout and stderr, dup the write pipe onto both */
- X
- X close(1);
- X dup(outfile_fd);
- X close(2);
- X dup(outfile_fd);
- X close(outfile_fd);
- X
- X /* Set the group and owner for the at job */
- X
- X if(setgid(statbuf.st_gid) < 0) {
- X
- X syslog(LOGDEST, "couldn't set gid for jobfile %s: %s\n",
- X jobfile, sys_errlist[errno]);
- X recover(jobfile);
- X exit(1);
- X }
- X
- X if(setuid(statbuf.st_uid) < 0) {
- X
- X syslog(LOGDEST, "couldn't set uid for jobfile %s: %s\n",
- X jobfile, sys_errlist[errno]);
- X recover(jobfile);
- X exit(1);
- X }
- X
- X /* Now run the at job via sh passing a NULL environment */
- X
- X execle("/bin/sh", "sh", jobfile, (char *)NULL, (char *)NULL);
- X
- X /* If we're here, the exec failed! */
- X
- X syslog(LOGDEST,
- X "could exec /bin/sh from slave atrun for jobfile %s: %s\n",
- X jobfile, sys_errlist[errno]);
- X recover(jobfile);
- X exit(1);
- X }
- X
- X else {
- X
- X int mail_open;
- X int mail_uid;
- X int status;
- X
- X#ifdef DEBUG
- X if (debug_level)
- X syslog(LOGDEST,
- X "slave atrun (pid=%d) created child (pid=%d) to 'sh %s'\n",
- X getpid(), pid, jobfile);
- X#endif
- X
- X /* Parent (slave atrun): close tmp output file */
- X
- X close(outfile_fd);
- X mail_open = FALSE;
- X mail_uid = statbuf.st_uid;
- X
- X /* Wait for an exit status from the at job */
- X
- X wait(&status);
- X#ifdef DEBUG
- X if (debug_level)
- X syslog(LOGDEST, "jobfile %s (pid=%d) terminated with status=%d\n",
- X jobfile, pid, status);
- X#endif
- X
- X /* Determine if the job generated any output: stat the file ... */
- X
- X if (stat(outfile, &statbuf) < 0) {
- X
- X syslog(LOGDEST, "couldn't stat output file for jobfile %s: %s\n",
- X jobfile, sys_errlist[errno]);
- X }
- X
- X /* ... and check its size */
- X
- X else if (statbuf.st_size > 0) {
- X
- X mail_open = TRUE;
- X open_mail(mail_uid);
- X copy_output(jobfile, jobno, outfile);
- X }
- X
- X#ifdef DEBUG
- X if (debug_level)
- X syslog(LOGDEST, "output file for jobfile %s was %d bytes\n",
- X jobfile, statbuf.st_size);
- X#endif
- X
- X /* Dispose of the temporary file */
- X
- X if (unlink(outfile) < 0)
- X syslog(LOGDEST, "couldn't unlink output %s from jobfile %s: %s\n",
- X outfile, jobfile, sys_errlist[errno]);
- X
- X /* If an error status is received, put it in the mail message */
- X
- X if (status != 0) {
- X
- X char buffer[128];
- X
- X if (!mail_open)
- X open_mail(mail_uid);
- X else
- X write(1, "\n", 1);
- X
- X sprintf(buffer, "Your \"at\" job \"%d\" exited with status=%d\n",
- X jobno, status);
- X write(1, buffer, strlen(buffer));
- X }
- X
- X /* If there hasn't been any mail generated, see if the user */
- X /* requested confirmation of the run by mail */
- X
- X else if (!mail_open)
- X mail_open = confirmation(mail_uid, jobfile, jobno);
- X
- X /* Close the mail pipe, if any; check status */
- X
- X if (mail_open) {
- X
- X close(1);
- X
- X if (wait(&status) != -1 && status != 0)
- X syslog(LOGDEST, "mailer exited with status=%d\n", status);
- X }
- X
- X /* Dispose of the at jobfile */
- X
- X if (unlink(jobfile) < 0)
- X syslog(LOGDEST, "can't unlink completed jobfile %s: %s\n",
- X jobfile, sys_errlist[errno]);
- X
- X /* Exit the slave atrun */
- X
- X exit(0);
- X }
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- X/* Select at jobfiles from the queue directory that should be run. Move */
- X/* them to the working directory and store their file names. */
- X
- Xselect_jobs(atq)
- X
- Xstruct queue **atq;
- X
- X{
- X char buffer[MAXPATHLEN];
- X
- X int n_jobs;
- X int qsize;
- X
- X struct dirent *entry;
- X register struct queue *qptr;
- X
- X time_t tod;
- X
- X DIR *dp;
- X
- X n_jobs = 0;
- X qsize = 0;
- X time(&tod);
- X
- X /* Open the queue directory */
- X
- X if ((dp = opendir(".")) == NULL) {
- X
- X syslog(LOGDEST, "can't open at directory\n");
- X exit(1);
- X }
- X
- X /* Get the name of each directory entry */
- X
- X while ((entry = readdir(dp)) != NULL) {
- X
- X int runtime;
- X int jobno;
- X
- X struct stat statbuf;
- X
- X /* See if the file name matches the jobfile name format */
- X
- X if (sscanf(entry->d_name, "%d.%d", &runtime, &jobno) != 2) {
- X
- X#ifdef DEBUG
- X if (debug_level)
- X syslog(LOGDEST, "file %s rejected - not a jobfile name\n",
- X entry->d_name);
- X#endif
- X continue;
- X }
- X
- X /* Should this one be run yet? */
- X
- X if (runtime > tod) {
- X
- X#ifdef DEBUG
- X if (debug_level)
- X syslog(LOGDEST, "jobfile %s rejected - not time yet\n",
- X entry->d_name);
- X#endif
- X continue;
- X }
- X
- X /* Is the queue data structure large enough to store this entry? */
- X
- X if ((n_jobs + 1) > qsize) {
- X
- X if ((qsize = qalloc(atq, qsize)) < 0) {
- X
- X fprintf(stderr, "can't allocate space for queue data\n");
- X exit(1);
- X }
- X
- X qptr = *atq;
- X }
- X
- X /* Save the info on this entry */
- X
- X qptr[n_jobs].qu_start = runtime;
- X qptr[n_jobs].qu_jobno = jobno;
- X strcpy(qptr[n_jobs].qu_name, entry->d_name);
- X
- X /* Move the entry to the working directory */
- X
- X sprintf(buffer, "%s/%s", PASTDIR, entry->d_name);
- X
- X if (rename(entry->d_name, buffer) < 0) {
- X
- X syslog(LOGDEST, "can't rename job file %s: %s\n",
- X entry->d_name, sys_errlist[errno]);
- X continue;
- X }
- X
- X#ifdef DEBUG
- X if (debug_level)
- X syslog(LOGDEST, "jobfile %s selected\n", entry->d_name);
- X#endif
- X
- X n_jobs++;
- X }
- X
- X closedir(dp);
- X
- X#ifdef DEBUG
- X if (debug_level)
- X syslog(LOGDEST, "%d jobs selected\n", n_jobs);
- X#endif
- X
- X return(n_jobs);
- X}
- END_OF_FILE
- if test 17097 -ne `wc -c <'src/atrun.c'`; then
- echo shar: \"'src/atrun.c'\" unpacked with wrong size!
- fi
- # end of 'src/atrun.c'
- fi
- if test -f 'src/date.y' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/date.y'\"
- else
- echo shar: Extracting \"'src/date.y'\" \(13039 characters\)
- sed "s/^X//" >'src/date.y' <<'END_OF_FILE'
- X%token MONTH DAY RELDAY MERIDIAN NUMBER ORDINAL UNIT MONUNIT
- X%token SECOND STDZONE DSTZONE ABSHOUR STRING
- X
- X%{
- X
- X#include <stdio.h>
- X#include <sys/types.h>
- X#include <ctype.h>
- X#include <sys/timeb.h>
- X#include <sys/time.h>
- X
- X/* Define some useful time constants */
- X
- X#define SEC_PER_MIN (60)
- X#define SEC_PER_HOUR (3600)
- X#define MIN_PER_HOUR (60)
- X#define HOUR_PER_DAY (24)
- X#define SEC_PER_DAY (SEC_PER_HOUR * HOUR_PER_DAY)
- X#define DAY_PER_WEEK (7)
- X
- X#define EPOCH 1970
- X
- X/* Define some symbolic values */
- X
- X#define FALSE 0
- X#define TRUE 1
- X
- X#define AM 0
- X#define PM 1
- X#define MILITARY 2
- X
- X#define NOT_GIVEN -1
- X#define STANDARD 0
- X#define DST 1
- X
- X/* Declare external values used by the following routines */
- X
- Xstatic char *lptr; /* Pointer to input string for parsing */
- Xstatic char *lstring; /* Input string pointer (allocated space) */
- X
- Xchar yyerrmsg[1024]; /* Description of any detected error */
- X
- Xstatic int day; /* Day-of-month value */
- Xstatic int hour; /* Hour value */
- Xstatic int inc_mon; /* Number of incremental months */
- Xstatic int meridian; /* AM/PM/Military flag */
- Xstatic int minute; /* Minute value */
- Xstatic int month; /* Month value */
- Xstatic int ordinal; /* Ordinal value (first=1, etc.) */
- Xstatic int second; /* Second value */
- Xstatic int timezone; /* Time zone value */
- Xstatic int weekday; /* Day-of-week value */
- Xstatic int year; /* Year value */
- Xstatic int zone_type; /* Indicates if DST is possible */
- X
- Xstatic int month_len[12] =
- X {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* Length of months */
- X
- Xstatic time_t inc_sec; /* Number of incremental seconds */
- Xstatic time_t rel_sec; /* Number of relative day seconds */
- X
- X/* The following externals contain field counts used in syntax checking */
- X
- Xstatic int date_fields; /* Number of date fields */
- Xstatic int day_fields; /* Number of day of week fields */
- Xstatic int inc_fields; /* Number of increment/decrement fields */
- Xstatic int rday_fields; /* Number of relative day fields */
- Xstatic int time_fields; /* Number of time-of-day fields */
- Xstatic int zone_fields; /* Number of time zone fields */
- X
- X/* Declare function types */
- X
- Xchar *malloc();
- Xtime_t parse_date();
- Xvoid num_to_time();
- X
- X/* Declare external variables */
- X
- Xextern char *sys_errlist[];
- X
- Xextern int errno;
- X
- X%}
- X
- X%%
- X
- Xfullspec: timestr increment
- X ;
- X
- Xtimestr: /* empty */
- X | timestr field
- X ;
- X
- Xfield: date
- X { date_fields++; }
- X | day
- X { day_fields++; }
- X | number
- X | relday
- X { rday_fields++; }
- X | time
- X { time_fields++; }
- X | zone
- X { zone_fields++; }
- X ;
- X
- Xdate: NUMBER '/' NUMBER
- X { month = $1 - 1; day = $3; }
- X | NUMBER '/' NUMBER '/' NUMBER
- X { month = $1 - 1; day = $3; year = $5; }
- X | MONTH NUMBER
- X { month = $1; day = $2; }
- X | MONTH NUMBER ',' NUMBER
- X { month = $1; day = $2; year = $4; }
- X | NUMBER MONTH NUMBER
- X { month = $2; day = $1; year = $3; }
- X | NUMBER MONTH
- X { month = $2; day = $1; }
- X ;
- X
- Xday: DAY
- X { weekday = $1; ordinal = 1; }
- X | DAY ','
- X { weekday = $1; ordinal = 1; }
- X | ORDINAL DAY
- X { weekday = $2; ordinal = $1; }
- X | SECOND DAY
- X { weekday = $2; ordinal = 2; }
- X ;
- X
- Xrelday: RELDAY
- X { rel_sec += $1; }
- X | ORDINAL RELDAY
- X { rel_sec += ($1 * $2); }
- X | SECOND RELDAY
- X { rel_sec += (2 * $2); }
- X ;
- X
- Xtime: NUMBER MERIDIAN
- X { hour = $1; minute = 0; second = 0; meridian = $2; }
- X | NUMBER ':' NUMBER
- X { hour = $1; minute = $3; second = 0; meridian = MILITARY; }
- X | NUMBER ':' NUMBER MERIDIAN
- X { hour = $1; minute = $3; second = 0; meridian = $4; }
- X | NUMBER ':' NUMBER ':' NUMBER
- X { hour = $1; minute = $3; second = $5; meridian = MILITARY; }
- X | NUMBER ':' NUMBER ':' NUMBER MERIDIAN
- X { hour = $1; minute = $3; second = $5; meridian = $6; }
- X | ABSHOUR
- X { hour = $1; minute = 0; second = 0; meridian = MILITARY; }
- X ;
- X
- Xzone: STDZONE
- X { timezone = $1; zone_type = STANDARD; }
- X | DSTZONE
- X { timezone = $1; zone_type = DST; }
- X ;
- X
- Xincrement: /* empty */
- X | increment incfield
- X { inc_fields++; }
- X ;
- X
- Xincfield: sign unit
- X { inc_sec += ($1 * $2); }
- X | sign NUMBER unit
- X { inc_sec += ($1 * $2 * $3); }
- X | sign MONUNIT
- X { inc_mon += ($1 * $2); }
- X | sign NUMBER MONUNIT
- X { inc_mon += ($1 * $2 * $3); }
- X ;
- X
- Xsign: '+'
- X { $$ = 1; }
- X | '-'
- X { $$ = -1; }
- X ;
- X
- Xunit: UNIT
- X | SECOND
- X ;
- X
- Xnumber: NUMBER
- X {
- X if (time_fields > 0 && date_fields > 0)
- X year = $1;
- X else {
- X
- X time_fields++;
- X num_to_time($1);
- X meridian = MILITARY;
- X }
- X }
- X ;
- X
- X%%
- X
- X/* ------------------------------------------------------------------------- */
- X
- X#include "lex.yy.c"
- X
- X/* ------------------------------------------------------------------------- */
- X
- Xtime_t
- Xparse_date(string, base)
- X
- Xchar *string;
- Xstruct timeb *base;
- X
- X{
- X register int i;
- X int length;
- X int rc;
- X
- X struct timeb *ourtime;
- X struct timeb current;
- X struct tm *local;
- X
- X time_t tod;
- X
- X /* Initialize the various global variables */
- X
- X date_fields = 0;
- X day_fields = 0;
- X inc_fields = 0;
- X rday_fields = 0;
- X time_fields = 0;
- X zone_fields = 0;
- X
- X inc_mon = 0;
- X inc_sec = 0;
- X rel_sec = 0;
- X ordinal = 0;
- X meridian = MILITARY;
- X yyerrmsg[0] = '\0';
- X zone_type = NOT_GIVEN;
- X
- X /* Determine the base time (for "now" and relative references) */
- X
- X if (base == NULL) {
- X
- X ourtime = ¤t;
- X
- X if (ftime(ourtime) < 0) {
- X
- X sprintf(yyerrmsg, "can't get current time via ftime: %s",
- X sys_errlist[errno]);
- X return(-1);
- X }
- X }
- X
- X else
- X ourtime = base;
- X
- X tod = ourtime->time;
- X
- X if ((local = localtime(&tod)) == NULL) {
- X
- X sprintf(yyerrmsg, "couldn't convert time via localtime: %s",
- X sys_errlist[errno]);
- X return(-1);
- X }
- X
- X /* Initialize values for current time */
- X
- X month = local->tm_mon;
- X day = local->tm_mday;
- X weekday = local->tm_wday;
- X year = local->tm_year;
- X hour = local->tm_hour;
- X minute = local->tm_min;
- X second = local->tm_sec;
- X timezone = ourtime->timezone * SEC_PER_MIN;
- X
- X /* Make a local, lower case copy of the date/time string */
- X
- X length = strlen(string);
- X
- X if ((lstring = malloc(length + 1)) == NULL) {
- X
- X sprintf(yyerrmsg, "couldn't allocate memory for date/time string: %s",
- X sys_errlist[errno]);
- X return(-1);
- X }
- X
- X for (i = 0 ; i <= length ; i++) {
- X
- X register char ch;
- X
- X ch = string[i];
- X
- X if (isalpha(ch) && isupper(ch))
- X lstring[i] = tolower(ch);
- X else
- X lstring[i] = ch;
- X }
- X
- X lptr = lstring;
- X
- X /* Attempt to parse the input */
- X
- X if ((rc = yyparse()) != 0)
- X return(-1);
- X
- X /* Check for an allowable number of each field */
- X
- X if ((day_fields + rday_fields) > 1) {
- X
- X strcpy(yyerrmsg, "more than one day specified");
- X return(-1);
- X }
- X
- X if (date_fields > 1) {
- X
- X strcpy(yyerrmsg, "more than one date specified");
- X return(-1);
- X }
- X
- X if (time_fields > 1) {
- X
- X strcpy(yyerrmsg, "more than one time specified");
- X return(-1);
- X }
- X
- X if (zone_fields > 1) {
- X
- X strcpy(yyerrmsg, "more than one timezone specified");
- X return(-1);
- X }
- X
- X /* Convert the parsed fields into a time from epoch */
- X
- X if (date_fields > 0 || time_fields > 0 || day_fields > 0) {
- X
- X if ((tod = comp_tval()) < 0)
- X return(tod);
- X }
- X
- X /* Adjust for any relative day (e.g., tomorrow) specified */
- X
- X tod += rel_sec;
- X
- X /* Rationalize the time value, if no date is given (i.e., make */
- X /* certain it is not a past time) */
- X
- X if (date_fields == 0 && rday_fields == 0 && tod < ourtime->time
- X && ordinal >= 0)
- X tod += SEC_PER_DAY;
- X
- X /* Adjust for any increment(s) specified */
- X
- X if (inc_mon > 0)
- X tod += month_adjust(tod);
- X
- X if (day_fields > 0 && date_fields == 0)
- X tod += day_adjust(tod);
- X
- X tod += inc_sec;
- X
- X /* Free the storage for the local copy of the input string */
- X
- X (void)free(lstring);
- X
- X /* Return the computed time */
- X
- X return(tod);
- X}
- X
- X/* ------------------------------------------------------------------------- */
- X
- Xtime_t
- Xcomp_tval()
- X
- X{
- X register int i;
- X int n_days;
- X int offset;
- X int rc;
- X
- X time_t tval;
- X
- X /* Adjust year, if necessary */
- X
- X if (year < EPOCH)
- X year += 1900;
- X
- X /* Adjust length of February for leap year, if necessary */
- X /* (True, reality is more complicated, but not needed for this program) */
- X
- X month_len[1] = 28 + (year % 4 == 0);
- X
- X /* Check for valid month and day */
- X
- X if (month < 0 || month > 11) {
- X
- X strcpy(yyerrmsg, "invalid month");
- X return(-1);
- X }
- X
- X if (day < 1 || day > month_len[month]) {
- X
- X strcpy(yyerrmsg, "invalid day of month");
- X return(-1);
- X }
- X
- X /* Compute number of days from epoch to this day */
- X
- X n_days = day - 1;
- X
- X for (i = 0 ; i < month ; i++)
- X n_days += month_len[i];
- X
- X for (i = EPOCH ; i < year ; i++)
- X n_days += 365 + (i % 4 == 0);
- X
- X /* Check for valid minutes and seconds */
- X
- X if (minute < 0 || minute > 59) {
- X
- X strcpy(yyerrmsg, "invalid minutes value");
- X return(-1);
- X }
- X
- X
- X if (second < 0 || second > 59) {
- X
- X strcpy(yyerrmsg, "invalid seconds value");
- X return(-1);
- X }
- X
- X /* Compute the number of seconds since beginning of this day */
- X
- X rc = 0;
- X offset = 0;
- X
- X switch (meridian) {
- X
- X case MILITARY:
- X
- X if (hour < 0 || hour > 23)
- X rc = -1;
- X
- X else
- X tval = ((hour * MIN_PER_HOUR) + minute) * SEC_PER_MIN + second;
- X
- X break;
- X
- X case PM:
- X
- X offset = 12;
- X /* fall through to next case */
- X
- X case AM:
- X
- X if (hour < 1 || hour > 12)
- X rc = -1;
- X
- X else
- X tval = (((hour + offset) * MIN_PER_HOUR) + minute)
- X * SEC_PER_MIN + second;
- X
- X break;
- X
- X default:
- X
- X strcpy(yyerrmsg,
- X "can't happen: unknown meridian value in comp_tval");
- X return(-1);
- X }
- X
- X if (rc < 0) {
- X
- X strcpy(yyerrmsg, "invalid hours value");
- X return(-1);
- X }
- X
- X /* Add seconds since beginning of epoch to seconds this day */
- X
- X tval += n_days * SEC_PER_DAY;
- X
- X /* Add in adjustment for timezone (to get to GMT) */
- X
- X tval += timezone;
- X
- X /* Adjust for daylight saving time, if necessary */
- X
- X if (zone_type == DST ||
- X (zone_type == NOT_GIVEN && localtime(&tval)->tm_isdst))
- X tval -= SEC_PER_HOUR;
- X
- X return(tval);
- X}
- X
- X/* ------------------------------------------------------------------------- */
- X
- Xday_adjust(tod)
- X
- Xtime_t tod;
- X
- X{
- X int day_diff;
- X int inc;
- X int ord_value;
- X
- X struct tm *now;
- X
- X now = localtime(&tod);
- X
- X /* Compute the number of days until the specified day */
- X /* and the corresponding number of seconds */
- X
- X day_diff = (weekday - now->tm_wday + 7) % 7;
- X inc = SEC_PER_DAY * day_diff;
- X
- X /* If a postitive ordinal value was specified (e.g., third), */
- X /* adjust the value used in the subsequent computations */
- X
- X if (ordinal > 0) {
- X
- X /* If the ordinal is "first" or "next" and the day is the same */
- X /* as today, make sure we move ahead one week */
- X
- X if (ordinal == 1 && day_diff == 0)
- X ord_value = 1;
- X else
- X ord_value = ordinal - 1;
- X }
- X
- X else
- X ord_value = ordinal;
- X
- X /* Adjust for the number of weeks until the specified day */
- X
- X inc += 7 * SEC_PER_DAY * ord_value;
- X
- X return(inc);
- X}
- X
- X/* ------------------------------------------------------------------------- */
- X
- X#include "table.h"
- X
- Xstatic int hash[256]; /* One value for each ASCII character */
- Xstatic int lookup_init = TRUE; /* TRUE if init needs to be done */
- Xstatic int table_size; /* Number of string table entries */
- X
- Xstatic void linit();
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xlookup_string(string)
- X
- Xchar *string;
- X
- X{
- X register int i;
- X
- X /* Do we need to initialize the string and hash tables? */
- X
- X if (lookup_init)
- X linit();
- X
- X /* Scan all table entries with a matching first letter */
- X
- X for (i = hash[*string] ;
- X i < table_size && *string_table[i].ta_name == *string ; i++) {
- X
- X if (strcmp(string_table[i].ta_name, string) == 0) {
- X
- X yylval = string_table[i].ta_value;
- X return(string_table[i].ta_type);
- X }
- X }
- X
- X return(-1);
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xstatic
- Xentry_comp(ptr1, ptr2)
- X
- Xregister struct table_def *ptr1;
- Xregister struct table_def *ptr2;
- X
- X{
- X return(strcmp(ptr1->ta_name, ptr2->ta_name));
- X}
- X
- X/* -------------------------------------------------------------------------- */
- X
- Xstatic void
- Xlinit()
- X
- X{
- X register int ch;
- X register int i;
- X
- X /* Indicate that an initialization is no longer needed */
- X
- X lookup_init = FALSE;
- X table_size = sizeof(string_table) / sizeof(struct table_def);
- X
- X /* Sort the table by ASCII code */
- X
- X qsort(string_table, table_size, sizeof(struct table_def), entry_comp);
- X
- X /* Build a simple hash code table (based on first character of string) */
- X /* If there are no entries for a hash value, the hash table has the */
- X /* index of the next existing hash value. */
- X
- X for (i = 0, ch = 0 ; i < table_size ; i++) {
- X
- X while (*string_table[i].ta_name >= ch)
- X hash[ch++] = i;
- X }
- X
- X return;
- X}
- X
- X/* ------------------------------------------------------------------------- */
- X
- Xmonth_adjust(tod)
- X
- Xtime_t tod;
- X
- X{
- X int n_months;
- X
- X struct tm *now;
- X
- X now = localtime(&tod);
- X n_months = now->tm_mon + inc_mon;
- X year += n_months / 12;
- X month = n_months % 12;
- X
- X return(comp_tval() - tod);
- X}
- X
- X/* ------------------------------------------------------------------------- */
- X
- Xvoid
- Xnum_to_time(value)
- X
- Xint value;
- X
- X{
- X minute = 0;
- X second = 0;
- X
- X if (value >= 10000) {
- X second = value % 100;
- X value /= 100;
- X }
- X if (value >= 100) {
- X minute = value % 100;
- X value /= 100;
- X }
- X
- X hour = value;
- X return;
- X}
- X
- X/* ------------------------------------------------------------------------- */
- X
- Xyyerror(string)
- X
- Xchar *string;
- X
- X{
- X sprintf(yyerrmsg, "%s: at or near token \"%s\"", string, yytext);
- X}
- END_OF_FILE
- if test 13039 -ne `wc -c <'src/date.y'`; then
- echo shar: \"'src/date.y'\" unpacked with wrong size!
- fi
- # end of 'src/date.y'
- fi
- echo shar: End of archive 2 \(of 2\).
- cp /dev/null ark2isdone
- MISSING=""
- for I in 1 2 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked both archives.
- rm -f ark[1-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
- --
- -----------------------------------------------------------------------------
- Keith Pyle UUCP: ...!cs.utexas.edu!execu!keith
- Execucom Systems Corp., Austin, Texas Internet: keith@execu.com
- "It's 10 o'clock. Do you know where Disclaimer: Everything I say is
- your child processes are?" true unless I use the word 'the'.
- -----------------------------------------------------------------------------
-