From c1c1670a7443f8c34302d974fea31b39fe2224bb Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 20 Mar 2016 22:25:42 +0100 Subject: [PATCH] getting legacy grideye application to work on clixon --- apps/backend/backend_commit.c | 6 +- apps/backend/clixon_backend_handle.c | 12 +- apps/backend/clixon_backend_transaction.c | 6 +- apps/cli/Makefile.in | 2 +- apps/cli/cli_common.c | 585 +----------------- apps/cli/cli_generate.c | 4 +- apps/cli/cli_show.c | 692 ++++++++++++++++++++++ apps/cli/clixon_cli_api.h | 16 +- apps/netconf/netconf_filter.c | 4 +- apps/netconf/netconf_rpc.c | 2 +- lib/clixon/clixon_xml.h | 17 +- lib/src/clixon_options.c | 6 +- lib/src/clixon_proc.c | 94 +-- lib/src/clixon_proto.c | 4 +- lib/src/clixon_proto_client.c | 8 +- lib/src/clixon_qdb.c | 43 +- lib/src/clixon_xml.c | 196 ++++-- lib/src/clixon_xml_db.c | 27 +- lib/src/clixon_xml_db_rpc.c | 12 +- lib/src/clixon_xml_map.c | 4 +- lib/src/clixon_xsl.c | 12 +- lib/src/clixon_yang_type.c | 5 +- 22 files changed, 952 insertions(+), 805 deletions(-) create mode 100644 apps/cli/cli_show.c diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 874ec1ad..7eb6607c 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -154,7 +154,7 @@ candidate_commit(clicon_handle h, &td->td_avec, /* added: only in candidate */ &td->td_alen, &td->td_scvec, /* changed: original values */ - &td->td_tcvec, /* changed: wanted values */ + &td->td_tcvec, /* changed: wanted values */ &td->td_clen) < 0) goto done; if (debug) @@ -164,15 +164,17 @@ candidate_commit(clicon_handle h, xn = td->td_dvec[i]; xml_flag_set(xn, XML_FLAG_DEL); xml_apply(xn, CX_ELMNT, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_DEL); + xml_apply_ancestor(xn, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE); } for (i=0; itd_alen; i++){ /* Also down */ xn = td->td_avec[i]; xml_flag_set(xn, XML_FLAG_ADD); xml_apply(xn, CX_ELMNT, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_ADD); + xml_apply_ancestor(xn, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE); } for (i=0; itd_clen; i++){ /* Also up */ xn = td->td_scvec[i]; - xml_flag(xn, XML_FLAG_CHANGE); + xml_flag_set(xn, XML_FLAG_CHANGE); xml_apply_ancestor(xn, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE); xn = td->td_tcvec[i]; xml_flag_set(xn, XML_FLAG_CHANGE); diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c index de034e88..feabe6ba 100644 --- a/apps/backend/clixon_backend_handle.c +++ b/apps/backend/clixon_backend_handle.c @@ -108,7 +108,10 @@ backend_handle_exit(clicon_handle h) * @see also backend_notify_xml() */ int -backend_notify(clicon_handle h, char *stream, int level, char *event) +backend_notify(clicon_handle h, + char *stream, + int level, + char *event) { struct client_entry *ce; struct client_subscription *su; @@ -144,7 +147,7 @@ backend_notify(clicon_handle h, char *stream, int level, char *event) * @param[in] h Clicon handle * @param[in] stream Name of event stream. CLICON is predefined as LOG stream * @param[in] level Event level (not used yet) - * @param[in] event Actual message as xml tree + * @param[in] x Actual message as xml tree * * Stream is a string used to qualify the event-stream. Distribute the * event to all clients registered to this backend. @@ -153,7 +156,10 @@ backend_notify(clicon_handle h, char *stream, int level, char *event) * @see also backend_notify() */ int -backend_notify_xml(clicon_handle h, char *stream, int level, cxobj *x) +backend_notify_xml(clicon_handle h, + char *stream, + int level, + cxobj *x) { struct client_entry *ce; struct client_subscription *su; diff --git a/apps/backend/clixon_backend_transaction.c b/apps/backend/clixon_backend_transaction.c index c85188b4..48ee54be 100644 --- a/apps/backend/clixon_backend_transaction.c +++ b/apps/backend/clixon_backend_transaction.c @@ -62,7 +62,7 @@ transaction_id(transaction_data td) return ((transaction_data_t *)td)->td_id; } -/*! Get plugin/application specific callbackargument +/*! Get plugin/application specific callback argument * @param[in] td transaction_data * @retval arg callback argument * @note NYI @@ -73,7 +73,7 @@ transaction_arg(transaction_data td) return ((transaction_data_t *)td)->td_arg; } -/*! Get Source database xml tree +/*! Get source database xml tree * @param[in] td transaction_data * @retval src source xml tree containing original state */ @@ -160,7 +160,7 @@ transaction_scvec(transaction_data td) cxobj ** transaction_tcvec(transaction_data td) { - return ((transaction_data_t *)td)->td_dvec; + return ((transaction_data_t *)td)->td_tcvec; } /*! Get length of changed xml vector diff --git a/apps/cli/Makefile.in b/apps/cli/Makefile.in index 5e7a2d0f..00f90a1d 100644 --- a/apps/cli/Makefile.in +++ b/apps/cli/Makefile.in @@ -62,7 +62,7 @@ MYLIBLINK = lib$(MYNAME)$(SH_SUFFIX) MYLIB = $(MYLIBLINK).$(CLIXON_MAJOR).$(CLIXON_MINOR) MYLIBSO = $(MYLIBLINK).$(CLIXON_MAJOR) -LIBSRC = cli_plugin.c cli_common.c cli_handle.c cli_generate.c +LIBSRC = cli_plugin.c cli_common.c cli_show.c cli_handle.c cli_generate.c LIBOBJS = $(LIBSRC:.c=.o) all: $(MYLIB) $(APPL) test diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index f07b4049..392a7762 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -61,9 +61,6 @@ #include "cli_common.h" -static int xml2csv(FILE *f, cxobj *x, cvec *cvv); -//static int xml2csv_raw(FILE *f, cxobj *x); - /*! Initialize candidate database * We have implemented these: * shared - all users share a common candidate db @@ -334,229 +331,6 @@ cli_validate(clicon_handle h, cvec *vars, cg_var *arg) return retval; } -/*! Completion callback primarily intended for automatically generated data model - * - * Returns an expand-type list of commands as used by cligen 'expand' - * functionality. - * arg is a string: " ". - * is either running or candidate - * matches a set of database keys following clicon_dbspec. - * Eg a[].b[] $!x $!y - * the last being the variable to expand for. - * Example: - * dbspec is a[].b[] $!x $!y - * clispec is a b (|; - * db contains entries: - * a.0 $x=5 - * a.1 $x=10 - * a.0.b.0 $x=5 $y=12 - * a.0.b.1 $x=5 $y=20 - * a.1.b.0 $x=10 $y=99 - * - * The user types a 5 b which produces the following output: - * - * 12 - * 20 - * - * Assume callback given in a cligen spec: a " - * @param[out] len len of return commands & helptxt - * @param[out] commands vector of function pointers to callback functions - * @param[out] helptxt vector of pointers to helptexts - * @see cli_expand_var_generate This is where arg is generated - */ -int -expand_dbvar_dbxml(void *h, - char *name, - cvec *cvv, - cg_var *arg, - int *nr, - char ***commands, - char ***helptexts) -{ - int nvec; - char **vec = NULL; - int retval = -1; - char *xkfmt; - char *str; - char *dbstr; - cxobj *xt = NULL; - char *xk = NULL; - cxobj **xvec = NULL; - size_t xlen = 0; - cxobj *x; - char *bodystr; - int i; - int i0; - - if (arg == NULL || (str = cv_string_get(arg)) == NULL){ - clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); - goto done; - } - /* In the example, str = "candidate a[].b[] $!x $!y" */ - if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){ - clicon_err(OE_PLUGIN, errno, "clicon_strsplit"); - goto done; - } - dbstr = vec[0]; - if (strcmp(dbstr, "running") != 0 && - strcmp(dbstr, "candidate") != 0){ - clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr); - goto done; - } - xkfmt = vec[1]; - /* xkfmt = /interface/%s/address/%s - --> ^/interface/eth0/address/.*$ - --> /interface/[name=eth0]/address - */ - if (xmlkeyfmt2xpath(xkfmt, cvv, &xk) < 0) - goto done; - if (xmldb_get(h, dbstr, xk, 1, &xt, &xvec, &xlen) < 0) - goto done; - i0 = *nr; - *nr += xlen; - if ((*commands = realloc(*commands, sizeof(char *) * (*nr))) == NULL) { - clicon_err(OE_UNIX, errno, "realloc: %s", strerror (errno)); - goto done; - } - for (i = 0; i < xlen; i++) { - x = xvec[i]; - if ((bodystr = xml_body(x)) == NULL){ - clicon_err(OE_CFG, 0, "No xml body"); - goto done; - } - (*commands)[i0+i] = strdup(bodystr); - } - retval = 0; - done: - unchunk_group(__FUNCTION__); - if (xvec) - free(xvec); - if (xt) - xml_free(xt); - if (xk) - free(xk); - return retval; - -} - -/* - * expand_dir - * List files in a directory - */ -int -expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail) -{ - DIR *dirp; - struct dirent *dp; - struct stat st; - char *str; - char *cmd; - int len; - int retval = -1; - struct passwd *pw; - char filename[MAXPATHLEN]; - - if ((dirp = opendir(dir)) == 0){ - fprintf(stderr, "expand_dir: opendir(%s) %s\n", - dir, strerror(errno)); - return -1; - } - *nr = 0; - while ((dp = readdir(dirp)) != NULL) { - if ( -#if 0 - strcmp(dp->d_name, ".") != 0 && - strcmp(dp->d_name, "..") != 0 -#else - dp->d_name[0] != '.' -#endif - ) { - snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp->d_name); - if (lstat(filename, &st) == 0){ - if ((st.st_mode & flags) == 0) - continue; - -#if EXPAND_RECURSIVE - if (S_ISDIR(st.st_mode)) { - int nrsav = *nr; - if(expand_dir(filename, nr, commands, detail) < 0) - goto quit; - while(nrsav < *nr) { - len = strlen(dp->d_name) + strlen((*commands)[nrsav]) + 2; - if((str = malloc(len)) == NULL) { - fprintf(stderr, "expand_dir: malloc: %s\n", - strerror(errno)); - goto quit; - } - snprintf(str, len-1, "%s/%s", - dp->d_name, (*commands)[nrsav]); - free((*commands)[nrsav]); - (*commands)[nrsav] = str; - - nrsav++; - } - continue; - } -#endif - if ((cmd = strdup(dp->d_name)) == NULL) { - fprintf(stderr, "expand_dir: strdup: %s\n", - strerror(errno)); - goto quit; - } - if (0 &&detail){ - if ((pw = getpwuid(st.st_uid)) == NULL){ - fprintf(stderr, "expand_dir: getpwuid(%d): %s\n", - st.st_uid, strerror(errno)); - goto quit; - } - len = strlen(cmd) + - strlen(pw->pw_name) + -#ifdef __FreeBSD__ - strlen(ctime(&st.st_mtimespec.tv_sec)) + -#else - strlen(ctime(&st.st_mtim.tv_sec)) + -#endif - - strlen("{ by }") + 1 /* \0 */; - if ((str=realloc(cmd, strlen(cmd)+len)) == NULL) { - fprintf(stderr, "expand_dir: malloc: %s\n", - strerror(errno)); - goto quit; - } - snprintf(str + strlen(dp->d_name), - len - strlen(dp->d_name), - "{%s by %s}", -#ifdef __FreeBSD__ - ctime(&st.st_mtimespec.tv_sec), -#else - ctime(&st.st_mtim.tv_sec), -#endif - - pw->pw_name - ); - cmd = str; - } - if (((*commands) = - realloc(*commands, ((*nr)+1)*sizeof(char**))) == NULL){ - perror("expand_dir: realloc"); - goto quit; - } - (*commands)[(*nr)] = cmd; - (*nr)++; - if (*nr >= 128) /* Limit number of options */ - break; - } - } - } - retval = 0; - quit: - closedir(dirp); - return retval; -} /*! Compare two dbs using XML. Write to file and run diff */ @@ -949,250 +723,6 @@ discard_changes(clicon_handle h, cvec *cvv, cg_var *arg) return clicon_rpc_copy(h, "running", "candidate"); } -/*! Generic function for showing configurations. - * the callback differs. - * @param[in] h CLICON handle - * @param[in] cvv Vector of variables (not needed) - * @param[in] arg A string: - * is either running or candidate - * xpath expression as in nertconf get-config - * @param fn - * @param fnarg - * @code - * # cligen spec - * show config id , show_conf_as("running interfaces/interface[name=eth*]"); - * @endcode - */ -static int -show_conf_xmldb_as(clicon_handle h, - cvec *cvv, - cg_var *arg, - cxobj **xt) /* top xml */ -{ - int retval = -1; - char *db; - char **vec = NULL; - int nvec; - char *str; - char *xpath; - - if (arg == NULL || (str = cv_string_get(arg)) == NULL){ - clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); - goto done; - } - if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){ - clicon_err(OE_PLUGIN, errno, "clicon_strsplit"); - goto done; - } - if (nvec != 2){ - clicon_err(OE_PLUGIN, 0, "format error \"%s\" - expected ", str); - goto done; - } - /* Dont get attr here, take it from arg instead */ - db = vec[0]; - if (strcmp(db, "running") != 0 && strcmp(db, "candidate") != 0) { - clicon_err(OE_PLUGIN, 0, "No such db name: %s", db); - goto done; - } - xpath = vec[1]; - if (xmldb_get(h, db, xpath, 0, xt, NULL, NULL) < 0) - goto done; - retval = 0; -done: - unchunk_group(__FUNCTION__); - return retval; -} - - -/*! Show a configuration database on stdout using XML format - * Utility function used by cligen spec file - */ -static int -show_conf_as_xml1(clicon_handle h, cvec *cvv, cg_var *arg, int netconf) -{ - cxobj *xt = NULL; - cxobj *xc; - int retval = -1; - - if (show_conf_xmldb_as(h, cvv, arg, &xt) < 0) - goto done; - if (netconf) /* netconf prefix */ - fprintf(stdout, "\n"); - xc = NULL; /* Dont print xt itself */ - while ((xc = xml_child_each(xt, xc, -1)) != NULL) - clicon_xml2file(stdout, xc, netconf?2:0, 1); - if (netconf) /* netconf postfix */ - fprintf(stdout, "]]>]]>\n"); - retval = 0; - done: - if (xt) - xml_free(xt); - return retval; - -} - -/*! Show configuration as prettyprinted xml - * Utility function used by cligen spec file - */ -int -show_conf_as_xml(clicon_handle h, cvec *cvv, cg_var *arg) -{ - return show_conf_as_xml1(h, cvv, arg, 0); -} - -/*! Show configuration as prettyprinted xml with netconf hdr/tail - * Utility function used by cligen spec file - */ -int -show_conf_as_netconf(clicon_handle h, cvec *cvv, cg_var *arg) -{ - return show_conf_as_xml1(h, cvv, arg, 1); -} - -/*! Show configuration as JSON - * Utility function used by cligen spec file - */ -int -show_conf_as_json(clicon_handle h, cvec *cvv, cg_var *arg) -{ - cxobj *xt = NULL; - cxobj *xc; - int retval = -1; - - if (show_conf_xmldb_as(h, cvv, arg, &xt) < 0) - goto done; - xc = NULL; /* Dont print xt itself */ - while ((xc = xml_child_each(xt, xc, -1)) != NULL) - xml2json(stdout, xc, 1); - retval = 0; - done: - if (xt) - xml_free(xt); - return retval; -} - -int -show_conf_xpath(clicon_handle h, cvec *cvv, cg_var *arg) -{ - int retval = -1; - char *str; - char *xpath; - cg_var *cv; - cxobj *xt = NULL; - cxobj **xv = NULL; - size_t xlen; - int i; - - if (arg == NULL || (str = cv_string_get(arg)) == NULL){ - clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); - goto done; - } - /* Dont get attr here, take it from arg instead */ - if (strcmp(str, "running") != 0 && strcmp(str, "candidate") != 0){ - clicon_err(OE_PLUGIN, 0, "No such db name: %s", str); - goto done; - } - cv = cvec_find_var(cvv, "xpath"); - xpath = cv_string_get(cv); - if (xmldb_get(h, str, xpath, 1, &xt, &xv, &xlen) < 0) - goto done; - for (i=0; i CSV commands - * Can only be made in a 'flat tree', ie on the form: - * B --> - * Type, A - * X, B - * @param[in] f Output file - * @param[in] x XML tree - * @param[in] cvv A vector of field names present in XML - * This means that only fields in x that are listed in cvv will be printed. - */ -static int -xml2csv(FILE *f, cxobj *x, cvec *cvv) -{ - cxobj *xe, *xb; - int retval = -1; - cg_var *vs; - - fprintf(f, "%s", xml_name(x)); - xe = NULL; - - vs = NULL; - while ((vs = cvec_each(cvv, vs))) { - if ((xe = xml_find(x, cv_name_get(vs))) == NULL){ - fprintf(f, ";"); - continue; - } - if (xml_child_nr(xe)){ - xb = xml_child_i(xe, 0); - fprintf(f, ";%s", xml_value(xb)); - } - } - fprintf(f, "\n"); - retval = 0; - return retval; -} - - -static int -show_conf_as_csv1(clicon_handle h, cvec *cvv0, cg_var *arg) -{ - cxobj *xt = NULL; - cxobj *xc; - int retval = -1; - cvec *cvv=NULL; - char *str; - - if (show_conf_xmldb_as(h, cvv0, arg, &xt) < 0) - goto done; - xc = NULL; /* Dont print xt itself */ - while ((xc = xml_child_each(xt, xc, -1)) != NULL){ - if ((str = chunk_sprintf(__FUNCTION__, "%s[]", xml_name(xc))) == NULL) - goto done; -#ifdef NOTYET /* yang-spec? */ - if (ds==NULL && (ds = key2spec_key(dbspec, str)) != NULL){ - cg_var *vs; - fprintf(stdout, "Type"); - cvv = db_spec2cvec(ds); - vs = NULL; - while ((vs = cvec_each(cvv, vs))) - fprintf(stdout, ";%s", cv_name_get(vs)); - fprintf(stdout, "\n"); - } /* Now values just need to follow,... */ -#endif /* yang-spec? */ - if (cvv== NULL) - goto done; - xml2csv(stdout, xc, cvv); /* csv syntax */ - } - retval = 0; - done: - if (xt) - xml_free(xt); - unchunk_group(__FUNCTION__); - return retval; -} - -int -show_conf_as_csv(clicon_handle h, cvec *cvv, cg_var *arg) -{ - return show_conf_as_csv1(h, cvv, arg); -} diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c index d532df73..cebd7731 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -54,7 +54,7 @@ #define GENERATE_CALLBACK "cli_set" /* variable expand function */ -#define GENERATE_EXPAND_XMLDB "expand_dbvar_dbxml" +#define GENERATE_EXPAND_XMLDB "expand_dbvar" /*===================================================================== * YANG generate CLI @@ -163,7 +163,7 @@ cvtype_max2str_dup(enum cv_type type) * @param[in] ys yang_stmt of the node at hand * @param[in] cvtype Type of the cligen variable * @param[in] cb0 The string where the result format string is inserted. - * @see expand_dbvar_dbxml This is where the expand string is used + * @see expand_dbvar This is where the expand string is used */ static int cli_expand_var_generate(clicon_handle h, diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c new file mode 100644 index 00000000..adf068c5 --- /dev/null +++ b/apps/cli/cli_show.c @@ -0,0 +1,692 @@ +/* + * + Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON. + + CLIXON is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + CLIXON is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CLIXON; see the file LICENSE. If not, see + . + + * + */ + +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef HAVE_CRYPT_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* cligen */ +#include + +/* clicon */ +#include + +/* Exported functions in this file are in clixon_cli_api.h */ +#include "clixon_cli_api.h" + +static int xml2csv(FILE *f, cxobj *x, cvec *cvv); +//static int xml2csv_raw(FILE *f, cxobj *x); + +/*! Completion callback intended for automatically generated data model + * + * Returns an expand-type list of commands as used by cligen 'expand' + * functionality. + * + * Assume callback given in a cligen spec: a " + * @param[out] len len of return commands & helptxt + * @param[out] commands vector of function pointers to callback functions + * @param[out] helptxt vector of pointers to helptexts + * @see cli_expand_var_generate This is where arg is generated + */ +int +expand_dbvar(void *h, + char *name, + cvec *cvv, + cg_var *arg, + int *nr, + char ***commands, + char ***helptexts) +{ + int nvec; + char **vec = NULL; + int retval = -1; + char *xkfmt; + char *str; + char *dbstr; + cxobj *xt = NULL; + char *xk = NULL; + cxobj **xvec = NULL; + size_t xlen = 0; + cxobj *x; + char *bodystr; + int i; + int i0; + + if (arg == NULL || (str = cv_string_get(arg)) == NULL){ + clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); + goto done; + } + /* In the example, str = "candidate a[].b[] $!x $!y" */ + if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){ + clicon_err(OE_PLUGIN, errno, "clicon_strsplit"); + goto done; + } + dbstr = vec[0]; + if (strcmp(dbstr, "running") != 0 && + strcmp(dbstr, "candidate") != 0){ + clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr); + goto done; + } + xkfmt = vec[1]; + /* xkfmt = /interface/%s/address/%s + --> ^/interface/eth0/address/.*$ + --> /interface/[name=eth0]/address + */ + if (xmlkeyfmt2xpath(xkfmt, cvv, &xk) < 0) + goto done; + if (xmldb_get(h, dbstr, xk, 1, &xt, &xvec, &xlen) < 0) + goto done; + i0 = *nr; + *nr += xlen; + if ((*commands = realloc(*commands, sizeof(char *) * (*nr))) == NULL) { + clicon_err(OE_UNIX, errno, "realloc: %s", strerror (errno)); + goto done; + } + for (i = 0; i < xlen; i++) { + x = xvec[i]; + if ((bodystr = xml_body(x)) == NULL){ + clicon_err(OE_CFG, 0, "No xml body"); + goto done; + } + (*commands)[i0+i] = strdup(bodystr); + } + retval = 0; + done: + unchunk_group(__FUNCTION__); + if (xvec) + free(xvec); + if (xt) + xml_free(xt); + if (xk) + free(xk); + return retval; + +} + +/*! List files in a directory + */ +int +expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail) +{ + DIR *dirp; + struct dirent *dp; + struct stat st; + char *str; + char *cmd; + int len; + int retval = -1; + struct passwd *pw; + char filename[MAXPATHLEN]; + + if ((dirp = opendir(dir)) == 0){ + fprintf(stderr, "expand_dir: opendir(%s) %s\n", + dir, strerror(errno)); + return -1; + } + *nr = 0; + while ((dp = readdir(dirp)) != NULL) { + if ( +#if 0 + strcmp(dp->d_name, ".") != 0 && + strcmp(dp->d_name, "..") != 0 +#else + dp->d_name[0] != '.' +#endif + ) { + snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp->d_name); + if (lstat(filename, &st) == 0){ + if ((st.st_mode & flags) == 0) + continue; + +#if EXPAND_RECURSIVE + if (S_ISDIR(st.st_mode)) { + int nrsav = *nr; + if(expand_dir(filename, nr, commands, detail) < 0) + goto quit; + while(nrsav < *nr) { + len = strlen(dp->d_name) + strlen((*commands)[nrsav]) + 2; + if((str = malloc(len)) == NULL) { + fprintf(stderr, "expand_dir: malloc: %s\n", + strerror(errno)); + goto quit; + } + snprintf(str, len-1, "%s/%s", + dp->d_name, (*commands)[nrsav]); + free((*commands)[nrsav]); + (*commands)[nrsav] = str; + + nrsav++; + } + continue; + } +#endif + if ((cmd = strdup(dp->d_name)) == NULL) { + fprintf(stderr, "expand_dir: strdup: %s\n", + strerror(errno)); + goto quit; + } + if (0 &&detail){ + if ((pw = getpwuid(st.st_uid)) == NULL){ + fprintf(stderr, "expand_dir: getpwuid(%d): %s\n", + st.st_uid, strerror(errno)); + goto quit; + } + len = strlen(cmd) + + strlen(pw->pw_name) + +#ifdef __FreeBSD__ + strlen(ctime(&st.st_mtimespec.tv_sec)) + +#else + strlen(ctime(&st.st_mtim.tv_sec)) + +#endif + + strlen("{ by }") + 1 /* \0 */; + if ((str=realloc(cmd, strlen(cmd)+len)) == NULL) { + fprintf(stderr, "expand_dir: malloc: %s\n", + strerror(errno)); + goto quit; + } + snprintf(str + strlen(dp->d_name), + len - strlen(dp->d_name), + "{%s by %s}", +#ifdef __FreeBSD__ + ctime(&st.st_mtimespec.tv_sec), +#else + ctime(&st.st_mtim.tv_sec), +#endif + + pw->pw_name + ); + cmd = str; + } + if (((*commands) = + realloc(*commands, ((*nr)+1)*sizeof(char**))) == NULL){ + perror("expand_dir: realloc"); + goto quit; + } + (*commands)[(*nr)] = cmd; + (*nr)++; + if (*nr >= 128) /* Limit number of options */ + break; + } + } + } + retval = 0; + quit: + closedir(dirp); + return retval; +} + +/*! Generic function for showing configurations. + * Utility function used by cligen spec file + * @param[in] h CLICON handle + * @param[in] cvv Vector of variables from CLIgen command-line + * @param[in] arg A string: [] + * @param[out] xt Configuration as xml tree. + * Format of arg: + * "running", "candidate" + * xpath expression + * optional name of variable in cvv. If set, xpath must have a '%s' + * @code + * show config id , show_conf_as("running interfaces/interface[name=%s] n"); + * @endcode + */ +static int +show_conf_as(clicon_handle h, + cvec *cvv, + cg_var *arg, + cxobj **xt) /* top xml */ +{ + int retval = -1; + char *db; + char **vec = NULL; + int nvec; + char *str; + char *xpath; + char *attr = NULL; + cbuf *cbx = NULL; + int i; + int j; + cg_var *cvattr; + char *val = NULL; + + if (arg == NULL || (str = cv_string_get(arg)) == NULL){ + clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); + goto done; + } + if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){ + clicon_err(OE_PLUGIN, errno, "clicon_strsplit"); + goto done; + } + if (nvec != 2 && nvec != 3){ + clicon_err(OE_PLUGIN, 0, "format error \"%s\" - expected []", str); + goto done; + } + /* Dont get attr here, take it from arg instead */ + db = vec[0]; + if (strcmp(db, "running") != 0 && strcmp(db, "candidate") != 0) { + clicon_err(OE_PLUGIN, 0, "No such db name: %s", db); + goto done; + } + xpath = vec[1]; + if ((cbx = cbuf_new()) == NULL){ + clicon_err(OE_PLUGIN, errno, "cbuf_new"); + goto done; + } + if (nvec == 3){ + attr = vec[2]; + j = 0; + for (i=0; i [] + * @param[in] netconf If set print as netconf edit-config, otherwise just xml + * @see show_conf_as the main function + */ +static int +show_conf_as_xml1(clicon_handle h, + cvec *cvv, + cg_var *arg, + int netconf) +{ + cxobj *xt = NULL; + cxobj *xc; + int retval = -1; + + if (show_conf_as(h, cvv, arg, &xt) < 0) + goto done; + if (netconf) /* netconf prefix */ + fprintf(stdout, "\n"); + xc = NULL; /* Dont print xt itself */ + while ((xc = xml_child_each(xt, xc, -1)) != NULL) + clicon_xml2file(stdout, xc, netconf?2:0, 1); + if (netconf) /* netconf postfix */ + fprintf(stdout, "]]>]]>\n"); + retval = 0; + done: + if (xt) + xml_free(xt); + return retval; + +} + +/*! Show configuration as prettyprinted xml + * Utility function used by cligen spec file + * @param[in] h CLICON handle + * @param[in] cvv Vector of variables from CLIgen command-line + * @param[in] arg A string: [] + * @see show_conf_as the main function + */ +int +show_conf_as_xml(clicon_handle h, + cvec *cvv, + cg_var *arg) +{ + return show_conf_as_xml1(h, cvv, arg, 0); +} + +/*! Show configuration as prettyprinted xml with netconf hdr/tail + * Utility function used by cligen spec file + * @param[in] h CLICON handle + * @param[in] cvv Vector of variables from CLIgen command-line + * @param[in] arg A string: [] + * @see show_conf_as the main function + */ +int +show_conf_as_netconf(clicon_handle h, + cvec *cvv, + cg_var *arg) +{ + return show_conf_as_xml1(h, cvv, arg, 1); +} + +/*! Show configuration as JSON + * Utility function used by cligen spec file + * @param[in] h CLICON handle + * @param[in] cvv Vector of variables from CLIgen command-line + * @param[in] arg A string: [] + * @see show_conf_as the main function + */ +int +show_conf_as_json(clicon_handle h, + cvec *cvv, + cg_var *arg) +{ + cxobj *xt = NULL; + cxobj *xc; + int retval = -1; + + if (show_conf_as(h, cvv, arg, &xt) < 0) + goto done; + xc = NULL; /* Dont print xt itself */ + while ((xc = xml_child_each(xt, xc, -1)) != NULL) + xml2json(stdout, xc, 1); + retval = 0; + done: + if (xt) + xml_free(xt); + return retval; +} + +/*! Show configuration as text givebn an xpath + * Utility function used by cligen spec file + * @param[in] h CLICON handle + * @param[in] cvv Vector of variables from CLIgen command-line + * @param[in] arg A string: + * @note Hardcoded that a variable in cvv is named "xpath" + */ +int +show_conf_xpath(clicon_handle h, + cvec *cvv, + cg_var *arg) +{ + int retval = -1; + char *str; + char *xpath; + cg_var *cv; + cxobj *xt = NULL; + cxobj **xv = NULL; + size_t xlen; + int i; + + if (arg == NULL || (str = cv_string_get(arg)) == NULL){ + clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); + goto done; + } + /* Dont get attr here, take it from arg instead */ + if (strcmp(str, "running") != 0 && strcmp(str, "candidate") != 0){ + clicon_err(OE_PLUGIN, 0, "No such db name: %s", str); + goto done; + } + cv = cvec_find_var(cvv, "xpath"); + xpath = cv_string_get(cv); + if (xmldb_get(h, str, xpath, 1, &xt, &xv, &xlen) < 0) + goto done; + for (i=0; i CSV commands + * Can only be made in a 'flat tree', ie on the form: + * B --> + * Type, A + * X, B + * @param[in] f Output file + * @param[in] x XML tree + * @param[in] cvv A vector of field names present in XML + * This means that only fields in x that are listed in cvv will be printed. + */ +static int +xml2csv(FILE *f, cxobj *x, cvec *cvv) +{ + cxobj *xe, *xb; + int retval = -1; + cg_var *vs; + + fprintf(f, "%s", xml_name(x)); + xe = NULL; + + vs = NULL; + while ((vs = cvec_each(cvv, vs))) { + if ((xe = xml_find(x, cv_name_get(vs))) == NULL){ + fprintf(f, ";"); + continue; + } + if (xml_child_nr(xe)){ + xb = xml_child_i(xe, 0); + fprintf(f, ";%s", xml_value(xb)); + } + } + fprintf(f, "\n"); + retval = 0; + return retval; +} + +static int +show_conf_as_csv1(clicon_handle h, cvec *cvv0, cg_var *arg) +{ + cxobj *xt = NULL; + cxobj *xc; + int retval = -1; + cvec *cvv=NULL; + char *str; + + if (show_conf_as(h, cvv0, arg, &xt) < 0) + goto done; + xc = NULL; /* Dont print xt itself */ + while ((xc = xml_child_each(xt, xc, -1)) != NULL){ + if ((str = chunk_sprintf(__FUNCTION__, "%s[]", xml_name(xc))) == NULL) + goto done; +#ifdef NOTYET /* yang-spec? */ + if (ds==NULL && (ds = key2spec_key(dbspec, str)) != NULL){ + cg_var *vs; + fprintf(stdout, "Type"); + cvv = db_spec2cvec(ds); + vs = NULL; + while ((vs = cvec_each(cvv, vs))) + fprintf(stdout, ";%s", cv_name_get(vs)); + fprintf(stdout, "\n"); + } /* Now values just need to follow,... */ +#endif /* yang-spec? */ + if (cvv== NULL) + goto done; + xml2csv(stdout, xc, cvv); /* csv syntax */ + } + retval = 0; + done: + if (xt) + xml_free(xt); + unchunk_group(__FUNCTION__); + return retval; +} + +int +show_conf_as_csv(clicon_handle h, cvec *cvv, cg_var *arg) +{ + return show_conf_as_csv1(h, cvv, arg); +} diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h index 1f75e1fe..50ca7919 100644 --- a/apps/cli/clixon_cli_api.h +++ b/apps/cli/clixon_cli_api.h @@ -70,15 +70,22 @@ int cli_start_shell(clicon_handle h, cvec *vars, cg_var *argv); int cli_quit(clicon_handle h, cvec *vars, cg_var *arg); int cli_commit(clicon_handle h, cvec *vars, cg_var *arg); int cli_validate(clicon_handle h, cvec *vars, cg_var *arg); -int expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail); -int expand_dbvar_dbxml(void *h, char *name, cvec *cvv, cg_var *arg, - int *nr, char ***commands, char ***helptexts); int compare_dbs(clicon_handle h, cvec *vars, cg_var *arg); int load_config_file(clicon_handle h, cvec *vars, cg_var *arg); int save_config_file(clicon_handle h, cvec *vars, cg_var *arg); int delete_all(clicon_handle h, cvec *vars, cg_var *arg); int discard_changes(clicon_handle h, cvec *vars, cg_var *arg); +int cli_notify(clicon_handle h, cvec *cvv, cg_var *arg); +int cli_notification_register(clicon_handle h, char *stream, enum format_enum format, + char *filter, int status, + int (*fn)(int, void*), void *arg); + +/* In cli_show.c */ +int expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail); +int expand_dbvar(void *h, char *name, cvec *cvv, cg_var *arg, + int *nr, char ***commands, char ***helptexts); + int show_conf_as_xml(clicon_handle h, cvec *vars, cg_var *arg); int show_conf_as_netconf(clicon_handle h, cvec *vars, cg_var *arg); int show_conf_as_json(clicon_handle h, cvec *vars, cg_var *arg); @@ -86,8 +93,5 @@ int show_conf_as_text(clicon_handle h, cvec *vars, cg_var *arg); int show_conf_as_cli(clicon_handle h, cvec *vars, cg_var *arg); int show_conf_as_csv(clicon_handle h, cvec *vars, cg_var *arg); int show_yang(clicon_handle h, cvec *vars, cg_var *arg); -int cli_notification_register(clicon_handle h, char *stream, enum format_enum format, - char *filter, int status, - int (*fn)(int, void*), void *arg); #endif /* _CLIXON_CLI_API_H_ */ diff --git a/apps/netconf/netconf_filter.c b/apps/netconf/netconf_filter.c index b3761376..05a8a9a9 100644 --- a/apps/netconf/netconf_filter.c +++ b/apps/netconf/netconf_filter.c @@ -153,7 +153,7 @@ xml_filter2(cxobj *xfilter, sprev = s = NULL; while ((s = xml_child_each(xparent, s, CX_ELMNT)) != NULL) { if ((f = xml_find(xfilter, xml_name(s))) == NULL){ - xml_prune(xparent, s, 1); + xml_purge(s); s = sprev; continue; } @@ -166,7 +166,7 @@ xml_filter2(cxobj *xfilter, if (xml_filter2(f, s, &remove_s) < 0) return -1; if (remove_s){ - xml_prune(xparent, s, 1); + xml_purge(s); s = sprev; } sprev = s; diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index e33c2e67..fc6c9411 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -114,7 +114,7 @@ netconf_filter_xmldb(clicon_handle h, switch (foption){ case FILTER_SUBTREE: /* Get the whole database as xml */ - if (xmldb_get(h, source, NULL, 0, &xdb, NULL, NULL) < 0){ + if (xmldb_get(h, source, "/", 0, &xdb, NULL, NULL) < 0){ netconf_create_rpc_error(cb_err, xorig, "operation-failed", "application", diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index ab8493d2..5bfb45c0 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -51,9 +51,9 @@ typedef int (xml_applyfn_t)(cxobj *yn, void *arg); * xml_flag() flags: */ #define XML_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */ -#define XML_FLAG_ADD 0x02 /* Node is added (commits) */ -#define XML_FLAG_DEL 0x04 /* Node is deleted (commits) */ -#define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) */ +#define XML_FLAG_ADD 0x02 /* Node is added (commits) or parent added rec*/ +#define XML_FLAG_DEL 0x04 /* Node is deleted (commits) or parent deleted rec */ +#define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) or child changed rec */ /* * Prototypes @@ -88,12 +88,18 @@ cxobj *xml_new_spec(char *name, cxobj *xn_parent, void *spec); void *xml_spec(cxobj *x); cxobj *xml_find(cxobj *xn_parent, char *name); +int xml_addsub(cxobj *xp, cxobj *xc); +cxobj *xml_insert(cxobj *xt, char *tag); +int xml_purge(cxobj *xc); +int xml_child_rm(cxobj *xp, int i); +int xml_rootchild(cxobj *xp, int i, cxobj **xcp); + char *xml_body(cxobj *xn); char *xml_find_value(cxobj *xn_parent, char *name); char *xml_find_body(cxobj *xn, char *name); int xml_free(cxobj *xn); -int xml_prune(cxobj *xp, cxobj *xc, int freeit); + int clicon_xml2file(FILE *f, cxobj *xn, int level, int prettyprint); int clicon_xml2cbuf(cbuf *xf, cxobj *xn, int level, int prettyprint); int clicon_xml_parse_file(int fd, cxobj **xml_top, char *endtag); @@ -101,8 +107,7 @@ int clicon_xml_parse_string(char **str, cxobj **xml_top); int xml_copy(cxobj *x0, cxobj *x1); cxobj *xml_dup(cxobj *x0); -int xml_addsub(cxobj *xp, cxobj *xc); -cxobj *xml_insert(cxobj *xt, char *tag); + int cxvec_dup(cxobj **vec0, size_t len0, cxobj ***vec1, size_t *len1); int cxvec_append(cxobj *x, cxobj ***vec, size_t *len); int xml_apply(cxobj *xn, enum cxobj_type type, xml_applyfn_t fn, void *arg); diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 3cd3a799..442a8fd6 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -183,6 +183,10 @@ clicon_option_default(clicon_hash_t *copt) if (hash_add(copt, "CLICON_CLI_GENMODEL_COMPLETION", "0", strlen("0")+1) < 0) goto catch; } + if (!hash_lookup(copt, "CLICON_XMLDB_RPC")){ + if (hash_add(copt, "CLICON_XMLDB_RPC", "0", strlen("0")+1) < 0) + goto catch; + } retval = 0; catch: unchunk_group(__FUNCTION__); @@ -600,7 +604,7 @@ clicon_xmldb_rpc(clicon_handle h) char *s; if ((s = clicon_option_str(h, "CLICON_XMLDB_RPC")) == NULL) - return -1; + return 0; /* default 0 */ return atoi(s); } diff --git a/lib/src/clixon_proc.c b/lib/src/clixon_proc.c index 15fb9979..a86ea632 100644 --- a/lib/src/clixon_proc.c +++ b/lib/src/clixon_proc.c @@ -72,10 +72,10 @@ clicon_proc_sigint(int sig) kill (_clicon_proc_child, SIGINT); } -/* - * Fork a child process, setup a pipe between parent and child, allowing - * parent to read the output of the child. If 'doerr' is non-zero, stderr - * will be directed to the pipe as well. The pipe for the parent to write +/*! Fork a child process, setup a pipe between parent and child. + * Allowing parent to read the output of the child. + * @param[in] doerr If non-zero, stderr will be directed to the pipe as well. + * The pipe for the parent to write * to the child is closed and cannot be used. * * When child process is done with the pipe setup, execute the specified @@ -85,10 +85,13 @@ clicon_proc_sigint(int sig) * until eof. The read output will be sent to the specified output callback, * 'outcb' function. * - * Return number of matches (processes affected). -1 on error. + * @retval number Matches (processes affected). + * @retval -1 Error. */ int -clicon_proc_run (char *cmd, void (outcb)(char *), int doerr) +clicon_proc_run (char *cmd, + void (outcb)(char *), + int doerr) { char **argv, @@ -182,9 +185,7 @@ clicon_proc_run (char *cmd, void (outcb)(char *), int doerr) return retval; } - -/* - * Spawm command and report exit status +/*! Spawn command and report exit status */ int clicon_proc_daemon (char *cmd) @@ -251,81 +252,6 @@ clicon_proc_daemon (char *cmd) } -#ifdef moved_to_osr -#ifdef linux -/* - * Send 'sig' (if not 0) to all processes matching 'name'. - * Return the number of matches. - */ -int -clicon_proc_killbyname (const char *name, int sig) -{ - /* XXXX FIXME. Should scan /proc//status */ - char buf[512]; - snprintf (buf, sizeof (buf)-1, "pkill -%d %s", sig, name); - - return clicon_proc_run (buf, NULL, 0); -} -#endif /* Linux */ - - -#ifdef BSD -/* - * Send 'sig' (if not 0) to all processes matching 'name'. - * Return the number of matches. - */ -int -clicon_proc_killbyname (const char *name, int sig) -{ - int - i, - nproc, - nmatch, - mib[3]; - size_t - size; - struct proc - *p; - struct kinfo_proc - *kp = NULL; - - mib[0] = CTL_KERN; - mib[1] = KERN_NPROCS; /* KERN_MACPROC, KERN_PROC */ - size = sizeof (nproc); - if (sysctl(mib, 2, &nproc, &size, NULL, 0) < 0) - return -1; - - size = nproc * sizeof(struct kinfo_proc); - kp = chunk(size * sizeof(char), "bsdkill"); - if (kp == NULL) - goto error; - - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_ALL; - - if (sysctl(mib, 3, kp, &size, NULL, 0) < 0) - goto error; - - nproc = size / sizeof(struct kinfo_proc); - for (nmatch = i = 0; i < nproc; i++) { - p = (struct proc *)&kp[i].kp_proc; - if (!strcmp (name, p->p_comm)) { - nmatch++; - kill (p->p_pid, sig); - } - } - - unchunk (kp); - return nmatch; - - error: - if (kp) - unchunk (kp); - return -1; -} -#endif /* BSD */ -#endif - /*! Translate group name to gid. Return -1 if error or not found. */ int diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index 5293dff3..d4057ceb 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -464,7 +464,9 @@ send_msg_ok(int s) } int -send_msg_notify(int s, int level, char *event) +send_msg_notify(int s, + int level, + char *event) { int retval = -1; struct clicon_msg *msg; diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index be85453f..c6769773 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -174,11 +174,11 @@ clicon_rpc_validate(clicon_handle h, * @note special case: remove all: key:"/" op:OP_REMOVE */ int -clicon_rpc_change(clicon_handle h, - char *db, +clicon_rpc_change(clicon_handle h, + char *db, enum operation_type op, - char *key, - char *val) + char *key, + char *val) { int retval = -1; struct clicon_msg *msg; diff --git a/lib/src/clixon_qdb.c b/lib/src/clixon_qdb.c index 93b5a09b..153e9352 100644 --- a/lib/src/clixon_qdb.c +++ b/lib/src/clixon_qdb.c @@ -83,14 +83,15 @@ db_init_mode(char *file, /* Open database for writing */ if ((dp = dpopen(file, omode | DP_OLCKNB, 0)) == NULL){ - clicon_err(OE_DB, 0, "db_init: dpopen(%s): %s", - file, - dperrmsg(dpecode)); + clicon_err(OE_DB, errno, "dpopen(%s): %s", + file, + dperrmsg(dpecode)); return -1; } clicon_debug(1, "db_init(%s)", file); if (dpclose(dp) == 0){ - clicon_err(OE_DB, 0, "db_set: dpclose: %s", dperrmsg(dpecode)); + clicon_err(OE_DB, errno, "db_set: dpclose: %s", + dperrmsg(dpecode)); return -1; } return 0; @@ -138,7 +139,7 @@ db_set(char *file, char *key, void *data, size_t datalen) /* Open database for writing */ if ((dp = dpopen(file, DP_OWRITER|DP_OLCKNB , 0)) == NULL){ - clicon_err(OE_DB, 0, "db_set: dpopen(%s): %s", + clicon_err(OE_DB, errno, "db_set: dpopen(%s): %s", file, dperrmsg(dpecode)); return -1; @@ -146,7 +147,7 @@ db_set(char *file, char *key, void *data, size_t datalen) clicon_debug(2, "%s: db_put(%s, len:%d)", file, key, (int)datalen); if (dpput(dp, key, -1, data, datalen, DP_DOVER) == 0){ - clicon_err(OE_DB, 0, "%s: db_set: dpput(%s, %d): %s", + clicon_err(OE_DB, errno, "%s: db_set: dpput(%s, %d): %s", file, key, datalen, @@ -181,7 +182,7 @@ db_get(char *file, /* Open database for readinf */ if ((dp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){ - clicon_err(OE_DB, dpecode, "%s: db_get(%s, %d): dpopen: %s", + clicon_err(OE_DB, errno, "%s: db_get(%s, %d): dpopen: %s", file, key, datalen, @@ -195,7 +196,7 @@ db_get(char *file, *datalen = 0; } else{ - clicon_err(OE_DB, 0, "db_get: dpgetwb: %s (%d)", + clicon_err(OE_DB, errno, "db_get: dpgetwb: %s (%d)", dperrmsg(dpecode), dpecode); dpclose(dp); return -1; @@ -205,7 +206,7 @@ db_get(char *file, *datalen = len; clicon_debug(2, "db_get(%s, %s)=%s", file, key, (char*)data); if (dpclose(dp) == 0){ - clicon_err(OE_DB, 0, "db_get: dpclose: %s", dperrmsg(dpecode)); + clicon_err(OE_DB, errno, "db_get: dpclose: %s", dperrmsg(dpecode)); return -1; } return 0; @@ -243,7 +244,7 @@ db_get_alloc(char *file, /* Open database for writing */ if ((dp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){ - clicon_err(OE_DB, 0, "%s: dpopen(%s): %s", + clicon_err(OE_DB, errno, "%s: dpopen(%s): %s", __FUNCTION__, file, dperrmsg(dpecode)); @@ -257,7 +258,7 @@ db_get_alloc(char *file, } else{ /* No entry vs error? */ - clicon_err(OE_DB, 0, "db_get_alloc: dpgetwb: %s (%d)", + clicon_err(OE_DB, errno, "db_get_alloc: dpgetwb: %s (%d)", dperrmsg(dpecode), dpecode); dpclose(dp); return -1; @@ -265,7 +266,7 @@ db_get_alloc(char *file, } *datalen = len; if (dpclose(dp) == 0){ - clicon_err(OE_DB, 0, "db_get_alloc: dpclose: %s", dperrmsg(dpecode)); + clicon_err(OE_DB, errno, "db_get_alloc: dpclose: %s", dperrmsg(dpecode)); return -1; } return 0; @@ -286,7 +287,7 @@ db_del(char *file, char *key) /* Open database for writing */ if ((dp = dpopen(file, DP_OWRITER | DP_OLCKNB, 0)) == NULL){ - clicon_err(OE_DB, 0, "db_del: dpopen(%s): %s", + clicon_err(OE_DB, errno, "db_del: dpopen(%s): %s", file, dperrmsg(dpecode)); return -1; @@ -295,7 +296,7 @@ db_del(char *file, char *key) retval = 1; } if (dpclose(dp) == 0){ - clicon_err(OE_DB, 0, "db_del: dpclose: %s", dperrmsg(dpecode)); + clicon_err(OE_DB, errno, "db_del: dpclose: %s", dperrmsg(dpecode)); return -1; } return retval; @@ -316,18 +317,18 @@ db_exists(char *file, char *key) /* Open database for reading */ if ((dp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){ - clicon_err(OE_DB, dpecode, "%s: dpopen: %s", + clicon_err(OE_DB, errno, "%s: dpopen: %s", __FUNCTION__, dperrmsg(dpecode)); return -1; } len = dpvsiz(dp, key, -1); if (len < 0 && dpecode != DP_ENOITEM) - clicon_err(OE_DB, 0, "^s: dpvsiz: %s (%d)", + clicon_err(OE_DB, errno, "^s: dpvsiz: %s (%d)", __FUNCTION__, dperrmsg(dpecode), dpecode); if (dpclose(dp) == 0) { - clicon_err(OE_DB, 0, "%s: dpclose: %s", dperrmsg(dpecode),__FUNCTION__); + clicon_err(OE_DB, errno, "%s: dpclose: %s", dperrmsg(dpecode),__FUNCTION__); return -1; } @@ -378,7 +379,7 @@ db_regexp(char *file, if (regexp) { if ((status = regcomp(&iterre, regexp, REG_EXTENDED)) != 0) { regerror(status, &iterre, errbuf, sizeof(errbuf)); - clicon_err(OE_DB, 0, "%s: regcomp: %s", __FUNCTION__, errbuf); + clicon_err(OE_DB, errno, "%s: regcomp: %s", __FUNCTION__, errbuf); return -1; } } @@ -392,7 +393,7 @@ db_regexp(char *file, /* Initiate iterator */ if(dpiterinit(iterdp) == 0) { - clicon_err(OE_DB, 0, "%s: dpiterinit: %s", __FUNCTION__, dperrmsg(dpecode)); + clicon_err(OE_DB, errno, "%s: dpiterinit: %s", __FUNCTION__, dperrmsg(dpecode)); goto quit; } @@ -549,7 +550,7 @@ main(int argc, char **argv) } else if (strcmp(verb, "openread")==0){ if ((dp = dpopen(filename, DP_OREADER | DP_OLCKNB, 0)) == NULL){ - clicon_err(OE_DB, dpecode, "dbopen: %s", + clicon_err(OE_DB, errno, "dbopen: %s", dperrmsg(dpecode)); return -1; } @@ -557,7 +558,7 @@ main(int argc, char **argv) } else if (strcmp(verb, "openwrite")==0){ if ((dp = dpopen(filename, DP_OWRITER | DP_OLCKNB, 0)) == NULL){ - clicon_err(OE_DB, dpecode, "dbopen: %s", + clicon_err(OE_DB, errno, "dbopen: %s", dperrmsg(dpecode)); return -1; } diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index a6fc54ca..e7d0065d 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -201,7 +201,7 @@ xml_value(cxobj *xn) return xn->x_value; } -/*! Set value of xnode, value is copied +/*! Set value of xml node, value is copied * @param[in] xn xml node * @param[in] val new value, null-terminated string, copied by function * @retval -1 on error with clicon-err set @@ -230,7 +230,8 @@ xml_value_set(cxobj *xn, char *val) * @retval new value */ char * -xml_value_append(cxobj *xn, char *val) +xml_value_append(cxobj *xn, + char *val) { int len0; int len; @@ -263,7 +264,8 @@ xml_type(cxobj *xn) * @retval type old type */ enum cxobj_type -xml_type_set(cxobj *xn, enum cxobj_type type) +xml_type_set(cxobj *xn, + enum cxobj_type type) { enum cxobj_type old = xn->x_type; @@ -289,7 +291,8 @@ xml_index(cxobj *xn) * index/key is used in case of yang list constructs where one element is key */ int -xml_index_set(cxobj *xn, int index) +xml_index_set(cxobj *xn, + int index) { int old = xn->x_index; @@ -313,7 +316,8 @@ xml_child_nr(cxobj *xn) * @retval child in XML tree, or NULL if no such child, or empty child */ cxobj * -xml_child_i(cxobj *xn, int i) +xml_child_i(cxobj *xn, + int i) { if (i < xn->x_childvec_len) return xn->x_childvec[i]; @@ -321,7 +325,9 @@ xml_child_i(cxobj *xn, int i) } cxobj * -xml_child_i_set(cxobj *xt, int i, cxobj *xc) +xml_child_i_set(cxobj *xt, + int i, + cxobj *xc) { if (i < xt->x_childvec_len) xt->x_childvec[i] = xc; @@ -460,7 +466,8 @@ xml_spec(cxobj *x) * @retval NULL if no such node found. */ cxobj * -xml_find(cxobj *x_up, char *name) +xml_find(cxobj *x_up, + char *name) { cxobj *x = NULL; @@ -478,14 +485,25 @@ xml_find(cxobj *x_up, char *name) * @see xml_insert */ int -xml_addsub(cxobj *xp, cxobj *xc) +xml_addsub(cxobj *xp, + cxobj *xc) { cxobj *oldp; + int i; - if ((oldp = xml_parent(xc)) != NULL) - xml_prune(oldp, xc, 0); + if ((oldp = xml_parent(xc)) != NULL){ + /* Find child order i in old parent*/ + for (i=0; ix_childvec[i] = NULL; + xml_parent_set(xc, NULL); + xp->x_childvec_len--; + /* shift up, note same index i used but ok since we break */ + for (; ix_childvec_len; i++) + xp->x_childvec[i] = xp->x_childvec[i+1]; + retval = 0; + done: + return retval; +} + +/*! Return a child sub-tree, while removing parent and all other children + * Given a root xml node, and the i:th child, remove the child from its parent + * and return it, remove the parent and all other children. + * Before: xp-->[..xc..] + * After: xc + * @param[in] xp xml parent node. Will be deleted + * @param[in] i Child nr in parent child vector + * @param[out] xcp xml child node. New root + * @code + * @endcode + * @see xml_child_rm + */ +int +xml_rootchild(cxobj *xp, + int i, + cxobj **xcp) +{ + int retval = -1; + cxobj *xc; + + if (xml_parent(xp) != NULL){ + clicon_err(OE_XML, 0, "Parent is not root"); + goto done; + } + if ((xc = xml_child_i(xp, i)) == NULL){ + clicon_err(OE_XML, 0, "Child not found"); + goto done; + } + if (xml_child_rm(xp, i) < 0) + goto done; + if (xml_free(xp) < 0) + goto done; + *xcp = xc; + retval = 0; + done: + return retval; +} + + /*! Get the first sub-node which is an XML body. * @param[in] xn xml tree node * @retval The returned body as a pointer to the name string @@ -532,7 +654,6 @@ xml_body(cxobj *xn) return NULL; } - /*! Find and return the value of a sub xml node * * The value can be of an attribute or body. @@ -554,14 +675,13 @@ xml_find_value(cxobj *x_up, char *name) return NULL; } - /*! Find and return a body (string) of a sub xml node * @param[in] xn xml tree node * @param[in] name name of xml tree node * @retval The returned body as a pointer to the name string * @retval NULL if no such node or no body in found node - * Note, make a copy of the return value to use it properly - * See also xml_find_value + * @note, make a copy of the return value to use it properly + * @see xml_find_value */ char * xml_find_body(cxobj *xn, char *name) @@ -573,51 +693,9 @@ xml_find_body(cxobj *xn, char *name) return NULL; } -/*! Remove an xml node from a parent xml node. - * @param[in] xparent xml parent node - * @param[in] xchild xml child node (to remove) - * @param[in] purge if 1, free the child node, not just remove from parent - * @retval 0 OK - * @retval -1 - * @note you cannot remove xchild in the loop (unless yoy keep track of xprev) - * - * @see xml_free - * Differs from xml_free in two ways: - * 1. It is removed from parent. - * 2. If you set the purge flag to 1, the child tree will be freed - * (otherwise it will not) - */ -int -xml_prune(cxobj *xparent, - cxobj *xchild, - int purge) -{ - int i; - cxobj *xc = NULL; - - for (i=0; ix_childvec[i] = NULL; - xml_parent_set(xc, NULL); - if (purge) - xml_free(xchild); - xparent->x_childvec_len--; - /* shift up, note same index i used but ok since we break */ - for (;ix_childvec_len; i++) - xparent->x_childvec[i] = xparent->x_childvec[i+1]; - return 0; - } - clicon_err(OE_XML, 0, "%s: child not found", __FUNCTION__); - return -1; -} - /*! Free an xl sub-tree recursively, but do not remove it from parent * @param[in] x the xml tree to be freed. - * @see xml_prune - * Differs from xml_prune in that it is _not_ removed from parent. + * @see xml_purge where x is also removed from parent */ int xml_free(cxobj *x) @@ -662,7 +740,7 @@ clicon_xml2file(FILE *f, int retval = -1; if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_XML, errno, "%s: cbuf_new", __FUNCTION__); + clicon_err(OE_XML, errno, "cbuf_new"); goto done; } if (clicon_xml2cbuf(cb, xn, level, prettyprint) < 0) @@ -864,7 +942,6 @@ clicon_xml_parse_file(int fd, return (*cx)?0:-1; } - /*! Read an XML definition from string and parse it into a parse-tree. * * @param[in] str Pointer to string containing XML definition. NOTE: destructively @@ -1081,6 +1158,7 @@ xml_apply_ancestor(cxobj *xn, goto done; if (xml_apply_ancestor(xp, fn, arg) < 0) goto done; + xn = xp; } retval = 0; done: diff --git a/lib/src/clixon_xml_db.c b/lib/src/clixon_xml_db.c index 0ee6ccb4..688a2646 100644 --- a/lib/src/clixon_xml_db.c +++ b/lib/src/clixon_xml_db.c @@ -389,6 +389,9 @@ db_islocked(char *db) * @param[out] filename Filename. Unallocate after use with free() * @retval 0 OK * @retval -1 Error + * @note Could need a way to extend which databases exists, eg to register new. + * The currently allowed databases are: + * candidate, tmp, running, result * The filename reside in CLICON_XMLDB_DIR option */ static int @@ -410,6 +413,7 @@ db2file(clicon_handle h, } if (strcmp(db, "running") != 0 && strcmp(db, "candidate") != 0 && + strcmp(db, "result") != 0 && strcmp(db, "tmp") != 0){ clicon_err(OE_XML, 0, "Unexpected database: %s", db); goto done; @@ -539,7 +543,7 @@ xml_tree_prune_unmarked(cxobj *xt, if (submark) mark++; else{ /* Safe with xml_child_each if last */ - if (xml_prune(xt, x, 1) < 0) + if (xml_purge(x) < 0) goto done; x = xprev; } @@ -816,14 +820,16 @@ xmldb_get_tree(char *dbname, } if (xml_tree_prune_unmarked(xt, NULL) < 0) goto done; + if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0) + goto done; } *xtop = xt; if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0) goto done; - if (1) + if (1) /* sanity */ if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0) goto done; - if (0) + if (debug) clicon_xml2file(stdout, xt, 0, 1); retval = 0; done: @@ -841,7 +847,7 @@ xmldb_get_tree(char *dbname, * The function returns a minimal tree that includes all sub-trees that match * xpath. * @param[in] dbname Name of database to search in (filename including dir path - * @param[in] xpath String with XPATH syntax (or NULL for all) + * @param[in] xpath String with XPATH syntax * @param[in] yspec Yang specification * @param[out] xtop Single XML tree which xvec points to. Free with xml_free() * @param[out] xvec Vector of xml trees. Free after use @@ -955,9 +961,9 @@ xmldb_get_local(clicon_handle h, * xpath. * @param[in] h Clicon handle * @param[in] db running | candidate - * @param[in] xpath String with XPATH syntax (or NULL for all) + * @param[in] xpath String with XPATH syntax * @param[in] vector If set, return list of results in xvec, else single tree. - * @param[out] xtop XML tree. Freed by xml_free() + * @param[out] xtop XML tree. Freed by xml_free()p * @param[out] xvec Vector of xml trees. Free after use * @param[out] xlen Length of vector. * @retval 0 OK @@ -967,7 +973,7 @@ xmldb_get_local(clicon_handle h, * cxobj **xvec; * size_t xlen; * yang_spec *yspec = clicon_dbspec_yang(h); - * if (xmldb_get(dbname, "/interfaces/interface[name="eth"]", yspec, + * if (xmldb_get("running", "/interfaces/interface[name="eth"]", * 1, &xt, &xvec, &xlen) < 0) * err; * for (i=0; i") < 0) goto done; - xn = xml_child_i(xt, 0); - xml_prune(xt, xn, 0); /* kludge to remove top-level tag (eg top/clicon) */ - xml_parent_set(xn, NULL); - xml_free(xt); + if (xml_rootchild(xt, 0, &xn) < 0) + goto done; if (strcmp(argv[5], "set") == 0) op = OP_REPLACE; else diff --git a/lib/src/clixon_xml_db_rpc.c b/lib/src/clixon_xml_db_rpc.c index b5b509d4..e0d4bb30 100644 --- a/lib/src/clixon_xml_db_rpc.c +++ b/lib/src/clixon_xml_db_rpc.c @@ -59,6 +59,8 @@ #include "clixon_xml_db.h" #include "clixon_xml_db_rpc.h" +/*! Make an rpc call to xmldb daemon + */ static int xmldb_rpc(clicon_handle h, char *data, @@ -273,13 +275,9 @@ xmldb_get_rpc(clicon_handle h, xt = NULL; } else{ - if ((xc = xml_child_i(xt, 0)) != NULL){ - xml_prune(xt, xc, 0); /* kludge to remove top-level tag (eg top/clicon) */ - xml_parent_set(xc, NULL); - if (debug > 1) - clicon_xml2file(stderr, xc, 0, 1); - } - *xtop = xc; + if (xml_rootchild(xt, 0, xtop) < 0) + goto done; + xt = NULL; } retval = 0; diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index afbde0fa..d3c0edc1 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -520,7 +520,9 @@ xml2cvec(cxobj *xt, * @see cvec2xml This does more but has an internal xml2cvec translation */ int -cvec2xml_1(cvec *cvv, char *toptag, cxobj **xt0) +cvec2xml_1(cvec *cvv, + char *toptag, + cxobj **xt0) { int retval = -1; cxobj *xt = NULL; diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index 3e98517d..66edeef3 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -784,14 +784,14 @@ xpath_each(cxobj *cxtop, * * @code * cxobj **xv; - * int xlen; - * if ((xv = xpath_vec(cxtop, "//symbol/foo", &xlen)) != NULL) { - * for (i=0; iys_typecache != NULL){ + /* Cache does not work for eg string length 32 */ + if (!yang_builtin(type) && ytype->ys_typecache != NULL){ if (yang_type_cache_get(ytype->ys_typecache, yrestype, options, mincv, maxcv, pattern, fraction) < 0) goto done; @@ -803,7 +804,7 @@ yang_type_resolve(yang_stmt *ys, } else while (1){ - /* Check upwards in hierarchy for matcing typedefs */ + /* Check upwards in hierarchy for matching typedefs */ if ((ys = ys_typedef_up(ys)) == NULL){ /* If reach top */ *yrestype = NULL; break;