The server supports RPC operations defined in any YANG module, and the code can be contained in a SIL library.

There are 3 simple steps for returning data in an RPC operation:

  1. Create a val_value_t node for each data node to be returned
  2. Add data to the rpc_dataQ in the repsonse message
  3. Set the data type in the response message


    dlq_enque(msgindentval, &msg->rpc_dataQ);
    msg->rpc_data_type = RPC_DATA_YANG;



Consider this simple RPC operation <get-my-session> from sample-mysession.yang:


module sample-mysession {

  namespace "http://netconfcentral.org/ns/sample-mysession";

  prefix "myses";

  import yuma-app-common { prefix appcmn; }
  import ietf-netconf-with-defaults { prefix wd; }

 grouping session-params {
    uses appcmn:IndentParm;
      
    leaf linesize {
      description
        "The desired maximum number of characters printed
         per line.  The server may exceed this number.
         It is only a suggestion, not a hard limit.";
      type uint32 { range "40 .. 1024"; }
      default 72;
    }
    leaf with-defaults {
      description
        "The desired maximum number of characters printed
         per line.  The server may exceed this number.
         It is only a suggestion, not a hard limit.";
      type wd:with-defaults-mode;
    }
    leaf message-indent {
      type int8 {
        range "-1 .. 9";
      }
      default -1;
      description
        "The number of spaces to indent for each level of
         output in a protocol message, e.g. NETCONF request.
         The value zero means no indent, just line feeds.
         The value -1 means no indent and no line feeds either.";
    }
  }

  rpc get-my-session {
    description
      "Get the customization settings for this session";
    output {
      uses session-params;
    }
  }        

  rpc set-my-session {
    description
      "Set the customization settings for this session.
       This is like a merge operation.  Only the values that
       are present will be used to overwrite the existing
       settings.";
    input {
      uses session-params;
    }
  }        

}



Since there are no input parameters for the <get-my-session> operation, there is no need for an RPC validate callback. The RPC invoke callback is needed. The source code from sample-mysession.c is shown below:


#include <xmlstring.h>

#include "procdefs.h"
#include "agt.h"
#include "agt_action.h"
#include "agt_cb.h"
#include "agt_rpc.h"
#include "agt_sil_lib.h"
#include "agt_timer.h"
#include "agt_util.h"
#include "dlq.h"
#include "ncx.h"
#include "ncx_feature.h"
#include "ncxmod.h"
#include "ncxtypes.h"
#include "rpc.h"
#include "ses.h"
#include "status.h"
#include "val.h"
#include "val_util.h"
#include "xml_util.h"
#include "sample-mysession.h"

/* module static variables */
static ncx_module_t *sample_mysession_mod;
static obj_template_t *get_my_session_obj;
static obj_template_t *set_my_session_obj;

/* put your static variables here */

/********************************************************************
* FUNCTION y_sample_mysession_init_static_vars
*
* initialize module static variables
*
********************************************************************/
static void y_sample_mysession_init_static_vars (void)
{
    sample_mysession_mod = NULL;
    get_my_session_obj = NULL;
    set_my_session_obj = NULL;

    /* init your static variables here */

} /* y_sample_mysession_init_static_vars */


/********************************************************************
* FUNCTION y_sample_mysession_get_my_session_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_sample_mysession_get_my_session_invoke (
    ses_cb_t *scb,
    rpc_msg_t *msg,
    xml_node_t *methnode)
{
    (void)methnode;

    xmlChar numbuff[NCX_MAX_NUMLEN];

    /* get the namespace ID of the module
    * from the ncx_module_t struct that is
    * set in the init function
    */
    xmlns_id_t nsid = sample_mysession_mod->nsid;

    /* indent */
    snprintf((char *)numbuff, sizeof(numbuff), "%d", scb->indent);
    val_value_t *indentval =
        val_make_string(nsid, NCX_EL_INDENT, numbuff);
    if (indentval == NULL) {
        return ERR_INTERNAL_MEM;
    }

    /* linesize */
    snprintf((char *)numbuff, sizeof(numbuff), "%u", scb->linesize);
    val_value_t *linesizeval =
        val_make_string(nsid, NCX_EL_LINESIZE, numbuff);
    if (linesizeval == NULL) {
        val_free_value(indentval);
        return ERR_INTERNAL_MEM;
    }

    /* with-defaults */
    val_value_t *withdefval =
        val_make_string(nsid,
                        NCX_EL_WITH_DEFAULTS,
                        ncx_get_withdefaults_string(scb->withdef));
    if (withdefval == NULL) {
        val_free_value(indentval);
        val_free_value(linesizeval);
        return ERR_INTERNAL_MEM;
    }

    /* message-indent */
    snprintf((char *)numbuff, sizeof(numbuff), "%d", scb->msg_indent);
    val_value_t *msgindentval =
        val_make_string(nsid, NCX_EL_MESSAGE_INDENT, numbuff);
    if (msgindentval == NULL) {
        val_free_value(indentval);
        val_free_value(linesizeval);
        val_free_value(withdefval);
        return ERR_INTERNAL_MEM;
    }

    dlq_enque(indentval, &msg->rpc_dataQ);
    dlq_enque(linesizeval, &msg->rpc_dataQ);
    dlq_enque(withdefval, &msg->rpc_dataQ);
    dlq_enque(msgindentval, &msg->rpc_dataQ);

    msg->rpc_data_type = RPC_DATA_YANG;
    return NO_ERR;

} /* y_sample_mysession_get_my_session_invoke */


