System Callbacks

The callback functions described in this section are invoked as part of normal server operation.

They can be registered from the yp-system library module or a SIL module. Some internal functions are documented as well. These are needed by the system and multiple callbacks are not supported.

Refer to the file /usr/share/yumapro/src/libsystem/src/example-system.c for examples of the callbacks in this section.

Note

System Callback code can be in a SIL library or the yp-system library.

API

Description

Candidate Reload Callback

cfg_reload_candidate_cb_t: Invoked each time the candidate datastore is reloaded from the running datastore, such as the <discard-changes> operation.

Module Load Callback

ncx_load_cbfn_t: Invoked when a module is loaded into the server.

Module Unload Callback

ncx_unload_cbfn_t: Invoked when a module is unloaded from the server.

Config Replay Callback

agt_replay_fn_t: Invoked when a configuration replay procedure is started or finished.

NV-Load Callback

agt_nvload_fn_t: Invoked when the server needs to retrieve the configuration contents from non-volatile storage.

NV-Save Callback

agt_nvsave_fn_t: Invoked when the configuration needs to be saved to Non-Volatile storage.

Periodic Timer Service

agt_timer_fn_t: Invoked periodically (1-shot or repeat) to do a short amount of work, and not block the running process.

Session Hook Callback

agt_cb_session_hook_t: Invoked when a session starts and ends.

Shutdown Callback

agt_cb_shutdown_t: Invoked when the server is about to restart or shutdown.

Command Complete Callback

agt_cb_command_complete_t: Invoked when the server has completed an RPC operation and the response has been generated to the client (may not have been sent yet).

NACM External Groups Callback

agt_acm_group_cbfn_t: Invoked when the server creates a new client session for NETCONF or RESTCONF sessions. It is used to retrieve a list of group names that should be used, as defined in RFC 8341

User Defined Data Types Callbacks

Invoked when a data node needs to be processed to process the value as it is being created.

Candidate Reload Callback

The Candidate Reload callback is an internal system callback that is used to clear some cached validation data when the running configuration is loaded into to candidate datastore.

Note

This is an internal API and cannot be replaced. Do not use.

There can be one callback registered to be invoked each time the candidate datastore is reloaded from the running datastore, such as the <discard-changes> operation, or anytime the candidate is filled in from <startup>, <running>, or inline configuration.

If the server is run with the :writable-running capability the callback will not be available since the <candidate> datastore is not present when the capability is used. The --target CLI parameter is used to control this behavior.

Module Load Callback

The Module Load callback is a user/system callback that is invoked when a YANG module is loaded into the server. This callback is only invoked for main modules, not submodules.

The following function template definition is used for Module Load callback functions:

typedef void (*ncx_load_cbfn_t)(ncx_module_t *mod)

user function callback template when a module is loaded into the system

ncx_load_cbfn_t

Run an instrumentation-defined function for a 'module-loaded' event

Param mod

module that was added to the registry

  • The 'mod' pointer represents a module control block structure that is defined in the netconf/src/ncx/ncxtypes.h.

  • This control block is a representation of one module or submodule during and after parsing.

  • It provides access to the module specific information, such as module name, revision, prefix, and other module specific information.

  • Note: do not alter any fields in this structure

Module Load Callback Initialization and Cleanup

The 'ncx_set_load_callback' function is used to declare the Module Load callback. The registration can be done during the Initialization Phase 2 after the running configuration has been loaded from the startup file. It can actually be done at any time, as needed.

status_t ncx_set_load_callback(ncx_load_cbfn_t cbfn)

Set the callback function for a load-module event.

Parameters

cbfn -- callback function to use

Returns

status

Example registration function usage:

 static status_t interfaces_init2 (void)
 {
     // ...

     /* register load module callback */
     res = ncx_set_load_callback(add_module_callback);
     if (res != NO_ERR) {
         return res;
     }

     // ...
 }

The 'ncx_clear_load_callback' function is used to unload the Module Load callback.

void ncx_clear_load_callback(ncx_load_cbfn_t cbfn)

Clear the callback function for a load-module event.

Parameters

cbfn -- callback function to use

Example un-register function usage:

 void interfaces_cleanup (void)
 {
     // ...
     ncx_clear_load_callback(add_module_callback);

     ...
 }

Module Load Callback Function Example

The 'load_module_callback' function is used to to record a backptr for each module or imported module that is encountered during the <load> operation.

static void load_module_callback (ncx_module_t *mod)
{
    ncx_backptr_t *backptr = ncx_new_backptr(mod);
    if (backptr == NULL) {
        log_error("\nError: malloc backptr failed");
        return;
    }
    dlq_enque(backptr, &added_moduleQ);

}  /* load_module_callback */

The 'do_load_module' function is located in agt/agt_ncx_load.c. It is used to load a module at runtime. It does the following operations:

  • registers the callback

  • saves backptrs during the module load

  • reads the saved backptrs after the module load

  • unregisters the callback

  • cleans up the saved backptrs

