<SECTION>Introduction</SECTION>

Hello and welcome to the OmniMoni demonstration.  This Tcl/Tk Demonstration script is very simple, and lacks almost any error checking or robustness.  It's purpose is to allow you to read through the demonstration explanations and easily try out the OmniMoni demonstration scripts.  Having said that, on to OmniMoni.

This demo is, in part, a tutorial.  Each demo has a brief explanation and shows the OmniMoni configuration file.  If you are just checking out OmniMoni then you don't have to read any of that stuff and you can just click (with the left mouse button) on the highlighted areas to see those demos.

If you would like to learn how to create your own configuration files, or how to alter these ones, then this tutorial should be used along with the OmniMoni man page.  It gives complete details on how to do everything in the configuration files.  Although I try to demonstrate each piece here, the best source is the man page.

All of the examples in this Demonstration depend on the usage and output format of the command being executed as being consistent.  As you move from one operating system to another, commands have different flags and different output.  This was written on a Linux system.  If a particular command does not run, or does not exist, you will see an error message appear in shell window from which the Demonstration was run.  If this happens you can retry that command by clicking on it with the middle mouse button to see it as a static command, or you can go on to the next example.  If you know that the command will not run, you can select that demo with the middle mouse button to begin with.  This will run OmniMoni in debug level 4 mode (see the man page) which will still show the layout of the demonstration, but all of the fields will be static, hence the command is never run.

With all OmniMoni displays, you can click on any dynamic label with the left mouse button to "demand" an update.  This is most useful for those items that only update rarely.

To quit a running OmniMoni type "Q" (capital "q") while the mouse is in the OmniMoni window.

To quit this demo/tutorial, type "Q" while the mouse in the the demo window.

You can scroll through this text with the cursor up and down keys and the page up and page down keys.

If you've created any configuration files you think would like to share, please e-mail them to me at rvm@fore.com.  I am especially looking for general, useful, simple scripts for non-Linux operating systems.  Examples are memory, disk space, load monitors, etc.


				Thank you,

				Rainer Mager



<SECTION>1:  Who are you?</SECTION>

Ok, since we're just starting out I will be simple.  This script will do something not particularly useful, tell who you are.  Type "Q" (capital "q") while the mouse pointer is in the window to stop OmniMoni, and go to the next demonstration.

The config file follows a relatively simple, recursive, format.  The idea is that you have a list of commands where each command takes one argument.  Some command's argument can be a curly brace enclosed list which contains more commands.

<FX>WINDOW {
<FX>	NAME whoami
<FX>	LABEL "OmniMoni Window"
<FX>	GROUP {
<FX>		NAME whoami
<FX>		COMMAND {whoami}
<FX>		LABEL {
<FX>			EXPRESSION "$0"
<FX>		}
<FX>	}
<FX>}

This creates a WINDOW called "whoami" and a LABEL of "OmniMoni Window".  The name is simply used for internal reference and should not contain any unusual character or spaces.  The label is what appears in the window's title-bar.  Within this window a GROUP, also called "whoami", is created.  It will run the COMMAND "whoami".  Within this group is a label which will print the results of the EXPRESSION "$0".  For complete information on what the expressions mean see the man page, but for now suffice it to say that a "$" sign designates that we are taking a field of the output and 0 is the first field.

To see this demo, <DEMO-IT>demos/whoami.omni</DEMO-IT>, click on it.



<SECTION>2:  How busy are you?</SECTION>

This one's still pretty simple, but now we're actually getting useful information.  Also, we're looking at multiple fields and we're looking at them from the right.  OmniMoni can pull fields out of the program's output from the left to the right and from the top to the bottom (the way English is read), or the opposite way.  Also, in this one, we add a static label.

<FX>WINDOW {
<FX>	NAME load
<FX>	LABEL "Is the system busy?"
<FX>	GROUP {
<FX>		NAME load
<FX>		COMMAND {uptime}
<FX>		LABEL {
<FX>			NAME Title
<FX>			STATIC "Load Average"
<FX>		}
<FX>		LABEL {
<FX>			NAME 1-Minute
<FX>			EXPRESSION "%2"
<FX>		}
<FX>		LABEL {
<FX>			NAME 5-Minutes
<FX>			EXPRESSION "%1"
<FX>		}
<FX>		LABEL {
<FX>			NAME 15-Minutes
<FX>			EXPRESSION "%0"
<FX>		}
<FX>	}
<FX>}

