API Plugins
June 7-9, 2016
iRODS User Group Meeting 2016
Chapel Hill, NC
Jason M. Coposky
@jason_coposky
Interim Executive Director
The iRODS Plugin Architecture
iRODS 4.2 provides 7 plugin interfaces
Anatomy of a Plugin
Building and Installing the Example Code
sudo apt-get -y install irods-externals-*
export PATH=/opt/irods-externals/cmake3.5.2-0/bin:/opt/irods-externals/clang3.8-0/bin:$PATH
which clang++
/opt/irods-externals/clang3.8-0/bin/clang++
which cmake
/opt/irods-externals/cmake3.5.2-0/bin/cmake
sudo apt-get install irods-dev
mkdir ~/build_api
cd ~/build_api
cmake ~/irods_training/advanced/irods_api_example
make package
sudo dpkg -i irods-api-plugin-example_4.2.0~trusty_amd64.deb
Follow along in the irods_training repository
The code can be found at:
~/irods_training/advanced/irods_api_example/src/libapi-example.cpp
Start with the Factory
extern "C"
irods::api_entry* plugin_factory(
const std::string&, //_inst_name
const std::string& ) { // _context
....
return api;
}
Define an api_def_t object
irods::apidef_t def = { 1310, // api number
RODS_API_VERSION, // api version
NO_USER_AUTH, // client auth
NO_USER_AUTH, // proxy auth
"HelloInp_PI", 0, // in PI / bs flag
"HelloOut_PI", 0, // out PI / bs flag
std::function<
int( rsComm_t*,helloInp_t*,helloOut_t**)>(
rs_hello_world), // operation
"rs_hello_world", // operation name
0, // null clear fcn
(funcPtr)CALL_HELLOINP_HELLO_OUT
};
Instantiate the api_entry and wire the serialization fcns
irods::api_entry* api = new irods::api_entry( def );
#ifdef RODS_SERVER
irods::re_serialization::add_operation(
typeid(helloInp_t*),
serialize_helloInp_ptr );
irods::re_serialization::add_operation(
typeid(helloOut_t**),
serialize_helloOut_ptr_ptr );
#endif // RODS_SERVER
Assign packing instructions to the api_entry
...
api->in_pack_key = "HelloInp_PI";
api->in_pack_value = HelloInp_PI;
api->out_pack_key = "HelloOut_PI";
api->out_pack_value = HelloOut_PI;
api->extra_pack_struct[ "OtherOut_PI" ] = OtherOut_PI;
return api;
}
Assign packing instruction key-value pairs for input and output types
Define the input and output types and packInst
typedef struct {
int _this;
char _that [64];
} helloInp_t;
typedef struct {
double _value;
} otherOut_t;
typedef struct {
int _this;
char _that [64];
otherOut_t _other;
} helloOut_t;
#define HelloInp_PI "int _this; str _that[64];"
#define OtherOut_PI "double _value;"
#define HelloOut_PI "int _this; str _that[64]; struct OtherOut_PI;"
Define a call handler for this combination of types
int call_helloInp_helloOut(
irods::api_entry* _api,
rsComm_t* _comm,
helloInp_t* _inp,
helloOut_t** _out ) {
return _api->call_handler<
rsComm_t*,
helloInp_t*,
helloOut_t** >(
_comm,
_inp,
_out );
}
Each combination of parameter types has a unique call handler to deal with the void* type erasure of the packing instructions - this is used in the std::function of the api_def_t
The call handler - Client vs Server Building
#ifdef RODS_SERVER
#define CALL_HELLOINP_HELLO_OUT call_helloInp_helloOut
#else
#define CALL_HELLOINP_HELLO_OUT NULL
#endif
Based on macros, the same plugin source file generates both
Several parts are undefined for the client build
The actual API definition
int rs_hello_world( rsComm_t*, helloInp_t* _inp, helloOut_t** _out ) {
rodsLog( LOG_NOTICE, "Dynamic API - HELLO WORLD" );
( *_out ) = ( helloOut_t* )malloc( sizeof( helloOut_t ) );
( *_out )->_this = 42;
strncpy( ( *_out )->_that, "hello, world.", 63 );
( *_out )->_other._value = 128.0;
rodsLog( LOG_NOTICE, "Dynamic API - this [%d] that [%s]", _inp->_this, _inp->_that );
rodsLog( LOG_NOTICE, "Dynamic API - DONE" );
return 0;
}
Questions?
Follow along in the irods_training repository
The code can be found at:
~/irods_training/advanced/irods_api_example/src/iapi_example.cpp
Client Main - get the iRODS environment and connect
int
main( int, char** ) {
signal( SIGPIPE, SIG_IGN );
rodsEnv myEnv;
int status = getRodsEnv( &myEnv );
if ( status < 0 ) {
rodsLogError( LOG_ERROR, status, "main: getRodsEnv error. " );
exit( 1 );
}
rErrMsg_t errMsg;
rcComm_t *conn;
conn = rcConnect(
myEnv.rodsHost,
myEnv.rodsPort,
myEnv.rodsUserName,
myEnv.rodsZone,
0, &errMsg );
if ( conn == NULL ) {
exit( 2 );
}
Initialize the API table
irods::pack_entry_table& pk_tbl = irods::get_pack_table();
irods::api_entry_table& api_tbl = irods::get_client_api_table();
init_api_table( api_tbl, pk_tbl );
Initialize the API table with all of the client-side plugins
Login to iRODS as a client user
if ( strcmp( myEnv.rodsUserName, PUBLIC_USER_NAME ) != 0 ) {
status = clientLogin( conn );
if ( status != 0 ) {
rcDisconnect( conn );
exit( 7 );
}
}
Instantiate the input parameter and set values
helloInp_t inp; memset( &inp, 0, sizeof( inp ) ); inp._this = 42; strncpy( inp._that, "hello, world.", 64 );
Make the Client RPC API Request by number
void *tmp_out = NULL;
status = procApiRequest( conn, 1310, &inp, NULL, &tmp_out, NULL );
Error handling, reporting, and disconnect
if ( status < 0 ) {
printf( "\n\nERROR - failed to call our api\n\n\n" );
return 0;
}
else {
helloOut_t* out = static_cast<helloOut_t*>( tmp_out );
if ( out != NULL ) {
printf( "\n\nthis [%d] that [%s] other [%f]\n",
out->_this, out->_that, out->_other._value );
}
else {
printf( "ERROR: the 'out' variable is null\n" );
}
}
rcDisconnect( conn ); }
Testing Output
$ iapi_example
this [42] that [hello, world.] other [128.000000]
Questions?