Go to main content

Remote Administration Daemon Client User's Guide

Exit Print View

Updated: November 2020
 
 

RAD C Client

    The public interfaces that are not specific to RAD modules, are exported in the /usr/lib/libradclient.so library and are defined in the following headers:

  • /usr/include/rad/radclient.h – The client function and datatype definitions

  • /usr/include/rad/radclient_basetypes.h – Helper routines for managing the built-in RAD types

The list of #include statements at the beginning of each example shows the headers that are required for that specific functionality.

Building RAD C Clients

A C client program must include the <rad/radclient.h> header file. To authenticate a connection to a remote instance by using the rc_auth_login() function, the client application must use the #include <rad/client/c/2/auth_login.h> header. To include signatures of functions to interact with a RAD module, the client application must use the #include </rad/client/c/<module_version>/<module_name>.h> header.

Connecting to RAD in C

RAD instances establish connections by using the rc_connect_*() set of functions. You can obtain connections for various transports such as TLS, TCP, and local UNIX socket. Each function returns a rc_conn_t reference. This reference acts as a handle for interactions with RAD over its connection. Every connect function has one common argument, a locale to use for the connection. When locale is NULL, the locale of the local client is used.

To close the connection, you must call the rc_disconnect() function with the connection handle.

Connecting to a Local RAD Instance in C

You can connect to a local instance using the rc_connect_unix() function. An implicit authentication is performed against your user ID and most RAD tasks that you request with this connection are performed with the privileges available to your user account.

    The rc_connect_unix() function takes the following arguments:

  • A string, path of the UNIX socket

  • A string, locale for the connection

If the value of socket path is NULL, the default RAD UNIX socket path is used. If the value of locale is NULL, the locale of the local client is used.

Example 1  C Language – Creating a RAD Local Connection
#include <rad/radclient.h>
rc_conn_t conn = rc_connect_unix(NULL, NULL);

Connecting to a Remote Instance and Authenticating in C Using RAD

When connecting to a remote instance, no implicit authentication is performed. The connection is not established until you authenticate. You can authenticate a connection to a remote instance by using the rc_auth_login() function. The client application must use the #include <rad/client/2/auth_login.h> header and link to the authentication module C binding library, /usr/lib/rad/client/c/libauthentication2_client.so.

Authentication is non-interactive, and a username and a password must be provided. Optionally, a handle to the PAM authentication object is returned if a reference is provided as the second argument to the rc_auth_login() function.

Example 2  C Language – Creating a RAD Remote Connection Over TCP IPv4 on Port 7777
#include <rad/radclient.h>
#include <rad/client/2/auth_login.h>

rc_instance_t *pam_inst;
rc_conn_t conn = rc_connect_tcp("host1",7777, NULL);

if (conn !=NULL) {
            rc_err_t status = rc_auth_login(conn, &pam_inst, "user", "password");
            if (status == RCE_OK){
                printf("Connected and authenticated!\n");
                }    
}

RAD is deployed as two cooperating processes. A proxy process is responsible for authentication and establishing communications. A slave process is created by the proxy and handles module processing. A slave is created for each client connection.

Connecting to a RAD Instance by Using a URI in C

You can use a uniform resource identifier (URI) to connect to a local or remote RAD instance. For more information, see Connecting in Python to a RAD Instance by Using a URI.

    The following functions are supported in C:

  • rc_uri_t *rc_alloc_uri(const char *src, rc_scheme_t schemes)
  • rc_credentials_t *rc_alloc_pam_credentials(const char *pass)
  • void rc_free_credentials(rc_credentials_t *cred)
  • rc_credentials_class_t rc_uri_get_cred_class(rc_uri_t *uri)
  • rc_uri_t *rc_alloc_uri(const char *src, rc_scheme_t schemes)
  • rc_conn_t * rc_connect_uri(const char *uri, rc_credentials_t *cred)
  • void rc_uri_set_cred_class(rc_uri_t *uri, rc_credentials_class_t class)
  • rc_scheme_t rc_uri_get_schemes(rc_uri_t *uri)
  • int rc_uri_get_port(rc_uri_t *uri)
  • const char *rc_uri_get_host(rc_uri_t *uri)
  • rc_scheme_t rc_uri_get_scheme(rc_uri_t *uri)
  • const char *rc_uri_scheme_tostr(rc_scheme_t scheme)
  • const char *rc_uri_get_src(rc_uri_t *uri)
  • const char *rc_uri_get_user(rc_uri_t *uri)
  • const char *rc_uri_get_path(rc_uri_t *uri)
  • void rc_free_uri(rc_uri_t *uri)

