diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 400f6ead..28c11602 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -244,7 +244,7 @@ from_client_xmlput(clicon_handle h, cvec *cvv = NULL; char *str = NULL; char *xml = NULL; - cxobj *xt; + cxobj *xt = NULL; int piddb; if (clicon_msg_xmlput_decode(msg, @@ -283,6 +283,8 @@ from_client_xmlput(clicon_handle h, free(str); if (cvv) cvec_free (cvv); + if (xt) + xml_free(xt); return retval; } diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 7eb6607c..b1b5a60d 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -112,51 +112,41 @@ generic_validate(yang_spec *yspec, return retval; } -/*! Do a diff between candidate and running, then start a commit transaction - * - * The code reverts changes if the commit fails. But if the revert - * fails, we just ignore the errors and proceed. Maybe we should - * do something more drastic? - * @param[in] h Clicon handle -*/ -int -candidate_commit(clicon_handle h, - char *candidate) +/*! Common code of candidate_validate and candidate_commit + */ +static int +validate_common(clicon_handle h, + char *candidate, + transaction_data_t *td) { - int retval = -1; - int i; - cxobj *xn; - void *firsterr = NULL; - yang_spec *yspec; - transaction_data_t *td = NULL; + int retval = -1; + yang_spec *yspec; + int i; + cxobj *xn; + if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_FATAL, 0, "No DB_SPEC"); goto done; } - - /* 1. Start transaction */ - if ((td = transaction_new()) == NULL) - goto done; - - /* 2. Parse xml trees */ + /* 2. Parse xml trees */ if (xmldb_get(h, "running", "/", 0, &td->td_src, NULL, NULL) < 0) goto done; if (xmldb_get(h, candidate, "/", 0, &td->td_target, NULL, NULL) < 0) goto done; - /* 3. Compute differences */ - if (xml_diff(yspec, - td->td_src, - td->td_target, - &td->td_dvec, /* removed: only in running */ - &td->td_dlen, - &td->td_avec, /* added: only in candidate */ - &td->td_alen, - &td->td_scvec, /* changed: original values */ - &td->td_tcvec, /* changed: wanted values */ - &td->td_clen) < 0) - goto done; + /* 3. Compute differences */ + if (xml_diff(yspec, + td->td_src, + td->td_target, + &td->td_dvec, /* removed: only in running */ + &td->td_dlen, + &td->td_avec, /* added: only in candidate */ + &td->td_alen, + &td->td_scvec, /* changed: original values */ + &td->td_tcvec, /* changed: wanted values */ + &td->td_clen) < 0) + goto done; if (debug) transaction_print(stderr, td); /* Mark as changed in tree */ @@ -183,19 +173,45 @@ candidate_commit(clicon_handle h, /* 4. Call plugin transaction start callbacks */ if (plugin_transaction_begin(h, td) < 0) - goto done; + goto done; /* 5. Make generic validation on all new or changed data. */ if (generic_validate(yspec, td) < 0) - goto done; + goto done; /* 6. Call plugin transaction validate callbacks */ - if (plugin_transaction_validate(h, td) < 0) - goto done; + if (plugin_transaction_validate(h, td) < 0) + goto done; - /* 7. Call plugin transaction complete callbacks */ - if (plugin_transaction_complete(h, td) < 0) - goto done; + /* 7. Call plugin transaction complete callbacks */ + if (plugin_transaction_complete(h, td) < 0) + goto done; + retval = 0; + done: + return retval; +} + +/*! Do a diff between candidate and running, then start a commit transaction + * + * The code reverts changes if the commit fails. But if the revert + * fails, we just ignore the errors and proceed. Maybe we should + * do something more drastic? + * @param[in] h Clicon handle +*/ +int +candidate_commit(clicon_handle h, + char *candidate) +{ + int retval = -1; + transaction_data_t *td = NULL; + + /* 1. Start transaction */ + if ((td = transaction_new()) == NULL) + goto done; + + /* Common steps (with validate) */ + if (validate_common(h, candidate, td) < 0) + goto done; /* 7. Call plugin transaction commit callbacks */ if (plugin_transaction_commit(h, td) < 0) @@ -214,7 +230,6 @@ candidate_commit(clicon_handle h, clicon_log(LOG_NOTICE, "Error in rollback, trying to continue"); goto done; } - retval = 0; done: /* In case of failure, call plugin transaction termination callbacks */ @@ -222,8 +237,6 @@ candidate_commit(clicon_handle h, plugin_transaction_abort(h, td); if (td) transaction_free(td); - if (firsterr) - clicon_err_restore(firsterr); return retval; } @@ -237,73 +250,15 @@ candidate_validate(clicon_handle h, char *candidate) { int retval = -1; - yang_spec *yspec; transaction_data_t *td = NULL; - int i; - cxobj *xn; - - if ((yspec = clicon_dbspec_yang(h)) == NULL){ - clicon_err(OE_FATAL, 0, "No DB_SPEC"); - goto done; - } /* 1. Start transaction */ if ((td = transaction_new()) == NULL) goto done; - /* 2. Parse xml trees */ - if (xmldb_get(h, "running", "/", 0, &td->td_src, NULL, NULL) < 0) + /* Common steps (with commit) */ + if (validate_common(h, candidate, td) < 0) goto done; - if (xmldb_get(h, "candidate", "/", 0, &td->td_target, NULL, NULL) < 0) - goto done; - - /* 3. Compute differences */ - if (xml_diff(yspec, - td->td_src, - td->td_target, - &td->td_dvec, /* removed: only in running */ - &td->td_dlen, - &td->td_avec, /* added: only in candidate */ - &td->td_alen, - &td->td_scvec, /* changed: original values */ - &td->td_tcvec, /* changed: wanted values */ - &td->td_clen) < 0) - goto done; - - if (debug) - transaction_print(stderr, td); - - /* Mark as changed in tree */ - for (i=0; itd_dlen; i++){ /* Also down */ - xn = td->td_dvec[i]; - xml_flag_set(xn, XML_FLAG_DEL); - } - for (i=0; itd_alen; i++){ /* Also down */ - xn = td->td_avec[i]; - xml_flag_set(xn, XML_FLAG_ADD); - } - for (i=0; itd_clen; i++){ /* Also up */ - xn = td->td_scvec[i]; - xml_flag_set(xn, XML_FLAG_CHANGE); - xn = td->td_tcvec[i]; - xml_flag(xn, XML_FLAG_CHANGE); - } - - /* 4. Call plugin start transaction callbacks */ - if (plugin_transaction_begin(h, td) < 0) - goto done; - - /* 5. Make generic validation on all new or changed data. */ - if (generic_validate(yspec, td) < 0) - goto done; - - /* 6. Call plugin validate transaction callbacks */ - if (plugin_transaction_validate(h, td) < 0) - goto done; - - /* 7. Call plugin complete transaction callbacks */ - if (plugin_transaction_complete(h, td) < 0) - goto done; retval = 0; done: diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c index 3e187309..c1c2a81d 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -632,6 +632,15 @@ plugin_transaction_complete(clicon_handle h, return retval; } +/*! Revert a commit + * @param[in] h CLICON handle + * @param[in] td Transaction data + * @param[in] nr The plugin where an error occured. + * @retval 0 OK + * @retval -1 Error + * The revert is made in plugin before this one. Eg if error occurred in + * plugin 2, then the revert will be made in plugins 1 and 0. + */ int plugin_transaction_revert(clicon_handle h, transaction_data_t *td, @@ -651,10 +660,11 @@ plugin_transaction_revert(clicon_handle h, tr.td_dvec = td->td_avec; tr.td_alen = td->td_dlen; tr.td_avec = td->td_dvec; + tr.td_clen = td->td_clen; tr.td_scvec = td->td_tcvec; tr.td_tcvec = td->td_scvec; - for (i = nr-1; i; i--){ + for (i = nr-1; i>=0; i--){ p = &plugins[i]; if (p->p_trans_commit) if ((p->p_trans_commit)(h, (transaction_data)&tr) < 0){ diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c index feabe6ba..1b88a5a8 100644 --- a/apps/backend/clixon_backend_handle.c +++ b/apps/backend/clixon_backend_handle.c @@ -167,6 +167,7 @@ backend_notify_xml(clicon_handle h, cbuf *cb = NULL; struct handle_subscription *hs; + clicon_debug(1, "%s %s", __FUNCTION__, stream); /* Now go thru all clients(sessions), and all subscriptions and find matches */ for (ce = backend_client_list(h); ce; ce = ce->ce_next) for (su = ce->ce_subscription; su; su = su->su_next) @@ -185,8 +186,6 @@ backend_notify_xml(clicon_handle h, } } /* Then go thru all global (handle) subscriptions and find matches */ - /* XXX: x contains name==dk-ore, but filter is - id==/[userid=d2d5e46c-c6f9-42f3-9a69-fb52fe60940d] */ hs = NULL; while ((hs = subscription_each(h, hs)) != NULL){ if (hs->hs_format != MSG_NOTIFY_XML) diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 392a7762..7080f4af 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -310,7 +310,7 @@ cli_commit(clicon_handle h, "running", snapshot, /* snapshot */ snapshot)) < 0){ /* startup */ - cli_output(stderr, "Commit failed. Edit and try again or discard changes\n"); + cli_output(stderr, "Commit failed. Edit and try again or discard changes"); goto done; } retval = 0; @@ -327,7 +327,7 @@ cli_validate(clicon_handle h, cvec *vars, cg_var *arg) int retval = -1; if ((retval = clicon_rpc_validate(h, "candidate")) < 0) - clicon_err(OE_CFG, 0, "Validate failed. Edit and try again or discard changes\n"); + clicon_err(OE_CFG, 0, "Validate failed. Edit and try again or discard changes"); return retval; } @@ -578,7 +578,7 @@ load_config_file(clicon_handle h, } filename = vecp[0]; if (stat(filename, &st) < 0){ - clicon_err(OE_UNIX, 0, "load_config: stat(%s): %s\n", + clicon_err(OE_UNIX, 0, "load_config: stat(%s): %s", filename, strerror(errno)); goto done; } @@ -901,7 +901,7 @@ cli_notification_register(clicon_handle h, if ((p = hash_value(cdat, logname, &len)) != NULL) s_exist = *(int*)p; - if (status){ + if (status){ /* start */ if (s_exist!=-1){ clicon_err(OE_PLUGIN, 0, "%s: result log socket already exists", __FUNCTION__); goto done; @@ -913,7 +913,7 @@ cli_notification_register(clicon_handle h, if (hash_add(cdat, logname, &s, sizeof(s)) == NULL) goto done; } - else{ + else{ /* stop */ if (s_exist != -1){ cligen_unregfd(s_exist); } diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 5bfb45c0..5a9b5233 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -113,4 +113,8 @@ 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); int xml_apply_ancestor(cxobj *xn, xml_applyfn_t fn, void *arg); +int xml_body_parse(cxobj *xb, enum cv_type type, cg_var **cvp); +int xml_body_int32(cxobj *xb, int32_t *val); +int xml_body_uint32(cxobj *xb, uint32_t *val); + #endif /* _CLIXON_XML_H */ diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index 6af247ad..249cde0f 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -44,7 +44,7 @@ int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt, const char int xml2json(FILE *f, cxobj *x, int level); int xml_yang_validate(cxobj *xt, yang_stmt *ys) ; int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0); -int cvec2xml_1(cvec *cvv, char *toptag, cxobj **xt0); +int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0); int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2, cxobj ***first, size_t *firstlen, cxobj ***second, size_t *secondlen, diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index c6769773..964396a4 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -430,10 +430,11 @@ clicon_rpc_subscription(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, NULL, NULL, s0, __FUNCTION__) < 0) goto done; - if (status == 0){ + if (status == 0 && s0){ close(*s0); *s0 = -1; } + retval = 0; done: unchunk_group(__FUNCTION__); return retval; diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index e7d0065d..16299753 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -90,7 +90,8 @@ xml_name(cxobj *xn) * @retval 0 OK */ int -xml_name_set(cxobj *xn, char *name) +xml_name_set(cxobj *xn, + char *name) { if (xn->x_name){ free(xn->x_name); @@ -122,7 +123,8 @@ xml_namespace(cxobj *xn) * @retval 0 OK */ int -xml_namespace_set(cxobj *xn, char *namespace) +xml_namespace_set(cxobj *xn, + char *namespace) { if (xn->x_namespace){ free(xn->x_namespace); @@ -153,7 +155,8 @@ xml_parent(cxobj *xn) * @retval 0 OK */ int -xml_parent_set(cxobj *xn, cxobj *parent) +xml_parent_set(cxobj *xn, + cxobj *parent) { xn->x_up = parent; return 0; @@ -164,7 +167,8 @@ xml_parent_set(cxobj *xn, cxobj *parent) * @retval flag Flags value, see XML_FLAG_* */ uint16_t -xml_flag(cxobj *xn, uint16_t flag) +xml_flag(cxobj *xn, + uint16_t flag) { return xn->x_flags&flag; } @@ -174,7 +178,8 @@ xml_flag(cxobj *xn, uint16_t flag) * @param[in] flag Flags value to set, see XML_FLAG_* */ int -xml_flag_set(cxobj *xn, uint16_t flag) +xml_flag_set(cxobj *xn, + uint16_t flag) { xn->x_flags |= flag; return 0; @@ -185,7 +190,8 @@ xml_flag_set(cxobj *xn, uint16_t flag) * @param[in] flag Flags value to reset, see XML_FLAG_* */ int -xml_flag_reset(cxobj *xn, uint16_t flag) +xml_flag_reset(cxobj *xn, + uint16_t flag) { xn->x_flags &= ~flag; return 0; @@ -208,7 +214,8 @@ xml_value(cxobj *xn) * @retval 0 OK */ int -xml_value_set(cxobj *xn, char *val) +xml_value_set(cxobj *xn, + char *val) { if (xn->x_value){ free(xn->x_value); @@ -377,7 +384,8 @@ xml_child_each(cxobj *xparent, * Note: does not do anything with child, you may need to set its parent, etc */ static int -xml_child_append(cxobj *x, cxobj *xc) +xml_child_append(cxobj *x, + cxobj *xc) { x->x_childvec_len++; x->x_childvec = realloc(x->x_childvec, x->x_childvec_len*sizeof(cxobj*)); @@ -397,7 +405,8 @@ xml_child_append(cxobj *x, cxobj *xc) * @endcode */ int -xml_childvec_set(cxobj *x, int len) +xml_childvec_set(cxobj *x, + int len) { x->x_childvec_len = len; if ((x->x_childvec = calloc(len, sizeof(cxobj*))) == NULL){ @@ -416,7 +425,8 @@ xml_childvec_set(cxobj *x, int len) * @retval NULL if error and clicon_err() called */ cxobj * -xml_new(char *name, cxobj *xp) +xml_new(char *name, + cxobj *xp) { cxobj *xn; @@ -438,7 +448,9 @@ xml_new(char *name, cxobj *xp) /*! Create new xml node given a name, parent and spec. Free it with xml_free(). */ cxobj * -xml_new_spec(char *name, cxobj *xp, void *spec) +xml_new_spec(char *name, + cxobj *xp, + void *spec) { cxobj *x; @@ -518,7 +530,8 @@ xml_addsub(cxobj *xp, * The name of the function is somewhat misleading */ cxobj * -xml_insert(cxobj *xp, char *tag) +xml_insert(cxobj *xp, + char *tag) { cxobj *xc; /* new child */ @@ -578,7 +591,7 @@ xml_purge(cxobj *xc) */ int xml_child_rm(cxobj *xp, - int i) + int i) { int retval = -1; cxobj *xc = NULL; @@ -666,7 +679,8 @@ xml_body(cxobj *xn) * See also xml_find_body */ char * -xml_find_value(cxobj *x_up, char *name) +xml_find_value(cxobj *x_up, + char *name) { cxobj *x; @@ -684,7 +698,8 @@ xml_find_value(cxobj *x_up, char *name) * @see xml_find_value */ char * -xml_find_body(cxobj *xn, char *name) +xml_find_body(cxobj *xn, + char *name) { cxobj *x; @@ -826,7 +841,8 @@ clicon_xml2cbuf(cbuf *cb, * @see clicon_xml_parse_file clicon_xml_parse_string */ static int -xml_parse(char **str, cxobj *x_up) +xml_parse(char **str, + cxobj *x_up) { int retval = -1; struct xml_parse_yacc_arg ya = {0,}; @@ -853,7 +869,9 @@ xml_parse(char **str, cxobj *x_up) * FSM to detect a substring */ static inline int -FSM(char *tag, char ch, int state) +FSM(char *tag, + char ch, + int state) { if (tag[state] == ch) return state+1; @@ -977,7 +995,8 @@ clicon_xml_parse_string(char **str, /*! Copy single xml node without copying children */ static int -copy_one(cxobj *xn0, cxobj *xn1) +copy_one(cxobj *xn0, + cxobj *xn1) { xml_type_set(xn1, xml_type(xn0)); if (xml_value(xn0)){ /* malloced string */ @@ -1002,7 +1021,8 @@ copy_one(cxobj *xn0, cxobj *xn1) * @endcode */ int -xml_copy(cxobj *x0, cxobj *x1) +xml_copy(cxobj *x0, + cxobj *x1) { int retval = -1; cxobj *x; @@ -1164,3 +1184,93 @@ xml_apply_ancestor(cxobj *xn, done: return retval; } + +/*! Generic parse function for xml values + * @param[in] xb xml tree body node, ie containing a value to be parsed + * @param[in] type Type of value to be parsed in value + * @param[out] cvp CLIgen variable containing the parsed value + * @note free cv with cv_free after use. + * @see xml_body_int32 etc, for type-specific parse functions + */ +int +xml_body_parse(cxobj *xb, + enum cv_type type, + cg_var **cvp) +{ + int retval = -1; + cg_var *cv = NULL; + int cvret; + char *bstr; + char *reason = NULL; + + if ((bstr = xml_body(xb)) == NULL){ + clicon_err(OE_XML, 0, "No body found"); + goto done; + } + if ((cv = cv_new(type)) == NULL){ + clicon_err(OE_XML, errno, "cv_new"); + goto done; + } + if ((cvret = cv_parse1(bstr, cv, &reason)) < 0){ + clicon_err(OE_XML, errno, "cv_parse"); + goto done; + } + if (cvret == 0){ /* parsing failed */ + clicon_err(OE_XML, errno, "Parsing CV: %s", &reason); + if (reason) + free(reason); + } + *cvp = cv; + retval = 0; + done: + if (retval < 0 && cv != NULL) + cv_free(cv); + return retval; + +} + +/*! Parse an xml body as int32 + * The real parsing functions are in the cligen code + * @param[in] xb xml tree body node, ie containing a value to be parsed + * @param[out] val Value after parsing + * @retval 0 OK, parsed value in 'val' + * @retval -1 Error, one of: body not found, parse error, + * alloc error. + * @note extend to all other cligen var types and generalize + * @note use yang type info? + */ +int +xml_body_int32(cxobj *xb, + int32_t *val) +{ + cg_var *cv = NULL; + + if (xml_body_parse(xb, CGV_INT32, &cv) < 0) + return -1; + *val = cv_int32_get(cv); + cv_free(cv); + return 0; +} + +/*! Parse an xml body as uint32 + * The real parsing functions are in the cligen code + * @param[in] xb xml tree body node, ie containing a value to be parsed + * @param[out] val Value after parsing + * @retval 0 OK, parsed value in 'val' + * @retval -1 Error, one of: body not found, parse error, + * alloc error. + * @note extend to all other cligen var types and generalize + * @note use yang type info? + */ +int +xml_body_uint32(cxobj *xb, + uint32_t *val) +{ + cg_var *cv = NULL; + + if (xml_body_parse(xb, CGV_UINT32, &cv) < 0) + return -1; + *val = cv_uint32_get(cv); + cv_free(cv); + return 0; +} diff --git a/lib/src/clixon_xml_db.c b/lib/src/clixon_xml_db.c index 688a2646..0ede3ddb 100644 --- a/lib/src/clixon_xml_db.c +++ b/lib/src/clixon_xml_db.c @@ -137,6 +137,8 @@ yang2xmlkeyfmt_1(yang_stmt *ys, cbuf *cb) } /* switch */ retval = 0; done: + if (cvk) + cvec_free(cvk); return retval; } @@ -154,8 +156,10 @@ yang2xmlkeyfmt(yang_stmt *ys, char **xkfmt) int retval = -1; cbuf *cb = NULL; - if ((cb = cbuf_new()) == NULL) + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; + } if (yang2xmlkeyfmt_1(ys, cb) < 0) goto done; if ((*xkfmt = strdup(cbuf_get(cb))) == NULL){ @@ -208,8 +212,10 @@ xmlkeyfmt2key(char *xkfmt, // goto done; } #endif - if ((cb = cbuf_new()) == NULL) + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; + } j = 1; /* j==0 is cli string */ for (i=0; imodule->syntaxnode - */ + */ if (nvec < 2){ clicon_err(OE_XML, 0, "Malformed key: %s", xk); goto done; @@ -645,26 +640,47 @@ get(char *dbname, /* The value is a list of keys: [ ]* */ if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) goto done; + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } cvi = NULL; /* Iterate over individual yang keys */ - while ((cvi = cvec_each(cvk, cvi)) != NULL) { - keyname = cv_string_get(cvi); + cprintf(cb, "%s", name); + while ((cvi = cvec_each(cvk, cvi)) != NULL){ i++; if (i>=nvec){ clicon_err(OE_XML, errno, "List %s without argument", name); goto done; } arg = vec[i]; - if (create_keyvalues(x, - y, - ykey, - name, - arg, - keyname, - &xc) < 0) + cprintf(cb, "[%s=%s]", cv_string_get(cvi), arg); + } + if ((xc = xpath_first(x, cbuf_get(cb))) == NULL){ + if ((xc = xml_new_spec(name, x, y)) == NULL) goto done; - x = xc; - } /* while */ + cvi = NULL; + i -= cvec_len(cvk); + /* Iterate over individual yang keys */ + while ((cvi = cvec_each(cvk, cvi)) != NULL) { + keyname = cv_string_get(cvi); + i++; + arg = vec[i]; + if (create_keyvalues(xc, + ykey, + arg, + keyname) < 0) + goto done; + } /* while */ + } + if (cb){ + cbuf_free(cb); + cb = NULL; + } + if (cvk){ + cvec_free(cvk); + cvk = NULL; + } break; case Y_LEAF: case Y_CONTAINER: @@ -690,6 +706,8 @@ get(char *dbname, retval = 0; done: unchunk_group(__FUNCTION__); + if (cvk) + cvec_free(cvk); return retval; } @@ -705,15 +723,15 @@ xml_sanity(cxobj *x, ys = (yang_stmt*)xml_spec(x); if (ys==NULL){ clicon_err(OE_XML, 0, "No spec for xml node %s", xml_name(x)); - return -1; + goto done; } - if (strcmp(xml_name(x), ys->ys_argument)){ + if (strstr(ys->ys_argument, xml_name(x))==NULL){ clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'", xml_name(x), ys->ys_argument); - return -1; + goto done; } retval = 0; - // done: + done: return retval; } @@ -1063,8 +1081,10 @@ put(char *dbname, if (get_operation(xt, &op) < 0) goto done; body = xml_body(xt); - if ((cbxk = cbuf_new()) == NULL) + if ((cbxk = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; + } cprintf(cbxk, "%s/%s", xk0, xml_name(xt)); switch (ys->ys_keyword){ case Y_LIST: /* Note: can have many keys */ @@ -1231,10 +1251,14 @@ xmldb_put_xkey_local(clicon_handle h, clicon_err(OE_DB, 0, "Invalid key: %s", xk); goto done; } - if ((ckey = cbuf_new()) == NULL) + if ((ckey = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; - if ((csubkey = cbuf_new()) == NULL) + } + if ((csubkey = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; + } if ((vec = clicon_strsplit(xk, "/", &nvec, __FUNCTION__)) == NULL) goto done; if (nvec < 2){ @@ -1304,6 +1328,10 @@ xmldb_put_xkey_local(clicon_handle h, if (db_set(filename, cbuf_get(csubkey), val2, strlen(val2)+1) < 0) goto done; } + if (cvk){ + cvec_free(cvk); + cvk = NULL; + } break; default: if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE) @@ -1341,8 +1369,10 @@ xmldb_put_xkey_local(clicon_handle h, } case OP_REMOVE: /* Read in complete database (this can be optimized) */ - if ((crx = cbuf_new()) == NULL) + if ((crx = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; + } cprintf(crx, "^%s.*$", xk); if ((npairs = db_regexp(filename, cbuf_get(crx), __FUNCTION__, &pairs, 0)) < 0) goto done; @@ -1364,16 +1394,18 @@ xmldb_put_xkey_local(clicon_handle h, cbuf_free(csubkey); if (crx) cbuf_free(crx); + if (cvk) + cvec_free(cvk); unchunk_group(__FUNCTION__); return retval; } /*! Modify database provided an XML database key and an operation - * @param[in] dbname Name of database to search in (filename including dir path) + * @param[in] h CLICON handle + * @param[in] db Database name * @param[in] xk XML Key, eg /aa/bb/17/name * @param[in] val Key value, eg "17" - * @param[in] yspec Yang specification * @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc * @retval 0 OK * @retval -1 Error @@ -1719,7 +1751,7 @@ main(int argc, char **argv) yangmod = argv[4]; db_init(db); if ((yspec = yspec_new()) == NULL) - goto done; + goto done if (yang_parse(h, yangdir, yangmod, NULL, yspec) < 0) goto done; if (strcmp(cmd, "get")==0){ diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index d3c0edc1..2f086fb0 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -513,6 +513,7 @@ xml2cvec(cxobj *xt, /*! Translate a cligen variable vector to an XML tree with depth one * @param[in] cvv CLIgen variable vector. Should be freed by cvec_free() * @param[in] toptag The XML tree in xt will have this XML tag + * @param[in] xt Parent, or NULL * @param[out] xt Pointer to XML tree containing one top node. Should be freed with xml_free * @retval 0 Everything OK, cvv allocated and set * @retval -1 Something wrong, clicon_err() called to set error. No xt returned @@ -522,6 +523,7 @@ xml2cvec(cxobj *xt, int cvec2xml_1(cvec *cvv, char *toptag, + cxobj *xp, cxobj **xt0) { int retval = -1; @@ -536,7 +538,7 @@ cvec2xml_1(cvec *cvv, cv = NULL; while ((cv = cvec_each(cvv, cv)) != NULL) len++; - if ((xt = xml_new(toptag, NULL)) == NULL) + if ((xt = xml_new(toptag, xp)) == NULL) goto err; if (xml_childvec_set(xt, len) < 0) goto err; @@ -672,6 +674,10 @@ xml_diff1(yang_stmt *ys, else if (cxvec_append(x1, first, firstlen) < 0) goto done; + if (cvk){ + cvec_free(cvk); + cvk = NULL; + } break; case Y_CONTAINER: /* Equal regardless */ @@ -764,6 +770,10 @@ xml_diff1(yang_stmt *ys, if (!equal) if (cxvec_append(x2, second, secondlen) < 0) goto done; + if (cvk){ + cvec_free(cvk); + cvk = NULL; + } break; case Y_CONTAINER: /* Equal regardless */ @@ -788,6 +798,8 @@ xml_diff1(yang_stmt *ys, } /* while xt1 */ retval = 0; done: + if (cvk) + cvec_free(cvk); return retval; } diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index 66edeef3..69a387bb 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -23,6 +23,8 @@ * different xpath expressions. */ /* +https://www.w3.org/TR/xpath/ + Implementation of a limited xslt xpath syntax. Some examples. Given the following xml tree: @@ -58,6 +60,15 @@ to the xml standards: 22 (NB spaces) etc For xpath v1.0 see http://www.w3.org/TR/xpath/ +record[name=c][time=d] +in + + c + + 45654df4-2292-45d3-9ca5-ee72452568a8 + + + */ #include #include @@ -121,11 +132,16 @@ static const struct map_str2int atmap[] = { {NULL, -1} }; +struct xpath_predicate{ + struct xpath_predicate *xp_next; + char *xp_expr; +}; + struct xpath_element{ - struct xpath_element *xe_next; - enum axis_type xe_type; - char *xe_str; /* eg for child */ - char *xe_predicate; /* eg within [] */ + struct xpath_element *xe_next; + enum axis_type xe_type; + char *xe_str; /* eg for child */ + struct xpath_predicate *xe_predicate; /* eg within [] */ }; static int xpath_split(char *xpathstr, char **pathexpr); @@ -146,24 +162,64 @@ axis_type2str(enum axis_type type) static int xpath_print(FILE *f, struct xpath_element *xplist) { - struct xpath_element *xe; + struct xpath_element *xe; + struct xpath_predicate *xp; - for (xe=xplist; xe; xe=xe->xe_next) - fprintf(f, "\t:%s %s %s\n", axis_type2str(xe->xe_type), - xe->xe_str?xe->xe_str:"", - xe->xe_predicate?xe->xe_predicate:""); + for (xe=xplist; xe; xe=xe->xe_next){ + fprintf(f, "\t:%s %s ", axis_type2str(xe->xe_type), + xe->xe_str?xe->xe_str:""); + for (xp=xe->xe_predicate; xp; xp=xp->xp_next) + fprintf(f, "[%s]", xp->xp_expr); + } return 0; } +static int +xpath_parse_predicate(struct xpath_element *xe, + char *pred) +{ + int retval = -1; + struct xpath_predicate *xp; + char *s; + int i; + int len; + + len = strlen(pred); + for (i=len-2; i>=0; i--){ /* -2 since we search for ][ */ + s = &pred[i]; + if (i==0 || + (*(s)==']' && *(s+1)=='[')){ + if (i) { + *(s)= '\0'; + s += 2; + } + if ((xp = malloc(sizeof(*xp))) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(xp, 0, sizeof(*xp)); + if ((xp->xp_expr = strdup(s)) == NULL){ + clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__); + goto done; + } + xp->xp_next = xe->xe_predicate; + xe->xe_predicate = xp; + } + } + retval = 0; + done: + return retval; +} + static int xpath_element_new(enum axis_type atype, char *str, struct xpath_element ***xpnext) { - int retval = -1; - struct xpath_element *xe; - char *str1 = NULL; - char *pred; + int retval = -1; + struct xpath_element *xe; + char *str1 = NULL; + char *pred; if ((xe = malloc(sizeof(*xe))) == NULL){ clicon_err(OE_UNIX, errno, "malloc"); @@ -176,7 +232,7 @@ xpath_element_new(enum axis_type atype, clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__); goto done; } - if (xpath_split(str1, &pred) < 0) + if (xpath_split(str1, &pred) < 0) /* Can be more predicates */ goto done; if (strlen(str1)){ if ((xe->xe_str = strdup(str1)) == NULL){ @@ -190,9 +246,9 @@ xpath_element_new(enum axis_type atype, goto done; } } - if (pred && strlen(pred) && (xe->xe_predicate = strdup(pred)) == NULL){ - clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__); - goto done; + if (pred && strlen(pred)){ + if (xpath_parse_predicate(xe, pred) < 0) + goto done; } } (**xpnext) = xe; @@ -207,10 +263,16 @@ xpath_element_new(enum axis_type atype, static int xpath_element_free(struct xpath_element *xe) { + struct xpath_predicate *xp; + if (xe->xe_str) free(xe->xe_str); - if (xe->xe_predicate) - free(xe->xe_predicate); + while ((xp = xe->xe_predicate) != NULL){ + xe->xe_predicate = xp->xp_next; + if (xp->xp_expr) + free(xp->xp_expr); + free(xp); + } free(xe); return 0; } @@ -231,7 +293,8 @@ xpath_free(struct xpath_element *xplist) * // is short for /descendant-or-self::node()/ */ static int -xpath_parse(char *xpath, struct xpath_element **xplist0) +xpath_parse(char *xpath, + struct xpath_element **xplist0) { int retval = -1; int nvec = 0; @@ -295,10 +358,10 @@ xpath_parse(char *xpath, struct xpath_element **xplist0) * @param[in] xn_parent Base XML object * @param[in] name shell wildcard pattern to match with node name * @param[in] node_type CX_ELMNT, CX_ATTR or CX_BODY - * @param[in,out] vec1 internal buffers with results - * @param[in,out] vec0 internal buffers with results - * @param[in,out] vec_len internal buffers with length of vec0,vec1 - * @param[in,out] vec_max internal buffers with max of vec0,vec1 + * @param[in,out] vec1 internal buffers with results + * @param[in,out] vec0 internal buffers with results + * @param[in,out] vec_len internal buffers with length of vec0,vec1 + * @param[in,out] vec_max internal buffers with max of vec0,vec1 * returns: * 0 on OK, -1 on error */ @@ -334,8 +397,21 @@ recursive_find(cxobj *xn, return retval; } +/*! XPath predicate expression check + * @param[in] predicate_expression xpath expression as a string + * @param[in] flags Extra xml flag checks that must match (apart from predicate) + * @param[in,out] vec0 Vector or xml nodes that are checked. Not matched are filtered + * @param[in,out] vec0len Length of vector or matches + * On input, vec0 contains a list of xml nodes to match. + * On output, vec0 contains only the subset that matched the epxression. + * The predicate expression is a subset of the standard, namely: + * - @= + * - + * - = + * @see https://www.w3.org/TR/xpath/#predicates + */ static int -xpath_expr(char *e00, +xpath_expr(char *predicate_expression, uint16_t flags, cxobj ***vec0, size_t *vec0len) @@ -354,7 +430,7 @@ xpath_expr(char *e00, char *e0; char *e; - if ((e0 = strdup(e00)) == NULL){ + if ((e0 = strdup(predicate_expression)) == NULL){ clicon_err(OE_UNIX, errno, "strdup"); goto done; } @@ -436,6 +512,7 @@ xpath_expr(char *e00, * @param[in] descendants0 * @param[in] vec0 * @param[in] vec0len + * @param[in] flags * @param[out] vec1 * @param[out] vec1len * XXX: Kommer in i funktionen med vec0, resultatet appendas i vec1 @@ -463,6 +540,7 @@ xpath_find(struct xpath_element *xe, int descendants = 0; cxobj **vec1 = NULL; size_t vec1len = 0; + struct xpath_predicate *xp; if (xe == NULL){ /* append */ @@ -543,9 +621,11 @@ xpath_find(struct xpath_element *xe, } } } - if (xe->xe_predicate) - if (xpath_expr(xe->xe_predicate, flags, &vec0, &vec0len) < 0) + + for (xp = xe->xe_predicate; xp; xp = xp->xp_next){ + if (xpath_expr(xp->xp_expr, flags, &vec0, &vec0len) < 0) goto done; + } if (xpath_find(xe->xe_next, descendants, vec0, vec0len, flags, vec2, vec2len) < 0) @@ -555,13 +635,19 @@ xpath_find(struct xpath_element *xe, return retval; } -/*! Transform eg "a/b[kalle]" -> "a/b" e="kalle" */ +/*! Transform eg "a/b[kalle]" -> "a/b" e="kalle" + * @param[in,out] xpathstr Eg "a/b[kalle]" -> "a/b" + * @param[out] pathexpr Eg "kalle" + * Which also means: + * "a/b[foo][bar]" -> pathexpr: "foo][bar" + * @note destructively modify xpathstr, no new strings allocated + */ static int -xpath_split(char *xpathstr, char **pathexpr) +xpath_split(char *xpathstr, + char **pathexpr) { int retval = -1; int last; - int i; char *pe = NULL; if (strlen(xpathstr)){ @@ -569,19 +655,15 @@ xpath_split(char *xpathstr, char **pathexpr) if (xpathstr[last] == ']'){ xpathstr[last] = '\0'; if (strlen(xpathstr)){ - last = strlen(xpathstr) - 1; /* recompute due to null */ - for (i=last; i>=0; i--){ - if (xpathstr[i] == '['){ - xpathstr[i] = '\0'; - pe = &xpathstr[i+1]; - break; - } - } - if (pe==NULL){ - clicon_err(OE_XML, errno, "%s: mismatched []: %s", __FUNCTION__, xpathstr); - goto done; + if ((pe = index(xpathstr,'[')) != NULL){ + *pe = '\0'; + pe++; } } + if (pe==NULL){ + clicon_err(OE_XML, errno, "%s: mismatched []: %s", __FUNCTION__, xpathstr); + goto done; + } } } retval = 0; @@ -594,6 +676,7 @@ xpath_split(char *xpathstr, char **pathexpr) * @param[in] xpath * @param[in] vec0 * @param[in] vec0len + * @param[in] flags * @param[out] vec * @param[out] veclen */ @@ -778,20 +861,20 @@ xpath_each(cxobj *cxtop, * @param[in] cxtop xml-tree where to search * @param[in] xpath string with XPATH syntax * @param[out] vec vector of xml-trees. Vector must be free():d after use - * @param[out] xv_len returns length of vector in return value + * @param[out] veclen returns length of vector in return value * @retval 0 OK * @retval -1 error. * * @code - * cxobj **xv; - * size_t xlen; - * if (xpath_vec(cxtop, "//symbol/foo", &xv, &xlen) < 0) + * cxobj **vec; + * size_t veclen; + * if (xpath_vec(cxtop, "//symbol/foo", &vec, &veclen) < 0) * got err; - * for (i=0; i] + * Usage: xpath [] * read xml from input * Example compile: - gcc -g -o xpath -I. -I../clicon ./clicon_xsl.c -lclicon -lcligen + gcc -g -o xpath -I. -I../clixon ./clixon_xsl.c -lclixon -lcligen + * Example run: + echo "" | xpath "a" */ #if 0 /* Test program */ @@ -847,7 +932,7 @@ main(int argc, char **argv) cxobj **xv; cxobj *x; cxobj *xn; - int xlen = 0; + size_t xlen = 0; if (argc != 2){ usage(argv[0]); @@ -860,8 +945,8 @@ main(int argc, char **argv) printf("\n"); if (xpath_vec(x, argv[1], &xv, &xlen) < 0) - goto done; - if (xv) + return -1; + if (xv){ for (i=0; i