An API is defined by a module developer and contains a variety of components designed to accomplish a task. These components are:
Enums
Values
Structs
Fields
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. More details in the role of ADR and rad can be found in Abstract Data Representation. Brief descriptions of each component follows.
Enumerations are primarily used to offer a restricted range of choices for a property, an interface method parameter, result, or error.
Enumerated types are defined in the binding header with the type prepended with the module name. Values are prepended to follow (as closely as possible) the C coding standard naming conventions.
Example 3-9 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;
Structs are used to define new types and are composed from existing built-in types and other user defined types. In essence, they are simple forms of interfaces: no methods or events and they are not present in the RAD namespace.
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 by nested data.
Example 3-10 The zonemgr Property Struct Definition and its Free Functiontypedef 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 *);
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.
See the Rad Namespace section.
Once we have an object reference we can use this to interact with RAD in a very straightforward fashion. All attributes and methods defined in the IDL are accessible by invoking calling functions in the generated client binding.
Here is an example which gets a reference to a zone and then boots the zone.
Example 3-11 Working with 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); } }
Here is an example for accessing a remote property.
Example 3-12 Accessing a 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, we accessed the list of Zone global resource properties and printed out the name and value of every Property that has a value.
In this next example we are going to look at events. The ZoneManager instance defines a "stateChange" event which clients can subscribe to for information about changes in the runtime state of a zone.
Example 3-13 Subscribing and Handling 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);
This is a simple example. We subscribe to the single event and pass in a handler and a handle for our ZoneManager object. The handler will be invoked asynchronously by the framework with the various event details and the supplied user data (the user data in this case being NULL).
Finally, a quick look at error handling when manipulating remote references. The list of possible errors is defined by the enum rc_err_t(). There are a variety of errors which can be delivered by RAD, but the one which potentially requires additional handling is the rc_err_t() value RCE_SERVER_OBJECT. The following snippet, shows how it can be used:
Example 3-14 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); } }