You can use the rc_uri_t structure to connect to a RAD instance. rc_uri_t is the main structure with which you interact.

You can allocate a rc_uri_t structure with the rc_alloc_uri() function. This function returns NULL on failure or a pointer to a valid rc_uri_t structure. For example, if you require PAM authentication for a remote connection, you must allocate a rc_credentials_t structure using one of the alloc() credential functions. This allocation depends on the authentication type. RAD supports two types of authentication, PAM and generic security service (GSS).

You can connect to RAD by using the rc_connect_uri() function. This returns a rc_conn_t() function that can be used to establish the connection by using rc_connect_unix(), rc_connect_tcp(), or other functions. You can use the other informative functions to interact with the allocated structure to obtain useful information. The various rc_free_uri() functions can clean the memory after you finish using the structures.

RAD Namespace in C

Most RAD objects are represented in the abstract data representation (ADR) document as interfaces. You can search RAD objects by searching the RAD namespace.

    To access a RAD object, you need the following:

  • A proxy, which can be used to search the RAD namespace. An interface proxy class allows you to use a proxy to search the RAD namespace. Interface proxy is defined in the binding module of each interface.

  • The list and lookup functions (module_interface__rad_list() and module_interface__ rad_lookup()) provided by client binding library of a module. These functions also provide the option to perform either strict or relaxed versioning.


    Note -  These functions use a double underscore between interface and the following function.

As RAD interfaces become more extensive in Oracle Solaris, the probability of two clients interoperating increases when using the strict and relaxed API versioning. Each versioning mode is applicable on a per-interface and per-connection basis.

Relaxed versioning is the default mode for RAD communication. In this mode, RAD accepts connections for any minor version of an interface if the major version of the server module matches the major version of the client binding. Relaxed versioning mode maximizes the cross-system compatibility of the interfaces. In strict versioning mode, RAD rejects the client unless the minor version of the module at the server is greater than or equal to the minor version of the client binding.

The proxy automatically provides the base name and the version details using functions for the interface instances. It is structured as follows:

<domain name>:type=<interface name>[,optional additional key value pairs]

The <domain name> and the <interface name> are automatically derived from the ADR interface description language (IDL) definition and are stored in the module binding.

Certain interfaces return or accept object references directly to or from clients. These objects might not be named. Objects that are not named are anonymous. Anonymous objects cannot be looked up in the RAD namespace, but the interface provides access methods that make it simple to interact with them.

Creating a Name for a RAD Object in C

The client is not required to create an object name, as module_interface__rad_list() creates it internally.

Searching for RAD Objects in C

Client binding of a module provides a search function for each interface defined in the form: module_interface__rad_list(). You can provide a pattern (glob or regex) to narrow the search within the objects of an interface type.

In addition, the libradclient library provides the function rc_list(), where the caller provides the entire name or pattern and version to search the objects.

Obtaining a Reference to a RAD Singleton in C

A module developer creates a singleton to represent an interface. This interface can be accessed easily. For example, the zonemgr module defines a singleton interface, ZoneInfo. It contains information about the zone that contains the RAD instance with which you are communicating.

Example 3  C Language – Obtaining a Reference to a RAD Singleton
#include <rad/radclient.h>
#include<rad/client/1/zonemgr.h>

rc_instance_t *inst;
rc_err_t status;
char *name;

rc_conn_t *conn = rc_connect_unix(NULL, NULL);
if (conn !=NULL) {
   status = zonemgr_ZoneInfo__rad_lookup(conn, B_TRUE, &inst, 0);
  if(status == RCE_OK) {
    status =zonemgr_ZoneInfo_get_name(inst, &name);
  if (status ==RCE_OK)
     printf("Zone name: %s\n", name);
   }
}

In the preceding example, you have connected to a local RAD instance, and have obtained a remote object reference directly using the lookup function provided by the zonemgr binding. After you have the remote reference, you can access the properties with the module_interface__get_<property>() function.

