[ Previous | Next | Table of Contents | Index | Legal | ]

Performance Toolbox Version 2 and 3 Guide and Reference


Chapter 19. Remote Statistics Interface Programming Guide

This chapter provides information about the Remote Statistics Interface.


Remote Statistics Interface API Overview

An application programming interface (API) is available for those who want to develop programs that access the statistics available from one or more xmservd daemons. The API is called the Remote Statistics Interface or the RSI Interface. This chapter describes how you use the RSI Interface API by walking you through a couple of sample programs. Those sample programs, and others, are provided in machine-readable form as well. The sample programs can be found in the /usr/samples/perfmgr directory.

Use the RSI Interface API to write programs that access one or more xmservd daemons. This allows you to develop programs that print, post-process, or otherwise manipulate the raw statistics provided by the xmservd daemons. Such programs are known as Data-Consumer programs.

AIX 5L Version 5.1 Technical Reference: Communications Volume 2 must be installed to see the RSi subroutines.

Makefiles

The included files are based upon a number of define directives being properly set. They are usually defined with the -D preprocessor flag. The following define directives should be in effect for compiling on AIX 3.2 and AIX Version 4.

A Makefile to build all the sample programs provided could look like the one shown in the following listing:

LIBS = -L./ -lbsd -lSpmi
CC = cc
CFLAGS = -D_BSD -D_AIX
 
all:: RsiCons RsiCons1 chmon
 
RsiCons: RsiCons.c
      $(CC) -o RsiCons RsiCons.c $(CFLAGS) $(LIBS)
 
RsiCons1: RsiCons1.c
      $(CC) -o RsiCons1 RsiCons1.c $(CFLAGS) $(LIBS)
 
chmon: chmon.c $
      $(CC) -o chmon chmon.c $(CFLAGS) $(LIBS) -lcurses

If the system on which you compile doesn't support ANSI function prototypes, add the following flag:

-D_NO_PROTO

Remote Statistics Interface List of Subroutines

As interesting and useful as it may be to watch the graphics display of statistics shown by xmperf, many other uses of the wealth of statistics are available through the xmservd daemons on all the hosts in a network. The Remote Statistics Interface API allows you to create data-consumer programs that can get full access to the statistics of any host's xmservd daemon.

The RSI interface consists of several groups of subroutines that are discussed in the following section.

Initialization and Termination


RSiInit Allocates or changes the table of RSI handles.
RSiOpen Initializes the RSI interface for a remote host.
RSiClose Terminates the RSI interface for a remote host and releases all memory allocated.
RSiInvite Invites data suppliers on the network to identify themselves and returns a table of data-supplier host names.

Instantiation and Traversal of Context Hierarchy


RSiInstantiate Creates (instantiates) all subcontexts of a context object.
RSiPathGetCx Searches the context hierarchy for a context that matches a context path name.
RSiFirstCx Returns the first subcontext of a context.
RSiNextCx Returns the next subcontext of a context.
RSiFirstStat Returns the first statistic of a context.
RSiNextStat Returns the next statistic of a context.

Defining Sets of Statistics to Receive


RSiAddSetHot Adds a single set of peer statistics to a hotset.
RSiCreateHotSet Creates an empty hotset.
RSiCreateStatSet Creates an empty statset.
RSiPathAddSetStat Adds a single statistic to a statset.
RSiDelSetHot Deletes a single set of peer statistics from a hotset.
RSiDelSetStat Deletes a single statistic from a statset.
RSiStatGetPath Finds the full path name of a statistic identified by an SpmiStatVals pointer.

Starting, Changing and Stopping Data Feeding


RSiStartFeed Tells xmservd to start sending data feeds for a statset.
RSiStartHotFeed Tells xmservd to start sending hot feeds for a hotset.
RSiChangeFeed Tells xmservd to change the time interval between sending data feeds for a statset.
RSiChangeHotFeed Tells xmservd to change the time interval between sending hot feeds for a hotset.
RSiStopFeed Tells xmservd to stop sending data feeds for a statset.
RSiStopHotFeed Tells xmservd to stop sending hot feeds for a hotset.

Receiving and Decoding Data Feed Packets


RSiGetHotItem Returns the peer context name and data value for the first (next) SpmiHotItems element by extraction from data feed packet.
RSiMainLoop Allows an application to suspend execution and wait to get waked up when data feeds arrive.
RSiGetValue Returns data value for a given SpmiStatVals pointer by extraction from data feed packet.
RSiGetRawValue Returns a pointer to a valid SpmiStatVals structure for a given SpmiStatVals pointer by extraction from data feed packet.

RSI Interface Concepts and Terms

Before you start using the RSI interface API you need to be aware of the format and use of the RSI interface data structures. This section explains the structures and also introduces you to the commonalities of the library functions and to some important design concepts. This section has the following subsections:

