RPC Operation Interface
All RPC operations are data-driven within the server, using the YANG rpc statement for the operation and SIL callback functions.
Any new protocol operation can be added by defining a new YANG rpc statement in a module, and providing the proper SIL code.
RPC Callbacks
RPC Callback Template
The 'agt_rpc_method_t' typedef in agt/agt_rpc.h
is used as the
callback template for all RPC callback phases.
-
typedef status_t (*agt_rpc_method_t)(ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode)
Template for RPC server callbacks.
The same template is used for all RPC callback phases The callback is expected to validate if needed and then invoke if needed.
- Param scb:
session invoking the RPC
- Param msg:
message in progress for this <rpc> request
the msg->rpc_input value node contains the input (if any). It is a container matching the rpc/input node for the YANG rpc
- Param methnode:
XML node for the operation, which can be used in error reporting (or ignored)
- Return:
return status for the phase
An error in validate phase will cancel invoke phase
An rpc-error will be added if an error is returned and the msg error Q is empty
RPC Callback Initialization
The 'agt_rpc_register_method' function in agt/agt_rpc.h
is used
to provide a callback function for a specific callback phase. The same
function can be used for multiple phases if desired.
This function call is usually generated automatically in the SIL or SIL-SA code stubs.
-
status_t agt_rpc_register_method(const xmlChar *module, const xmlChar *method_name, agt_rpc_phase_t phase, agt_rpc_method_t method)
add callback for 1 phase of RPC processing
- Parameters:
module -- name of the module that contains the RPC statement
method_name -- Identifier for the rpc statement
phase -- RPC server callback phase for this callback
AGT_PH_VALIDATE(0): validate phase
AGT_PH_INVOKE(1): invoke phase
AGT_PH_POST_REPLY(2): post-reply phase
method -- pointer to callback function to register
- Returns:
status of the operation
Example Registration Code:
res = agt_rpc_register_method(
y_toaster_M_toaster,
y_toaster_N_make_toast,
AGT_RPC_PH_VALIDATE,
y_toaster_make_toast_validate);
if (res != NO_ERR) {
return res;
}
RPC Callback Cleanup
The 'agt_rpc_unregister_method' function in agt/agt_rpc.h
is used
to remove a callback function for all callback phases.
Warning
This function must be called once if any callback phase was registered. Memory may not be properly freed if an RPC callback is not properly unregistered.
This function call is usually generated automatically in the SIL or SIL-SA code stubs.
-
void agt_rpc_unregister_method(const xmlChar *module, const xmlChar *method_name)
remove the callback functions for all phases of RPC or Action processing for the specified RPC method or action
- Parameters:
module -- module name of RPC method or action name
method_name -- RPC method or action name
Example Unregistration Code:
agt_rpc_unregister_method(
y_toaster_M_toaster,
y_toaster_N_make_toast);
Force an RPC Operation to be Unsupported
It is possible for the SIL instrumentation to turn support for an RPC operation off in the server.
If this API is used then the RPC operation will be removed from the list of operations that can be used in the server.
-
void agt_rpc_unsupport_method(const xmlChar *module, const xmlChar *method_name)
mark an RPC method or action as unsupported within the server
this is needed for operations dependent on capabilities
- Parameters:
module -- module name of RPC method (really module name)
method_name -- RPC method name
Use this function with care. It may break the server if this API is used on a server-provided RPC operation.
Example Usage:
agt_rpc_unsupport_method(
y_toaster_M_toaster,
y_toaster_N_make_toast);
RPC Message Header
The NETCONF server will parse the incoming XML message and construct an RPC message header, which is used to maintain state and any other message-specific data during the processing of an incoming <rpc> request.
rpc_msg_t
The 'rpc_msg_t' data structure defined in ncx/rpc.h
is used to represent an RPC operation.
It is a high-level control block which contains a low-level 'xml_msg_hdr_t'
struct within it.
-
struct rpc_msg_t
NETCONF Server and Client RPC Request/Reply Message Header.
Public Members
-
dlq_hdr_t qhdr
Queue header to store RPC messages in a queue (within the session header)
-
xml_msg_hdr_t mhdr
generic XML message header Most in-message state is kept in the mhdr There are several places in the code where the mhdr is used alone so there is no coupling to the RPC layer.
Contains XML message prefix map and other data used to parse the request and generate the reply.
-
xml_attrs_t *rpc_in_attrs
incoming: top-level rpc element data Queue of xml_attr_t representing any XML attributes that were present in the <rpc> element.
A callback function may add xml_attr_t structs to this queue to send in the reply.
-
struct obj_template_t_ *rpc_method
incoming: Back-pointer to the object template for this RPC operation.
2nd-level method name element data, used in agt_output_filter to check get or get-config; cannot import obj.h here! The object template type will be OBJ_TYP_RPC for an RPC method For an action this will be the top-level <action> RPC wrapper in the YANG namespace
-
int rpc_agt_state
incoming: SERVER RPC processing state Enum value (0, 1, 2) for the current RPC callback phase.
-
op_errop_t rpc_err_option
Enum value for the <error-option> parameter.
This is only set if an edit operation is in progress.
-
op_editop_t rpc_top_editop
Enum value for the <default-operation> parameter.
This is only set if an edit operation is in progress.
-
val_value_t *rpc_input
Value tree representing the container of 'input' parameters for this RPC operation.
For an action, the rpc_input node is not used. Instead the rpc_actionval backptr is used instead. This is malloced in rpc_new_msg but not filled in.
-
struct sil_sa_cb_t_ *rpc_sil_sa_cb
backptr to SIL-SA edit control block if WITH_YCONTROL=1
-
dlq_hdr_t rpc_inputQ
the rpc_inputQ is used with JSON encoded input since an array is allowed at the top-level; it is used instead of rpc_input if encoding == JSON, even if only 1 array node is parsed;
Q of val_value_t *
-
void *rpc_user1
Void pointer that can be used by method routines to save context or whatever to store SIL-specific data.
This pointer is preserved, transferred from validate to invoke so data does not need to be regenerated or retrieved
-
void *rpc_user2
Same use as rpc_user1.
-
uint32 rpc_returncode
Internal return code used to control nested callbacks.
-
rpc_data_t rpc_data_type
incoming: get method reply handling builtin For RPC operations that return data, this enumeration MUST be set to indicate which type of data is returned.
RPC_DATA_STD: A <data> container will be used to encapsulate any returned data, within the <rpc-reply> element.
RPC_DATA_YANG: The <rpc-reply> element will be the only container encapsulated any returned data. If the rpc_datacb is non-NULL then it will be used as a callback to generate the rpc-reply inline, instead of buffering the output. The rpc_data and rpc_filter parameters are optionally used by the rpc_datacb function to generate a reply.
-
void *rpc_datacb
For operations that return streamed data, this pointer is set to the desired callback function to use for generated the data portion of the <rpc-reply> XML response.
The template for this callback is agt_rpc_data_cb_t, found in agt_rpc.h
-
dlq_hdr_t rpc_dataQ
For operations that return stored data, this queue of val_value_t structures can be used to provide the response data.
Each val_value_t structure will be encoded as one of the corresponding RPC output parameters. The data will be returned in order. The val_value_t nodes will be freed when the rpc_msg_t is freed
-
op_filter_t rpc_filter
Internal structure for optimizing subtree and XPath retrieval operations.
backptrs for get* methods.
-
struct agt_cfg_transaction_t_ *rpc_txcb
incoming: agent database edit transaction control block must be freed by an upper layer if set to malloced data
-
boolean rpc_parse_errors
load-config parse-error and —startup-error=continue flag if the val_purge_errors_from_root function is needed
-
xmlChar *rpc_message_id
debugging and audit message string.
Contains the message-id attribute found in the <rpc> header. backptr into rpc_in_attrs.
-
xmlChar *rpc_trace_id
debugging and audit message string.
Contains the trace-id attribute found in the <rpc> header. backptr into rpc_in_attrs.
-
boolean rpc_replay_config
TRUE if this RPC is being called in replay config mode.
-
boolean rpc_with_template
—with-template parameter was seen in the validate phase
-
dlq_hdr_t hook_inputQ
points to add_edit_value node comming from the users freed in the end of transaction.
Used only with set-hook. Contains Q of val_value_t.
-
boolean rpc_defer_reply
YPSERVER mode is skipping the regular rpc-reply phase and will send the reply after doing the remote task.
-
xml_attrs_t rpc_defer_in_attrs
the top->attrs gets deleted so a deferred rpc-reply needs to save the rpc_in_attrs.
This is malloced and cleaned when rpc-msg_t is freed
-
rpc_rpytyp_t rpc_reply_type
saved reply type needed for audit record
-
status_t rpc_status
saved processing status for audit record
-
time_t rpc_start_time
saved timestamp when started for audit record
-
const xmlChar *subrpc_filespec
saved by agt_db_api.c so the ycontrol callback function can generate an external value with the DB-API subrpc response message
-
dlq_hdr_t qhdr
RPC Validate Callback Function

