Logo
Home page Net-SNMP

Archive Search:

Require all words?

Site Search:
Google
Net-SNMP Tutorial -- Asynchronous Demo Application

Net-SNMP Tutorial -- Asynchronous Demo Application

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.

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

FileDescription
MakefileA simple Makefile to build the application
asyncapp.cThe C source code

This discussion assumes that you alread 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 simplicitys 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 hold to 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
%
    

Valid CSS!


Last modified: Wednesday, 01-Aug-2018 04:41:28 UTC
For questions regarding web content and site functionality, please write to the net-snmp-users mail list.