Listing RAD Instances of an Interface in C

An interface can contain multiple RAD instances. For example, the zonemgr module defines a Zone interface and an instance of this interface exists for each zone on the system. A module provides a list function for each of its interfaces in the form, module_interface__rad_list().

Example 4  C Language – Listing RAD Interface Instances
#include<rad/radclient.h>
#include<rad/radclient_basetypes.h>
#include<rad/client/1/zonemgr.h>

rc_err_t status; 
adr_name_t **name_list;
int name_count;

rc_conn_t *conn = rc_connect_unix(NULL, NULL);
if (conn !=NULL) {
  status = zonemgr_Zone__rad_list(conn, B_TRUE, NS_GLOB, &name_list,
    &name_count, 0);
   if(status == RCE_OK) {
      for (int i =0; i < name_count; i++) {
      char*name =adr_name_tostr(name_list[i]);
        printf("%s\n", name);
      }
     name_array_free(name_list, name_count);
     } 
  }

Obtaining a Remote Object Reference From a Name in C

The list function returns a name, in the form of a adr_name_t reference. Once you retrieve a name, you can obtain a remote object reference as shown in the following example.

Example 5  C Language – Obtaining a Remote Object Reference From a Name
#include <rad/radclient.h>
#include <rad/radclient_basetypes.h>
#include<rad/client/1/zonemgr.h>
 
rc_err_t status;
adr_name_t **name_list;
rc_instance_t *zone_inst;
int name_count;

rc_conn_t *conn = rc_connect_unix(NULL, NULL);
if (conn != NULL) {
        status = zonemgr_Zone__rad_list(conn, B_TRUE, NS_GLOB, &name_list,
          &name_count, 0);
      if (status == RCE_OK) {
               status = rc_lookup(conn, name_list[0],
                  NULL, B_TRUE, &zone_inst);
              if (status == RCE_OK) {
                      char *name;
                       status = zonemgr_Zone_get_name(zone_inst, &name);
                      if (status == RCE_OK)
                               printf("Zone name: %s\n",
                                   name);
                      free(name);
                }
                name_array_free(name_list, name_count);
        }
}

Sophisticated RAD Searches in C

You can search for a zone by its name or ID, or search for a set of zones by pattern matching. Use the list function to restrict the results. For example, if zones are identified by name, you can search for a zone named test-0 by using glob patterns as follows.

Example 6  C Language – Using Glob Patterns
#include <rad/radclient.h>
#include <rad/radclient_basetypes.h>
#include <rad/client/1/zonemgr.h>

rc_err_t status;
adr_name_t **name_list;
int name_count;

rc_conn_t *conn = rc_connect_unix(NULL, B_TRUE, NULL);
if (conn != NULL) {
   status = zonemgr_Zone__rad_list(conn, B_TRUE, NS_GLOB, B_TRUE, &name_list,
    &name_count, 1, "name", "test-0");
      if (status == RCE_OK) {
           for (int i = 0; i < name_count; i++) {
                const char *name = adr_name_tostr(name_list[i]);
                printf("%s\n", name);
             }
           name_array_free(name_list, name_count);
        }
}
Glob Pattern Searching in RAD in C

You can use a glob pattern to find zones with wildcard pattern matching. Keys or values in the pattern may contain an asterisk, *, for wildcard pattern matching. For example, you can search all the zones with a name that begins with test as follows.

Example 7  C Language – Using Glob Patterns With Wildcards
#include <rad/radclient.h>
#include <rad/radclient_basetypes.h>
#include <rad/client/1/zonemgr.h>

rc_err_t status;
adr_name_t **name_list;
int name_count;

rc_conn_t *conn = rc_connect_unix(NULL, B_TRUE, NULL);
if (conn != NULL) {
    status = zonemgr_Zone__rad_list(conn, B_TRUE, NS_GLOB, &name_list,
      &name_count, 1, "name", "test*");
     if (status == RCE_OK) {
          for (int i = 0; i < name_count; i++) {
               const char *name = adr_name_tostr(name_list[i]);
               printf("%s\n", name);
             }
             name_array_free(name_list, name_count);
        }
}
Regex Pattern Searching in RAD in C

