Home
About NuDesign
Sales
Support
Eval Download
Partners
Testimonials
SNMP Links
Tutorials
News

Tutorial for Application Monitoring SNMP Agent

This document explains the architecture of SNMP agents that implement ND-APPLICATION-MIB mib generated using NuDesign Visual xAgentBuilder for C++.  If you are not familiar with xAgentBuilder please take a look at the xAgentBuilder help topics titled "Create Standalone SNMP Agent" and "Create SNMP Extension Dll".

This series of agents demonstrate three different ways of architecting an SNMP agent.  All projects are built using the evaluation version of NuDesign Visual SNMP xAgentBuilder.  You may need to update your Include and Lib directories in your project settings to reflect the installation directory of the NuDesign Visual SNMP xAgentBuilder on your system.

Each agent can be built as a standalone agent or an extension DLL.  The project for extension DLL has the name as corresponding standalone agent will 'Dll' added to it.  For example "AppMon.dsp" is the project that creates the standalone agent, and "AppMonDll.dsp" creates the same agent as an extension DLL to be used with SNMP agent service.  Note that the only difference between a standalone agent and an extension DLL version is in one file: the standalone agent includes "main.cpp" in the project, while the extension DLL needs "DllMain.cpp".  Instrumentation code is identical in each case (except for handling of notifications!).

NOTE: Win32 API used to access running processes information is NT/2000 specific.  If you want to run this samples on Win98/ME you have to reimplement GetProcessStatus function to use Win98/ME specific API.  Two different methods are used for GetProcessStatus implementation.  One by using Win32 Performance counters and the other by using PSAPI.DLL.  The result is the same in both cases, but in order to use PSAPI variation PSAPI.DLL must be present on the system, along with the corresponding header and import library.  It is part of the Microsoft Platform SDK and is not by default installed along with Visual C++.

Application Monitoring MIB

The application monitoring MIB, ND-APPLICATION-MIB, is a very simple MIB.  It consists of two scalar objects:

appName OBJECT-TYPE
   SYNTAX DisplayString
   MAX-ACCESS read-write
   STATUS current
   DESCRIPTION "Name of the application to monitor."
::= { appObjects 1 }

 

appState OBJECT-TYPE
   SYNTAX INTEGER {
     unknown (0),
     started (1),
     stopped (2)
     }
   MAX-ACCESS read-only
   STATUS current
   DESCRIPTION "Current state of the application."
::= { appObjects 2 }

The second object appState represents the current state of the application appName.  Possible states of the application are:

MIB also defines one notification object appStartedEvent

appStartedEvent NOTIFICATION-TYPE
   OBJECTS {
     appName,
   }
   STATUS current
   DESCRIPTION "The SNMP trap that is generated when the application is started."
::= { appEvents 1 }

 SNMP agent should send notification (trap) when application appName is started.  Included in the trap's varbind list is name of the application that was started.

Here is tree structure of the ND-APPLICATION-MIB

[1.3.6.1.4.1] iso.org.dod.internet.private.enterprises

  |

  +-[4761] nuDesign

    |

    +-[99] ndtExperimental

      |

      +-[12] app

        |

        +-[ 1] appObjects

        | |

        | +-[ 1] -RW- DisplayString appName

        | +-[ 2] -RO- appState

        |

        +-[ 2] appEvents

          |

          +-[ 1] appStartedEvent

 

Please note that the sample projects assume the directories for includes and libraries as the default install directories.  If you have installed the xAgentBuilder product in a directory other than the default directory then you should

AppMon1 - Agent #1

This is the simplest of the three agents.  Source code for this agent is in the AppMon1 directory.  Both MIB objects are implemented in DAppObjects class.

appName is instrumented as class member variable m_strAppName.

When a Get SNMP request for appState variable arrives, OnGet_AppState method of DAppObjects class gets called.  State of the application is simply retrieved by calling GetProcessStatus method.

The possible states of the application reported by the agent are:

Note that this agent does not implement the appStarted event.

To test this agent, please start Visual MIBrowser and load the ND-APPLICATION-MIB.  Open the iso.org.dod.internet.private.enterprises.NuDesign nodes.  Right click on the NuDesign node and click on 'Walk' to walk the agent.  You should see a response coming back from the agent.  Now right click on the appName object and choose set.  Double click on the entry in the Set window.  You can set the name of the process whose status you would like to check.  For example, 'winlogon.exe'.  You set this value and then do a GET on 'appState'.  You should see the response as 'started (1)'.

