home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2757 < prev    next >
Internet Message Format  |  1991-02-13  |  59KB

  1. From: keith@sequoia.execu.com (Keith Pyle)
  2. Newsgroups: alt.sources
  3. Subject: Keith's at package, part 2 of 2
  4. Message-ID: <32217@sequoia.execu.com>
  5. Date: 14 Feb 91 04:46:51 GMT
  6.  
  7.  
  8. #! /bin/sh
  9. # This is a shell archive.  Remove anything before this line, then unpack
  10. # it by saving it into a file and typing "sh file".  To overwrite existing
  11. # files, type "sh file -c".  You can also feed this as standard input via
  12. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  13. # will see the following message at the end:
  14. #        "End of archive 2 (of 2)."
  15. # Contents:  doc/at.1 src/at.c src/atq.c src/atrun.c src/date.y
  16. # Wrapped by keith@lime on Wed Feb 13 22:33:50 1991
  17. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  18. if test -f 'doc/at.1' -a "${1}" != "-c" ; then 
  19.   echo shar: Will not clobber existing file \"'doc/at.1'\"
  20. else
  21. echo shar: Extracting \"'doc/at.1'\" \(7841 characters\)
  22. sed "s/^X//" >'doc/at.1' <<'END_OF_FILE'
  23. X.TH "AT" 1L "7 November 1990" "Execucom" "LOCAL USER COMMANDS"
  24. X.SH NAME
  25. Xat \- execute commands at a specified time
  26. X.SH SYNOPSIS
  27. X.LP
  28. X.B at
  29. X[
  30. X.B \-m
  31. X] [
  32. X.B \-cks
  33. X]
  34. X.I time
  35. X[
  36. X.I date
  37. X] [
  38. X.B \+increment
  39. X] [
  40. X.I script
  41. X]
  42. X.SH DESCRIPTION
  43. X.B at
  44. Xis a utility which accepts user commands to be executed at a specified later
  45. Xtime.  The execution time can be given as a time only, as a time and date,
  46. Xor as either of these with an increment added.  If a
  47. X.I script
  48. Xis specified, this file will be read to obtain the commands that will be
  49. Xexecuted.  Otherwise,
  50. X.B at
  51. Xwill read standard input for the commands to be queued.  If standard input
  52. Xis a terminal device,
  53. X.B at
  54. Xwill prompt for input with the string
  55. X.LP
  56. X.RS
  57. Xat>
  58. X.RE
  59. X.LP
  60. XThe shell used to process the commands will be determined as follows: (1) if
  61. Xa command line argument is used to specify a shell, that shell will be used,
  62. X(2) if the commands come from a
  63. X.I script
  64. Xand that script specifies a shell on its first line, e.g., #!/bin/sh, the
  65. Xindicated shell will be used, or (3) the shell specified in the current
  66. XSHELL environment variable will be used.
  67. X.LP
  68. XIf the queued job generates output on either standard output or standard
  69. Xerror, the output will be mailed to the user who submitted the job.  If no
  70. Xoutput is generated or if it is redirected in the commands, there will be
  71. Xno mail to the user unless either (1) the commands terminated with an
  72. Xerror status, or (2) the user requests confirmation of the run through the
  73. X.B \-m
  74. Xoption.
  75. X.LP
  76. XThe user's environment variables (except TERM), current directory, and
  77. Xumask are copied to the queued job when it is created.  If
  78. X.I script
  79. Xis specified, its contents are copied to the queued job.  Thus, subsequent
  80. Xchanges to
  81. X.I script
  82. Xor its deletion will not affect the
  83. X.B at
  84. Xjob.
  85. X.LP
  86. XThe format of
  87. X.I time
  88. Xis quite flexible.  It may consist of one or two digits to indicate hours only.
  89. XIf three or four digits are given, they are taken to indicate hours and minutes.
  90. XAn optional colon between the hours and minutes is allowed.  The hours
  91. Xspecification is assumed to be a 24 hour clock reference unless an am or pm
  92. Xsuffix is present.  The time zone may be included; if not, the current time
  93. Xzone is assumed.  The special times
  94. X.B now, noon,
  95. Xand
  96. X.B midnight
  97. Xare also recognized.  Case is not significant for any of the character fields.
  98. X.LP
  99. XThe
  100. X.I date
  101. Xis optional and may be any of the following forms: (1) month name and day,
  102. X(2) month name, day, comma, and year, (3) day and month name, (4) day, month
  103. Xname, and year, (5) month number, slash, and day, (6) month number,
  104. Xslash, day, slash, and year, or (7) a day of the week reference.
  105. XThe month name may be abbreviated to the first
  106. Xthree letters in the forms using it.
  107. XIn those forms where the year is not specified,
  108. Xthe current year is used.  If a two digit year is specified, 1900 is added to
  109. Xit.  The special dates today and tomorrow can be used in place of the
  110. X.I date
  111. Xfield.  If no
  112. X.I date
  113. Xis specified, the current date is used.
  114. X.LP
  115. XThe optional
  116. X.I +increment
  117. Xor
  118. X.I -increment
  119. Xconsists of a simple number suffixed by one of the following: minute, hour,
  120. Xday, week, month, or year.  The plural form of any of these keywords will also
  121. Xwork.  The following abbreviations are also valid: min, m, hr, h, d, wk, w,
  122. Xmon, yr, and y.
  123. X.LP
  124. XIf the
  125. X.I date
  126. Xspecification is omitted, the time will be interpreted as the next occurrence
  127. Xof that time.  For instance, if it is 11 AM and the command
  128. X.LP
  129. X.RS
  130. Xat 10 am
  131. X.RE
  132. X.LP
  133. Xis given, the at job will be scheduled for 10 AM on the following day.
  134. X.LP
  135. XThe following are example of allowable time/date specifications for the
  136. Xcurrent time assuming it is 1:00 AM on August 24, 1990:
  137. X.LP
  138. X.RS
  139. X1
  140. X.br
  141. X100
  142. X.br
  143. X1:00
  144. X.br
  145. X0100
  146. X.br
  147. X1:00 am
  148. X.br
  149. X1:00 aug 24
  150. X.br
  151. X1:00 24 aug
  152. X.br
  153. X0100 8/24
  154. X.br
  155. X01 8/24/90
  156. X.RE
  157. X.LP
  158. XThe use of the day of week reference consists of a time and the name of a
  159. Xday of the week.  The day name may be preceded by an ordinal reference (e.g.,
  160. Xthird). For example,
  161. X.LP
  162. X.RS
  163. X1 pm friday
  164. X.br
  165. X1300 second friday
  166. X.RE
  167. X.LP
  168. XThe special ordinal value
  169. X.B this
  170. Xis provided, however, it has no influence on the computation since
  171. X"this Friday" is synonymous with simply "Friday".
  172. X.B next
  173. Xis synonymous
  174. Xwith the ordinal
  175. X.B first
  176. Xand both imply the next occurence of the specified day.
  177. XThus, both "first Friday"
  178. Xand "next Friday" will be
  179. Xthe first Friday after today.  If today were Friday, either of these would
  180. Xrefer to the day one week from now.
  181. X.LP
  182. XWhen the job is entered into the
  183. X.B at
  184. Xqueue, a unique job number and the scheduled time will be displayed on
  185. Xstandard output.
  186. X.LP
  187. XThere may be restrictions on
  188. X.B at
  189. Xusage depending upon the policies of the installation.  These restrictions
  190. Xare accomplished through one of two files:
  191. X.B at\.allow
  192. Xor
  193. X.B at\.deny.
  194. XIf
  195. X.B at\.allow
  196. Xexists, only those users listed in it may use the
  197. X.B at
  198. Xprograms.  If it does not exist, but
  199. X.B at\.deny
  200. Xdoes exist, all users except those listed may submit jobs.  If
  201. X.B at\.deny
  202. Xis empty, all users are allowed to submit jobs.  If neither file exists,
  203. Xonly the super-user may use
  204. X.B at.
  205. XEntries in either file consist of one user name per line.
  206. X.LP
  207. XThe execution time specified is the earliest that the job will be run.
  208. XAll jobs are run via the
  209. X.BR atrun (8)
  210. Xcommand, which is typically invoked periodically by
  211. X.BR cron (8).
  212. XThe exact time of execution will depend upon the frequency of execution
  213. Xof
  214. X.BR atrun (8).
  215. X.SH OPTIONS
  216. X.TP
  217. X.B \-c
  218. XUse the C shell to execute the specified commands.
  219. X.TP
  220. X.B \-k
  221. XUse the Korn shell to execute the specified commands.
  222. X.TP
  223. X.B \-m
  224. XSend mail to the user who submitted the job on completion, even if no
  225. Xoutput is generated and no error occurs.
  226. X.TP
  227. X.B \-s
  228. XUse the Bourne shell to execute the specified commands.
  229. X.SH EXAMPLES
  230. X(1) To delete a .forward file on June 29 at 10:38 in the morning (e.g., to
  231. Xterminate vacation processing), the sequence could be
  232. X.sp
  233. X.RS
  234. X.B "at 10:38 am June 29"
  235. X.br
  236. Xat>
  237. X.B "rm .forward"
  238. X.br
  239. Xat>
  240. X.B "CTRL-D"
  241. X.RE
  242. X.sp
  243. XThere is no limit on the number of commands that can be entered.
  244. XNote that CTRL-D is the control-D character (hold down control and press D).
  245. X.LP
  246. X(2) To execute the script
  247. X.B cleanup
  248. Xat 3:00 AM on this coming Friday, the
  249. X.B at
  250. Xcommand could be
  251. X.sp
  252. X.RS
  253. X.B "at 3:00 AM Friday cleanup"
  254. X.RE
  255. X.LP
  256. X(3) To perform the same script at the same time, but with notification of
  257. Xits completion, the following command could be used.
  258. X.sp
  259. X.RS
  260. X.B "at -m 0300 fri cleanup"
  261. X.RE
  262. X.LP
  263. X(4) If the
  264. X.B cleanup
  265. Xscript were written for the C shell and the
  266. X.B at
  267. Xjob is being submitted while running a different shell (e.g., the Bourne
  268. Xshell), it would be necessary to force
  269. X.B at
  270. Xto run the commands in the script using the C shell.
  271. X.sp
  272. X.RS
  273. X.B "at -c -m 3 Fri cleanup"
  274. X.RE
  275. X.sp
  276. XNote that the time specifications shown in the preceding three examples
  277. Xwill all result in the same execution time.
  278. X.SH AUTHOR
  279. XKeith Pyle
  280. X.SH FILES
  281. X.ta 2.5i
  282. X\fB/usr/spool/at\fR    spool directory
  283. X.br
  284. X\fB/usr/spool/at/at.allow\fR    allowed users
  285. X.br
  286. X\fB/usr/spool/at/at.deny\fR    prohibited users
  287. X.br
  288. X\fB/usr/spool/at/.seq\fR    job sequence number
  289. X.SH "SEE ALSO"
  290. X.BR atcat (1L),
  291. X.BR atq (1L),
  292. X.BR atrm (1L),
  293. X.BR atrun (8)
  294. X.SH DIAGNOSTICS
  295. XThe exit status is 0 when a job is correctly queued.  For file access failures
  296. Xand other execution problems, the exit status is set to 1.
  297. XAn exit status of 2 is set for syntax errors.
  298. X.SH BUGS
  299. X.LP
  300. XIf a job does not complete due to a system failure, it will not be requeued.
  301. XHowever, it would be possible to affect this function by modification of the
  302. Xappropriate boot file (e.g., rc.local on a BSD system).  Assuming that the
  303. X.B at
  304. Xqueue directory is /usr/spool/at and the working directory is
  305. X/usr/spool/at/past, the following shell
  306. Xcommand could be used:
  307. X.sp
  308. X.RS
  309. X.ft B
  310. Xfind /usr/spool/at/past -name '[1-9]*'
  311. X.if n \{ \\
  312. X.br
  313. X.ti +0.5i
  314. X\}
  315. X-exec mv {} /usr/spool/at \\;
  316. X.ft R
  317. X.RE
  318. X.LP
  319. XThe next execution of
  320. X.BR atrun (8)
  321. Xwill restart these jobs.
  322. END_OF_FILE
  323. if test 7841 -ne `wc -c <'doc/at.1'`; then
  324.     echo shar: \"'doc/at.1'\" unpacked with wrong size!
  325. fi
  326. # end of 'doc/at.1'
  327. fi
  328. if test -f 'src/at.c' -a "${1}" != "-c" ; then 
  329.   echo shar: Will not clobber existing file \"'src/at.c'\"
  330. else
  331. echo shar: Extracting \"'src/at.c'\" \(8859 characters\)
  332. sed "s/^X//" >'src/at.c' <<'END_OF_FILE'
  333. X/* at --- a member of the kat family (Keith's at package) */
  334. X
  335. X/* at(1) queues jobs for later execution by atrun.  It is similar in */
  336. X/* function to the at(1) supplied with most BSD systems, but with additional */
  337. X/* capabilities in date parsing, shell selection, and confirmation of the */
  338. X/* run. */
  339. X
  340. X/* Copyright (c) 1990, W. Keith Pyle, Austin, Texas */
  341. X
  342. X#include "at.h"
  343. X
  344. Xchar at_file[MAXPATHLEN];
  345. Xchar at_tmp[MAXPATHLEN];
  346. Xchar buffer[MAXLINE];
  347. Xchar *verstring = PATCHLEVEL;
  348. X
  349. Xint jobno;
  350. X
  351. Xchar *atname();
  352. Xchar *ctime();
  353. Xchar *rindex();
  354. Xtime_t parse_date();
  355. Xvoid quit();
  356. X
  357. Xextern char *optarg;
  358. X
  359. Xextern int opterr;
  360. Xextern int optind;
  361. X
  362. X/* -------------------------------------------------------------------------- */
  363. X
  364. Xmain(argc, argv, envp)
  365. X
  366. Xint argc;
  367. Xchar *argv[];
  368. Xchar *envp[];
  369. X
  370. X{
  371. X    char *lastarg;
  372. X    char owner[32];
  373. X    char script[128];
  374. X    char shell[33];
  375. X
  376. X    int flag;
  377. X    int mail;
  378. X    int mask;
  379. X
  380. X    time_t at_time;
  381. X    time_t now_time;
  382. X
  383. X    FILE *fp_at_file;
  384. X    FILE *fp_script;
  385. X
  386. X    if (argc == 1)
  387. X        usage();
  388. X
  389. X    /* Some initialization */
  390. X
  391. X    mail = FALSE;
  392. X    shell[0] = '\0';
  393. X
  394. X    /* Get the user name */
  395. X
  396. X    whoami(owner);
  397. X
  398. X    /* Check for allowed at usage (just use two handy arrays for the names) */
  399. X
  400. X    (void)sprintf(at_tmp, "%s/%s", ATDIR, ALLOW_FILE);
  401. X    (void)sprintf(at_file, "%s/%s", ATDIR, DENY_FILE);
  402. X    
  403. X    if (!permit(owner, at_tmp, at_file)) {
  404. X
  405. X        (void)fprintf(stderr, "you are not authorized to use at.  Sorry.\n");
  406. X        exit(1);
  407. X    }
  408. X
  409. X    /* Parse any option flags */
  410. X
  411. X    while ((flag = getopt(argc, argv, "ckms")) != EOF) {
  412. X
  413. X        switch (flag) {
  414. X
  415. X            case 'c':
  416. X
  417. X                (void)strcpy(shell,  C_SHELL);
  418. X                break;
  419. X            
  420. X            case 'k':
  421. X
  422. X                (void)strcpy(shell, KORN_SHELL);
  423. X                break;
  424. X            
  425. X            case 'm':
  426. X
  427. X                mail = TRUE;
  428. X                break;
  429. X            
  430. X            case 's':
  431. X
  432. X                (void)strcpy(shell, BOURNE_SHELL);
  433. X                break;
  434. X            
  435. X            default:
  436. X
  437. X                usage();
  438. X        }
  439. X    }
  440. X
  441. X    /* Try to parse all of the remaining arguments as a time specification. */
  442. X    /* If this fails and there at least three arguments, see if the last */
  443. X    /* argument is a file name. */
  444. X
  445. X    lastarg = argv[argc - 1];
  446. X    (void)time(&now_time);
  447. X    now_time -= now_time % 60;
  448. X
  449. X    if ((at_time = parse_args(argc - optind, argv + optind)) != -1) {
  450. X
  451. X        fp_script = stdin;
  452. X        (void)strcpy(script, "stdin");
  453. X    }
  454. X
  455. X    else if ((argc - optind) > 1 &&
  456. X            (at_time = parse_args(argc - optind - 1, argv + optind)) != -1) {
  457. X
  458. X        char *ptr;
  459. X
  460. X        if (access(lastarg, F_OK) != 0) {
  461. X
  462. X            (void)fprintf(stderr, "at: %s does not exist\n", lastarg);
  463. X            exit(1);
  464. X        }
  465. X
  466. X        if ((ptr = rindex(lastarg, '/')) == NULL)
  467. X            ptr = lastarg;
  468. X        else
  469. X            ptr++;
  470. X            
  471. X        (void)strncpy(script, ptr, 127);
  472. X
  473. X        if ((fp_script = fopen(lastarg, "r")) == NULL) {
  474. X
  475. X            perror(lastarg);
  476. X            exit(1);
  477. X        }
  478. X    }
  479. X
  480. X    else {
  481. X
  482. X        (void)fprintf(stderr, "bad date/time specification\n");
  483. X        exit(2);
  484. X    }
  485. X
  486. X    /* Check the 'at time' against the current time */
  487. X
  488. X    if (at_time < now_time) {
  489. X
  490. X        (void)fprintf(stderr, "too late\n");
  491. X        exit(1);
  492. X    }
  493. X
  494. X    /* Build file names for the temporary file and the 'at file' */
  495. X
  496. X    (void)sprintf(at_tmp, "%s/_at%d", ATDIR, getpid());
  497. X    jobno = atseq();
  498. X    (void)sprintf(at_file, "%s/%s", ATDIR, atname(at_time, jobno));
  499. X
  500. X    /* Set up signal handlers */
  501. X
  502. X    signal_init();
  503. X
  504. X    /* Open the temporary file */
  505. X
  506. X    if ((fp_at_file = fopen(at_tmp, "w")) == NULL) {
  507. X
  508. X        perror("can't create a job for you");
  509. X        exit(1);
  510. X    }
  511. X
  512. X    /* If the shell hasn't been specified, determine what should be used */
  513. X
  514. X    if (shell[0] == '\0') {
  515. X
  516. X        /* If the commands are from stdin, use the default shell; */
  517. X        /* otherwise, look at the script to see if a shell is specified */
  518. X
  519. X        if (fp_script == stdin || which_shell(fp_script, shell))
  520. X            (void)strcpy(shell, "$SHELL");
  521. X    }
  522. X
  523. X    /* Put the identification comments in the job file, copy the current */
  524. X    /* environment, and the shell command */
  525. X
  526. X    comments(owner, script, mail, fp_at_file);
  527. X    dumpenv(envp, fp_at_file);
  528. X    shell_start(shell, fp_at_file);
  529. X
  530. X    /* Copy commands from the appropriate source to the job file */
  531. X
  532. X    if (copy_commands(fp_script, fp_at_file) == 0) {
  533. X
  534. X        (void)fprintf(stderr, "nothing specified\n");
  535. X        quit();
  536. X        /* NOTREACHED */
  537. X    }
  538. X
  539. X    /* Change the ownership of the job file to the real user */
  540. X
  541. X    if (chown(at_tmp, getuid(), getgid()) < 0) {
  542. X    
  543. X        perror("can't change the owner/group of your job to you");
  544. X        exit(1);
  545. X    }
  546. X
  547. X    /* Set the mode of the file according to the owner's preference */
  548. X    /* (but allow owner read/write access in any case) */
  549. X    
  550. X    mask = umask(0);
  551. X    (void)umask(mask);
  552. X
  553. X    if (chmod(at_tmp, (mask ^ 0777) | 0600) < 0) {
  554. X
  555. X        perror("can't change the mode of your job");
  556. X        exit(1);
  557. X    }
  558. X
  559. X    /* Close the temporary job file and rename it to the 'at name' */
  560. X
  561. X    (void)fclose(fp_at_file);
  562. X
  563. X    if (rename(at_tmp, at_file) < 0) {
  564. X        
  565. X        perror(at_tmp);
  566. X        (void)unlink(at_tmp);
  567. X        exit(1);
  568. X    }
  569. X
  570. X    /* Say, "Nice user, good user..." */
  571. X
  572. X    (void)printf("job %d at %s", jobno, ctime(&at_time));
  573. X
  574. X    exit(0);
  575. X    /* NOTREACHED */
  576. X}
  577. X
  578. X/* -------------------------------------------------------------------------- */
  579. X
  580. Xvoid
  581. Xquit()
  582. X
  583. X{
  584. X    (void)unlink(at_tmp);
  585. X    (void)unlink(at_file);
  586. X    exit(1);
  587. X}
  588. X
  589. X/* -------------------------------------------------------------------------- */
  590. X
  591. Xcomments(owner, script, mail, fp_at_file)
  592. X
  593. Xchar *owner;
  594. Xchar *script;
  595. Xint mail;
  596. XFILE *fp_at_file;
  597. X
  598. X{
  599. X    (void)fprintf(fp_at_file, "# at job\n");
  600. X    (void)fprintf(fp_at_file, "# owner: %.127s\n", owner);
  601. X    (void)fprintf(fp_at_file, "# jobname: %.127s\n", script);
  602. X    (void)fprintf(fp_at_file, "# shell: sh\n");
  603. X    (void)fprintf(fp_at_file, "# notify by mail: %s\n", mail ? "yes" : "no");
  604. X    (void)fprintf(fp_at_file, "\n");
  605. X}
  606. X
  607. X/* -------------------------------------------------------------------------- */
  608. X
  609. Xcopy_commands(fp_script, fp_at_file)
  610. X
  611. XFILE *fp_script;
  612. XFILE *fp_at_file;
  613. X
  614. X{
  615. X    int ncmd;
  616. X
  617. X    /* If a script has been specified or if stdin has been redirected, */
  618. X    /* read and copy the script */
  619. X
  620. X    if (fp_script != stdin || isatty(0) == 0) {
  621. X
  622. X        for (ncmd = 0 ; fgets(buffer, sizeof(buffer), fp_script) ; ncmd++)
  623. X            fputs(buffer, fp_at_file);
  624. X    }
  625. X
  626. X    /* Otherwise, prompt for each line and copy it */
  627. X
  628. X    else {
  629. X
  630. X        for (ncmd = 0 ; (void)printf("at> "), fgets(buffer, sizeof(buffer),
  631. X                stdin) ; ncmd++)
  632. X            fputs(buffer, fp_at_file);
  633. X
  634. X        (void)printf("<EOT>\n");
  635. X    }
  636. X
  637. X    return(ncmd);
  638. X}
  639. X
  640. X/* -------------------------------------------------------------------------- */
  641. X
  642. X#if defined(GENERIC_SYSV)
  643. X
  644. X/* A dummy routine to keep certain System V machines happy since parse_date */
  645. X/* wants to use it and we want to use the stock version of parse_date */
  646. X
  647. Xftime(dummy)
  648. X
  649. Xstruct timeb *dummy;
  650. X
  651. X{
  652. X}
  653. X
  654. X#endif
  655. X
  656. X/* -------------------------------------------------------------------------- */
  657. X
  658. Xparse_args(nargs, argv)
  659. X
  660. Xint nargs;
  661. Xchar *argv[];
  662. X
  663. X{
  664. X    int i;
  665. X
  666. X    struct timeb *now;
  667. X
  668. X    time_t at_time;
  669. X
  670. X    buffer[0] = '\0';
  671. X
  672. X    /* Build up a string of the specified arguments */
  673. X
  674. X    for (i = 0 ; i < nargs ; i++) {
  675. X
  676. X        (void)strcat(buffer, argv[i]);
  677. X        (void)strcat(buffer, " ");
  678. X    }
  679. X
  680. X    /* Now try to parse it as a time specification */
  681. X
  682. X    now = NULL;
  683. X    at_time = parse_date(buffer, now);
  684. X
  685. X    /* Drop any seconds in the specification */
  686. X
  687. X    if (at_time > 0)
  688. X        at_time -= at_time % 60;
  689. X
  690. X    return(at_time);
  691. X}
  692. X
  693. X/* -------------------------------------------------------------------------- */
  694. X
  695. Xshell_start(shell, fp_at_file)
  696. X
  697. Xchar *shell;
  698. XFILE *fp_at_file;
  699. X
  700. X{
  701. X    char path[MAXPATHLEN];
  702. X    int mask;
  703. X
  704. X    (void)fprintf(fp_at_file,
  705. X        "%s << '...use the rest of this file for input'\n", shell);
  706. X
  707. X#ifdef HAS_GETCWD
  708. X    if (getcwd(path, MAXPATHLEN) == (char *)NULL) {
  709. X#else
  710. X    if (getwd(path) == (char *)NULL) {
  711. X#endif
  712. X
  713. X        (void)fprintf(stderr, "%s\n", path);
  714. X        exit(1);
  715. X    }
  716. X
  717. X    (void)fprintf(fp_at_file, "cd %s\n", path);
  718. X    mask = umask(0);
  719. X    umask(mask);
  720. X    (void)fprintf(fp_at_file, "umask %o\n", mask);
  721. X}
  722. X
  723. X/* -------------------------------------------------------------------------- */
  724. X
  725. Xsignal_init()
  726. X
  727. X{
  728. X    (void)signal(SIGHUP, quit);
  729. X    (void)signal(SIGQUIT, quit);
  730. X    (void)signal(SIGINT, quit);
  731. X    (void)signal(SIGTERM, quit);
  732. X}
  733. X
  734. X/* -------------------------------------------------------------------------- */
  735. X
  736. Xusage()
  737. X
  738. X{
  739. X    (void)printf("usage: at [-c|k|s] [-m] time [date] [script]\n");
  740. X    exit(2);
  741. X}
  742. X
  743. X/* -------------------------------------------------------------------------- */
  744. X
  745. Xwhich_shell(fp_script, shell)
  746. X
  747. XFILE *fp_script;
  748. Xchar *shell;
  749. X
  750. X{
  751. X    char *ptr;
  752. X
  753. X    int length;
  754. X
  755. X    /* Determine if a shell is specified in the script */
  756. X
  757. X    /* If we get EOF, just use the current shell */
  758. X
  759. X    if (fgets(buffer, sizeof(buffer), fp_script) == NULL)
  760. X        return(1);
  761. X    
  762. X    /* Reposition to the beginning of the file */
  763. X
  764. X    if (fseek(fp_script, 0L, 0) < 0) {
  765. X
  766. X        perror("seek on input script failed");
  767. X        exit(1);
  768. X    }
  769. X    
  770. X    /* If we don't get a cookie, forget it */
  771. X
  772. X    if (buffer[0] != '#' || buffer[1] != '!')
  773. X        return(1);
  774. X    
  775. X    /* Skip leading spaces and drop the newline */
  776. X
  777. X    for (ptr = buffer + 2 ; *ptr == ' ' ; ptr++)
  778. X        ;
  779. X    
  780. X    length = strlen(ptr);
  781. X    *(ptr + --length) = '\0';
  782. X
  783. X    /* Is this too long? */
  784. X
  785. X    if (length > 32) {
  786. X
  787. X        (void)fprintf(stderr,
  788. X            "shell specifier (%s) in script is too long - using default\n",
  789. X            ptr);
  790. X        return(1);
  791. X    }
  792. X
  793. X    (void)strcpy(shell, ptr);
  794. X    return(0);
  795. X}
  796. END_OF_FILE
  797. if test 8859 -ne `wc -c <'src/at.c'`; then
  798.     echo shar: \"'src/at.c'\" unpacked with wrong size!
  799. fi
  800. # end of 'src/at.c'
  801. fi
  802. if test -f 'src/atq.c' -a "${1}" != "-c" ; then 
  803.   echo shar: Will not clobber existing file \"'src/atq.c'\"
  804. else
  805. echo shar: Extracting \"'src/atq.c'\" \(6069 characters\)
  806. sed "s/^X//" >'src/atq.c' <<'END_OF_FILE'
  807. X/* atq --- a member of the kat family (Keith's at package) */
  808. X
  809. X/* atq(1) lists all or part of the queue of jobs submitted via at(1). */
  810. X
  811. X/* Copyright (c) 1990, W. Keith Pyle, Austin, Texas */
  812. X
  813. X#include "at.h"
  814. X
  815. Xchar *verstring = PATCHLEVEL;
  816. X
  817. Xchar *disp_date();
  818. Xchar *nth();
  819. Xint comp_ctime();
  820. Xint comp_start();
  821. X
  822. Xextern char *optarg;
  823. Xextern char *sys_errlist[];
  824. X
  825. Xextern int optind;
  826. Xextern int opterr;
  827. X
  828. X/* -------------------------------------------------------------------------- */
  829. X
  830. Xmain(argc, argv)
  831. X
  832. Xint argc;
  833. Xchar *argv[];
  834. X
  835. X{
  836. X    char jobname[32];                    /* Jobname from queued jobfile */
  837. X    char owner[16];                        /* Owner of queued job */
  838. X
  839. X    int count_only;                        /* TRUE for display count only */
  840. X    int flag;                            /* The getopt found flag */
  841. X    int i;                                /* An index */
  842. X    int n_jobs;                            /* The number of jobs to be listed */
  843. X    int qsize;                            /* Size of queue data structure */
  844. X
  845. X    int (*sort_func)();                    /* Pointer to function for sort use */
  846. X
  847. X    struct dirent *entry;                /* Pointer to directory entry */
  848. X    struct queue *atq;                    /* The queue list data structure ptr */
  849. X    register struct queue *qptr;        /* A working queue list pointer */
  850. X
  851. X    DIR *dp;                            /* Descriptor for queue directory */
  852. X
  853. X    /* Check for allowed at usage */
  854. X     
  855. X    whoami(owner);
  856. X    
  857. X    /* Change to the at directory and check permissions */
  858. X
  859. X    if (chdir(ATDIR) < 0) {
  860. X
  861. X        perror("can't change directory to the at directory");
  862. X        exit(1);
  863. X    }
  864. X
  865. X    if (!permit(owner, ALLOW_FILE, DENY_FILE)) {
  866. X        fprintf(stderr, "you are not authorized to use at.  Sorry.\n");
  867. X        exit(1);
  868. X    }
  869. X
  870. X    /* Some initialization */
  871. X
  872. X    count_only = FALSE;
  873. X    qsize = 0;
  874. X    sort_func = comp_start;
  875. X
  876. X    /* Parse any arguments */
  877. X
  878. X    while ((flag = getopt(argc, argv, "cn")) != EOF) {
  879. X
  880. X        switch (flag) {
  881. X
  882. X            case 'c':
  883. X
  884. X                sort_func = comp_ctime;
  885. X                break;
  886. X            
  887. X            case 'n':
  888. X
  889. X                count_only = TRUE;
  890. X                break;
  891. X
  892. X            default:
  893. X
  894. X                fprintf(stderr, "usage: atq [-c|n] [username]\n");
  895. X                exit(2);
  896. X        }
  897. X    }
  898. X
  899. X    /* Try to open the at job directory */
  900. X
  901. X    if ((dp = opendir(".")) == NULL) {
  902. X
  903. X        perror("can't read the at directory");
  904. X        exit(1);
  905. X    }
  906. X
  907. X    /* Process all of the directory entries which have names of the proper */
  908. X    /* format for a queued at job */
  909. X
  910. X    while ((entry = readdir(dp)) != NULL) {
  911. X
  912. X        int jobno;
  913. X
  914. X        time_t tod;
  915. X
  916. X        struct stat statbuf;
  917. X
  918. X        /* Is the name of the proper format? */
  919. X
  920. X        if (sscanf(entry->d_name, "%d.%d", &tod, &jobno) != 2)
  921. X            continue;
  922. X        
  923. X        if (stat(entry->d_name, &statbuf) < 0) {
  924. X
  925. X            perror(entry->d_name);
  926. X            continue;
  927. X        }
  928. X
  929. X        /* If there are names in the argument list, do any of them */
  930. X        /* match the owner of this jobfile? */
  931. X
  932. X        if (optind < argc) {
  933. X
  934. X            int found;
  935. X
  936. X            struct passwd *pw;
  937. X
  938. X            if ((pw = getpwuid(statbuf.st_uid)) == NULL) {
  939. X
  940. X                perror(entry->d_name);
  941. X                continue;
  942. X            }
  943. X
  944. X            found = FALSE;
  945. X
  946. X            for (i = optind ; i < argc ; i++) {
  947. X
  948. X                if (strcmp(argv[i], pw->pw_name) == 0) {
  949. X                    
  950. X                    found = TRUE;
  951. X                    break;
  952. X                }
  953. X            }
  954. X
  955. X            if (!found)
  956. X                continue;
  957. X        }
  958. X
  959. X        /* This entry should be listed.  Make sure there is space */
  960. X        /* in the queue data structure to store it */
  961. X
  962. X        if ((n_jobs + 1) > qsize) {
  963. X
  964. X            if ((qsize = qalloc(&atq, qsize)) < 0) {
  965. X
  966. X                fprintf(stderr, "can't allocate space for queue data\n");
  967. X                exit(1);
  968. X            }
  969. X        }
  970. X            
  971. X        /* Save the info */
  972. X
  973. X        atq[n_jobs].qu_start = tod;
  974. X        strcpy(atq[n_jobs].qu_name, entry->d_name);
  975. X        atq[n_jobs].qu_jobno = jobno;
  976. X        atq[n_jobs].qu_ctime = statbuf.st_ctime;
  977. X        n_jobs++;
  978. X    }
  979. X
  980. X    closedir(dp);
  981. X
  982. X    /* Do we do a count only? */
  983. X
  984. X    if (count_only) {
  985. X
  986. X        printf("%d\n", n_jobs);
  987. X        exit(0);
  988. X    }
  989. X
  990. X    /* Were there any jobs? */
  991. X
  992. X    if (n_jobs == 0) {
  993. X
  994. X        printf("no files in queue\n");
  995. X        exit(0);
  996. X    }
  997. X
  998. X    /* If more than one job, sort them */
  999. X
  1000. X    else if (n_jobs > 1)
  1001. X        qsort(atq, n_jobs, sizeof(struct queue), sort_func);
  1002. X    
  1003. X    /* Print the info on the selected queued entries */
  1004. X
  1005. X    printf(" Rank     Execution Date     Owner     Job #   Job Name\n");
  1006. X
  1007. X    for (i = 1, qptr = atq ; i <= n_jobs ; i++, qptr++) {
  1008. X
  1009. X        if (get_ident(qptr->qu_name, owner, jobname) < 0)
  1010. X            continue;
  1011. X
  1012. X        printf("%3d%s   %s   %-10s%5d   %s\n", i, nth(i),
  1013. X            disp_date(qptr->qu_start), owner, qptr->qu_jobno, jobname);
  1014. X    }
  1015. X
  1016. X    exit(0);
  1017. X}
  1018. X
  1019. X/* -------------------------------------------------------------------------- */
  1020. X
  1021. Xcomp_ctime(str1, str2)
  1022. X
  1023. Xstruct queue *str1;
  1024. Xstruct queue *str2;
  1025. X
  1026. X{
  1027. X    /* Compare the creation times of the two entries */
  1028. X
  1029. X    return(str1->qu_ctime - str2->qu_ctime);
  1030. X}
  1031. X
  1032. X/* -------------------------------------------------------------------------- */
  1033. X
  1034. Xcomp_start(str1, str2)
  1035. X
  1036. Xstruct queue *str1;
  1037. Xstruct queue *str2;
  1038. X
  1039. X{        
  1040. X    /* Compare the start times of the two entries */
  1041. X
  1042. X    return(str1->qu_start - str2->qu_start);
  1043. X}
  1044. X
  1045. X/* -------------------------------------------------------------------------- */
  1046. X
  1047. Xchar *
  1048. Xdisp_date(tod)
  1049. X
  1050. Xtime_t tod;
  1051. X
  1052. X{
  1053. X    static char datebuf[20];
  1054. X    static char *mon[] = {
  1055. X        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1056. X        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
  1057. X        };
  1058. X
  1059. X    struct tm *tm;
  1060. X
  1061. X    tm = localtime(&tod);
  1062. X    sprintf(datebuf, "%s %2d, %4d %02d:%02d", mon[tm->tm_mon], tm->tm_mday,
  1063. X        tm->tm_year + 1900, tm->tm_hour, tm->tm_min);
  1064. X    
  1065. X    return(datebuf);
  1066. X}
  1067. X
  1068. X/* -------------------------------------------------------------------------- */
  1069. X
  1070. Xget_ident(jobfile, owner, jobname)
  1071. X
  1072. Xchar *jobfile;
  1073. Xchar *owner;
  1074. Xchar *jobname;
  1075. X
  1076. X{
  1077. X    char line[MAXLINE];
  1078. X
  1079. X    int found_job;
  1080. X    int found_owner;
  1081. X
  1082. X    FILE *fp;
  1083. X
  1084. X    /* Open the jobfile */
  1085. X
  1086. X    if ((fp = fopen(jobfile, "r")) == NULL) {
  1087. X
  1088. X        printf("atq: Can't open job file %s: %s\n",
  1089. X            jobfile, sys_errlist[errno]);
  1090. X        return(-1);
  1091. X    }
  1092. X
  1093. X    /* Look for the owner and jobname lines of the jobfile */
  1094. X    
  1095. X    found_job = found_owner = FALSE;
  1096. X    *owner = *jobname = '\0';
  1097. X
  1098. X    while (fgets(line, sizeof(line), fp)) {
  1099. X
  1100. X        if (sscanf(line, "# owner: %10s", owner) == 1)
  1101. X            found_owner = TRUE;
  1102. X        
  1103. X        else if (sscanf(line, "# jobname: %27s%*[^\n]", jobname) == 1)
  1104. X            found_job = TRUE;
  1105. X        
  1106. X        if (found_owner && found_job)
  1107. X            break;
  1108. X    }
  1109. X
  1110. X    fclose(fp);
  1111. X    return(0);
  1112. X}
  1113. X
  1114. X/* -------------------------------------------------------------------------- */
  1115. X
  1116. Xchar *
  1117. Xnth(n)
  1118. X
  1119. Xint n;
  1120. X
  1121. X{
  1122. X    switch (n) {
  1123. X
  1124. X        case 1:
  1125. X
  1126. X            return("st");
  1127. X        
  1128. X        case 2:
  1129. X
  1130. X            return("nd");
  1131. X        
  1132. X        case 3:
  1133. X
  1134. X            return("rd");
  1135. X        
  1136. X        default:
  1137. X
  1138. X            return("th");
  1139. X    }
  1140. X}
  1141. END_OF_FILE
  1142. if test 6069 -ne `wc -c <'src/atq.c'`; then
  1143.     echo shar: \"'src/atq.c'\" unpacked with wrong size!
  1144. fi
  1145. # end of 'src/atq.c'
  1146. fi
  1147. if test -f 'src/atrun.c' -a "${1}" != "-c" ; then 
  1148.   echo shar: Will not clobber existing file \"'src/atrun.c'\"
  1149. else
  1150. echo shar: Extracting \"'src/atrun.c'\" \(17097 characters\)
  1151. sed "s/^X//" >'src/atrun.c' <<'END_OF_FILE'
  1152. X/* atrun --- a member of the kat family (Keith's at package) */
  1153. X
  1154. X/* atrun(8) controls execution of jobs in the at(1) queue.  Provision is */
  1155. X/* made to mail output generated during the execution to the submitting */
  1156. X/* user and to control the number of concurrent at processes.  atrun(8) */
  1157. X/* is typically run by cron periodically (e.g., every 15 minutes). */
  1158. X
  1159. X/* Copyright (c) 1990, W. Keith Pyle, Austin, Texas */
  1160. X
  1161. X#include "at.h"
  1162. X
  1163. Xchar *verstring = PATCHLEVEL;
  1164. X
  1165. X#include <syslog.h>
  1166. X#ifdef HAS_TERMIO
  1167. X#include <termio.h>
  1168. X#else
  1169. X#include <sgtty.h> 
  1170. X#endif
  1171. X
  1172. X#define LOCKFILE    "atrun.lock"
  1173. X
  1174. Xchar *progname;
  1175. X
  1176. X#ifdef DEBUG
  1177. X
  1178. X/* These options define logging behavior for a debugging version only */
  1179. X
  1180. Xint debug_level;
  1181. Xextern char *optarg;
  1182. X
  1183. X#ifdef NOSYSLOG
  1184. X#define syslog    fprintf
  1185. X#define LOGDEST    logfile
  1186. X#define LOGFILE    "./atrun.debug"
  1187. XFILE *logfile;
  1188. X#endif
  1189. X
  1190. X#else
  1191. X
  1192. X/* These options apply to logging in the production version */
  1193. X
  1194. X#ifdef HAS_SYSLOGFACILITY
  1195. X#define LOGDEST LOG_ERR | LOG_DAEMON
  1196. X#else
  1197. X#define LOGDEST LOG_ERR
  1198. X#endif
  1199. X
  1200. X#endif    /* DEBUG */
  1201. X
  1202. Xchar *rindex();
  1203. X
  1204. Xint comp_start();
  1205. X
  1206. Xextern char *sys_errlist[];
  1207. X
  1208. X/* -------------------------------------------------------------------------- */
  1209. X
  1210. Xmain(argc, argv)
  1211. X
  1212. Xint argc;
  1213. Xchar *argv[];
  1214. X
  1215. X{
  1216. X    char lockfile[MAXPATHLEN];
  1217. X    char logname[32];
  1218. X    char *ptr;
  1219. X
  1220. X    int flag;
  1221. X    int n_jobs;
  1222. X    int tod;
  1223. X
  1224. X    struct queue *atq;
  1225. X
  1226. X    FILE *fp;
  1227. X
  1228. X    /* Make sure only root runs this */
  1229. X
  1230. X    if (geteuid() != 0) {
  1231. X
  1232. X        fprintf(stderr, "permission denied\n");
  1233. X        exit(1);
  1234. X    }
  1235. X
  1236. X    /* Make sure we are allowed to run: check for lock file */
  1237. X
  1238. X    sprintf(lockfile, "%s/%s", ATDIR, LOCKFILE);
  1239. X
  1240. X    if (access(lockfile, F_OK) == 0) {
  1241. X
  1242. X        fprintf(stderr, "atrun.lock exists: cannot execute\n");
  1243. X        exit(1);
  1244. X    }
  1245. X
  1246. X    /* Convert to a daemon */
  1247. X
  1248. X    daemonize();
  1249. X
  1250. X    /* Set up syslog options */
  1251. X
  1252. X#ifndef NOSYSLOG
  1253. X    if ((ptr = rindex(argv[0], '/')) != NULL)
  1254. X        ptr++;
  1255. X    else
  1256. X        ptr = argv[0];
  1257. X    
  1258. X    strcpy(logname, ptr);
  1259. X    
  1260. X#ifdef HAS_SYSLOGFACILITY
  1261. X    openlog(logname, LOG_CONS, LOG_DAEMON);
  1262. X#else
  1263. X    openlog(logname, 0);
  1264. X#endif
  1265. X#else
  1266. X    if ((logfile = fopen(LOGFILE, "a")) == NULL) {
  1267. X
  1268. X        perror(LOGFILE);
  1269. X        exit(1);
  1270. X    }
  1271. X
  1272. X    setbuf(logfile, NULL);
  1273. X    time(&tod);
  1274. X    fprintf(logfile, "atrun started at %s", ctime(&tod));
  1275. X#endif
  1276. X
  1277. X    /* Make our name stand out for ps(1) */
  1278. X
  1279. X    progname = argv[0];
  1280. X
  1281. X    for (ptr = argv[0] ; *ptr ; ptr++)
  1282. X        *ptr = islower(*ptr) ? toupper(*ptr) : *ptr;
  1283. X
  1284. X#ifdef DEBUG
  1285. X    /* In the DEBUG version, check for a debug level argument */
  1286. X
  1287. X    while ((flag = getopt(argc, argv, "x")) != EOF) {
  1288. X
  1289. X        switch (flag) {
  1290. X
  1291. X            case 'x':
  1292. X
  1293. X                debug_level = TRUE;
  1294. X                break;
  1295. X            
  1296. X            default:
  1297. X
  1298. X                syslog(LOGDEST, "invalid argument: %c\n", flag);
  1299. X                exit(1);
  1300. X        }
  1301. X    }
  1302. X#endif
  1303. X
  1304. X    /* Change to the at job directory */
  1305. X
  1306. X    if (chdir(ATDIR) < 0) {
  1307. X
  1308. X        syslog(LOGDEST, "can't chdir to %s: %s\n", ATDIR, sys_errlist[errno]);
  1309. X        exit(1);
  1310. X    }
  1311. X
  1312. X    /* Make sure we can update the last done file */
  1313. X
  1314. X    if ((fp = fopen("lasttimedone", "w")) == NULL) {
  1315. X
  1316. X        syslog(LOGDEST, "can't open 'lasttimedone': %s\n", sys_errlist[errno]);
  1317. X        exit(1);
  1318. X    }
  1319. X
  1320. X    /* Select the job files to be run */
  1321. X
  1322. X    n_jobs = select_jobs(&atq);
  1323. X
  1324. X    /* Update the last done file */
  1325. X
  1326. X    time(&tod);
  1327. X    fprintf(fp, "%d (%.19s)\n", tod, ctime(&tod));
  1328. X    fclose(fp);
  1329. X
  1330. X    /* Now run the selected jobs */
  1331. X
  1332. X    if (n_jobs != 0)
  1333. X        run_all(n_jobs, atq);
  1334. X
  1335. X    exit(0);
  1336. X}
  1337. X
  1338. X/* -------------------------------------------------------------------------- */
  1339. X
  1340. X/* Compare function for use with sort: compare job start times */
  1341. X
  1342. Xcomp_start(ptr1, ptr2)
  1343. X
  1344. Xstruct queue *ptr1;
  1345. Xstruct queue *ptr2;
  1346. X
  1347. X{
  1348. X    return(ptr1->qu_start - ptr2->qu_start);
  1349. X}
  1350. X
  1351. X/* -------------------------------------------------------------------------- */
  1352. X
  1353. X/* Determine if the user requested mail confirming the completion */
  1354. X/* of the job */
  1355. X
  1356. Xconfirmation(mail_uid, jobfile, jobno)
  1357. X
  1358. Xint mail_uid;
  1359. Xchar *jobfile;
  1360. Xint jobno;
  1361. X
  1362. X{
  1363. X    char buffer[MAXLINE];
  1364. X
  1365. X    int confirm;
  1366. X
  1367. X    FILE *jobfile_fp;
  1368. X
  1369. X    confirm = FALSE;
  1370. X
  1371. X    /* Open the job file and look for the notify line */
  1372. X
  1373. X    if ((jobfile_fp = fopen(jobfile, "r")) != NULL) {
  1374. X
  1375. X        while (fgets(buffer, sizeof(buffer), jobfile_fp)) {
  1376. X
  1377. X            if (strcmp(buffer, "# notify by mail: yes\n") == 0) {
  1378. X
  1379. X                confirm = TRUE;
  1380. X                break;
  1381. X            }
  1382. X        }
  1383. X
  1384. X        fclose(jobfile_fp);
  1385. X    }
  1386. X
  1387. X    /* Did we find a comfirm line? */
  1388. X
  1389. X    if (confirm) {
  1390. X
  1391. X        /* Oh, yes, we did! */
  1392. X
  1393. X        open_mail(mail_uid);
  1394. X        sprintf(buffer, "Your \"at\" job \"%d\" completed.\n", jobno);
  1395. X        write(1, buffer, strlen(buffer));
  1396. X    }
  1397. X
  1398. X    return(confirm);
  1399. X}
  1400. X
  1401. X/* -------------------------------------------------------------------------- */
  1402. X
  1403. X/* Send generated output to user via mail */
  1404. X
  1405. Xcopy_output(jobfile, jobno, outfile)
  1406. X
  1407. Xchar *jobfile;
  1408. Xint jobno;
  1409. Xchar *outfile;
  1410. X
  1411. X{
  1412. X    char buffer [8192];
  1413. X
  1414. X    int bytes;
  1415. X    int outfile_fd;
  1416. X
  1417. X    sprintf(buffer,
  1418. X        "Your \"at\" job \"%d\" generated the following output:\n\n",
  1419. X        jobno);
  1420. X    write(1, buffer, strlen(buffer));
  1421. X
  1422. X    /* Open the generated output file and write it to sendmail */
  1423. X
  1424. X    if ((outfile_fd = open(outfile, O_RDONLY)) >= 0) {
  1425. X
  1426. X        while ((bytes = read(outfile_fd, buffer, sizeof(buffer))) > 0)
  1427. X            write(1, buffer, bytes);
  1428. X    
  1429. X        close(outfile_fd);
  1430. X    }
  1431. X
  1432. X    else
  1433. X        syslog(LOGDEST,
  1434. X            "couldn't open output %s from jobfile %s for reading: %s\n",
  1435. X            outfile, jobfile, sys_errlist[errno]);
  1436. X}
  1437. X
  1438. X/* -------------------------------------------------------------------------- */
  1439. X
  1440. X/* Convert the master process to a daemon */
  1441. X
  1442. Xdaemonize()
  1443. X
  1444. X{
  1445. X    int fd;
  1446. X    int pid;
  1447. X
  1448. X    /* Ignore the terminal stop signals */
  1449. X
  1450. X    (void) signal(SIGTTOU, SIG_IGN);
  1451. X    (void) signal(SIGTTIN, SIG_IGN);
  1452. X    (void) signal(SIGTSTP, SIG_IGN);
  1453. X
  1454. X    /* Fork to a child process, parent terminates */
  1455. X
  1456. X    if ((pid = fork()) < 0) {
  1457. X
  1458. X        syslog(LOGDEST, "could not fork to create daemon\n");
  1459. X        exit(1);
  1460. X    }
  1461. X
  1462. X    else if (pid > 0)
  1463. X        exit(0);
  1464. X    
  1465. X    /* Disassociate ourselves from the old process group */
  1466. X
  1467. X    if (setpgrp(0, getpid()) == -1) {
  1468. X
  1469. X        syslog(LOGDEST, "couldn't set process group in master atrun\n");
  1470. X        exit(1);
  1471. X    }
  1472. X
  1473. X#ifdef HAS_TIOCNOTTY
  1474. X    /* Lose the controlling terminal */
  1475. X
  1476. X    if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
  1477. X
  1478. X        (void) ioctl(fd, TIOCNOTTY, (char *) NULL);
  1479. X        (void) close(fd);
  1480. X    }
  1481. X#endif
  1482. X
  1483. X    /* Make stdin, stdout, and stderr worthless but keep them as placeholders */
  1484. X    /* since we may need to do forks and use them in the child */
  1485. X
  1486. X    close(0);
  1487. X    close(1);
  1488. X
  1489. X    if (open("/dev/null", O_RDONLY) != 0) {
  1490. X
  1491. X        syslog(LOGDEST, "couldn't reopen stdin as /dev/null");
  1492. X        exit(1);
  1493. X    }
  1494. X
  1495. X    if (open("/dev/null", O_WRONLY) != 1) {
  1496. X
  1497. X        syslog(LOGDEST, "couldn't reopen stdout as /dev/null");
  1498. X        exit(1);
  1499. X    }
  1500. X
  1501. X    close(2);
  1502. X    dup(1);
  1503. X}
  1504. X
  1505. X/* -------------------------------------------------------------------------- */
  1506. X
  1507. X/* Create a sendmail process to mail output to the at job owner */
  1508. X
  1509. Xopen_mail(uid)
  1510. X
  1511. Xuid_t uid;
  1512. X
  1513. X{
  1514. X    int mail_pipe[2];
  1515. X    int pid;
  1516. X
  1517. X    struct passwd *pw;
  1518. X
  1519. X    /* Determine the user name for this job */
  1520. X
  1521. X    if ((pw = getpwuid(uid)) == NULL) {
  1522. X
  1523. X        syslog(LOGDEST, "can't find passwd entry for uid %d: %s\n",
  1524. X            uid, sys_errlist[errno]);
  1525. X        return;
  1526. X    }
  1527. X
  1528. X#ifdef DEBUG
  1529. X    if (debug_level)
  1530. X        syslog(LOGDEST, "opening mail for %s in slave atrun (pid=%d)\n",
  1531. X            pw->pw_name, getpid());
  1532. X#endif
  1533. X
  1534. X    if (pipe(mail_pipe) < 0) {
  1535. X
  1536. X        syslog(LOGDEST, "can't create pipe for mail: %s\n", sys_errlist[errno]);
  1537. X        return;
  1538. X    }
  1539. X
  1540. X    if ((pid = fork()) < 0) {
  1541. X
  1542. X        syslog(LOGDEST, "can't fork for mail: %s\n", sys_errlist[errno]);
  1543. X        return;
  1544. X    }
  1545. X
  1546. X    else if (pid > 0) {
  1547. X
  1548. X        char buffer[256];
  1549. X
  1550. X        /* Parent (slave atrun): make stdout go to sendmail and write header */
  1551. X
  1552. X        close(1);
  1553. X        dup(mail_pipe[1]);
  1554. X        close(mail_pipe[0]);
  1555. X        close(mail_pipe[1]);
  1556. X
  1557. X#ifdef USE_SENDMAIL
  1558. X        sprintf(buffer, "To: %s\nSubject: Output from \"at\" job\n\n",
  1559. X            pw->pw_name);
  1560. X        write(1, buffer, strlen(buffer));
  1561. X#endif
  1562. X
  1563. X        return;
  1564. X    }
  1565. X
  1566. X    else {
  1567. X
  1568. X        /* Child: convert pipe to stdin and exec to sendmail */
  1569. X
  1570. X        close(0);
  1571. X        dup(mail_pipe[0]);
  1572. X        close(mail_pipe[0]);
  1573. X        close(mail_pipe[1]);
  1574. X
  1575. X#ifdef USE_SENDMAIL
  1576. X        execl("/usr/lib/sendmail", "sendmail", "-t", (char *)NULL);
  1577. X#endif
  1578. X
  1579. X#ifdef USE_UCBMAIL
  1580. X        execl("/usr/ucb/Mail", "Mail", "-s", "Output from \"at\" job",
  1581. X            pw->pw_name, (char *)NULL);
  1582. X#endif
  1583. X
  1584. X#ifdef USE_BINMAIL
  1585. X        execl("/bin/mail", "mail", pw->pw_name, (char *)NULL);
  1586. X#endif
  1587. X
  1588. X        syslog(LOGDEST, "exec to mailer failed: %s\n", sys_errlist[errno]);
  1589. X        exit(1);
  1590. X    }
  1591. X}
  1592. X
  1593. X/* -------------------------------------------------------------------------- */
  1594. X
  1595. X/* If a slave atrun fails, this will try to move the jobfile back to the */
  1596. X/* normal queue directory */
  1597. X
  1598. Xrecover(jobfile)
  1599. X
  1600. Xchar *jobfile;
  1601. X
  1602. X{
  1603. X    char newname[MAXPATHLEN];
  1604. X
  1605. X#ifdef DEBUG
  1606. X    if (debug_level)
  1607. X        syslog(LOGDEST,
  1608. X            "attempting recovery of jobfile %s in slave atrun (pid=%d)\n",
  1609. X            jobfile, getpid());
  1610. X#endif
  1611. X
  1612. X    sprintf(newname, "%s/%s", ATDIR, jobfile);
  1613. X
  1614. X    if (rename(jobfile, newname) < 0)
  1615. X        syslog(LOGDEST,
  1616. X            "couldn't recover jobfile %s from the past directory: %s\n",
  1617. X            jobfile, sys_errlist[errno]);
  1618. X}
  1619. X
  1620. X/* -------------------------------------------------------------------------- */
  1621. X
  1622. X/* Create slave atrun processes for each job in queue. Manage total */
  1623. X/* number running so as not to exceed configured limit. */
  1624. X
  1625. Xrun_all(n_jobs, atq)
  1626. X
  1627. Xint n_jobs;
  1628. Xstruct queue *atq;
  1629. X
  1630. X{
  1631. X    int i;
  1632. X    int n_slave;
  1633. X    int status;
  1634. X
  1635. X    register struct queue *qptr;
  1636. X
  1637. X    /* cd to the working directory */
  1638. X
  1639. X    if (chdir(PASTDIR) < 0) {
  1640. X
  1641. X        syslog(LOGDEST, "can't chdir to past: %s\n", sys_errlist[errno]);
  1642. X        exit(1);
  1643. X    }
  1644. X
  1645. X    /* Sort the entries in order of starting time */
  1646. X
  1647. X    if (n_jobs > 1)
  1648. X        qsort(atq, n_jobs, sizeof(struct queue), comp_start);
  1649. X
  1650. X    /* Run them */
  1651. X
  1652. X    n_slave = 0;
  1653. X
  1654. X    for (i = 0 , qptr = atq ; i < n_jobs ; i++, qptr++) {
  1655. X
  1656. X#if MAXATPROC
  1657. X        /* Check number of slaves running */
  1658. X
  1659. X        if (n_slave >= MAXATPROC) {
  1660. X
  1661. X            /* At configured limit; wait for one to finish */
  1662. X
  1663. X#ifdef DEBUG
  1664. X            if (debug_level)
  1665. X                syslog(LOGDEST,
  1666. X                    "MAXATPROC hit; waiting for slave to finish...\n");
  1667. X#endif
  1668. X
  1669. X            if (wait(&status) == -1)
  1670. X                syslog(LOGDEST,
  1671. X                    "shouldn't happen: wait reports no slaves running\n");
  1672. X
  1673. X            n_slave--;
  1674. X        }
  1675. X#endif    /* MAXATPROC */
  1676. X
  1677. X        /* Run the next queue entry */
  1678. X
  1679. X        run_one(qptr);
  1680. X        n_slave++;
  1681. X    }
  1682. X
  1683. X    /* Wait for any remaining slaves to finish */
  1684. X
  1685. X    while (wait(&status) != -1)
  1686. X        ;
  1687. X}
  1688. X
  1689. X/* -------------------------------------------------------------------------- */
  1690. X
  1691. X/* Manage creation and execution of one queue entry */
  1692. X
  1693. Xrun_one(qptr)
  1694. X
  1695. Xstruct queue *qptr;
  1696. X
  1697. X{
  1698. X    char *ptr;
  1699. X    char *jobfile;
  1700. X    char outfile[32];
  1701. X
  1702. X    int jobno;
  1703. X    int pid;
  1704. X    int mail_pipe[2];
  1705. X    int outfile_fd;
  1706. X
  1707. X    struct stat statbuf;
  1708. X
  1709. X    jobfile = qptr->qu_name;
  1710. X    jobno = qptr->qu_jobno;
  1711. X
  1712. X    /* Create a slave atrun; return if master process */
  1713. X
  1714. X    if ((pid = fork()) < 0) {
  1715. X
  1716. X        syslog(LOGDEST, "can't create fork slave atrun for jobfile %s: %s\n",
  1717. X            jobfile, sys_errlist[errno]);
  1718. X        recover(jobfile);
  1719. X        return;
  1720. X    }
  1721. X
  1722. X    else if (pid > 0)
  1723. X        return;
  1724. X
  1725. X#ifdef DEBUG
  1726. X    if (debug_level)
  1727. X        syslog(LOGDEST, "slave atrun (pid=%d) created for jobfile %s\n",
  1728. X            getpid(), jobfile);
  1729. X#endif
  1730. X    
  1731. X    /* We're now a slave atrun responsible for only one jobfile. */
  1732. X    /* Fix our name so ps(1) shows it a little differently than the master. */
  1733. X
  1734. X    if ((ptr = rindex(progname, '/')) != NULL)
  1735. X        ptr += 2;
  1736. X    else
  1737. X        ptr = progname + 1;
  1738. X
  1739. X    for ( ; *ptr ; ptr++)
  1740. X        *ptr = isupper(*ptr) ? tolower(*ptr) : *ptr;
  1741. X
  1742. X    /* Create a file to receive the stdout and stderr from the at job. */
  1743. X
  1744. X    strcpy(outfile, "atoutXXXXXX");
  1745. X
  1746. X    if (mktemp(outfile) == NULL) {
  1747. X
  1748. X        syslog(LOGDEST,
  1749. X            "can't create tmp file for output from jobfile %s: %s\n",
  1750. X            jobfile, sys_errlist[errno]);
  1751. X        recover(jobfile);
  1752. X        exit(1);
  1753. X    }
  1754. X
  1755. X    if ((outfile_fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC,
  1756. X            0600)) < 0) {
  1757. X        
  1758. X        syslog(LOGDEST,
  1759. X            "can't open tmp file for output from jobfile %s: %s\n",
  1760. X            jobfile, sys_errlist[errno]);
  1761. X        recover(jobfile);
  1762. X        exit(1);
  1763. X    }
  1764. X
  1765. X#ifdef DEBUG
  1766. X    syslog(LOGDEST,
  1767. X        "tmp file %s created for jobfile %s in slave atrun (pid=%d)\n",
  1768. X        outfile, jobfile, getpid());
  1769. X#endif
  1770. X
  1771. X    /* Find out who the owner of the job should be */
  1772. X
  1773. X    if (stat(jobfile, &statbuf) < 0) {
  1774. X
  1775. X        syslog(LOGDEST, "couldn't stat jobfile %s: %s\n", jobfile,
  1776. X            sys_errlist[errno]);
  1777. X        recover(jobfile);
  1778. X        exit(1);
  1779. X    }
  1780. X
  1781. X    /* Fork to create child for running at job */
  1782. X
  1783. X    if ((pid = fork()) < 0) {
  1784. X
  1785. X        syslog(LOGDEST, "can't fork from slave atrun for jobfile %s: %s\n",
  1786. X            jobfile, sys_errlist[errno]);
  1787. X        recover(jobfile);
  1788. X        exit(1);
  1789. X    }
  1790. X
  1791. X    else if (pid == 0) {
  1792. X
  1793. X        /* Child: close stdout and stderr, dup the write pipe onto both */
  1794. X
  1795. X        close(1);
  1796. X        dup(outfile_fd);
  1797. X        close(2);
  1798. X        dup(outfile_fd);
  1799. X        close(outfile_fd);
  1800. X
  1801. X        /* Set the group and owner for the at job */
  1802. X
  1803. X        if(setgid(statbuf.st_gid) < 0) {
  1804. X
  1805. X            syslog(LOGDEST, "couldn't set gid for jobfile %s: %s\n",
  1806. X                jobfile, sys_errlist[errno]);
  1807. X            recover(jobfile);
  1808. X            exit(1);
  1809. X        }
  1810. X
  1811. X        if(setuid(statbuf.st_uid) < 0) {
  1812. X
  1813. X            syslog(LOGDEST, "couldn't set uid for jobfile %s: %s\n",
  1814. X                jobfile, sys_errlist[errno]);
  1815. X            recover(jobfile);
  1816. X            exit(1);
  1817. X        }
  1818. X
  1819. X        /* Now run the at job via sh passing a NULL environment */
  1820. X
  1821. X        execle("/bin/sh", "sh", jobfile, (char *)NULL, (char *)NULL);
  1822. X
  1823. X        /* If we're here, the exec failed! */
  1824. X
  1825. X        syslog(LOGDEST,
  1826. X            "could exec /bin/sh from slave atrun for jobfile %s: %s\n",
  1827. X            jobfile, sys_errlist[errno]);
  1828. X        recover(jobfile);
  1829. X        exit(1);
  1830. X    }
  1831. X
  1832. X    else {
  1833. X
  1834. X        int mail_open;
  1835. X        int mail_uid;
  1836. X        int status;
  1837. X
  1838. X#ifdef DEBUG
  1839. X        if (debug_level)
  1840. X            syslog(LOGDEST,
  1841. X                "slave atrun (pid=%d) created child (pid=%d) to 'sh %s'\n",
  1842. X                getpid(), pid, jobfile);
  1843. X#endif
  1844. X
  1845. X        /* Parent (slave atrun): close tmp output file */
  1846. X
  1847. X        close(outfile_fd);
  1848. X        mail_open = FALSE;
  1849. X        mail_uid = statbuf.st_uid;
  1850. X
  1851. X        /* Wait for an exit status from the at job */
  1852. X
  1853. X        wait(&status);
  1854. X#ifdef DEBUG
  1855. X        if (debug_level)
  1856. X            syslog(LOGDEST, "jobfile %s (pid=%d) terminated with status=%d\n",
  1857. X                jobfile, pid, status);
  1858. X#endif
  1859. X
  1860. X        /* Determine if the job generated any output: stat the file ... */
  1861. X
  1862. X        if (stat(outfile, &statbuf) < 0) {
  1863. X
  1864. X            syslog(LOGDEST, "couldn't stat output file for jobfile %s: %s\n",
  1865. X                jobfile, sys_errlist[errno]);
  1866. X        }
  1867. X
  1868. X        /* ... and check its size */
  1869. X
  1870. X        else if (statbuf.st_size > 0) {
  1871. X
  1872. X            mail_open = TRUE;
  1873. X            open_mail(mail_uid);
  1874. X            copy_output(jobfile, jobno, outfile);
  1875. X        }
  1876. X
  1877. X#ifdef DEBUG
  1878. X        if (debug_level)
  1879. X            syslog(LOGDEST, "output file for jobfile %s was %d bytes\n",
  1880. X                jobfile, statbuf.st_size);
  1881. X#endif
  1882. X
  1883. X        /* Dispose of the temporary file */
  1884. X
  1885. X        if (unlink(outfile) < 0)
  1886. X            syslog(LOGDEST, "couldn't unlink output %s from jobfile %s: %s\n",
  1887. X                outfile, jobfile, sys_errlist[errno]);
  1888. X
  1889. X        /* If an error status is received, put it in the mail message */
  1890. X
  1891. X        if (status != 0) {
  1892. X
  1893. X            char buffer[128];
  1894. X
  1895. X            if (!mail_open)
  1896. X                open_mail(mail_uid);
  1897. X            else
  1898. X                write(1, "\n", 1);
  1899. X
  1900. X            sprintf(buffer, "Your \"at\" job \"%d\" exited with status=%d\n",
  1901. X                jobno, status);
  1902. X            write(1, buffer, strlen(buffer));
  1903. X        }
  1904. X
  1905. X        /* If there hasn't been any mail generated, see if the user */
  1906. X        /* requested confirmation of the run by mail */
  1907. X
  1908. X        else if (!mail_open)
  1909. X            mail_open = confirmation(mail_uid, jobfile, jobno);
  1910. X
  1911. X        /* Close the mail pipe, if any; check status */
  1912. X
  1913. X        if (mail_open) {
  1914. X
  1915. X            close(1);
  1916. X
  1917. X            if (wait(&status) != -1 && status != 0)
  1918. X                syslog(LOGDEST, "mailer exited with status=%d\n", status);
  1919. X        }
  1920. X        
  1921. X        /* Dispose of the at jobfile */
  1922. X
  1923. X        if (unlink(jobfile) < 0)
  1924. X            syslog(LOGDEST, "can't unlink completed jobfile %s: %s\n",
  1925. X                jobfile, sys_errlist[errno]);
  1926. X
  1927. X        /* Exit the slave atrun */
  1928. X
  1929. X        exit(0);
  1930. X    }
  1931. X}
  1932. X
  1933. X/* -------------------------------------------------------------------------- */
  1934. X
  1935. X/* Select at jobfiles from the queue directory that should be run.  Move */
  1936. X/* them to the working directory and store their file names. */
  1937. X
  1938. Xselect_jobs(atq)
  1939. X
  1940. Xstruct queue **atq;
  1941. X
  1942. X{
  1943. X    char buffer[MAXPATHLEN];
  1944. X
  1945. X    int n_jobs;
  1946. X    int qsize;
  1947. X
  1948. X    struct dirent *entry;
  1949. X    register struct queue *qptr;
  1950. X
  1951. X    time_t tod;
  1952. X
  1953. X    DIR *dp;
  1954. X
  1955. X    n_jobs = 0;
  1956. X    qsize = 0;
  1957. X    time(&tod);
  1958. X
  1959. X    /* Open the queue directory */
  1960. X
  1961. X    if ((dp = opendir(".")) == NULL) {
  1962. X
  1963. X        syslog(LOGDEST, "can't open at directory\n");
  1964. X        exit(1);
  1965. X    }
  1966. X
  1967. X    /* Get the name of each directory entry */
  1968. X
  1969. X    while ((entry = readdir(dp)) != NULL) {
  1970. X
  1971. X        int runtime;
  1972. X        int jobno;
  1973. X
  1974. X        struct stat statbuf;
  1975. X
  1976. X        /* See if the file name matches the jobfile name format */
  1977. X
  1978. X        if (sscanf(entry->d_name, "%d.%d", &runtime, &jobno) != 2) {
  1979. X
  1980. X#ifdef DEBUG
  1981. X            if (debug_level)
  1982. X                syslog(LOGDEST, "file %s rejected - not a jobfile name\n",
  1983. X                    entry->d_name);
  1984. X#endif
  1985. X            continue;
  1986. X        }
  1987. X
  1988. X        /* Should this one be run yet? */
  1989. X        
  1990. X        if (runtime > tod) {
  1991. X
  1992. X#ifdef DEBUG
  1993. X            if (debug_level)
  1994. X                syslog(LOGDEST, "jobfile %s rejected - not time yet\n",
  1995. X                    entry->d_name);
  1996. X#endif
  1997. X            continue;
  1998. X        }
  1999. X
  2000. X        /* Is the queue data structure large enough to store this entry? */
  2001. X
  2002. X        if ((n_jobs + 1) > qsize) {
  2003. X
  2004. X            if ((qsize = qalloc(atq, qsize)) < 0) {
  2005. X
  2006. X                fprintf(stderr, "can't allocate space for queue data\n");
  2007. X                exit(1);
  2008. X            }
  2009. X
  2010. X            qptr = *atq;
  2011. X        }
  2012. X
  2013. X        /* Save the info on this entry */
  2014. X
  2015. X        qptr[n_jobs].qu_start = runtime;
  2016. X        qptr[n_jobs].qu_jobno = jobno;
  2017. X        strcpy(qptr[n_jobs].qu_name, entry->d_name);
  2018. X        
  2019. X        /* Move the entry to the working directory */
  2020. X
  2021. X        sprintf(buffer, "%s/%s", PASTDIR, entry->d_name);
  2022. X
  2023. X        if (rename(entry->d_name, buffer) < 0) {
  2024. X
  2025. X            syslog(LOGDEST, "can't rename job file %s: %s\n",
  2026. X                entry->d_name, sys_errlist[errno]);
  2027. X            continue;
  2028. X        }
  2029. X
  2030. X#ifdef DEBUG
  2031. X        if (debug_level)
  2032. X            syslog(LOGDEST, "jobfile %s selected\n", entry->d_name);
  2033. X#endif
  2034. X
  2035. X        n_jobs++;
  2036. X    }
  2037. X
  2038. X    closedir(dp);
  2039. X
  2040. X#ifdef DEBUG
  2041. X    if (debug_level)
  2042. X        syslog(LOGDEST, "%d jobs selected\n", n_jobs);
  2043. X#endif
  2044. X
  2045. X    return(n_jobs);
  2046. X}
  2047. END_OF_FILE
  2048. if test 17097 -ne `wc -c <'src/atrun.c'`; then
  2049.     echo shar: \"'src/atrun.c'\" unpacked with wrong size!
  2050. fi
  2051. # end of 'src/atrun.c'
  2052. fi
  2053. if test -f 'src/date.y' -a "${1}" != "-c" ; then 
  2054.   echo shar: Will not clobber existing file \"'src/date.y'\"
  2055. else
  2056. echo shar: Extracting \"'src/date.y'\" \(13039 characters\)
  2057. sed "s/^X//" >'src/date.y' <<'END_OF_FILE'
  2058. X%token MONTH DAY RELDAY MERIDIAN NUMBER ORDINAL UNIT MONUNIT
  2059. X%token SECOND STDZONE DSTZONE ABSHOUR STRING
  2060. X
  2061. X%{
  2062. X
  2063. X#include <stdio.h>
  2064. X#include <sys/types.h>
  2065. X#include <ctype.h>
  2066. X#include <sys/timeb.h>
  2067. X#include <sys/time.h>
  2068. X
  2069. X/* Define some useful time constants */
  2070. X
  2071. X#define SEC_PER_MIN        (60)
  2072. X#define SEC_PER_HOUR    (3600)
  2073. X#define MIN_PER_HOUR    (60)
  2074. X#define HOUR_PER_DAY    (24)
  2075. X#define SEC_PER_DAY        (SEC_PER_HOUR * HOUR_PER_DAY)
  2076. X#define DAY_PER_WEEK    (7)
  2077. X
  2078. X#define EPOCH            1970
  2079. X
  2080. X/* Define some symbolic values */
  2081. X
  2082. X#define FALSE        0
  2083. X#define TRUE        1
  2084. X
  2085. X#define AM            0
  2086. X#define PM            1
  2087. X#define MILITARY    2
  2088. X
  2089. X#define    NOT_GIVEN    -1
  2090. X#define STANDARD    0
  2091. X#define DST            1
  2092. X
  2093. X/* Declare external values used by the following routines */
  2094. X
  2095. Xstatic char *lptr;            /* Pointer to input string for parsing */
  2096. Xstatic char *lstring;        /* Input string pointer (allocated space) */
  2097. X
  2098. Xchar yyerrmsg[1024];        /* Description of any detected error */
  2099. X
  2100. Xstatic int day;                /* Day-of-month value */
  2101. Xstatic int hour;            /* Hour value */
  2102. Xstatic int inc_mon;            /* Number of incremental months */
  2103. Xstatic int meridian;        /* AM/PM/Military flag */
  2104. Xstatic int minute;            /* Minute value */
  2105. Xstatic int month;            /* Month value */
  2106. Xstatic int ordinal;            /* Ordinal value (first=1, etc.) */
  2107. Xstatic int second;            /* Second value */
  2108. Xstatic int timezone;        /* Time zone value */
  2109. Xstatic int weekday;            /* Day-of-week value */
  2110. Xstatic int year;            /* Year value */
  2111. Xstatic int zone_type;        /* Indicates if DST is possible */
  2112. X
  2113. Xstatic int month_len[12] =
  2114. X    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};    /* Length of months */
  2115. X
  2116. Xstatic time_t inc_sec;        /* Number of incremental seconds */
  2117. Xstatic time_t rel_sec;        /* Number of relative day seconds */
  2118. X
  2119. X/* The following externals contain field counts used in syntax checking */
  2120. X
  2121. Xstatic int date_fields;        /* Number of date fields */
  2122. Xstatic int day_fields;        /* Number of day of week fields */
  2123. Xstatic int inc_fields;        /* Number of increment/decrement fields */
  2124. Xstatic int rday_fields;        /* Number of relative day fields */
  2125. Xstatic int time_fields;        /* Number of time-of-day fields */
  2126. Xstatic int zone_fields;        /* Number of time zone fields */
  2127. X
  2128. X/* Declare function types */
  2129. X
  2130. Xchar *malloc();
  2131. Xtime_t parse_date();
  2132. Xvoid num_to_time();
  2133. X
  2134. X/* Declare external variables */
  2135. X
  2136. Xextern char *sys_errlist[];
  2137. X
  2138. Xextern int errno;
  2139. X
  2140. X%}
  2141. X
  2142. X%%
  2143. X
  2144. Xfullspec: timestr increment
  2145. X    ;
  2146. X
  2147. Xtimestr: /* empty */
  2148. X    | timestr field
  2149. X    ;
  2150. X
  2151. Xfield:  date
  2152. X        { date_fields++; }
  2153. X    | day
  2154. X        { day_fields++; }
  2155. X    | number
  2156. X    | relday
  2157. X        { rday_fields++; }
  2158. X    | time
  2159. X        { time_fields++; }
  2160. X    | zone
  2161. X        { zone_fields++; }
  2162. X    ;
  2163. X
  2164. Xdate:    NUMBER '/' NUMBER
  2165. X        { month = $1 - 1; day = $3; }
  2166. X    | NUMBER '/' NUMBER '/' NUMBER
  2167. X        { month = $1 - 1; day = $3; year = $5; }
  2168. X    | MONTH NUMBER
  2169. X        { month = $1; day = $2; }
  2170. X    | MONTH NUMBER ',' NUMBER
  2171. X        { month = $1; day = $2; year = $4; }
  2172. X    | NUMBER MONTH NUMBER
  2173. X        { month = $2; day = $1; year = $3; }
  2174. X    | NUMBER MONTH
  2175. X        { month = $2; day = $1; }
  2176. X    ;
  2177. X
  2178. Xday:    DAY
  2179. X        { weekday = $1; ordinal = 1; }
  2180. X    | DAY ','
  2181. X        { weekday = $1; ordinal = 1; }
  2182. X    | ORDINAL DAY
  2183. X        { weekday = $2; ordinal = $1; }
  2184. X    | SECOND DAY
  2185. X        { weekday = $2; ordinal = 2; }
  2186. X    ;
  2187. X
  2188. Xrelday:    RELDAY
  2189. X        { rel_sec += $1; }
  2190. X    | ORDINAL RELDAY
  2191. X        { rel_sec += ($1 * $2); }
  2192. X    | SECOND RELDAY
  2193. X        { rel_sec += (2 * $2); }
  2194. X    ;
  2195. X
  2196. Xtime:    NUMBER MERIDIAN
  2197. X        { hour = $1; minute = 0;  second = 0; meridian = $2; }
  2198. X    | NUMBER ':' NUMBER
  2199. X        { hour = $1; minute = $3; second = 0; meridian = MILITARY; }
  2200. X    | NUMBER ':' NUMBER MERIDIAN
  2201. X        { hour = $1; minute = $3; second = 0; meridian = $4; }
  2202. X    | NUMBER ':' NUMBER ':' NUMBER
  2203. X        { hour = $1; minute = $3; second = $5; meridian = MILITARY; }
  2204. X    | NUMBER ':' NUMBER ':' NUMBER MERIDIAN
  2205. X        { hour = $1; minute = $3; second = $5; meridian = $6; }
  2206. X    | ABSHOUR
  2207. X        { hour = $1; minute = 0; second = 0; meridian = MILITARY; }
  2208. X    ;
  2209. X
  2210. Xzone:    STDZONE
  2211. X        { timezone = $1; zone_type = STANDARD; }
  2212. X    | DSTZONE
  2213. X        { timezone = $1; zone_type = DST; }
  2214. X    ;
  2215. X
  2216. Xincrement:    /* empty */
  2217. X    | increment incfield
  2218. X        { inc_fields++; }
  2219. X    ;
  2220. X
  2221. Xincfield:    sign unit
  2222. X        { inc_sec += ($1 * $2); }
  2223. X    | sign NUMBER unit
  2224. X        { inc_sec += ($1 * $2 * $3); }
  2225. X    | sign MONUNIT
  2226. X        { inc_mon += ($1 * $2); }
  2227. X    | sign NUMBER MONUNIT
  2228. X        { inc_mon += ($1 * $2 * $3); }
  2229. X    ;
  2230. X
  2231. Xsign:    '+'
  2232. X        { $$ = 1; }
  2233. X    | '-'
  2234. X        { $$ = -1; }
  2235. X    ;
  2236. X
  2237. Xunit:    UNIT
  2238. X    | SECOND
  2239. X    ;
  2240. X
  2241. Xnumber:    NUMBER
  2242. X        {
  2243. X            if (time_fields > 0 && date_fields > 0)
  2244. X                year = $1;
  2245. X            else {
  2246. X
  2247. X                time_fields++;
  2248. X                num_to_time($1);
  2249. X                meridian = MILITARY;
  2250. X            }
  2251. X        }
  2252. X    ;
  2253. X
  2254. X%%
  2255. X
  2256. X/* ------------------------------------------------------------------------- */
  2257. X
  2258. X#include "lex.yy.c"
  2259. X
  2260. X/* ------------------------------------------------------------------------- */
  2261. X
  2262. Xtime_t
  2263. Xparse_date(string, base)
  2264. X
  2265. Xchar *string;
  2266. Xstruct timeb *base;
  2267. X
  2268. X{
  2269. X    register int i;
  2270. X    int length;
  2271. X    int rc;
  2272. X
  2273. X    struct timeb *ourtime;
  2274. X    struct timeb current;
  2275. X    struct tm *local;
  2276. X
  2277. X    time_t tod;
  2278. X
  2279. X    /* Initialize the various global variables */
  2280. X
  2281. X    date_fields = 0;
  2282. X    day_fields = 0;
  2283. X    inc_fields = 0;
  2284. X    rday_fields = 0;
  2285. X    time_fields = 0;
  2286. X    zone_fields = 0;
  2287. X
  2288. X    inc_mon = 0;
  2289. X    inc_sec = 0;
  2290. X    rel_sec = 0;
  2291. X    ordinal = 0;
  2292. X    meridian = MILITARY;
  2293. X    yyerrmsg[0] = '\0';
  2294. X    zone_type = NOT_GIVEN;
  2295. X
  2296. X    /* Determine the base time (for "now" and relative references) */
  2297. X
  2298. X    if (base == NULL) {
  2299. X
  2300. X        ourtime = ¤t;
  2301. X
  2302. X        if (ftime(ourtime) < 0) {
  2303. X
  2304. X            sprintf(yyerrmsg, "can't get current time via ftime: %s",
  2305. X                sys_errlist[errno]);
  2306. X            return(-1);
  2307. X        }
  2308. X    }
  2309. X
  2310. X    else
  2311. X        ourtime = base;
  2312. X    
  2313. X    tod = ourtime->time;
  2314. X    
  2315. X    if ((local = localtime(&tod)) == NULL) {
  2316. X
  2317. X        sprintf(yyerrmsg, "couldn't convert time via localtime: %s",
  2318. X            sys_errlist[errno]);
  2319. X        return(-1);
  2320. X    }
  2321. X
  2322. X    /* Initialize values for current time */
  2323. X
  2324. X    month = local->tm_mon;
  2325. X    day = local->tm_mday;
  2326. X    weekday = local->tm_wday;
  2327. X    year = local->tm_year;
  2328. X    hour = local->tm_hour;
  2329. X    minute = local->tm_min;
  2330. X    second = local->tm_sec;
  2331. X    timezone = ourtime->timezone * SEC_PER_MIN;
  2332. X
  2333. X    /* Make a local, lower case copy of the date/time string */
  2334. X
  2335. X    length = strlen(string);
  2336. X
  2337. X    if ((lstring = malloc(length + 1)) == NULL) {
  2338. X
  2339. X        sprintf(yyerrmsg, "couldn't allocate memory for date/time string: %s",
  2340. X            sys_errlist[errno]);
  2341. X        return(-1);
  2342. X    }
  2343. X
  2344. X    for (i = 0 ; i <= length ; i++) {
  2345. X
  2346. X        register char ch;
  2347. X
  2348. X        ch = string[i];
  2349. X
  2350. X        if (isalpha(ch) && isupper(ch))
  2351. X            lstring[i] = tolower(ch);
  2352. X        else
  2353. X            lstring[i] = ch;
  2354. X    }
  2355. X
  2356. X    lptr = lstring;
  2357. X
  2358. X    /* Attempt to parse the input */
  2359. X
  2360. X    if ((rc = yyparse()) != 0)
  2361. X        return(-1);
  2362. X
  2363. X    /* Check for an allowable number of each field */
  2364. X
  2365. X    if ((day_fields + rday_fields) > 1) {
  2366. X
  2367. X        strcpy(yyerrmsg, "more than one day specified");
  2368. X        return(-1);
  2369. X    }
  2370. X
  2371. X    if (date_fields > 1) {
  2372. X
  2373. X        strcpy(yyerrmsg, "more than one date specified");
  2374. X        return(-1);
  2375. X    }
  2376. X
  2377. X    if (time_fields > 1) {
  2378. X
  2379. X        strcpy(yyerrmsg, "more than one time specified");
  2380. X        return(-1);
  2381. X    }
  2382. X
  2383. X    if (zone_fields > 1) {
  2384. X
  2385. X        strcpy(yyerrmsg, "more than one timezone specified");
  2386. X        return(-1);
  2387. X    }
  2388. X
  2389. X    /* Convert the parsed fields into a time from epoch */
  2390. X
  2391. X    if (date_fields > 0 || time_fields > 0 || day_fields > 0) {
  2392. X
  2393. X        if ((tod = comp_tval()) <  0)
  2394. X            return(tod);
  2395. X    }
  2396. X
  2397. X    /* Adjust for any relative day (e.g., tomorrow) specified */
  2398. X
  2399. X    tod += rel_sec;
  2400. X    
  2401. X    /* Rationalize the time value, if no date is given (i.e., make */
  2402. X    /* certain it is not a past time) */
  2403. X
  2404. X    if (date_fields == 0  && rday_fields == 0 && tod < ourtime->time
  2405. X            && ordinal >= 0)
  2406. X        tod += SEC_PER_DAY;
  2407. X
  2408. X    /* Adjust for any increment(s) specified */
  2409. X
  2410. X    if (inc_mon > 0)
  2411. X        tod += month_adjust(tod);
  2412. X    
  2413. X    if (day_fields > 0 && date_fields == 0)
  2414. X        tod += day_adjust(tod);
  2415. X
  2416. X    tod += inc_sec;
  2417. X
  2418. X    /* Free the storage for the local copy of the input string */
  2419. X
  2420. X    (void)free(lstring);
  2421. X
  2422. X    /* Return the computed time */
  2423. X
  2424. X    return(tod);
  2425. X}
  2426. X
  2427. X/* ------------------------------------------------------------------------- */
  2428. X
  2429. Xtime_t
  2430. Xcomp_tval()
  2431. X
  2432. X{
  2433. X    register int i;
  2434. X    int n_days;
  2435. X    int offset;
  2436. X    int rc;
  2437. X
  2438. X    time_t tval;
  2439. X
  2440. X    /* Adjust year, if necessary */
  2441. X
  2442. X    if (year < EPOCH)
  2443. X        year += 1900;
  2444. X
  2445. X    /* Adjust length of February for leap year, if necessary */
  2446. X    /* (True, reality is more complicated, but not needed for this program) */
  2447. X
  2448. X    month_len[1] = 28 + (year % 4 == 0);
  2449. X
  2450. X    /* Check for valid month and day */
  2451. X
  2452. X    if (month < 0 || month > 11) {
  2453. X
  2454. X        strcpy(yyerrmsg, "invalid month");
  2455. X        return(-1);
  2456. X    }
  2457. X
  2458. X    if (day < 1 || day > month_len[month]) {
  2459. X
  2460. X        strcpy(yyerrmsg, "invalid day of month");
  2461. X        return(-1);
  2462. X    }
  2463. X
  2464. X    /* Compute number of days from epoch to this day */
  2465. X
  2466. X    n_days = day - 1;
  2467. X
  2468. X    for (i = 0 ; i < month ; i++)
  2469. X        n_days += month_len[i];
  2470. X
  2471. X    for (i = EPOCH ; i < year ; i++)
  2472. X        n_days += 365 + (i % 4 == 0);
  2473. X
  2474. X    /* Check for valid minutes and seconds */
  2475. X
  2476. X    if (minute < 0 || minute > 59) {
  2477. X
  2478. X        strcpy(yyerrmsg, "invalid minutes value");
  2479. X        return(-1);
  2480. X    }
  2481. X
  2482. X
  2483. X    if (second < 0 || second > 59) {
  2484. X
  2485. X        strcpy(yyerrmsg, "invalid seconds value");
  2486. X        return(-1);
  2487. X    }
  2488. X
  2489. X    /* Compute the number of seconds since beginning of this day */
  2490. X
  2491. X    rc = 0;
  2492. X    offset = 0;
  2493. X
  2494. X    switch (meridian) {
  2495. X
  2496. X        case MILITARY:
  2497. X
  2498. X            if (hour < 0 || hour > 23)
  2499. X                rc = -1;
  2500. X
  2501. X            else
  2502. X                tval = ((hour * MIN_PER_HOUR) + minute) * SEC_PER_MIN + second;
  2503. X
  2504. X            break;
  2505. X
  2506. X        case PM:
  2507. X
  2508. X            offset = 12;
  2509. X            /* fall through to next case */
  2510. X
  2511. X        case AM:
  2512. X
  2513. X            if (hour < 1 || hour > 12)
  2514. X                rc = -1;
  2515. X
  2516. X            else
  2517. X                tval = (((hour + offset) * MIN_PER_HOUR) + minute)
  2518. X                    * SEC_PER_MIN + second;
  2519. X
  2520. X            break;
  2521. X
  2522. X        default:
  2523. X
  2524. X            strcpy(yyerrmsg,
  2525. X                "can't happen: unknown meridian value in comp_tval");
  2526. X            return(-1);
  2527. X    }
  2528. X
  2529. X    if (rc < 0) {
  2530. X
  2531. X        strcpy(yyerrmsg, "invalid hours value");
  2532. X        return(-1);
  2533. X    }
  2534. X
  2535. X    /* Add seconds since beginning of epoch to seconds this day */
  2536. X
  2537. X    tval += n_days * SEC_PER_DAY;
  2538. X
  2539. X    /* Add in adjustment for timezone (to get to GMT) */
  2540. X
  2541. X    tval += timezone;
  2542. X
  2543. X    /* Adjust for daylight saving time, if necessary */
  2544. X
  2545. X    if (zone_type == DST ||
  2546. X            (zone_type == NOT_GIVEN && localtime(&tval)->tm_isdst))
  2547. X        tval -= SEC_PER_HOUR;
  2548. X    
  2549. X    return(tval);
  2550. X}
  2551. X
  2552. X/* ------------------------------------------------------------------------- */
  2553. X
  2554. Xday_adjust(tod)
  2555. X
  2556. Xtime_t tod;
  2557. X
  2558. X{
  2559. X    int day_diff;
  2560. X    int inc;
  2561. X    int ord_value;
  2562. X
  2563. X    struct tm *now;
  2564. X
  2565. X    now = localtime(&tod);
  2566. X
  2567. X    /* Compute the number of days until the specified day */
  2568. X    /* and the corresponding number of seconds */
  2569. X
  2570. X    day_diff = (weekday - now->tm_wday + 7) % 7;
  2571. X    inc = SEC_PER_DAY * day_diff;
  2572. X
  2573. X    /* If a postitive ordinal value was specified (e.g., third), */
  2574. X    /* adjust the value used in the subsequent computations */
  2575. X
  2576. X    if (ordinal > 0) {
  2577. X
  2578. X        /* If the ordinal is "first" or "next" and the day is the same */
  2579. X        /* as today, make sure we move ahead one week */
  2580. X
  2581. X        if (ordinal == 1 && day_diff == 0)
  2582. X            ord_value = 1;
  2583. X        else
  2584. X            ord_value = ordinal - 1;
  2585. X    }
  2586. X
  2587. X    else
  2588. X        ord_value = ordinal;
  2589. X
  2590. X    /* Adjust for the number of weeks until the specified day */
  2591. X
  2592. X    inc += 7 * SEC_PER_DAY * ord_value;
  2593. X
  2594. X    return(inc);
  2595. X}
  2596. X
  2597. X/* ------------------------------------------------------------------------- */
  2598. X
  2599. X#include "table.h"
  2600. X
  2601. Xstatic int hash[256];                /* One value for each ASCII character */
  2602. Xstatic int lookup_init = TRUE;        /* TRUE if init needs to be done */
  2603. Xstatic int table_size;                /* Number of string table entries */
  2604. X
  2605. Xstatic void linit();
  2606. X
  2607. X/* -------------------------------------------------------------------------- */
  2608. X
  2609. Xlookup_string(string)
  2610. X
  2611. Xchar *string;
  2612. X
  2613. X{
  2614. X    register int i;
  2615. X
  2616. X    /* Do we need to initialize the string and hash tables? */
  2617. X
  2618. X    if (lookup_init)
  2619. X        linit();
  2620. X    
  2621. X    /* Scan all table entries with a matching first letter */
  2622. X
  2623. X    for (i = hash[*string] ;
  2624. X            i < table_size && *string_table[i].ta_name == *string ; i++) {
  2625. X
  2626. X        if (strcmp(string_table[i].ta_name, string) == 0) {
  2627. X
  2628. X            yylval = string_table[i].ta_value;
  2629. X            return(string_table[i].ta_type);
  2630. X        }
  2631. X    }
  2632. X
  2633. X    return(-1);
  2634. X}
  2635. X
  2636. X/* -------------------------------------------------------------------------- */
  2637. X
  2638. Xstatic
  2639. Xentry_comp(ptr1, ptr2)
  2640. X
  2641. Xregister struct table_def *ptr1;
  2642. Xregister struct table_def *ptr2;
  2643. X
  2644. X{
  2645. X    return(strcmp(ptr1->ta_name, ptr2->ta_name));
  2646. X}
  2647. X
  2648. X/* -------------------------------------------------------------------------- */
  2649. X
  2650. Xstatic void
  2651. Xlinit()
  2652. X
  2653. X{
  2654. X    register int ch;
  2655. X    register int i;
  2656. X
  2657. X    /* Indicate that an initialization is no longer needed */
  2658. X
  2659. X    lookup_init = FALSE;
  2660. X    table_size = sizeof(string_table) / sizeof(struct table_def);
  2661. X
  2662. X    /* Sort the table by ASCII code */
  2663. X
  2664. X    qsort(string_table, table_size, sizeof(struct table_def), entry_comp);
  2665. X
  2666. X    /* Build a simple hash code table (based on first character of string) */
  2667. X    /* If there are no entries for a hash value, the hash table has the */
  2668. X    /* index of the next existing hash value. */
  2669. X
  2670. X    for (i = 0, ch = 0 ; i < table_size ; i++) {
  2671. X
  2672. X        while (*string_table[i].ta_name >= ch)
  2673. X            hash[ch++] = i;
  2674. X    }
  2675. X
  2676. X    return;
  2677. X}
  2678. X
  2679. X/* ------------------------------------------------------------------------- */
  2680. X
  2681. Xmonth_adjust(tod)
  2682. X
  2683. Xtime_t tod;
  2684. X
  2685. X{
  2686. X    int n_months;
  2687. X
  2688. X    struct tm *now;
  2689. X
  2690. X    now = localtime(&tod);
  2691. X    n_months = now->tm_mon + inc_mon;
  2692. X    year += n_months / 12;
  2693. X    month = n_months % 12;
  2694. X
  2695. X    return(comp_tval() - tod);
  2696. X}
  2697. X
  2698. X/* ------------------------------------------------------------------------- */
  2699. X
  2700. Xvoid
  2701. Xnum_to_time(value)
  2702. X
  2703. Xint value;
  2704. X
  2705. X{
  2706. X    minute = 0;
  2707. X    second = 0;
  2708. X
  2709. X    if (value >= 10000) {
  2710. X        second = value % 100;
  2711. X        value /= 100;
  2712. X    }
  2713. X    if (value >= 100) {
  2714. X        minute = value % 100;
  2715. X        value /= 100;
  2716. X    }
  2717. X
  2718. X    hour = value;
  2719. X    return;
  2720. X}
  2721. X
  2722. X/* ------------------------------------------------------------------------- */
  2723. X
  2724. Xyyerror(string)
  2725. X
  2726. Xchar *string;
  2727. X
  2728. X{
  2729. X    sprintf(yyerrmsg, "%s: at or near token \"%s\"", string, yytext);
  2730. X}
  2731. END_OF_FILE
  2732. if test 13039 -ne `wc -c <'src/date.y'`; then
  2733.     echo shar: \"'src/date.y'\" unpacked with wrong size!
  2734. fi
  2735. # end of 'src/date.y'
  2736. fi
  2737. echo shar: End of archive 2 \(of 2\).
  2738. cp /dev/null ark2isdone
  2739. MISSING=""
  2740. for I in 1 2 ; do
  2741.     if test ! -f ark${I}isdone ; then
  2742.     MISSING="${MISSING} ${I}"
  2743.     fi
  2744. done
  2745. if test "${MISSING}" = "" ; then
  2746.     echo You have unpacked both archives.
  2747.     rm -f ark[1-9]isdone
  2748. else
  2749.     echo You still need to unpack the following archives:
  2750.     echo "        " ${MISSING}
  2751. fi
  2752. ##  End of shell archive.
  2753. exit 0
  2754. -- 
  2755. -----------------------------------------------------------------------------
  2756. Keith Pyle                                UUCP: ...!cs.utexas.edu!execu!keith
  2757. Execucom Systems Corp., Austin, Texas     Internet: keith@execu.com
  2758. "It's 10 o'clock.  Do you know where      Disclaimer: Everything I say is
  2759.    your child processes are?"               true unless I use the word 'the'.
  2760. -----------------------------------------------------------------------------
  2761.