static status_t
    do_module_load (ses_cb_t *scb,
                    rpc_msg_t *msg,
                    const xmlChar *modname,
                    const xmlChar *revision,
                    ncx_module_t **retmod)
{
    boolean callback_loaded = FALSE;
    ncx_module_t *mod = NULL;
    boolean already_loaded =
        ncx_find_module(modname, revision) ? TRUE : FALSE;

    *retmod = NULL;

    /* save all the loaded modules including imported modules */
    status_t res = ncx_set_load_callback(load_module_callback);
    if (res == NO_ERR) {
        callback_loaded = TRUE;
    } else {
        return res;
    }

#ifdef STATIC_SERVER
    res = ncxmod_load_module(modname,
                             revision,
                             agt_get_savedevQ(),
                             &mod);
#else
    res = agt_load_sil_code(modname,
                            revision,
                            TRUE,
                            AGT_SILTYP_MODULE,
                            FALSE);
    if (res == ERR_NCX_SKIPPED) {
        log_warn("\nWarning: SIL code for module '%s' not found",
                 modname);
        res = ncxmod_load_module(modname,
                                 revision,
                                 agt_get_savedevQ(),
                                 &mod);
        if (res == NO_ERR && mod) {
            ncx_set_mod_unloadable(mod);
        }
    }
#endif  // STATIC_SERVER

    /* reget the module; it should be found if status == NO_ERR */
    if (res == NO_ERR) {
        mod = ncx_find_module(modname, revision);
        if (mod) {
            /* use the list in added_moduleQ not just this 1 module
             * this tracks the imports loaded dynamically as well
             * as the main module loaded by the <load> operation
             */
            res = process_loaded_modules(scb, msg, NULL);
        } else {
            res = ERR_NCX_OPERATION_FAILED;
        }
    }

    if (callback_loaded) {
        ncx_clear_load_callback(load_module_callback);
    }

    /* done with the added_moduleQ; just used to process modules
     * not to save the list of loaded modules (load_moduleQ)
     */
    ncx_clean_backptrQ(&added_moduleQ);

    if (res == NO_ERR) {
        SET_MOD_SIL_LOADED(mod);
        *retmod = mod;
        if (!already_loaded) {
            /* add to the load_moduleQ */
            add_loaded_module(modname);
        }
    }

    return res;

} /* do_module_load */

The following functions may be used to obtain required information from the loaded module:

  • ncx_get_modname() - Get the main module name

  • ncx_get_mod_nsid() - Get the main module namespace ID

  • ncx_get_modversion() - Get the module version

  • ncx_get_modnamespace() - Get the module namespace URI

const xmlChar *ncx_get_modname(const ncx_module_t *mod)

Get the main module name.

Parameters

mod -- module or submodule to get main module name

Returns

main module name or NULL if error

xmlns_id_t ncx_get_mod_nsid(const ncx_module_t *mod)

Get the main module namespace ID.

Parameters

mod -- module or submodule to get main module namespace ID

Returns

namespace id number

const xmlChar *ncx_get_modversion(const ncx_module_t *mod)

Get the [sub]module version.

Parameters

mod -- module or submodule to get module version

Returns

module version or NULL if error

const xmlChar *ncx_get_modnamespace(const ncx_module_t *mod)

Get the module namespace URI.

Parameters

mod -- module or submodule to get module namespace

Returns

module namespace or NULL if error

Module Unload Callback

The Module Unload callback is a user/system callback that is invoked when a YANG module is unloaded from the server. This callback is only invoked for main modules, not submodules.

The following function template definition is used for Module Unload callback functions:

typedef void (*ncx_unload_cbfn_t)(ncx_module_t *mod)

user function callback template when a module is unloaded from the system

ncx_unload_cbfn_t

Run an instrumentation-defined function for a 'module-loaded' event

Param mod

module that is being removed

  • The 'mod' pointer represents a module control block structure that is defined in the netconf/src/ncx/ncxtypes.h.

  • This control block is a representation of one module or submodule during and after parsing.

  • It provides access to the module specific information, such as module name, revision, prefix, and other module specific information.

  • Note: do not alter any fields in this structure

Module Unload Callback Initialization and Cleanup

The 'ncx_set_unload_callback' function is used to declare the Module Unload callback. The registration can be done during the Initialization Phase 2 after the running configuration has been loaded from the startup file. It can actually be done at any time, as needed.

status_t ncx_set_unload_callback(ncx_unload_cbfn_t cbfn)

Set the callback function for an unload-module event.

Parameters

cbfn -- callback function to use

Returns

status

Example Module Unload register usage:

 static status_t interfaces_init2 (void)
 {
     // ...

     /* register unload module callback */
     res = ncx_set_unload_callback(remove_module_callback);
     if (res != NO_ERR) {
         return res;
     }

     // ...
 }

The 'ncx_clear_unload_callback' function is used to unload the Module Unload callback.

void ncx_clear_unload_callback(ncx_unload_cbfn_t cbfn)

Clear the callback function for an unload-module event.

Parameters

cbfn -- callback function to use

Example Module Unload un-register usage:

 void interfaces_cleanup (void)
 {
     // ...
     ncx_clear_unload_callback(remove_module_callback);

     // ...
 }

Module Unload Callback Function Example

In this example, the callback code will find a val_value_t tree, based on the information from 'mod' pointer, and remove it from the static data list.


