diff --git a/CHANGELOG.md b/CHANGELOG.md
index 16267558..40fa5a2b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,11 @@ Expected: July 2020
### Major New features
+* Auto-CLI enhancements
+ * A generated clispec including state (default @datanodestate) also generated along with the config clispec tree (default @datanode)
+ * New mode `GT_HIDE` set by option `CLICON_CLI_GENMODEL_TYPE` to collapse non-presence containers that only contain a single list
+ * Added a prefix for cli_show_config/cli_show_auto so that it can produce parseable output
+ * Thanks dcornejo@netgate.com for trying it out and suggestions
* Embedding restconf into the existing [libevhtp](https://github.com/criticalstack/libevhtp) embedded web server. Experimental.
* The existing FCGI restconf solution will continue to be supported for NGINX and other reverese proxies with an fast CGI API.
* The restconf code has been refactored to support both modes. Hopefully, it should be straightforward to add another embedded server, such as GNU microhttpd.
@@ -35,9 +40,10 @@ Expected: July 2020
* `--with-restconf=evhtp Integrate restconf with libevhtp server`
* `--without-restconf Disable restconf altogether`
+### C/CLI-API changes on existing features (For developers)
-### C-API changes on existing features (For developers)
-
+* Added prefix for cli_show_config/cli_show_auto so that it can produce parseable output
+* Replaced the global variable `debug` with access function: `clicon_debug_get()`.
* Due to name collision with libevent, all clixon event functions prepended with `clixon_`. You need to rename your event functions as follows:
* event_reg_fd() -> clixon_event_reg_fd()
* event_unreg_fd() -> clixon_event_unreg_fd()
@@ -53,6 +59,10 @@ Expected: July 2020
* Added new function `clicon_xml2str()` to complement xml_print and others that returns a malloced string.
* Added new function `xml_child_index_each()` to iterate over the children of an XML node according to the order defined by an explicit index variable. This is a complement to `xml_child_each()` which iterates using the default order.
+### Corrected Bugs
+
+* Fixed: The module `clixon-rfc5277` was always enabled, but should only be enabled when `CLICON_STREAM_DISCOVERY_RFC5277` is enabled.
+
## 4.5.0
12 May 2020
diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index 43c4f6b2..43b7d61a 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -1066,7 +1066,7 @@ from_client_get(clicon_handle h,
(ret = xml_yang_validate_add(h, xret, &xerr)) < 0)
goto done;
if (ret == 0){
- if (debug)
+ if (clicon_debug_get())
clicon_log_xml(LOG_DEBUG, xret, "VALIDATE_STATE");
if (clixon_netconf_internal_error(xerr,
". Internal error, state callback returned invalid XML",
@@ -1362,7 +1362,7 @@ from_client_debug(clicon_handle h,
clicon_debug_init(level, NULL); /* 0: dont debug, 1:debug */
setlogmask(LOG_UPTO(level?LOG_DEBUG:LOG_INFO)); /* for syslog */
- clicon_log(LOG_NOTICE, "%s debug:%d", __FUNCTION__, debug);
+ clicon_log(LOG_NOTICE, "%s debug:%d", __FUNCTION__, clicon_debug_get());
cprintf(cbret, "");
ok:
retval = 0;
diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c
index c020dc90..823eb878 100644
--- a/apps/backend/backend_commit.c
+++ b/apps/backend/backend_commit.c
@@ -453,7 +453,7 @@ from_validate_common(clicon_handle h,
&td->td_tcvec, /* changed: wanted values */
&td->td_clen) < 0)
goto done;
- if (debug>1)
+ if (clicon_debug_get()>1)
transaction_print(stderr, td);
/* Mark as changed in tree */
for (i=0; itd_dlen; i++){ /* Also down */
diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c
index 55fb6ac9..7afa8091 100644
--- a/apps/backend/backend_main.c
+++ b/apps/backend/backend_main.c
@@ -297,7 +297,7 @@ check_drop_priv(clicon_handle h,
clicon_err(OE_DAEMON, EPERM, "Privileges can only be dropped from root user (uid is %u)\n", uid);
goto done;
}
- /* When dropping priveleges, datastores are created if they do not exist.
+ /* When dropping privileges, datastores are created if they do not exist.
* But when drops are not made, datastores are created on demand.
* XXX: move the creation to top-level so they are always created at init?
*/
@@ -461,6 +461,7 @@ main(int argc,
cvec *nsctx_global = NULL; /* Global namespace context */
size_t cligen_buflen;
size_t cligen_bufthreshold;
+ int dbg;
/* In the startup, logs to stderr & syslog and debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@@ -472,6 +473,7 @@ main(int argc,
once = 0;
zap = 0;
extraxml_file = NULL;
+ dbg = 0;
/*
* Command-line options for help, debug, and config-file
@@ -489,7 +491,7 @@ main(int argc,
help = 1;
break;
case 'D' : /* debug */
- if (sscanf(optarg, "%d", &debug) != 1)
+ if (sscanf(optarg, "%d", &dbg) != 1)
usage(h, argv[0]);
break;
case 'f': /* config file */
@@ -513,8 +515,8 @@ main(int argc,
* XXX: if started in a start-daemon script, there will be irritating
* double syslogs until fork below.
*/
- clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, logdst);
- clicon_debug_init(debug, NULL);
+ clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst);
+ clicon_debug_init(dbg, NULL);
/* Find and read configfile */
if (clicon_options_main(h) < 0){
@@ -622,7 +624,7 @@ main(int argc,
/* Access the remaining argv/argc options (after --) w clicon-argv_get() */
clicon_argv_set(h, argv0, argc, argv);
- clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, logdst);
+ clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst);
/* Defer: Wait to the last minute to print help message */
if (help)
@@ -880,7 +882,7 @@ main(int argc,
demonized errors OK. Before this stage, errors are logged on stderr
also */
if (foreground==0){
- clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO,
+ clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO,
logdst==CLICON_LOG_FILE?CLICON_LOG_FILE:CLICON_LOG_SYSLOG);
if (daemon(0, 0) < 0){
fprintf(stderr, "config: daemon");
@@ -911,8 +913,8 @@ main(int argc,
goto done;
if (clicon_socket_set(h, ss) < 0)
goto done;
- if (debug)
- clicon_option_dump(h, debug);
+ if (dbg)
+ clicon_option_dump(h, dbg);
/* Depending on configure setting, privileges may be dropped here after
* initializations */
if (check_drop_priv(h, gid) < 0)
diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c
index 1eaceee1..e2b3bcc3 100644
--- a/apps/backend/backend_plugin.c
+++ b/apps/backend/backend_plugin.c
@@ -246,7 +246,7 @@ clixon_plugin_statedata_all(clicon_handle h,
continue;
}
#if 1
- if (debug)
+ if (clicon_debug_get())
clicon_log_xml(LOG_DEBUG, x, "%s STATE:", __FUNCTION__);
#endif
/* XXX: ret == 0 invalid yang binding should be handled as internal error */
diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c
index 0e7224c1..39d95289 100644
--- a/apps/cli/cli_generate.c
+++ b/apps/cli/cli_generate.c
@@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2020 Olof Hagsand
+ Copyright (C) 2009-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
@@ -62,9 +63,9 @@
#include "cli_plugin.h"
#include "cli_generate.h"
-/* This is the default callback function. But this is typically overwritten */
-#define GENERATE_CALLBACK "overwrite_me"
-
+/*
+ * Constants
+ */
/* variable expand function */
#define GENERATE_EXPAND_XMLDB "expand_dbvar"
@@ -340,8 +341,8 @@ yang2cli_var_pattern(clicon_handle h,
}
/* Forward */
-static int yang2cli_stmt(clicon_handle h, yang_stmt *ys,
- enum genmodel_type gt, int level, cbuf *cb);
+static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, enum genmodel_type gt,
+ int level, int state, cbuf *cb);
static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
yang_stmt *ytype, char *helptext, cbuf *cb);
@@ -657,7 +658,7 @@ yang2cli_leaf(clicon_handle h,
*s = '\0';
}
cprintf(cb, "%*s", level*3, "");
- if (gt == GT_VARS|| gt == GT_ALL){
+ if (gt == GT_VARS|| gt == GT_ALL || gt == GT_HIDE){
cprintf(cb, "%s", yang_argument_get(ys));
if (helptext)
cprintf(cb, "(\"%s\")", helptext);
@@ -686,6 +687,7 @@ yang2cli_leaf(clicon_handle h,
* @param[in] ys Yang statement
* @param[in] gt CLI Generate style
* @param[in] level Indentation level
+ * @param[in] state Include syntax for state not only config
* @param[out] cb Buffer where cligen code is written
*/
static int
@@ -693,6 +695,7 @@ yang2cli_container(clicon_handle h,
yang_stmt *ys,
enum genmodel_type gt,
int level,
+ int state,
cbuf *cb)
{
yang_stmt *yc;
@@ -700,26 +703,34 @@ yang2cli_container(clicon_handle h,
int retval = -1;
char *helptext = NULL;
char *s;
+ int hide = 0;
- cprintf(cb, "%*s%s", level*3, "", yang_argument_get(ys));
- if ((yd = yang_find(ys, Y_DESCRIPTION, NULL)) != NULL){
- if ((helptext = strdup(yang_argument_get(yd))) == NULL){
- clicon_err(OE_UNIX, errno, "strdup");
- goto done;
+ /* If non-presence container && HIDE mode && only child is
+ * a list, then skip container keyword
+ * See also xml2cli
+ */
+ if ((hide = yang_container_cli_hide(ys, gt)) == 0){
+ cprintf(cb, "%*s%s", level*3, "", yang_argument_get(ys));
+ if ((yd = yang_find(ys, Y_DESCRIPTION, NULL)) != NULL){
+ if ((helptext = strdup(yang_argument_get(yd))) == NULL){
+ clicon_err(OE_UNIX, errno, "strdup");
+ goto done;
+ }
+ if ((s = strstr(helptext, "\n\n")) != NULL)
+ *s = '\0';
+ cprintf(cb, "(\"%s\")", helptext);
}
- if ((s = strstr(helptext, "\n\n")) != NULL)
- *s = '\0';
- cprintf(cb, "(\"%s\")", helptext);
+ if (cli_callback_generate(h, ys, cb) < 0)
+ goto done;
+ cprintf(cb, ";{\n");
}
- if (cli_callback_generate(h, ys, cb) < 0)
- goto done;
- cprintf(cb, ";{\n");
yc = NULL;
while ((yc = yn_each(ys, yc)) != NULL)
- if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
+ if (yang2cli_stmt(h, yc, gt, level+1, state, cb) < 0)
goto done;
- cprintf(cb, "%*s}\n", level*3, "");
+ if (hide == 0)
+ cprintf(cb, "%*s}\n", level*3, "");
retval = 0;
done:
if (helptext)
@@ -732,6 +743,7 @@ yang2cli_container(clicon_handle h,
* @param[in] ys Yang statement
* @param[in] gt CLI Generate style
* @param[in] level Indentation level
+ * @param[in] state Include syntax for state not only config
* @param[out] cb Buffer where cligen code is written
*/
static int
@@ -739,6 +751,7 @@ yang2cli_list(clicon_handle h,
yang_stmt *ys,
enum genmodel_type gt,
int level,
+ int state,
cbuf *cb)
{
yang_stmt *yc;
@@ -775,7 +788,8 @@ yang2cli_list(clicon_handle h,
/* Print key variable now, and skip it in loop below
Note, only print callback on last statement
*/
- if (yang2cli_leaf(h, yleaf, gt==GT_VARS?GT_NONE:gt, level+1,
+ if (yang2cli_leaf(h, yleaf,
+ (gt==GT_VARS||gt==GT_HIDE)?GT_NONE:gt, level+1,
cvec_next(cvk, cvi)?0:1, cb) < 0)
goto done;
}
@@ -794,7 +808,7 @@ yang2cli_list(clicon_handle h,
}
if (cvi != NULL)
continue;
- if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
+ if (yang2cli_stmt(h, yc, gt, level+1, state, cb) < 0)
goto done;
}
cprintf(cb, "%*s}\n", level*3, "");
@@ -811,6 +825,7 @@ yang2cli_list(clicon_handle h,
* @param[in] ys Yang statement
* @param[in] gt CLI Generate style
* @param[in] level Indentation level
+ * @param[in] state Include syntax for state not only config
* @param[out] cb Buffer where cligen code is written
@example
choice interface-type {
@@ -826,6 +841,7 @@ yang2cli_choice(clicon_handle h,
yang_stmt *ys,
enum genmodel_type gt,
int level,
+ int state,
cbuf *cb)
{
int retval = -1;
@@ -835,7 +851,7 @@ yang2cli_choice(clicon_handle h,
while ((yc = yn_each(ys, yc)) != NULL) {
switch (yang_keyword_get(yc)){
case Y_CASE:
- if (yang2cli_stmt(h, yc, gt, level+2, cb) < 0)
+ if (yang2cli_stmt(h, yc, gt, level+2, state, cb) < 0)
goto done;
break;
case Y_CONTAINER:
@@ -843,45 +859,47 @@ yang2cli_choice(clicon_handle h,
case Y_LEAF_LIST:
case Y_LIST:
default:
- if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
+ if (yang2cli_stmt(h, yc, gt, level+1, state, cb) < 0)
goto done;
break;
}
}
retval = 0;
- done:
+ done:
return retval;
}
/*! Generate CLI code for Yang statement
* @param[in] h Clixon handle
* @param[in] ys Yang statement
- * @param[out] cb Buffer where cligen code is written
* @param[in] gt CLI Generate style
* @param[in] level Indentation level
+ * @param[in] state Include syntax for state not only config
+ * @param[out] cb Buffer where cligen code is written
*/
static int
yang2cli_stmt(clicon_handle h,
yang_stmt *ys,
enum genmodel_type gt,
- int level, /* indentation level for pretty-print */
+ int level,
+ int state,
cbuf *cb)
{
yang_stmt *yc;
int retval = -1;
- if (yang_config(ys)){
+ if (state || yang_config(ys)){
switch (yang_keyword_get(ys)){
case Y_CONTAINER:
- if (yang2cli_container(h, ys, gt, level, cb) < 0)
+ if (yang2cli_container(h, ys, gt, level, state, cb) < 0)
goto done;
break;
case Y_LIST:
- if (yang2cli_list(h, ys, gt, level, cb) < 0)
+ if (yang2cli_list(h, ys, gt, level, state, cb) < 0)
goto done;
break;
case Y_CHOICE:
- if (yang2cli_choice(h, ys, gt, level, cb) < 0)
+ if (yang2cli_choice(h, ys, gt, level, state, cb) < 0)
goto done;
break;
case Y_LEAF_LIST:
@@ -894,7 +912,7 @@ yang2cli_stmt(clicon_handle h,
case Y_MODULE:
yc = NULL;
while ((yc = yn_each(ys, yc)) != NULL)
- if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
+ if (yang2cli_stmt(h, yc, gt, level+1, state, cb) < 0)
goto done;
break;
default: /* skip */
@@ -902,7 +920,7 @@ yang2cli_stmt(clicon_handle h,
}
}
retval = 0;
- done:
+ done:
return retval;
}
@@ -911,6 +929,7 @@ yang2cli_stmt(clicon_handle h,
* @param[in] yspec Yang specification
* @param[in] gt CLI Generate style
* @param[in] printgen Log generated CLIgen syntax
+ * @param[in] state Also include state syntax
* @param[out] ptnew CLIgen parse-tree
*
* Code generation styles:
@@ -922,6 +941,7 @@ yang2cli(clicon_handle h,
yang_stmt *yspec,
enum genmodel_type gt,
int printgen,
+ int state,
parse_tree *ptnew)
{
cbuf *cb = NULL;
@@ -936,7 +956,7 @@ yang2cli(clicon_handle h,
/* Traverse YANG, loop through all modules and generate CLI */
ymod = NULL;
while ((ymod = yn_each(yspec, ymod)) != NULL)
- if (yang2cli_stmt(h, ymod, gt, 0, cb) < 0)
+ if (yang2cli_stmt(h, ymod, gt, 0, state, cb) < 0)
goto done;
if (printgen)
clicon_log(LOG_NOTICE, "%s: Generated CLI spec:\n%s", __FUNCTION__, cbuf_get(cb));
@@ -951,10 +971,10 @@ yang2cli(clicon_handle h,
goto done;
cvec_free(globals);
/* Resolve the expand callback functions in the generated syntax.
- This "should" only be GENERATE_EXPAND_XMLDB
- handle=NULL for global namespace, this means expand callbacks must be in
- CLICON namespace, not in a cli frontend plugin.
- */
+ * This "should" only be GENERATE_EXPAND_XMLDB
+ * handle=NULL for global namespace, this means expand callbacks must be in
+ * CLICON namespace, not in a cli frontend plugin.
+ */
if (cligen_expandv_str2fn(ptnew, (expandv_str2fn_t*)clixon_str2fn, NULL) < 0)
goto done;
diff --git a/apps/cli/cli_generate.h b/apps/cli/cli_generate.h
index 84ee329c..819082d3 100644
--- a/apps/cli/cli_generate.h
+++ b/apps/cli/cli_generate.h
@@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2020 Olof Hagsand
+ Copyright (C) 2009-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
@@ -36,10 +37,22 @@
#ifndef _CLI_GENERATE_H_
#define _CLI_GENERATE_H_
+/*
+ * Constants
+ */
+/* This is the default "virtual" callback function of the auto-cli. It should be overwritten by
+ * a callback specified in a clispec, such as:
+ * @code
+ * set @datamodel, cli_set();
+ * @endcode
+ * where the virtual callback (overwrite_me) is overwritten by cli_set.
+ */
+#define GENERATE_CALLBACK "overwrite_me"
+
/*
* Prototypes
*/
int yang2cli(clicon_handle h, yang_stmt *yspec, enum genmodel_type gt,
- int printgen, parse_tree *ptnew);
+ int printgen, int state, parse_tree *ptnew);
#endif /* _CLI_GENERATE_H_ */
diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c
index 622a6023..eb29197d 100644
--- a/apps/cli/cli_main.c
+++ b/apps/cli/cli_main.c
@@ -236,6 +236,103 @@ cli_interactive(clicon_handle h)
return retval;
}
+/*! Generate one autocli clispec tree
+ *
+ * @param[in] h Clixon handle
+ * @param[in] name Name of tree
+ * @param[in] gt genmodel-type, ie HOW to generate the CLI
+ * @param[in] printgen Print CLI syntax to stderr
+ *
+ * Generate clispec (datamodel) from YANG dataspec and add to the set of cligen trees
+ * (as a separate mode)
+ * This tree is referenced from the main CLI spec (CLICON_CLISPEC_DIR) using the "tree reference"
+ * syntax, ie @datamodel
+ * @param[in] h Clixon handle
+ * @param[in] printgen Print CLI syntax generated from dbspec
+ * @retval 0 OK
+ * @retval -1 Error
+ *
+ * @note that yang2cli generates syntax for ALL modules under the loaded yangspec.
+ */
+static int
+autocli_tree(clicon_handle h,
+ char *name,
+ enum genmodel_type gt,
+ int state,
+ int printgen)
+{
+ int retval = -1;
+ parse_tree *pt = NULL; /* cli parse tree */
+ yang_stmt *yspec;
+
+ if ((pt = pt_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "pt_new");
+ goto done;
+ }
+ yspec = clicon_dbspec_yang(h);
+ /* Generate tree (this is where the action is) */
+ if (yang2cli(h, yspec, gt, printgen, state, pt) < 0)
+ goto done;
+ /* Append cligen tree and name it */
+ if (cligen_tree_add(cli_cligen(h), name, pt) < 0)
+ goto done;
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Generate autocli, ie if enabled, generate clispec from YANG and add to cligen parse-trees
+ *
+ * Generate clispec (datamodel) from YANG dataspec and add to the set of cligen trees
+ * (as a separate mode)
+ * This tree is referenced from the main CLI spec (CLICON_CLISPEC_DIR) using the "tree reference"
+ * syntax, ie @datamodel
+ * Also (if enabled) generate a second "state" tree called @datamodelstate
+ *
+ * @param[in] h Clixon handle
+ * @param[in] printgen Print CLI syntax generated from dbspec
+ * @retval 0 OK
+ * @retval -1 Error
+ */
+static int
+autocli_start(clicon_handle h,
+ int printgen)
+{
+ int retval = -1;
+ int autocli_model = 0;
+ cbuf *treename = NULL;
+ enum genmodel_type gt;
+
+ /* If autocli disabled quit */
+ if ((autocli_model = clicon_cli_genmodel(h)) == 0)
+ goto ok;
+ /* Get the autocli type, ie HOW the cli is generated (could be much more here) */
+ gt = clicon_cli_genmodel_type(h);
+ /* Create treename cbuf */
+ if ((treename = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
+ /* The tree name is by default @datamodel but can be changed by option (why would one do that?) */
+ cprintf(treename, "%s", clicon_cli_model_treename(h));
+ if (autocli_tree(h, cbuf_get(treename), gt, 0, printgen) < 0)
+ goto done;
+
+ /* Create a tree for config+state. This tree's name has appended "state" to @datamodel (XXX)
+ */
+ if (autocli_model > 1){
+ cprintf(treename, "state");
+ if (autocli_tree(h, cbuf_get(treename), gt, 1, printgen) < 0)
+ goto done;
+ }
+ ok:
+ retval = 0;
+ done:
+ if (treename)
+ cbuf_free(treename);
+ return retval;
+}
+
static void
usage(clicon_handle h,
char *argv0)
@@ -294,6 +391,7 @@ main(int argc,
cvec *nsctx_global = NULL; /* Global namespace context */
size_t cligen_buflen;
size_t cligen_bufthreshold;
+ int dbg=0;
/* Defaults */
once = 0;
@@ -332,7 +430,7 @@ main(int argc,
help = 1;
break;
case 'D' : /* debug */
- if (sscanf(optarg, "%d", &debug) != 1)
+ if (sscanf(optarg, "%d", &dbg) != 1)
usage(h, argv[0]);
break;
case 'f': /* config file */
@@ -352,9 +450,9 @@ main(int argc,
/*
* Logs, error and debug to stderr or syslog, set debug level
*/
- clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, logdst);
+ clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst);
- clicon_debug_init(debug, NULL);
+ clicon_debug_init(dbg, NULL);
/* Find, read and parse configfile */
if (clicon_options_main(h) < 0){
@@ -535,29 +633,9 @@ main(int argc,
if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done;
- /* Create tree generated from dataspec. If no other trees exists, this is
- * the only one.
- * The following code creates the tree @datamodel
- * This tree is referenced from the main CLI spec (CLICON_CLISPEC_DIR)
- * using the "tree reference"
- * syntax, ie @datamodel
- * But note that yang2cli generates syntax for ALL modules, not just for
- * .
- */
- if (clicon_cli_genmodel(h)){
- parse_tree *pt = NULL; /* cli parse tree */
- char *treeref;
-
- if ((pt = pt_new()) == NULL){
- clicon_err(OE_UNIX, errno, "pt_new");
- goto done;
- }
- treeref = clicon_cli_model_treename(h);
- /* Create cli command tree from dbspec */
- if (yang2cli(h, yspec, clicon_cli_genmodel_type(h), printgen, pt) < 0)
- goto done;
- cligen_tree_add(cli_cligen(h), treeref, pt);
- }
+ /* Create autocli from YANG */
+ if (autocli_start(h, printgen) < 0)
+ goto done;
/* Initialize cli syntax */
if (cli_syntax_load(h) < 0)
@@ -588,8 +666,8 @@ main(int argc,
if (logclisyntax)
cli_logsyntax_set(h, logclisyntax);
- if (debug)
- clicon_option_dump(h, debug);
+ if (dbg)
+ clicon_option_dump(h, dbg);
/* Join rest of argv to a single command */
restarg = clicon_strjoin(argc, argv, " ");
diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c
index e5a956c4..0870926f 100644
--- a/apps/cli/cli_plugin.c
+++ b/apps/cli/cli_plugin.c
@@ -66,6 +66,7 @@
#include "clixon_cli_api.h"
#include "cli_plugin.h"
#include "cli_handle.h"
+#include "cli_generate.h"
/*
*
@@ -177,7 +178,7 @@ cli_syntax_unload(clicon_handle h)
* @param[in] handle Handle to plugin .so module as returned by dlopen
* @param[out] error Static error string, if set indicates error
* @retval fn Function pointer
- * @retval NULL FUnction not found or symbol NULL (check error for proper handling)
+ * @retval NULL Function not found or symbol NULL (check error for proper handling)
* @see see cli_plugin_load where (optional) handle opened
* @note the returned function is not type-checked which may result in segv at runtime
*/
@@ -188,8 +189,15 @@ clixon_str2fn(char *name,
{
void *fn = NULL;
+
+
/* Reset error */
*error = NULL;
+ /* Special check for auto-cli. If the virtual callback is used, it should be overwritten later
+ * by a callback given in the clispec, eg: set @datamodel, cli_set();
+ */
+ if (strcmp(name, GENERATE_CALLBACK) == 0)
+ return NULL;
/* First check given plugin if any */
if (handle) {
diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c
index e8dbf71a..11146ba0 100644
--- a/apps/cli/cli_show.c
+++ b/apps/cli/cli_show.c
@@ -421,10 +421,12 @@ show_yang(clicon_handle h,
* "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
* xpath expression, that may contain one %, eg "/sender[name='foo']"
* If xpath set, the namespace the symbols in xpath belong to (optional)
+ * to print before cli syntax output (optional)
* @code
* show config id , cli_show_config("running","xml","iface[name='foo']","urn:example:example");
* @endcode
* @note if state parameter is set, then db must be running
+ * @see cli_show_auto1
*/
static int
cli_show_config1(clicon_handle h,
@@ -446,9 +448,10 @@ cli_show_config1(clicon_handle h,
yang_stmt *yspec;
char *namespace = NULL;
cvec *nsc = NULL;
+ char *prefix = NULL;
- if (cvec_len(argv) != 3 && cvec_len(argv) != 4){
- clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: ,,[,]", cvec_len(argv));
+ if (cvec_len(argv) < 3 || cvec_len(argv) > 5){
+ clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: ,,[,, []]", cvec_len(argv));
goto done;
}
@@ -474,11 +477,14 @@ cli_show_config1(clicon_handle h,
}
cprintf(cbxpath, "%s", xpath);
/* Fourth argument is namespace */
- if (cvec_len(argv) == 4){
+ if (cvec_len(argv) > 3){
namespace = cv_string_get(cvec_i(argv, 3));
if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL)
goto done;
}
+ if (cvec_len(argv) > 4){
+ prefix = cv_string_get(cvec_i(argv, 4));
+ }
if (state == 0){ /* Get configuration-only from database */
if (clicon_rpc_get_config(h, NULL, db, cbuf_get(cbxpath), nsc, &xt) < 0)
goto done;
@@ -516,7 +522,7 @@ cli_show_config1(clicon_handle h,
goto done;
xc = NULL; /* Dont print xt itself */
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL)
- xml2cli(stdout, xc, NULL, gt); /* cli syntax */
+ xml2cli(stdout, xc, prefix, gt); /* cli syntax */
break;
case FORMAT_NETCONF:
fprintf(stdout, "\n");
@@ -549,6 +555,7 @@ done:
* "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
* xpath expression, that may contain one %, eg "/sender[name="%s"]"
* If xpath set, the namespace the symbols in xpath belong to (optional)
+ * to print before cli syntax output
* @code
* show config id , cli_show_config("running","xml","iface[name='foo']","urn:example:example");
* @endcode
@@ -675,8 +682,10 @@ int cli_show_version(clicon_handle h,
* Generated API PATH
* "running"|"candidate"|"startup"
* "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
+ * to print before cli syntax output
* @note if state parameter is set, then db must be running
* @note that first argument is generated by code.
+ * @see cli_show_config1
*/
static int
cli_show_auto1(clicon_handle h,
@@ -687,7 +696,6 @@ cli_show_auto1(clicon_handle h,
int retval = 1;
yang_stmt *yspec;
char *api_path_fmt; /* xml key format */
- // char *api_path = NULL; /* xml key */
char *db;
char *xpath = NULL;
cvec *nsc = NULL;
@@ -698,9 +706,10 @@ cli_show_auto1(clicon_handle h,
cxobj *xerr;
enum genmodel_type gt;
char *api_path = NULL;
+ char *prefix = NULL;
- if (cvec_len(argv) != 3){
- clicon_err(OE_PLUGIN, 0, "Usage: * . (*) generated.");
+ if (cvec_len(argv) < 3 || cvec_len(argv) > 4){
+ clicon_err(OE_PLUGIN, 0, "Usage: * . (*) generated.");
goto done;
}
/* First argv argument: API_path format */
@@ -709,6 +718,10 @@ cli_show_auto1(clicon_handle h,
db = cv_string_get(cvec_i(argv, 1));
/* Third format: output format */
formatstr = cv_string_get(cvec_i(argv, 2));
+ if (cvec_len(argv) > 3){
+ /* Fourth format: prefix to print before cli syntax */
+ prefix = cv_string_get(cvec_i(argv, 3));
+ }
if ((int)(format = format_str2int(formatstr)) < 0){
clicon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr);
goto done;
@@ -758,7 +771,7 @@ cli_show_auto1(clicon_handle h,
case FORMAT_CLI:
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
goto done;
- xml2cli(stdout, xp, NULL, gt); /* cli syntax */
+ xml2cli(stdout, xp, prefix, gt); /* cli syntax */
break;
case FORMAT_NETCONF:
fprintf(stdout, "\n");
@@ -786,7 +799,9 @@ cli_show_auto1(clicon_handle h,
* Generated API PATH
* "running"|"candidate"|"startup"
* "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
+ * to print before cli syntax outptu
* @see cli_show_auto_state For config and state
+ * @note SHOULD be used: ... @datamodel, cli_show_auto(,...) to get correct #args
*/
int
cli_show_auto(clicon_handle h,
@@ -801,7 +816,9 @@ cli_show_auto(clicon_handle h,
* Generated API PATH
* "running"
* "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
+ * to print before cli syntax output
* @see cli_show_auto For config only
+ * @see cli_show_config_state Not auto-generated
*/
int
cli_show_auto_state(clicon_handle h,
diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h
index 80653f66..c7c31040 100644
--- a/apps/cli/clixon_cli_api.h
+++ b/apps/cli/clixon_cli_api.h
@@ -69,54 +69,44 @@ int cli_notification_register(clicon_handle h, char *stream, enum format_enum fo
/* cli_common.c: CLIgen new vector callbacks */
-
int cli_set(clicon_handle h, cvec *vars, cvec *argv);
int cli_merge(clicon_handle h, cvec *vars, cvec *argv);
int cli_create(clicon_handle h, cvec *vars, cvec *argv);
+
int cli_remove(clicon_handle h, cvec *vars, cvec *argv);
int cli_del(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_cli(clicon_handle h, cvec *vars, cvec *argv);
-
int cli_debug_backend(clicon_handle h, cvec *vars, cvec *argv);
-
int cli_debug_restconf(clicon_handle h, cvec *vars, cvec *argv);
int cli_set_mode(clicon_handle h, cvec *vars, cvec *argv);
-
int cli_start_shell(clicon_handle h, cvec *vars, cvec *argv);
-
int cli_quit(clicon_handle h, cvec *vars, cvec *argv);
-
int cli_commit(clicon_handle h, cvec *vars, cvec *argv);
int cli_validate(clicon_handle h, cvec *vars, cvec *argv);
-
int compare_dbs(clicon_handle h, cvec *vars, cvec *argv);
int load_config_file(clicon_handle h, cvec *vars, cvec *argv);
int save_config_file(clicon_handle h, cvec *vars, cvec *argv);
-
int delete_all(clicon_handle h, cvec *vars, cvec *argv);
-
int discard_changes(clicon_handle h, cvec *vars, cvec *argv);
-
int cli_notify(clicon_handle h, cvec *cvv, cvec *argv);
-
int db_copy(clicon_handle h, cvec *cvv, cvec *argv);
int cli_lock(clicon_handle h, cvec *cvv, cvec *argv);
@@ -133,7 +123,6 @@ int expand_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
/* cli_show.c: CLIgen new vector arg callbacks */
int show_yang(clicon_handle h, cvec *vars, cvec *argv);
-
int show_conf_xpath(clicon_handle h, cvec *cvv, cvec *argv);
int cli_show_config(clicon_handle h, cvec *cvv, cvec *argv);
diff --git a/apps/netconf/netconf_lib.c b/apps/netconf/netconf_lib.c
index a42dde08..995fa69e 100644
--- a/apps/netconf/netconf_lib.c
+++ b/apps/netconf/netconf_lib.c
@@ -197,7 +197,7 @@ netconf_output(int s,
int retval = -1;
clicon_debug(1, "SEND %s", msg);
- if (debug > 1){ /* XXX: below only works to stderr, clicon_debug may log to syslog */
+ if (clicon_debug_get() > 1){ /* XXX: below only works to stderr, clicon_debug may log to syslog */
cxobj *xt = NULL;
if (clixon_xml_parse_string(buf, YB_NONE, NULL, &xt, NULL) == 0){
clicon_xml2file(stderr, xml_child_i(xt, 0), 0, 0);
diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c
index 376edd89..5d30ec02 100644
--- a/apps/netconf/netconf_main.c
+++ b/apps/netconf/netconf_main.c
@@ -401,6 +401,7 @@ main(int argc,
cvec *nsctx_global = NULL; /* Global namespace context */
size_t cligen_buflen;
size_t cligen_bufthreshold;
+ int dbg = 0;
/* Create handle */
if ((h = clicon_handle_init()) == NULL)
@@ -421,7 +422,7 @@ main(int argc,
usage(h, argv[0]);
break;
case 'D' : /* debug */
- if (sscanf(optarg, "%d", &debug) != 1)
+ if (sscanf(optarg, "%d", &dbg) != 1)
usage(h, argv[0]);
break;
case 'f': /* override config file */
@@ -442,8 +443,8 @@ main(int argc,
/*
* Logs, error and debug to stderr or syslog, set debug level
*/
- clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, logdst);
- clicon_debug_init(debug, NULL);
+ clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst);
+ clicon_debug_init(dbg, NULL);
/* Find, read and parse configfile */
if (clicon_options_main(h) < 0)
@@ -592,8 +593,8 @@ main(int argc,
send_hello(h, 1, id);
if (clixon_event_reg_fd(0, netconf_input_cb, h, "netconf socket") < 0)
goto done;
- if (debug)
- clicon_option_dump(h, debug);
+ if (dbg)
+ clicon_option_dump(h, dbg);
if (tv.tv_sec || tv.tv_usec){
struct timeval t;
gettimeofday(&t, NULL);
diff --git a/apps/restconf/Makefile.in b/apps/restconf/Makefile.in
index 92a9124b..b0680280 100644
--- a/apps/restconf/Makefile.in
+++ b/apps/restconf/Makefile.in
@@ -78,35 +78,33 @@ CPPFLAGS = @CPPFLAGS@ -fPIC
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
-# Applications
-ifeq ($(with_restconf),fcgi)
-APPL = clixon_restconf # fcgi / nginx
-else
-APPL = clixon_restconf_$(with_restconf)
-endif
+# Application
+APPL = clixon_restconf
# Common source - not accessible from plugin - independent of restconf package (fcgi|evhtp)
-#APPSRC = restconf_lib.c
+APPSRC =
+APPSRC += restconf_api.c # maybe empty
+APPSRC += restconf_api_$(with_restconf).c # cant be .so since libevhtp is a.
+APPSRC += restconf_root.c
+APPSRC += restconf_$(with_restconf)_main.c
# Fcgi-specific source including main
ifeq ($(with_restconf),fcgi)
-APPSRC = restconf_fcgi_lib.c
-APPSRC += restconf_fcgi_main.c
-APPSRC += restconf_methods.c # These should be moved ^
+APPSRC += restconf_fcgi_lib.c # Most of these should be made generic
+APPSRC += restconf_methods.c
APPSRC += restconf_methods_post.c
APPSRC += restconf_methods_get.c
APPSRC += restconf_stream.c
endif
-# Evhtp-specific source including main
-ifeq ($(with_restconf),evhtp)
-APPSRC = restconf_evhtp_main.c
-endif
-
APPOBJ = $(APPSRC:.c=.o)
# Accessible from plugin
+# XXX actually this does not work properly, there are functions in lib
+# (eg restconf_method_notallowed) that uses symbols in restconf_api that
+# are not in the lib.
LIBSRC = restconf_lib.c
+
LIBOBJ = $(LIBSRC:.c=.o)
# This lib is very small but used for clixon restconf applications to access clixon restconf lib
diff --git a/apps/restconf/restconf_api.c b/apps/restconf/restconf_api.c
new file mode 100644
index 00000000..26638858
--- /dev/null
+++ b/apps/restconf/restconf_api.c
@@ -0,0 +1,66 @@
+/*
+ *
+ ***** BEGIN LICENSE BLOCK *****
+
+ Copyright (C) 2009-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
+
+ This file is part of CLIXON.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ 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.
+
+ Alternatively, the contents of this file may be used under the terms of
+ the GNU General Public License Version 3 or later (the "GPL"),
+ in which case the provisions of the GPL are applicable instead
+ of those above. If you wish to allow use of your version of this file only
+ under the terms of the GPL, and not to allow others to
+ use your version of this file under the terms of Apache License version 2,
+ indicate your decision by deleting the provisions above and replace them with
+ the notice and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this file under
+ the terms of any one of the Apache License version 2 or the GPL.
+
+ ***** END LICENSE BLOCK *****
+ * Generic restconf API functions
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "clixon_config.h" /* generated by config & autoconf */
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include /* chmod */
+
+/* cligen */
+#include
+
+/* clicon */
+#include
+
+/* restconf */
+#include "restconf_lib.h"
+#include "restconf_api.h"
+
diff --git a/apps/restconf/restconf_api.h b/apps/restconf/restconf_api.h
new file mode 100644
index 00000000..50084654
--- /dev/null
+++ b/apps/restconf/restconf_api.h
@@ -0,0 +1,54 @@
+/*
+ *
+ ***** BEGIN LICENSE BLOCK *****
+
+ Copyright (C) 2009-2019 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
+
+ This file is part of CLIXON.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ 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.
+
+ Alternatively, the contents of this file may be used under the terms of
+ the GNU General Public License Version 3 or later (the "GPL"),
+ in which case the provisions of the GPL are applicable instead
+ of those above. If you wish to allow use of your version of this file only
+ under the terms of the GPL, and not to allow others to
+ use your version of this file under the terms of Apache License version 2,
+ indicate your decision by deleting the provisions above and replace them with
+ the notice and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this file under
+ the terms of any one of the Apache License version 2 or the GPL.
+
+ ***** END LICENSE BLOCK *****
+ *
+ * Virtual clixon restconf API functions.
+ */
+
+#ifndef _RESTCONF_API_H_
+#define _RESTCONF_API_H_
+
+/*
+ * Prototypes
+ */
+int restconf_reply_status_code(void *req, int code);
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+int restconf_reply_header_add(void *req, char *name, char *vfmt, ...) __attribute__ ((format (printf, 3, 4)));
+#else
+int restconf_reply_header_add(FCGX_Request *req, char *name, char *vfmt, ...);
+#endif
+
+int restconf_reply_send(void *req, cbuf *cb);
+
+#endif /* _RESTCONF_API_H_ */
diff --git a/apps/restconf/restconf_api_evhtp.c b/apps/restconf/restconf_api_evhtp.c
new file mode 100644
index 00000000..be3d4aa6
--- /dev/null
+++ b/apps/restconf/restconf_api_evhtp.c
@@ -0,0 +1,183 @@
+/*
+ *
+ ***** BEGIN LICENSE BLOCK *****
+
+ Copyright (C) 2009-2020 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
+
+ This file is part of CLIXON.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ 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.
+
+ Alternatively, the contents of this file may be used under the terms of
+ the GNU General Public License Version 3 or later (the "GPL"),
+ in which case the provisions of the GPL are applicable instead
+ of those above. If you wish to allow use of your version of this file only
+ under the terms of the GPL, and not to allow others to
+ use your version of this file under the terms of Apache License version 2,
+ indicate your decision by deleting the provisions above and replace them with
+ the notice and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this file under
+ the terms of any one of the Apache License version 2 or the GPL.
+
+ ***** END LICENSE BLOCK *****
+
+ * Concrete functions for libevhtp of the
+ * Virtual clixon restconf API functions.
+ * @see restconf_api.h for virtual API
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* evhtp */
+#include
+#include
+
+/* cligen */
+#include
+
+/* clicon */
+#include
+
+#include "restconf_lib.h"
+#include "restconf_api.h" /* Virtual api */
+
+
+/*! Add HTTP header field name and value to reply, evhtp specific
+ * @param[in] req Evhtp http request handle
+ * @param[in] code HTTP status code
+ * @see eg RFC 7230
+ */
+int
+restconf_reply_status_code(void *req0,
+ int code)
+{
+ evhtp_request_t *req = (evhtp_request_t *)req0;
+
+ req->status = code;
+ return 0;
+}
+
+/*! Add HTTP header field name and value to reply, evhtp specific
+ * @param[in] req Evhtp http request handle
+ * @param[in] name HTTP header field name
+ * @param[in] vfmt HTTP header field value format string w variable parameter
+ * @see eg RFC 7230
+ */
+int
+restconf_reply_header_add(void *req0,
+ char *name,
+ char *vfmt,
+ ...)
+
+{
+ evhtp_request_t *req = (evhtp_request_t *)req0;
+ int retval = -1;
+ size_t vlen;
+ char *value = NULL;
+ va_list ap;
+ evhtp_header_t *evhdr;
+
+ if (req == NULL || name == NULL || vfmt == NULL){
+ clicon_err(OE_CFG, EINVAL, "req, name or value is NULL");
+ return -1;
+ }
+ va_start(ap, vfmt);
+ vlen = vsnprintf(NULL, 0, vfmt, ap);
+ va_end(ap);
+ /* allocate value string exactly fitting */
+ if ((value = malloc(vlen+1)) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ /* second round: compute actual value */
+ va_start(ap, vfmt);
+ if (vsnprintf(value, vlen+1, vfmt, ap) < 0){
+ clicon_err(OE_UNIX, errno, "vsnprintf");
+ va_end(ap);
+ goto done;
+ }
+ va_end(ap);
+ if ((evhdr = evhtp_header_new(name, value, 0, 1)) == NULL){ /* 1: free after use */
+ clicon_err(OE_CFG, errno, "evhttp_header_new");
+ goto done;
+ }
+ value = NULL; /* freed by evhtp */
+ evhtp_headers_add_header(req->headers_out, evhdr);
+ retval = 0;
+ done:
+ if (value)
+ free(value);
+ return retval;
+}
+
+/*! Send HTTP reply with potential message body
+ * @param[in] req Evhtp http request handle
+ * @param[in] cb Body as a cbuf, send if
+ *
+ * Prerequisites: status code set, headers given, body if wanted set
+ */
+int
+restconf_reply_send(void *req0,
+ cbuf *cb)
+{
+ evhtp_request_t *req = (evhtp_request_t *)req0;
+ int retval = -1;
+ evhtp_connection_t *conn;
+ struct evbuffer *eb = NULL;
+
+#if 1 /* Optional? */
+ if ((conn = evhtp_request_get_connection(req)) == NULL){
+ clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
+ goto done;
+ }
+ htp_sslutil_add_xheaders(req->headers_out, conn->ssl, HTP_SSLUTILS_XHDR_ALL);
+#endif
+
+ /* create evbuffer* : bufferevent_write_buffer/ drain,
+ ie send everything , except body */
+ evhtp_send_reply_start(req, req->status);
+
+ /* Write a body if cbuf is nonzero */
+ if (cb != NULL && cbuf_len(cb)){
+ /* Suboptimal, copy from cbuf to evbuffer */
+ if ((eb = evbuffer_new()) == NULL){
+ clicon_err(OE_CFG, errno, "evbuffer_new");
+ goto done;
+ }
+ if (evbuffer_add(eb, cbuf_get(cb), cbuf_len(cb)) < 0){
+ clicon_err(OE_CFG, errno, "evbuffer_add");
+ goto done;
+ }
+ evhtp_send_reply_body(req, eb); /* conn->bev = eb, body is different */
+ }
+ evhtp_send_reply_end(req); /* just flag finished */
+ retval = 0;
+ done:
+ if (eb)
+ evhtp_safe_free(eb, evbuffer_free);
+ return retval;
+}
diff --git a/apps/restconf/restconf_api_fcgi.c b/apps/restconf/restconf_api_fcgi.c
new file mode 100644
index 00000000..ef5be280
--- /dev/null
+++ b/apps/restconf/restconf_api_fcgi.c
@@ -0,0 +1,217 @@
+/*
+ *
+ ***** BEGIN LICENSE BLOCK *****
+
+ Copyright (C) 2009-2020 Olof Hagsand
+ Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
+
+ This file is part of CLIXON.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ 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.
+
+ Alternatively, the contents of this file may be used under the terms of
+ the GNU General Public License Version 3 or later (the "GPL"),
+ in which case the provisions of the GPL are applicable instead
+ of those above. If you wish to allow use of your version of this file only
+ under the terms of the GPL, and not to allow others to
+ use your version of this file under the terms of Apache License version 2,
+ indicate your decision by deleting the provisions above and replace them with
+ the notice and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this file under
+ the terms of any one of the Apache License version 2 or the GPL.
+
+ ***** END LICENSE BLOCK *****
+
+ * Concrete functions for FCGI of the
+ * Virtual clixon restconf API functions.
+ * @see restconf_api.h for virtual API
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* cligen */
+#include
+
+/* clicon */
+#include
+
+#include /* Need to be after clixon_xml-h due to attribute format */
+
+#include "restconf_lib.h"
+#include "restconf_api.h" /* Virtual api */
+
+/*! Add HTTP header field name and value to reply, fcgi specific
+ * @param[in] req Fastcgi request handle
+ * @param[in] code HTTP status code
+ * @see eg RFC 7230
+ */
+int
+restconf_reply_status_code(void *req0,
+ int code)
+{
+ FCGX_Request *req = (FCGX_Request *)req0;
+
+ FCGX_SetExitStatus(code, req->out);
+ return 0;
+}
+
+/*! HTTP headers done, if there is a message body coming next
+ * @param[in] req Fastcgi request handle
+ * @retval body Handle for body handling (in fcgi same as req)
+ *
+ * HTTP-message = start-line *( header-field CRLF ) CRLF [ message-body ]
+ * @see eg RFC 7230
+ * XXX may be unecessary (or body start or something)
+ */
+FCGX_Request *
+restconf_reply_body_start(void *req0)
+{
+ FCGX_Request *req = (FCGX_Request *)req0;
+
+ FCGX_FPrintF(req->out, "\r\n");
+ return req;
+}
+
+/*! Add HTTP header field name and value to reply, fcgi specific
+ * @param[in] req Fastcgi request handle
+ * @param[in] name HTTP header field name
+ * @param[in] vfmt HTTP header field value format string w variable parameter
+ * @see eg RFC 7230
+ */
+int
+restconf_reply_header_add(void *req0,
+ char *name,
+ char *vfmt,
+ ...)
+
+{
+ FCGX_Request *req = (FCGX_Request *)req0;
+ int retval = -1;
+ size_t vlen;
+ char *value = NULL;
+ va_list ap;
+
+ if (req == NULL || name == NULL || vfmt == NULL){
+ clicon_err(OE_CFG, EINVAL, "req, name or value is NULL");
+ return -1;
+ }
+ va_start(ap, vfmt);
+ vlen = vsnprintf(NULL, 0, vfmt, ap);
+ va_end(ap);
+ /* allocate value string exactly fitting */
+ if ((value = malloc(vlen+1)) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ /* second round: compute actual value */
+ va_start(ap, vfmt);
+ if (vsnprintf(value, vlen+1, vfmt, ap) < 0){
+ clicon_err(OE_UNIX, errno, "vsnprintf");
+ va_end(ap);
+ goto done;
+ }
+ va_end(ap);
+ FCGX_FPrintF(req->out, "%s: %s\r\n", name, value);
+ retval = 0;
+ done:
+ if (value)
+ free(value);
+ return retval;
+}
+
+/*! Add HTTP message body to reply, fcgi specific
+ * @param[in] req Fastcgi request handle
+ * @param[in,out] content_len This is for Content-Length header
+ * @param[in] bfmt HTTP message body format string w variable parameter
+ * @see eg RFC 7230
+ */
+int
+restconf_reply_body_add(void *req0,
+ size_t *content_len,
+ char *bfmt,
+ ...)
+{
+ FCGX_Request *req = (FCGX_Request *)req0;
+ int retval = -1;
+ size_t sz;
+ size_t blen;
+ char *body = NULL;
+ va_list ap;
+
+ if (req == NULL || bfmt == NULL){
+ clicon_err(OE_CFG, EINVAL, "req or body is NULL");
+ return -1;
+ }
+ va_start(ap, bfmt);
+ blen = vsnprintf(NULL, 0, bfmt, ap);
+ va_end(ap);
+ /* allocate body string exactly fitting */
+ if ((body = malloc(blen+1)) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ /* second round: compute actual body */
+ va_start(ap, bfmt);
+ if (vsnprintf(body, blen+1, bfmt, ap) < 0){
+ clicon_err(OE_UNIX, errno, "vsnprintf");
+ va_end(ap);
+ goto done;
+ }
+ va_end(ap);
+ FCGX_FPrintF(req->out, "%s", body);
+ /* Increment in/out Content-Length parameter */
+ if (content_len){
+ sz = strlen(body);
+ *content_len += sz;
+ }
+ retval = 0;
+ done:
+ if (body)
+ free(body);
+ return retval;
+}
+
+/*! Send HTTP reply with potential message body
+ * @param[in] req Fastcgi request handle
+ * @param[in] cb Body as a cbuf, send if
+ *
+ * Prerequisites: status code set, headers given, body if wanted set
+ */
+int
+restconf_reply_send(void *req0,
+ cbuf *cb)
+{
+ FCGX_Request *req = (FCGX_Request *)req0;
+ int retval = -1;
+
+ /* Write a body if cbuf is nonzero */
+ if (cb != NULL && cbuf_len(cb)){
+ FCGX_FPrintF(req->out, "\r\n");
+ FCGX_FPrintF(req->out, "%s", cbuf_get(cb));
+ }
+ retval = 0;
+ return retval;
+}
diff --git a/apps/restconf/restconf_evhtp_main.c b/apps/restconf/restconf_evhtp_main.c
index da646841..2cdcd7e1 100644
--- a/apps/restconf/restconf_evhtp_main.c
+++ b/apps/restconf/restconf_evhtp_main.c
@@ -31,7 +31,8 @@
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
-
+
+ * libevhtp code
*/
/* XXX temp constant should go away, */
@@ -43,7 +44,7 @@
/* compilation withotu threading support
* XXX: could be disabled already in configure?
*/
-#define EVHTP_DISABLE_EVTHR
+//#define EVHTP_DISABLE_EVTHR
#define EVHTP_DISABLE_REGEX
#include
@@ -73,13 +74,9 @@
/* restconf */
-#include "restconf_lib.h"
-#if 0 /* These are all dependent on FCGX */
-#include "restconf_methods.h"
-#include "restconf_methods_get.h"
-#include "restconf_methods_post.h"
-#include "restconf_stream.h"
-#endif
+#include "restconf_lib.h" /* generic shared with plugins */
+#include "restconf_api.h" /* generic not shared with plugins */
+#include "restconf_root.h"
/* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:l:p:d:y:a:u:o:P:c:k:"
@@ -122,6 +119,178 @@ restconf_sig_child(int arg)
}
}
+static char*
+evhtp_method2str(enum htp_method m)
+{
+ switch (m){
+ case htp_method_GET:
+ return "GET";
+ break;
+ case htp_method_HEAD:
+ return "HEAD";
+ break;
+ case htp_method_POST:
+ return "POST";
+ break;
+ case htp_method_PUT:
+ return "PUT";
+ break;
+ case htp_method_DELETE:
+ return "DELETE";
+ break;
+ case htp_method_PATCH:
+ return "PATCH";
+ break;
+ default:
+ return "XXX";
+ break;
+ }
+}
+
+static int
+query_iterator(evhtp_header_t *hdr,
+ void *arg)
+{
+ cvec *qvec = (cvec *)arg;
+ char *key;
+ char *val;
+ char *valu = NULL; /* unescaped value */
+ cg_var *cv;
+
+ key = hdr->key;
+ val = hdr->val;
+ if (uri_percent_decode(val, &valu) < 0)
+ return -1;
+ if ((cv = cvec_add(qvec, CGV_STRING)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_add");
+ return -1;
+ }
+ cv_name_set(cv, key);
+ cv_string_set(cv, valu);
+ if (valu)
+ free(valu);
+ return 0;
+}
+
+/*! Map from evhtp information to "fcgi" type parameters used in clixon code
+ *
+ * While all these params come via one call in fcgi, the information must be taken from
+ * several different places in evhtp
+ * @param[in] h Clicon handle
+ * @param[in] req Evhtp request struct
+ * @retval 0 OK
+ * @retval -1 Error
+ * The following parameters are set:
+ * QUERY_STRING
+ * REQUEST_METHOD
+ * REQUEST_URI
+ * HTTPS
+ * HTTP_HOST
+ * HTTP_ACCEPT
+ * HTTP_CONTENT_TYPE
+ * @note there may be more used by an application plugin
+ */
+static int
+evhtp_params_set(clicon_handle h,
+ evhtp_request_t *req,
+ cvec *qvec)
+{
+ int retval = -1;
+ htp_method meth;
+ evhtp_uri_t *uri;
+ evhtp_path_t *path;
+ evhtp_header_t *hdr;
+
+ if ((uri = req->uri) == NULL){
+ clicon_err(OE_DAEMON, EFAULT, "No uri");
+ goto done;
+ }
+ if ((path = uri->path) == NULL){
+ clicon_err(OE_DAEMON, EFAULT, "No path");
+ goto done;
+ }
+ meth = evhtp_request_get_method(req);
+
+ /* QUERY_STRING */
+ if (qvec && uri->query)
+ if (evhtp_kvs_for_each(uri->query, query_iterator, qvec) < 0){
+ clicon_err(OE_CFG, errno, "evhtp_kvs_for_each");
+ goto done;
+ }
+
+ if (clixon_restconf_param_set(h, "REQUEST_METHOD", evhtp_method2str(meth)) < 0)
+ goto done;
+ if (clixon_restconf_param_set(h, "REQUEST_URI", path->full) < 0)
+ goto done;
+ if (clixon_restconf_param_set(h, "HTTPS", "https") < 0) /* some string or NULL */
+ goto done;
+ if ((hdr = evhtp_headers_find_header(req->headers_in, "Host")) != NULL){
+ if (clixon_restconf_param_set(h, "HTTP_HOST", hdr->val) < 0)
+ goto done;
+ }
+ if ((hdr = evhtp_headers_find_header(req->headers_in, "Accept")) != NULL){
+ if (clixon_restconf_param_set(h, "HTTP_ACCEPT", hdr->val) < 0)
+ goto done;
+ }
+ if ((hdr = evhtp_headers_find_header(req->headers_in, "Content-Type")) != NULL){
+ if (clixon_restconf_param_set(h, "HTTP_CONTENT_TYPE", hdr->val) < 0)
+ goto done;
+ }
+ retval = 0;
+ done:
+ return retval;
+}
+
+static int
+evhtp_params_clear(clicon_handle h)
+{
+ int retval = -1;
+ char *params[] = {"QUERY_STRING", "REQUEST_METHOD", "REQUEST_URI",
+ "HTTPS", "HTTP_HOST", "HTTP_ACCEPT", "HTTP_CONTENT_TYPE", NULL};
+ char *param;
+ int i=0;
+
+ while((param=params[i]) != NULL)
+ if (clixon_restconf_param_del(h, param) < 0)
+ goto done;
+ retval = 0;
+ done:
+ return retval;
+}
+
+static int
+print_header(evhtp_header_t *header,
+ void *arg)
+{
+ // clicon_handle h = (clicon_handle)arg;
+
+ clicon_debug(1, "%s %s %s",
+ __FUNCTION__, header->key, header->val);
+ return 0;
+}
+
+static evhtp_res
+cx_pre_accept(evhtp_connection_t *conn,
+ void *arg)
+{
+ // clicon_handle h = (clicon_handle)arg;
+
+ clicon_debug(1, "%s", __FUNCTION__);
+ return EVHTP_RES_OK;
+}
+
+static evhtp_res
+cx_post_accept(evhtp_connection_t *conn,
+ void *arg)
+{
+ // clicon_handle h = (clicon_handle)arg;
+
+ clicon_debug(1, "%s", __FUNCTION__);
+ return EVHTP_RES_OK;
+}
+
+/*! Generic callback called if no other callbacks are matched
+ */
static void
cx_gencb(evhtp_request_t *req,
void *arg)
@@ -129,7 +298,7 @@ cx_gencb(evhtp_request_t *req,
evhtp_connection_t *conn;
// clicon_handle h = arg;
- fprintf(stderr, "%s\n", __FUNCTION__);
+ clicon_debug(1, "%s", __FUNCTION__);
if (req == NULL){
errno = EINVAL;
return;
@@ -145,67 +314,105 @@ cx_gencb(evhtp_request_t *req,
return; /* void */
}
-static evhtp_res
-cx_pre_accept(evhtp_connection_t *conn,
- void *arg)
+/*! /.well-known callback
+ * @see cx_genb
+ */
+static void
+cx_path_wellknown(evhtp_request_t *req,
+ void *arg)
{
- fprintf(stderr, "%s\n", __FUNCTION__);
- return EVHTP_RES_OK;
+ clicon_handle h = arg;
+
+ /* input debug */
+ if (clicon_debug_get())
+ evhtp_headers_for_each(req->headers_in, print_header, h);
+ /* get accepted connection */
+
+ /* set fcgi-like paramaters (ignore query vector) */
+ if (evhtp_params_set(h, req, NULL) < 0)
+ goto done;
+ /* call generic function */
+ if (api_well_known(h, req) < 0)
+ goto done;
+
+ /* Clear (fcgi) paramaters from this request */
+ if (evhtp_params_clear(h) < 0)
+ goto done;
+ done:
+ return; /* void */
}
-static evhtp_res
-cx_post_accept(evhtp_connection_t *conn,
- void *arg)
-{
- fprintf(stderr, "%s\n", __FUNCTION__);
- return EVHTP_RES_OK;
-}
-
-static int
-print_header_(evhtp_header_t * header, void * arg) {
- fprintf(stderr, "%s: %s\n", header->key, header->val);
- return 0;
-}
-
-/*! Generic callback called if no other callbacks are matched
+/*! /restconf callback
+ * @see cx_genb
*/
static void
cx_path_restconf(evhtp_request_t *req,
void *arg)
{
evhtp_connection_t *conn;
- // clicon_handle h = arg;
+ clicon_handle h = arg;
struct evbuffer *b = NULL;
- htp_method meth;
+ cvec *qvec = NULL;
+ size_t len = 0;
+ cbuf *cblen = NULL;
- fprintf(stderr, "%s\n", __FUNCTION__);
+ clicon_debug(1, "%s", __FUNCTION__);
if (req == NULL){
errno = EINVAL;
goto done;
}
- if ((conn = evhtp_request_get_connection(req)) == NULL)
- goto done;
- meth = evhtp_request_get_method(req);
- fprintf(stderr, "%s method:%d\n", __FUNCTION__, meth);
- evhtp_headers_for_each(req->headers_in, print_header_, NULL);
+ /* input debug */
+ if (clicon_debug_get())
+ evhtp_headers_for_each(req->headers_in, print_header, h);
- if ((b = evbuffer_new()) == NULL){
+ if ((cblen = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
- htp_sslutil_add_xheaders(
- req->headers_out,
- conn->ssl,
- HTP_SSLUTILS_XHDR_ALL);
+ /* Query vector, ie the ?a=x&b=y stuff */
+ if ((qvec = cvec_new(0)) ==NULL){
+ clicon_err(OE_UNIX, errno, "cvec_new");
+ goto done;
+ }
+ /* get accepted connection */
+ if ((conn = evhtp_request_get_connection(req)) == NULL){
+ clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
+ goto done;
+ }
+ /* Get all parameters from this request (resembling fcgi) */
+ if (evhtp_params_set(h, req, qvec) < 0)
+ goto done;
+
+ /* 1. create body */
+ if ((b = evbuffer_new()) == NULL){
+ clicon_err(OE_DAEMON, errno, "evbuffer_new");
+ goto done;
+ }
+ cprintf(cblen, "%lu", len);
+
+ /* 2. add headers (can mix with body) */
+ evhtp_headers_add_header(req->headers_out, evhtp_header_new("Cache-Control", "no-cache", 0, 0));
+ evhtp_headers_add_header(req->headers_out, evhtp_header_new("Content-Type", "application/xrd+xml", 0, 0));
+ evhtp_headers_add_header(req->headers_out, evhtp_header_new("Content-Length", cbuf_get(cblen), 0, 0));
+
+ /* Optional? */
+ htp_sslutil_add_xheaders(req->headers_out, conn->ssl, HTP_SSLUTILS_XHDR_ALL);
+
+ /* 3. send reply */
evhtp_send_reply_start(req, EVHTP_RES_OK);
- evbuffer_add(b, "hej\n", strlen("hej\n\n"));
evhtp_send_reply_body(req, b);
evhtp_send_reply_end(req);
- // evhtp_headers_add_header(request->headers_out, evhtp_header_new("Host", "localhost", 0, 0)); evhtp_headers_add_headers(request->headers_out, headers);
-
-
-
+ /* Clear (fcgi)paramaters */
+ if (evhtp_params_clear(h) < 0)
+ goto done;
done:
+ if (qvec)
+ cvec_free(qvec);
+ if (cblen)
+ cbuf_free(cblen);
+ if (b)
+ evhtp_safe_free(b, evbuffer_free);
return; /* void */
}
@@ -246,26 +453,27 @@ int
main(int argc,
char **argv)
{
- int retval = -1;
- char *argv0 = argv[0];
- int c;
- clicon_handle h;
- char *dir;
- int logdst = CLICON_LOG_SYSLOG;
- yang_stmt *yspec = NULL;
- char *str;
- clixon_plugin *cp = NULL;
- cvec *nsctx_global = NULL; /* Global namespace context */
- size_t cligen_buflen;
- size_t cligen_bufthreshold;
- uint16_t port = 443;
+ int retval = -1;
+ char *argv0 = argv[0];
+ int c;
+ clicon_handle h;
+ char *dir;
+ int logdst = CLICON_LOG_SYSLOG;
+ yang_stmt *yspec = NULL;
+ char *str;
+ clixon_plugin *cp = NULL;
+ cvec *nsctx_global = NULL; /* Global namespace context */
+ size_t cligen_buflen;
+ size_t cligen_bufthreshold;
+ uint16_t port = 443;
#ifdef _EVHTP_NYI
- char *stream_path;
+ char *stream_path;
#endif
evhtp_t *htp = NULL;
struct event_base *evbase = NULL;
evhtp_ssl_cfg_t *ssl_config = NULL;
struct stat f_stat;
+ int dbg = 0;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@@ -282,7 +490,7 @@ main(int argc,
usage(h, argv0);
break;
case 'D' : /* debug */
- if (sscanf(optarg, "%d", &debug) != 1)
+ if (sscanf(optarg, "%d", &dbg) != 1)
usage(h, argv0);
break;
case 'f': /* override config file */
@@ -303,9 +511,9 @@ main(int argc,
/*
* Logs, error and debug to stderr or syslog, set debug level
*/
- clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, logdst);
+ clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst);
- clicon_debug_init(debug, NULL);
+ clicon_debug_init(dbg, NULL);
clicon_log(LOG_NOTICE, "%s: %u Started", __PROGRAM__, getpid());
if (set_signal(SIGTERM, restconf_sig_term, NULL) < 0){
clicon_err(OE_DAEMON, errno, "Setting signal");
@@ -390,7 +598,6 @@ main(int argc,
}
argc -= optind;
argv += optind;
-
/* Check ssl mandatory options */
if (ssl_config->pemfile == NULL || ssl_config->privfile == NULL)
usage(h, argv0);
@@ -412,6 +619,53 @@ main(int argc,
/* Access the remaining argv/argc options (after --) w clicon-argv_get() */
clicon_argv_set(h, argv0, argc, argv);
+ /* Init evhtp */
+ if ((evbase = event_base_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "event_base_new");
+ goto done;
+ }
+ /* create a new evhtp_t instance */
+ if ((htp = evhtp_new(evbase, NULL)) == NULL){
+ clicon_err(OE_UNIX, errno, "evhtp_new");
+ goto done;
+ }
+ if (evhtp_ssl_init(htp, ssl_config) < 0){
+ clicon_err(OE_UNIX, errno, "evhtp_new");
+ goto done;
+ }
+#ifndef EVHTP_DISABLE_EVTHR
+ evhtp_use_threads_wexit(htp, NULL, NULL, 4, NULL);
+#endif
+
+ /* Callback before the connection is accepted. */
+ evhtp_set_pre_accept_cb(htp, cx_pre_accept, h);
+
+ /* Callback right after a connection is accepted. */
+ evhtp_set_post_accept_cb(htp, cx_post_accept, h);
+
+ /* Callback to be executed for all /restconf api calls */
+ if (evhtp_set_cb(htp, "/" RESTCONF_API, cx_path_restconf, h) == NULL){
+ clicon_err(OE_EVENTS, errno, "evhtp_set_cb");
+ goto done;
+ }
+ /* Callback to be executed for all /restconf api calls */
+ if (evhtp_set_cb(htp, RESTCONF_WELL_KNOWN, cx_path_wellknown, h) == NULL){
+ clicon_err(OE_EVENTS, errno, "evhtp_set_cb");
+ goto done;
+ }
+ /* Generic callback called if no other callbacks are matched */
+ evhtp_set_gencb(htp, cx_gencb, h);
+
+ /* bind to a socket, optionally with specific protocol support formatting
+ * If port is proteced must be done as root?
+ */
+ if (evhtp_bind_socket(htp, "127.0.0.1", port, 128) < 0){
+ clicon_err(OE_UNIX, errno, "evhtp_bind_socket");
+ goto done;
+ }
+ if (restconf_drop_privileges(h, WWWUSER) < 0)
+ goto done;
+
/* Init cligen buffers */
cligen_buflen = clicon_option_int(h, "CLICON_CLI_BUF_START");
cligen_bufthreshold = clicon_option_int(h, "CLICON_CLI_BUF_THRESHOLD");
@@ -422,7 +676,6 @@ main(int argc,
*/
if (netconf_module_features(h) < 0)
goto done;
-
/* Create top-level yang spec and store as option */
if ((yspec = yspec_new()) == NULL)
goto done;
@@ -491,8 +744,8 @@ main(int argc,
goto done;
/* Dump configuration options on debug */
- if (debug)
- clicon_option_dump(h, debug);
+ if (dbg)
+ clicon_option_dump(h, dbg);
/* Call start function in all plugins before we go interactive
*/
@@ -503,40 +756,6 @@ main(int argc,
if (clicon_options_main(h) < 0)
goto done;
- /* Init evhtp */
- if ((evbase = event_base_new()) == NULL){
- clicon_err(OE_UNIX, errno, "event_base_new");
- goto done;
- }
- /* create a new evhtp_t instance */
- if ((htp = evhtp_new(evbase, NULL)) == NULL){
- clicon_err(OE_UNIX, errno, "evhtp_new");
- goto done;
- }
- if (evhtp_ssl_init(htp, ssl_config) < 0){
- clicon_err(OE_UNIX, errno, "evhtp_new");
- goto done;
- }
- /* Generic callback called if no other callbacks are matched */
- evhtp_set_gencb(htp, cx_gencb, h);
-
- /* Callback before the connection is accepted. */
- evhtp_set_pre_accept_cb(htp, cx_pre_accept, h);
-
- /* Callback right after a connection is accepted. */
- evhtp_set_post_accept_cb(htp, cx_post_accept, h);
-
- /* Callback to be executed on a specific path */
- if (evhtp_set_cb(htp, "/" RESTCONF_API, cx_path_restconf, h) == NULL){
- clicon_err(OE_EVENTS, errno, "evhtp_set_cb");
- goto done;
- }
-
- /* bind to a socket, optionally with specific protocol support formatting */
- if (evhtp_bind_socket(htp, "127.0.0.1", port, 128) < 0){
- clicon_err(OE_UNIX, errno, "evhtp_bind_socket");
- goto done;
- }
event_base_loop(evbase, 0);
diff --git a/apps/restconf/restconf_fcgi_lib.c b/apps/restconf/restconf_fcgi_lib.c
index 1ad9678c..34ae8c0c 100644
--- a/apps/restconf/restconf_fcgi_lib.c
+++ b/apps/restconf/restconf_fcgi_lib.c
@@ -65,166 +65,166 @@
#include "restconf_fcgi_lib.h"
/*! HTTP error 400
- * @param[in] r Fastcgi request handle
+ * @param[in] req Fastcgi request handle
*/
int
restconf_badrequest(clicon_handle h,
- FCGX_Request *r)
+ FCGX_Request *req)
{
char *path;
path = clixon_restconf_param_get(h, "REQUEST_URI");
- FCGX_SetExitStatus(400, r->out);
- FCGX_FPrintF(r->out, "Status: 400 Bad Request\r\n"); /* 400 bad request */
- FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
- FCGX_FPrintF(r->out, "
Clixon Bad request/h1>\n");
- FCGX_FPrintF(r->out, "The requested URL %s or data is in some way badly formed.\n",
+ FCGX_SetExitStatus(400, req->out);
+ FCGX_FPrintF(req->out, "Status: 400 Bad Request\r\n"); /* 400 bad request */
+ FCGX_FPrintF(req->out, "Content-Type: text/html\r\n\r\n");
+ FCGX_FPrintF(req->out, "
\n");
+ FCGX_FPrintF(req->out, "The requested URL %s was forbidden.\n", path);
return 0;
}
/*! HTTP error 404
- * @param[in] r Fastcgi request handle
+ * @param[in] req Fastcgi request handle
*/
int
restconf_notfound(clicon_handle h,
- FCGX_Request *r)
+ FCGX_Request *req)
{
char *path;
path = clixon_restconf_param_get(h, "REQUEST_URI");
- FCGX_SetExitStatus(404, r->out);
- FCGX_FPrintF(r->out, "Status: 404 Not Found\r\n"); /* 404 not found */
- FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
- FCGX_FPrintF(r->out, "
Not Found
\n");
- FCGX_FPrintF(r->out, "Not Found\n");
- FCGX_FPrintF(r->out, "The requested URL %s was not found on this server.\n",
+ FCGX_SetExitStatus(404, req->out);
+ FCGX_FPrintF(req->out, "Status: 404 Not Found\r\n"); /* 404 not found */
+ FCGX_FPrintF(req->out, "Content-Type: text/html\r\n\r\n");
+ FCGX_FPrintF(req->out, "
Not Found
\n");
+ FCGX_FPrintF(req->out, "Not Found\n");
+ FCGX_FPrintF(req->out, "The requested URL %s was not found on this server.\n",
path);
return 0;
}
/*! HTTP error 406 Not acceptable
- * @param[in] r Fastcgi request handle
+ * @param[in] req Fastcgi request handle
*/
int
restconf_notacceptable(clicon_handle h,
- FCGX_Request *r)
+ FCGX_Request *req)
{
char *path;
path = clixon_restconf_param_get(h, "REQUEST_URI");
- FCGX_SetExitStatus(406, r->out);
- FCGX_FPrintF(r->out, "Status: 406 Not Acceptable\r\n"); /* 406 not acceptible */
+ FCGX_SetExitStatus(406, req->out);
+ FCGX_FPrintF(req->out, "Status: 406 Not Acceptable\r\n"); /* 406 not acceptible */
- FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
- FCGX_FPrintF(r->out, "
Not Acceptable
\n");
- FCGX_FPrintF(r->out, "Not Acceptable\n");
- FCGX_FPrintF(r->out, "The target resource does not have a current representation that would be acceptable to the user agent.\n",
+ FCGX_FPrintF(req->out, "Content-Type: text/html\r\n\r\n");
+ FCGX_FPrintF(req->out, "
Not Acceptable
\n");
+ FCGX_FPrintF(req->out, "Not Acceptable\n");
+ FCGX_FPrintF(req->out, "The target resource does not have a current representation that would be acceptable to the user agent.\n",
path);
return 0;
}
/*! HTTP error 409
- * @param[in] r Fastcgi request handle
+ * @param[in] req Fastcgi request handle
*/
int
-restconf_conflict(FCGX_Request *r)
+restconf_conflict(FCGX_Request *req)
{
- FCGX_SetExitStatus(409, r->out);
- FCGX_FPrintF(r->out, "Status: 409 Conflict\r\n"); /* 409 Conflict */
- FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
- FCGX_FPrintF(r->out, "