rsalz@uunet.UU.NET (Rich Salz) (09/29/87)
Submitted-by: Kenneth Ingham <ingham@charon.unm.edu> Posting-number: Volume 11, Issue 83 Archive-name: watcher/part02 #! /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: Docs/Paper Docs/watcher.1 control.y defs.h PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f Docs/Paper -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"Docs/Paper\" else echo shar: Extracting \"Docs/Paper\" \(13821 characters\) sed "s/^X//" >Docs/Paper <<'END_OF_Docs/Paper' X.TI Keeping watch over the flocks X.TI by night (and day) X.AU 7 XKenneth Ingham XUniversity of New Mexico Computing Center XDistributed Systems Group X2701 Campus NE XAlbuquerque, NM 87131 X(505) 277-8044 Xingham@charon.unm.edu or ucbvax!unmvax!charon!ingham X.AB XOver the last several years, the number of machines maintained by the XUniversity of New Mexico Computing Center has increased rapidly, yet Xthe number of system managers monitoring these systems has remained Xstatic. Consequently, the system managers were faced with the task of Xwatching more and more machines; since only one system manager is on Xcall at any time (known affectionately as "DOC"), this soon proved to Xbe an unacceptable situation. Shell scripts running every six hours Xgave some assistance; this was offset by the fact that the scripts Xgenerated a great deal of output indicating normal system operation, Xwhich the system manager still had to scan carefully for signs of Xtrouble. This paper describes \fIwatcher\fR, a flexible system monitor Xwhich watches the system more closely than the human system manager Xwhile generating less output for him to examine. X.sp XRunning more often than the above mentioned set of shell scripts, X\fIwatcher\fR is able to keep closer tabs on the system; since it Xdelivers only a list of potential problems, however, this extra Xmonitoring produces \fIno\fR corresponding increase in the demand on XDOC. No problems slip by unnoticed in the more concise output, Xleading to an improvement in overall system availability as well as the Xmore effective utilization of the system manager's time. X.BD X.SE 0. Acknowledgments (I couldn't have done it without you) XI would like to thank Leslie Gorsline for her assistance in the writing Xof this paper. Without her, this paper might not have been. Also Xthanks to the UNMCC distributed systems group for their comments that helped Ximprove \fIwatcher\fR. X.SE 1. Background (the problem) XThe computing facilities offered by the University of New Mexico XComputing Center (UNMCC) include three microvaxen, five large vaxen X(780 or bigger), and a Sequent B8000. In addition to these Unix/VMS Xmachines, the UNMCC Distributed Systems Group (DSG) monitors a number Xof the various microvaxen and sun workstations scattered across Xcampus. This duty falls to the DSG Programmer designated as "DOC", or X"DSG On Call", who receives his beeper based on a monthly rotation Xschedule. X.sp XIn the past, shell scripts running every six hours reported various Xsystem statistics to DOC, who then scanned the output for signs of Xpossible trouble. The output of these shell scripts became Xoverwhelming as the number of machines and potential problems grew; Xcorresponding to this increase in output was an increase in the amount Xof time that DOC had to spend reading this output. In addition, most Xof this output merely indicated normal system operation; potential Xproblems were buried amongst non-problems. Because of this, DOC could Xoften waste a tremendous amount of time wading through system status Xreports, time which can be better spent actually fixing system Xproblems. X.sp XUnix is equipped with many powerful tools for program development, but Xnone which simply watch the system for signs of trouble. Programs like X\fIps\fR and \fIdf\fR provide information regarding the current state Xof the machine, yet it still remains DOC's responsibility to interpret Xthis information and assess the health of the system at any given Xtime. This deficiency can be rectified by providing the Xsystem with the capacity to determine its own state of health, advising XDOC when it notices a problem which requires DOC's intervention. X.SE 2. Design Goals (devising the solution) XIn designing \fIwatcher\fR, the author closely examined just what DOC Xdoes in monitoring the system; just how \fIdoes\fR DOC spot potential Xtrouble in the DOC reports? These reports consist of output from \fIdf X-i\fR, \fIruptime\fR, \fIps -aux | sort\fR, and the tail of X\fIcronlog\fR, which usually only changes in the middle of the night. XIt was determined that DOC's task consisted primarily of scanning Xvarious numbers in this output, deciding whether or not they had Xexceeded an allowable maximum or minimum, or if the values had changed Xtoo much from the last time the command was run, assuming the last Xvalue is even remembered. Getting a computer to do this is more Xcomplicated than might seem at first glance, due to inconsistencies in Xthe location of pertinent information between runs of these commands. XFor instance, the process occupying the fifth line of \fIps -ax\fR Xmight next time appear on the eighth line; similarly, \fIuptime\fR does Xnot consistently put germane information in the same place on the line. X.sp XWhile flexibility is certainly a primary design consideration, it is Xnot the whole story. In order to improve DOC's effectiveness, the Xprogram should run frequently, roughly every two or three hours, Xcatching problems early (hopefully before they have affected Xthe users). Thus, the program should also be as silent as possible Xexcept when it detects a potential problem; any advantage DOC gains in Xusing \fIwatcher\fR would be eliminated if the program delivered an Xexceedingly verbose status report every two hours. \fIwatcher\fR's Xproblem reports should be exact and concise, leading DOC immediately to Xthe trouble. X.sp XThe problem of reducing the amount of output DOC must process can be Xapproached in different ways, including the redesign of the current Xshell scripts. A simple \fIawk\fR script can watch the output from X\fIdf\fR [1]. However, each command would require a custom tailored X\fIawk\fR script to look at it. This task grows more complicated as the Xnumber of programs running increases. XWhile a program could be written to Xgenerate these \fIawk\fR scripts, this process is needlessly complex; Xfor only a bit more work, an efficient C program such as X\fIwatcher\fR can be developed. X.SE 3. Design (actual implementation of the solution) XRun at intervals specified in \fIcrontab\fR, \fIwatcher\fR parses a Xcontrol file (./\fIwatcherfile\fR by default) Xwith a \fIyacc\fR generated parser, building a data structure Xcontaining all of the information from the file. The file contains the Xlist of commands \fIwatcher\fR Xshould run (the pipeline), output specifications Xfor each command (the output format), and the guidelines used in Xdetermining if something is amiss and should be reported to DOC (the Xchange format). A sample \fIwatcher\fR control file would look Xsomething like this (comment lines begin with a '#'): X.EX X# Here is the pipeline and its alias: X(df -i | /usr/ucb/tail +2) { df } X# the output format; this is a column output format: X $1-9 device%k $41-42 spaceused%d $64-65 inodesused%d: X# and the change format: X spaceused 15%; X spaceused 0 89; X inodesused 15%; X inodesused 0 49. X X# another command example: X(/usr/ucb/ruptime | fgrep -f UnmHosts) { ruptime } X# this is a relative output format X 2 status%s 1 machine%k 7 loadav%d: X# and another change format: X loadav 0 10; X status "up". X.NX XThe first entry causes \fIwatcher\fR to run the \fIdf\fR pipeline Xlisted in parentheses. When reporting problems, \fIwatcher\fR refers Xto this command by the alias provided in the braces; if no alias Xappears, \fIwatcher\fR uses the entire pipeline. X.sp XThe output format Xinstructs \fIwatcher\fR how to parse the output; Xcolumn format, indicated in the output format by \fBnum-num\fR, Xinstructs \fIwatcher\fR that the output should be parsed Xby columns, while relative format, denoted by a single integer, shows Xthat the output should be broken up by whitespaces. XThrough the convention \fBname%type\fR, the output format also names each Xfield, indicating whether the field is numeric, string, or Xkeyword, specified by \fBd\fR, \fBs\fR, or \fBk\fR respectively. XKeyword fields are Xused to match up corresponding output lines between runs. Thus X.EX X 41-42 spaceused%d X.NX Xindicates that this field, named \fBspaceused\fR, contains numeric Xinformation in columns 41-42, while X.EX X 2 status%s X.NX Xinforms \fIwatcher\fR that the second word (group of non-whitespace Xcharacters) on the line is a string field named \fBstatus\fR. XFor the \fIdf\fR example given above, X.EX XFilesystem kbytes used avail capacity iused ifree %iused Mounted on X/dev/hp1f 52431 39763 7424 84% 6937 9447 42% /develop X.NX X\fBdevice\fR would be \fI/dev/hp1f\fR, \fBspaceused\fR would be 84, Xand \fBinodesused\fR would be 42. Similarly, the output from the X\fIruptime\fR example, which looks like this X.EX Xcharon up 26+07:53, 17 users, load 3.12, 2.90, 2.66 X.NX Xwould be broken at the following places: X.EX Xcharon | up | 26+07:53, | 17 | users, | load | 3.12, | 2.90, | 2.66, X.NX Xassigning "up" to \fBstatus\fR, and 3.12 to \fBloadav\fR. X.sp XThe name field also appears in the change format, designating allowable Xvalues for this field to have. These values can be specified as Xsingle character strings in the case of string fields; in the case of Xnumeric fields, the values take the form of either Xpercentage or absolute changes, or a minimum and maximum which delineate Xan acceptable range. XThus X.EX X inodesused 15%; X inodesused 0 49. X.NX Xsignifies that DOC should be notified if the field named \fBinodesused\fR Xincreases by more than 15% from the last run, or if it is outside the Xrange 0 to 49; similarly X.EX X status "up"; X.NX Xinforms \fIwatcher\fR to notify DOC if the \fBstatus\fR field contains Xanything other than the word "up". X.sp XAs \fIwatcher\fR parses the output of a pipeline, it stores the Xpertinent parts of the output in a history file (by Xdefault, ./\fIwatcher.history\fR). XThe next time \fIwatcher\fR runs, it reads this file to provide Xcomparison values for the command. If a command is new (i.e. it has no Xpreviously-stored output in the history file), \fIwatcher\fR checks the Xfields which require no previous data, such as min-max fields, while Xstill storing \fIall\fR of the relevant information to the history file. XThus, the next time the new command is run, it will be an \fIold\fR command, Xand meaningful between-run comparisons can be made. X.sp XWhen \fIwatcher\fR Xdetects no problems with the system, DOC receives an empty mail message Xwith the subject "\fIhostname\fR had no problems at \fIdate\fR"; Xthis is to insure that \fImail\fR is running correctly. XWhen it notices a problem which should be brought to DOC's attention, Xit mails the system problem report in a concise Xformat, explaining what is wrong and why. XThus, rather than the megabytes of shell script output that DOC Xused to receive and have to read, Xhe merely sees this when he reads his mail: X.EX XMail version 5.2 6/21/85. Type ? for help. X"/usr/spool/mail/ingham": 5 messages 5 new X N 1 root@charon.unm Sat Apr 11 16:00 8/212 "charon had no problems at Sat" X N 2 root@ariel.unm Sat Apr 11 16:00 8/208 "ariel had no problems at Sat " X N 3 root@geinah.unm Sat Apr 11 16:00 11/417 "System problem report for gei" X N 4 root@izar.unm Sat Apr 11 16:00 8/204 "izar had no problems at Sat A" X N 5 root@deimos.unm Sat Apr 11 16:00 8/212 "deimos had no problems at Sat" X.NX XThe letters indicating no problems can be immediately deleted, and DOC Xcan turn his attention to the letter indicating a Xsystem problems. A sample problem report Xwould look something like this: X.EX Xdf has a max/min value out of range: X/dev/hp0h 140488 111195 15244 91% 10145 28767 26% /usr Xwhere spaceused = 91.00; valid range 0.00 to 89.00. XAlso it had inodesused change by more than 10%. XPrevious value 20.00; current value 26.00. X.NX XNote that if a line has more than one indication of a problem, all Xanomalies are included in the report. XThis provides DOC with as much information as possible, allowing him Xto determine the problem quickly and devise Xa rapid fix (hopefully before users know something is amiss). X.sp X.SE 4. Results (how its helped us) X\fIwatcher\fR's primary advantage lies in the reduction of DOC's work Xload. It has taken over the more menial aspects of monitoring a system, Xtasks like reading and comparing numbers, Xgiving DOC more time to concentrate on bugs of a nature which X\fIwatcher\fR isn't set up to monitor, such as problems in the Xaccounting system. XDOC is apprised of potential problems quickly, and in Xsome cases can repair them in less time than simply Xreading the shell script output Xwould have taken. X.sp XThe ability to monitor changes between runs has also helped bring to our Xattention some Xproblems which were missed in the DOC reports. For example, Xdisk space on \fI/u2\fR on one of our machines jumped by more than 15%. Since Xthis jump did not force the total space used above 90%, at which point XDOC would have investigated the filesystem, it is unlikely Xthat DOC would have even noticed this sudden change. The facility to Xwatch for relative changes between runs enables DOC to catch problems in Xtheir infancy, and fix problems such as filesystems filling up too Xrapidly before they inconvenience the users. X.sp XSince the system manager specifies not only the commands \fIwatcher\fR will Xexecute and the time lapse between successive runs, but also the Xparameters which indicate system anomalies, X\fIwatcher\fR can easily be seen as a very flexible, general system Xmonitor. Its use at UNM has provided an increase in the Xproductivity of the system manager, which has led in turn to the Xincrease in the reliability and availability of the systems at UNMCC. X.SE 5. Availability (how to get one) X\fIwatcher\fR will be sent to the moderator of mod.sources after the Xconference is over. X.SE 6. References (you might also find this interesting) X.in +0.5i X.ti -0.5i X[1] Monitoring Free Disk Space, Rik Farrow, Wizard's Grabbag, \fIUnix XWorld\fR, Vol. IV, no. 3, pp. 86-87. X.in -0.5i END_OF_Docs/Paper if test 13821 -ne `wc -c <Docs/Paper`; then echo shar: \"Docs/Paper\" unpacked with wrong size! fi # end of overwriting check fi if test -f Docs/watcher.1 -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"Docs/watcher.1\" else echo shar: Extracting \"Docs/watcher.1\" \(6175 characters\) sed "s/^X//" >Docs/watcher.1 <<'END_OF_Docs/watcher.1' X.de EX X.nf X.sp X.in +0.5i X.. X.de NX X.sp X.in -0.5i X.ad X.fi X.. X.TH WATCHER 1 "April 10, 1987" X.UC 6 X.ad X.SH NAME Xwatcher \- system monitoring program X.SH SYNOPSIS X.B watcher X[ \-p] [ \-v ] X[ \-h histfile ] [ \-f controlfile ] X.SH DESCRIPTION X.I Watcher Xis a program to watch the system, reporting only when it finds something Xamiss. X.I Watcher Xreads commands from X.I controlfile Xto determine what to watch, the output format of the commands it is to Xrun, and the acceptable limits for the output of those commands. XIf no X.B \-f Xoption is present, the program looks first for `watcherfile', Xthen `Watcherfile' to use as the input. X.PP XThe X.B \-h histfile Xflag tells watcher what file to use as a history file for comparisons Xbetween runs. The default is `watcher.history'. X.PP XThe X.B \-p Xoption has X.I watcher Xpretty print the control file. This is useful to make sure that watcher Xis parsing the file the way expected, and to provide a prettier version Xof the control file to use (i.e. it is of limited use). X.PP XThe X.B \-v Xoption tells watcher to be verbose as it is running. It will print out Xvarious information about where it is looking for the files that it Xuses, the commands that it is executing, and the output from these Xcommands. This option is mainly of use when debugging control files or Xdebugging watcher itself. X.PP X.I Watcherfile Xcontains a sequence of entries that specify the commands to be executed, Xthe output format of those commands, and what changes should be Xreported. The format of the control file is one or more of the Xfollowing: X.EX X( <pipeline> ) { <alias> } X <output format> : X <change format>. X.NX XA <pipeline> is a series of commands joined together with pipes ('|'). XThis command is executed and the output parsed according to the output Xformat specified. It is then checked against the change format for Xpotential problems. An <alias> is optional; it is used when identifying Xthe command in the report of problems encountered. If there is no Xalias, the entire pipeline is used. The reason for using an alias is Xto keep the report clean; the pipelines tend to be long and messy. X.PP XAn <output format> is either a column format or a relative format. A Xcolumn format is one or more of the following: X.EX X <start> - <end> <name> % <type> X.NX XWhere <start> is the first column containing the information to be compared and X<end> is the last one. <name> is the name of the field. This name is Xmatched with the names in the change format to identify where in the Xoutput the appropriate information is. <type> is either "d", "s", or X"k" specifying numeric or string data, or a keyword which is used in Xmatching output from the various programs between runs. X.PP XRelative formats are one or more of the following: X.EX X<field> <name> % <type> X.NX XWhere <field> is the field on the line (a field is defined as a sequence Xof non-whitespace surrounded by whitespace). <name> and <type> are the Xsame as for above. X.PP XA change format consists of various names and what changes are Xallowable. Change format entries are separated by semicolons (';'). The Xlist of change formats is terminated by a period ('.'). A semicolon Xdoes not follow the last change format. X.I Watcher Xknows about 4 types of changes. It can compare the output (numeric) Xto the previous value and calculate the percentage change. If the Xchange is greater that a set amount, a message is generated. The syntax Xof this format is: X.EX X <name> <value> % X.NX Xwhere <name> is a name matching a name in the output format and <value Xis the maximum percentage change which is allowed before a report is Xissued. X.PP XVery similar to the percentage change is the absolute change. The only Xdifference is that a percentage is not calculated. The difference is Xcalculated and compared to the value given. Values greater that what is Xprovided are reported. The syntax is: X.EX X <name> <value> X.NX X.PP XA maximum and minimum may be specified for numeric data also. This is Xuseful for only numeric data. The format for this is: X.EX X <name> <max> <min> X.NX X.PP X.I Watcher Xcan also watch for string values changing from a given value to any Xother value. This syntax is: X.EX X <name> "<value>" X.NX X.PP XA sample control file is provided below: X.EX X(df -i | /usr/ucb/tail +2) { df } X 1-9 filesystem%k 41-42 spaceused%d 64-65 inodesused%d 1-9 device%k: X spaceused 15%; X spaceused 0 89; X inodesused 15%; X inodesused 0 49. X(/usr/ucb/ruptime | fgrep -f UnmHosts) { ruptime } X 2 status%s 1 machine%k 7 loadav%d: X loadav 0 10; X status "up". X(ps -aux | fgrep -v -f Daemons | /usr/ucb/tail +2) { 'ps with no daemons' } X 9-14 pid%k 16-19 percentcpu%d 42-45 cputime%d: X cputime 0 10. X.NX XNote that there is no order for the output format specifiers; the second Xfield may be specified before the first. X.PP XAll names are of arbitrary length, start with [a-zA-Z] and contain no Xwhite space unless enclosed in tics ("'"). X.PP XThe pipeline is executed by X.I popen(3), Xwhich uses X.I sh(1) Xto expand the command; therefore shell metacharacters may be used. X.PP XThe control file may have comments in it. Comments are delimited by a # Xon the left and a newline on the right. X.SH FILES X.nf XWatcherfile or watcherfile default control file. Xwatcher.history default file containing results of previous run. X.SH AUTHOR X.nf XKenneth Ingham XUniversity of New Mexico Computing Center X2701 Campus NE XAlbuquerque, NM, 87131 Xingham@charon.unm.edu X.SH "SEE ALSO" Xpopen(3), sh(1), X.I Keeping Watch over the X.I Flocks by Night (and day) Xby Kenneth Ingham, Summer 1987 Usenix proceedings. X.SH DIAGNOSTICS XFiles which can't be opened cause a message about which files couldn't Xbe found and the program exits. X.sp XThere are various syntax errors when parsing the controlfile. These Xalso cause an exit. X.sp X.I Watcher Xcomplains when output does not parse according to the format Xprovided. It will continue to look at the rest of the output. X.SH BUGS XSyntax errors in control file are not easy to find based on the error Xmessages. X.sp XDoesn't warn when a string variable has been selected for a numeric Xcomparison. X.sp XOnly notices changes which get bigger; things which suddenly Xdrop will not be noticed. END_OF_Docs/watcher.1 if test 6175 -ne `wc -c <Docs/watcher.1`; then echo shar: \"Docs/watcher.1\" unpacked with wrong size! fi # end of overwriting check fi if test -f control.y -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"control.y\" else echo shar: Extracting \"control.y\" \(6895 characters\) sed "s/^X//" >control.y <<'END_OF_control.y' X/* X the grammer describing the control file for watcher. X X Kenneth Ingham X X Copyright (C) 1987 The University of New Mexico X*/ X X%token PIPELINE NUMBER STRING X%start command X X%{ X#include "defs.h" X#define SMAXMIN struct max_min_st X#define SOUTFMT struct out_fmt_st X#define SCOLOUT struct col_out_st X#define SRELOUT struct rel_out_st X#define SCMD struct cmd_st X#define SCH struct change_fmt_st Xextern char *strval, ostrval[], pipeline[]; Xextern int intval, ointval; Xextern struct cmd_st *clist; Xint out_type; Xchar alias[MAX_STR]; Xunion out_fmt_u key; X%} X X%% X/* X THE PARSER DESCRIPTION (read aloud to the beginning of X _Also_sprach_Zarathustra_) X X As things are discovered, they are put into the linked list structure X detailed in defs.h. Some error checking is done, but the messages X leave a bit to be desired. Yacc and I didn't get along too well in X figuring out how to handle errors. X X Assuming that pointers will fit into YYSTYPE (int on 4.3 vax). X Should use yacc's union facility. X*/ X Xcommand : one_command X { X if (clist == NULL) X clist = (SCMD *) $1; X } X | command one_command X { X SCMD *p; X X if (clist != NULL) { X for (p=clist; p->next!=NULL; p=p->next) X ; X p->next = (SCMD *)$2; X } X else X clist = (SCMD *)$1; X } X | error '.' X { X fprintf(stderr, "Command error near "); X fprintf(stderr, "'%s'\n", pipeline); X fprintf(stderr,"Last string read was '%s'\n", strval); X } X ; X Xone_command : PIPELINE alias out_fmt ':' change_fmt '.' X { X SCMD *p; X X p = (SCMD *) malloc(sizeof(SCMD)); X if (p == NULL) { X fprintf(stderr,"malloc error\n"); X exit(1); X } X p->pipeline = malloc((unsigned)strlen(pipeline)+1); X if (p->pipeline == NULL) { X fprintf(stderr,"malloc error\n"); X exit(1); X } X (void) strcpy(p->pipeline, pipeline); X X if (alias[0]) { X p->alias = malloc((unsigned)strlen(alias)+1); X if (p->alias == NULL) { X fprintf(stderr,"malloc error\n"); X exit(1); X } X (void) strcpy(p->alias, alias); X } X else X p->alias = NULL; X X p->change_fmt = (SCH *)$5; X p->next = NULL; X p->out_type = out_type; X if (out_type == RELATIVE) { X p->out_fmt.rel_fmt = (SRELOUT *)$3; X p->key.rel_fmt = key.rel_fmt; X } X else { /* it better be column */ X p->out_fmt.col_fmt = (SCOLOUT *)$3; X p->key.col_fmt = key.col_fmt; X } X X key.rel_fmt = NULL; X $$ = (int)p; X } X ; X Xout_fmt : rel_out_fmt X { X out_type = RELATIVE; X $$ = $1; X } X | col_out_fmt X { X out_type = COLUMN; X $$ = $1; X } X | error '.' X { X fprintf(stderr,"Output format error on "); X fprintf(stderr,"'%s'\n", pipeline); X fprintf(stderr,"Last string read was '%s'\n", X strval); X } X ; X Xchange_fmt : one_change_fmt X | change_fmt ';' one_change_fmt X { X SCH *p; X X for (p=(SCH *)$1; p->next != NULL; p = p->next) X ; X X p->next = (SCH *)$3; X X $$ = (int)$1; X } X ; X Xone_change_fmt : pct_change_fmt X { $$ = $1; } /* Is this default? */ X | abs_change_fmt X { $$ = $1; } X | max_min_fmt X { $$ = $1; } X | str_change_fmt X { $$ = $1; } X | error '.' X { X fprintf(stderr,"Unknown change format type "); X fprintf(stderr,"on '%s'\n", pipeline); X fprintf(stderr,"Last string read '%s'\n", strval); X } X ; X Xrel_out_fmt : one_rel_fmt X | rel_out_fmt one_rel_fmt X { X SRELOUT *p; X X for (p=(SRELOUT *)$1;p->next != NULL; p=p->next) X ; X p->next = (SRELOUT *)$2; X $$ = (int)$1; X } X ; X Xcol_out_fmt : one_col_fmt X | col_out_fmt one_col_fmt X { X SCOLOUT *p; X X for (p=(SCOLOUT *)$1;p->next != NULL; p=p->next) X ; X p->next = (SCOLOUT *)$2; X $$ = (int)$1; X } X /* X | error '.' X { X fprintf(stderr,"Column output format error\n"); X fprintf(stderr,"Last string read '%s'\n", strval); X } X */ X ; X Xone_rel_fmt : NUMBER STRING '%' STRING X { X extern int parse_error; X SRELOUT *p; X X p = (SRELOUT *) malloc(sizeof(SRELOUT)); X p->name = malloc((unsigned)strlen(ostrval)+1); X (void) strcpy(p->name, ostrval); X p->field = intval; X p->next = NULL; X X if (strval[1] != '\0') { X fprintf(stderr,"%s: Invalid type specifier '%s'.\n", X NAME, strval); X parse_error = True; X $$ = (int)p; X } X X switch (*strval) { X case 'd': X case 'f': X p->type = NUM; X break; X case 's': X p->type = STRING; X break; X case 'k': X p->type = KEY; X key.rel_fmt = p; X break; X default: X fprintf(stderr,"%s: Invalid type specifier '%s'.\n", X NAME, strval); X parse_error = True; X } X X $$ = (int) p; X } X ; X Xone_col_fmt : NUMBER '-' NUMBER STRING '%' STRING X { X extern int parse_error; X SCOLOUT *p; X X p = (SCOLOUT *) malloc(sizeof(SCOLOUT)); X p->name = malloc((unsigned)strlen(ostrval)+1); X (void) strcpy(p->name, ostrval); X p->start = ointval; X p->end = intval; X p->next = NULL; X X if (ointval >= intval) { X fprintf(stderr,"%s: start %d larger than end %d!\n", NAME, ointval, intval); X parse_error = True; X $$ = (int)p; X } X X if (strval[1] != '\0') { X fprintf(stderr,"%s: Invalid %s '%s'.\n", X NAME, "type specifier", strval); X parse_error = True; X $$ = (int)p; X } X X switch (*strval) { X case 'd': X case 'f': X p->type = NUM; X break; X case 's': X p->type = STRING; X break; X case 'k': X p->type = KEY; X key.col_fmt = p; X break; X default: X fprintf(stderr,"%s: %s '%s'.\n", X NAME, X "Bad type specifier", X strval); X parse_error = True; X } X $$ = (int) p; X } X ; X Xpct_change_fmt : STRING NUMBER '%' X { X SCH *p; X X p = (SCH *) malloc(sizeof(SCH)); X p->name = malloc((unsigned)strlen(strval)+1); X (void) strcpy(p->name, strval); X p->fmt.percent = (float)intval / 100; X p->type = PERCENT; X X $$ = (int) p; X } X ; X Xabs_change_fmt : STRING NUMBER X { X SCH *p; X X p = (SCH *) malloc(sizeof(SCH)); X p->name = malloc((unsigned) strlen(strval)+1); X (void) strcpy(p->name, strval); X p->fmt.abs_amount = intval; X p->type = ABSOLUTE; X X $$ = (int) p; X } X ; X Xmax_min_fmt : STRING NUMBER NUMBER X { X SCH *p; X X p = (SCH *) malloc(sizeof(SCH)); X p->name = malloc((unsigned)strlen(strval)+1); X (void) strcpy(p->name, strval); X p->fmt.max_min.max = intval; X p->fmt.max_min.min = ointval; X p->type = MAX_MIN; X X $$ = (int) p; X } X ; X Xstr_change_fmt : STRING '"' STRING '"' X { X SCH *p; X X p = (SCH *) malloc(sizeof(SCH)); X p->name = malloc((unsigned)strlen(ostrval)+1); X (void) strcpy(p->name, ostrval); X p->fmt.str_value = malloc((unsigned)strlen(strval)+1); X (void) strcpy(p->fmt.str_value, strval); X p->type = STRING; X X $$ = (int) p; X } X ; X Xalias : '{' STRING '}' X { (void) strcpy(alias, strval); } X | empty X { alias[0] = '\0'; } X ; X Xempty : ; END_OF_control.y if test 6895 -ne `wc -c <control.y`; then echo shar: \"control.y\" unpacked with wrong size! fi # end of overwriting check fi if test -f defs.h -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"defs.h\" else echo shar: Extracting \"defs.h\" \(4706 characters\) sed "s/^X//" >defs.h <<'END_OF_defs.h' X/* X Structure definitions, included files needed and useful constants for X Watcher. X X Kenneth Ingham X X Copyright (C) 1987 The University of New Mexico X*/ X X#include <stdio.h> X#include <ctype.h> X X#ifdef BSD X#include <strings.h> X#else X#include <string.h> X#endif X X#include <signal.h> X#include "y.tab.h" X X#define False 0 X#define True 1 X#define NAME "watcher" X#define MAX_STR 256 X#define DEF_CONTROL "watcherfile" X#define DEF_CONTROL2 "Watcherfile" X#define DEF_HISTFILE "watcher.history" X#define MAX_NAME 16 X#define MAX_VEC 100 X X#define INT 1 X#define FLOAT 2 X#define PERCENT 3 X#define ABSOLUTE 4 X#define MAX_MIN 5 X#define RELATIVE 6 X#define COLUMN 7 X#define NUM 8 /* really needs to be replaced by int or float */ X#define KEY 9 X X/* X below lie the structure definitions for all of the linked lists used X in watcher. Make piece with your god before venturing onward X*/ X X/* X what a column output fromat entry looks like: X*/ Xstruct col_out_st { X char *name; /* name of output field */ X int start; /* where it starts... */ X int end; /* ... and ends */ X int type; /* what type of data to find there */ X struct col_out_st *next; /* and the next in the list is... */ X}; X X/* X and a relative output format... X*/ Xstruct rel_out_st { X char *name; /* name of output field */ X int field; /* Which field to look in for it */ X int type; /* what type of data to find there */ X struct rel_out_st *next; /* and the next in the list */ X}; X X/* X types of change formats; all joined in a union later. X*/ Xstruct max_min_st { X float max; /* max allowable value... */ X float min; /* ...and the min */ X}; X Xunion fmt_u { X float percent; /* percent change */ X float abs_amount; /* absolute change */ X struct max_min_st max_min; /* max and min */ X char *str_value; /* what the string should be */ X}; X X/* X the actual structure describing how things are allowed to change. X*/ Xstruct change_fmt_st { X char *name; /* name of the field this pertains to */ X int type; /* what type of change */ X union fmt_u fmt; /* the format, depending on type */ X struct change_fmt_st *next; /* and of course the next in the list */ X}; X X/* X for the various types of output formats: X*/ Xunion out_fmt_u { X struct rel_out_st *rel_fmt; /* a relative output format (fields) */ X struct col_out_st *col_fmt; /* or one that uses columns */ X}; X X/* X what we are all about: the command structure. Here we bring it all X together and have something to work with (luckily the subroutines are X also set up in a similar heirarchy and we can pass various parts of X the linked lists to them and they don't care about the "upper" parts. X*/ Xstruct cmd_st { X char *pipeline; /* the pipeline to execute */ X char *alias; /* a name to use when refering to it */ X int out_type; /* what type of output format used */ X union out_fmt_u out_fmt; /* the output format */ X union out_fmt_u key; /* what to key on for btwn run cmps */ X struct change_fmt_st *change_fmt;/*the things to watch for changes */ X struct cmd_st *next; /* and of course the next in the list */ X}; X X/* X now we get into the structures for the history linked list... X*/ X X/* X a way of storing any data type: X*/ Xunion all_u { X char *strval; X int intval; X float floatval; X}; X X/* X which is used here where we hold the value for a specified key value X from the previous output. X*/ Xstruct val_st { X char *name; /* output field name */ X int type; /* what type of data in the union */ X union all_u val; /* ... the data (wow!) */ X struct val_st *next; /* and where would we be without a next one? */ X}; X X/* X for each line in the output of a command, we grab the key and store X all of the values obtained from the line. Here is how it is done. X*/ Xstruct key_st { X char *key_value; /* value for the key (could you guess?) */ X struct val_st *vals; /* the various interesting parts of the line */ X struct key_st *next; /* and of course the next one */ X}; X X/* X inally we come to the reaon for all of the above structures. This is X how previous commands are stored once they have been read in from the X history file. X*/ Xstruct old_cmd_st { X char *pipeline; /* the pipeline executed */ X struct key_st *keys; /* the keys and their useful parts */ X struct old_cmd_st *next; /* and the next pipeline... */ X}; X X/* X way of converting between type stored in struct and char for a X person's use. This works on the cmd_st.out_type values. X*/ X#define TCHAR(i) (i==STRING ? 's' : (i == KEY ? 'k' : 'd')) X Xchar *malloc(); /* really should switch back to malloc */ Xchar *get_rel_field(), *get_col_field(); Xdouble atof(); Xstruct old_cmd_st *find_prev_cmd(); X X/* bezerkeley vs system v differences... */ X#ifdef SYSV X#define index strchr X#endif END_OF_defs.h if test 4706 -ne `wc -c <defs.h`; then echo shar: \"defs.h\" unpacked with wrong size! fi # end of overwriting check 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