From 5f7d011654a88adce9ed78b94d8d05871b59df6c Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Thu, 25 Jul 2019 11:39:17 +0000 Subject: [PATCH] memleaks --- CHANGELOG.md | 2 +- apps/restconf/restconf_main.c | 7 +++++-- example/main/README.md | 22 ++++++++++----------- example/main/clixon-example@2019-07-23.yang | 6 +++--- example/main/example_backend.c | 7 +++++-- lib/src/clixon_datastore_write.c | 10 +++++++++- lib/src/clixon_yang.c | 14 ++++++++----- lib/src/clixon_yang_parse.y | 17 ++++++++++++++-- test/test_stream.sh | 8 ++++++++ 9 files changed, 66 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 463ab2c6..fb84956b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ * Restconf monitoring capabilities (RFC Section 9.1) * Added support for Yang extensions * New plugin callback: ca_extension - * Main backend example includes example code on how to implement a Yang extension in a plugin. + * The main example explains how to implement a Yang extension in a backend plugin. ### API changes on existing features (you may need to change your code) * JSON changes diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index e8cbe8a2..40f5b420 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -495,6 +495,7 @@ restconf_main_extension_cb(clicon_handle h, char *modname; yang_stmt *ymod; yang_stmt *yc; + yang_stmt *yn = NULL; ymod = ys_module(yext); modname = yang_argument_get(ymod); @@ -502,9 +503,11 @@ restconf_main_extension_cb(clicon_handle h, if (strcmp(modname, "ietf-restconf") != 0 || strcmp(extname, "yang-data") != 0) goto ok; clicon_debug(1, "%s Enabled extension:%s:%s", __FUNCTION__, modname, extname); - if ((yc = ys_prune(ys, 0)) == NULL) + if ((yc = yang_find(ys, 0, NULL)) == NULL) + goto ok; + if ((yn = ys_dup(yc)) == NULL) goto done; - if (yn_insert(yang_parent_get(ys), yc) < 0) + if (yn_insert(yang_parent_get(ys), yn) < 0) goto done; ok: retval = 0; diff --git a/example/main/README.md b/example/main/README.md index 441e5aee..baa4339d 100644 --- a/example/main/README.md +++ b/example/main/README.md @@ -183,11 +183,11 @@ Restconf support is also supported, see (restc)[../../apps/restconf/README.md]. ## RPC Operations -Clixon implements Yang RPC operations by an extension mechanism. The -extension mechanism enables you to add application-specific -operations. It works by adding user-defined callbacks for added -netconf operations. It is possible to use the extension mechanism -independent of the yang rpc construct, but it is recommended. The example includes an example: +Clixon implements Yang RPC operations by a mechanism that enables you +to add application-specific operations. It works by adding +user-defined callbacks for added netconf operations. It is possible to +use the extension mechanism independent of the yang rpc construct, but +not recommended . The example includes an example: Example using CLI: ``` @@ -273,14 +273,14 @@ The example contains some stubs for authorization according to [RFC8341(NACM)](h ## Extensions -Clixon supports Yang extensions, but you need to write plugin code. +Clixon supports Yang extensions by writing plugin callback code. The example backend implements an "example:e4" Yang extension, as follows: ``` extension e4 { description - "The first child of the ex:e4 (unknown) statement is replaced with its first - child. This means that 'uses bar;' in the ex:e4 statement below is a valid - data node"; + "The first child of the ex:e4 (unknown) statement is inserted into + the module as a regular data statement. This means that 'uses bar;' + in the ex:e4 statement below is a valid data node"; argument arg; } ex:e4 arg1{ @@ -295,8 +295,8 @@ The backend plugin code registers an extension callback in the init struct: The callback then receives a callback on all "unknown" Yang statements during yang parsing. If the extension matches "example:e4", it applies -the extension. In the example, it replaces the "ex:e4" statements with -its first child, making it a proper yang statement. +the extension. In the example, it copies the child of the "ex:e4" statement and +inserts in as a proper yang statement in the example module. ## Systemd diff --git a/example/main/clixon-example@2019-07-23.yang b/example/main/clixon-example@2019-07-23.yang index cfb4c629..da93468f 100644 --- a/example/main/clixon-example@2019-07-23.yang +++ b/example/main/clixon-example@2019-07-23.yang @@ -45,9 +45,9 @@ module clixon-example { /* yang extension implemented by the example backend code. */ extension e4 { description - "The first child of the ex:e4 (unknown) statement is replaced with its first - child. This means that 'uses bar;' in the ex:e4 statement below is a valid - data node"; + "The first child of the ex:e4 (unknown) statement is inserted into + the module as a regular data statement. This means that 'uses bar;' + in the ex:e4 statement below is a valid data node"; argument arg; } grouping bar { diff --git a/example/main/example_backend.c b/example/main/example_backend.c index 5a4f3b29..eac3e9da 100644 --- a/example/main/example_backend.c +++ b/example/main/example_backend.c @@ -369,6 +369,7 @@ example_extension(clicon_handle h, char *modname; yang_stmt *ymod; yang_stmt *yc; + yang_stmt *yn = NULL; ymod = ys_module(yext); modname = yang_argument_get(ymod); @@ -376,9 +377,11 @@ example_extension(clicon_handle h, if (strcmp(modname, "example") != 0 || strcmp(extname, "e4") != 0) goto ok; clicon_debug(1, "%s Enabled extension:%s:%s", __FUNCTION__, modname, extname); - if ((yc = ys_prune(ys, 0)) == NULL) + if ((yc = yang_find(ys, 0, NULL)) == NULL) + goto ok; + if ((yn = ys_dup(yc)) == NULL) goto done; - if (yn_insert(yang_parent_get(ys), yc) < 0) + if (yn_insert(yang_parent_get(ys), yn) < 0) goto done; ok: retval = 0; diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c index 9689ed4e..56fd444a 100644 --- a/lib/src/clixon_datastore_write.c +++ b/lib/src/clixon_datastore_write.c @@ -665,6 +665,7 @@ xmldb_put(clicon_handle h, int permit = 0; /* nacm permit all */ char *format; cvec *nsc = NULL; /* nacm namespace context */ + int firsttime = 0; if (cbret == NULL){ clicon_err(OE_XML, EINVAL, "cbret is NULL"); @@ -686,6 +687,7 @@ xmldb_put(clicon_handle h, } /* If there is no xml x0 tree (in cache), then read it from file */ if (x0 == NULL){ + firsttime++; /* to avoid leakage on error, see fail from text_modify */ if (xmldb_readfile(h, db, yspec, &x0, NULL) < 0) goto done; } @@ -725,8 +727,14 @@ xmldb_put(clicon_handle h, if ((ret = text_modify_top(h, x0, x1, yspec, op, username, xnacm, permit, cbret)) < 0) goto done; /* If xml return - ie netconf error xml tree, then stop and return OK */ - if (ret == 0) + if (ret == 0){ + /* If first time and quit here, x0 is not written back into cache and leaks */ + if (firsttime && x0){ + xml_free(x0); + x0 = NULL; + } goto fail; + } /* Remove NONE nodes if all subs recursively are also NONE */ if (xml_tree_prune_flagged_sub(x0, XML_FLAG_NONE, 0, NULL) <0) diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 90abe7bf..7496f465 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -2589,6 +2589,10 @@ ys_schemanode_check(yang_stmt *ys, yang_stmt *yp; char *arg; enum rfc_6020 keyword; + char **vec = NULL; + char *v; + int nvec; + int i; yp = yang_parent_get(ys); arg = yang_argument_get(ys); @@ -2609,10 +2613,6 @@ ys_schemanode_check(yang_stmt *ys, } break; case Y_UNIQUE:{ - char **vec = NULL; - char *v; - int nvec; - int i; /* Unique: Sec 7.8.3 It takes as an argument a string that contains a space- separated list of schema node identifiers */ if ((vec = clicon_strsep(arg, " \t\n", &nvec)) == NULL) @@ -2643,6 +2643,8 @@ ys_schemanode_check(yang_stmt *ys, } retval = 0; done: + if (vec) + free(vec); return retval; } @@ -3395,7 +3397,7 @@ ys_parse(yang_stmt *ys, * That is, siblings, etc, may not be there. Complete checks are made in * ys_populate instead. * @param[in] ys yang statement - * @param[in] extra Yang extra for cornercases (unknown/extension). + * @param[in] extra Yang extra for cornercases (unknown/extension). Is consumed * * The cv:s created in parse-tree as follows: * fraction-digits : Create cv as uint8, check limits [1:8] (must be made in 1st pass) @@ -3499,6 +3501,8 @@ ys_parse_sub(yang_stmt *ys, } retval = 0; done: + if (extra) + free(extra); return retval; } diff --git a/lib/src/clixon_yang_parse.y b/lib/src/clixon_yang_parse.y index 84c48830..81189eb8 100644 --- a/lib/src/clixon_yang_parse.y +++ b/lib/src/clixon_yang_parse.y @@ -231,12 +231,15 @@ yang_parse_exit(struct clicon_yang_yacc_arg *yy) return 0; } +/*! Pop a yang parse context on stack + * @param[in] yy Yang yacc argument + */ int ystack_pop(struct clicon_yang_yacc_arg *yy) { struct ys_stack *ystack; - if ((ystack = yy->yy_stack) == NULL){ + if ((ystack = yy->yy_stack) == NULL){ clicon_err(OE_YANG, 0, "ystack is NULL"); return -1; } @@ -245,6 +248,10 @@ ystack_pop(struct clicon_yang_yacc_arg *yy) return 0; } +/*! Push a yang parse context on stack + * @param[in] yy Yang yacc argument + * @param[in] yn Yang node to push + */ struct ys_stack * ystack_push(struct clicon_yang_yacc_arg *yy, yang_stmt *yn) @@ -307,7 +314,13 @@ ysp_add(struct clicon_yang_yacc_arg *yy, return NULL; } -/*! combination of ysp_add and ysp_push for sub-modules */ +/*! Add a yang statement to existing top-of-stack and then push it on stack + * + * @param[in] yy Yang yacc argument + * @param[in] keyword Yang keyword + * @param[in] argument Yang argument + * @param[in] extra Yang extra for cornercases (unknown/extension) + */ static yang_stmt * ysp_add_push(struct clicon_yang_yacc_arg *yy, enum rfc_6020 keyword, diff --git a/test/test_stream.sh b/test/test_stream.sh index a4dcd703..70713344 100755 --- a/test/test_stream.sh +++ b/test/test_stream.sh @@ -206,6 +206,8 @@ if [ $nr -lt 1 -o $nr -gt 2 ]; then err 1 "$nr" fi +sleep 2 + # 2c new "2c) start sub 8s - replay from start -8s - expect 3-4 notifications" ret=$($clixon_util_stream -u http://localhost/streams/EXAMPLE -t 10 -s -8) @@ -219,6 +221,8 @@ if [ $nr -lt 3 -o $nr -gt 4 ]; then err 4 "$nr" fi +sleep 2 + # 2d) start sub 8s - replay from start -8s to stop +4s - expect 3 notifications new "2d) start sub 8s - replay from start -8s to stop +4s - expect 3 notifications" ret=$($clixon_util_stream -u http://localhost/streams/EXAMPLE -t 10 -s -30 -e +4) @@ -232,6 +236,8 @@ if [ $nr -lt 4 -o $nr -gt 10 ]; then err 6 "$nr" fi +sleep 2 + # 2e) start sub 8s - replay from -90s w retention 60s - expect 10 notifications new "2e) start sub 8s - replay from -90s w retention 60s - expect 10 notifications" ret=$($clixon_util_stream -u http://localhost/streams/EXAMPLE -t 10 -s -90 -e +0) @@ -246,6 +252,8 @@ if [ $nr -lt 9 -o $nr -gt 14 ]; then err 10 "$nr" fi +sleep 2 + # Try parallell # start background job curl -s -X GET -H "Accept: text/event-stream" -H "Cache-Control: no-cache" -H "Connection: keep-alive" "http://localhost/streams/EXAMPLE" > /dev/null &