# Compaq ACMS for OpenVMS
Remote Systems Management Guide

Previous | Contents | Index

# Chapter 6
Management Programming Using ONC RPC

Programmers who want to access and maintain the ACMS Remote Manager from their own programs can use the following two interfaces:

- Simple Network Management Protocol (SNMP)
  The SNMP interface is provided for integration with enterprise management packages such as PATROL® from BMC® and Tivoli from IBM®. For more information, see Chapter 7.
- Open Network Computing (ONC) Remote Procedure Call (RPC)
  The ONC RPC interface is for system managers and system programmers who want to write custom tools and applications that access the ACMS Remote Manager.

This chapter describes the ONC RPC interface. Programmers who are familiar with the C programming language and RPC mechanisms can use this information when coding and building their own client programs. For a more complete discussion of ONC RPC programming, see Power Programming with RPC by John Bloomer, published by O'Reilly & Associates, Inc., Sebastopol, CA.

# 6.1 ONC RPC Overview

ONC RPC is a widely used and supported remote procedure call (RPC) mechanism. Similar to other RPC mechanisms, the ONC RPC protocol supports a request/response model, in which client applications make requests of servers and receive responses. Clients typically make synchronous calls to remote servers over a network. The RPC mechanism hides the network operations from the programmer, making each remote procedure call appear to be a local function invocation.

Unlike the SNMP interface, which connects to the ACMS Remote Manager using the SNMP master agent, access through ONC RPC is directly to the ACMS Remote Manager.

Figure 6-1 provides a graphical overview of the ONC RPC interface.

Figure 6-1 ONC RPC Interface Overview

Programming for ONC RPC is based on interface definitions coded in Interface Definition Language (IDL). Functions and their arguments are described in IDL source files, which are precompiled using an IDL compiler. The outputs from the IDL compiler are a set of C source and header files that are then compiled and linked with client and server programs to form run-time executables. (For Remote Manger client development, server stub files are not needed and can be discarded.)

Figure 6-2 provides a graphical overview of programming for ONC RPC.

Figure 6-2 ONC RPC Programming Overview

The IDL that describes the procedures supported by the ACMS Remote Manager is provided with the ACMS Remote Manager installation and provides the basis for ACMS management programming. Users write their own client programs, calling the functions described in the ACMS Remote Manager IDL file (ACMSMGMT_RPC.X). They precompile the IDL file with the precompiler provided by their TCP/IP package, and then compile and link their client programs. No compilation or linking is required for the Remote Manager; it contains all the support required by ONC RPC client programs.

The ACMS Remote Manager provides several types of procedures that are callable through the ONC RPC interface. These procedures provide read and write access to each table maintained by the Remote Manager, as well as command routines (such as start and stop). Table 6-1 summarizes the types of procedures available.

Table 6-1 Procedures for Accessing Remote Manager Functions
Procedure Type | Table or Object | Description
--- | --- | ---
Add | Collection, Error Filter, Trap | Allows entries to be added to configuration tables.
Delete | Collection, Error Filter, Trap | Allows entries to be removed from configuration tables.
Get | ACC, Error Filter, Manager (MGR) Status, Parameters, QTI, TSC, Version | Returns all columns in the table.
List | Collection, CP, Error Data, EXC, Interfaces, Log, Process, Server, Task Group, Trap, Users | Returns a linked list of records based on selection criteria. All columns in the table are returned with each row.
Replace | Server | Allows an application server to be replaced.
Reset | Error Data, Log | Allows the current version of the Remote Manager log or error log to be closed and a new version to be opened.
Save | Error Filter | Saves the current error filter records for a specific node and writes them to an error filter file.
Set | ACC, Collection, CP, EXC, Interfaces, Parameters, QTI, Server, Trap, TSC | Allows modifications to the table. For configuration tables, set functions allow rows to be added to tables. (Entity rows can only be added by starting the appropriate process.)
Start | ACC, EXC, QTI, Trace Monitor, TSC | Allows ACMS processes to be started.
Stop | Manager, ACC, EXC, QTI, TSC, Trace Monitor | Allows ACMS processes to be stopped.

The procedure names and arguments for each procedure type are similar --- all get calls have similar names and arguments; set calls have similar names and arguments, and so on.

The sections that follow describe in more detail how to write programs that access these functions.

# 6.2 Building Multithreaded Clients