/********************************************************************
* FUNCTION y_sample_mysession_set_my_session_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_sample_mysession_set_my_session_invoke (
    ses_cb_t *scb,
    rpc_msg_t *msg,
    xml_node_t *methnode)
{
    (void)methnode;

   /* get the input value, either already set or in the rpc_inputQ
    * expecting only 1 node in the Q even though valid JSON could
    * send more than 1 top-level object to edit
    */

    val_value_t *inputval = msg->rpc_input;
    if (!dlq_empty(&msg->rpc_inputQ)) {
        inputval = (val_value_t *)dlq_firstEntry(&msg->rpc_inputQ);
    }

    /* get the indent parameter */
    val_value_t *childval =
        val_find_child(inputval,
                       y_sample_mysession_M_sample_mysession,
                       NCX_EL_INDENT);
    if (childval && childval->res == NO_ERR) {
        scb->indent = VAL_INT8(childval);
    }

    /* get the linesize parameter */
    childval = val_find_child(inputval,
                              y_sample_mysession_M_sample_mysession,
                              NCX_EL_LINESIZE);
    if (childval && childval->res == NO_ERR) {
        scb->linesize = VAL_UINT32(childval);
    }

    /* get the with-defaults parameter */
    childval = val_find_child(inputval,
                              y_sample_mysession_M_sample_mysession,
                              NCX_EL_WITH_DEFAULTS);
    if (childval && childval->res == NO_ERR) {
        scb->withdef =
            ncx_get_withdefaults_enum(VAL_ENUM_NAME(childval));
    }

    /* get the message-indent parameter */
    childval = val_find_child(inputval,
                              y_sample_mysession_M_sample_mysession,
                              NCX_EL_MESSAGE_INDENT);
    if (childval && childval->res == NO_ERR) {
        scb->msg_indent = VAL_INT8(childval);
    }

    return NO_ERR;

} /* y_sample_mysession_set_my_session_invoke */


/********************************************************************
* FUNCTION y_sample_mysession_init
*
* initialize the sample-mysession server instrumentation library
*
* INPUTS:
*    modname == requested module name
*    revision == requested version (NULL for any)
*
* RETURNS:
*     error status
********************************************************************/
status_t y_sample_mysession_init (
    const xmlChar *modname,
    const xmlChar *revision)
{
    status_t res = NO_ERR;

    y_sample_mysession_init_static_vars();

    /* change if custom handling done */
    if (xml_strcmp(modname, y_sample_mysession_M_sample_mysession)) {
        return ERR_NCX_UNKNOWN_MODULE;
    }

    if (revision && xml_strcmp(revision, y_sample_mysession_R_sample_mysession)) {
        return ERR_NCX_WRONG_VERSION;
    }


    res = ncxmod_load_module(
        y_sample_mysession_M_sample_mysession,
        y_sample_mysession_R_sample_mysession,
        agt_get_savedevQ(),
        &sample_mysession_mod);
    if (res != NO_ERR) {
        return res;
    }

    get_my_session_obj = ncx_find_object(
        sample_mysession_mod,
        y_sample_mysession_N_get_my_session);
    if (get_my_session_obj == NULL) {
        return ERR_NCX_DEF_NOT_FOUND;
    }

    set_my_session_obj = ncx_find_object(
        sample_mysession_mod,
        y_sample_mysession_N_set_my_session);
    if (set_my_session_obj == NULL) {
        return ERR_NCX_DEF_NOT_FOUND;
    }

    res = agt_rpc_register_method(
        y_sample_mysession_M_sample_mysession,
        y_sample_mysession_N_get_my_session,
        AGT_RPC_PH_INVOKE,
        y_sample_mysession_get_my_session_invoke);
    if (res != NO_ERR) {
        return res;
    }

    res = agt_rpc_register_method(
        y_sample_mysession_M_sample_mysession,
        y_sample_mysession_N_set_my_session,
        AGT_RPC_PH_INVOKE,
        y_sample_mysession_set_my_session_invoke);
    if (res != NO_ERR) {
        return res;
    }

    /* put your module initialization code here */

    return res;

} /* y_sample_mysession_init */

/********************************************************************
* FUNCTION y_sample_mysession_init2
*
* SIL init phase 2: non-config data structures
* Called after running config is loaded
*
* RETURNS:
*     error status
********************************************************************/
status_t y_sample_mysession_init2 (void)
{
    status_t res = NO_ERR;

    /* put your init2 code here */

    return res;

} /* y_sample_mysession_init2 */

/********************************************************************
* FUNCTION y_sample_mysession_cleanup
*    cleanup the server instrumentation library
*
********************************************************************/
void y_sample_mysession_cleanup (void)
{

    agt_rpc_unregister_method(
        y_sample_mysession_M_sample_mysession,
        y_sample_mysession_N_get_my_session);

    agt_rpc_unregister_method(
        y_sample_mysession_M_sample_mysession,
        y_sample_mysession_N_set_my_session);

    /* put your cleanup code here */

} /* y_sample_mysession_cleanup */

/* END SIL sample_mysession.c */



The output of the RPC request looks as below


{
    "sample-mysession:output": {
        "indent": "2",
        "linesize": "72",
        "message-indent": "-1",
        "with-defaults": "explicit"
    }
}