The RPC validate callback function is optional to use. Its purpose is to validate any aspects of an RPC operation, beyond the constraints checked by the server engine. Only 1 validate function can register for each YANG rpc statement. The standard NETCONF operations are reserved by the server engine. There is usually zero or one of these callback functions for every 'rpc' statement in the YANG module associated with the SIL code.
It is enabled with the agt_rpc_register_method function, within the phase 1 initialization callback function.
The yangdump-sdk code generator will create this SIL callback function by default. There will be C comments in the code to indicate where your additional C code should be added.
The val_find_child (or val_child_find) function is commonly used to find particular parameters within the RPC input section, which is encoded as a val_value_t tree.
The agt_record_error function is commonly used to record any parameter or other errors. In the libtoaster example, there are internal state variables ("toaster_enabled" and "toaster_toasting"), maintained by the SIL code, which are checked in addition to any provided parameters.
Example RPC YANG definition:
rpc make-toast {
description
"Make some toast.
The toastDone notification will be sent when
the toast is finished.
An 'in-use' error will be returned if toast
is already being made.
A 'resource-denied' error will be returned
if the toaster service is disabled.";
input {
leaf toasterDoneness {
type uint32 {
range "1 .. 10";
}
default 5;
description
"This variable controls how well-done is the
ensuing toast. It should be on a scale of 1 to 10.
Toast made at 10 generally is considered unfit
for human consumption; toast made at 1 is warmed
lightly.";
}
leaf toasterToastType {
type identityref {
base toast:toast-type;
}
default toast:wheat-bread;
description
"This variable informs the toaster of the type of
material that is being toasted. The toaster
uses this information, combined with
toasterDoneness, to compute for how
long the material must be toasted to achieve
the required doneness.";
}
}
}
Example SIL Function:
/********************************************************************
* FUNCTION y_toaster_make_toast_validate
*
* RPC validation phase
* All YANG constraints have passed at this point.
* Add description-stmt checks in this function.
*
* INPUTS:
* see agt/agt_rpc.h for details
*
* RETURNS:
* error status
********************************************************************/
static status_t
y_toaster_make_toast_validate (
ses_cb_t *scb,
rpc_msg_t *msg,
xml_node_t *methnode)
{
status_t res;
val_value_t *errorval;
const xmlChar *errorstr;
val_value_t *toasterDoneness_val;
val_value_t *toasterToastType_val;
uint32 toasterDoneness;
val_idref_t *toasterToastType;
res = NO_ERR;
errorval = NULL;
errorstr = NULL;
toasterDoneness_val = val_find_child(
msg->rpc_input,
y_toaster_M_toaster,
y_toaster_N_toasterDoneness);
if (toasterDoneness_val != NULL && toasterDoneness_val->res == NO_ERR) {
toasterDoneness = VAL_UINT(toasterDoneness_val);
}
toasterToastType_val = val_find_child(
msg->rpc_input,
y_toaster_M_toaster,
y_toaster_N_toasterToastType);
if (toasterToastType_val != NULL && toasterToastType_val->res == NO_ERR) {
toasterToastType = VAL_IDREF(toasterToastType_val);
}
/* added code starts here */
if (toaster_enabled) {
/* toaster service enabled, check if in use */
if (toaster_toasting) {
res = ERR_NCX_IN_USE;
} else {
/* this is where a check on bread inventory would go */
/* this is where a check on toaster HW ready would go */
}
} else {
/* toaster service disabled */
res = ERR_NCX_RESOURCE_DENIED;
}
/* added code ends here */
/* if error: set the res, errorstr, and errorval parms */
if (res != NO_ERR) {
agt_record_error(
scb,
&msg->mhdr,
NCX_LAYER_OPERATION,
res,
methnode,
NCX_NT_STRING,
errorstr,
NCX_NT_VAL,
errorval);
}
return res;
} /* y_toaster_make_toast_validate */
RPC Invoke Callback Function