The file ACMS$MGMT_EXAMPLES_BUILD.COM describes how to use the client stub provided with the Remote Manager when compiling and linking your application. The sample procedures provided in ACMS$MGMT_EXAMPLES.C are designed to use the thread-safe client stub, and as a result, each procedure contains one or more "free" calls (to prevent memory leaks).

To implement a non-thread safe client using the RPC-generated stub, omit the "free" calls. See ACMS$MGMT_EXAMPLES_BUILD.COM for detailed build instructions.

# 6.3 API Overview

Remote management client programs follow a typical programming model that involves the following phases:

- Initialization
  During the initialization phase, client programs establish connections with the Remote Managers they will be calling. As part of this phase, the programs select a security mode (explicit or implicit). Once this phase is complete, the Remote Managers have been verified to be available, and the client authentication has been verified. This phase involves using a combination of ONC RPC function calls and an ACMS Remote Manager function call (if explicit authentication is being used).
- Processing
  During the processing phase, client programs make procedure calls to the Remote Managers. During this phase, clients obtain or modify management information. This phase involves the use of the functions defined in the ACMS$RM_EXAMPLES:ACMSMGMT_RPC.X IDL file.
- Termination
  During the termination phase, clients halt execution. There is no API support or programming requirement for this phase.

# 6.4 Initialization and Security

In order to perform initialization, ACMS remote client programs must first determine the type of authentication (explicit or implicit) they will use. The type of authentication determines whether or not the client program must obtain credentials.

The Remote Manager performs authentication either explicitly, using a valid OpenVMS account name and password, or implicitly, using ACMS proxies. Implicit authentication is allowed only if it has been enabled on the Remote Manager node, and does not require the use of credentials. Explicit authentication requires the use of credentials and also requires that the client process execute a separate login using the ACMSMGR utility.

See Section 4.4 for a discussion of the various security modes and how to log in using ACMSMGR.

Once the authentication mode has been determined, remote management clients perform the following tasks:

- Establish an RPC connection with the Remote Manager on the target node.
  The clnt_create function call establishes RPC client connections.
- Establish the security context and, optionally, populate it with credentials information.
  The security context is established by calling the authunix_create_default function. As a result of this call, client process-identity information is passed to the server on each procedure call. The Remote Manager uses this information to authorize the user for each function.
  The default security context is not sufficient if explicit authentication is being used. Clients that need to support explicit authentication call the acms$mgmt_get_credentials function to obtain a client ID, which was previously issued for the client process by executing a login through the ACMSMGR utility. This client ID is used on subsequent RPC calls.

  Note In order for credentials information to be created, the client process must first execute the login command of the ACMSMGR utility. The only way to create credentials files is by using the ACMSMGR utility.

## 6.4.1 Initialization Example

The following example code shows a client program that establishes an RPC connection with the Remote Manager, establishes the security context, and then populates it with credentials information if a logical name (ACMS$MGMT_USER) has been defined.

    #include <rpc/rpc.h>
    #include string
    #include "acmsmgmt_rpc.h"

    CLIENT *cl;
    char sname[] = "sparks";
    char *username_p, username[13] = "";
    int client_id;
    int status;

    int acms$mgmt_get_creds();

    int main ()
    {

        /* if the logical is defined, credential information will be used */
        username_p = getenv("ACMS$MGMT_USER");
        if (username_p)
              strcpy(username,username_p);

        /* establish an rpc connection to the server */
        cl = clnt_create(sname, ACMSMGMT_RPC, ACMSMGMT_VERSION, "tcp");

        /* if the connection was established */
        if (cl != NULL) {

     /* create a security context */
            cl->cl_auth = authunix_create_default();
            client_id = 0;

     /* optionally, get credentials for this user & server */
            if (strlen(username))
               status = acms$mgmt_get_creds(sname,username,&client_id);

          }

    return(1);
    }

# 6.5 Get Procedures

Get procedures are available for all ACMS Remote Manager tables. Get procedures return all columns from a single table row.

As Table 6-2 shows, a separate get procedure is available for each entity and table.

Input arguments to get procedures are client_id. See Chapter 8 for details about each call.

Table 6-2 Get Procedures
Procedure | Description
--- | ---
acmsmgmt_get_acc_2 | No keys; only 1 ACC per node.
acmsmgmt_get_err_filter_2 | No keys; only 1 Error Filter per node.
acmsmgmt_get_mgr_status_1 | No keys; only one row in the Manager Status table.
acmsmgmt_get_param_2 | No keys; only one row in the Parameter table.
acmsmgmt_get_qti_2 | No keys; only 1 QTI per node.
acmsmgmt_get_tsc_2 | No keys; only 1 TSC per node.
acmsmgmt_get_version_2 | No keys; only 1 version per node.

