Example program

The following example program, monitor.c, demonstrates the steps outlined in the previous section. Commentary for each step follows the example.

/*monitor.c
 ** Example program showing logic flow of Monitor Client Library
 ** application. This example assumes the use of an ANSI C
 ** compliant compiler. This program creates two connections
 ** to the Monitor Server. Data is extracted from one connection
 ** at the beginning and end of the monitoring session.
 ** Data is extracted from the other connection every
 ** SAMPLE_INTERVAL seconds NUM_OF_SAMPLES times.
 */
#include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
/* The mcpublic.h header file contains function prototypes, etc. 
 ** for monitor client library functions. It also includes a 
 ** header file called mctypes.h, which defines the datatypes 
 ** used for monitor client library applications.
 */
#include "mcpublic.h"
#define NUM_OF_SAMPLES 10
 #define SAMPLE_INTERVAL 5
 #define NUM_SERVER_DATA_ITEMS 3
 #define NUM_DB_INFO_ITEMS 14
 #define NUM_NW_INFO_ITEMS 6
 #define OPTIONAL_CALLS -1

/*Error signals*/
 #define VIEW_NONEXISTENT -1
 #define CONNECT_NONEXISTENT -1

SMC_RETURN_CODE main (SMC_INT argc, SMC_CHARP argv[])
 {
  SMC_VALUE_UNION serverNameUnion;
   SMC_VALUE_UNION userNameUnion;
   SMC_VALUE_UNION passwordUnion;
   SMC_VALUE_UNION interfacesFileUnion;
   SMC_VALUE_UNION workUnion;
   SMC_VALUE_UNION returnedDataUnion;
   SMC_CONNECT_ID connect1_id;
   SMC_CONNECT_ID connect2_id;
   SMC_VIEW_ID server_view_id;
   SMC_VIEW_ID db_info_view_id;
   SMC_VIEW_ID nw_info_view_id;
   
  SMC_RETURN_CODE ret;
   SMC_DATAITEM_TYPE dataitem_type;  /*Holds data item type
                                     returned by get_dataitem_type
                                function call*/
/*Needed if alarms and filters are used */
 #ifdef OPTIONAL_CALLS
   SMC_ALARM_ID   alarm_id;
   SMC_FILTER_ID  filter_id;
   SMC_CHARP      filter_strings[2]; /*datatype is pointer to
                                     string. This is an array
                                     of pointers.*/
 #endif
   SMC_SIZET  row,num_of_rows,item;  /*This is an integer data
                                     type*/
   SMC_SIZET  outputLength;          /*Length of output returned
                                     by smc_connect_props
                                function call*/
/*
 ** Definition of SMC_DATAITEM_STRUCT datatype
 */
   SMC_DATAITEM_STRUCT  server_info_view[NUM_SERVER_DATA_ITEMS];
   SMC_DATAITEM_STRUCT  db_info_view[NUM_DB_INFO_ITEMS];
   SMC_DATAITEM_STRUCT  nw_bytes_view[NUM_NW_INFO_ITEMS];
 
   SMC_VALUE_UNION   server_data[NUM_SERVER_DATA_ITEMS];
   SMC_VALUE_UNION   db_data[NUM_DB_INFO_ITEMS];
   SMC_VALUE_UNION   nw_data[NUM_NW_INFO_ITEMS];
     
/*Callback function prototypes. Actual functions are defined
 ** below.
 */
  SMC_VOID errorCallback(SMC_CONNECT_ID,SMC_COMMAND_ID,SMC_VOIDP);
  SMC_VOID alarmCallback(SMC_CONNECT_ID,SMC_COMMAND_ID,SMC_VOIDP);
 
  SMC_BOOL explicitInterfacesFile = FALSE;
 
  int index,iterations;

/*
 ** These are labels used when printing out data returned by the
 ** database info view.
 */
   SMC_CHARP db_info_labels[NUM_DB_INFO_ITEMS] = {
     "Database ID:  ",
     "Object ID:  ",
     "Database name:  ",
     "Object name:  ",
     "Page hit percent:  ",
     "Page I/O:  ",
     "Page logical reads this sample:  ",
     "Page logical reads this session:  ",
     "Page logical read rate this sample:  ",
     "Page logical read rate this session:  ",
     "Page physical reads this sample:  ",
     "Page physical reads this session:  ",
     "Page physical read rate this sample:  ",
     "Page physical read rate this session:  "
   };

/*
 ** These are labels used when printing out data returned by
 ** network info view.
 */
   SMC_CHARP nw_info_labels[NUM_NW_INFO_ITEMS] = {
     "Network bytes received this sample:  ",
     "Network bytes received this session:  ",
     "Network bytes sent this sample:  ",
     "Network bytes sent this session:  ",
     "Network byte I/O rate this sample:  ",
     "Network byte I/O rate this session:  "
   };
   if (argc <5){
     printf("Usage <%s> -U <user_name> [-P <password>]\
           -S <monserver name> [-I <interfaces_file>]\n",argv[0]);
     exit(1);
   }
/*
** Connect to a server.
*/

For commentary, see “Step 2: connect to a server”

/*
** Allocate first connection
*/
   ret=smc_connect_alloc(errorCallback,
                         &connect1_id   /*Pointer to connect_id!*/
                         );
   if (ret != SMC_RET_SUCCESS) {
     printf("Attempt to allocate first connection failed \
             with error %d.\n",ret);
     exit(1);
   }
/*
 ** Allocate second connection
 */
   ret=smc_connect_alloc(errorCallback,
                         &connect2_id   /*Pointer to connect_id!*/
                         );
   if (ret != SMC_RET_SUCCESS) {
     printf("Attempt to allocate second connection failed \
             with error %d.\n",ret);
     exit(1);
   }
/*
** Set mandatory and some optional connection properties.
** Mandatory connection properties are user name, server name,
 ** and password if user password is not NULL. If interfaces
 ** file name is not set, default is "interfaces" in directory
 ** pointed to by $SYBASE environment variable.

For commentary, see “Required connection properties”.

*/
  
  for (index=1;index<argc;index++) {
/*User name*/
    if (strncmp(argv[index],"-U",2) == 0) {
       userNameUnion.stringValue = argv[index+1];
       ret=smc_connect_props(connect1_id,
                             SMC_PROP_ACT_SET, /*Property action*/
                             SMC_PROP_USERNAME,/*Property*/
                             &userNameUnion, /*Note that union,
                                             not member of union,
                                             is used for
                                             property value*/
                             SMC_NULLTERM,   /*Indicates null-
                                             terminated string
                                             for buffer length*/
                             NULL            /*Use NULL when 
                                             setting a property*/
                             );
     }                            /*End if argument is user name*/
   if (ret != SMC_RET_SUCCESS) {
     printf("Could not set user name.\n");
     exit(SMC_RET_FAILURE);
   }
 /*Password. Default password is a null string*/
    if (strncmp(argv[index],"-P",2) == 0) {
       passwordUnion.stringValue = argv[index+1];
       ret=smc_connect_props(connect1_id,
                             SMC_PROP_ACT_SET, /*Property action*/
                             SMC_PROP_PASSWORD,/*Property*/
                             &passwordUnion,  /*Note that union,
                                              not member of union,
                                              is used for
                                              property value*/
                             SMC_NULLTERM,    /*Indicates null-
                                              terminated string
                                              for buffer length*/
                             NULL             /*Use NULL when 
                                              setting a property*/
                             );
     }                             /*End if argument is password*/
   if (ret != SMC_RET_SUCCESS) {
     printf("Could not set password.\n");
     exit(SMC_RET_FAILURE);
   }
/*Server name*/
     if (strncmp(argv[index],"-S",2) == 0) {
       serverNameUnion.stringValue = argv[index+1];
       ret=smc_connect_props(connect1_id,
                            SMC_PROP_ACT_SET,  /*Property action*/
                            SMC_PROP_SERVERNAME,/*Property*/
                            &serverNameUnion, /*Note that union,
                                              not member of union,
                                              is used for 
                                              property value*/
                             SMC_NULLTERM,    /*Indicates null-
                                              terminated string
                                              for buffer length*/
                             NULL             /*Use NULL when
                                              setting a property*/
                             );
     }                        /*End if argument is server name*/
   if (ret != SMC_RET_SUCCESS) {
     printf("Could not set server name.\n");
     exit(SMC_RET_FAILURE);
   }
 /*Interfaces file. If unspecified, $SYBASE/interfaces is used*/
     if (strncmp(argv[index],"-I",2) == 0) {
       interfacesFileUnion.stringValue = argv[index+1];
       ret=smc_connect_props(connect1_id,
                            SMC_PROP_ACT_SET,  /*Property action*/
                            SMC_PROP_IFILE,    /*Property*/
                            &interfacesFileUnion, /*Note that
                                                 pointer to union,
                                                 not member of
                                                 union,is used for 
                                                 property value*/
                             SMC_NULLTERM,     /*Indicates null-
                                               terminated string
                                               for buffer length*/
                             NULL              /*Use NULL when 
                                              setting a property*/
                             );
       explicitInterfacesFile = TRUE;
     }             /*End if argument is interfaces file pathname*/
   if (ret != SMC_RET_SUCCESS) {
     printf("Could not set interfaces file name.\n");
     printf("Using default interfaces file.\n");
   }
   }              /*End for loop getting connection properties
               from command-line arguments*/
/*
 ** Optional smc_get_connect_props call that sets a pointer to be
 ** passed to error callback. In this case, the pointer is to a
 ** string that tells which connection encountered the error.
 */
   workUnion.voidpValue = "first connection"; /*Call to set user
                                              data handle looks
                                              for value to set in
                                              void pointer member
                                              of union.*/
   ret=smc_connect_props(connect1_id,SMC_PROP_ACT_SET,\
                  SMC_PROP_USERDATA,&workUnion,SMC_NULLTERM,NULL);
   if (ret != SMC_RET_SUCCESS){
   printf("smc_connect_props call failed to \
           set userDataHandle.\n");
  }
/*
 ** Demonstration of "get" mode for smc_get_connect_props
 */
 /*Check if user name has been set*/
  ret=smc_connect_props(connect1_id,
                     SMC_PROP_ACT_GET,/*Property action is "get"*/
                     SMC_PROP_USERNAME,
                     &workUnion,
                     SMC_UNUSED,     /*Length parameter ignored
                                       on "get" operations*/
                     &outputLength   /*Note this is a pointer!*/
                         );
  if (ret != SMC_RET_SUCCESS) {
    printf ("Could not get user name. Execution continuing.\n");
  }
  else {
    if (outputLength == 0) {
      printf("User name not set.  Quitting execution.\n");
      exit(SMC_RET_FAILURE);
    }
   else {
/*
 ** Application is responsible for freeing
 ** memory allocated to string member of SMC_VALUE_UNION by
 ** library.
 */
    free(workUnion.stringValue);
   }
   }
 /*Check if server name has been set*/
  ret=smc_connect_props(connect1_id,
                     SMC_PROP_ACT_GET,/*Property action is "get"*/
                     SMC_PROP_SERVERNAME,
                     &workUnion,
                     SMC_UNUSED,      /*Length parameter ignored
                                      on "get" operations*/
                     &outputLength    /*Note this is a pointer!*/
                         );
   if (ret != SMC_RET_SUCCESS) {
    printf ("Could not get server name. Execution continuing.\n");
   }
   else {
     if (outputLength == 0) {
       printf("Server name not set. Quitting execution.\n");
      exit(SMC_RET_FAILURE);
     }
     else {
       free(workUnion.stringValue);
     }
   }
/*
 ** Allocate properties for second connection. No need to 
 ** repeat error checking.
 */
  ret=smc_connect_props(connect2_id,SMC_PROP_ACT_SET, \
             SMC_PROP_USERNAME,&userNameUnion,SMC_NULLTERM, NULL);
   if (ret != SMC_RET_SUCCESS) {
     printf("Could not set user name for second connection.\n");
     exit(SMC_RET_FAILURE);
   }
   ret=smc_connect_props(connect2_id,SMC_PROP_ACT_SET, \
             SMC_PROP_PASSWORD,&passwordUnion,SMC_NULLTERM,NULL);
   if (ret != SMC_RET_SUCCESS) {
     printf("Could not set password for second connection.\n");
     exit(SMC_RET_FAILURE);
   }
   ret=smc_connect_props(connect2_id,SMC_PROP_ACT_SET, \
          SMC_PROP_SERVERNAME,&serverNameUnion,SMC_NULLTERM,NULL);
   if (ret != SMC_RET_SUCCESS) {
     printf("Could not set server name for second connection.\n");
     exit(SMC_RET_FAILURE);
   }
   if (explicitInterfacesFile) {
     ret=smc_connect_props(connect2_id,SMC_PROP_ACT_SET, \
           SMC_PROP_IFILE,&interfacesFileUnion,SMC_NULLTERM,NULL);
    if (ret != SMC_RET_SUCCESS) {
     printf("Could not set server name for second connection.\n");
     exit(SMC_RET_FAILURE);
     }
   }
/*
 ** Optional smc_connect_props call to set user-defined pointer to
 ** be passed to error callback. This pointer points to a 
 ** string that tells where the error callback was triggered.
 */
 workUnion.voidpValue = "second connection"; /*Call to set user
                                             data handle looks for
                                             value to set in void
                                             pointer member 
                                             of union.*/
 ret=smc_connect_props(connect2_id,SMC_PROP_ACT_SET, \
                  SMC_PROP_USERDATA,&workUnion,SMC_NULLTERM,NULL);
 if (ret != SMC_RET_SUCCESS){
 printf("smc_connect_props call failed to set userDataHandle.\n");
   }
/*
** Connect to monitor server

For commentary, see “Connecting to a server”

*/
 /*
 ** First connection
 */
   ret=smc_connect_ex(connect1_id);
   if (ret != SMC_RET_SUCCESS) {
    printf("First connection failed to connect to \
          monitor server.\n");
    exit(SMC_RET_FAILURE);
   }
/*
 ** Second connection
 */
   ret=smc_connect_ex(connect2_id);
   if (ret != SMC_RET_SUCCESS) {
     printf("Second connection failed to connect to \
          monitor server.\n");
     exit(SMC_RET_FAILURE);
   }
/*
 ** Create views on connections.
 */

For commentary, see “Step 3: create a view”

** Define views. 
/*
** Each data item must be paired with a 
 ** statistic type . View definitions are used in create_view 
 ** calls after connecting to monitor server.
 */
 /*This is a server-wide view that returns one row of data*/
 server_info_view[0].dataItemName =SMC_NAME_SQL_SERVER_NAME;
  server_info_view[0].dataItemStatType = SMC_STAT_VALUE_SAMPLE;
  server_info_view[1].dataItemName = SMC_NAME_SQL_SERVER_VERSION;
  server_info_view[1].dataItemStatType = SMC_STAT_VALUE_SAMPLE;
  server_info_view[2].dataItemName = SMC_NAME_TIMESTAMP;
  server_info_view[2].dataItemStatType = SMC_STAT_VALUE_SAMPLE;
/*
 ** This is a view with key and result data items that returns
 ** multiple rows of data.
 */
db_info_view[0].dataItemName = SMC_NAME_DB_ID; /*Key data items*/
 db_info_view[0].dataItemStatType = SMC_STAT_VALUE_SAMPLE;
 db_info_view[1].dataItemName = SMC_NAME_OBJ_ID; 
 db_info_view[1].dataItemStatType = SMC_STAT_VALUE_SAMPLE;
 db_info_view[2].dataItemName = SMC_NAME_DB_NAME;    /*Result data
                                                     items*/
 db_info_view[2].dataItemStatType = SMC_STAT_VALUE_SAMPLE;
 db_info_view[3].dataItemName = SMC_NAME_OBJ_NAME;
 db_info_view[3].dataItemStatType = SMC_STAT_VALUE_SAMPLE;
 db_info_view[4].dataItemName = SMC_NAME_PAGE_HIT_PCT;
 db_info_view[4].dataItemStatType = SMC_STAT_VALUE_SAMPLE;
 db_info_view[5].dataItemName =SMC_NAME_PAGE_IO;
 db_info_view[5].dataItemStatType = SMC_STAT_VALUE_SAMPLE;
 db_info_view[6].dataItemName = SMC_NAME_PAGE_LOGICAL_READ;
 db_info_view[6].dataItemStatType = SMC_STAT_VALUE_SAMPLE;
 db_info_view[7].dataItemName = SMC_NAME_PAGE_LOGICAL_READ;
 db_info_view[7].dataItemStatType = SMC_STAT_VALUE_SESSION;
 db_info_view[8].dataItemName = SMC_NAME_PAGE_LOGICAL_READ;
 db_info_view[8].dataItemStatType = SMC_STAT_RATE_SAMPLE;
 db_info_view[9].dataItemName = SMC_NAME_PAGE_LOGICAL_READ;
 db_info_view[9].dataItemStatType = SMC_STAT_RATE_SESSION;
 db_info_view[10].dataItemName = SMC_NAME_PAGE_PHYSICAL_READ;
 db_info_view[10].dataItemStatType = SMC_STAT_VALUE_SAMPLE;
 db_info_view[11].dataItemName = SMC_NAME_PAGE_PHYSICAL_READ;
 db_info_view[11].dataItemStatType = SMC_STAT_VALUE_SESSION;
 db_info_view[12].dataItemName = SMC_NAME_PAGE_PHYSICAL_READ;
 db_info_view[12].dataItemStatType = SMC_STAT_RATE_SAMPLE;
 db_info_view[13].dataItemName = SMC_NAME_PAGE_PHYSICAL_READ;
 db_info_view[13].dataItemStatType = SMC_STAT_RATE_SESSION;
/*
 ** Another server-wide view
 */
   nw_bytes_view[0].dataItemName = SMC_NAME_NET_BYTES_RCVD;
   nw_bytes_view[0].dataItemStatType = SMC_STAT_VALUE_SAMPLE;
   nw_bytes_view[1].dataItemName = SMC_NAME_NET_BYTES_RCVD;
   nw_bytes_view[1].dataItemStatType = SMC_STAT_VALUE_SESSION;
   nw_bytes_view[2].dataItemName = SMC_NAME_NET_BYTES_SENT;
   nw_bytes_view[2].dataItemStatType = SMC_STAT_VALUE_SAMPLE;
   nw_bytes_view[3].dataItemName = SMC_NAME_NET_BYTES_SENT;
   nw_bytes_view[3].dataItemStatType = SMC_STAT_VALUE_SESSION;
   nw_bytes_view[4].dataItemName = SMC_NAME_NET_BYTE_IO;
   nw_bytes_view[4].dataItemStatType = SMC_STAT_RATE_SAMPLE;
   nw_bytes_view[5].dataItemName = SMC_NAME_NET_BYTE_IO;
   nw_bytes_view[5].dataItemStatType = SMC_STAT_RATE_SESSION;

ret=smc_create_view (connect1_id,      /*Connect ID assigned when
                                        connect allocated*/
                      server_info_view, /*This is a pointer to
                                     array of SMC_DATAITEM_STRUCTS
                                     which defines the view*/
                      NUM_SERVER_DATA_ITEMS, /*No. of items in
                                             the view*/
                      "server info view",    /*Ignored on a live
                                             connection*/
                      &server_view_id        /*Value is assigned
                                             by this call*/
                      ); 
 if (ret != SMC_RET_SUCCESS) {               /*Cleanup from failed
                                             create_view call*/
   ret=smc_connect_drop(connect1_id);        /*Create view failed
                                             so no further use for
                                             this connection*/
   connect1_id = CONNECT_NONEXISTENT;
 }
 /*
 ** The second connection will have two views
 */
   ret=smc_create_view(connect2_id,db_info_view,NUM_DB_INFO_ITEMS,
                       "db info view",&db_info_view_id);
   if (ret != SMC_RET_SUCCESS) {
     db_info_view_id = VIEW_NONEXISTENT;
   }
   ret=smc_create_view(connect2_id,nw_bytes_view,NUM_NW_INFO_ITEMS,
                       "nw bytes view",&nw_info_view_id);
   if (ret != SMC_RET_SUCCESS) {
     nw_info_view_id = VIEW_NONEXISTENT;
   }
/*
** Create a filter.
*/

For commentary, see “Step 4: create filters”

/*
 ** Filters and alarms may be applied to data items within a view.
 ** This is optional.
 ** In this case, we only want to see I/O activity for a 
 ** particular database and tempdb. If any physical reads occur,
 ** an alarm is triggered that posts a message to the screen.
 */
#ifdef OPTIONAL_CALLS
   filter_strings[0] = "my_db";     /*Change to db of interest*/
   filter_strings[1] = "tempdb";
   workUnion.voidpValue = filter_strings;
   ret=smc_create_filter(connect2_id,       /*Connection id*/
                         db_info_view_id,   /*View id*/
                         &db_info_view[2],  /*Pointer to a data
                                            item within the view
                                            to be filtered*/
                         SMC_FILT_T_EQ,     /*Type of filter*/
                         &workUnion,        /*Filter value*/
                         2,                 /*Number of elements
                                            in array of filter
                                            values*/
                         SMC_DI_TYPE_CHARP, /*datatype of filter
                                            values*/
                         &filter_id         /*Value is assigned by
                                            this function call*/
                         );
   if (ret != SMC_RET_SUCCESS) {
     printf("Filters were not applied. Continuing execution.\n");
   }
/*
** Set alarms.
*/

For commentary, see “Step 5: set alarms”

workUnion.longValue = 1;                   /*Value above which
                                              alarm is triggered*/
   ret=smc_create_alarm_ex(connect2_id,       /*Connection id*/
                           db_info_view_id,   /*View id*/
                           &db_info_view[11], /*Pointer to a data
                                              item within the view
                                              to which the alarm
                                              is applied*/
                           &workUnion,        /*Where value that
                                              triggers the alarm
                                              is located*/
                           SMC_DI_TYPE_LONG,  /*datatype of item
                                              to which alarm is
                                              applied*/
                           SMC_ALARM_A_NOTIFY,/*Trigger alarm
                                              callback function.
                                              This is the only
                                              action possible when
                                              the server mode is
                                              LIVE.*/
                           NULL,    /*For server mode HISTORICAL,
                                    this is where log file to be
                                    written to or program to be
                                    run is specified. For server
                                    mode LIVE, this field is
                                    ignored.*/

/*The following is a string that is passed to the alarm callback function.*/
                         "Physical read occurred in database.",
                          alarmCallback,     /*Alarm callback
                                             function*/
                          &alarm_id          /*Variable into which
                                             alarm id is placed.*/
                          );
   if (ret != SMC_RET_SUCCESS) {
     printf("Alarm was not applied. Execution continuing.\n");
   }
 #endif
/*
** Request data and process results.
*/

For commentary, see “Step 6: request performance data and process results”

/*
 ** Get data from first connection. As server name and version
 ** do not change during the connection, we only get it once.
 ** Post the time when the refresh was done.
 */
   if (connect1_id != CONNECT_NONEXISTENT) { /*If the connect is
                                              not successful,the
                                              error callback is
                                              triggered. For a
                                              friendlier display,
                                              we check first.*/
     ret=smc_refresh_ex(connect1_id,         /*ID of connect*/
                        0                    /*STEP not used in
                                             live connection*/
                        );
     if (ret != SMC_RET_SUCCESS) {
       printf("refresh call failed on first connect ID.\n");
    }
    else {                 /*Check row count even though only one
                           row is expected in this case. If no
                           rows are returned, get_dataitem_value
                           calls will return errors.*/
     ret=smc_get_row_count(connect1_id,
                         server_view_id,
                         #_of_rows);
     if (ret != SMC_RET_SUCCESS){
      printf("Get row count call failed.\n");
     }
     else {
      if (num_of_rows > 0){
/*
 ** A get_dataitem_value call is made for each item in the view.
 ** The retrieved data is stored in an array of SMC_VALUE_UNIONs.
 */
          for (index=0;index <NUM_SERVER_DATA_ITEMS;index++){
            ret=smc_get_dataitem_value(connect1_id,
                              server_view_id,
                              &server_info_view[index],/*Look at
                                                        each data
                                                        item in
                                                        the view*/
                              0,             /*Only one row of
                                             data is returned for
                                             this particular view,
                                             so the value for row
                                             is hard-coded in this
                                             case.*/
                               &server_data[index]   /*Retrieved
                                                      data stored
                                                      here*/
                               );
           }                             /*End for loop*/
/*
 ** Display the returned data.
 */
         printf("Adaptive Server Enterprise name is: \
                 %s.\n",server_data[0].stringValue);
          printf("Adaptive Server Enterprise version is: \
                 %s.\n",server_data[1].stringValue);
          printf("Date and time is: \
               %s.\n",server_data[2].stringValue);
/*
 ** The application is responsible for freeing memory allocated
 ** by the Monitor Client Library for string members of 
 ** SMC_VALUE_UNIONs. This also illustrates the use of the
 ** smc_get_dataitem_type function call.
 */
  for (index=0;index <NUM_SERVER_DATA_ITEMS;index++){
    ret=smc_get_dataitem_type(&server_info_view[index], \
                              &dataitem_type);
    if (ret != SMC_RET_SUCCESS) {
      printf("Get dataitem type failed for item %d \
               in server_info_view.\n");
    }
    else {
      if (dataitem_type == SMC_DI_TYPE_CHARP) {
        free(server_data[index].stringValue);
      }
    }
  }                /*End for loop*/
         }         /*End if number of rows > 0*/
       }           /*End case get_row_count was successful*/
     }             /*End case smc_refresh_ex call was successful*/
   }               /*End case connect still valid*/
 /*
 ** Get the data from the views in the second connection to see
 ** how the data changes over time. To do this, we sample 
 ** NUM_OF_SAMPLES times, pausing SAMPLE_INTERVAL times between 
 ** each sample. The process of retrieving data is within a loop. 
 */
  for (iterations=0;iterations<NUM_OF_SAMPLES;iterations++){
     sleep(SAMPLE_INTERVAL);
     ret=smc_refresh_ex(connect2_id,     /*Note second connection
                                         specified for refresh*/
                        0                /*Step not used in live
                                         connection*/
                        );
     if (ret == SMC_RET_SUCCESS) {
       if (db_info_view_id != VIEW_NONEXISTENT){ /*Attempting
                                                 get_row_count for
                                                 nonexistent view
                                                 will cause errors
                                                 so check if view
                                                 was actually
                                                 created*/
         ret=smc_get_row_count(connect2_id,
                               db_info_view_id,
                               #_of_rows   /*Multiple rows will
                                              be returned. For
                                              each row of data
                                              returned, use
                                              get_dataitem_value
                                              loop. Function call
                                              puts number of rows
                                              returned into
                                              variable.*/
                             );
         for(row=0;row<num_of_rows;row++){
           for (index=0;index <NUM_DB_INFO_ITEMS;index++){
             ret=smc_get_dataitem_value(connect2_id,
                            db_info_view_id, /*View specified for
                                             get_dataitem_value.*/
                            &db_info_view[index],
                            row,              /*Multiple rows in
                                              this case */
                            &db_data[index]
                            );
             if (ret != SMC_RET_SUCCESS) {
               printf("Get dataitem value failed for data item \
                       %s.\n",db_info_labels[index]);
             }
             else {
               printf("%s",db_info_labels[index]);
               ret=smc_get_dataitem_type(&db_info_view[index],\
                                       &dataitem_type);
               if (ret != SMC_RET_SUCCESS){
                 printf("Get data item type failed for data item \
                         %s.\n",db_info_view[index]);
               }
               else {
                 switch (dataitem_type) {
                 case SMC_DI_TYPE_CHARP:
                   printf("%s.\n",db_data[index].stringValue);
                   free(db_data[index].stringValue); 
                   /*Application is responsible for freeing
                   memory allocated for strings by library*/
                   break;
                 case SMC_DI_TYPE_LONG:
                   printf("%d.\n",db_data[index].longValue);
                   break;
                 case SMC_DI_TYPE_DOUBLE: /*Rates are generally
                                        floating point variables*/
                   printf("%f.\n",db_data[index].doubleValue);
                   break;
                 default:
                   printf("Unknown datatype encountered.\n");
                   break;
                 }    /*End switch*/
               }      /*End case get_dataitem_type successful*/
             }        /*End case get_dataitem_value successful*/
           }          /*End for loop to get each data item value*/
         }            /*End for loop to get each row of data*/
       }           /*End case view exists*/
/*
 ** Retrieve data from second view in refresh.
 ** Processing is much the same.
 */
       if (nw_info_view_id != VIEW_NONEXISTENT){ /*Attempting
                                                 get_row_count for
                                                 nonexistent view
                                                 causes errors, so
                                                 check to see if
                                                 view was actually
                                                 created*/
         ret=smc_get_row_count(connect2_id,
                               nw_info_view_id,
                               #_of_rows    /*This is a server-
                                               wide view so only
                                               one row should be
                                               returned*/
                             );
         if (num_of_rows > 0 ){
           for (index=0;index <NUM_NW_INFO_ITEMS;index++){
             ret=smc_get_dataitem_value(connect2_id,
                               nw_info_view_id, /*Note view
                                                specified for
                                              get_dataitem_value*/
                               &nw_bytes_view[index],
                               0,         /*One row in this case*/
                               &nw_data[index]
                               );
             if (ret != SMC_RET_SUCCESS) {
               printf("Get dataitem value failed for data item \
                       %s.\n",nw_info_labels[index]);
             }
             else {
               printf("%s",nw_info_labels[index]);
               ret=smc_get_dataitem_type(&nw_bytes_view[index],\
                                         &dataitem_type);
               if (ret != SMC_RET_SUCCESS){
                 printf("Get data item type failed for data item \
                         %s.\n",nw_bytes_view[index]);
               }
               else {
                 switch (dataitem_type) {
                 case SMC_DI_TYPE_CHARP:
                   printf("%s.\n",nw_data[index].stringValue);
                   free(nw_data[index].stringValue);
                   /*Application is responsible for freeing
                   memory allocated for strings by library*/
                   break;
                 case SMC_DI_TYPE_LONG:
                   printf("%d.\n",nw_data[index].longValue);
                   break;
                 case SMC_DI_TYPE_DOUBLE:    /*Rates are generally
                                             floating point
                                             variables*/
                   printf("%f.\n",nw_data[index].doubleValue);
                   break;
                 default:
                   printf("Unknown datatype encountered.\n");
                   break;
                 }    /*End switch*/
               }      /*End case get_dataitem_type successful*/
             }        /*End case get_dataitem_value successful*/
           }          /*End for loop to get each data item value*/
         }            /*End if any rows of data returned*/
         else {
           printf("No data returned for network info view.\n");
         }
       }             /*End case view exists*/
     }               /*End case refresh successful*/
     else {
       printf("Refresh of second connect failed. \
               Return code is %d.\n",ret);
     }
   }                 /*End for loop for number of iterations*/
/*
 ** This shows how to drop filters and alarms. It is not necessary
 ** to do this prior to closing a connection, as it is done 
 ** automatically when the connection is closed. Filters may be 
 ** dropped, for example, to see the filtered results of a query 
 ** followed by the unfiltered results.
 */
 #ifdef OPTIONAL_CALLS
   ret=smc_drop_filter(connect2_id,db_info_view_id,filter_id);
   if (ret != SMC_RET_SUCCESS) {
     printf("Attempt to drop filter failed.\n");
   }
   ret=smc_drop_alarm(connect2_id,db_info_view_id,alarm_id);
   if (ret != SMC_RET_SUCCESS) {
     printf("Attempt to drop alarm failed.\n");
   }
 #endif
/*
 ** Get another time stamp before disconnecting. To do this,
 ** do a refresh on the first connection again and only display
 ** the time stamp data returned.
 */
   if (connect1_id != CONNECT_NONEXISTENT) { 
     ret=smc_refresh_ex(connect1_id,0 );
     if (ret != SMC_RET_SUCCESS) {
       printf("refresh call failed on first connect ID.\n");
     }
     else {                          /*Check row count even though
                                     only one row is expected. If
                                     no rows are returned,
                                     get_dataitem_value calls
                                     will return errors.*/
       ret=smc_get_row_count(connect1_id,
                             server_view_id,
                             #_of_rows);
       if (ret != SMC_RET_SUCCESS){
         printf("Get row count call on first connection \
                 failed.\n");
       }
       else {
         if (num_of_rows > 0){
             ret=smc_get_dataitem_value(connect1_id,
                             server_view_id,
                             &server_info_view[2],  /*In this case
                                                    we are only
                                                    interested in
                                                    the third data
                                                    item*/
                             0,            /*Only one row of data
                                           is returned for this
                                           particular view, so the
                                           value for row is hard-
                                           coded in this case.*/
                             &server_data[2]
                             );
             printf("Date and time on conclusion of monitoring:\
                     %s\n",server_data[2].stringValue);
             free(server_data[2].stringValue);  
             /*Application must free string memory returned
             by library*/
           }                 /*End if row of data returned*/
       }                     /*End case get_row_count successful*/
     }                       /*End case refresh successful*/
   }                         /*End case connection exists*/

/*
** Close and deallocate the connection.
*/

For commentary, see “Step 7: close and deallocate connections”

/*
 ** Cleanup. This consists of closing all connections, then 
 ** de-allocating them. Alternatively, connections can be re-used.
 */
   ret=smc_close(connect1_id,
                 SMC_CLOSE_REQUEST        /*Close only if no
                                          outstanding commands
                                          (only close request type
                                          currently supported)*/
                 );
   if (ret != SMC_RET_SUCCESS) {
     printf("Attempt to close first connection failed. \
             Return code is %d.\n",ret);
   }
   ret=smc_close(connect2_id,SMC_CLOSE_REQUEST);
   if (ret != SMC_RET_SUCCESS) {
     printf("Attempt to close second connection failed. \
             Return code is %d.\n",ret);
   }
/*
 ** Connections can be re-used at this point, for example, to
 ** connect to different servers. However, we de-allocate them.
 */
   ret=smc_connect_drop(connect1_id);
   if (ret != SMC_RET_SUCCESS){
     printf("Attempt to drop first connection failed. \
             Return code is %d.\n",ret);
   }
   ret=smc_connect_drop(connect2_id);
   if (ret != SMC_RET_SUCCESS){
     printf("Attempt to drop second connection failed. \
             Return code is %d.\n",ret);
   }
   return(SMC_RET_SUCCESS);
 }                                         /*End main*/
/*
** Callback functions

For commentary, see “Step 1: define error handling”.

*/
 SMC_VOID errorCallback(
   SMC_CONNECT_ID connectID,
   SMC_COMMAND_ID commandID,        /*Value internal to Monitor
                                    Client Library*/
   SMC_VOIDP userDataHandle         /*User-defined pointer. Set by
                                    smc_connect_propscall*/
   )
 {
   SMC_SIZET          ret;
   SMC_VALUE_UNION    errorInfo;    /*Used for getting information
                                    from smc_get_command_info
                                    function call*/
   SMC_SIZET          returned_msg_length;
   printf ("Inside new error callback.\n");
/*
 ** Use smc_get_command_info function call to get information
 ** from error and alarm callbacks.
 */
   ret=smc_get_command_info(connectID,
                         commandID,
                         SMC_INFO_ERR_MAPSEVERITY, /*Information
                                                   requested about
                                                   command*/
                         &errorInfo,         /*Where information
                                             returned about
                                             command is placed*/
                         NULL                /*Value is numeric
                                             so length of returned
                                       data not needed*/
                        );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call requesting error map \
             severity failed. Error returned is:  %d\n",ret);
     }
   else{
     printf("Monitor Client Library error severity level is: \
             %d\n",errorInfo.sizetValue);
   }
   ret=smc_get_command_info(connectID,
                           commandID,
                           SMC_INFO_ERR_MSG,
                           &errorInfo,
                           &returned_msg_length      /*Find string
                                                     length */
                                                                                             );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call requesting error message \
             failed. Error returned is:  %d\n",ret);
   }
   else{
     printf("Error message text is:  %s\n",errorInfo.stringValue);
     free(errorInfo.stringValue);
     /*Application is responsible for freeing string buffer
     memory allocated by library*/
   }
   ret=smc_get_command_info(connectID,
                            commandID,
                            SMC_INFO_ERR_NUM,
                            &errorInfo,
                            NULL
                            );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call requesting error number \ 
             failed. Error returned is:  %d\n",ret);
   }
   else{
     printf("Error number is: %d\n",errorInfo.sizetValue);
  }
   ret=smc_get_command_info(connectID,
                            commandID,
                            SMC_INFO_ERR_SEVERITY,
                            &errorInfo,
                            NULL
                            );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call requesting error severity \
             failed.  Error returned is:  %d\n",ret);
   }
   else{
     printf("Error severity level is: %d\n",errorInfo.sizetValue);
   }
   ret=smc_get_command_info(connectID,
                            commandID,
                            SMC_INFO_ERR_SOURCE,
                            &errorInfo,
                            NULL
                            );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call requesting error source \
             failed.  Error returned is:  %d\n",ret);
   }
   else{
     printf(" Error source is:  %d\n",errorInfo.sizetValue);
   }
   ret=smc_get_command_info(connectID,
                            commandID,
                            SMC_INFO_ERR_STATE,
                            &errorInfo,
                            NULL
                            );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call requesting state failed. \
             Error returned is: %d\n",ret);
   }
   else{
     printf(" Error state is:  %d\n",errorInfo.sizetValue);
   }