You can also use the extended regular expression (ERE) search capabilities of RAD to search for a zone. For example, you can find only zones with the name test-0 or test-1 as follows.

Example 8  C Language – Using Regex Patterns
#include <rad/radclient.h>
#include <rad/radclient_basetypes.h>
#include <rad/client/1/zonemgr.h>

rc_err_t status;
adr_name_t **name_list;
int name_count;

rc_conn_t *conn = rc_connect_unix(NULL, B_TRUE, NULL);
if (conn != NULL) {
      status = zonemgr_Zone__rad_list(conn, B_TRUE, NS_REGEX, 
    &name_list, &name_count, 1, "name", "test-0|test-1");
    if (status == RCE_OK) {
         for (int i = 0; i < name_count; i++) {
              const char *name = adr_name_tostr(name_list[i]);
              printf("%s\n", name);
            }
            name_array_free(name_list, name_count);
        }
}

The key and the value must be valid EREs as determined by the connected RAD instance. The expression is compiled and executed on the server.

RAD Interface Components in C

    The module developer defines an API in an ADR IDL document. It contains one or more of the following components, each of which performs a task:

  • Enumerations

    • Values

  • Structures

    • Fields

  • Dictionary

  • Interfaces

    • Properties

    • Methods

    • Events

The radadrgen utility is used to process the document to generate language-specific components, which facilitates client-server interaction within RAD. For more information about the role of ADR and RAD, see Chapter 2, Abstract Data Representation for RAD in Remote Administration Daemon Module Developer’s Guide. The following sections describe each component.

RAD Enumerations in C

Enumerations provide a restricted range of choices for a property, an interface method parameter, a result, or an error.

Using RAD Enumeration Types in C

Enumerated types are defined in the binding header with the type prepended with the module name. The values of the enumerated types are prepended to follow the C coding standard naming conventions.

Example 9  C Language – zonemgr ErrorCode Enumeration for RAD in C
typedef enum zonemgr_ErrorCode {
     ZEC_NONE =0,
     ZEC_FRAMEWORK_ERROR = 1,
     ZEC_SNAPSHOT_ERROR = 2,
     ZEC_COMMAND_ERROR = 3,
     ZEC_RESOURCE_ALREADY_EXISTS = 4,
     ZEC_RESOURCE_NOT_FOUND = 5,
     ZEC_RESOURCE_TOO_MANY = 6,
     ZEC_RESOURCE_UNKNOWN = 7,
     ZEC_ALREADY_EDITING = 8,
     ZEC_PROPERTY_UNKNOWN = 9,
     ZEC_NOT_EDITING = 10,
     ZEC_SYSTEM_ERROR = 11,
     ZEC_INVALID_ARGUMENT = 12,
     ZEC_INVALID_ZONE_STATE = 13,
}zonemgr_ErrorCode_t;

RAD Structures in C

Structures (Structs) are used to define new types and are composed from existing built-in types and other user defined types. Structs are simple forms of interfaces with no methods or events. They are not included in the RAD namespace.

Using RAD Struct Types in C

The zonemgr module defines a property struct, which represents an individual zone configuration property. The structure has the following members, name, type, value, listValue, and complexValue. Like enumerations, structures are defined in the binding header and follow similar naming conventions.

To free a structure, free functions module_structure_free() are provided by the binding to ensure proper cleanup of any memory held in the nested data.

Example 10  C Language – zonemgr Property Struct Definition and Its Free Function
typedef enum zonemgr_PropertyValueType {
     ZPVT_PROP_SIMPLE = 0,
     ZPVT_PROP_LIST = 1,
     ZPVT_PROP_COMPLEX = 2,
} zonemgr_PropertyValueType_t;

typedef struct zonemgr_Property {
 char * zp_name;
 char * zp_value;
 zonemgr_PropertyValueType_t zp_type;
 char * * zp_listvalue;
 int zp_listvalue_count;
 char * * zp_complexvalue;
 int zp_complexvalue_count;
} zonemgr_Property_t;

void zonemgr_Property_free(zonemgr_Property_t *);

Dictionary Support in C for RAD