/********************************************************************
* FUNCTION remove_module
*
* Remove a module entry from the static modules list
*
* INPUTS:
*   mod == module to remove
*
********************************************************************/
static void
    remove_module (ncx_module_t *mod)
{

    xmlns_id_t nsid = val_get_nsid(mymodules_val);

    val_value_t *module = val_get_first_child(mymodules_val);
    for (; module; module = val_get_next_child(module)) {

        val_value_t *checkval =
            val_find_child_fast(module, nsid, (const xmlChar *)"name");

        /* check the module name, if it match, remove the module from the list */
        if (checkval) {
            if (xml_strcmp(VAL_STR(checkval), ncx_get_modname(mod))) {
                continue;
            }
        } else {
            continue;  // error
        }

        /* check the module revision, if it match, remove the module from the list */
        if (ncx_get_modversion(mod)) {
            checkval =
                val_find_child_fast(module, nsid, (const xmlChar *)"revision");
            if (checkval) {
                if (xml_strcmp(VAL_STR(checkval), ncx_get_modversion(mod))) {
                    continue;
                }
            } else {
                continue;  // error!
            }
        }

        val_remove_child(module);
        val_free_value(module);
        return;
    }

}  /* remove_module */

Config Replay Callback

The Config Replay callback is a user callback that is invoked when the replay configuration procedure is started and finished.

Only one callback function can be registered.

Refer to the Configuration Replay section for complete details on triggering a Configuration Replay procedure.

The following function template definition is used for Config Replay callback functions:

typedef void (*agt_replay_fn_t)(boolean is_start)

replay callback (agt_replay_fn_t)

this callback is invoked when a configuration replay is started or finished, which has been triggered by a call to agt_request_replay()

Param is_start

TRUE if start; FALSE if finish

Config Replay Callback Initialization and Cleanup

The 'agt_register_replay_callback' function is used to register this callback.

void agt_register_replay_callback(agt_replay_fn_t cbfn)

Register a callback function for a replay config event.

Parameters

cbfn -- replay function to use (==NULL to clear)

Example Config Replay register usage:

 static status_t interfaces_init2 (void)
 {
     // ...

     /* register config replay callback */
     res = agt_register_replay_callback(replay_callback);
     if (res != NO_ERR) {
         return res;
     }

     // ...
 }

There is no unregister function for this callback. The server will cleanup the system automatically during shutdown.

Config Replay Callback Example

static void replay_callback (boolean is_start)
{
    if (is_start) {
        log_debug("\nConfig Replay is starting");
    } else {
        log_debug("\nConfig Replay is ending");
    }
}

NV-Load Callback

The NV-Load callback function is a user callback that is invoked when the running configuration needs to be read from non-volatile storage.

  • This callback is used to provide the server with an XML file instead of the default from the --config CLI parameter.

  • The server will invoke this callback to get the file name and file encoding of the startup configuration file.

  • Only XML encoding is supported at this time.

  • Only one callback can be registered.

  • The check_load_external_config function in agt/agt.c will check this callback during the <load-config> operation.

  • This callback will not be used if the --no-nvstore CLI parameter is used.

Note

If the callback returns NO_ERR and *filespec is empty then the server will use the factory configuration.

The following function template definition is used for the NV-Load callback function:

typedef status_t (*agt_nvload_fn_t)(ncx_display_mode_t *encoding, xmlChar **filespec)

nvload callback

this callback is invoked when some config needs to be read from non-volatile storage

Param encoding

address of encoding for the config

Param filespec

address of filespec containing the config that was loaded

Retval *encoding

set to the enum for the encoding used in the config

Retval *filespec

malloced filespec containing the config that was loaded

== NULL if loading the factory default config

Return

status; error indicates NV-load failed somehow If return NO_ERR and *filespec == NULL then use the factory config

NV-Load Callback Initialization and Cleanup

The 'agt_register_local_nv_handler' function is used to declare the NV-Load callback. The registration can be done during the Initialization Phase 2, after the running configuration has been loaded from the startup file.

status_t agt_register_local_nv_handler(agt_nvload_fn_t load_fn, agt_nvsave_fn_t store_fn)

Register a set of nvstore and nvload callback functions to handle the non-volatile storage of the configuration.

Parameters
  • load_fn -- NV-load callback function

  • store_fn -- NV-save callback function

Returns

status

 static status_t interfaces_init2 (void)
 {
     // ...

     /* register NV-storage handler to load/save config
      * uncomment following to enable
      */
     res = agt_register_local_nv_handler(nvload_callback,
                                         nvsave_callback);
     if (res != NO_ERR) {
         return res;
     }

     // ...
 }

There is no unregister function for this callback. The cleanup will be done automatically by the server.

NV-Load Callback Function Example

/********************************************************************
* FUNCTION nvload_callback
*
* this callback is invoked when some config needs to be read
* from non-volatile storage
*
* INPUTS:
*    encoding == address of encoding for the config
*    filespec == address of filespec containing the config that was loaded
*
* OUTPUTS:
*    *encoding == set to the enum for the encoding used in the config
*    *filespec == malloced filespec containing the config that was loaded
*
* RETURNS:
*    status; error indicates NV-load failed somehow
*    If return NO_ERR and *filespec == NULL then use the factory config
*
*********************************************************************/
static status_t
    nvload_callback (ncx_display_mode_t *encoding,
                    xmlChar **filespec)
{
    log_debug("\nEnter nvload_callback ");

    *filespec = NULL;
    *encoding = NCX_DISPLAY_MODE_XML;

    status_t res = NO_ERR;

    if (ncxmod_test_filespec(EXAMPLE_CONFIG_SPEC)) {

        /* file exists so copy the filespec */
        *filespec = xml_strdup(EXAMPLE_CONFIG_SPEC);
        if (*filespec == NULL) {
            res = ERR_INTERNAL_MEM;
        }
    }

    return res;

}  /* nvload_callback */