The RPC invoke callback function is used to perform the operation requested by the client session. Only 1 invoke function can register for each YANG rpc statement. The standard NETCONF operations are reserved by the server engine. There is usually one of these callback functions for every 'rpc' statement in the YANG module associated with the SIL code.
The RPC invoke callback function is optional to use, although if no invoke callback is provided, then the operation will have no affect. Normally, this is only the case if the module is be tested by an application developer, using netconfd-pro as a server simulator.
It is enabled with the agt_rpc_register_method function, within the phase 1 initialization callback function.
The yangdump-sdk code generator will create this SIL callback function by default. There will be C comments in the code to indicate where your additional C code should be added.
For RPC operations that return either an <ok> or <rpc-error> response, there is nothing more required of the RPC invoke callback function.
For operations which return some data or <rpc-error>, the SIL code must do 1 of 2 additional tasks:
add a val_value_t structure to the 'rpc_dataQ' queue in the rpc_msg_t for each parameter listed in the YANG rpc 'output' section.
set the 'rpc_datacb' pointer in the rpc_msg_t structure to the address of your data reply callback function.
Example RPC Invoke SIL Function Registration
res = agt_rpc_register_method(
y_toaster_M_toaster,
y_toaster_N_make_toast,
AGT_RPC_PH_INVOKE,
y_toaster_make_toast_invoke);
if (res != NO_ERR) {
return res;
}
Example RPC Invoke SIL Function
/********************************************************************
* FUNCTION y_toaster_make_toast_invoke
*
* RPC invocation phase
* All constraints have passed at this point.
* Call device instrumentation code in this function.
*
* INPUTS:
* see agt/agt_rpc.h for details
*
* RETURNS:
* error status
********************************************************************/
static status_t
y_toaster_make_toast_invoke (
ses_cb_t *scb,
rpc_msg_t *msg,
xml_node_t *methnode)
{
status_t res;
val_value_t *toasterDoneness_val;
val_value_t *toasterToastType_val;
uint32 toasterDoneness;
val_idref_t *toasterToastType;
res = NO_ERR;
toasterDoneness = 0;
toasterDoneness_val = val_find_child(
msg->rpc_input,
y_toaster_M_toaster,
y_toaster_N_toasterDoneness);
if (toasterDoneness_val != NULL && toasterDoneness_val->res == NO_ERR) {
toasterDoneness = VAL_UINT(toasterDoneness_val);
}
toasterToastType_val = val_find_child(
msg->rpc_input,
y_toaster_M_toaster,
y_toaster_N_toasterToastType);
if (toasterToastType_val != NULL && toasterToastType_val->res == NO_ERR) {
toasterToastType = VAL_IDREF(toasterToastType_val);
}
/* invoke your device instrumentation code here */
/* make sure the toasterDoneness value is set */
if (toasterDoneness_val == NULL) {
toasterDoneness = 5; /* set the default */
}
/* arbitrary formula to convert toaster doneness to the
* number of seconds the toaster should be on
*/
toaster_duration = toasterDoneness * 12;
/* this is where the code would go to adjust the duration
* based on the bread type
*/
if (LOGDEBUG) {
log_debug("\ntoaster: starting toaster for %u seconds",
toaster_duration);
}
/* this is where the code would go to start the toaster
* heater element
*/
/* start a timer to toast for the specified time interval */
res = agt_timer_create(toaster_duration,
FALSE,
toaster_timer_fn,
NULL,
&toaster_timer_id);
if (res == NO_ERR) {
toaster_toasting = TRUE;
} else {
agt_record_error(
scb,
&msg->mhdr,
NCX_LAYER_OPERATION,
res,
methnode,
NCX_NT_NONE,
NULL,
NCX_NT_NONE,
NULL);
}
/* added code ends here */
return res;
} /* y_toaster_make_toast_invoke */
RPC Data Output Handling
RPC operations can return data to the client if the operation succeeds. The YANG “output” statement defines the return data for each RPC operation. Constructing YANG data is covered in detail elsewhere. This section shows a simple example SIL invoke function that returns data.
The output data is usually constructed with APIs like agt_make_leaf from
agt/agt_util.h
.
To use these APIs, the output object is needed.
The following code can be used to get this object:
obj_template_t *obj = RPC_MSG_METHOD(msg);
if (obj) {
obj = obj_find_child(obj, NULL, NCX_EL_OUTPUT);
}
if (obj == NULL) {
return ERR_NCX_DEF_NOT_FOUND; // should not happen
}
Once this object template is retrieved from the 'msg' parameter data can be added to the 'msg' using val_value_t structures.
The following snippet shows a leaf being created using an int32 value:
int32 result = 42;
val_value_t *val =
agt_make_int_leaf(obj,
y_addrpc_N_sum,
result,
&res);
After the value is created it must be added to the message using the 'agt_rpc_add_return_val' API:
-
void agt_rpc_add_return_val(val_value_t *return_val, rpc_msg_t *msg)
Add a return value to the msg.
- Parameters:
return_val -- value to add
msg -- message to add value into
if (val) {
agt_rpc_add_return_val(val, msg);
}
RPC Data Output Example
Example YANG Module:
module addrpc {
namespace "http://www.yumaworks.com/ns/addrpc";
prefix add;
revision "2020-02-25";
rpc add {
description "Get the sum of two numbers";
input {
leaf num1 {
type int32;
mandatory true;
description "First number to add";
}
leaf num2 {
type int32;
mandatory true;
description "Second number to add";
}
}
output {
leaf sum {
type int32;
mandatory true;
description "The sum of the 2 numbers";
}
}
}
}
Example SIL Invoke Function Returning Output Data:
/********************************************************************
* FUNCTION y_addrpc_add_invoke
*
* RPC invocation phase
* All constraints have passed at this point.
* Call device instrumentation code in this function.
*
* INPUTS:
* see agt/agt_rpc.h for details
*
* RETURNS:
* error status
********************************************************************/
static status_t y_addrpc_add_invoke (
ses_cb_t *scb,
rpc_msg_t *msg,
xml_node_t *methnode)
{
status_t res = NO_ERR;
if (LOGDEBUG) {
log_debug("\nStart SIL invoke rpc <add> from module addrpc");
}
val_value_t *inputval = agt_get_rpc_input(msg);
if (inputval == NULL) return ERR_NCX_OPERATION_FAILED;
val_value_t *v_num1_val = NULL;
int32 v_num1 = 0;
val_value_t *v_num2_val = NULL;
int32 v_num2 = 0;
v_num1_val = val_find_child(
inputval,
y_addrpc_M_addrpc,
y_addrpc_N_num1);
if (v_num1_val) {
v_num1 = VAL_INT(v_num1_val);
}
v_num2_val = val_find_child(
inputval,
y_addrpc_M_addrpc,
y_addrpc_N_num2);
if (v_num2_val) {
v_num2 = VAL_INT(v_num2_val);
}
/* remove the next line if scb is used */
(void)scb;
/* remove the next line if methnode is used */
(void)methnode;
/* invoke your device instrumentation code here */
/* Following output nodes expected:
* leaf sum
*/
int32 result = v_num1 + v_num2;
obj_template_t *obj = RPC_MSG_METHOD(msg);
if (obj) {
obj = obj_find_child(obj, NULL, NCX_EL_OUTPUT);
}
if (obj == NULL) {
return ERR_NCX_DEF_NOT_FOUND; // should not happen
}
val_value_t *val =
agt_make_int_leaf(obj,
y_addrpc_N_sum,
result,
&res);
if (val) {
agt_rpc_add_return_val(val, msg);
}
return res;
} /* y_addrpc_add_invoke */
RPC Post Reply Callback Function

The RPC post-reply callback function is used to clean up after a message has been processed. Only 1 function can register for each YANG rpc statement. The standard NETCONF operations are reserved by the server engine. This callback is not needed unless the SIL validate or invoke callback allocated some memory that needs to deleted after the <rpc-reply> is sent.
The RPC post reply callback function is optional to use. It is enabled with the 'agt_rpc_register_method' function, within the phase 1 initialization callback function.
The yangdump-pro code generator will not create this SIL callback function by default.
Example SIL Function Registration
Example SIL Function:
/********************************************************************
* FUNCTION y_foo_command_post
*
* RPC post reply phase
*
* INPUTS:
* see agt/agt_rpc.h for details
*
* RETURNS:
* error status
********************************************************************/
static status_t
y_foo_command_post (
ses_cb_t *scb,
rpc_msg_t *msg,
xml_node_t *methnode)
{
(void)scb;
(void)methnode;
if (msg->rpc_user1 != NULL) {
m__free(msg->rpc_user1);
msg->rpc_user1 = NULL;
}
return NO_ERR;
} /* y_foo_command_post */