C does not support dictionary data types natively. To support dictionary in types and functions, you must enable the dictionary functionality for each dictionary type as part of a module's C binding. You can create, free, and query a dictionary for its size. The supported operations on a dictionary include getting, putting, and removing an element. The functions _keys() and _values() return an array of all keys and values, respectively. The _map() function is called with a pointer to a function that is invoked with each key-value pair. For more information about dictionary, see Dictionary Definitions in RAD Modules in Remote Administration Daemon Module Developer’s Guide.

The C binding dictionary is a wrapper around the libadr library. The libadr library functions that are supported for dictionary are similar to the functions supported by C. The functions are in the native C type instead of the libadr (adr_data_t()) type. For more information, see Dictionary Support in libadr in Remote Administration Daemon Module Developer’s Guide.

The following is an example of a generated type and API of a dictionary where the key type is integer and the value type is string. In this example, <module> is the name of the module.

typedef struct <module>__rad_dict_integer_string
   <module>__rad_dict_integer_string_t;

<module>__rad_dict_integer_string_t *
   <module>__rad_dict_integer_string_create(
   const rc_instance_t *inst);

void <module>__rad_dict_integer_string_free(
   <module>__rad_dict_integer_string_t *dict);
rc_err_t <module>__rad_dict_integer_string_contains(
   <module>__rad_dict_integer_string_t *dict, int key);
unsigned int <module>__rad_dict_integer_string_size(
   <module>__rad_dict_integer_string_t *dict);
rc_err_t <module>__rad_dict_integer_string_remove(
   <module>__rad_dict_integer_string_t *dict, int key,
   char **value);
rc_err_t <module>__rad_dict_integer_string_get(
   <module>__rad_dict_integer_string_t *dict, int key,
    char **value);
rc_err_t <module>__rad_dict_integer_string_put(
   <module>__rad_dict_integer_string_t *dict, int key,
   const char *value, char **old_value);
int *<module>__rad_dict_integer_string_keys(
   <module>__rad_dict_integer_string_t *dict);
char **<module>__rad_dict_integer_string_values(
   <module>__rad_dict_integer_string_t *dict);
int <module>__rad_dict_integer_string_map(
   <module>__rad_dict_integer_string_t *dict,
int (*func)(int, const char *, void *), void *arg);

The generated type can be used like any other type in RAD. A sample C client binding definition is as follows:

rc_err_t <module>_<interface>_set_DictProp(rc_instance_t *,
  <module>__rad_dict_integer_string_t *);

Note -  The dictionary type and associated functions are thread-safe.

RAD Interfaces in C

Interfaces, also known as objects, are the entities which populate the RAD namespace. They must have a name. An interface is composed of events, properties, and methods.

Obtaining a RAD Object Reference in C

See the RAD Namespace in C section.

Working With RAD Object References in C

Once you have an object reference, you can use this object reference to interact with RAD directly. All attributes and methods defined in IDL are accessible by invoking calling functions in the generated client binding.

The following example shows how to work with the object references. In this example, you get a reference to a zone and then boot the zone.

Example 11  C Language – Working With RAD Object References
#include <rad/radclient.h>
#include <rad/radclient_basetypes.h>
#include <rad/client/1/zonemgr.h>

rc_err_t status;
rc_instance_t *zone_inst;
zonemgr_Result_t *result;
zonemgr_Result_t *error;

rc_conn_t *conn = rc_connect_unix(NULL, B_TRUE, NULL);
if (conn != NULL) {
     status = zonemgr_Zone__rad_lookup(conn, B_TRUE, &zone_inst, 1, "name", "test-0");
     if (status == RCE_OK) {
          status = zonemgr_Zone_boot(zone_inst, NULL, 0, &result, &error);
          rc_instance_rele(zone_inst);
      }
}
Accessing a Remote Property in RAD in C

This example shows how to access a remote property.

Example 12  C Language – Accessing a RAD Remote Property
#include <rad/radclient.h>
#include <rad/radclient_basetypes.h>
#include <rad/client/1/zonemgr.h>