The following function is used in the example to search for the specified filespec:

boolean ncxmod_test_filespec(const xmlChar *filespec)

Check the exact filespec to see if it a file.

Parameters

filespec -- file spec to check

Returns

TRUE if valid readable file; FALSE otherwise

NV-Save Callback

The NV-Save callback function is a user callback that is invoked when the running configuration needs to be saved to non-volatile storage.

  • This callback is used to provide the system with an XML file to save, instead of the default from the --config CLI parameter.

  • The server will invoke this callback to pass the file name and file encoding of the configuration file to save.

  • Only XML encoding is supported at this time.

  • Only one callback can be registered.

  • The agt_check_save_external_config function in agt/agt.c will check this callback during the "save-config" procedure.

The following function template definition is used for NV-Save callback functions:

typedef status_t (*agt_nvsave_fn_t)(ncx_display_mode_t encoding, const xmlChar *filespec)

brief nvsave callback

this callback is invoked when some config needs to be saved to non-volatile storage

Param encoding

encoding format for the config (xml only allowed value)

Param filespec

filespec containing the config to save

Return

status; error indicates NV-save failed somehow

NV-Save Callback Initialization and Cleanup

This callback is regered at the same time as the NV Load callback using the 'agt_register_local_nv_handler' function.

There is no cleanup function for this callback. The cleanup will be done automatically by the server.

Refer to the NV-Load Callback Initialization and Cleanup section for details.

NV-Save Callback Function Example

The following example can be found in example-system.c.

#define EXAMPLE_CONFIG_SPEC (const xmlChar *)"/tmp/example-config.xml"
/********************************************************************
* FUNCTION example_nvsave
*
* Nvsave callback is invoked when some config needs to be saved
* to non-volatile storage
*
* INPUTS:
*    encoding == encoding format for the config (xml only allowed value)
*    filespec == filespec containing the config to save
*
* RETURNS:
*    status; error indicates NV-save failed somehow
*
*********************************************************************/
static status_t
  example_nvsave (ncx_display_mode_t encoding,
                  const xmlChar *filespec)
{
    status_t res = NO_ERR;

    if (filespec == NULL || *filespec == 0) {
        res = ERR_NCX_INVALID_VALUE;
    } else if (encoding != NCX_DISPLAY_MODE_XML) {
        res = ERR_NCX_INVALID_VALUE;
    } else {
        res = ncxmod_copy_text_file(filespec, EXAMPLE_CONFIG_SPEC);
    }

    return res;

}  /* example_nvsave */

Periodic Timer Service

Some SIL code may need to be called at periodic intervals to check system status, update counters, and/or perhaps send notifications.

The file agt/agt_timer.h contains the timer access function declarations.

This section provides a brief overview of the SIL timer service.

Timer Callback Function

The timer callback function is expected to do a short amount of work, and not block the running process. The function returns zero for a normal exit, and -1 if there was an error and the timer should be destroyed.

The 'agt_timer_fn_t' template in agt/agt_timer.h is used to define the SIL timer callback function prototype. This typedef defines the callback function template expected by the server for use with the timer service:

typedef int (*agt_timer_fn_t)(uint32 timer_id, void *cookie)

timer callback function

Provided the the SIL code registering the timer. It is expected to process the timer expired event.

Param timer_id

timer identifier

Param cookie

context pointer, such as a session control block, passed to agt_timer_set function (may be NULL)

Return

0 normal exit

-1 error exit, delete timer upon return

Timer Access Functions

A SIL timer can be set up as a periodic timer or a one-time event.

The timer interval (in seconds) and the SIL timer callback function are provided when the timer is created. A timer can also be restarted if it is running, and the time interval can be changed as well.

The following table highlights the SIL timer access functions in agt/agt_timer.h:

SIL Timer Access Functions

agt_timer_create

Create a SIL timer.

agt_timer_restart

Restart a SIL timer

agt_timer_delete

Delete a SIL timer

status_t agt_timer_create(uint32 seconds, boolean is_periodic, agt_timer_fn_t timer_fn, void *cookie, uint32 *ret_timer_id)

Malloc and start a new timer control block.

Main User API - FOr SIL only at this time

Parameters
  • seconds -- number of seconds to wait between polls

  • is_periodic --

    TRUE if periodic timer

    FALSE if a 1-event timer

  • timer_fn -- address of callback function to invoke when the timer poll event occurs

  • cookie -- address of user cookie to pass to the timer_fn ret_timer_id address of return timer ID

  • ret_timer_id -- address of return timer ID

Return values

*ret_timer_id -- timer ID for the allocated timer, if the return value is NO_ERR

Returns

NO_ERR if all okay, the minimum spare requests will be malloced

status_t agt_timer_restart(uint32 timer_id, uint32 seconds)

Restart a timer with a new timeout value.