/*
 ** Demonstrate use of userDataHandle. This value was set as a
 ** connection property for the connection in the main program and
 ** is passed to this function.
 */
   if (userDataHandle != NULL){
     printf("Connection on which error occurred is \ 
             %s.\n",userDataHandle);
   }
 }                                          /*End errorCallback */
 /*Alarm callback*/
 SMC_VOID alarmCallback(
   SMC_CONNECT_ID connectID,
   SMC_COMMAND_ID commandID,           /*Value internal to Monitor
                                       Client Library*/
   SMC_VOIDP userDataHandle
   )
 {
 #define MSG_BUFFER_LENGTH 80
   SMC_SIZET          ret;
   SMC_VALUE_UNION    alarmInfo;      /*Union into which requested
                                      data is placed*/
   SMC_SIZET          returned_msg_length;
   printf ("Alarm callback triggered.\n");
/*
 ** Use smc_get_command_info function call to get information 
 ** from error and alarm callbacks.
 */
   ret=smc_get_command_info(connectID,
                            commandID,
                            SMC_INFO_ALARM_ALARMID,
                            &alarmInfo,
                            NULL
                            );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call failed.  \
             Error returned is:  %d",ret);
   }
   else{
     printf("Alarm ID is:  %d\n",alarmInfo.sizetValue);
   }