Again we set up the top level WINDOW first.  Inside it we create the top GROUP which is the one that runs the COMMAND "uptime" and displays a little message as a static LABEL.  Within this group we also have 3 other LABELs, each of which displays an EXPRESSION.  These expressions all use the "%" symbol.  This symbol is the same as the "$" symbol except that is takes the fields starting at the bottom right.

On my system, the command "uptime" return output in the form:

<FX>11:25am  up 16:34,  2 users,  load average: 1.27, 1.18, 1.11

But, it will also sometimes return output in the form:

<FX>11:25am  up 2 days,  2 users,  load average: 1.27, 1.18, 1.11

For the second one, there is 1 more field from left to right.  That is why I pull the fields out from right to left.  I am guaranteed that the first three fields, from the right, are what I want.

To see this demo, <DEMO-IT>demos/loadavg.omni</DEMO-IT>, click on it.



<SECTION>3:  Esthetics, esthetics.</SECTION>

Now, if you were paying attention (and your OS displayed it the way mine does), you might have noticed some ugly commas in that previous one.  This one's the same as the last one, but we get rid of the commas using the split ability.  Also, we change the placement of some of the text.

<FX>WINDOW {
<FX>	NAME load
<FX>	LABEL "Is the system busy?"
<FX>	GROUP {
<FX>		NAME load
<FX>		COMMAND "uptime"
<FX>		LABEL {
<FX>			NAME load
<FX>			STATIC "Load Average"
<FX>			EXTRA {"-font 10x20"}
<FX>		}
<FX>		LABEL {
<FX>			NAME t-1
<FX>			EXPRESSION "|, %2"
<FX>			PLACE left
<FX>		}
<FX>		LABEL {
<FX>			NAME t-5
<FX>			EXPRESSION "|, %1"
<FX>			PLACE left
<FX>		}
<FX>		LABEL {
<FX>			NAME t-15
<FX>			EXPRESSION "|, %0"
<FX>			PLACE left
<FX>		}
<FX>	}
<FX>}

So, what's different?  First we added the command EXTRA to the static LABEL.  The extra command just passes the given string to Tcl/Tk's configure command for the given widget.  In this case we configure the font to "10x20" (if your system does not have a font with this name then a warning will be displayed in the window from where you ran this Demonstration script).  We also added the PLACE command to each of the other 3 groups.  This command tells where to place this group within its parent.

Notice that it is important to enclose the extra command's arguments in the curly braces.  This is because the extra command takes each argument it finds and configures the first field of that argument as the configure option and the second field as the configure argument.  Had we not enclosed the argument in curly braces OmniMoni would have attempted to first configure the open "-font" with no arguments, then configure the option "10x20" with no options.