If this is a periodic timer, then the interval will be changed to the new value. Otherwise a 1-shot timer will just be reset to the new value

Parameters
  • timer_id -- timer ID to reset

  • seconds -- new timeout value

Returns

status, NO_ERR if all okay,

void agt_timer_delete(uint32 timer_id)

Remove and delete a timer control block.

Periodic timers need to be deleted to be stopped 1-shot timers will be deleted automatically after they expire and the callback is invoked

Parameters

timer_id -- timer ID to destroy

Example Timer Callback Function

The following example from toaster.c simply completes the toast when the timer expires and calls the auto-generated 'toastDone' send notification function:

/********************************************************************
* FUNCTION toaster_timer_fn
*
* Added timeout function for toaster function
*
* INPUTS:
*     see agt/agt_timer.h
*
* RETURNS:
*     0 for OK; -1 to kill periodic timer
********************************************************************/
static int
    toaster_timer_fn (uint32 timer_id,
                      void *cookie)
{
    (void)timer_id;
    (void)cookie;

    /* toast is finished */
    toaster_toasting = FALSE;
    toaster_timer_id = 0;
    if (LOGDEBUG2) {
        log_debug2("\ntoast is finished");
    }
    y_toaster_toastDone_send((const xmlChar *)"done");
    return 0;

} /* toaster_timer_fn */

Session Hook Callback

The Session Hook callback function is a user callback that is invoked when a session starts and ends.

This callback is invoked before any data structures and components have been cleaned up and their data destroyed. It can be used to perform session end or start tasks that are not related to a specific YANG module.

  • Max Callbacks: No limit (except available heap memory)

  • File: agt/agt\_cb.h

The following function template definition is used for Session Hook callback functions:

typedef void (*agt_cb_session_hook_t)(ncx_ses_event_t ses_event, const ses_cb_t *scb)

Typedef of the agt_cb_session_hook_t callback.

The Session Hook callback is invoked when a session starts and ends. Use ses.h macros like SES_MY_USERNAME(scb) to get data like client username. Max Callbacks: No limit (except available heap memory)

Param ses_event

session event type

Param scb

session control block for the session event

Session Hook Callback Initialization and Cleanup

The 'agt_cb_session_hook_register' function is used to declare the Session Hook callback. The registration can be done during the Initialization Phase, before or after the running configuration has been loaded from the startup file.

status_t agt_cb_session_hook_register(agt_cb_session_hook_t cbfn)

Register a Session Hook callback.

Max Callbacks: No limit (except available heap memory)

Parameters

cbfn -- address of callback function to use

Returns

the status of the operation.

Initialization function with the Session Hook callback registration may look as follows:

 status_t
     y_yumaworks_server_init (const xmlChar *modname,
                              const xmlChar *revision)
 {
     status_t res = NO_ERR;

     // ... load module, etc.

     /* Register a Session Hook callback */
     res = agt_cb_session_hook_register(session_hook_callback);

     return res;
 }

The 'agt_cb_session_hook_unregister' function is used to cleanup this callback.

void agt_cb_session_hook_unregister(agt_cb_session_hook_t cbfn)

Unregister a Session Hook callback.

This function unregisters a Session Hook callback.

Parameters

cbfn -- address of callback function to use

The following example code illustrates how the Session Hook callback can be cleaned up.

 void y_yumaworks_server_cleanup (void)
 {
     // ...

     /* Unregister a Session Hook callback. */
     agt_cb_session_hook_unregister(session_hook_callback);

     // ...
 }

Session Hook Callback Function Example

In the following example we are going to register Session Hook callbacks. In the callback function we are going to check if a session that is getting started is a RESTCONF session and the input encoding is XML and then will generate specific logging information for the current session. Refer to the attached YANG module and its SIL code for a complete example.

/********************************************************************
* FUNCTION session_hook_callback
*
* The Session Hook callback is invoked when a session starts
* and ends.
*
* INPUTS:
*    ses_event == session event type
*    scb == session control block for the session event
*
* RETURNS:
*   status
********************************************************************/
static void
    session_hook_callback (ncx_ses_event_t ses_event,
                           const ses_cb_t *scb)
{
    if (LOGDEBUG) {
        log_debug("\n\nEnter Session Hook callback for %[email protected]%s",
                 SES_MY_USERNAME(scb) ? SES_MY_USERNAME(scb) : NCX_EL_NOBODY,
                 SES_PEERADDR(scb) ? SES_PEERADDR(scb) : NCX_EL_LOCALHOST);
    }

    /* Perform specific tasks when the session is ended or started
     * and only if the current session protocol is RESTCONF
     * and the session has an input that is encoded in XML
     */
    ncx_display_mode_t input_mode = SES_IN_ENCODING(scb);
    if (input_mode == NCX_DISPLAY_MODE_XML) {

        if (SES_PROTOCOL(scb) == NCX_PROTO_RESTCONF) {
            if (ses_event == NCX_SES_EVENT_START) {

                if (LOGDEBUG) {
                    log_debug("\n ++++ Session Start ++++");
                }
            } else if (ses_event == NCX_SES_EVENT_END) {

                if (LOGDEBUG) {
                    log_debug("\n ---- Session END ----");
                }
            }
        }
    }

    return;

}  /* session_hook_callback */

Shutdown Callback

