diff --git a/CHANGELOG.md b/CHANGELOG.md index bfaa7d48..8351b410 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ Expected: February 2024 ### Minor features +* Feature: [Add support for -V option to give version](https://github.com/clicon/clixon/issues/472) + * All clixon applications added command-line option `-V` for printing version + * New ca_version callback for customized version output * Optimization: * Added mountpoint cache as yang flag `YANG_FLAG_MTPOINT_POTENTIAL` * Optimized `yang_find`, especially namespace lookup diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index fab131ca..2a017654 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -79,7 +79,7 @@ #include "backend_plugin_restconf.h" /* Command line options to be passed to getopt(3) */ -#define BACKEND_OPTS "hD:f:E:l:C:d:p:b:Fza:u:P:1qs:c:U:g:y:o:" +#define BACKEND_OPTS "hVD:f:E:l:C:d:p:b:Fza:u:P:1qs:c:U:g:y:o:" #define BACKEND_LOGFILE "/usr/local/var/clixon_backend.log" @@ -462,6 +462,7 @@ usage(clicon_handle h, fprintf(stderr, "usage:%s *\n" "where options are\n" "\t-h\t\tHelp\n" + "\t-V \t\tPrint version and exit\n" "\t-D \tDebug level\n" "\t-f \tClixon config file\n" "\t-E \tExtra configuration file directory\n" @@ -532,6 +533,7 @@ main(int argc, size_t sz; int config_dump; enum format_enum config_dump_format = FORMAT_XML; + int print_version = 0; /* In the startup, logs to stderr & syslog and debug flag set later */ clicon_log_init(__PROGRAM__, LOG_INFO, logdst); @@ -560,6 +562,10 @@ main(int argc, */ help = 1; break; + case 'V': + cligen_output(stdout, "Clixon version %s\n", CLIXON_VERSION_STRING); + print_version++; /* plugins may also print versions w ca-version callback */ + break; case 'D' : /* debug */ if (sscanf(optarg, "%d", &dbg) != 1) usage(h, argv[0]); @@ -611,12 +617,13 @@ main(int argc, while ((c = getopt(argc, argv, BACKEND_OPTS)) != -1) switch (c) { case 'h' : /* help */ + case 'V' : /* version */ case 'D' : /* debug */ - case 'f': /* config file */ - case 'E': /* extra config dir */ + case 'f' : /* config file */ + case 'E' : /* extra config dir */ case 'l' : break; /* see above */ - case 'C': /* Explicitly dump configuration */ + case 'C' : /* Explicitly dump configuration */ if ((config_dump_format = format_str2int(optarg)) == (enum format_enum)-1){ fprintf(stderr, "Unrecognized dump format: %s(expected: xml|json|text)\n", argv[0]); usage(h, argv[0]); @@ -823,7 +830,12 @@ main(int argc, clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, clicon_option_str(h, "CLICON_BACKEND_REGEXP")) < 0) goto done; - + /* Print version, customized variant must wait for plugins to load */ + if (print_version){ + if (clixon_plugin_version_all(h, stdout) < 0) + goto done; + exit(0); + } /* Load Yang modules * 1. Load a yang module as a specific absolute filename */ if ((str = clicon_yang_main_file(h)) != NULL) diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 1dadc023..1eb53060 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -73,8 +73,7 @@ #include "cli_handle.h" /* Command line options to be passed to getopt(3) */ -#define CLI_OPTS "+hD:f:E:l:C:F:1a:u:d:m:qp:GLy:c:U:o:" - +#define CLI_OPTS "+hVD:f:E:l:C:F:1a:u:d:m:qp:GLy:c:U:o:" /*! Check if there is a CLI history file and if so dump the CLI histiry to it * * Just log if file does not exist or is not readable @@ -481,6 +480,7 @@ usage(clicon_handle h, "and extra-options are app-dependent and passed to the plugin init function\n" "where options are\n" "\t-h \t\tHelp\n" + "\t-V \t\tPrint version and exit\n" "\t-D \tDebug level\n" "\t-f \tConfig-file (mandatory)\n" "\t-E \tExtra configuration file directory\n" @@ -532,8 +532,9 @@ main(int argc, size_t cligen_bufthreshold; int dbg=0; int nr; - int config_dump; + int config_dump; enum format_enum config_dump_format = FORMAT_XML; + int print_version = 0; /* Defaults */ once = 0; @@ -574,6 +575,10 @@ main(int argc, */ help = 1; break; + case 'V': /* version */ + cligen_output(stdout, "Clixon version %s\n", CLIXON_VERSION_STRING); + print_version++; /* plugins may also print versions w ca-version callback */ + break; case 'D' : /* debug */ if (sscanf(optarg, "%d", &dbg) != 1) usage(h, argv[0]); @@ -616,11 +621,12 @@ main(int argc, while ((c = getopt(argc, argv, CLI_OPTS)) != -1){ switch (c) { case 'D' : /* debug */ - case 'f': /* config file */ - case 'E': /* extra config dir */ - case 'l': /* Log destination */ + case 'V' : /* version */ + case 'f' : /* config file */ + case 'E' : /* extra config dir */ + case 'l' : /* Log destination */ break; /* see above */ - case 'C': /* Explicitly dump configuration */ + case 'C' : /* Explicitly dump configuration */ if ((config_dump_format = format_str2int(optarg)) == (enum format_enum)-1){ fprintf(stderr, "Unrecognized dump format: %s(expected: xml|json|text)\n", argv[0]); usage(h, argv[0]); @@ -766,7 +772,12 @@ main(int argc, goto done; } #endif - + /* Print version, customized variant must wait for plugins to load */ + if (print_version){ + if (clixon_plugin_version_all(h, stdout) < 0) + goto done; + exit(0); + } /* Add (hardcoded) netconf features in case ietf-netconf loaded here * Otherwise it is loaded in netconf_module_load below */ diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 762e9b85..0666f860 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -70,7 +70,7 @@ #include "netconf_rpc.h" /* Command line options to be passed to getopt(3) */ -#define NETCONF_OPTS "hD:f:E:l:C:q01ca:u:d:p:y:U:t:eo:" +#define NETCONF_OPTS "hVD:f:E:l:C:q01ca:u:d:p:y:U:t:eo:" #define NETCONF_LOGFILE "/tmp/clixon_netconf.log" @@ -637,6 +637,7 @@ usage(clicon_handle h, fprintf(stderr, "usage:%s\n" "where options are\n" "\t-h\t\tHelp\n" + "\t-V \t\tPrint version and exit\n" "\t-D \tDebug level\n" "\t-f \tConfiguration file (mandatory)\n" "\t-E \tExtra configuration file directory\n" @@ -683,6 +684,7 @@ main(int argc, size_t sz; int config_dump = 0; enum format_enum config_dump_format = FORMAT_XML; + int print_version = 0; /* Create handle */ if ((h = clicon_handle_init()) == NULL) @@ -702,6 +704,10 @@ main(int argc, case 'h' : /* help */ usage(h, argv[0]); break; + case 'V': /* version */ + cligen_output(stdout, "Clixon version %s\n", CLIXON_VERSION_STRING); + print_version++; /* plugins may also print versions w ca-version callback */ + break; case 'D' : /* debug */ if (sscanf(optarg, "%d", &dbg) != 1) usage(h, argv[0]); @@ -743,12 +749,13 @@ main(int argc, while ((c = getopt(argc, argv, NETCONF_OPTS)) != -1) switch (c) { case 'h' : /* help */ + case 'V' : /* version */ case 'D' : /* debug */ - case 'f': /* config file */ - case 'E': /* extra config dir */ - case 'l': /* log */ + case 'f' : /* config file */ + case 'E' : /* extra config dir */ + case 'l' : /* log */ break; /* see above */ - case 'C': /* Explicitly dump configuration */ + case 'C' : /* Explicitly dump configuration */ if ((config_dump_format = format_str2int(optarg)) == (enum format_enum)-1){ fprintf(stderr, "Unrecognized dump format: %s(expected: xml|json|text)\n", argv[0]); usage(h, argv[0]); @@ -855,7 +862,12 @@ main(int argc, if ((dir = clicon_netconf_dir(h)) != NULL && clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0) goto done; - + /* Print version, customized variant must wait for plugins to load */ + if (print_version){ + if (clixon_plugin_version_all(h, stdout) < 0) + goto done; + exit(0); + } /* Load Yang modules * 1. Load a yang module as a specific absolute filename */ if ((str = clicon_yang_main_file(h)) != NULL){ diff --git a/apps/restconf/restconf_main_fcgi.c b/apps/restconf/restconf_main_fcgi.c index 2012f872..b983de10 100644 --- a/apps/restconf/restconf_main_fcgi.c +++ b/apps/restconf/restconf_main_fcgi.c @@ -88,7 +88,7 @@ #include "restconf_stream.h" /* Command line options to be passed to getopt(3) */ -#define RESTCONF_OPTS "hD:f:E:l:C:p:d:y:a:u:rW:R:o:" +#define RESTCONF_OPTS "hVD:f:E:l:C:p:d:y:a:u:rW:R:o:" /*! Convert FCGI parameters to clixon runtime data * @@ -270,6 +270,7 @@ usage(clicon_handle h, fprintf(stderr, "usage:%s [options]\n" "where options are\n" "\t-h \t\t Help\n" + "\t-V \t\tPrint version and exit\n" "\t-D \t Debug level\n" "\t-f \t Configuration file (mandatory)\n" "\t-E \t Extra configuration file directory\n" @@ -321,6 +322,7 @@ main(int argc, size_t sz; int config_dump = 0; enum format_enum config_dump_format = FORMAT_XML; + int print_version = 0; /* In the startup, logs to stderr & debug flag set later */ clicon_log_init(__PROGRAM__, LOG_INFO, logdst); @@ -336,6 +338,10 @@ main(int argc, case 'h': usage(h, argv[0]); break; + case 'V': + cligen_output(stdout, "Clixon version %s\n", CLIXON_VERSION_STRING); + print_version++; /* plugins may also print versions w ca-version callback */ + break; case 'D' : /* debug */ if (sscanf(optarg, "%d", &dbg) != 1) usage(h, argv[0]); @@ -391,6 +397,7 @@ main(int argc, while ((c = getopt(argc, argv, RESTCONF_OPTS)) != -1) switch (c) { case 'h' : /* help */ + case 'V' : /* version */ case 'D' : /* debug */ case 'f': /* config file */ case 'E': /* extra config dir */ @@ -480,6 +487,12 @@ main(int argc, if ((dir = clicon_restconf_dir(h)) != NULL) if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0) return -1; + /* Print version, customized variant must wait for plugins to load */ + if (print_version){ + if (clixon_plugin_version_all(h, stdout) < 0) + goto done; + exit(0); + } /* Create a pseudo-plugin to create extension callback to set the ietf-routing * yang-data extension for api-root top-level restconf function. */ diff --git a/apps/restconf/restconf_main_native.c b/apps/restconf/restconf_main_native.c index a1b07557..ad1d3850 100644 --- a/apps/restconf/restconf_main_native.c +++ b/apps/restconf/restconf_main_native.c @@ -159,7 +159,7 @@ #endif /* Command line options to be passed to getopt(3) */ -#define RESTCONF_OPTS "hD:f:E:l:C:p:y:a:u:rW:R:o:" +#define RESTCONF_OPTS "hVD:f:E:l:C:p:y:a:u:rW:R:o:" /* If set, open outwards socket non-blocking, as opposed to blocking * Should work both ways, but in the ninblocking case, @@ -903,6 +903,7 @@ restconf_openssl_init(clicon_handle h, * That is, EITHER local config OR read config from backend once * @param[in] h Clixon handle * @param[in] inline_config If set, restconf conf is given by -R command-line + * @param[in] print_version If set, print version and exit * @param[out] xrestconf XML restconf config, malloced (if retval = 1) * @retval 1 OK (and xrestconf set) * @retval 0 Fail - no config @@ -911,6 +912,7 @@ restconf_openssl_init(clicon_handle h, int restconf_clixon_init(clicon_handle h, char *inline_config, + int print_version, cxobj **xrestconfp) { int retval = -1; @@ -954,6 +956,12 @@ restconf_clixon_init(clicon_handle h, if ((dir = clicon_restconf_dir(h)) != NULL) if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0) return -1; + /* Print version, customized variant must wait for plugins to load */ + if (print_version){ + if (clixon_plugin_version_all(h, stdout) < 0) + goto done; + exit(0); + } /* Create a pseudo-plugin to create extension callback to set the ietf-routing * yang-data extension for api-root top-level restconf function. */ @@ -1101,6 +1109,7 @@ usage(clicon_handle h, fprintf(stderr, "usage:%s [options]\n" "where options are\n" "\t-h \t\t Help\n" + "\t-V \t\tPrint version and exit\n" "\t-D \t Debug level, overrides any config debug setting\n" "\t-f \t Configuration file (mandatory)\n" "\t-E \t Extra configuration file directory\n" @@ -1136,8 +1145,9 @@ main(int argc, int ret; cxobj *xrestconf = NULL; char *inline_config = NULL; - int config_dump = 0; + int config_dump = 0; enum format_enum config_dump_format = FORMAT_XML; + int print_version = 0; /* In the startup, logs to stderr & debug flag set later */ clicon_log_init(__PROGRAM__, LOG_INFO, logdst); @@ -1151,6 +1161,10 @@ main(int argc, case 'h': usage(h, argv0); break; + case 'V': /* version */ + cligen_output(stdout, "Clixon version %s\n", CLIXON_VERSION_STRING); + print_version++; /* plugins may also print versions w ca-version callback */ + break; case 'D' : /* debug. Note this overrides any setting in the config */ if (sscanf(optarg, "%d", &dbg) != 1) usage(h, argv0); @@ -1300,7 +1314,7 @@ main(int argc, goto done; /* Clixon inits / configs */ - if ((ret = restconf_clixon_init(h, inline_config, &xrestconf)) < 0) + if ((ret = restconf_clixon_init(h, inline_config, print_version, &xrestconf)) < 0) goto done; if (ret == 0){ /* restconf disabled */ clicon_log(LOG_INFO, "restconf configuration not found or disabled"); diff --git a/apps/snmp/snmp_main.c b/apps/snmp/snmp_main.c index 9893a15b..7f2fbbc3 100644 --- a/apps/snmp/snmp_main.c +++ b/apps/snmp/snmp_main.c @@ -70,7 +70,7 @@ #include "snmp_register.h" /* Command line options to be passed to getopt(3) */ -#define SNMP_OPTS "hD:f:l:C:o:z" +#define SNMP_OPTS "hVD:f:l:C:o:z" /* Forward */ static int clixon_snmp_input_cb(int s, void *arg); @@ -319,6 +319,7 @@ usage(clicon_handle h, fprintf(stderr, "usage:%s\n" "where options are\n" "\t-h\t\tHelp\n" + "\t-V \t\tPrint version and exit\n" "\t-D \tDebug level (>1 for extensive libnetsnmp debug)\n" "\t-f \tConfiguration file (mandatory)\n" "\t-l (e|o|s|f) Log on std(e)rr, std(o)ut, (s)yslog(default), (f)ile\n" @@ -373,23 +374,27 @@ main(int argc, case 'h' : /* help */ usage(h, argv[0]); break; + case 'V': /* version */ + cligen_output(stdout, "Clixon version %s\n", CLIXON_VERSION_STRING); + exit(0); + break; case 'D' : /* debug */ if (sscanf(optarg, "%d", &dbg) != 1) usage(h, argv[0]); break; - case 'f': /* override config file */ + case 'f': /* override config file */ if (!strlen(optarg)) usage(h, argv[0]); clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg); break; - case 'l': /* Log destination: s|e|o */ + case 'l': /* Log destination: s|e|o */ if ((logdst = clicon_log_opt(optarg[0])) < 0) usage(h, argv[0]); if (logdst == CLICON_LOG_FILE && strlen(optarg)>1 && clicon_log_file(optarg+1) < 0) goto done; - break; + break; } /* diff --git a/example/main/example_cli.c b/example/main/example_cli.c index 3b8aee08..705bf0b0 100644 --- a/example/main/example_cli.c +++ b/example/main/example_cli.c @@ -269,14 +269,34 @@ example_cli_errmsg(clicon_handle h, return retval; } +/*! Callback for printing version output and exit + * + * A plugin can customize a version (or banner) output on stdout. + * Several version strings can be printed if there are multiple callbacks. + * If not regstered plugins exist, clixon prints CLIXON_VERSION_STRING + * Typically invoked by command-line option -V + * @param[in] h Clixon handle + * @param[in] f Output file + * @retval 0 OK + * @retval -1 Error + */ +int +example_version(clicon_handle h, + FILE *f) +{ + cligen_output(f, "Clixon main example version 0\n"); + return 0; +} + #ifndef CLIXON_STATIC_PLUGINS static clixon_plugin_api api = { - "example", /* name */ - clixon_plugin_init, /* init */ - NULL, /* start */ - NULL, /* exit */ - .ca_yang_mount=example_cli_yang_mount, /* RFC 8528 schema mount */ - .ca_errmsg=example_cli_errmsg, /* customize errmsg */ + "example", /* name */ + clixon_plugin_init, /* init */ + NULL, /* start */ + NULL, /* exit */ + .ca_yang_mount= example_cli_yang_mount, /* RFC 8528 schema mount */ + .ca_errmsg = example_cli_errmsg, /* customize errmsg */ + .ca_version = example_version /* Customized version string */ }; /*! CLI plugin initialization diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h index d9211a61..e6abaeda 100644 --- a/lib/clixon/clixon_plugin.h +++ b/lib/clixon/clixon_plugin.h @@ -130,7 +130,6 @@ typedef int (plgstart_t)(clicon_handle); /* Plugin start */ */ typedef int (plgdaemon_t)(clicon_handle); /* Plugin pre/post daemonized */ - /* Called just before plugin unloaded. * @param[in] h Clixon handle */ @@ -325,6 +324,19 @@ typedef int (yang_patch_t)(clicon_handle h, yang_stmt *ymod); */ typedef int (netconf_errmsg_t)(clicon_handle, cxobj *xerr, cbuf *cberr); +/*! Callback for printing version output and exit + * + * A plugin can customize a version (or banner) output on stdout. + * Several version strings can be printed if there are multiple callbacks. + * If not regstered plugins exist, clixon prints CLIXON_VERSION_STRING + * Typically invoked by command-line option -V + * @param[in] h Clixon handle + * @param[in] f Output file + * @retval 0 OK + * @retval -1 Error + */ +typedef int (plgversion_t)(clicon_handle, FILE*); + /*! Startup status for use in startup-callback * * Note that for STARTUP_ERR and STARTUP_INVALID, running runs in failsafe mode @@ -343,18 +355,19 @@ enum startup_status{ * Note: Implicit init function */ struct clixon_plugin_api; -typedef struct clixon_plugin_api* (plginit2_t)(clicon_handle); /* Clixon plugin Init */ +typedef struct clixon_plugin_api* (plginit_t)(clicon_handle); /* Clixon plugin Init */ struct clixon_plugin_api{ /*--- Common fields. ---*/ char ca_name[MAXPATHLEN]; /* Name of plugin (given by plugin) */ - plginit2_t *ca_init; /* Clixon plugin Init (implicit) */ + plginit_t *ca_init; /* Clixon plugin Init (implicit) */ plgstart_t *ca_start; /* Plugin start */ plgexit_t *ca_exit; /* Plugin exit */ plgextension_t *ca_extension; /* Yang extension/unknown handler */ yang_mount_t *ca_yang_mount; /* RFC 8528 schema mount */ yang_patch_t *ca_yang_patch; /* Patch yang after parse */ netconf_errmsg_t *ca_errmsg; /* Customize error message callback */ + plgversion_t *ca_version; /* Output a customized version message */ union { struct { /* cli-specific */ cli_prompthook_t *ci_prompt; /* Prompt hook */ @@ -370,7 +383,6 @@ struct clixon_plugin_api{ plgdaemon_t *cb_pre_daemon; /* Plugin just before daemonization (only daemon) */ plgdaemon_t *cb_daemon; /* Plugin daemonized (always called) */ plgreset_t *cb_reset; /* Reset system status */ - plgstatedata_t *cb_statedata; /* Provide state data XML from plugin */ plglockdb_t *cb_lockdb; /* Database lock changed state */ trans_cb_t *cb_trans_begin; /* Transaction start */ @@ -490,6 +502,9 @@ int clixon_plugin_yang_patch_all(clicon_handle h, yang_stmt *ymod); int clixon_plugin_netconf_errmsg_one(clixon_plugin_t *cp, clicon_handle h, cxobj *xerr, cbuf *cberr); int clixon_plugin_netconf_errmsg_all(clicon_handle h, cxobj *xerr, cbuf *cberr); +int clixon_plugin_version_one(clixon_plugin_t *cp, clicon_handle h, FILE *f); +int clixon_plugin_version_all(clicon_handle h, FILE *f); + /* rpc callback API */ int rpc_callback_register(clicon_handle h, clicon_rpc_cb cb, void *arg, const char *ns, const char *name); int rpc_callback_call(clicon_handle h, cxobj *xe, void *arg, int *nrp, cbuf *cbret); diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index 2124822b..34592b01 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -329,7 +329,7 @@ plugin_load_one(clicon_handle h, int retval = -1; char *error; void *handle = NULL; - plginit2_t *initfn; + plginit_t *initfn; clixon_plugin_api *api = NULL; clixon_plugin_t *cp = NULL; char *name; @@ -690,7 +690,7 @@ plugin_context_check(clicon_handle h, */ int clixon_plugin_start_one(clixon_plugin_t *cp, - clicon_handle h) + clicon_handle h) { int retval = -1; plgstart_t *fn; /* Plugin start */ @@ -747,7 +747,7 @@ clixon_plugin_start_all(clicon_handle h) */ static int clixon_plugin_exit_one(clixon_plugin_t *cp, - clicon_handle h) + clicon_handle h) { int retval = -1; char *error; @@ -804,6 +804,7 @@ clixon_plugin_exit_all(clicon_handle h) /*! Run the restconf user-defined credentials callback * + * @param[in] cp Plugin handle * @param[in] h Clixon handle * @param[in] req Per-message request www handle to use with restconf_api.h * @param[in] auth_type Authentication type: none, user-defined, or client-cert @@ -973,7 +974,7 @@ clixon_plugin_extension_all(clicon_handle h, * Upgrade datastore on load before or as an alternative to module-specific upgrading mechanism */ int -clixon_plugin_datastore_upgrade_one(clixon_plugin_t *cp, +clixon_plugin_datastore_upgrade_one(clixon_plugin_t *cp, clicon_handle h, const char *db, cxobj *xt, @@ -1188,7 +1189,7 @@ clixon_plugin_netconf_errmsg_one(clixon_plugin_t *cp, goto done; if (fn(h, xerr, cberr) < 0) { if (clicon_errno < 0) - clicon_log(LOG_WARNING, "%s: Internal error: Yang patch callback in plugin: %s returned -1 but did not make a clicon_err call", + clicon_log(LOG_WARNING, "%s: Internal error: Netconf err callback in plugin: %s returned -1 but did not make a clicon_err call", __FUNCTION__, cp->cp_name); goto done; } @@ -1225,6 +1226,64 @@ clixon_plugin_netconf_errmsg_all(clicon_handle h, return retval; } +/*! Call one plugin to get version output + * + * @param[in] cp Plugin handle + * @param[in] h Clixon handle + * @param[in] f Output file + * @retval 0 OK + * @retval -1 Error + */ +int +clixon_plugin_version_one(clixon_plugin_t *cp, + clicon_handle h, + FILE *f) +{ + int retval = -1; + plgversion_t *fn; + void *wh = NULL; + + if ((fn = cp->cp_api.ca_version) != NULL){ + wh = NULL; + if (plugin_context_check(h, &wh, cp->cp_name, __FUNCTION__) < 0) + goto done; + if (fn(h, f) < 0) { + if (clicon_errno < 0) + clicon_log(LOG_WARNING, "%s: Internal error: version callback in plugin: %s returned -1 but did not make a clicon_err call", + __FUNCTION__, cp->cp_name); + goto done; + } + if (plugin_context_check(h, &wh, cp->cp_name, __FUNCTION__) < 0) + goto done; + } + retval = 0; + done: + return retval; +} + +/*! Call all plugins to get version output + * + * @param[in] h Clixon handle + * @param[in] f Output file + * @retval 0 OK + * @retval -1 Error + */ +int +clixon_plugin_version_all(clicon_handle h, + FILE *f) +{ + int retval = -1; + clixon_plugin_t *cp = NULL; + cp = NULL; + while ((cp = clixon_plugin_each(h, cp)) != NULL) { + if (clixon_plugin_version_one(cp, h, f) < 0) + goto done; + } + retval = 0; + done: + return retval; +} + /*-------------------------------------------------------------------- * RPC callbacks for both client/frontend and backend plugins. */ diff --git a/test/test_cli.sh b/test/test_cli.sh index 7ea65768..103a79c9 100755 --- a/test/test_cli.sh +++ b/test/test_cli.sh @@ -176,6 +176,9 @@ fi new "wait backend" wait_backend +new "cli version" +expectpart "$($clixon_cli -1 -f $cfg -V)" 0 "Clixon version $CLIXON_VERSION" "Clixon main example version" + new "cli configure top" expectpart "$($clixon_cli -1 -f $cfg set interfaces)" 0 "^$"