Combo-app is a sample netconfd-pro server SIL-SA + DB-API subsystem application that provides functionality to send edits and get requests to the server with help of DB-API and at the same time server SIL-SA libraries. It demonstrates how the SIL-SA, DB-API, and YControl libraries can be used together within an application.
If the --getconfig and --filespec parameters are present then the <get-config> operation will be sent about once per second, which the sil-sa-app is also active and processing SIL-SA requests. Use control-C to exit the program.
Refer to How do I use SIL-SA and sil-sa-app? and How do I use DB-API and db-api-app? on how to use and what API are supported for both sil-sa-app and db-api-app. The combo application uses the same set of CLI parameters.
The following code illustrates a sample code that can be used to run combo-app.
/* * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved. * Copyright (c) 2012 - 2021, YumaWorks, Inc., All Rights Reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* FILE sil_sa_app {main}.c Distributed SIL Sub-Agent API Sample Main Program * ********************************************************************* * * * I N C L U D E F I L E S * * * *********************************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #ifdef MEMORY_DEBUG #ifndef MACOSX #include <mcheck.h> #endif #endif #include "procdefs.h" #include "agt_sil_lib.h" #include "db_api.h" #include "log.h" #include "sil_sa.h" #include "status_enum.h" #include "ycontrol.h" /******************************************************************** * * * C O N S T A N T S * * * *********************************************************************/ #ifdef DEBUG // #define COMBO_APP_DEBUG 1 // #define COMBO_APP_NOTIF_TEST 1 // #define COMBO_APP_STATLIB_TEST 1 #endif // DEBUG /* uncomment to force a usleep in the main loop */ // #define DO_SLEEP 1 /******************************************************************** * * * T Y P E S * * * *********************************************************************/ /******************************************************************** * * * V A R I A B L E S * * * *********************************************************************/ /******************************************************************** * FUNCTION send_test_edit * * This is an example send edit function. * The module test.yang needs to be loaded for this to work *********************************************************************/ static void send_test_edit (void) { /* EXAMPLE EDIT: * TBD: replace with parameters from command line */ /* set 3 top-level leafs at once with test module */ const xmlChar *path_str = (const xmlChar *)"/"; const xmlChar *operation_str = (const xmlChar *)"merge"; const xmlChar *value_str = (const xmlChar *) "<config>" "<int8.1 xmlns='http://netconfcentral.org/ns/test'>22</int8.1>" "<int16.1 xmlns='http://netconfcentral.org/ns/test'>11</int16.1>" "<int32.1 xmlns='http://netconfcentral.org/ns/test'>1000</int32.1>" "</config>"; const xmlChar *patch_id_str = NULL; boolean system_edit = FALSE; const xmlChar *insert_point = NULL; const xmlChar *insert_where = NULL; status_t res = db_api_send_edit_full(path_str, operation_str, value_str, patch_id_str, system_edit, insert_point, insert_where); if (res != NO_ERR) { log_error("\nSend test edit failed %s %s = %s (%s)\n", operation_str, path_str, value_str, get_error_string(res)); } else if (LOGDEBUG) { log_debug("\nSend test edit OK %s %s = %s\n", operation_str, path_str, value_str); } } /* send_test_edit */ /******************************************************************** * FUNCTION send_test_getconfig * * Send a <getconfig> request *********************************************************************/ static void send_test_getconfig (const char *filespec, boolean withdef, boolean with_state, const char *xpath_filter) { status_t res; if (xpath_filter || with_state) { res = db_api_send_getfilter((const xmlChar *)filespec, withdef, !with_state, (const xmlChar *)xpath_filter); } else { res = db_api_send_getconfig((const xmlChar *)filespec, withdef); } if (res != NO_ERR) { log_error("\nSend test getconfig failed (%s)\n", get_error_string(res)); } else if (LOGDEBUG) { log_debug("\nSend test getconfig OK\n"); } } /* send_test_getconfig */ /******************************************************************** * FUNCTION print_usage * * Print the program usage text *********************************************************************/ static void print_usage (void) { log_info("\nUsage:"); log_info_append("\n combo-app [--address=string] [--port=num] " "[--subsys-id=string] [--library=name] [--retry-limit=num]"); log_info_append("\n [--getconfig [--withdef] [--with-state] " "[--xpath-filter=str] --filespec=/path/to/output.xml]"); log_info_append("\nUse the --library parameter to select each library " "instead of all SIL-SA libraries"); log_info_append("\nExample:"); log_info_append("\n combo-app --address=192.168.0.10 " "--port=2088"); log_info_append("\n Connect to the main server\n\n"); } /* print_usage */ /******************************************************************** * FUNCTION get_subsys_parm * * Check the CLI parameters for the subsys-id parameter needed first * * RETURNS: * status code *********************************************************************/ static status_t get_subsys_parm (char **argv, char **subsys) { *subsys = NULL; if (argv[0] == NULL) { return NO_ERR; // should not happen } const char *sub = "--subsys="; int sub_len = strlen(sub); int i = 1; for (; argv[i]; i++) { char *str = argv[i]; if (str == NULL) { return NO_ERR; } int reallen = strlen(str); if (!strncmp(str, sub, sub_len)) { if (reallen > sub_len) { *subsys = &str[sub_len]; } else { return ERR_NCX_INVALID_VALUE; } } } return NO_ERR; } /* get_subsys_parm */ /******************************************************************** * FUNCTION get_silsa_cli_parms * * Check the CLI parameters for the getconfig commands * * RETURNS: * status code *********************************************************************/ static status_t get_silsa_cli_parms (char **argv, char **address, uint16 *port, uint16 *retry_limit) { *address = NULL; *port = 0; *retry_limit = 0; if (argv[0] == NULL) { return NO_ERR; // should not happen } const char *ad = "--address="; const char *po = "--port="; const char *li = "--library="; const char *rl = "--retry-limit="; int ad_len = strlen(ad); int po_len = strlen(po); int li_len = strlen(li); int rl_len = strlen(rl); int i = 1; for (; argv[i]; i++) { char *str = argv[i]; if (str == NULL) { return NO_ERR; } int reallen = strlen(str); if (!strncmp(str, ad, ad_len)) { if (reallen > ad_len) { *address = &str[ad_len]; } else { return ERR_NCX_INVALID_VALUE; } } else if (!strncmp(str, po, po_len)) { if (reallen > po_len) { char *num = &str[po_len]; int c = atoi(num); if (c > 0 && c <= 65535) { *port = (uint16)c; } else { return ERR_NCX_INVALID_VALUE; } } else { return ERR_NCX_INVALID_VALUE; } } else if (!strncmp(str, li, li_len)) { if (reallen > li_len) { status_t res = sil_sa_add_library_parm((const xmlChar *)&str[li_len]); if (res != NO_ERR) { return res; } } else { return ERR_NCX_INVALID_VALUE; } } else if (!strncmp(str, rl, rl_len)) { if (reallen > rl_len) { char *num = &str[rl_len]; int c = atoi(num); if (c > 0 && c <= 65535) { *retry_limit = (uint16)c; } else { return ERR_NCX_INVALID_VALUE; } } else { return ERR_NCX_INVALID_VALUE; } } /* else ignore because probably an NCX CLI parm */ } return NO_ERR; } /* get_silsa_cli_parms */ /******************************************************************** * FUNCTION get_dbapi_cli_parms * * Check the CLI parameters for the getconfig commands * * RETURNS: * status code if error connecting or logging into ncxserver *********************************************************************/ static status_t get_dbapi_cli_parms (char **argv, boolean *getconfig, boolean *withdef, boolean *with_state, const char **filespec, const char **xpath_filter, uint32 *count) { *getconfig = false; *withdef = false; *with_state = false; *filespec = NULL; *xpath_filter = NULL; *count = 0; if (argv[0] == NULL) { return NO_ERR; // should not happen } const char *gc = "--getconfig"; const char *wd = "--withdef"; const char *ws = "--with-state"; const char *fp = "--filespec="; const char *xp = "--xpath-filter="; const char *cnt = "--count="; int fp_len = strlen(fp); int xp_len = strlen(xp); int cnt_len = strlen(cnt); int i = 1; for (; argv[i]; i++) { char *str = argv[i]; if (str == NULL) { return NO_ERR; } if (!strcmp(str, gc)) { *getconfig = true; } else if (!strcmp(str, wd)) { *withdef = true; } else if (!strcmp(str, ws)) { *with_state = true; } else if (!strncmp(str, fp, fp_len)) { int reallen = strlen(str); if (reallen > fp_len) { *filespec = &str[fp_len]; } else { return ERR_NCX_INVALID_VALUE; } } else if (!strncmp(str, xp, xp_len)) { int reallen = strlen(str); if (reallen > xp_len) { /* check if quotes need to be removed */ char *p = &str[xp_len]; if ((*p == '"') || (*p == '\'')) { if (str[reallen - 1] == *p) { p++; str[reallen - 1] = 0; } else { return ERR_NCX_INVALID_VALUE; } } *xpath_filter = p; } else { return ERR_NCX_INVALID_VALUE; } } else if (!strncmp(str, cnt, cnt_len)) { int reallen = strlen(str); if (reallen > cnt_len) { char *num = &str[cnt_len]; int c = atoi(num); if (c > 0 && c <= 10000) { *count = (uint32)c; } else { return ERR_NCX_INVALID_VALUE; } } else { return ERR_NCX_INVALID_VALUE; } } } return NO_ERR; } /* get_dbapi_cli_parms */ #ifdef COMBO_APP_STATLIB_TEST /* extern definitions for the 3 expected callbacks */ AGT_SIL_LIB_EXTERN(test2) static status_t static_silsa_init (void) { /* example: module=test2; * need to use in Makefile (example) * STATIC_SILSA=-L /home/andy/silsa -ltest2_sa * The actual library names are not needed in this code */ status_t res = agt_sil_lib_register_statlib((const xmlChar *)"test2", y_test2_init, y_test2_init2, y_test2_cleanup); return res; } #endif // COMBO_APP_STATLIB_TEST /******************************************************************** * FUNCTION main * * This is an example SIL-SA service main function. * * RETURNS: * 0 if NO_ERR * status code if error connecting or logging into ncxserver *********************************************************************/ int main (int argc, char **argv) { #ifdef MEMORY_DEBUG mtrace(); #endif char *subsys = NULL; boolean ycontrol_done = FALSE; /* need to check for the subsys-id parm before * the system is initialized */ status_t res = get_subsys_parm(argv, &subsys); if (res != NO_ERR) { print_usage(); } /* 1) setup yumapro messaging service profile */ if (res == NO_ERR) { if (subsys == NULL) { res = ycontrol_init(argc, argv, (const xmlChar *)"subsys1"); } else { res = ycontrol_init(argc, argv, (const xmlChar *)subsys); } ycontrol_done = TRUE; } char *address = NULL; uint16 port = 0; uint16 retry_limit = 0; if (res == NO_ERR) { res = get_silsa_cli_parms(argv, &address, &port, &retry_limit); if (res != NO_ERR) { print_usage(); } } boolean getconfig = false; boolean withdef = false; boolean with_state = false; const char *filespec = NULL; const char *xpath_filter = NULL; uint32 max_count = 0; uint32 wait_count = 0; uint32 cnt = 0; if (res == NO_ERR) { res = get_dbapi_cli_parms(argv, &getconfig, &withdef, &with_state, &filespec, &xpath_filter, &max_count); if (res != NO_ERR) { print_usage(); } } if ((res == NO_ERR) && getconfig && (filespec==NULL)) { res = ERR_NCX_MISSING_PARM; print_usage(); } /* 2) register services with the control layer */ if (res == NO_ERR) { res = sil_sa_register_service(); } if (res == NO_ERR) { res = db_api_register_service(); } #ifdef COMBO_STATLIB_TEST /* 2B) setup any static SIL-SA libraries */ if (res == NO_ERR) { res = static_silsa_init(); } #endif // COMBO_APP_STATLIB_TEST /* set the retry limit if provided */ if ((res == NO_ERR) && (retry_limit > 0)) { ycontrol_set_retry_limit(retry_limit); } /* It is also possible to set the retry_interval but there is * no CLI parameter provided for this purposes * if (res == NO_ERR) { * ycontrol_set_retry_interval(retry_int_milliseconds); * } */ /* 3) do 2nd stage init of the control manager (connect to server) */ if (res == NO_ERR) { if (address) { if (port == 0) { port = 2023; } res = ycontrol_init2_ha("server1", address, port); } else { res = ycontrol_init2(); } } boolean done = FALSE; boolean test_done = FALSE; const xmlChar *error_msg = NULL; /* 4) call ycontrol_check_io periodically from the main program * control loop */ #ifdef COMBO_APP_DEBUG int id = 0; #endif // COMBO_APP_DEBUG #ifdef COMBO_APP_NOTIF_TEST uint32 loop_cnt = 0; #endif // COMBO_APP_NOTIF_TEST while (!done && (res == NO_ERR)) { #ifdef COMBO_APP_DEBUG if (LOGDEBUG3) { log_debug3("\ncombo-app: checking ycontrol IO %d", id++); } #endif // COMBO_APP_DEBUG res = ycontrol_check_io(); if (ycontrol_shutdown_now()) { // YControl has received a <shutdown-event> // from the server subsystem is no longer active // could ignore or shut down YControl IO loop done = TRUE; continue; } // Using sleep to represent other program work; remove for real #ifdef DO_SLEEP if (!done && (res == NO_ERR)) { useconds_t usleep_val = 100000; // 100K micro-sec == 1/10 sec (void)usleep(usleep_val); } #endif // DO_SLEEP if (!done && (res == NO_ERR) && db_api_service_ready()) { if (test_done) { /* check the test edit */ res = db_api_check_edit_ex(&error_msg); if (res == NO_ERR) { log_info("\nTest %u succeeded\n", cnt+1); if (cnt < max_count) { cnt++; log_info("\nStart send edit %u", cnt); send_test_edit(); } else { done = TRUE; } } else { if (res == ERR_NCX_SKIPPED) { res = ERR_NCX_OPERATION_FAILED; } log_info("\nTest failed with error: %d '%s'\n\n", res, error_msg ? error_msg : (const xmlChar *)get_error_string(res)); done = TRUE; } } else if (++wait_count == 100) { wait_count = 0; if (getconfig) { send_test_getconfig(filespec, withdef, with_state, xpath_filter); } else { /* send the simple test edit */ send_test_edit(); } test_done = TRUE; } } #ifdef COMBO_APP_NOTIF_TEST loop_cnt++; if (loop_cnt == 500) { sil_sa_notif_test(10, 20, (const xmlChar *)"this is a test"); loop_cnt = 0; } #endif // COMBO_APP_NOTIF_TEST } /* 5) cleanup the control layer before exit */ if (ycontrol_done) { ycontrol_cleanup(); } #ifdef MEMORY_DEBUG muntrace(); #endif return (int)res; } /* main */ /* END combo_app {main}.c */