The Shutdown callback function is a user callback that is invoked when the server is about to restart or shutdown.

This callback is invoked before any data structures and components have been cleaned up and their data destroyed. It can be used to perform on-exit tasks that are not related to a specific YANG module.

  • Max Callbacks: No limit (except available heap memory)

The following function template definition is used for Shutdown callback functions:

typedef void (*agt_cb_shutdown_t)(void)

Typedef of the agt_cb_shutdown_t callback.

The Shutdown callback is the user/system callback that is invoked at the start of a normal agt_cleanup A normal shutdown or restart is in progress. This callback is not invoked if the server crashes.

Max Callbacks: No limit (except available heap memory)

Shutdown Callback Initialization and Cleanup

The 'agt_cb_shutdown_register' function is used to declare the Shutdown callback. The registration can be done during the Initialization Phase 2, before or after the running configuration has been loaded from the startup file.

status_t agt_cb_shutdown_register(agt_cb_shutdown_t cbfn)

Register a Shutdown callback.

Max Callbacks: No limit (except available heap memory)

Parameters

cbfn -- address of callback function to use

Returns

the status of the operation.

Example Initialization function with the Shutdown callback registration:

 static status_t server_init (void)
 {
     // ..

     res = agt_cb_shutdown_register(server_shutdown_cb);

     // ..
 }

There is no cleanup function for this callback. The cleanup will be done automatically by the server.

Shutdown Callback Function Example

The following example code shows a Shutdown callback:

static void server_shutdown_cb (void)
{

     // do some platform-specific cleanup

}

Command Complete Callback

The Command Complete callback function is a user callback that is invoked when the server has completed an RPC operation and the response has been generated to the client (may not have been sent yet). This callback can be used instead of a command-specific Post-RPC-Reply callback function.

  • Max Callbacks: No limit (except available heap memory)

The following function template definition is used for Command Complete callback functions:

typedef void (*agt_cb_command_complete_t)(ses_cb_t *scb, rpc_msg_t *msg, const xmlChar *command_modname, const xmlChar *command_name)

Typedef of the agt_command_complete_cb_t callback.

The Command Complete callback is the user/system callback that is invoked after each client command is executed for the NETCONF or RESTCONF protocols.

The Command Complete is typically used for retrieval operations (get, get-config, get-bulk) to release resources used during GET2 callbacks invoked during the operation.

Max Callbacks: No limit (except available heap memory)

Param scb

session control block making the request

Param msg

incoming rpc_msg_t in progress

Param command_modname

YANG module name of command that is completed

Param command_name

YANG RPC object name of command that is completed

Command Complete Callback Initialization and Cleanup

The 'agt_cb_command_complete_register' function is used to declare the Command Complete callback. The registration can be done during the Initialization Phase 2, before or after the running configuration has been loaded from the startup file.

status_t agt_cb_command_complete_register(agt_cb_command_complete_t cbfn)

Register a Command Complete callback.

Max Callbacks: No limit (except available heap memory)

Parameters

cbfn -- address of callback function to use

Returns

the status of the operation.

Example Initialization function for the Command Complete callback:

 static status_t my_init (void)
 {
     status_t res = NO_ERR;

     // … load module, etc.

     res = agt_cb_command_complete_register(example_command_complete_cbfn);

     // ...
 }

The 'agt_cb_command_complete_unregister' function can be used to remove this callback. This is optional. The server will cleanup automatically on shutdown.

void agt_cb_command_complete_unregister(agt_cb_command_complete_t cbfn)

Unregister a Command Complete callback.

This function unregisters a Command Complete callback.

Parameters

cbfn -- address of callback function to use

Example Cleanup function for the Command Complete callback:

 static void my_cleanup (void)
 {
     // ...

     agt_cb_command_complete_unregister(example_command_complete_cbfn);

     // ...
 }

Command Complete Callback Function Example

The following example code shows a Command Complete callback:

/*******************************************************************
* FUNCTION  example_command_complete_cbfn
*
* The Command Complete callback is the user/system callback
* that is invoked after each client command is executed
* for the NETCONF or RESTCONF protocols.
*
* The Command Complete is typically used for retrieval
* operations (get, get-config, get-bulk) to release resources
* used during GET2 callbacks invoked during the operation.
*
* Max Callbacks: No limit (except available heap memory)
*
* INPUTS:
*   scb == session control block making the request
*   msg == incoming rpc_msg_t in progress
*   command_modname == YANG module name of command that is completed
*   command_name == YANG RPC object name of command that is completed
*
* OUTPUTS:
*    none
*
* RETURNS:
*    none
*******************************************************************/
static void
    example_command_complete_cbfn (ses_cb_t *scb,
                                   rpc_msg_t *msg,
                                   const xmlChar *command_modname,
                                   const xmlChar *command_name)
{
    (void)command_modname;

    if (!xml_strcmp(command_name, (const xmlChar *)"get") ||
        !xml_strcmp(command_name, (const xmlChar *)"get-config") ||
        !xml_strcmp(command_name, (const xmlChar *)"get-bulk")) {

        /* cleanup our internal get cache data
         * example_clean_get2_cache(scb, msg);
         */
    }

}

NACM External Groups Callback