int
main(int argc, char **argv)
{
        rc_err_t status;
        rc_instance_t *zone_inst;
        char *name;
        zonemgr_Property_t **result;
        zonemgr_Result_t *error;
        int result_count;

        rc_conn_t *conn = rc_connect_unix(NULL, NULL);

        if (conn != NULL) {
                status = zonemgr_Zone__rad_lookup(conn, B_TRUE, &zone_inst,
1,
                    "name", "test-0");
                if (status == RCE_OK) {
                        zonemgr_Resource_t global = { .zr_type = "global"};
                        status =
zonemgr_Zone_getResourceProperties(zone_inst,
                            &global, NULL, 0, &result, &result_count, &error);
                        if (status == RCE_OK) {
                                for (int i = 0; i < result_count; i++){
                                        if (result[i]->zp_value != NULL &&
                                            result[i]->zp_value[0] != '\0') {
                                                printf("%s=%s\n",
                                                    result[i]->zp_name,
                                                    result[i]->zp_value);
                                        }
                                }
                                zonemgr_Property_array_free(result,
result_count);
                        }
                        rc_instance_rele(zone_inst);
                }
        }
}

In this example, you have accessed the list of global resource properties of the Zone and printed the name and value of every property that has a value.

RAD Event Handling in C

An event is an asynchronous notification generated by RAD and consumed by clients. For more information, see RAD Events in Remote Administration Daemon Module Developer’s Guide.

The following example shows how to subscribe to and handle events. The ZoneManager instance defines a StateChange event that clients can subscribe to information about the changes in the runtime state of a zone.

Example 13  C Language – Subscribing to and Handling RAD Events
#include <unistd.h>
#include <time.h>
#include <rad/radclient.h>
#include <rad/radclient_basetypes.h>
#include <rad/client/1/zonemgr.h>

void stateChange_handler(rc_instance_t *inst, zonemgr_StateChange_t *payload, struct timespec timestamp, void *arg)
{       
    printf("event: zone state change\n");
    printf("payload:\n zone: %s\n old state: %s\n new state: %s\n",
    payload->zsc_zone, payload->zsc_oldstate, payload->zsc_newstate);
       
    zonemgr_StateChange_free(payload);
}

rc_err_t status;
rc_instance_t *zm_inst;
int result_count;

rc_conn_t *conn = rc_connect_unix(NULL, B_TRUE, NULL);
if (conn != NULL) {
        status = zonemgr_ZoneManager__rad_lookup(conn, B_TRUE, &zm_inst, 0);
        if (status == RCE_OK) {
            status = zonemgr_ZoneManager_subscribe_stateChange(zm_inst, stateChange_handler, NULL);
            if (status == RCE_OK)
                printf("Successfully subscribed to statechange event!\n");
            rc_instance_rele(zm_inst);
        }
 }
 for (;;)
 sleep(1);

In this example, you have subscribed to a single event by passing a handler and a handle for the ZoneManager object. The handler is invoked asynchronously by the framework with various event details and user data. In this example, the user data is NULL.

RAD Error Handling in C

The list of possible errors are defined by the rc_err_t enumeration. The following example shows how it can be used.

Example 14  C Language – Handling RAD Errors
#include <rad/radclient.h>
#include <rad/radclient_basetypes.h>
#include <rad/client/1/zonemgr.h>

rc_err_t status;
rc_instance_t *zone_inst;
zonemgr_Result_t *result;
zonemgr_Result_t *error;

rc_conn_t *conn = rc_connect_unix(NULL, B_TRUE, NULL);
if (conn != NULL) {
      status = zonemgr_Zone__rad_lookup(conn, B_TRUE, &zone_inst, 1, "name", "test-0");
     if (status == RCE_OK) {
             status = zonemgr_Zone_boot(zone_inst, NULL, 0, &result, &error);
            if (status == RCE_SERVER_OBJECT) {
                 printf("Error Code %d\n", error->zr_code);
                    if (error->zr_stdout != NULL)
                         printf("stdout: %s\n", error->zr_stdout);
                     if (error->zr_stderr != NULL)
                         printf("stderr: %s\n", error->zr_stderr);
                      zonemgr_Result_free(error);
                }
                rc_instance_rele(zone_inst);
        }
}

Note -  You might get a payload with rc_err_t, value RCE_SERVER_OBJECT. This means that the server is sending additional information about the error. This payload is only present if your interface method or property has defined an error element, where the payload is the content of that error. If the interface method or property defines no error element for the interface method or property, no payload exists and no error reference argument exists for the get or set functions.