## 6.5.1 Get Example

The following example code shows how a client program calls the acmsmgmt_get_param_2 procedure and displays the current value of a parameter.

    int get_param_data(int client_id,CLIENT *cl)
     {
       int x = 0;
       int y = 0;

       param_rec2       *params;
       param_rec_out2   *param_rec;
       static struct sub_id_struct sub_rec;
       int status;

       sub_rec.client_id = client_id;

       param_rec = acmsmgmt_get_param_2(&sub_rec,cl);

       if (!param_rec) {
          printf("\n RPC Call to get Parameter data failed");
          return(MGMT_FAIL);
       }

       if (param_rec->status != MGMT_SUCCESS) {
          printf("\n Call to get Parameter data failed, returning status code %d",
                 param_rec->status);
          status = param_rec->status;
            xdr_free(xdr_param_rec_out2, param_rec);
            free(param_rec);
          return(status);
       }

       params = &param_rec->param_rec_out2_u.data;

          printf("\n Maximum logins allowed is %d",params->max_logins);
            xdr_free(xdr_param_rec_out2, param_rec);
            free(param_rec);
          return(0);
    }

# 6.6 List Procedures

List procedures operate on all rows in a table. Procedures are available for each entity and each configuration table with more than one row. There are no list procedures for the following tables, since they contain only one row:

- ACC table
- Parameter table
- QTI table
- TSC table

As Table 6-3 shows, separate list procedures are provided for the remainder of the management information and configuration tables. Input to a list procedure is a selection criteria record, which varies depending on the table being accessed. Some key values in the selection criteria records support wildcards (*, %).

Table 6-3 List Procedures
Procedure | Description
--- | ---
acmsmgmt_list_collections_2 | Key value is table index.
acmsmgmt_list_cp_2 | No keys.
acmsmgmt_list_err_2 | No keys.
acmsmgmt_list_err_filter_2 | No keys.
acmsmgmt_list_exc_2 | Key value is application name or table index.
acmsmgmt_list_interfaces_1 | No keys.
acmsmgmt_list_log_1 | No keys; selection criteria is before_time, since_time, file_name, facility, severity.
acmsmgmt_list_proc_1 | No keys.
acmsmgmt_list_server_1 | Key value is application name, server name, or table index.
acmsmgmt_list_tg_2 | Key value is application name, task group name, or table index.
acmsmgmt_list_trap_1 | No keys.
acmsmgmt_list_users_1 | No keys.

For all list procedures, only entire rows (that is, all columns in the row) are returned. Data is returned in a linked list. The number of nodes in the list is determined by the systemwide parameter table field max_rpc_return_recs. When the number of rows to be returned exceeds the value of max_rpc_return_recs, the caller must reissue the call, providing the appropriate key values to fetch the next set of rows. The call returns status MGMT_NO_MORE_ROWS if there are no more rows available. Procedures with no keys return all rows in the table on the first call, regardless of the value of the max_rpc_return_recs field.

## 6.6.1 Linked List Example

Data from list calls is returned in a linked list. The example in this section uses the acmsmgmt_list_log_1 procedure to illustrate how linked list processing works.

The call to the acmsmgmt_list_log_1 procedure requires the following input structure:

    struct  log_sel_struct {
        int          client_id;
        string       before_time<TIME_SIZE_A>;
        string       since_time<TIME_SIZE_A>;
        string       file_name<STORAGE_LOC_SIZE>;
        int          dup_count;
        int          facility;
        int          severity;
        };

In the code example that follows, the lines of code beginning with log_rec initialize the fields in this structure as follows:

- Client_id is set to 0 to select proxy authentication.
- Before_time is set to a NULL string to specify no end date for viewing log entries. Note that you cannot provide a NULL pointer.
- Since_time is set to the 1st of January 1998. Log entries from this date and later will be viewed.
- File_name is set to an empty string, which causes the active log file to be used.
- Dup_count is set to -1. This field is used to uniquely identify log records with identical times.
- Facility is set to -1, which causes entries for all facilities to be returned.
- Severity is set to -1, which causes entries of all severity levels to be returned.