The NACM External Groups callback function is a user callback that is invoked when the server creates a new client session for NETCONF or RESTCONF sessions. It is used to retrieve a list of group names that should be used for the specified username, just for that session. These group names are added to any NACM configured groups in the ietf-netconf-acm YANG module.

  • Max Callbacks: 1

  • File: agt/agt_acm_ietf.h

The following function template definition is used for NACM External Groups callback functions:

typedef status_t (*agt_acm_group_cbfn_t)(const xmlChar *username, xmlChar **retgroups)

typedef for NACM External Groups callback function

Get the list of group names for this username These groups are added to the usergroup cache for the session

Param username

return the list of group names that this username is a member

Param retgroups

address of return malloced string

Retval *retgroups

is set to a malloced string that will be parsed.

It contains a whitespace delimited list of group named ' group1 group2 group3'

The caller will free this string with m__free

Return

status: if an error occurs the session will only use NACM groups

NACM External Groups Callback Initialization and Cleanup

The 'agt_acm_ietf_register_group_cbfn' function is used to declare the NACM External Group callback. The registration can be done during the Initialization Phase 2, before or after the running configuration has been loaded from the startup file.

void agt_acm_ietf_register_group_cbfn(agt_acm_group_cbfn_t cbfn)

Register a get-external-groups callback function.

This will be invoked at the start of each session as the acm_cache is created for a session

Parameters

cbfn -- callnack function to register

Example Initialization function for the NACM External Group callback:

 static status_t my_init (void)
 {
     status_t res = NO_ERR;

     // … load module, etc.

     /* example -- Register a NACM External Groups Callback */
     agt_acm_ietf_register_group_cbfn(nacm_external_group_cbfn);

     return res;
 }

There is no unregister function for this callback. The server will cleanup automatically on shutdown.

NACM External Groups Callback Function Example

The following example code shows a NACM External Group callback:

/********************************************************************
 * FUNCTION nacm_external_group_cbfn
 *
 * Get the list of group names for this username
 * These groups are added to the usergroup cache for the session
 * INPUTS:
 *   username: return the list of group names that this username
 *             is a member
 *   retgroups == address of return malloced string
 * OUTPUTS:
 *   *retgroups is set to a malloced string that will be parsed.
 *         It contains a whitespace delimited list of group named
 *            ' group1 group2 group3'
 *         The caller will free this string with m__free
 * RETURNS:
 *   status: if an error occurs the session will only use NACM groups
*********************************************************************/
static status_t
    nacm_external_group_cbfn (const xmlChar *username,
                              xmlChar **retgroups)
{
    if (retgroups == NULL) {
        return ERR_NCX_INVALID_VALUE;
    }

    (void)username;

    /* MUST use a function that allocates memory with m__getMem
     * Will be freed by the caller with m__free macro
     */
    *retgroups = xml_strdup("group1 group2 group5");
    if (*retgroups == NULL) {
        return ERR_INTERNAL_MEM;
    }

    return NO_ERR;
}

User Defined Data Types Callbacks

User defined data types are supported with callback functions that apply to all YANG leaf or leaf-list nodes using a specific data type.

There are 3 types of callbacks supported to allow customization of YANG data types:

  • validate: custom validation function, used when a data node is set

  • canonical: convert a value to canonical format, used when a data node is set

  • compare: compare 2 values of the same data type, used when a data node is set

There are several standard YANG data types that are supported with built-in canonical functions, which can be found in ncx/ipaddr_typ.c (if sources are available). The server will automatically check for validate and canonical callbacks when a data node is parsed from XML or JSON input. The automatic canonical check can be disabled using the --with-canonical CLI parameter.

User Defined Data Types Callback Initialization and Cleanup

A user defined type is registered with the 'typ_userdef_register' function.

  • This must be done after the YANG module containing the typedef statement has been loaded.

  • Only top-level typedefs are supported.

  • Typedefs within groupings or nested within containers or lists are not supported.

status_t typ_userdef_register(const xmlChar *modname, const xmlChar *typname, typ_validate_fn_t validate_fn, typ_canonical_fn_t canonical_fn, typ_compare_fn_t compare_fn, void *cookie)

Register the callback functions for a user defined type.

At least one callback needs to be set in order for this userdef type to have any affect

Parameters
  • modname -- module name defining the type

  • typname -- name of the type

  • validate_fn -- validate callback (may be NULL)

  • canonical_fn -- canonical callback (may be NULL)

  • compare_fn -- compare callback (may be NULL)

  • cookie -- cookie to pass to callback function

Returns

status

Example Registration function:

/* ietf-inet-types:ipv6-address */
status_t res =
    typ_userdef_register(NCXMOD_IETF_INET_TYPES,
                         (const xmlChar *)"ipv6-address",
                         NULL,   // validate_fn
                         ipv6_address_canonical_fn,
                         NULL,   // compare_fn
                         NULL);  // cookie
if (res != NO_ERR) {
    return res;
}

typ_validate_fn_t

The validate callback is used for special custom validation of values conforming to a YANG data type.

typedef status_t (*typ_validate_fn_t)(typ_def_t *typdef, val_value_t *val, void *cookie)

userdef validate callback function user validation callback for a userdef type

Param typdef

type definition for the user defined type

Param inval

input value to convert

Param cookie

cookie value passed to register function

Return

the validation status

This function returns NO_ERR even if it does not do any validation.

