TUT:Simple Async Application

From Net-SNMP Wiki
Revision as of 14:33, 11 November 2014 by Stephen (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Here we discuss how to write an asynchronous application. It's only purpose is to retrieve the value of a set of variables from a set of remote hosts. (This is for the C library, see TUT:Simple_Perl_Async_Application for Perl.)

Here are the files discussed in this example so you can download them:

File Description
Makefile A simple makefile used to build the projects
NET-SNMP-TUTORIAL-MIB.txt The MIB we'll be writing code for in the various pieces of the agent extension tutorial
asyncapp.c The Asynchronous C source code

This discussion assumes that you already have read the snmpdemoapp tutorial section.

The program assumes that you have a set of hosts that you want to interrogate for a set of variables. For simplicity's sake each query just asks for one variable.

The program defines two structures, struct host which holds the host specific data, hostname and community to be used, and struct oid which holds the object to be queried for.

 /*
  * a list of hosts to query
  */
 struct host {
   char *name;
   char *community;
 } hosts[] = {
   { "test1",            "public" },
   { "test2",            "public" },
   { "test3",            "public" },
   { "test4",            "public" },
   { NULL }
 };
 
 /*
  * a list of variables to query for
  */
 struct oid {
   char *Name;
   oid Oid[MAX_OID_LEN];
   int OidLen;
 } oids[] = {
   { "system.sysDescr.0" },
   { "interfaces.ifNumber.1" },
   { "interfaces.ifNumber.0" },
   { NULL }
 };

As a brush up, we then define the function synchronous that scans the hosts using synchronous calls:

 void synchronous (void)
 {
   struct host *hp;
 
   for (hp = hosts; hp->name; hp++) {
     struct snmp_session ss, *sp;
     struct oid *op;
 
     snmp_sess_init(&ss);                        /* initialize session */
     ss.version = SNMP_VERSION_2c;
     ss.peername = hp->name;
     ss.community = hp->community;
     ss.community_len = strlen(ss.community);
     snmp_synch_setup(&ss);
     if (!(sp = snmp_open(&ss))) {
       snmp_perror("snmp_open");
       continue;
     }
     for (op = oids; op->Name; op++) {
       struct snmp_pdu *req, *resp;
       int status;
       req = snmp_pdu_create(SNMP_MSG_GET);
       snmp_add_null_var(req, op->Oid, op->OidLen);
       status = snmp_synch_response(sp, req, &resp);
       if (!print_result(status, sp, resp)) break;
       snmp_free_pdu(resp);
     }
     snmp_close(sp);
   }
 }

This should contain no surprises, as you already mastered the snmpdemoapp chapter.

Now onto the new material. We define a struct session to hold our per-host state:

 struct session {
   struct snmp_session *sess;          /* SNMP session data */ 
   struct oid *current_oid;            /* How far in our poll are we */
 } sessions[sizeof(hosts)/sizeof(hosts[0])];
 
 int active_hosts;                     /* hosts that we have not completed */

We then need a callback function, to be called whenever a response is received:

 int asynch_response(int operation, struct snmp_session *sp, int reqid,
                     struct snmp_pdu *pdu, void *magic)
 {
   struct session *host = (struct session *)magic;
   struct snmp_pdu *req;
 

whenever a response is received, we will print it out, and send the next request out:

   if (operation == RECEIVED_MESSAGE) {
     if (print_result(STAT_SUCCESS, host->sess, pdu)) {
       host->current_oid++;                      /* send next GET (if any) */
       if (host->current_oid->Name) {
 

there is still more to do ... build the next request

         req = snmp_pdu_create(SNMP_MSG_GET);
         snmp_add_null_var(req, host->current_oid->Oid, host->current_oid->OidLen);
         if (snmp_send(host->sess, req))
 

we are still in business

           return 1;
         else {
 

something went wrong. Print out error message, and fall through to decrement active_hosts

           snmp_perror("snmp_send");
           snmp_free_pdu(req);
         }
       }
     }
   }
   else
     print_result(STAT_TIMEOUT, host->sess, pdu);
   /* something went wrong (or end of variables)
    * this host not active any more
    */
   active_hosts--;
   return 1;
 }

Now the code to fire this up, and keep it running until we are done

 void asynchronous(void)
 {
   struct session *hs;
   struct host *hp;

First we loop through all the hosts, opening a session for each and sending out the first request:

   /* startup all hosts */
   for (hs = sessions, hp = hosts; hp->name; hs++, hp++) {
     struct snmp_pdu *req;
     struct snmp_session sess;
     snmp_sess_init(&sess);                    /* initialize session */
     sess.version = SNMP_VERSION_2c;
     sess.peername = hp->name;
     sess.community = hp->community;
     sess.community_len = strlen(sess.community);

The next two lines is where we deviate from the good old sync setup

     sess.callback = asynch_response;            /* default callback */
     sess.callback_magic = hs;
     if (!(hs->sess = snmp_open(&sess))) {
       snmp_perror("snmp_open");
       continue;
     }
     hs->current_oid = oids;
     req = snmp_pdu_create(SNMP_MSG_GET);        /* send the first GET */
     snmp_add_null_var(req, hs->current_oid->Oid, hs->current_oid->OidLen);
     if (snmp_send(hs->sess, req))
       active_hosts++;
     else {
       snmp_perror("snmp_send");
       snmp_free_pdu(req);
     }
   }

That was the startup code. Now we just wait for things to settle down again. All the real work is handled inside the callback function.

   /* loop while any active hosts */
   while (active_hosts) {
     int fds = 0, block = 1;
     fd_set fdset;
     struct timeval timeout;

snmp_select_info is the function that gets us all we need to be able to call select

     FD_ZERO(&fdset);
     snmp_select_info(&fds, &fdset, &timeout, &block);
     fds = select(fds, &fdset, NULL, NULL, block ? NULL : &timeout);

snmp_read will read all sockets with pending data, and process them

     if (fds) snmp_read(&fdset);
     else snmp_timeout();
   }

Now active_hosts == 0, and all data have been returned. Close all sessions and finish up.

   /* cleanup */
   for (hp = hosts, hs = sessions; hp->name; hs++, hp++) {
     if (hs->sess) snmp_close(hs->sess);
   }
 }

The main program just demonstrates the two ways to do the job:

 int main (int argc, char **argv)
 {
   initialize();
 
   printf("---------- synchronous -----------\n");
   synchronous();
 
   printf("---------- asynchronous -----------\n");
   asynchronous();
 
   return 0;
 }

Thats it. Lets now see it in action:

 % ./asyncapp
 ---------- synchronous -----------
 23:08:49.429595 test1: Timeout
 23:08:49.609789 test2: system.sysDescr.0 = SunOS test2 5.6 Generic_105181-16 sun4u
 23:08:49.759717 test2: interfaces.ifNumber.1 = No Such Instance currently exists
 23:08:49.899715 test2: interfaces.ifNumber.0 = No Such Object available on this agent
 23:08:50.059725 test3: system.sysDescr.0 = Linux test3 2.2.5-22 #1 Wed Jun 2 09:17:03 EDT 1999 i686
 23:08:50.199715 test3: interfaces.ifNumber.1 = No Such Object available on this agent
 23:08:50.339712 test3: interfaces.ifNumber.0 = No Such Object available on this agent
 23:08:56.429595 test4: Timeout
 ---------- asynchronous -----------
 23:08:50.519702 test2: system.sysDescr.0 = SunOS test2 5.6 Generic_105181-16 sun4u
 23:08:50.569680 test3: system.sysDescr.0 = Linux test3 2.2.5-22 #1 Wed Jun 2 09:17:03 EDT 1999 i686
 23:08:50.669682 test2: interfaces.ifNumber.1 = No Such Instance currently exists
 23:08:50.699669 test3: interfaces.ifNumber.1 = No Such Object available on this agent
 23:08:50.809714 test2: interfaces.ifNumber.0 = No Such Object available on this agent
 23:08:50.879675 test3: interfaces.ifNumber.0 = No Such Object available on this agent
 23:08:56.429590 test1: Timeout
 23:08:56.430095 test4: Timeout
 %

Tutorial Sections

About the SNMP Protocol

These tutorial links talk about SNMP generically and how the protocol itself works. They are good introductory reading material and the concepts are important to understand before diving into the later tutorials about Net-SNMP itself.

Net-SNMP Command Line Applications

These tutorial pages discuss the command line tools provided in the Net-SNMP suite of tools. Nearly all the example commands in these tutorials works if you try it yourself, as they're all examples that talk to our online Net-SNMP test agent. Given them a shot!

Application Configuration

All of our applications support configuration to allow you to customize how they behave.

Net-SNMP Daemons

Net-SNMP comes with two long-running daemons: a SNMP agent (snmpd) for responding to management requests and a notification receiver (snmptrapd) for receiving SNMP notifications.

Coding Tutorials

Net-SNMP comes with a highly flexible and extensible API. The API allows you to create your own commands, add extensions to the agent to support your own MIBs and perform specialized processing of notifications.

Debugging SNMP Applications and Agents

All our tools and applications have extensive debugging output. These tutorials talk about how the debugging system works and how you can add your own debugging statements to you code:

Operating System Specific Tutorials