Finally, we added a bit to the expressions.  The "|" symbol means to replace each instance of the following character "," with a space (this essentially splits the output into more fields, but in this case gets rid of the commas we didn't want).

To see this demo, <DEMO-IT>demos/loadavg2.omni</DEMO-IT>, click on it.



<SECTION>4:  Can you say "GRAPH"?</SECTION>

So far our load average display is pretty good, but what if we want to look at the load average over time?  For this we need a graph.

<FX>WINDOW {
<FX>	NAME load
<FX>	LABEL "Is the system busy?"
<FX>	GROUP {
<FX>		NAME load
<FX>		COMMAND "uptime"
<FX>		UPDATE 2
<FX>		LABEL {
<FX>			NAME load
<FX>			STATIC "Load Average"
<FX>			EXTRA {"-font 10x20"}
<FX>		}
<FX>		GROUP {
<FX>			NAME group
<FX>			PLACE top
<FX>			LABEL {
<FX>				NAME t-1
<FX>				EXPRESSION "|, %2"
<FX>				PLACE left
<FX>			}
<FX>			LABEL {
<FX>				NAME t-5
<FX>				EXPRESSION "|, %1"
<FX>				PLACE left
<FX>			}
<FX>			LABEL {
<FX>				NAME t-15
<FX>				EXPRESSION "|, %0"
<FX>				PLACE left
<FX>			}
<FX>		}
<FX>		GRAPH {
<FX>			NAME graph
<FX>			PLACE bottom
<FX>			UPDATE 2
<FX>			PLOT {
<FX>				NAME t-1
<FX>				EXPRESSION "|, %2"
<FX>				COLOR "#000"
<FX>			}
<FX>			PLOT {
<FX>				NAME t-5
<FX>				EXPRESSION "|, %1"
<FX>				COLOR "#444"
<FX>			}
<FX>			PLOT {
<FX>				NAME t-15
<FX>				EXPRESSION "|, %0"
<FX>				COLOR "#888"
<FX>			}
<FX>		}
<FX>	}
<FX>}

What did we do this time?  First you'll notice the new GROUP around the 3 dynamic labels.  This was necessary to allow the graph to go below them (go ahead, try taking putting those 3 directly in the "load" group).  Also, obviously, we added a graph.

A GRAPH command is very similar to a group command, except it contains PLOTs instead of LABELs.  For the COLORs in the graph command, it is perfectly ok to use standard color names, if they're in your RGB database, I chose to give explicit values for these, though, in case some systems do not have the same names.

To see this demo, <DEMO-IT>demos/loadavg3.omni</DEMO-IT>, click on it.



<SECTION>5:  Cool...variables.</SECTION>

This one's a calendar and a clock, I know...not very exciting.  The main point of this one is to show the ability to use OmniMoni's variables.  These variable substitutions can appear anywhere in the configuration file where an argument is expected.  Variables are only substituted when that argument is first parsed.

This demo depends on the output of "date" being in the form like:

<FX>Wed Feb  8 22:04:03 JST 1995

If yours is not the same then you will see output, but it won't be what I intended.

<FX>CONFIGURE {
<FX>	ASSIGN "COLOR #f00"
<FX>}

<FX>WINDOW {
<FX>	NAME date
<FX>	GROUP {
<FX>		NAME date
<FX>		COMMAND "date"
<FX>		UPDATE 1
<FX>		LABEL {
<FX>			NAME title
<FX>			STATIC "The current date and time:"
<FX>			EXTRA {"-fg $COLOR"}
<FX>		}
<FX>		GROUP {
<FX>			NAME Date	
<FX>			LABEL {
<FX>				NAME Day
<FX>				EXPRESSION "$0"
<FX>				PLACE left
<FX>			}
<FX>			LABEL {
<FX>				NAME Month
<FX>				EXPRESSION "$1"
<FX>				PLACE left
<FX>			}
<FX>			LABEL {
<FX>				NAME Date
<FX>				EXPRESSION "$2"
<FX>				PLACE left
<FX>			}
<FX>			LABEL {
<FX>				NAME Zone
<FX>				EXPRESSION "$4"
<FX>				PLACE left
<FX>			}
<FX>			LABEL {
<FX>				NAME Year
<FX>				EXPRESSION "$5"
<FX>				PLACE left
<FX>			}
<FX>		}
<FX>		LABEL {
<FX>			NAME Time
<FX>			EXPRESSION $3
<FX>		}
<FX>	}
<FX>}

In this demo we added the CONFIGURE command at the beginning.  This command can take a number of different other commands, but in this case we are showing the ASSIGN command.  This command is pretty obvious in that it assigns the value "#f00" to the variable "COLOR" (yes, these variables must be all upper case).  If you'll notice, then, in the LABEL "title" it uses the variable "COLOR".  Also notice that in the running of this demo below, we can override the assign command with command line arguments (CLAs).  It is necessary, however, to put some value in the configuration file so that if this demo is run without any CLAs it will still assign a value to "COLOR".

To see this demo in blue, <DEMO-IT>demos/date.omni -a COLOR blue</DEMO-IT>, click on it.
To see this demo in red, <DEMO-IT>demos/date.omni -a COLOR red</DEMO-IT>, click on it.
To see this demo in green, <DEMO-IT>demos/date.omni -a COLOR green</DEMO-IT>, click on it.
To see this demo in blue-green, <DEMO-IT>demos/date.omni -a COLOR #0ff</DEMO-IT>, click on it.



<SECTION>6:  Hey, what's going on?</SECTION>

This one begins to show the flexibility possible.  It does a command approximately like a "top 3", that is it shows the top most CPU intensive processes.  The only problem with this is that since OmniMoni is generally running hard at the time the "ps" command happens, especially if OmniMoni is doing complex stuff with graphs, it is generally shown as one of the busiest processes.

<FX>CONFIGURE {
<FX>	DEFAULT {
<FX>		LABEL-EXTRA {"-font 8x16"}
<FX>	}
<FX>}

<FX>WINDOW {
<FX>	NAME Top
<FX>	LABEL "My titles are too boring."
<FX>	GROUP {
<FX>		NAME Top
<FX>		COMMAND {ps -auxwc | awk '{print $3" \{ "$0"\}"}' | sort -rn}
<FX>		UPDATE 10
<FX>		PLACE top
<FX>		EXTRA {"-relief sunken"}
<FX>		GROUP {
<FX>			NAME CPU
<FX>			PLACE left
<FX>			LABEL {
<FX>				NAME Title
<FX>				STATIC "% CPU"
<FX>			}
<FX>			LABEL {
<FX>				NAME First
<FX>				EXPRESSION "$1{$2}"
<FX>			}
<FX>			LABEL {
<FX>				NAME Second
<FX>				EXPRESSION "$3{$2}"
<FX>			}
<FX>			LABEL {
<FX>				NAME Third
<FX>				EXPRESSION "$5{$2}"
<FX>			}
<FX>		}
<FX>		GROUP {
<FX>			NAME PID
<FX>			PLACE left
<FX>			LABEL {
<FX>				NAME Title
<FX>				STATIC "PID"
<FX>			}
<FX>			LABEL {
<FX>				NAME First
<FX>				EXPRESSION "$1{$1}"
<FX>			}
<FX>			LABEL {
<FX>				NAME Second
<FX>				EXPRESSION "$3{$1}"
<FX>			}
<FX>			LABEL {
<FX>				NAME Third
<FX>				EXPRESSION "$5{$1}"
<FX>			}
<FX>		}
<FX>		GROUP {
<FX>			NAME Time
<FX>			PLACE left
<FX>			LABEL {
<FX>				NAME Title
<FX>				STATIC "Time"
<FX>			}
<FX>			LABEL {
<FX>				NAME First
<FX>				EXPRESSION "$1{%1}"
<FX>			}
<FX>			LABEL {
<FX>				NAME Second
<FX>				EXPRESSION "$3{%1}"
<FX>			}
<FX>			LABEL {
<FX>				NAME Third
<FX>				EXPRESSION "$5{%1}"
<FX>			}
<FX>		}
<FX>		GROUP {
<FX>			NAME User
<FX>			PLACE left
<FX>			LABEL {
<FX>				NAME Title
<FX>				STATIC "User"
<FX>			}
<FX>			LABEL {
<FX>				NAME First
<FX>				EXPRESSION "$1{$0}"
<FX>			}
<FX>			LABEL {
<FX>				NAME Second
<FX>				EXPRESSION "$3{$0}"
<FX>			}
<FX>			LABEL {
<FX>				NAME Third
<FX>				EXPRESSION "$5{$0}"
<FX>			}
<FX>		}
<FX>		GROUP {
<FX>			NAME Name
<FX>			PLACE left
<FX>			LABEL {
<FX>				NAME Title
<FX>				STATIC "Name"
<FX>			}
<FX>			LABEL {
<FX>				NAME First
<FX>				EXPRESSION "$1{%0}"
<FX>			}
<FX>			LABEL {
<FX>				NAME Second
<FX>				EXPRESSION "$3{%0}"
<FX>			}
<FX>			LABEL {
<FX>				NAME Third
<FX>				EXPRESSION "$5{%0}"
<FX>			}
<FX>		}
<FX>	}
<FX>}

There are a few items of note in this one.  The CONFIGURE command at the beginning uses the command DEFAULT to set the default value for all EXTRA commands for LABELs.  There is a predefined set of commands for which a default can be set.  Check the man page for the complete listing.

After that an interesting part is the COMMAND being run.  The awk is necessary to put the CPU usage first so that the sort can be applied to it.  This also groups the whole result into a known number of fields.  In this case the first field ($0) is the CPU usage for the top process, the second field ($1) is the total result for that process, the third file ($2) is the CPU usage for the second top process, etc.  Were we not to use awk then the sorting would be impossible and each process would have an unknown number of fields.  All of this of course also depends on the format of your ps command and what the flags do.  In my case "ps -auxwc" gives output in the form:

<FX>USER       PID %CPU %MEM SIZE  RSS TTY STAT START   TIME COMMAND

The final item of interest in this demo is the use of curly braces in the EXPRESSION commands.  This allows OmniMoni to pull fields from text which is enclosed in such braces (or double quotes).  Read the man page for more information on this.

To see this demo, <DEMO-IT>demos/top.omni</DEMO-IT>, click on it.



<SECTION>7:  What are the differences?</SECTION>

This one demonstrates how to get a difference of two outputs.  This is useful for showing the delta or change is a numerical result.  Specifically this one shows the packets per second (average over the last 10 seconds).  This command depends on the "netstat -i" command which varies in output from OS to OS.  On Linux it is like:

<FX>Iface   MTU Met  RX-OK RX-ERR RX-DRP RX-OVR  TX-OK TX-ERR TX-DRP TX-OVR Flags
<FX>eth0   1500   0 1079799      0      2      0 1833826      0      0      0 BRU

Thus we would take fields %4 and %8 for transmit and receive packets.


on SunOS it is like:

<FX>Name  Mtu  Net/Dest      Address        Ipkts  Ierrs Opkts  Oerrs Collis Queue 
<FX>le0   1500 202.236.58.0  tomato         301677  0    176886  0    470    0

Where we would take fields %4 and %6 for transmit and receive packets.

Since I don't know what OS you are running I will give you some options at the bottom.

<FX>CONFIGURE {
<FX>	ASSIGN "INTERFACE lo"
<FX>	ASSIGN "TRANSMIT 4"
<FX>	ASSIGN "RECEIVE 8"
<FX>}

<FX>WINDOW {
<FX>	NAME netstat
<FX>	LABEL "Pardon me, but do you network."
<FX>	GROUP {
<FX>		NAME netstat
<FX>		COMMAND "/bin/netstat -i | grep $INTERFACE"
<FX>		LABEL {
<FX>			NAME Title
<FX>			STATIC "Packets per second for $INTERFACE:"
<FX>			PLACE top
<FX>		}
<FX>		GROUP {
<FX>			NAME Transmit
<FX>			PLACE left
<FX>			!LABEL {
<FX>				NAME Title
<FX>				STATIC "Transmitted"
<FX>			}
<FX>			LABEL {
<FX>				NAME Transmit
<FX>				EXPRESSION "( %$TRANSMIT - =%$TRANSMIT ) / double ( %s )"
<FX>			}
<FX>		}
<FX>		GROUP {
<FX>			NAME Receive
<FX>			PLACE left
<FX>			!LABEL {
<FX>				NAME Title
<FX>				STATIC "Received"
<FX>			}
<FX>			LABEL {
<FX>				NAME Receive
<FX>				EXPRESSION "( %$RECEIVE - =%$RECEIVE ) / double ( %s )"
<FX>			}
<FX>		}
<FX>	}
<FX>}

Again in this one we use the ASSIGN command to define some variables.  Notice the grep in the COMMAND to get only the line of output that we want.  The real interesting part, however, is the EXPRESSION commands.  Don't let the variables throw you, just pretend there constant numbers (as far as OmniMoni is concerned, they are).  The cool part is the "=" construct.  This takes the output from the previous time COMMAND was run and takes that field.  This allows us to perform a difference on the two numbers.  We also use the %s special symbol which represents the number of seconds since the last time we ran this command.  So, we take the current total packets minus the past total packets divided by the number of elapsed seconds.  This, of course, gives us the average packets per second over that time.

To see this demo, <DEMO-IT>demos/netstat.omni -a INTERFACE eth0 -a PATH /bin -a TRANSMIT 4 -a RECEIVE 8</DEMO-IT>, if your netstat is in "/bin", your interface is "eth0", and the fields are 4 and 8 (Linux), click on it.

To see this demo, <DEMO-IT>demos/netstat.omni -a INTERFACE ec0 -a PATH /usr/etc -a TRANSMIT 2 -a RECEIVE 4</DEMO-IT>, if your netstat is in "/usr/etc", your interface is "ec0", and the fields are 2 and 4 (SGI), click on it.

To see this demo, <DEMO-IT>demos/netstat.omni -a INTERFACE le0 -a PATH /usr/ucb -a TRANSMIT 3 -a RECEIVE 5</DEMO-IT>, if your netstat is in "/usr/ucb", your interface is "le0", and the fields are 3 and 5 (SunOS), click on it.

If I don't show your parameters above you can click on one anyway and will get a warning, but the window will come up anyway, or you can run this demo from the command line line this:

<FX>wish -f src/omnimoni.tcl -f demos/netstat.omni -a INTERFACE . . .

Of course you must fill in all of the -a flags with the appropriate values.  If you've got a few minutes to spare I'd appreciate it if you could email me output from your "netstat -i" command along with the path to netstat so I could include your system in my next release.


One last thing on this demo.  After the window comes up, regardless of whether there is nice output there or not, you can click on the right mouse button to bring up a menu of the window's hierarchy.  Go to "netstat" -> "Transmit" -> "Title" and see what happens.  Try the same for "Receive".  The reason these 2 widgets started out unpacked was because of the exclamation mark in the config file.  After this try clicking on any widget in the window with the middle button.  Now check the menu again.  Cool eh?



<SECTION>8:  The grand finale!</SECTION>

This last demo tries to bring everything together.  I won't show you all of OmniMoni config files involved, just the main one.  You can check the others on your own if you wish.  I try to build off of what we've done up to now, adding here and there to make everything more polished.

<FX>CONFIGURE {
<FX>    EXTRA "*font 8x16"
<FX>    EXTRA "*borderWidth 2"
<FX>    EXTRA "*padX 1"
<FX>    EXTRA "*padY 1"
<FX>    EXTRA "*ipadX 1"
<FX>    EXTRA "*ipadY 1"
<FX>}

<FX>INCLUDE "demos/loadavg.fancy.omni"
<FX>INCLUDE "demos/top.fancy.omni"
<FX>INCLUDE "demos/netstat.fancy.omni"

The only new thing shown here is the INCLUDE command.  It essentially inserts the specified file as if that code was actually present in the calling file.

To see this demo, <DEMO-IT>demos/everything.omni -a INTERFACE eth0 -a PATH /bin -a TRANSMIT 4 -a RECEIVE 8 -c</DEMO-IT>, if your netstat is in "/bin", your interface is "eth0", and the fields are 4 and 8 (Linux), click on it.

To see this demo, <DEMO-IT>demos/everything.omni -a INTERFACE ec0 -a PATH /usr/etc -a TRANSMIT 2 -a RECEIVE 4 -c</DEMO-IT>, if your netstat is in "/usr/etc", your interface is "ec0", and the fields are 2 and 4 (SGI), click on it.

To see this demo, <DEMO-IT>demos/everything.omni -a INTERFACE le0 -a PATH /usr/ucb -a TRANSMIT 3 -a RECEIVE 5 -c</DEMO-IT>, if your netstat is in "/usr/ucb", your interface is "le0", and the fields are 3 and 5 (SunOS), click on it.

Notice the last flag sent to OmniMoni.  This "-c" tells it to iconify the main window at startup.  Make sure you try uniconify that window and click the right mouse button in it.  This menu shows a list of all sub windows OmniMoni has created.



<SECTION>Hints and Tips:</SECTION>

This section is a disorganized list of hints, tips, and ideas that I or other users have come up with.  If you have anything to contribute to this, please email it to me so that all OmniMoni users can benefit.


<SECTION>1</SECTION>  If you would like to have a graph have a maximum value and not scale with each change, you can graph an explicit number with a color of  ""  (clear) as in:

<FX>PLOT {
<FX>	NAME Max
<FX>	EXPRESSION  "10"
<FX>	COLOR ""
<FX>	TYPE lined 
<FX>}

This will always graph a "clear" line at 10 on the graph.  If a dynamic value exceeds 10, though, then the graph will begin to automatically scale again.


<SECTION>2</SECTION>  To save CPU resources it is critical that you make the update delta a large as possible.  If you have a script that is only updated once every 60 seconds and you have the update delta set to 1 second then you will still use a lot of CPU.  You should, in this case, set the update delta to 60 seconds.

If you do this then you will get a flurry of CPU activity when the script is run and then zero usage between each update.


<SECTION>3</SECTION>  In conjunction with the "-c" flag, if you give OmniMoni a "-geometry" you can have it start up and place all you windows as you like them without needing interaction.