It returns an error code if validation fails.

Example Validate Callback function:

/********************************************************************
* FUNCTION admin_validate_fn
*
* validate callback for an administrative string
*
* INPUTS:
*   typdef == type definition for the user defined type
*   val == input value to validate
*   cookie == cookie value passed to register function
* RETURNS:
*   status
*/
static status_t
    admin_validate_fn (typ_def_t *typdef,
                       val_value_t *val,
                       void *cookie)
{
    (void)typdef;
    (void)cookie;

    if (VAL_TYPE(val) != NCX_BT_STRING) {
        return ERR_NCX_OPERATION_FAILED;
    }

    const xmlChar *valstr = val->v.str;
    if (valstr == NULL) {
        return NO_ERR;
    }

    /* check if the string passes admin format checks */
    if (check_admin_string_ok(valstr)) {
       return NO_ERR;
    }
    return ERR_NCX_INVALID_VALUE;


} /* admin_validate_fn */

typ_canonical_fn_t

The canonical callback is used to convert a data node value to the canonical format for the data type. This is important for list key leafs, or else different representations of the same value (E.g. ipv6-address) will be treated as separate list entries.

This function returns NO_ERR even if it does not do any conversion to canonical format.

It returns an error code if some error occurs and conversion to canonical format fails.

typedef status_t (*typ_canonical_fn_t)(typ_def_t *typdef, val_value_t *val, void *cookie)

userdef canonical callback function convert the inval to the canonical format for the type

Param typdef

type definition for the user defined type

Param val

input value to convert

Param cookie

cookie value passed to register function

Retval val

can be converted to canonical format

Return

status

Example Canonical Callback function:

/********************************************************************
* FUNCTION lowercase_canonical_fn
*
* <generic convert to lowercase>
* canonical callback for a domain name string
* convert the inval to the canonical format for the type
*
* INPUTS:
*   typdef == type definition for the user defined type
*   val == input value to convert
*   cookie == cookie value passed to register function
* OUTPUTS:
*   val == can be changed to canonical format
* RETURNS:
*   status
*/
static status_t
    lowercase_canonical_fn (typ_def_t *typdef,
                            val_value_t *val,
                            void *cookie)
{
    (void)typdef;
    (void)cookie;

    if (VAL_TYPE(val) != NCX_BT_STRING) {
        return ERR_NCX_OPERATION_FAILED;
    }

    xmlChar *valstr = VAL_STRING(val);
    if (valstr == NULL) {
        return NO_ERR;
    }

    /* convert the string to lowercase in place */
    xmlChar *p = valstr;
    while (*p) {
        *p = (xmlChar)tolower((int)*p);
    }

    return NO_ERR;

} /* lowercase_canonical_fn */

typ_compare_fn_t

The compare callback is used to compare two values of the same data type. It is used in the val_compare functions. The standard compare function will skipped if this callback is present for the data type. This function is only needed if the standard compare function for the data type is not correct for some reason (E.g: a vendor-specific structured string).

This function returns NO_ERR if the comparison is done and no need to do the standard compare.

This function returns ERR_NCX_SKIPPED if the comparison is not done so the standard compare function needs to be invoked.

This function returns some error code if an error occurs while attempting the comparison (E.g. ERR_INTERNAL_MEM if a malloc fails)

typedef status_t (*typ_compare_fn_t)(const typ_def_t *typdef, const val_value_t *val1, const val_value_t *val2, void *cookie, int *retval)

userdef compare callback function compare 2 val_value_t nodes of the same user defined type

Param typdef

type definition for the user defined type

Param val1

input value 1 to comapre

Param val2

input value 2 to comapre

Param cookie

cookie value passed to register function

Param retval

address of return compare value

Retval *retval

return compare value

Return

status (ERR_NCX_SKIPPED if no compare done)

Example Compare Callback function:

/********************************************************************
 * compare 2 val_value_t nodes of the same user defined type
 * INPUTS:
 *   typdef == type definition for the user defined type
 *   val1 == input value 1 to compare
 *   val2 == input value 2 to compare
 *   cookie == cookie value passed to register function
 *   retval == address of return compare value
 * OUTPUTS:
 *   *retval == return compare value
 * RETURNS:
 *   status (ERR_NCX_SKIPPED if no compare done)
 */
static status_t
    admin_compare_fn (typ_def_t *typdef,
                      const val_value_t *val1,
                      const val_value_t *val2,
                      void *cookie,
                      int *retval)
{
    (void)typdef;
    (void)cookie;

    if (VAL_TYPE(val1) != NCX_BT_STRING) {
        return ERR_NCX_OPERATION_FAILED;
    }

    if (VAL_TYPE(val12) != NCX_BT_STRING) {
        return ERR_NCX_OPERATION_FAILED;
    }

    const xmlChar *val1str = VAL_STRING(val1);
    if (val1str == NULL) {
        return ERR_NCX_SKIPPED;
    }

    const xmlChar *val2str = VAL_STRING(val2);
    if (val2str == NULL) {
        return ERR_NCX_SKIPPED;
    }

    /* compare as case-insensitive strings */
    int ret = strcasecmp(const char *)val1str, (const char *)val2str);
    *retval = ret;
    return NO_ERR;

} /* admin_compare_fn */