Downloads
49182.zip

Systems administrators are well aware of the many technologies available for monitoring and maintaining the machines on their networks. Unfortunately, Windows doesn't offer a built-in solution that reacts to specific system events (e.g., by sending an email alert or a page). There are, however, complete end-to-end solutions that monitor and respond to events on networked machines. Although such tools are powerful, they can be quite expensive, particularly for low-budget shops.

But the Windows platform does provide system components that let administrators easily write code to enable event response and don't require a large investment. One of the most flexible technologies with such capabilities is Windows Management Instrumentation (WMI).

WMI
In previous articles, I've discussed the use of WMI to enumerate lists of devices and properties—for example, listing all the drives on a machine.WMI classes enable this kind of functionality. Anyone with an Object Oriented Programming (OOP) background—using such languages as C++, C#, and Perl—knows that a class is a collection of properties and functions.

WMI makes copious use of classes. For most uses, you ask WMI to create an object based on a class, and your code simply examines (and perhaps modifies) the object's properties and calls into its functions. Actually, the most difficult aspect of programming in WMI is keeping track of all the classes. Because WMI is so heavily focused on classes, it's an easy technology to master: If you learn how to use it for one task (e.g., monitoring events), you've also learned how to use it for others (e.g., discovering devices on a machine). You don't have to learn new APIs for each task you need to do—only those that make up WMI. For more information about WMI, check out MSDN's Windows Management Instrumentation (WMI) page (http://msdn.microsoft.com/library/en-us/dnanchor/html/anch_wmi.asp).

To monitor events, a WMI script must accomplish three basic functions: connect, request, and process. The script's first task is to connect to the computer you intend to monitor—either your local machine or a remote box on the network. Second, the script needs to tell the remote machine what to monitor. To configure your script to do so, you can use an SQL-like query language that informs WMI exactly what you want to be informed about and the criteria that defines when you want to be informed. When your script successfully connects and submits its query, WMI returns an object known as an event-source object. This object will act as the source of all events that WMI will provide as a result of your query. A simple method call into this object will determine when an event is available so that your script can process it.

A full discourse about all aspects of WMI would take up the space of several articles. I don't have that much space, so I'll point you to Microsoft's WMI Scripting Primer (http://msdn.microsoft.com/library/en-us/dnclinic/html/scripting06112002.asp). Also, I wrote a chapter about using Perl with WMI and related technologies in my book Win32 Perl Scripting: The Administrator's Handbook.

EventMonitor.pl
Listing 1 shows an excerpt from EventMonitor.pl, which uses WMI to monitor Win32 EventLog entries. (You can download the entire script from the Windows Scripting Solutions Web site. Go to http://www.windowsitpro.com/windowsscripting, enter 49182 in the InstantDoc ID text box, then click the 49182.zip hotlink.) To run the script, pass the name of the machine you want to monitor (or no name, if you want to monitor your own machine) and the -t option, specifying the types of events you want to monitor, as the following example illustrates:

perl EventMonitor.pl MyServer
  -t error -t warning

This example will monitor the computer called MyServer for errors and warnings.

The code at callout A in Listing 1 sets up the script's variables. The $TIMEOUT value specifies how long the script will wait for an event before quitting. The default value of -1 tells WMI to wait forever. This value is in milliseconds, so if you specify a value of 9000, the script will terminate if it doesn't receive an event within 9 seconds. The purpose of the %HRESULT hash is to compare return values from WMI. The %EVENT_TYPE hash maps keywords that a user specifies on the command line with an actual eventtype value that WMI will use to filter out unwanted events.

The $SqlQuery string is assigned with a WMI query that will select all Win32 EventLog events. This string is modified in the next block of code if the user specifies a list of event types on the command line by using the -t type parameter. The subsequent block of code determines whether the user has specified any event types by checking whether the $Config\{types\} array has been populated.

For each type that the user has specified, the script pushes a SQL WHERE clause onto a temporary @WhereClause array. At the end of this block, the $SqlQuery string is appended to include a modified WHERE clause, which includes eventtype specifications. Note, however, that this occurs only when the @WhereClause array is populated. If the array is empty, the $SqlQuery string isn't modified. This is an important aspect of the code, because if the user specifies a type of -t all, the @WhereClause is destroyed and $Sql-Query won't be modified. If you don't specify which events to monitor, WMI will monitor all events.

The code at callout B is the core WMI code. First, this code creates a $WMIServices WMI object by calling into Win32::OLE->GetObject(). The winmgmts: moniker invokes the appropriate providers to provide access to WMI. The \{impersonation-Level=impersonate, (security)\} component of the connection string tells WMI to connect to the specified machine as the same user who is running the script. (Think of this as you impersonating yourself.) There are other security configurations, but we're edging out of the scope of this article. For more information, check out MSDN's Connecting to WMI on a Remote Computer page (http://msdn.microsoft.com/library/en-us/wmisdk/wmi/connecting_to_wmi_on_a_remote_computer.asp).

After the connection to the remote machine is established, the script calls the ExecNotificationQuery( ) method and issues the SQL query. When the call is successful, the remote machine starts to queue up event notifications to send them to your machine. The method call will have returned a WMI object called $Events—this is the WMI event source object, which the script will use to grab events off the host machine.

The code at callout C enters a loop and calls into the $Events object's NextEvent() method, passing in a timeout value. This method causes the script to pause until either an event is received from the remote machine or the timeout value has expired.

When the NextEvent() method returns, it will return either an error or a WMI __InstanceCreationEvent object (which you can read about on MSDN's __InstanceCreationEvent page at http://msdn.microsoft.com/library/default.asp?url=/library/enus/wmisdk/wmi/__instancecreationevent.asp). When the object is returned, the code extracts the WMI Win32_NTLogEvent object and prints various object properties to the screen. The code at callout D is used only to convert the DateTime value that the Win32_NTLogEvent object provides into a more readable date and time string.

Monitoring Your Boxes
Now that you understand the fundamentals of WMI event monitoring, you can modify EventMonitor.pl to suit your needs. For example, when an event occurs, you can easily add code at callout C to compare the event with any specific event or error that interests you and send out a page to systems administrators. Or you can leave the script as is and sit all day watching various errors and warnings scroll across the screen.