RSI Interface Data Structures

The RSI interface is based upon control blocks (data structures) that describe the current view of the statistics on a remote host and the state of the interaction between a data consumer program and the remote host's xmservd daemon. Data structures to know about are as follows:

RSI handle
An RSI handle is a pointer to a data structure of type RSiHandleStruct. Prior to using any other RSI call, a data-consumer program must use the RSiInit subroutine to allocate a table of RSI handles. An RSI handle from the table is initialized when you open the logical connection to a host and that RSI handle must be specified as an argument on all subsequent subroutines to the same host. Only one of the internal fields of the RSI handle should be used by the data-consumer program, namely the pointer to received network packets, pi. Only in very special cases will you ever need to use this pointer, which is initialized by RSiOpen and must never be modified by a data-consumer program. If your program changes any field in the RSI handle structure, results are highly unpredictable. The RSI handle is defined in /usr/include/sys/Rsi.h.

SpmiStatVals
A single data value is represented by a structure defined in /usr/include/sys/Spmidef.h as struct SpmiStatVals. Be aware that none of the fields defined in the structure must be modified by application programs. The two handles in the structure are symbolic references to contexts and statistics and should not be confused with pointers. The last three fields are updated whenever a data_feed packet is received. These fields are as follows:

val The latest actual contents of the statistics data field.
val_change The difference (delta value) between the latest actual contents of the statistics data field and the previous value observed.
error An error code as defined by the enum Error in include file /usr/include/sys/Spmidef.h.

Notice that the two value fields are defined as union Value, which means that the actual data fields may be long or float, depending on flags in the corresponding SpmiStat structure. The SpmiStat structure cannot be accessed directly from the StatVals structure (the pointer is not valid, as previously mentioned). Therefore, to determine the type of data in the val and val_change fields, you must have saved the SpmiStat structure as returned by the RSiPathAddSetStat subroutine. This is rather clumsy, so the RSiGetValue subroutine does everything for you and you do not need to keep track of SpmiStat structures.

The SpmiStat structure is used to describe a statistic. It is defined in /usr/include/sys/Spmidef.h as type struct SpmiStat. If you ever need information from this data structure (apart from information that can be returned by the RSiStatGetPath subroutine) be sure to save it as it is returned by the RSiPathAddSetStat subroutine.

The RSiGetRawValue subroutine provides another way of getting access to an SpmiStat structure but can only do so while a data feed packet is being processed.

SpmiStatSet The xmservd daemon accepts the definition of sets of statistics that are to be extracted simultaneously and sent to the data-consumer program in a single data packet. The structure that describes such a set of statistics is defined in /usr/include/sys/Spmidef.h as of type struct SpmiStatSet. As returned by the RSiCreateStatSet, the SpmiStatSet pointer should be treated as a handle whose only purpose is to identify the correct set of statistics to several other subroutines.