AppMon2 - Agent #2

Source code for this agent is in the AppMon2 directory.  In this version a thread (instance of class ProcMonThread) will be created to monitor the state of the application.  This thread will periodically (once every second) check and save the state of the application appName.  If the current state is 'started' and the previous is either 'unknown' or 'stopped' a notification will be sent.

Both objects in the MIB are instrumented in the object of class ProcessData.  Since access to the data can occur from (at least) two different threads (SNMP request handler and Application Monitor), SingleWriter-MultipleReader synchronization objects is also part of the ProcessData.  Before methods of ProcessData are called an appropriate lock is acquired: for Get methods 'read' lock, for Set methods 'write' lock.

This architecture decouples the actual task of application monitoring from the SNMP agent.  SNMP requests are simply fulfilled on variables that represent MIB objects.  Note that the state retrieved through an SNMP request is actually the 'cashed' value, and does not reflect the app state at the time of the request, but at the last time when the monitor thread updated it.  In other words, it could be off by one second (i.e. the interval used for periodical app state checking).

Please use Visual MIBrowser or another management application to try out this agent.

AppMon3 - Agent #3

This version of the agent relies on the AppMonServer application.  AppMonServer is a console application that monitors the state of the application.  Source code for this agent is in the AppMon3 directory and source code for the helper app is in the AppMonServer directory.

Communication between Agent #3 and AppMonServer is established through the named pipes.  Two pipes will be opened.  One, \\.\pipe\appmon, for fulfilling requests from the agent (initiated by SNMP requests), the other, \\.\pipe\notif, for sending notification(s) from AppMonServer to the agent.

The architecture of the AppMonServer is similar to the architecture of the Agent #2.  On start, it creates an application monitor thread, ProcessMonitorThreadProc, then creates \\.\pipe\appmon pipe and eneters the endless loop.  Inside the loop it waits for the client (see PipeRequest function in Agent #3) to connect to the pipe.  When the connection occurs, a thread RqHndlrThreadProc is created to handle one request, and new wait for the client to connect to the pipe is started.

AppMonServer instruments 'appName' and 'appState' in the object of class ProcessData.  Since access to the data can occur from two different threads ( RqHndlrThreadProc and ProcessMonitorThreadProc), mutex synchronization objects are also part of the ProcessData.  Before methods of ProcessData are called a lock is acquired.

Thread RqHndlrThreadProc reads from the \\.\pipe\appmon pipe, decodes the request, performs the requested operation, writes the result to the same pipe and finally exits.  The format of the request is following:

<g|s>:<varname>:<value>

<g|s>

g = get, s = set

<varname>

variable name ("appState" or "appName")

<value>

variable value (for set request)

Note that the request handler can accept only one variable at a time.  This is chosen for the simplicity and to show one way of interprocess communication.  Depending on your application you may want to consider tcp/ip or some other way of communicating with an application.

The request is resolved by calling access methods of ProcessData.  Note that calls are serialized by acquiring a mutex lock.

Thread ProcessMonitorThreadProc simply checks and saves the state (in the instance of the ProcessData class) of the application every second.   If the current state is 'started' and the previous is either 'unknown' or 'stopped' the Notify function will be executed.  It tries to connect to the \\.\pipe\notif pipe and if it succeeds it simply writes appName to it and disconnects.

Agent #3 creates the thread NotifThread.  This thread creates \\.\pipe\notif pipe, and then enters the endless loop, Inside the loop it waits for the client (ProcessMonitorThreadProc in AppMonServer) to connect to it, and when connection occurs, reads the 'appName' from the pipe and send SNMP notification.

This agent also instantiates an object of class ProcessData, and uses its access methods to retrieve/modify MIB variables.  But this ProcessData class is different than the one in AppMonServer.  If you take a look at the implementation of the access methods you will see that all of them call the PipeRequest function.  The PipeRequest function tries to connect to the existing \\.\pipe\appmon pipe (that is the one created by AppMonServer) and if it succeeds writes a request to the pipe (format of the request is described above), then it reads the result from the same pipe, and finally disconnects.