Go to main content

Remote Administration Daemon Developer's Guide

Exit Print View

Updated: April 2020
 
 

C Client Language Environment in RAD

    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.


Note -  Many of these examples are based on zonemgr ADR Interface Description Language Example.

Connecting to RAD in C

The RAD instances can establish connections 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 two common arguments: a boolean to specify whether the connection must be multithreaded and a locale to use for the connection. As a best practice, set the boolean value as TRUE. 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 boolean, to determine if the connection must be multithreaded

  • A string, locale for the connection

If the value of socket path is NULL, the default RAD UNIX socket path is used. As a best practice, run the connection in multithreaded mode. If the value of locale is NULL, the locale of the local client system is used.

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

// do something with conn

Connecting to a Remote Instance and Authenticating in 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 using rc_pam_login() function. The client application must use #include <rad/client/1/pam_login.h> header and links to the pluggable authentication module (PAM) C binding library, /usr/lib/rad/client/c/libpam_client.so.

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

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

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

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

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)
  • rc_credentials_t *rc_alloc_gss_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 that are represented in the abstract data representation (ADR) document as interfaces are named and can be found by searching the RAD namespace. The key point to note is that to access a RAD object, you need a proxy, which is used to search the RAD namespace. This capability is provided by an interface proxy class, which is defined in each interface's binding module. The key point to note is that to access a RAD object, you should use the list and lookup functions provided by a module's client binding library (<module>_<interface>__rad_list(), <module>_<interface>__rad_lookup(). These functions also provide the option to do either strict or relaxed versioning.

The proxy automatically provides the base name and version details by using functions for interface instances and 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 IDL definition and are stored in the module binding.

Certain interfaces return or accept object references directly to or from clients and these objects might not be named. If the objects do not have a name, they are anonymous. Such objects cannot be looked up in the RAD namespace and the interface itself will provide access mechanisms that make it simple to interact with the anonymous objects.

Creating a Name for a RAD Object in C

You can create a name for a zonemgr Zone instance as follows:

adr_glob_vcreate("com.oracle.solaris.rad.zonemgr", 2, "type",
    "Zone", "name", "zone-1");

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 a 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 and 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 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, B_TRUE, 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 there is an instance 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, B_TRUE, 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, B_TRUE, 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

These components are all defined in an ADR Interface Description Language document. The radadrgen utility is used to process the document to generate language specific components which facilitate client/server interactions within RAD. For more information about the role of ADR and RAD, see Abstract Data Representation. Brief descriptions of each component follows.

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 Abstract Data Representation. 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
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.

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.

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>

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, B_TRUE, 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

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. RAD delivers a variety of errors, but the error which requires additional handling is rc_err_t(), value RCE_SERVER_OBJECT. 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.