When returned in a data feed packet, the SpmiStatSet structure holds the actual time the data feed packet was created (according to the remote host's clock) and the elapsed time since the latest previous data feed packet for the same SpmiStatSet was created.

SpmiHotSet (SpmiHotSet Structure)Represents another set of access structures that allow an application program to define an alternative way of extracting and processing metrics. They are used to extract data values for the most or least active statistics for a group of peer contexts. For example, it can be used to define that the program wants to receive information about the two highest loaded disks, optionally subject to the load exceeding a specified threshold.

When the SPMI receives a read request for an SpmiHotSet, the SPMI reads the latest value for all the peer sets of statistics in the hotset in one operation. This action reduces the system overhead caused by access of kernel structures and other system areas, and ensures that all data values for the peer sets of statistics within a hotset are read at the same time. The hotset may consist of one or many sets of peer statistics.

SpmiHotVals One SpmiHotVals structure is created for each set of peer statistics selected for the hotset. When the SPMI executes a request from the application program to read the data values for a hotset, all SpmiHotVals structures in the set are updated. The RSi application program can then traverse the list of SpmiHotVals structures by using the RSiGetHotItem subroutine call.

The SpmiHotVals structure carries the data values from the SPMI to the application program. Its data carrying fields are:

error Returns a zero value if the SPMI's last attempt to read the data values for a set of peer statistics was successful. Otherwise, this field contains an error code as defined in the sys/Spmidef.h file.
avail_resp Used to return the number of peer statistic data values that meet the selection criteria (threshold). The field max_responses determines the maximum number of entries actually returned.
count Contains the number of elements returned in the array items. This number will be the number of data values that met the selection criteria (threshold), capped at max_responses.
items The array used to return count elements. This array is defined in the SpmiHotItems data structure. Each element in the SpmiHotItems array has the following fields:

name
The name of the peer context for which the values are returned.

val
Returns the value of the counter or level field for the peer statistic. This field returns the statistic's value as maintained by the original supplier of the value. However, the val field is converted to an SPMI data format.

val_change
Returns the difference between the previous reading of the counter and the current reading when the statistic contains counter data. When this value is divided by the elapsed time returned in the SpmiHotSet Structure, an event rate-per-time-unit can be calculated.

The RSI Request-Response Interface

The RSI interface API has two distinctly different ways of operation. This section describes the RSI request-response protocol that sends a single request to xmservd and waits for a response. A timeout occurs if no response has been received within a specified time limit in which case one single retry is attempted. If the retry also results in a timeout, that fact is communicated to the caller by placing the constant RSiTimeout in the external integer field RSiErrno. If any other error occurred, the external integer field has some other non-zero value.

If neither a communications error nor a timeout occurred, a packet is available in the receive buffer pointed to by the pi pointer in the RSI handle. The packet includes a status code that tells whether the subroutine was successful at the xmservd side. You need only be concerned with checking the status code in a packet if it matters what exactly it is because the constant RSiBadStat is placed in RSiErrno to indicate to your program that a bad status code was received.

You can use the indication of error or success as defined for each subroutine to determine if the subroutine succeeded or you can test the external integer RSiErrno. If this field is RSiOkay the subroutine succeeded; otherwise it did not. The error codes returned in RSiErrno are defined in the enum RSiErrorType.

All the library functions use the request-response interface, except for RSiMainLoop (which uses a network driven interface) and RSiInit, RSiGetValue, and RSiGetRawValue (that do not involve network traffic).

The RSI Network Driven Interface

The xmquery protocol, which is described in detail in "The xmservd Interface", defines three types of data packets that are sent from the data supplier side (xmservd) without being solicited by a request packet. Those packet types are the still_alive, the data_feed, and the except_rec packets. The still_alive packets are handled internally in the RSI interface and require no programming in the data-consumer program.

The data_feed packets are received asynchronously with any packets produced by the request-response type subroutines. If a data_feed packet is received when processing a request-response function, control is passed to a callback function, which must be named when the RSI handle is initialized with the RSiOpen subroutine.

When the data-consumer program is not using the request-response functions, it still needs to be able to receive and process data_feed packets. This is done with the RSiMainLoop function, which invokes the callback function whenever a packet is received.

Actually, the data feed callback function is invoked for all packets received that cannot be identified as a response to the latest request sent, except if such packets are of type i_am_back, still_alive, or except_rec. Note that this means that responses to "request-response" packets that arrive after a timeout is sent to the callback function. It is the responsibility of your callback function to test for the packet type received.

The except_rec packets are received asynchronously with any packets produced by the request-response type subroutines. If an except_rec packet is received when processing a request-response function, control is passed to a callback function, which must be named when the RSI handle is initialized with the RSiOpen subroutine.

When the data-consumer program is not using the request-response functions, it still needs to be able to receive and process except_rec packets. This is done with the RSiMainLoop function which invokes the callback function whenever a packet is received.

Note that the API discards except_rec messages from a remote host unless a callback function to process the message type was specified on the RSiOpen subroutine call for that host.

Resynchronizing

Network connections can go bad, hosts can go down, interfaces can be taken down and processes can die. In the case of the xmservd protocol, such situations usually result in one or more of the following:

Missing packets
Responses to outstanding requests are not received, which generate a timeout. That's fairly easy to cope with because the data-consumer program has to handle other error return codes anyway. It also results in expected data feeds not being received. Your program may want to test for this happening. The proper way to handle this situation is to use the RSiClose function to release all memory related to the dead host and to free the RSI handle. After this is done, the data-consumer program may attempt another RSiOpen to the remote system or may simply exit.

Resynchronizing requests
Whenever an xmservd daemon hears from a given data-consumer program on a particular host for the first time, it responds with a packet of type i_am_back, effectively prompting the data-consumer program to resynchronize with the daemon. Also, when the daemon attempts to reconnect to data-consumer programs that it talked to when it was killed or died, it sends an i_am_back packet.

It is important that you understand how the xmservd daemon handles "first time contacted." It is based upon tables internal to the daemon. Those tables identify all the data-consumers that the daemon knows about. Be aware that a data-consumer program is known by the host name of the host where it executes suffixed by the IP port number used to talk to the daemon. Each data-consumer program running is identified uniquely as are multiple running copies of the same data-consumer program.

Whenever a data-consumer program exits orderly, it alerts the daemon that it intends to exit and the daemon removes it from the internal tables. If, however, the data-consumer program decides to not request data feeds from the daemon for some time, the daemon detects that the data consumer has lost interest and removes the data consumer from its tables as described in Life and Death of xmservd. If the data-consumer program decides later that it wants to talk to the xmservd again, the daemon responds with an i_am_back packet.

The i_am_back packets are given special treatment by the RSI interface. Each time one is received, a resynchronizing callback function is invoked. This function must be defined on the RSiOpen subroutine.

Note that all data-consumer programs can expect to have this callback invoked once during execution of the RSiOpen subroutine because the remote xmservd does not know the data consumer. This is usual and should not cause your program to panic. If the resynchronize callback is invoked twice during processing of the RSiOpen function, the open failed and can be retried, if appropriate.


A Simple Data-Consumer Program

In this section, the use of the API is illustrated by creating a small data-consumer program to produce a continuous list of statistics from a host. The first version accesses only CPU-related statistics. It assumes you want to get your statistics from the local host unless you specify a host name on the command line. The program continues to display the statistics until it is killed. Source code for the sample program can be found in /usr/samples/perfmgr/RsiCons1.c.

Initializing and Terminating the Program

The main function of the sample program uses the three subroutines as shown in the following code segment. The lines 12 through 15 use any command line argument to override the default host name obtained by the uname function. Then lines 17 through 28 initialize the RSI interface using the RSiInit and RSiOpen subroutines. The program exits if the initialization fails.

[01] extern char   RSiEMsg[];
[02] extern int   RSiErrno;
[03] char   host[64], apath[256], head1[24][10], head2[24][10];
[04] char   *nptr, **navn = &nptr, *dptr, **desc = &dptr;
[05] struct utsname  uname_struct;
[06] RSiHandle   rsh;
[07] struct SpmiStatVals *svp[24];
[08] int     lct = 99, tix = 0;
[09]
[10] main(int argc, char **argv)
[11] {
[12]     uname(&uname_struct);
[13]     strcpy(host, uname_struct.nodename);
[14]     if (argc > 1)
[15]        strcpy(host, argv[1]);
[16]
[17]     if (!(rsh = RSiInit(1)))
[18]     {
[19]        fprintf(stderr, "Unable to initialize RSI interface\n");
[20]        exit(98);
[21]     }
[22]     if (RSiOpen(rsh, 100, 2048, host, feeding, resync, NULL))
[23]     {
[24]        if (strlen(RSiEMsg))
[25]           fprintf(stderr, "%s", RSiEMsg);
[26]        fprintf(stderr, "Error contacting host\"%s\"\n", host);
[27]        exit(-99);
[28]     }
[29]     signal(SIGINT, must_exit);
[30]     signal(SIGTERM, must_exit);
[31]     signal(SIGSEGV, must_exit);
[32]     signal(SIGQUIT, must_exit);
[33]
[34]     strcpy(apath, "hosts/");
[35]     strcat(apath, host);
[36]     strcat(apath, "/");
[37]     lststats(apath);
[38]     RSiClose(rsh);
[39]     exit(0);
[40] }

The following lines (29-32) make sure that the program detects any attempt to kill or terminate it. If this happens, the function must_exit is invoked. This function has the sole purpose of making sure the association with the xmservd daemon is terminated. It does this as shown in the following piece of code:

void must_exit()
{
   RSiClose(rsh);
   exit(-9);
}

Finally, lines 34 through 36 prepare an initial value path name for the main processing loop of the data-consumer program. This is the way all value path names should be prepared. After doing this, the main processing loop in the internal function lststats is called. If this function returns, issue an RSiClose call and exit the program.

Defining a Statset

Eventually, you want the sample of the data-consumer program to receive data feeds from the xmservd daemon. Thus, start preparing the SpmiStatSet, which defines the set of statistics with which you are interested. This is done with the RSiCreateStatSet subroutine.

[01] voidlststats(char *basepath)
[02] {
[03]    struct SpmiStatSet *ssp;
[04]    char     tmp[128];
[05]
[06]    if (!(ssp = RSiCreateStatSet(rsh)))
[07]    {
[08]       fprintf(stderr, "RsiCons1 can\'t create StatSet\n");
[09]       exit(62);
[10]    }
[11]
[12]    strcpy(tmp, basepath);
[13]    strcat(tmp, "CPU/cpu0");
[14]    if ((tix = addstat(tix, ssp, tmp, "cpu0")) == -1)
[15]    {
[16]       if (strlen(RSiEMsg))
[17]          fprintf(stderr, "%s", RSiEMsg);
[18]       exit(63);
[19]    }
[20]
[21]    RSiStartFeed(rsh, ssp, 1000);
[22]    while(TRUE)
[23]       RSiMainLoop(499);
[24] }

In the sample program, the SpmiStatSet is created in the local function lststats shown previously in lines 6 through 10.

Lines 12 through 19 invoke the local function addstat (Adding Statistics to the Statset), which finds all the CPU-related statistics in the context hierarchy and initializes the arrays to collect and print the information. The first two lines expand the value path name passed to the function by appending CPU/cpu0. The resulting string is the path name of the context where all CPU-related statistics for "cpu0" are held. The path name has the format hosts/hostname/CPU/cpu0 without a terminating slash, which is what is expected by the subroutines that take a value path name as an argument. The function addstat is shown in the next section. It uses three of the traversal functions to access the CPU-related statistics.

Adding Statistics to the Statset

[01] int addstat(int ix, struct SpmiStatSet *ssp, char *path, char *txt)
[02] {
[03]    cx_handle   *cxh;
[04]    int     i = ix;
[05]    char     tmp[128];
[06]    struct SpmiStatLink *statlink;
[07]
[08]    if (!(cxh = RSiPathGetCx(rsh, path)))
[09]    {
[10]       fprintf(stderr, "RSiPathGetCx can\'t access host %s (path %s)\n", host, path);
[11]       exit(61);
[12]    }
[13]
[14]    if ((statlink = RSiFirstStat(rsh, cxh, navn, desc)))
[15]    {
[16]       while (statlink)
[17]       {
[18]          if (i > 23)
[19]              break;
[20]          strcpy(head1[i], txt);
[21]          strcpy(head2[i], *navn);
[22]          strcpy(tmp, path);
[23]          strcat(tmp, "/");
[24]          strcat(tmp, *navn);
[25]          if (!(svp[i] = RSiPathAddSetStat(rsh, ssp, tmp)))
[26]             return(-1);
[27]          i++;
[28]          statlink = RSiNextStat(rsh, cxh, statlink, navn, desc);
[29]       }
[30]    }
[31]  return(i);
[32] }

The use of RSiPathGetCx by the sample program is shown in lines 8 through 12. Following that, in lines 14 through 30, two subroutines are used to get all the statistics values defined for the CPU context. This is done by using RSiFirstStat and RSiNextStat.

In lines 20-21, the short name of the context ("cpu0") and the short name of the statistic are saved in two arrays for use when printing the column headings. Lines 22-24 construct the full path name of the statistics value by concatenating the full context path name and the short name of the value. This is necessary to proceed with adding the value to the SpmiStatSet with the RSiPathAddSetStat. The value is added by lines 25 and 26.

<H3>Data-Consumer Initialization of Data Feeds

The only part of the main processing function in the main section yet to explain consists of lines 21 through 23. The first line simply tells the xmservd daemon to start feeding observations of statistics for an SpmiStatSet by issuing the RSiStartFeed subroutine call. The next two lines define an infinite loop that calls the function RSiMainLoop to check for incoming data_feed packets.

There are two more subroutines concerned with controlling the flow of data feeds from xmservd. Neither is used in the sample program. The subroutines are described in RSiChangeFeed and RSiStopFeed.

Data-Consumer Decoding of Data Feeds

Whenever a data_feed is detected by the RSI interface, the data feed callback function defined in the RSiOpen subroutine is invoked, passing the RSI handle as an argument to the callback function. The sample program's callback function for data feeds is shown in the following example. Most of the lines in the function are concerned with printing headings after each 20 detail lines printed. This is in line numbers 9 through 19 and 26.

[01] void feeding(RSiHandle rsh, pack *p)
[02] {
[03]    int  i;
[04]    float f;
[05]    long  v;
[06]
[07]    if (p->type != data_feed)
[08]       return;
[09]    if (lct > 20)
[10]    {
[11]       printf("\n\n");
[12]       for (i = 0; i < tix; i++)
[13]          printf("%08s", head1[i]);
[14]       printf("\n");
[15]       for (i = 0; i < tix; i++)
[16]          printf("%08s", head2[i]);
[17]       printf("\n");
[18]       lct = 0;
[19]    }
[20]    for (i = 0; i < tix; i++)
[21]    {
[22]       v = RSiGetValue(rsh, svp[i]) * 10.0;
[23]       printf("%6d.%d", v/10, v%10);
[24]    }
[25]    printf("\n");
[26]    lct++;
[27] }

Actual processing of received statistics values is done by the lines 20-24. It involves the use of the library subroutine RSiGetValue. The following is an example of output from the sample program RsiCons1 when executed against AIX 3.2.

$ RsiCons1 umbra
 
    cpu0    cpu0    cpu0    cpu0    cpu0    cpu0    cpu0    cpu0
    user    kern    wait    idle  uticks  kticks  wticks  iticks
     0.0     0.0     0.0   100.0     0.0     0.0     0.0   100.0
     0.0     0.0     0.0   100.0     0.0     0.0     0.0    99.9
     0.2     3.1     0.0    96.5     0.2     3.2     0.0    96.6
     3.5     5.5     1.5    89.1     3.5     5.5     1.5    89.1
     5.8     3.4     0.0    90.8     5.8     3.4     0.0    90.8
     8.8     8.3     0.1    82.5     8.8     8.3     0.2    82.5
    67.5     2.4     3.0    27.0    67.5     2.3     2.9    26.9
    16.0     0.6     0.8    82.5    16.0     0.6     0.8    82.6
    67.5     5.0     0.0    27.3    67.5     5.0     0.0    27.3
    19.0     6.1     0.9    73.8    19.1     6.1     0.9    73.8
    22.5     0.8     1.6    75.0    22.5     0.8     1.6    74.9
    60.2     6.1     0.0    33.5    60.2     6.1     0.0    33.5
$

An Alternative Way to Decode Data Feeds

If you need to know more about the data received in data_feed packets than what can be obtained using theRSiGetValue subroutine, you can use the library subroutine RSiGetRawValue.


Expanding the Data-Consumer Program

A slightly more capable version of the sample program discussed in the previous sections is provided as /usr/samples/perfmgr/RsiCons.c. This program also lists the statistics with the short name xfer for all the disks found in the system where the daemon runs. To do so, the program uses some additional subroutines to traverse contexts as described in the following section.

Traversing Contexts

The adddisk function in the following list shows how the RSiFirstCx, RSiNextCx, and the RSiInstantiate subroutines are combined with RSiPathGetCx to make sure all subcontexts are accessed. The sample program's internal function addstat is used to add the statistics of each subcontext to the SpmiStatSet in turn. A programmer who wanted to traverse all levels of subcontexts below a start context could easily create a recursive function to do this.

[01] int adddisk(int ix, struct SpmiStatSet *ssp, char *path)
[02] {
[03]    int     i = ix;
[04]    char     tmp[128];
[05]    cx_handle   *cxh;
[06]    struct SpmiStatLink *statlink;
[07]    struct SpmiCxLink *cxlink;
[08]
[09]    cxh = RSiPathGetCx(rsh, path);
[10]    if ((!cxh) || (!cxh->cxt))
[11]    {
[12]       if (strlen(RSiEMsg))
[13]          fprintf(stderr, "%s", RSiEMsg);
[14]       fprintf(stderr, "RSiPathGetCx can\'t access host %s (path %s)\n",
[15]       host, path);
[16]       exit(64);
[17]    }
[18]    if (rsh->pi->data.getcx.context.inst_freq == SiContInst)
[19]    {
[20]       if ((i = RSiInstantiate(rsh, cxh)))
[21]          return(-1);
[22]    }
[23]    if ((cxlink = RSiFirstCx(rsh, cxh, navn, desc)))
[24]    {
[25]       while (cxlink)
[26]       {
[27]         strcpy(tmp, path);
[28]         if (strlen(tmp))
[29]            strcat(tmp, "/");
[30]         if (*navn)
[31]            strcat(tmp, *navn);
[32]         if ((i = addstat(i, ssp, tmp, *navn)) == -1)
[33]         {
[34]            if (strlen(RSiEMsg))
[35]               fprintf(stderr, "%s", RSiEMsg);
[36]            exit(63);
[37]         }
[38]         cxlink = RSiNextCx(rsh, cxh, cxlink, navn, desc);
[39]       }
[40]    }
[41]    return(i);
[42] }

The output from the RsiCons program when xmservd runs on an AIX 4.1 host is shown in the following example.

$ RsiCons encee
 
     CPU     CPU     CPU     CPU  hdisk3  hdisk1  hdisk0     cd0
  uticks  kticks  wticks  iticks    xfer    xfer    xfer    xfer
    19.6    10.0     4.1    67.1     2.7     4.1     0.0     0.0
    10.9    15.3     8.2    65.3     0.0     8.2     0.0     0.0
     0.5     2.0     0.0    97.5     0.0     0.0     0.0     0.0
    10.5     4.0     0.0    85.5     0.0     0.0     0.0     0.0
    55.4     8.9     0.0    35.4     2.4     0.0     0.0     0.0
    19.0     5.5     0.0    75.5     0.0     0.0     0.0     0.0
     5.9     6.4     0.0    87.4     0.0     0.0     0.0     0.0
    10.5     7.0     0.0    82.5     0.0     0.0     0.0     0.0
     7.9     7.4     0.0    84.4     0.0     0.0     0.0     0.0
    88.5     8.5     3.0     0.0     9.5     4.5     0.0     0.0
    89.4     8.9     1.4     0.0     5.9     0.0     0.0     0.0
    92.5     5.5     2.0     0.0     9.0     8.5     0.0     0.0
    71.0     6.0    23.0     0.0    44.0    41.0     0.0     0.0
    37.9     2.4    58.9     0.4    67.9    61.4     0.0     0.0
    17.5     4.5     0.0    78.0     1.5     3.0     0.0     0.0
     0.5     1.5    10.0    88.0     7.5     1.5     0.0     0.0
$

Inviting Data Suppliers

Sometimes you want to design programs that can present the end user with a list of potential data-supplier hosts rather than requiring the user to specify which host to monitor. The RSiInvite allows you to create such programs.

Identifying Data Suppliers

The RSiInvite subroutine uses one or more of the following methods to obtain the Internet Protocol (IP) addresses to which an invitational are_you_there message can be sent. The last two methods depend on the presence of the $HOME/Rsi.hosts file. PTX also has alternative locations of the Rsi.hosts file. The three ways to invite data-supplier hosts are:

  1. Unless instructed not to by the user, the broadcast address corresponding to each of the network interfaces of the local host is found. The invitational message is sent on each network interface using the corresponding broadcast address. Broadcasts are not attempted on the Localhost (loopback) interface or on point-to-point interfaces such as X.25 or SLIP (Serial Line Interface Protocol) connections.
  2. If a list of Internet broadcast addresses is supplied in the file $HOME/Rsi.hosts, an invitational message is sent on each such broadcast address. Note that if you specify the broadcast address of a local interface, broadcasts are sent twice on those interfaces. You may want to use this as a feature in order to minimize the likelihood of the invitation being lost.
  3. If a list of host names is supplied in the file $HOME/Rsi.hosts, the host IP address for each host in the list is looked up and a message is sent to each host. The look-up is done through a gethostbyname() call, so that whichever name service is active for the host where the data-consumer application runs is used to find the host address.

The file $HOME/Rsi.hosts has a simple layout. Only one keyword is recognized and only if placed in column one of a line. That keyword is:

nobroadcast

and means that the are_you_there message should not be broadcast using method 1 shown previously. This option is useful in situations where a large number of hosts are on the network and only a well-defined subset should be remotely monitored. To say that you don't want broadcasts but want direct contact to three hosts, your $HOME/Rsi.hosts file might look like this:

nobroadcast
birte.austin.ibm.com
gatea.almaden.ibm.com
umbra

This example shows that the hosts to monitor do not necessarily have to be in the same domain or on a local network. However, doing remote monitoring across a low-speed communications line is unlikely to be popular; neither with other users of that communications line nor with yourself.

Be aware that whenever you want to monitor remote hosts that are not on the same subnet as the data-consumer host, you must specify the broadcast address of the other subnets or all the host names of those hosts in the $HOME/Rsi.hosts file. The reason is that IP broadcasts do not propagate through IP routers or gateways.

The following example illustrates a situation where you want to do broadcasting on all local interfaces, want to broadcast on the subnet identified by the broadcast address 129.49.143.255, and also want to invite the host called umbra. (The subnet mask corresponding to the broadcast address in this example is 255.255.240.0 and the range of addresses covered by the broadcast is 129.49.128.0 - 129.49.143.255.)

129.49.143.255

If the RSiInvite subroutine detects that the name server is inoperational or has abnormally long response time, it returns the IP addresses of hosts rather than the host names. If the name server fails after the list of hosts is partly built, the same host may appear twice, once with its IP address and once with its host name.

The execution time of the RSiInvite subroutine depends primarily on the number of broadcast addresses you place in the $HOME/Rsi.hosts file. Each broadcast address increases the execution time with roughly 50 milliseconds plus the time required to process the responses. The minimum execution time of the subroutine is roughly 1.5 seconds, during which time your application only gets control if callback functions are specified and if packets arrive that must be given to those callback functions.


A Full-Screen, Character-based Monitor

Another sample program written to the data-consumer API is the program chmon. Source code to the program is in /usr/samples/perfmgr/chmon.c. The chmon program is also stored as an executable during the installation of the Manager component. This program uses the API and the curses programming interface to create a screen full of statistics as shown in the following example:

Data-Consumer API       Remote Monitor for host     Tue Apr 14 09:09:05
1992
CHMON Sample Program       ***   birte  ***         Interval:      5 seconds
 
% CPU                                            EVENTS/QUEUES  FILE/TTY
Kernel  13.3   |####                           | Pswitch  1295  Readch 24589
User    23.7   |#######                        | Syscall  6173  Writech 1646
Wait     6.5   |##                             | Reads     487  Rawin      0
Idle    56.1   |################               | Writes    143  Ttyout   106
                                                 Forks       1  Igets   1763
PAGING counts   PAGING SPACE   REAL MEM  48MB    Execs       1  Namei    809
Faults    131   % Used  33.7   % Comp    68.0    Runqueue    1  Dirblk   174
Steals      0   % Free  66.2   % NonComp 15.0    Swapqueue   0  Reads     48
Reclaim     0   Size,MB   96   % Client   4.0                   Writes   143
 
PAGING page/s   DISK       Read  Write     %     NETWORK   Read  Write
Pgspin      0   ACTIVITY KB/sec  KB/sec  Busy    ACTIVITY KB/sec KB/sec
Pgspout     0   hdisk0      0.0    35.1  15.7    lo0         1.1    1.1
Pagein      0   hdisk1      0.0     0.0   0.0    tr0         1.1    0.0
Pageout    11   hdisk2      0.0     9.5   3.5
Sios       10   cd1         0.0     0.0   0.0
 
      Process wait     (514)   %cpu 63.2, PgSp: 0.0mb, uid:
      Process xlcentry (12657) %cpu 58.0, PgSp: 1.1mb, uid: birte
      Process make     (21868) %cpu 15.0, PgSp: 0.2mb, uid: birte
      Process make     (5998)  %cpu 15.0, PgSp: 0.1mb, uid: birte

The chmon command line is:

chmon[-iseconds_interval][-pno_of_processes][hostname>]

where:

seconds_interval Is the interval between observations. Must be specified in seconds. No blanks must be entered between the flag and the interval. Defaults to 5 seconds.
no_of_processes Is the number of "hot" processes to be shown. A process is considered "hotter" the more CPU it uses. No blanks must be entered between the flag and the count field. Defaults to 0 (no) processes.
hostname Is the host name of the host to be monitored. Default is the local host.

The sample program exits after 2,000 observations have been taken, or when you type the letter "q" in its window.


List of RSi Error Codes

All RSI subroutines use constants to define error codes. The RSI Error Code table lists the error descriptions.

Symbolic Name Number Description
RSiTimeout 280 A time-out occurred while waiting for a response to a request.
RSiBusy 281 An RSiOpen subroutine was issued, but another is already active.
RSiSendErr 282 An error occurred when the library attempted to send a UDP packet with the sendto() system call.
RSiPollErr 283 A system error occurred while issuing or processing a poll() or select() system call.
RSiRecvErr 284 A system error occurred while attempting to read an incoming UDP packet with the recvfrom() system call.
RSiSizeErr 285 A recvfrom() system call returned a UDP packet with incorrect length or incorrect source address.
RSiResync 286 While waiting for a response to an outgoing request, one of the following occurred and cause an error return to the calling program:
  1. An error occurred while processing an exception packet.
  2. An error occurred while processing an i_am_back packet.
  3. An i_am_back packet was received in response to an output request other than are_you_there.
  4. While waiting for a response to an outgoing request, some asynchronous function closed the handle for the remote host.

The code may also be set when a success return code is returned to the caller, in which case it shows that either an exception packet or an i_am_back packet was processed successfully while waiting for a response.

RSiBadStat 287 A bad status code was received in the data packet received.
RSiBadArg 288 An argument that is not valid was passed to an RSi subroutine.
RSiBadHost 289 A valid host address cannot be constructed from an IP address or the nameservice doesn't know the hostname.
RSiDupHost 290 An RSiOpen call was issued against a host but a connection is already open to a host with this IP address and a different hostname.
RSiSockErr 291 An error occurred while opening or communicating with a socket.
RSiNoPort 292 The RSi is unable to find the port number to use when inviting remote suppliers. The likely cause is that the xmquery entry is missing from the /etc/services file or the NIS (Yellow Pages) server.
RSiNoMatch 293 One of the following occurred:
  1. The SpmiStatVals argument on the RSiStatGetPath call is not valid.
  2. On an RSiPathAddSetStat call, the SpmiStatSet argument is not valid or the path name given in the last argument does not exist.
  3. On an RSiAddSetHot call, the SpmiHotSet argument is not valid, the grand parent context doesn't exist or none of its subcontexts contain the specified statistic.
  4. On an RSiDelSetStat call, the SpmiStatSet or the SpmiStatVals argument is not valid.
  5. On an RSiDelSetHot call, the SpmiHotSet or the SpmiHotVals argument is not valid.
  6. On an RSiPathGetCx call, the path name given does not exist.
  7. On an RSiGetValue or RSiGetRawValue call, the SpmiStatVals argument is not valid
  8. On an RSiGetHotItem call, the SpmiHotSet argument was not valid.
RSiInstErr 294 An error was returned when attempting to instantiate a remote context.
RSiNoFeed 295 When extracting a data value with the RSiGetValue call, the data value was marked as not valid by the remote data supplier.
RSiTooMany 296 An attempt was made to add more values to a statset than the current buffer size permits.
RSiNoMem 297 Memory allocation error.
RSiNotInit 298 An RSi call was attempted before an RSiInit call was issued.
RSiNoLicense 299 License expired or no license found.
RSiNotSupported 300 The subroutine call requires a later protocol version that the one supported by the remote system's xmservd.


[ Previous | Next | Table of Contents | Index | Library Home | Legal | ]