The following example code shows the initialization of the client and the call to the acmsmgmt_list_log_1 procedure:

    #include <rpc/rpc.h>
    #include <stdio.h>
    #include string
    #include "acmsmgmt_rpc.h"

    CLIENT *cl;

    int main ()
    {

    int skip_rec = 0;

    char null_time_str[24] = "";
    char first_of_jan[24] = "01-JAN-1998 00:00:00.00";
    char file_spec[] = ""; /* use default, i.e. active log file */
    char time_cache[MGMT_S_TIME_A+1];
    static struct  log_sel_struct log_rec;
    log_data_list  *log;
    log_link  *nl;

    /* Initialize client connection; if that fails, exit*/

    cl = clnt_create(sname, ACMSMGMT_RPC, ACMSMGMT_VERSION, "tcp");
    if (!cl)
        return(MGMT_FAIL);

    /* Create a default security context */

    cl->cl_auth = authunix_create_default();

    /* So far so good. Initialize log selection data */

    log_rec.client_id   = 0;
    log_rec.before_time = null_time_str;
    log_rec.since_time  = first_of_jan;
    log_rec.file_name   = file_spec;
    log_rec.dup_count   = -1;
    log_rec.facility    = -1; /* don't match on facility */
    log_rec.severity    = -1; /* don't match on severity */

    top:
    /* Now make RPC */
    log = acmsmgmt_list_log_1(&log_rec,cl);

The return value from the calls to all list procedures (including acmsmgmt_list_log_1) is a pointer to a union. If the pointer returned is NULL, the call has failed. RPC error checking must be used to determine the cause of the error. If a valid pointer has been returned, it will point to a structure containing a union with the following structure:

    struct log_data_list {
            int status;
            union {
                    log_list list;
                    int rc;
            } log_data_list_u;
    };

The status field determines which structure is being returned. If the status is equal to MGMT_FAIL, the rc field is returned. The rc field contains a status code indicating the reason for failure.

If the status field is not equal to MGMT_FAIL, a pointer to a linked list has been returned.

The log_list field is defined as a pointer to linked list node, as follows:

    typedef struct log_link *log_list;

The linked list node has the following structure:

    struct log_link {
            logging_rec log_data;
            log_list pNext;
    };

In this structure, log_data is of type logging_rec, which is a record structure containing the log data. The pNext field is a pointer to the next node in the linked list (which is of type log_link).

Figure 6-3 illustrates the return structure and how the linked list is constructed.

Figure 6-3 Linked List: Return Structure and Construction

The following example code shows how to check whether the call completed successfully, and how to traverse the linked list to display the data:

    /* if a NULL pointer was returned, the RPC failed */
    if (!log)
           return(MGMT_FAIL);

    /* if bad status was returned, something failed in our call.
            log->log_data_list_u.rc contains the status */
    if (log->status == MGMT_FAIL)
            return(log->log_data_list_u.rc);

    /* while more data in the list, display the data */
    for (nl = log->log_data_list_u.list; nl != NULL; nl = nl->pNext) {
            if (skip_rec)
                    skip_rec = 0;
            else
                    printf("\n %-12s\t%-s",sname,nl->log_data.log_msg);

            /* save last time received to use as next time to read forward from */
            memcpy(&time_cache[0],nl->log_data.log_msg,23);
            log_rec.dup_count  = nl->log_data.dup_count;
            log_rec.since_time = time_cache;

    }
    if (log->status == MGMT_NOMORE_DATA)
            printf("\n *** End of data **");
    else {
            skip_rec = 1;
            goto top;
    }

    return(1);
    }

In this example, the returned pointer is checked for whether data has been returned (log is not NULL). Then the status code is checked for whether the call completed successfully.

If the call completed successfully, the code drops into a FOR loop and starts printing the data. For this particular call, the client prints all the records the very first time the RPC is called; on subsequent calls, the first record is a duplicate of the last one from the previous call and is not printed.

After printing a record, the key data is saved to be used again on a subsequent call. Remember that only max_rpc_return_recs is returned in each call to the acmsmgmt_list_log_data_1 procedure. There may be more log records than can be sent at once. It is the responsibility of the client to initialize the call properly to get the next set of records.

Once all the returned records have been returned, the code will call the acmsmgmt_list_log_data_1 procedure again if the status code from the call was not MGMT_NOMORE_DATA. In this way, all the records are retrieved.

Previous | Next | Contents | Index