/*
 ** This demonstrates the use of the SMC_INFO_ALARM_VALUE_DATATYPE
 ** information that might be useful in a generic alarm callback
 ** function.
 */
   ret=smc_get_command_info(connectID,
                            commandID,
                            SMC_INFO_ALARM_VALUE_DATATYPE,
                            &alarmInfo,
                            NULL
                            );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call failed. \
             Error returned is:  %d",ret);
   }
   else{
     switch(alarmInfo.intValue){
     case SMC_DI_TYPE_INT:
       ret=smc_get_command_info(connectID,
                                commandID,
                                SMC_INFO_ALARM_CURRENT_VALUE,
                                &alarmInfo,
                                NULL
                                );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call failed.  \
             Error returned is:  %d",ret);
   }
   else {
     printf("Current value of alarmed data item is:\
             %d.\n",alarmInfo.intValue);
   }
   break;
     case SMC_DI_TYPE_LONG:
       ret=smc_get_command_info(connectID,
                                commandID,
                                SMC_INFO_ALARM_CURRENT_VALUE,
                                &alarmInfo,
                                NULL
                                );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call failed.  \
             Error returned is:  %d",ret);
   }
   else {
     printf("Current value of alarmed data item is: \
             %d.\n",alarmInfo.longValue);
   }
   break;
     case SMC_DI_TYPE_DOUBLE:
       ret=smc_get_command_info(connectID,
                                commandID,
                                SMC_INFO_ALARM_CURRENT_VALUE,
                                &alarmInfo,
                                NULL
                                );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call failed.  Error returned is:  %d",ret);
   }
   else {
     printf("Current value of alarmed data item is: \
             %f.\n",alarmInfo.doubleValue);
   }
   break;
     default:
     printf("Invalid value returned for datatype of \
             current alarm value.\n");
     break;
     }                                              /*End switch*/
   }
   ret=smc_get_command_info(connectID,
                            commandID,
                            SMC_INFO_ALARM_ROW,
                            &alarmInfo,
                            NULL
                            );
  if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call failed.  \
             Error returned is:  %d",ret);
   }
   else{
     printf("Row of data which triggered alarm is: \
             %d\n",alarmInfo.sizetValue);
   }
   ret=smc_get_command_info(connectID,
                            commandID,
                            SMC_INFO_ALARM_VALUE_DATATYPE,
                            &alarmInfo,
                            NULL
                            );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call failed.  \
             Error returned is:  %d",ret);
   }
   else{
     switch(alarmInfo.intValue){
     case SMC_DI_TYPE_INT:
       ret=smc_get_command_info(connectID,
                                commandID,
                                SMC_INFO_ALARM_THRESHOLD_VALUE,
                                &alarmInfo,
                                NULL
                                );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call failed. \
             Error returned is:  %d",ret);
   }
   else {
     printf("Value of data item exceeded alarm-triggering \
             value of: %d.\n",alarmInfo.intValue);
   }
   break;
     case SMC_DI_TYPE_LONG:
       ret=smc_get_command_info(connectID,
                                commandID,
                                SMC_INFO_ALARM_THRESHOLD_VALUE,
                                &alarmInfo,
                                NULL
                                );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call failed. \
             Error returned is:  %d",ret);
   }
   else {
     printf("Value of data item exceeded alarm-triggering \
             value of: %d.\n",alarmInfo.longValue);
   }      
   break;
     case SMC_DI_TYPE_DOUBLE:
       ret=smc_get_command_info(connectID,
                                commandID,
                                SMC_INFO_ALARM_THRESHOLD_VALUE,
                                &alarmInfo,
                                NULL
                                );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call failed.  \
             Error returned is:  %d",ret);
   }
   else {
     printf("Value of data item exceeded alarm-triggering\
             value of: %f.\n",alarmInfo.doubleValue);
   } 
   break;
     default:
     printf("Invalid value returned for datatype of \
             THRESHOLD alarm value.\n");
     break;
     }                                              /*End switch*/
   }
   ret=smc_get_command_info(connectID,
                            commandID,
                            SMC_INFO_ALARM_TIMESTAMP,
                            &alarmInfo,
                            &returned_msg_length
                            );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call failed.  \
             Error returned is:  %d",ret);
    }
   else{
     printf("Time when alarm was triggered is: \
             %s\n",alarmInfo.stringValue);
     free(alarmInfo.stringValue); /*Application is responsible
                                  for freeing string buffer memory
                                  allocated by library.*/
   }
     
   ret=smc_get_command_info(connectID,
                            commandID,
                            SMC_INFO_ALARM_VIEWID,
                            &alarmInfo,
                            NULL
                            );
   if (ret != SMC_RET_SUCCESS){
     printf("get_command_info call failed.  \
             Error returned is:  %d",ret);
   }
   else{
     printf("ID of view which triggered alarm is: \
             %d.\n",alarmInfo.sizetValue);
   }
 }                               /*End newAlarmCallback*/