From 4758c902f7c572d4512a4ebe738bbbe05a3a6cb9 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Tue, 9 Nov 2021 13:17:37 +0100 Subject: [PATCH 01/70] server UPDATE support for latest sysrepo & lnc2 --- CMakeLists.txt | 13 +- CMakeModules/FindLibNETCONF2.cmake | 21 ++- README.md | 16 +- scripts/setup.sh | 8 +- src/common.c | 63 +++++-- src/common.h | 10 ++ src/err_netconf.c | 237 +++++-------------------- src/main.c | 155 ++++++++++------ src/netconf.c | 74 ++++---- src/netconf_acm.c | 184 +++++++++---------- src/netconf_acm.h | 12 +- src/netconf_confirmed_commit.c | 36 ++-- src/netconf_monitoring.c | 8 +- src/netconf_server.c | 3 +- src/netconf_server_ssh.c | 6 +- src/netconf_server_tls.c | 19 +- src/netconf_subscribed_notifications.c | 68 ++++--- src/subscribed_notifications.c | 30 ++-- src/yang_push.c | 71 +++++--- src/yang_push.h | 2 +- tests/np_test.c | 49 ++++- tests/np_test.h | 4 +- tests/test_candidate.c | 52 ++---- tests/test_confirmed_commit.c | 72 ++++---- tests/test_edit.c | 75 +++----- tests/test_filter.c | 75 +++----- tests/test_nacm.c | 65 +++---- tests/test_parallel_sessions.c | 21 ++- tests/test_rpc.c | 56 ++---- tests/test_sub_ntf.c | 74 +++----- tests/test_sub_ntf_advanced.c | 109 +++++------- tests/test_sub_ntf_filter.c | 85 ++++----- tests/test_subscribe_filter.c | 84 ++++----- tests/test_subscribe_param.c | 82 ++++----- tests/test_url.c | 58 +++--- tests/test_with_defaults.c | 46 ++--- tests/test_yang_push.c | 55 ++---- tests/test_yang_push_advanced.c | 55 ++---- 38 files changed, 919 insertions(+), 1234 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bc206860..e53661ce4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,12 +48,15 @@ set(NP2SRV_VERSION 2.0.35) # libyang required SO version set(LIBYANG_DEP_SOVERSION_MAJOR 2) +# libnetconf2 required SO version +set(LIBNETCONF2_DEP_SOVERSION_MAJOR 3) + # Version of sysrepo that this netopeer2 version depends on -set(SYSREPO_DEP_VERSION 2.0.33) -set(SYSREPO_DEP_SOVERSION 6.4.0) -set(SYSREPO_DEP_SOVERSION_MAJOR 6) +set(SYSREPO_DEP_VERSION 2.1.0) +set(SYSREPO_DEP_SOVERSION 7.0.0) +set(SYSREPO_DEP_SOVERSION_MAJOR 7) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=gnu99") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=gnu99") # # options @@ -212,7 +215,7 @@ if(LIBSSH_FOUND) endif() # dependencies - libnetconf2 (now, because we need to configure outselves based on it) -find_package(LibNETCONF2 REQUIRED) +find_package(LibNETCONF2 ${LIBNETCONF2_DEP_SOVERSION_MAJOR} REQUIRED) include_directories(${LIBNETCONF2_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBNETCONF2_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBNETCONF2_LIBRARIES}) diff --git a/CMakeModules/FindLibNETCONF2.cmake b/CMakeModules/FindLibNETCONF2.cmake index ebacf630a..b400dbb84 100644 --- a/CMakeModules/FindLibNETCONF2.cmake +++ b/CMakeModules/FindLibNETCONF2.cmake @@ -4,11 +4,12 @@ # LIBNETCONF2_FOUND - system has LibNETCONF2 # LIBNETCONF2_INCLUDE_DIRS - the LibNETCONF2 include directory # LIBNETCONF2_LIBRARIES - Link these to use LibNETCONF2 +# LIBNETCONF2_VERSION - SO version of the found libNETCONF2 library # LIBNETCONF2_ENABLED_SSH - LibNETCONF2 was compiled with SSH support # LIBNETCONF2_ENABLED_TLS - LibNETCONF2 was compiled with TLS support # # Author Michal Vasko -# Copyright (c) 2020 CESNET, z.s.p.o. +# Copyright (c) 2021 CESNET, z.s.p.o. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -34,7 +35,6 @@ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # include(FindPackageHandleStandardArgs) -include(CheckSymbolExists) if(LIBNETCONF2_LIBRARIES AND LIBNETCONF2_INCLUDE_DIRS) # in cache already @@ -68,13 +68,28 @@ else() ${CMAKE_INSTALL_PREFIX}/lib ) + if(LIBNETCONF2_INCLUDE_DIR) + find_path(NC_VERSION_PATH "nc_version.h" HINTS ${LIBNETCONF2_INCLUDE_DIR}) + if(NOT NC_VERSION_PATH) + message(STATUS "libnetconf2 version header not found, assuming libnetconf2 is too old and cannot be used!") + set(LIBNETCONF2_INCLUDE_DIR "LIBNETCONF2_INCLUDE_DIR-NOTFOUND") + set(LIBNETCONF2_LIBRARY "LIBNETCONF2_LIBRARY-NOTFOUND") + else() + file(READ "${NC_VERSION_PATH}/nc_version.h" NC_VERSION_FILE) + string(REGEX MATCH "#define NC_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\"" NC_VERSION_MACRO "${NC_VERSION_FILE}") + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LIBNETCONF2_VERSION "${NC_VERSION_MACRO}") + endif() + endif() + set(LIBNETCONF2_INCLUDE_DIRS ${LIBNETCONF2_INCLUDE_DIR}) set(LIBNETCONF2_LIBRARIES ${LIBNETCONF2_LIBRARY}) mark_as_advanced(LIBNETCONF2_INCLUDE_DIRS LIBNETCONF2_LIBRARIES) # handle the QUIETLY and REQUIRED arguments and set SYSREPO_FOUND to TRUE # if all listed variables are TRUE - find_package_handle_standard_args(LibNETCONF2 DEFAULT_MSG LIBNETCONF2_LIBRARY LIBNETCONF2_INCLUDE_DIR) + find_package_handle_standard_args(LibNETCONF2 FOUND_VAR LIBNETCONF2_FOUND + REQUIRED_VARS LIBNETCONF2_LIBRARY LIBNETCONF2_INCLUDE_DIR + VERSION_VAR LIBNETCONF2_VERSION) # check the configured options and make them available through cmake list(INSERT CMAKE_REQUIRED_INCLUDES 0 "${LIBNETCONF2_INCLUDE_DIR}") diff --git a/README.md b/README.md index 187694cf8..a7f06875c 100644 --- a/README.md +++ b/README.md @@ -105,20 +105,8 @@ a table with the exact data format. | 0 | `uint32_t` | NETCONF session ID | | 1 | `char *` | NETCONF username | -It is also possible to communicate a specific NETCONF error back to the server. The error format must be `NETCONF` -and the meaning of every piece of data corresponds to the [rpc-error](https://tools.ietf.org/html/rfc6241#section-4.3) -elements. All the expected types are strings (`char *`). Arbitrary optional elements can be skipped by being set to -an empty string. - -| Index | Mandatory | Name | -|:----- |:---------:|:----:| -| 0 | yes | `error-type` | -| 1 | yes | `error-tag` | -| 2 | yes | `error-message` | -| 3 | no | `error-app-tag` | -| 4 | no | `error-path` | -| n | no | `error-info` element | -| n + 1 | no | `error-info` value | +It is also possible to communicate a specific `NETCONF` error back to the server, use *sysrepo* utility functions +to create it. ### CLI diff --git a/scripts/setup.sh b/scripts/setup.sh index 0468753d2..0b2b5fc66 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -46,7 +46,7 @@ MODULES=( # functions INSTALL_MODULE() { - CMD="'$SYSREPOCTL' -a -i $MODDIR/$1 -s '$MODDIR' -p '$PERMS' -v2" + CMD="'$SYSREPOCTL' -i $MODDIR/$1 -s '$MODDIR' -p '$PERMS' -v2" if [ ! -z ${OWNER} ]; then CMD="$CMD -o '$OWNER'" fi @@ -61,7 +61,7 @@ INSTALL_MODULE() { } UPDATE_MODULE() { - CMD="'$SYSREPOCTL' -a -U $MODDIR/$1 -s '$MODDIR' -p '$PERMS' -v2" + CMD="'$SYSREPOCTL' -U $MODDIR/$1 -s '$MODDIR' -p '$PERMS' -v2" if [ ! -z ${OWNER} ]; then CMD="$CMD -o '$OWNER'" fi @@ -91,7 +91,7 @@ CHANGE_PERMS() { } ENABLE_FEATURE() { - "$SYSREPOCTL" -a -c $1 -e $2 -v2 + "$SYSREPOCTL" -c $1 -e $2 -v2 local rc=$? if [ $rc -ne 0 ]; then exit $rc @@ -122,7 +122,7 @@ for i in "${MODULES[@]}"; do sctl_owner=`echo "$SCTL_MODULE" | sed 's/\([^|]*|\)\{3\} \([^:]*\).*/\2/'` sctl_group=`echo "$SCTL_MODULE" | sed 's/\([^|]*|\)\{3\}[^:]*:\([^ ]*\).*/\2/'` sctl_perms=`echo "$SCTL_MODULE" | sed 's/\([^|]*|\)\{4\} \([^ ]*\).*/\2/'` - if [ "$sctl_perms" != "$PERMS" ] || [ ! -z ${OWNER} -a "$sctl_owner" != "$OWNER" ] || [ ! -z ${GROUP} -a "$sctl_group" != "$GROUP" ]; then + if [ "$sctl_perms" != "$PERMS" ] || [ ! -z "${OWNER}" -a "$sctl_owner" != "$OWNER" ] || [ ! -z "${GROUP}" -a "$sctl_group" != "$GROUP" ]; then # change permissions/owner CHANGE_PERMS "$name" fi diff --git a/src/common.c b/src/common.c index f5250c3b9..26d15c28b 100644 --- a/src/common.c +++ b/src/common.c @@ -243,6 +243,18 @@ np_ly_mod_has_data(const struct lys_module *mod, uint32_t config_mask) return 0; } +const struct ly_ctx * +np2srv_acquire_ctx_cb(void *cb_data) +{ + return sr_acquire_context(cb_data); +} + +void +np2srv_release_ctx_cb(void *cb_data) +{ + sr_release_context(cb_data); +} + int np2srv_new_session_cb(const char *UNUSED(client_name), struct nc_session *new_session) { @@ -250,6 +262,7 @@ np2srv_new_session_cb(const char *UNUSED(client_name), struct nc_session *new_se sr_val_t *event_data; sr_session_ctx_t *sr_sess = NULL; struct np2_user_sess *user_sess = NULL; + const struct ly_ctx *ly_ctx; const struct lys_module *mod; char *host = NULL; uint32_t nc_id; @@ -296,7 +309,8 @@ np2srv_new_session_cb(const char *UNUSED(client_name), struct nc_session *new_se goto error; } - if ((mod = ly_ctx_get_module_implemented(sr_get_context(np2srv.sr_conn), "ietf-netconf-notifications"))) { + ly_ctx = sr_acquire_context(np2srv.sr_conn); + if ((mod = ly_ctx_get_module_implemented(ly_ctx, "ietf-netconf-notifications"))) { /* generate ietf-netconf-notification's netconf-session-start event for sysrepo */ if (nc_session_get_ti(new_session) != NC_TI_UNIX) { host = (char *)nc_session_get_host(new_session); @@ -313,8 +327,8 @@ np2srv_new_session_cb(const char *UNUSED(client_name), struct nc_session *new_se event_data[2].type = SR_STRING_T; event_data[2].data.string_val = host; } - c = sr_event_notif_send(np2srv.sr_sess, "/ietf-netconf-notifications:netconf-session-start", event_data, - host ? 3 : 2, np2srv.sr_timeout, 1); + c = sr_notif_send(np2srv.sr_sess, "/ietf-netconf-notifications:netconf-session-start", event_data, host ? 3 : 2, + np2srv.sr_timeout, 1); if (c != SR_ERR_OK) { WRN("Failed to send a notification (%s).", sr_strerror(c)); } else { @@ -322,6 +336,7 @@ np2srv_new_session_cb(const char *UNUSED(client_name), struct nc_session *new_se } free(event_data); } + sr_release_context(np2srv.sr_conn); return 0; @@ -470,31 +485,31 @@ url_open(const char *url) struct lyd_node * op_parse_url(const char *url, uint32_t parse_options, int *rc, sr_session_ctx_t *sr_sess) { - struct lyd_node *config, *data; + struct lyd_node *config, *data = NULL; struct ly_ctx *ly_ctx; struct lyd_node_opaq *opaq; int fd; - ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); + ly_ctx = (struct ly_ctx *)sr_acquire_context(np2srv.sr_conn); fd = url_open(url); if (fd == -1) { *rc = SR_ERR_INVAL_ARG; sr_session_set_error_message(sr_sess, "Could not open URL."); - return NULL; + goto cleanup; } /* load the whole config element */ if (lyd_parse_data_fd(ly_ctx, fd, LYD_XML, parse_options, 0, &config)) { *rc = SR_ERR_LY; sr_session_set_error_message(sr_sess, ly_errmsg(ly_ctx)); - return NULL; + goto cleanup; } if (!config || config->schema) { *rc = SR_ERR_UNSUPPORTED; sr_session_set_error_message(sr_sess, "Missing top-level \"config\" element in URL data."); - return NULL; + goto cleanup; } opaq = (struct lyd_node_opaq *)config; @@ -502,12 +517,15 @@ op_parse_url(const char *url, uint32_t parse_options, int *rc, sr_session_ctx_t *rc = SR_ERR_UNSUPPORTED; sr_session_set_error_message(sr_sess, "Invalid top-level element in URL data, expected \"config\" with " "namespace \"urn:ietf:params:xml:ns:netconf:base:1.0\"."); - return NULL; + goto cleanup; } data = opaq->child; lyd_unlink_siblings(data); lyd_free_tree(config); + +cleanup: + sr_release_context(np2srv.sr_conn); return data; } @@ -520,14 +538,16 @@ op_export_url(const char *url, struct lyd_node *data, uint32_t print_options, in char curl_buffer[CURL_ERROR_SIZE], *str_data; struct lyd_node *config; struct ly_ctx *ly_ctx; + int ret = 0; - ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); + ly_ctx = (struct ly_ctx *)sr_acquire_context(np2srv.sr_conn); /* print the config as expected by the other end */ if (lyd_new_opaq2(NULL, ly_ctx, "config", NULL, NULL, "urn:ietf:params:xml:ns:netconf:base:1.0", &config)) { *rc = SR_ERR_LY; sr_session_set_error_message(sr_sess, ly_errmsg(ly_ctx)); - return -1; + ret = -1; + goto cleanup; } if (data) { lyd_insert_child(config, data); @@ -560,13 +580,17 @@ op_export_url(const char *url, struct lyd_node *data, uint32_t print_options, in ERR("Failed to upload data (curl: %s).", curl_buffer); *rc = SR_ERR_SYS; sr_session_set_error_message(sr_sess, curl_buffer); - return -1; + ret = -1; + goto curl_cleanup; } - /* cleanup */ +curl_cleanup: curl_easy_cleanup(curl); curl_global_cleanup(); - return 0; + +cleanup: + sr_release_context(np2srv.sr_conn); + return ret; } #else @@ -1112,13 +1136,14 @@ op_filter_data_get(sr_session_ctx_t *session, uint32_t max_depth, sr_get_oper_op const struct np2_filter *filter, sr_session_ctx_t *ev_sess, struct lyd_node **data) { const sr_error_info_t *err_info; - struct lyd_node *node; + sr_data_t *sr_data; uint32_t i; int rc; + LY_ERR lyrc; for (i = 0; i < filter->count; ++i) { /* get the selected data */ - rc = sr_get_data(session, filter->filters[i].str, max_depth, np2srv.sr_timeout, get_opts, &node); + rc = sr_get_data(session, filter->filters[i].str, max_depth, np2srv.sr_timeout, get_opts, &sr_data); if (rc) { ERR("Getting data \"%s\" from sysrepo failed (%s).", filter->filters[i].str, sr_strerror(rc)); sr_session_get_error(session, &err_info); @@ -1127,8 +1152,10 @@ op_filter_data_get(sr_session_ctx_t *session, uint32_t max_depth, sr_get_oper_op } /* merge */ - if (lyd_merge_siblings(data, node, LYD_MERGE_DESTRUCT)) { - lyd_free_siblings(node); + lyrc = lyd_merge_siblings(data, sr_data->tree, LYD_MERGE_DESTRUCT); + sr_data->tree = NULL; + sr_release_data(sr_data); + if (lyrc) { return SR_ERR_LY; } } diff --git a/src/common.h b/src/common.h index 19420fba6..87cf6188f 100644 --- a/src/common.h +++ b/src/common.h @@ -164,6 +164,16 @@ int np_ly_mod_has_notif(const struct lys_module *mod); */ int np_ly_mod_has_data(const struct lys_module *mod, uint32_t config_mask); +/** + * @brief NP2 callback for acquiring context. + */ +const struct ly_ctx *np2srv_acquire_ctx_cb(void *cb_data); + +/** + * @brief NP2 callback for releasing context. + */ +void np2srv_release_ctx_cb(void *cb_data); + /** * @brief NP2 callback for a new session creation. * diff --git a/src/err_netconf.c b/src/err_netconf.c index 56096d8ca..cb79cff06 100644 --- a/src/err_netconf.c +++ b/src/err_netconf.c @@ -19,72 +19,39 @@ #include +#include + #include "common.h" #include "compat.h" void np_err_nacm_access_denied(sr_session_ctx_t *ev_sess, const char *module_name, const char *user, const char *path) { - const char *str; char *msg; - int len; - - /* error format */ - sr_session_set_error_format(ev_sess, "NETCONF"); - /* error-type */ - str = "protocol"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); + /* generate message */ + if (asprintf(&msg, "Access to the data model \"%s\" is denied because \"%s\" NACM authorization failed.", + module_name, user) == -1) { + return; + } - /* error-tag */ - str = "access-denied"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); + /* set error */ + sr_session_set_netconf_error(ev_sess, "protocol", "access-denied", NULL, path, msg, 0); - /* error-message */ - len = asprintf(&msg, "Access to the data model \"%s\" is denied because \"%s\" NACM authorization failed.", - module_name, user); - sr_session_push_error_data(ev_sess, len + 1, msg); free(msg); - - /* error-app-tag */ - sr_session_push_error_data(ev_sess, 1, ""); - - /* error-path */ - sr_session_push_error_data(ev_sess, strlen(path) + 1, path); } void np_err_sr2nc_lock_denied(sr_session_ctx_t *ev_sess, const sr_error_info_t *err_info) { struct nc_session *nc_sess; - const char *str, *ptr; + const char *msg, *str, *ptr; char buf[11]; - /* error format */ - sr_session_set_error_format(ev_sess, "NETCONF"); - - /* error-type */ - str = "protocol"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-tag */ - str = "lock-denied"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-message */ - str = "Access to the requested lock is denied because the lock is currently held by another entity."; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-app-tag */ - sr_session_push_error_data(ev_sess, 1, ""); - - /* error-path */ - sr_session_push_error_data(ev_sess, 1, ""); - - /* error-info */ - str = "session-id"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); + /* message */ + msg = "Access to the requested lock is denied because the lock is currently held by another entity."; + /* error info session ID */ str = "DS-locked by session "; ptr = strstr(err_info->err[0].message, str); if (!ptr) { @@ -93,41 +60,22 @@ np_err_sr2nc_lock_denied(sr_session_ctx_t *ev_sess, const sr_error_info_t *err_i np_get_nc_sess_by_id(atoi(ptr + strlen(str)), 0, &nc_sess); sprintf(buf, "%" PRIu32, nc_sess ? nc_session_get_id(nc_sess) : 0); - sr_session_push_error_data(ev_sess, strlen(buf) + 1, buf); + + /* set error */ + sr_session_set_netconf_error(ev_sess, "protocol", "lock-denied", NULL, NULL, msg, 1, "session-id", buf); } void np_err_sr2nc_in_use(sr_session_ctx_t *ev_sess, const sr_error_info_t *err_info) { struct nc_session *nc_sess; - const char *str, *ptr; + const char *msg, *str, *ptr; char buf[11]; - /* error format */ - sr_session_set_error_format(ev_sess, "NETCONF"); - - /* error-type */ - str = "protocol"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-tag */ - str = "in-use"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-message */ - str = "The request requires a resource that already is in use."; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-app-tag */ - sr_session_push_error_data(ev_sess, 1, ""); - - /* error-path */ - sr_session_push_error_data(ev_sess, 1, ""); - - /* error-info */ - str = "session-id"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); + /* message */ + msg = "The request requires a resource that already is in use."; + /* error info session ID */ str = "DS-locked by session "; ptr = strstr(err_info->err[0].message, str); if (!ptr) { @@ -136,157 +84,54 @@ np_err_sr2nc_in_use(sr_session_ctx_t *ev_sess, const sr_error_info_t *err_info) np_get_nc_sess_by_id(atoi(ptr + strlen(str)), 0, &nc_sess); sprintf(buf, "%" PRIu32, nc_sess ? nc_session_get_id(nc_sess) : 0); - sr_session_push_error_data(ev_sess, strlen(buf) + 1, buf); + + /* set error */ + sr_session_set_netconf_error(ev_sess, "protocol", "in-use", NULL, NULL, msg, 1, "session-id", buf); } void np_err_sr2nc_same_ds(sr_session_ctx_t *ev_sess, const char *err_msg) { - const char *str; - - /* error format */ - sr_session_set_error_format(ev_sess, "NETCONF"); - - /* error-type */ - str = "application"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-tag */ - str = "invalid-value"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-message */ - sr_session_push_error_data(ev_sess, strlen(err_msg) + 1, err_msg); - - /* error-app-tag */ - sr_session_push_error_data(ev_sess, 1, ""); - - /* error-path */ - sr_session_push_error_data(ev_sess, 1, ""); + /* set error */ + sr_session_set_netconf_error(ev_sess, "application", "invalid-value", NULL, NULL, err_msg, 0); } void np_err_missing_element(sr_session_ctx_t *ev_sess, const char *elem_name) { - const char *str; - - /* error format */ - sr_session_set_error_format(ev_sess, "NETCONF"); + const char *msg; - /* error-type */ - str = "protocol"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); + /* message */ + msg = "An expected element is missing."; - /* error-tag */ - str = "missing-element"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-message */ - str = "An expected element is missing."; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-app-tag */ - sr_session_push_error_data(ev_sess, 1, ""); - - /* error-path */ - sr_session_push_error_data(ev_sess, 1, ""); - - /* error-info */ - str = "bad-element"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - sr_session_push_error_data(ev_sess, strlen(elem_name) + 1, elem_name); + /* set error */ + sr_session_set_netconf_error(ev_sess, "protocol", "missing-element", NULL, NULL, msg, 1, "bad-element", elem_name); } void np_err_bad_element(sr_session_ctx_t *ev_sess, const char *elem_name, const char *description) { - const char *str; - - /* error format */ - sr_session_set_error_format(ev_sess, "NETCONF"); - - /* error-type */ - str = "protocol"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-tag */ - str = "bad-element"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-message */ - sr_session_push_error_data(ev_sess, strlen(description) + 1, description); - - /* error-app-tag */ - sr_session_push_error_data(ev_sess, 1, ""); - - /* error-path */ - sr_session_push_error_data(ev_sess, 1, ""); - - /* error-info */ - str = "bad-element"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - sr_session_push_error_data(ev_sess, strlen(elem_name) + 1, elem_name); + /* set error */ + sr_session_set_netconf_error(ev_sess, "protocol", "bad-element", NULL, NULL, description, 1, "bad-element", elem_name); } void np_err_invalid_value(sr_session_ctx_t *ev_sess, const char *description, const char *bad_elem_name) { - const char *str; - - /* error format */ - sr_session_set_error_format(ev_sess, "NETCONF"); - - /* error-type */ - str = "application"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-tag */ - str = "invalid-value"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-message */ - sr_session_push_error_data(ev_sess, strlen(description) + 1, description); - - /* error-app-tag */ - sr_session_push_error_data(ev_sess, 1, ""); - - /* error-path */ - sr_session_push_error_data(ev_sess, 1, ""); - if (bad_elem_name) { - /* error-info */ - str = "bad-element"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - sr_session_push_error_data(ev_sess, strlen(bad_elem_name) + 1, bad_elem_name); + /* set error */ + sr_session_set_netconf_error(ev_sess, "application", "invalid-value", NULL, NULL, description, 1, "bad-element", + bad_elem_name); + } else { + /* set error */ + sr_session_set_netconf_error(ev_sess, "application", "invalid-value", NULL, NULL, description, 0); } } void np_err_ntf_sub_no_such_sub(sr_session_ctx_t *ev_sess, const char *message) { - const char *str; - - /* error format */ - sr_session_set_error_format(ev_sess, "NETCONF"); - - /* error-type */ - str = "application"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-tag */ - str = "invalid-value"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-message */ - sr_session_push_error_data(ev_sess, strlen(message) + 1, message); - - /* error-app-tag */ - str = "ietf-subscribed-notifications:no-such-subscription"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-path */ - sr_session_push_error_data(ev_sess, 1, ""); + /* set error */ + sr_session_set_netconf_error(ev_sess, "application", "invalid-value", + "ietf-subscribed-notifications:no-such-subscription", NULL, message, 0); } diff --git a/src/main.c b/src/main.c index 19312873c..caf736469 100644 --- a/src/main.c +++ b/src/main.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "common.h" #include "compat.h" @@ -103,6 +104,7 @@ np2srv_del_session_cb(struct nc_session *session) char *host = NULL; sr_val_t *event_data; struct np2_user_sess *user_sess; + const struct ly_ctx *ly_ctx; const struct lys_module *mod; if (nc_ps_del_session(np2srv.nc_ps, session)) { @@ -119,7 +121,8 @@ np2srv_del_session_cb(struct nc_session *session) /* stop sysrepo session, if no callback is using it */ np_release_user_sess(user_sess); - if ((mod = ly_ctx_get_module_implemented(sr_get_context(np2srv.sr_conn), "ietf-netconf-notifications"))) { + ly_ctx = sr_acquire_context(np2srv.sr_conn); + if ((mod = ly_ctx_get_module_implemented(ly_ctx, "ietf-netconf-notifications"))) { /* generate ietf-netconf-notification's netconf-session-end event for sysrepo */ if (nc_session_get_ti(session) != NC_TI_UNIX) { host = (char *)nc_session_get_host(session); @@ -162,7 +165,7 @@ np2srv_del_session_cb(struct nc_session *session) event_data[i++].data.enum_val = "other"; break; } - rc = sr_event_notif_send(np2srv.sr_sess, "/ietf-netconf-notifications:netconf-session-end", event_data, i, + rc = sr_notif_send(np2srv.sr_sess, "/ietf-netconf-notifications:netconf-session-end", event_data, i, np2srv.sr_timeout, 1); if (rc != SR_ERR_OK) { WRN("Failed to send a notification (%s).", sr_strerror(rc)); @@ -171,6 +174,7 @@ np2srv_del_session_cb(struct nc_session *session) } free(event_data); } + sr_release_context(np2srv.sr_conn); /* stop monitoring and free NC session */ ncm_session_del(session); @@ -187,77 +191,79 @@ static struct lyd_node * np2srv_err_nc(sr_error_info_err_t *err) { struct lyd_node *e = NULL, *err_info = NULL; - const char *err_type, *err_tag, *err_msg, *str, *str2; - uint32_t err_idx; + const struct ly_ctx *ly_ctx; + const char *err_type, *err_tag, *err_app_tag, *err_path, *err_msg, **err_info_elem = NULL, **err_info_val = NULL; + uint32_t err_info_count = 0, i; - /* mandatory */ - err_idx = 0; - if (sr_get_error_data(err, err_idx++, NULL, (const void **)&err_type)) { - WRN("Missing NETCONF error \"error-type\"."); - goto error; - } else if (sr_get_error_data(err, err_idx++, NULL, (const void **)&err_tag)) { - WRN("Missing NETCONF error \"error-tag\"."); - goto error; - } else if (sr_get_error_data(err, err_idx++, NULL, (const void **)&err_msg)) { - WRN("Missing NETCONF error \"error-message\"."); + /* only dictionary used, no need to keep locked */ + ly_ctx = sr_acquire_context(np2srv.sr_conn); + sr_release_context(np2srv.sr_conn); + + /* read the error */ + if (sr_err_get_netconf_error(err, &err_type, &err_tag, &err_app_tag, &err_path, &err_msg, &err_info_elem, + &err_info_val, &err_info_count)) { goto error; } + /* rpc-error */ - if (lyd_new_opaq2(NULL, sr_get_context(np2srv.sr_conn), "rpc-error", NULL, NULL, NC_NS_BASE, &e)) { + if (lyd_new_opaq2(NULL, ly_ctx, "rpc-error", NULL, NULL, NC_NS_BASE, &e)) { goto error; } + /* error-type */ if (lyd_new_opaq2(e, NULL, "error-type", err_type, NULL, NC_NS_BASE, NULL)) { goto error; } + /* error-tag */ if (lyd_new_opaq2(e, NULL, "error-tag", err_tag, NULL, NC_NS_BASE, NULL)) { goto error; } + /* error-severity */ if (lyd_new_opaq2(e, NULL, "error-severity", "error", NULL, NC_NS_BASE, NULL)) { goto error; } - /* error-message */ - if (nc_err_set_msg(e, err_msg, "en")) { - goto error; - } - /* error-app-tag */ - if (sr_get_error_data(err, err_idx++, NULL, (const void **)&str)) { - return e; - } - if (str[0]) { - if (nc_err_set_app_tag(e, str)) { + if (err_app_tag) { + /* error-app-tag */ + if (nc_err_set_app_tag(e, err_app_tag)) { goto error; } } - /* error-path */ - if (sr_get_error_data(err, err_idx++, NULL, (const void **)&str)) { - return e; - } - if (str[0]) { - if (nc_err_set_path(e, str)) { + + if (err_path) { + /* error-path */ + if (nc_err_set_path(e, err_path)) { goto error; } } + + /* error-message */ + if (nc_err_set_msg(e, err_msg, "en")) { + goto error; + } + /* error-info */ - while (!sr_get_error_data(err, err_idx++, NULL, (const void **)&str) && - !sr_get_error_data(err, err_idx++, NULL, (const void **)&str2)) { + for (i = 0; i < err_info_count; ++i) { if (!err_info) { if (lyd_new_opaq2(e, NULL, "error-info", NULL, NULL, NC_NS_BASE, &err_info)) { goto error; } } - if (lyd_new_opaq2(err_info, NULL, str, str2, NULL, NC_NS_BASE, NULL)) { + if (lyd_new_opaq2(err_info, NULL, err_info_elem[i], err_info_val[i], NULL, NC_NS_BASE, NULL)) { goto error; } } + free(err_info_elem); + free(err_info_val); return e; error: lyd_free_tree(e); + free(err_info_elem); + free(err_info_val); return NULL; } @@ -272,6 +278,7 @@ np2srv_err_reply_sr(const sr_error_info_t *err_info) { struct nc_server_reply *reply = NULL; struct lyd_node *e; + const struct ly_ctx *ly_ctx; size_t i; /* try to find a NETCONF error */ @@ -291,9 +298,10 @@ np2srv_err_reply_sr(const sr_error_info_t *err_info) return reply; } + ly_ctx = sr_acquire_context(np2srv.sr_conn); for (i = 0; i < err_info->err_count; ++i) { /* generic error */ - e = nc_err(sr_get_context(np2srv.sr_conn), NC_ERR_OP_FAILED, NC_ERR_TYPE_APP); + e = nc_err(ly_ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP); nc_err_set_msg(e, err_info->err[i].message, "en"); if (reply) { @@ -303,6 +311,7 @@ np2srv_err_reply_sr(const sr_error_info_t *err_info) } e = NULL; } + sr_release_context(np2srv.sr_conn); return reply; } @@ -322,7 +331,8 @@ np2srv_rpc_cb(struct lyd_node *rpc, struct nc_session *ncs) struct lyd_node *node; const sr_error_info_t *err_info; struct nc_server_reply *reply = NULL; - struct lyd_node *output, *child = NULL; + struct lyd_node *child = NULL; + sr_data_t *output; NC_WD_MODE nc_wd; struct lyd_node *e; char *str; @@ -362,7 +372,7 @@ np2srv_rpc_cb(struct lyd_node *rpc, struct nc_session *ncs) /* build RPC Reply */ if (output) { - LY_LIST_FOR(lyd_child(output), child) { + LY_LIST_FOR(lyd_child(output->tree), child) { if (!(child->flags & LYD_DEFAULT)) { break; } @@ -394,12 +404,13 @@ np2srv_rpc_cb(struct lyd_node *rpc, struct nc_session *ncs) nc_server_get_capab_withdefaults(&nc_wd, NULL); } - reply = nc_server_reply_data(output, nc_wd, NC_PARAMTYPE_FREE); + reply = nc_server_reply_data(output->tree, nc_wd, NC_PARAMTYPE_FREE); + output->tree = NULL; } else { - lyd_free_siblings(output); reply = nc_server_reply_ok(); } + sr_release_data(output); return reply; } @@ -468,7 +479,7 @@ np2srv_check_schemas(sr_session_ctx_t *sr_sess) return -1; \ } - ly_ctx = sr_get_context(sr_session_get_connection(sr_sess)); + ly_ctx = sr_session_acquire_context(sr_sess); /* check that internally used schemas are implemented and with required features: ietf-netconf, ... */ mod_name = "ietf-netconf"; @@ -512,6 +523,7 @@ np2srv_check_schemas(sr_session_ctx_t *sr_sess) NP2_CHECK_FEATURE("ssh-listen"); NP2_CHECK_FEATURE("ssh-call-home"); + sr_session_release_context(sr_sess); return 0; } @@ -535,7 +547,6 @@ np2srv_content_id_cb(void *UNUSED(user_data)) static int server_init(void) { - const struct ly_ctx *ly_ctx; int rc; /* connect to sysrepo */ @@ -551,8 +562,6 @@ server_init(void) WRN("Unable to set diff check callback (%s), NACM will not be applied when editing data.", sr_strerror(rc)); } - ly_ctx = sr_get_context(np2srv.sr_conn); - /* set the content-id callback */ nc_server_set_content_id_clb(np2srv_content_id_cb, NULL, NULL); @@ -574,8 +583,8 @@ server_init(void) /* init NACM */ ncac_init(); - /* init libnetconf2 (it modifies only the dictionary) */ - if (nc_server_init((struct ly_ctx *)ly_ctx)) { + /* init libnetconf2 */ + if (nc_server_init()) { goto error; } @@ -658,6 +667,7 @@ server_destroy(void) sess = nc_ps_get_session(np2srv.nc_ps, 0); nc_session_set_term_reason(sess, NC_SESSION_TERM_OTHER); np2srv_del_session_cb(sess); + sr_release_context(np2srv.sr_conn); } nc_ps_free(np2srv.nc_ps); } @@ -682,7 +692,7 @@ server_destroy(void) /* ietf-subscribed-notifications cleanup */ np2srv_sub_ntf_destroy(); - /* removes the context and clears all the sessions */ + /* disconnects and clears all the sessions */ sr_disconnect(np2srv.sr_conn); } @@ -709,7 +719,7 @@ server_rpc_subscribe(void) int rc; #define SR_RPC_SUBSCR(xpath, cb) \ - rc = sr_rpc_subscribe_tree(np2srv.sr_sess, xpath, cb, NULL, 0, SR_SUBSCR_CTX_REUSE, &np2srv.sr_rpc_sub); \ + rc = sr_rpc_subscribe_tree(np2srv.sr_sess, xpath, cb, NULL, 0, 0, &np2srv.sr_rpc_sub); \ if (rc != SR_ERR_OK) { \ ERR("Subscribing for \"%s\" RPC failed (%s).", xpath, sr_strerror(rc)); \ goto error; \ @@ -770,7 +780,7 @@ server_data_subscribe(void) int rc; #define SR_OPER_SUBSCR(mod_name, xpath, cb) \ - rc = sr_oper_get_items_subscribe(np2srv.sr_sess, mod_name, xpath, cb, NULL, SR_SUBSCR_CTX_REUSE, &np2srv.sr_data_sub); \ + rc = sr_oper_get_subscribe(np2srv.sr_sess, mod_name, xpath, cb, NULL, 0, &np2srv.sr_data_sub); \ if (rc != SR_ERR_OK) { \ ERR("Subscribing for providing \"%s\" state data failed (%s).", mod_name, sr_strerror(rc)); \ goto error; \ @@ -778,7 +788,7 @@ server_data_subscribe(void) #define SR_CONFIG_SUBSCR(mod_name, xpath, cb) \ rc = sr_module_change_subscribe(np2srv.sr_sess, mod_name, xpath, cb, NULL, 0, \ - SR_SUBSCR_CTX_REUSE | SR_SUBSCR_DONE_ONLY | SR_SUBSCR_ENABLED, &np2srv.sr_data_sub); \ + SR_SUBSCR_DONE_ONLY | SR_SUBSCR_ENABLED, &np2srv.sr_data_sub); \ if (rc != SR_ERR_OK) { \ ERR("Subscribing for \"%s\" data changes failed (%s).", mod_name, sr_strerror(rc)); \ goto error; \ @@ -954,6 +964,39 @@ server_data_subscribe(void) return -1; } +/** + * @brief Accept new NETCONF session. + */ +static void +server_accept_session(void) +{ + NC_MSG_TYPE msgtype; + const struct ly_ctx *ly_ctx; + struct nc_session *ncs = NULL; + + if (!nc_server_endpt_count()) { + /* no listening endpoints */ + return; + } + + ly_ctx = sr_acquire_context(np2srv.sr_conn); + if (!ly_ctx) { + ERR("Failed to acquire SR connection context."); + return; + } + + /* accept session */ + msgtype = nc_accept(0, ly_ctx, &ncs); + if ((msgtype == NC_MSG_HELLO) && !np2srv_new_session_cb(NULL, ncs)) { + /* callback success, keep the session with the context lock */ + return; + } + + /* no new session or callback fail, free the session, release context */ + nc_session_free(ncs, NULL); + sr_release_context(np2srv.sr_conn); +} + /** * @brief Server worker thread function. * @@ -973,15 +1016,7 @@ worker_thread(void *arg) while (ATOMIC_LOAD_RELAXED(loop_continue)) { /* try to accept new NETCONF sessions */ - if (nc_server_endpt_count()) { - msgtype = nc_accept(0, &ncs); - if (msgtype == NC_MSG_HELLO) { - if (np2srv_new_session_cb(NULL, ncs)) { - nc_session_free(ncs, NULL); - continue; - } - } - } + server_accept_session(); /* listen for incoming requests on active NETCONF sessions */ rc = nc_ps_poll(np2srv.nc_ps, NP2SRV_POLL_IO_TIMEOUT, &ncs); @@ -1008,6 +1043,7 @@ worker_thread(void *arg) if (rc & NC_PSPOLL_SESSION_TERM) { VRB("Session %d: thread %d event session terminated.", nc_session_get_id(ncs), idx); np2srv_del_session_cb(ncs); + sr_release_context(np2srv.sr_conn); } #ifdef NC_ENABLED_SSH else if (rc & NC_PSPOLL_SSH_CHANNEL) { @@ -1019,6 +1055,9 @@ worker_thread(void *arg) nc_session_free(ncs, NULL); continue; } + + /* for the new session */ + sr_acquire_context(np2srv.sr_conn); } else if (msgtype == NC_MSG_BAD_HELLO) { ncm_bad_hello(ncs); } diff --git a/src/netconf.c b/src/netconf.c index 2c5235807..c2a665607 100644 --- a/src/netconf.c +++ b/src/netconf.c @@ -442,6 +442,7 @@ np2srv_rpc_copyconfig_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), con sr_datastore_t ds = SR_DS_OPERATIONAL, sds = SR_DS_OPERATIONAL; struct ly_set *nodeset = NULL; struct lyd_node *config = NULL; + sr_data_t *sr_data; int rc = SR_ERR_OK, run_to_start = 0, source_is_config = 0; struct np2_user_sess *user_sess = NULL; const char *username; @@ -541,10 +542,13 @@ np2srv_rpc_copyconfig_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), con if (!source_is_config && !run_to_start) { /* get source datastore data and filter them */ sr_session_switch_ds(session, sds); - rc = sr_get_data(session, "/*", 0, np2srv.sr_timeout, 0, &config); + rc = sr_get_data(session, "/*", 0, np2srv.sr_timeout, 0, &sr_data); if (rc != SR_ERR_OK) { goto cleanup; } + config = sr_data->tree; + sr_data->tree = NULL; + sr_release_data(sr_data); sr_session_get_orig_data(session, 1, NULL, (const void **)&username); ncac_check_data_read_filter(&config, username); @@ -726,7 +730,7 @@ np2srv_rpc_un_lock_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const /* sysrepo API */ if (!strcmp(input->schema->name, "lock")) { - rc = sr_lock(user_sess->sess, NULL); + rc = sr_lock(user_sess->sess, NULL, 0); } else if (!strcmp(input->schema->name, "unlock")) { rc = sr_unlock(user_sess->sess, NULL); } @@ -937,10 +941,13 @@ np2srv_rpc_subscribe_ntf_cb(sr_session_ctx_t *UNUSED(session), uint32_t sub_id, struct nc_server_notif *nc_ntf = NULL; struct subscribe_ntf_data *cb_data = private_data; struct lyd_node *ly_ntf = NULL; + const struct ly_ctx *ly_ctx; NC_MSG_TYPE msg_type; char *datetime = NULL; struct timespec stop, cur_ts; + ly_ctx = sr_acquire_context(np2srv.sr_conn); + /* create these notifications, sysrepo only emulates them */ if (notif_type == SR_EV_NOTIF_REPLAY_COMPLETE) { ATOMIC_INC_RELAXED(cb_data->sr_ntf_replay_complete_count); @@ -949,7 +956,7 @@ np2srv_rpc_subscribe_ntf_cb(sr_session_ctx_t *UNUSED(session), uint32_t sub_id, goto cleanup; } - lyd_new_path(NULL, sr_get_context(np2srv.sr_conn), "/nc-notifications:replayComplete", NULL, 0, &ly_ntf); + lyd_new_path(NULL, ly_ctx, "/nc-notifications:replayComplete", NULL, 0, &ly_ntf); notif = ly_ntf; } else if (notif_type == SR_EV_NOTIF_TERMINATED) { sr_notif_sub_get_info(np2srv.sr_notif_sub, sub_id, NULL, NULL, NULL, &stop, NULL); @@ -965,7 +972,7 @@ np2srv_rpc_subscribe_ntf_cb(sr_session_ctx_t *UNUSED(session), uint32_t sub_id, goto cleanup; } - lyd_new_path(NULL, sr_get_context(np2srv.sr_conn), "/nc-notifications:notificationComplete", NULL, 0, &ly_ntf); + lyd_new_path(NULL, ly_ctx, "/nc-notifications:notificationComplete", NULL, 0, &ly_ntf); notif = ly_ntf; } else if ((notif_type == SR_EV_NOTIF_MODIFIED) || (notif_type == SR_EV_NOTIF_RESUMED) || (notif_type == SR_EV_NOTIF_SUSPENDED)) { @@ -1006,6 +1013,7 @@ np2srv_rpc_subscribe_ntf_cb(sr_session_ctx_t *UNUSED(session), uint32_t sub_id, nc_server_notif_free(nc_ntf); free(datetime); lyd_free_all(ly_ntf); + sr_release_context(np2srv.sr_conn); } int @@ -1152,8 +1160,7 @@ np2srv_rpc_subscribe_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), cons for (idx = 0; idx < mod_set.count; ++idx) { ly_mod = mod_set.objs[idx]; rc = sr_notif_subscribe_tree(user_sess->sess, ly_mod->name, xp, start.tv_sec ? &start : NULL, - stop.tv_sec ? &stop : NULL, np2srv_rpc_subscribe_ntf_cb, cb_data, SR_SUBSCR_CTX_REUSE, - &np2srv.sr_notif_sub); + stop.tv_sec ? &stop : NULL, np2srv_rpc_subscribe_ntf_cb, cb_data, 0, &np2srv.sr_notif_sub); if (rc != SR_ERR_OK) { sr_session_get_error(user_sess->sess, &err_info); sr_session_set_error_message(session, err_info->err[0].message); @@ -1164,7 +1171,7 @@ np2srv_rpc_subscribe_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), cons /* subscribe to the specific module (stream) */ cb_data->sr_sub_count = 1; rc = sr_notif_subscribe_tree(user_sess->sess, stream, xp, start.tv_sec ? &start : NULL, stop.tv_sec ? &stop : NULL, - np2srv_rpc_subscribe_ntf_cb, cb_data, SR_SUBSCR_CTX_REUSE, &np2srv.sr_notif_sub); + np2srv_rpc_subscribe_ntf_cb, cb_data, 0, &np2srv.sr_notif_sub); if (rc != SR_ERR_OK) { sr_session_get_error(user_sess->sess, &err_info); sr_session_set_error_message(session, err_info->err[0].message); @@ -1194,16 +1201,17 @@ np2srv_nc_ntf_oper_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const const char *UNUSED(path), const char *UNUSED(request_xpath), uint32_t UNUSED(request_id), struct lyd_node **parent, void *UNUSED(private_data)) { - struct lyd_node *root, *stream, *sr_data = NULL, *sr_mod, *rep_sup; - sr_conn_ctx_t *conn; + struct lyd_node *root, *stream; const struct ly_ctx *ly_ctx; - const struct lys_module *mod; - const char *mod_name; + const struct lys_module *ly_mod; char *buf; - int rc; + uint32_t idx = 0; + int enabled; + struct timespec earliest_notif; - conn = sr_session_get_connection(session); - ly_ctx = sr_get_context(conn); + /* context is locked by the callback anyway */ + ly_ctx = sr_session_acquire_context(session); + sr_session_release_context(session); if (lyd_new_path(NULL, ly_ctx, "/nc-notifications:netconf/streams", NULL, 0, &root)) { goto error; @@ -1222,42 +1230,30 @@ np2srv_nc_ntf_oper_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const goto error; } - /* go through all the sysrepo modules */ - rc = sr_get_module_info(conn, &sr_data); - if (rc != SR_ERR_OK) { - ERR("Failed to get sysrepo module info data (%s).", sr_strerror(rc)); - goto error; - } - LY_LIST_FOR(lyd_child(sr_data), sr_mod) { - if (strcmp(sr_mod->schema->name, "module")) { - continue; - } - - mod_name = lyd_get_value(lyd_child(sr_mod)); - - /* get the module */ - mod = ly_ctx_get_module_implemented(ly_ctx, mod_name); - assert(mod); - - if (!np_ly_mod_has_notif(mod)) { - /* no notifications in the module so do not consider it a stream */ + /* go through all the modules */ + while ((ly_mod = ly_ctx_get_module_iter(ly_ctx, &idx))) { + if (!ly_mod->implemented || !np_ly_mod_has_notif(ly_mod)) { + /* not implemented or no notifications in the module so do not consider it a stream */ continue; } /* generate information about the stream/module */ - if (lyd_new_list(lyd_child(root), NULL, "stream", 0, &stream, mod_name)) { + if (lyd_new_list(lyd_child(root), NULL, "stream", 0, &stream, ly_mod->name)) { goto error; } if (lyd_new_term(stream, NULL, "description", "Stream with all the notifications of a module.", 0, NULL)) { goto error; } - lyd_find_path(sr_mod, "replay-support", 0, &rep_sup); - if (lyd_new_term(stream, NULL, "replaySupport", rep_sup ? "true" : "false", 0, NULL)) { + /* learn whether replay is supported */ + if (sr_get_module_replay_support(sr_session_get_connection(session), ly_mod->name, &earliest_notif, &enabled)) { + goto error; + } + if (lyd_new_term(stream, NULL, "replaySupport", enabled ? "true" : "false", 0, NULL)) { goto error; } - if (rep_sup) { - ly_time_time2str(((struct lyd_node_term *)rep_sup)->value.uint64, NULL, &buf); + if (enabled) { + ly_time_ts2str(&earliest_notif, &buf); if (lyd_new_term(stream, NULL, "replayLogCreationTime", buf, 0, NULL)) { free(buf); goto error; @@ -1266,12 +1262,10 @@ np2srv_nc_ntf_oper_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const } } - lyd_free_siblings(sr_data); *parent = root; return SR_ERR_OK; error: lyd_free_tree(root); - lyd_free_siblings(sr_data); return SR_ERR_INTERNAL; } diff --git a/src/netconf_acm.c b/src/netconf_acm.c index 0fd249392..624b69c24 100644 --- a/src/netconf_acm.c +++ b/src/netconf_acm.c @@ -152,6 +152,20 @@ ncac_oper_cb(sr_session_ctx_t *UNUSED(session), uint32_t UNUSED(sub_id), const c return SR_ERR_OK; } +static struct ncac_group * +ncac_group_find(const char *group_name) +{ + uint32_t i; + + for (i = 0; i < nacm.group_count; ++i) { + if (!strcmp(nacm.groups[i].name, group_name)) { + return &nacm.groups[i]; + } + } + + return NULL; +} + /* /ietf-netconf-acm:nacm/groups/group */ int ncac_group_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNUSED(module_name), const char *xpath, @@ -162,14 +176,11 @@ ncac_group_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UN const struct lyd_node *node; const char *group_name, *user_name; struct ncac_group *group = NULL; - struct ly_ctx *ly_ctx; - uint32_t i, j; + uint32_t i; char *xpath2; int rc; void *mem; - ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); - if (asprintf(&xpath2, "%s//.", xpath) == -1) { EMEM; return SR_ERR_NO_MEMORY; @@ -205,25 +216,18 @@ ncac_group_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UN group = &nacm.groups[nacm.group_count]; ++nacm.group_count; - lydict_insert(ly_ctx, group_name, 0, &group->name); + group->name = strdup(group_name); group->users = NULL; group->user_count = 0; break; case SR_OP_DELETED: /* find it */ - for (i = 0; i < nacm.group_count; ++i) { - /* both in dictionary */ - if (nacm.groups[i].name == group_name) { - group = &nacm.groups[i]; - break; - } - } - assert(i < nacm.group_count); + group = ncac_group_find(group_name); /* delete it */ - lydict_remove(ly_ctx, group->name); - for (j = 0; j < group->user_count; ++j) { - lydict_remove(ly_ctx, group->users[j]); + free(group->name); + for (i = 0; i < group->user_count; ++i) { + free(group->users[i]); } free(group->users); @@ -247,15 +251,7 @@ ncac_group_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UN } else { /* name must be present */ assert(!strcmp(node->parent->child->schema->name, "name")); - group_name = lyd_get_value(node->parent->child); - group = NULL; - for (i = 0; i < nacm.group_count; ++i) { - /* both in dictionary */ - if (nacm.groups[i].name == group_name) { - group = &nacm.groups[i]; - break; - } - } + group = ncac_group_find(lyd_get_value(node->parent->child)); if (!strcmp(node->schema->name, "user-name")) { if ((op == SR_OP_DELETED) && !group) { @@ -275,20 +271,19 @@ ncac_group_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UN return SR_ERR_NO_MEMORY; } group->users = mem; - lydict_insert(ly_ctx, user_name, 0, (const char **)&group->users[group->user_count]); + group->users[group->user_count] = strdup(user_name); ++group->user_count; } else { assert(op == SR_OP_DELETED); for (i = 0; i < group->user_count; ++i) { - /* both in dictionary */ - if (group->users[i] == user_name) { + if (!strcmp(group->users[i], user_name)) { break; } } assert(i < group->user_count); /* delete it */ - lydict_remove(ly_ctx, group->users[i]); + free(group->users[i]); --group->user_count; if (i < group->user_count) { group->users[i] = group->users[group->user_count]; @@ -323,15 +318,12 @@ static void ncac_remove_rules(struct ncac_rule_list *list) { struct ncac_rule *rule, *tmp; - struct ly_ctx *ly_ctx; - - ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); LY_LIST_FOR_SAFE(list->rules, tmp, rule) { - lydict_remove(ly_ctx, rule->name); - lydict_remove(ly_ctx, rule->module_name); - lydict_remove(ly_ctx, rule->target); - lydict_remove(ly_ctx, rule->comment); + free(rule->name); + free(rule->module_name); + free(rule->target); + free(rule->comment); free(rule); } list->rules = NULL; @@ -368,7 +360,7 @@ ncac_sort_strcmp_cb(const void *ptr1, const void *ptr2) } /** - * @brief Find an item in a sorted array. The item structure first member must be (const char *) in the dictionary. + * @brief Find an item in a sorted array. * * @param[in] item Pointer to item to find. * @param[in] item_size Size of an item. @@ -397,10 +389,9 @@ ncac_strarr_sort_find(const char **item, size_t item_size, char **items, uint32_ } /** - * @brief Add an item into a sorted array. The item structure first member must be (const char *) in the dictionary. + * @brief Add an item into a sorted array. * - * @param[in] ly_ctx libyang context. - * @param[in] item Pointer to item to add, string does not need to be in the dictionary. + * @param[in] item Pointer to item to add. * @param[in] item_size Size of an item. * @param[in] check_dup Whether to check for duplicates before adding, returns SR_ERR_OK if duplicate found. * @param[in,out] items Pointer to the item array. @@ -408,8 +399,7 @@ ncac_strarr_sort_find(const char **item, size_t item_size, char **items, uint32_ * @return Sysrepo err value. */ static int -ncac_strarr_sort_add(const struct ly_ctx *ly_ctx, const char **item, size_t item_size, int check_dup, char ***items, - uint32_t *item_count) +ncac_strarr_sort_add(const char **item, size_t item_size, int check_dup, char ***items, uint32_t *item_count) { void *mem; uint32_t i; @@ -453,22 +443,21 @@ ncac_strarr_sort_add(const struct ly_ctx *ly_ctx, const char **item, size_t item } /* insert new item */ - lydict_insert(ly_ctx, *item, 0, (const char **)ITEM_IDX_PTR(*items, item_size, i)); + *ITEM_IDX_PTR(*items, item_size, i) = strdup(*item); ++(*item_count); return SR_ERR_OK; } /** - * @brief Remove an item from a sorted array. The item structure first member must be (const char *) in the dictionary. + * @brief Remove an item from a sorted array. * - * @param[in] ly_ctx libyang context. * @param[in] item Pointer to item to remove. * @param[in] item_size Size of an item. * @param[in,out] items Pointer to the item array. * @param[in,out] item_count Pointer to the number of @p items. */ static void -ncac_strarr_sort_del(const struct ly_ctx *ly_ctx, const char **item, size_t item_size, char ***items, uint32_t *item_count) +ncac_strarr_sort_del(const char **item, size_t item_size, char ***items, uint32_t *item_count) { int32_t i; @@ -477,7 +466,7 @@ ncac_strarr_sort_del(const struct ly_ctx *ly_ctx, const char **item, size_t item assert(i > -1); /* delete it, keep the order */ - lydict_remove(ly_ctx, *ITEM_IDX_PTR(*items, item_size, i)); + free(*ITEM_IDX_PTR(*items, item_size, i)); --(*item_count); if ((uint32_t)i < *item_count) { memmove(ITEM_IDX_PTR(*items, item_size, i), ITEM_IDX_PTR(*items, item_size, i + 1), (*item_count - i) * item_size); @@ -496,15 +485,12 @@ ncac_rule_list_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; - struct ly_ctx *ly_ctx; const char *prev_list, *rlist_name, *group_name; struct ncac_rule_list *rlist = NULL, *prev_rlist; char *xpath2; int rc, len; uint32_t i; - ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); - if (asprintf(&xpath2, "%s//.", xpath) == -1) { EMEM; return SR_ERR_NO_MEMORY; @@ -529,7 +515,7 @@ ncac_rule_list_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char case SR_OP_MOVED: /* find it */ prev_rlist = NULL; - for (rlist = nacm.rule_lists; rlist && (rlist->name != rlist_name); rlist = rlist->next) { + for (rlist = nacm.rule_lists; rlist && strcmp(rlist->name, rlist_name); rlist = rlist->next) { prev_rlist = rlist; } assert(rlist); @@ -552,7 +538,7 @@ ncac_rule_list_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char EMEM; return SR_ERR_NO_MEMORY; } - lydict_insert(ly_ctx, rlist_name, 0, &rlist->name); + rlist->name = strdup(rlist_name); } /* find previous list */ @@ -582,15 +568,15 @@ ncac_rule_list_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char case SR_OP_DELETED: /* find it */ prev_rlist = NULL; - for (rlist = nacm.rule_lists; rlist && (rlist->name != rlist_name); rlist = rlist->next) { + for (rlist = nacm.rule_lists; rlist && strcmp(rlist->name, rlist_name); rlist = rlist->next) { prev_rlist = rlist; } assert(rlist); /* delete it */ - lydict_remove(ly_ctx, rlist->name); + free(rlist->name); for (i = 0; i < rlist->group_count; ++i) { - lydict_remove(ly_ctx, rlist->groups[i]); + free(rlist->groups[i]); } free(rlist->groups); ncac_remove_rules(rlist); @@ -613,7 +599,7 @@ ncac_rule_list_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char /* name must be present */ assert(!strcmp(node->parent->child->schema->name, "name")); rlist_name = lyd_get_value(node->parent->child); - for (rlist = nacm.rule_lists; rlist && (rlist->name != rlist_name); rlist = rlist->next) {} + for (rlist = nacm.rule_lists; rlist && strcmp(rlist->name, rlist_name); rlist = rlist->next) {} if (!strcmp(node->schema->name, "group")) { if ((op == SR_OP_DELETED) && !rlist) { @@ -624,7 +610,7 @@ ncac_rule_list_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char group_name = lyd_get_value(node); if (op == SR_OP_CREATED) { - if ((rc = ncac_strarr_sort_add(ly_ctx, &group_name, sizeof rlist->groups, 0, &rlist->groups, + if ((rc = ncac_strarr_sort_add(&group_name, sizeof rlist->groups, 0, &rlist->groups, &rlist->group_count))) { /* NACM UNLOCK */ pthread_mutex_unlock(&nacm.lock); @@ -632,7 +618,7 @@ ncac_rule_list_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char } } else { assert(op == SR_OP_DELETED); - ncac_strarr_sort_del(ly_ctx, &group_name, sizeof rlist->groups, &rlist->groups, &rlist->group_count); + ncac_strarr_sort_del(&group_name, sizeof rlist->groups, &rlist->groups, &rlist->group_count); } } } @@ -658,15 +644,12 @@ ncac_rule_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNU sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; - struct ly_ctx *ly_ctx; const char *prev_list, *rule_name, *rlist_name, *str; struct ncac_rule_list *rlist; struct ncac_rule *rule = NULL, *prev_rule; char *xpath2; int rc, len; - ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); - if (asprintf(&xpath2, "%s//.", xpath) == -1) { EMEM; return SR_ERR_NO_MEMORY; @@ -686,7 +669,7 @@ ncac_rule_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNU /* find parent rule list */ assert(!strcmp(node->parent->child->schema->name, "name")); rlist_name = lyd_get_value(node->parent->child); - for (rlist = nacm.rule_lists; rlist && (rlist->name != rlist_name); rlist = rlist->next) {} + for (rlist = nacm.rule_lists; rlist && strcmp(rlist->name, rlist_name); rlist = rlist->next) {} if ((op == SR_OP_DELETED) && !rlist) { /* even parent rule-list was deleted */ continue; @@ -701,7 +684,7 @@ ncac_rule_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNU case SR_OP_MOVED: /* find it */ prev_rule = NULL; - for (rule = rlist->rules; rule && (rule->name != rule_name); rule = rule->next) { + for (rule = rlist->rules; rule && strcmp(rule->name, rule_name); rule = rule->next) { prev_rule = rule; } assert(rule); @@ -724,7 +707,7 @@ ncac_rule_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNU pthread_mutex_unlock(&nacm.lock); return SR_ERR_NO_MEMORY; } - lydict_insert(ly_ctx, rule_name, 0, &rule->name); + rule->name = strdup(rule_name); rule->target_type = NCAC_TARGET_ANY; } assert(rule); @@ -756,16 +739,16 @@ ncac_rule_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNU case SR_OP_DELETED: /* find it */ prev_rule = NULL; - for (rule = rlist->rules; rule && (rule->name != rule_name); rule = rule->next) { + for (rule = rlist->rules; rule && strcmp(rule->name, rule_name); rule = rule->next) { prev_rule = rule; } assert(rule); /* delete it */ - lydict_remove(ly_ctx, rule->name); - lydict_remove(ly_ctx, rule->module_name); - lydict_remove(ly_ctx, rule->target); - lydict_remove(ly_ctx, rule->comment); + free(rule->name); + free(rule->module_name); + free(rule->target); + free(rule->comment); if (prev_rule) { prev_rule->next = rule->next; } else { @@ -784,7 +767,7 @@ ncac_rule_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNU /* find parent rule list */ assert(!strcmp(node->parent->parent->child->schema->name, "name")); rlist_name = lyd_get_value(node->parent->parent->child); - for (rlist = nacm.rule_lists; rlist && (rlist->name != rlist_name); rlist = rlist->next) {} + for (rlist = nacm.rule_lists; rlist && strcmp(rlist->name, rlist_name); rlist = rlist->next) {} if ((op == SR_OP_DELETED) && !rlist) { /* even parent rule-list was deleted */ continue; @@ -794,7 +777,7 @@ ncac_rule_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNU /* name must be present */ assert(!strcmp(node->parent->child->schema->name, "name")); rule_name = lyd_get_value(node->parent->child); - for (rule = rlist->rules; rule && (rule->name != rule_name); rule = rule->next) {} + for (rule = rlist->rules; rule && strcmp(rule->name, rule_name); rule = rule->next) {} if ((op == SR_OP_DELETED) && !rule) { /* even parent rule was deleted */ continue; @@ -803,25 +786,25 @@ ncac_rule_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNU if (!strcmp(node->schema->name, "module-name")) { str = lyd_get_value(node); - lydict_remove(ly_ctx, rule->module_name); + free(rule->module_name); if (!strcmp(str, "*")) { rule->module_name = NULL; } else { - lydict_insert(ly_ctx, str, 0, &rule->module_name); + rule->module_name = strdup(str); } } else if (!strcmp(node->schema->name, "rpc-name") || !strcmp(node->schema->name, "notification-name") || !strcmp(node->schema->name, "path")) { if (op == SR_OP_DELETED) { - lydict_remove(ly_ctx, rule->target); + free(rule->target); rule->target = NULL; rule->target_type = NCAC_TARGET_ANY; } else { str = lyd_get_value(node); - lydict_remove(ly_ctx, rule->target); + free(rule->target); if (!strcmp(str, "*")) { rule->target = NULL; } else { - lydict_insert(ly_ctx, str, 0, &rule->target); + rule->target = strdup(str); } if (!strcmp(node->schema->name, "rpc-name")) { rule->target_type = NCAC_TARGET_RPC; @@ -862,12 +845,12 @@ ncac_rule_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNU } } else if (!strcmp(node->schema->name, "comment")) { if (op == SR_OP_DELETED) { - lydict_remove(ly_ctx, rule->comment); + free(rule->comment); rule->comment = NULL; } else { assert((op == SR_OP_MODIFIED) || (op == SR_OP_CREATED)); - lydict_remove(ly_ctx, rule->comment); - lydict_insert(ly_ctx, lyd_get_value(node), 0, &rule->comment); + free(rule->comment); + rule->comment = strdup(lyd_get_value(node)); } } } @@ -896,25 +879,22 @@ ncac_destroy(void) { struct ncac_group *group; struct ncac_rule_list *rule_list, *tmp; - struct ly_ctx *ly_ctx; uint32_t i, j; - ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); - for (i = 0; i < nacm.group_count; ++i) { group = &nacm.groups[i]; - lydict_remove(ly_ctx, group->name); + free(group->name); for (j = 0; j < group->user_count; ++j) { - lydict_remove(ly_ctx, group->users[j]); + free(group->users[j]); } free(group->users); } free(nacm.groups); LY_LIST_FOR_SAFE(nacm.rule_lists, tmp, rule_list) { - lydict_remove(ly_ctx, rule_list->name); + free(rule_list->name); for (i = 0; i < rule_list->group_count; ++i) { - lydict_remove(ly_ctx, rule_list->groups[i]); + free(rule_list->groups[i]); } free(rule_list->groups); ncac_remove_rules(rule_list); @@ -1016,34 +996,30 @@ ncac_allowed_tree(const struct lysc_node *root, const char *user) /** * @brief Collect all NACM groups for a user. If enabled, even system ones. * - * @param[in] ly_ctx libyang context for dictionary. * @param[in] user User to collect groups for. * @param[out] groups Sorted array of collected groups. * @param[out] group_count Number of @p groups. * @return 0 on success, -1 on error. */ static int -ncac_collect_groups(const struct ly_ctx *ly_ctx, const char *user, char ***groups, uint32_t *group_count) +ncac_collect_groups(const char *user, char ***groups, uint32_t *group_count) { struct group grp, *grp_p; gid_t user_gid; - const char *user_dict = NULL; char *buf = NULL; gid_t *gids = NULL; ssize_t buflen; uint32_t i, j; int gid_count = 0, ret, rc = -1; - lydict_insert(ly_ctx, user, 0, &user_dict); - *groups = NULL; *group_count = 0; /* collect NACM groups */ for (i = 0; i < nacm.group_count; ++i) { for (j = 0; j < nacm.groups[i].user_count; ++j) { - if (nacm.groups[i].users[j] == user_dict) { - if (ncac_strarr_sort_add(ly_ctx, &nacm.groups[i].name, sizeof **groups, 0, groups, group_count)) { + if (!strcmp(nacm.groups[i].users[j], user)) { + if (ncac_strarr_sort_add((const char **)&nacm.groups[i].name, sizeof **groups, 0, groups, group_count)) { goto cleanup; } } @@ -1096,7 +1072,7 @@ ncac_collect_groups(const struct ly_ctx *ly_ctx, const char *user, char ***group } /* add, if not already there */ - if (ncac_strarr_sort_add(ly_ctx, (const char **)&grp.gr_name, sizeof **groups, 1, groups, group_count)) { + if (ncac_strarr_sort_add((const char **)&grp.gr_name, sizeof **groups, 1, groups, group_count)) { goto cleanup; } } @@ -1108,7 +1084,6 @@ ncac_collect_groups(const struct ly_ctx *ly_ctx, const char *user, char ***group cleanup: free(gids); free(buf); - lydict_remove(ly_ctx, user_dict); return rc; } @@ -1220,7 +1195,7 @@ ncac_free_groups(char **groups, uint32_t group_count) } for (i = 0; i < group_count; ++i) { - lydict_remove(sr_get_context(np2srv.sr_conn), groups[i]); + free(groups[i]); } free(groups); } @@ -1291,7 +1266,7 @@ ncac_allowed_node(const struct lyd_node *node, const char *node_path, const stru if (node_schema->nodetype != LYS_RPC) { continue; } - if (rule->target && (rule->target != node_schema->name)) { + if (rule->target && strcmp(rule->target, node_schema->name)) { /* exact match needed */ continue; } @@ -1301,7 +1276,7 @@ ncac_allowed_node(const struct lyd_node *node, const char *node_path, const stru if (node_schema->parent || (node_schema->nodetype != LYS_NOTIF)) { continue; } - if (rule->target && (rule->target != node_schema->name)) { + if (rule->target && strcmp(rule->target, node_schema->name)) { /* exact match needed */ continue; } @@ -1334,7 +1309,7 @@ ncac_allowed_node(const struct lyd_node *node, const char *node_path, const stru } /* module name matching, after partial path matches */ - if (rule->module_name && (rule->module_name != node_schema->module->name)) { + if (rule->module_name && strcmp(rule->module_name, node_schema->module->name)) { continue; } @@ -1423,7 +1398,7 @@ ncac_check_operation(const struct lyd_node *data, const char *user) goto cleanup; } - if (ncac_collect_groups(sr_get_context(np2srv.sr_conn), user, &groups, &group_count)) { + if (ncac_collect_groups(user, &groups, &group_count)) { goto cleanup; } @@ -1571,7 +1546,7 @@ ncac_check_data_read_filter(struct lyd_node **data, const char *user) /* NACM LOCK */ pthread_mutex_lock(&nacm.lock); - if (ncac_collect_groups(sr_get_context(np2srv.sr_conn), user, &groups, &group_count)) { + if (ncac_collect_groups(user, &groups, &group_count)) { goto cleanup; } @@ -1642,7 +1617,8 @@ ncac_check_diff_r(const struct lyd_node *diff, const char *user, const char *par return NULL; } - /* check access for the node, none operation is always allowed, and partial access is relevant only for read operation */ + /* check access for the node, none operation is always allowed, and partial access is relevant only for + * read operation */ if (oper && !NCAC_ACCESS_IS_NODE_PERMIT(ncac_allowed_node(diff, NULL, NULL, oper, groups, group_count))) { node = diff; break; @@ -1667,7 +1643,7 @@ ncac_check_diff(const struct lyd_node *diff, const char *user) /* NACM LOCK */ pthread_mutex_lock(&nacm.lock); - if (ncac_collect_groups(sr_get_context(np2srv.sr_conn), user, &groups, &group_count)) { + if (ncac_collect_groups(user, &groups, &group_count)) { goto cleanup; } @@ -1694,7 +1670,7 @@ ncac_check_yang_push_update_notif(const char *user, struct ly_set *set, int *all uint32_t i, group_count, removed = 0; char **groups; - if (ncac_collect_groups(sr_get_context(np2srv.sr_conn), user, &groups, &group_count)) { + if (ncac_collect_groups(user, &groups, &group_count)) { return; } diff --git a/src/netconf_acm.h b/src/netconf_acm.h index afff4b73c..7386874be 100644 --- a/src/netconf_acm.h +++ b/src/netconf_acm.h @@ -58,7 +58,7 @@ struct ncac { * @brief NACM group. */ struct ncac_group { - const char *name; /**< Group name. */ + char *name; /**< Group name. */ char **users; /**< Array of users belonging to this group. */ uint32_t user_count; /**< Number of users. */ } *groups; /**< Sorted array of existing groups. */ @@ -68,7 +68,7 @@ struct ncac { * @brief NACM rule list. */ struct ncac_rule_list { - const char *name; /**< Rule list name. */ + char *name; /**< Rule list name. */ char **groups; /**< Sorted all groups associated with this rule list. */ uint32_t group_count; /**< Number of groups. */ @@ -76,13 +76,13 @@ struct ncac { * @brief NACM rule. */ struct ncac_rule { - const char *name; /**< Rule name. */ - const char *module_name; /**< Rule module name. */ - const char *target; /**< Rule target. */ + char *name; /**< Rule name. */ + char *module_name; /**< Rule module name. */ + char *target; /**< Rule target. */ NCAC_TARGET_TYPE target_type; /**< Rule target type. */ uint8_t operations; /**< Rule operations associated with it. */ char action_deny; /**< Whether the rule action is "deny" (otherwise "permit"). */ - const char *comment; /**< Rule comment. */ + char *comment; /**< Rule comment. */ struct ncac_rule *next; /**< Pointer to the next rule. */ } *rules; /**< List of rules in the rule list. */ diff --git a/src/netconf_confirmed_commit.c b/src/netconf_confirmed_commit.c index 089c0fc2b..e5e009ecd 100644 --- a/src/netconf_confirmed_commit.c +++ b/src/netconf_confirmed_commit.c @@ -311,7 +311,7 @@ changes_rollback(union sigval UNUSED(sev)) { int rc; struct lyd_node *node = NULL; - const struct ly_ctx *ctx = NULL; + const struct ly_ctx *ly_ctx; struct lys_module *module = NULL; sr_session_ctx_t *session; char *path = NULL, *module_name = NULL, *meta = NULL, *srv_path = NULL; @@ -320,7 +320,7 @@ changes_rollback(union sigval UNUSED(sev)) struct dirent *dirent = NULL; VRB("Confirmed commit timeout reached. Restoring previous running."); - ctx = sr_get_context(np2srv.sr_conn); + ly_ctx = sr_acquire_context(np2srv.sr_conn); /* Start a session */ if ((rc = sr_session_start(np2srv.sr_conn, SR_DS_RUNNING, &session))) { @@ -366,7 +366,7 @@ changes_rollback(union sigval UNUSED(sev)) EMEM; goto cleanup; } - module = ly_ctx_get_module_implemented(ctx, module_name); + module = ly_ctx_get_module_implemented(ly_ctx, module_name); if (!module) { ERR("Module \"%s\" does not exist/not implemented.", module_name); rename_failed_file(module_name, path); @@ -375,7 +375,7 @@ changes_rollback(union sigval UNUSED(sev)) /* get, restore and delete the backup */ VRB("Rolling back module \"%s\"", module->name); - if (get_running_backup(ctx, path, &node)) { + if (get_running_backup(ly_ctx, path, &node)) { rename_failed_file(module_name, path); continue; } @@ -400,6 +400,7 @@ changes_rollback(union sigval UNUSED(sev)) } cleanup: + sr_release_context(np2srv.sr_conn); closedir(dir); sr_session_stop(session); free(path); @@ -490,7 +491,7 @@ backup_module(sr_session_ctx_t *session, const struct lys_module *module) { int rc = SR_ERR_OK; char *path = NULL, *xpath = NULL, *ncc_path = NULL; - struct lyd_node *node = NULL; + sr_data_t *data = NULL; if (asprintf(&ncc_path, "%s/%s", np2srv.server_dir, NCC_DIR) == -1) { EMEM; @@ -505,7 +506,7 @@ backup_module(sr_session_ctx_t *session, const struct lys_module *module) goto cleanup; } - if ((rc = sr_get_data(session, xpath, 0, 0, 0, &node))) { + if ((rc = sr_get_data(session, xpath, 0, 0, 0, &data))) { ERR("Failed getting configuration of running for module \"%s\" (%s).", module->name, sr_strerror(rc)); goto cleanup; } @@ -515,15 +516,14 @@ backup_module(sr_session_ctx_t *session, const struct lys_module *module) rc = SR_ERR_NO_MEMORY; goto cleanup; } - if (lyd_print_path(path, node, LYD_JSON, LY_PRINT_SHRINK)) { - ERR("Failed backing up node of module \"%s\" into file \"%s\" (%s).", - module->name, path, ly_errmsg(LYD_CTX(node))); + if (lyd_print_path(path, data ? data->tree : NULL, LYD_JSON, LY_PRINT_SHRINK)) { + ERR("Failed backing up node of module \"%s\" into file \"%s\".", module->name, path); rc = SR_ERR_LY; goto cleanup; } cleanup: - lyd_free_tree(node); + sr_release_data(data); free(path); free(xpath); return rc; @@ -649,24 +649,23 @@ static int set_running_backup(void) { int rc = SR_ERR_OK, read = 0, write = 0; - const struct ly_ctx *ctx; - struct sr_session_ctx_s *session; + const struct ly_ctx *ly_ctx; + sr_session_ctx_t *session; struct lys_module *module; - sr_conn_ctx_t *conn; uint32_t index = 0; + ly_ctx = sr_acquire_context(np2srv.sr_conn); + if ((rc = sr_session_start(np2srv.sr_conn, SR_DS_RUNNING, &session))) { ERR("Failed starting a sysrepo session (%s).", sr_strerror(rc)); goto cleanup; } - /* Iterate over all implemented modules */ - conn = sr_session_get_connection(session); - ctx = sr_get_context(conn); + /* iterate over all implemented modules */ if ((rc = ncc_check_dir())) { goto cleanup; } - while ((module = ly_ctx_get_module_iter(ctx, &index))) { + while ((module = ly_ctx_get_module_iter(ly_ctx, &index))) { /* check if module should and can be backed up */ if (!module->implemented) { continue; @@ -676,7 +675,7 @@ set_running_backup(void) } /* Check if has both read and write permission for module in sysrepo */ - if ((rc = sr_check_module_ds_access(conn, "edit1", SR_DS_RUNNING, &read, &write))) { + if ((rc = sr_check_module_ds_access(np2srv.sr_conn, "edit1", SR_DS_RUNNING, &read, &write))) { ERR("Failed getting permissions of module \"%s\".", module->name); goto cleanup; } @@ -692,6 +691,7 @@ set_running_backup(void) } cleanup: + sr_release_context(np2srv.sr_conn); sr_session_stop(session); return rc; } diff --git a/src/netconf_monitoring.c b/src/netconf_monitoring.c index 6da087888..a64c75e0e 100644 --- a/src/netconf_monitoring.c +++ b/src/netconf_monitoring.c @@ -275,12 +275,14 @@ np2srv_ncm_oper_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const cha const struct lys_module *mod; sr_conn_ctx_t *conn; struct ly_ctx *ly_ctx; - const char **cpblts; + char **cpblts; char *time_str, buf[11]; uint32_t i; + /* context is locked while the callback is executed */ conn = sr_session_get_connection(session); - ly_ctx = (struct ly_ctx *)sr_get_context(conn); + ly_ctx = (struct ly_ctx *)sr_acquire_context(conn); + sr_release_context(conn); if (lyd_new_path(NULL, ly_ctx, "/ietf-netconf-monitoring:netconf-state", NULL, 0, &root)) { goto error; @@ -296,7 +298,7 @@ np2srv_ncm_oper_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const cha for (i = 0; cpblts[i]; ++i) { lyd_new_term(cont, NULL, "capability", cpblts[i], 0, NULL); - lydict_remove(ly_ctx, cpblts[i]); + free(cpblts[i]); } free(cpblts); diff --git a/src/netconf_server.c b/src/netconf_server.c index 082c7603b..b98a0b6cb 100644 --- a/src/netconf_server.c +++ b/src/netconf_server.c @@ -319,7 +319,8 @@ np2srv_ch_client_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const ch if (op == SR_OP_CREATED) { rc = nc_server_ch_add_client(client_name); if (!rc) { - rc = nc_connect_ch_client_dispatch(client_name, np2srv_new_session_cb); + rc = nc_connect_ch_client_dispatch(client_name, np2srv_acquire_ctx_cb, np2srv_release_ctx_cb, + np2srv.sr_conn, np2srv_new_session_cb); } } else if (op == SR_OP_DELETED) { rc = nc_server_ch_del_client(client_name); diff --git a/src/netconf_server_ssh.c b/src/netconf_server_ssh.c index 42e1dec9f..302beb855 100644 --- a/src/netconf_server_ssh.c +++ b/src/netconf_server_ssh.c @@ -46,7 +46,7 @@ np2srv_hostkey_cb(const char *name, void *UNUSED(user_data), char **UNUSED(privk { sr_session_ctx_t *sr_sess; char *xpath; - struct lyd_node *data = NULL; + sr_data_t *data = NULL; int r, rc = -1; r = sr_session_start(np2srv.sr_conn, SR_DS_RUNNING, &sr_sess); @@ -69,7 +69,7 @@ np2srv_hostkey_cb(const char *name, void *UNUSED(user_data), char **UNUSED(privk } /* parse private key values */ - if (np2srv_sr_get_privkey(data, privkey_data, privkey_type)) { + if (np2srv_sr_get_privkey(data->tree, privkey_data, privkey_type)) { goto cleanup; } @@ -77,7 +77,7 @@ np2srv_hostkey_cb(const char *name, void *UNUSED(user_data), char **UNUSED(privk rc = 0; cleanup: - lyd_free_siblings(data); + sr_release_data(data); sr_session_stop(sr_sess); return rc; } diff --git a/src/netconf_server_tls.c b/src/netconf_server_tls.c index 6e620793e..7d7a134de 100644 --- a/src/netconf_server_tls.c +++ b/src/netconf_server_tls.c @@ -38,7 +38,7 @@ np2srv_cert_cb(const char *name, void *UNUSED(user_data), char **UNUSED(cert_pat { sr_session_ctx_t *sr_sess; char *xpath; - struct lyd_node *data = NULL; + sr_data_t *data = NULL; int r, rc = -1; r = sr_session_start(np2srv.sr_conn, SR_DS_RUNNING, &sr_sess); @@ -47,7 +47,8 @@ np2srv_cert_cb(const char *name, void *UNUSED(user_data), char **UNUSED(cert_pat } /* get private key data from sysrepo */ - if (asprintf(&xpath, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[certificates/certificate/name='%s']", name) == -1) { + if (asprintf(&xpath, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[certificates/certificate/name='%s']", + name) == -1) { EMEM; goto cleanup; } @@ -61,7 +62,7 @@ np2srv_cert_cb(const char *name, void *UNUSED(user_data), char **UNUSED(cert_pat } /* parse private key values */ - if (np2srv_sr_get_privkey(data, privkey_data, privkey_type)) { + if (np2srv_sr_get_privkey(data->tree, privkey_data, privkey_type)) { goto cleanup; } @@ -71,7 +72,7 @@ np2srv_cert_cb(const char *name, void *UNUSED(user_data), char **UNUSED(cert_pat EMEM; goto cleanup; } - lyd_free_siblings(data); + sr_release_data(data); r = sr_get_subtree(sr_sess, xpath, 0, &data); free(xpath); if (r != SR_ERR_OK) { @@ -82,7 +83,7 @@ np2srv_cert_cb(const char *name, void *UNUSED(user_data), char **UNUSED(cert_pat } /* set cert data */ - *cert_data = strdup(lyd_get_value(data)); + *cert_data = strdup(lyd_get_value(data->tree)); if (!*cert_data) { EMEM; goto cleanup; @@ -92,7 +93,7 @@ np2srv_cert_cb(const char *name, void *UNUSED(user_data), char **UNUSED(cert_pat rc = 0; cleanup: - lyd_free_siblings(data); + sr_release_data(data); sr_session_stop(sr_sess); return rc; } @@ -103,7 +104,7 @@ np2srv_cert_list_cb(const char *name, void *UNUSED(user_data), char ***UNUSED(ce { sr_session_ctx_t *sr_sess; char *xpath; - struct lyd_node *data = NULL; + sr_data_t *data = NULL; struct ly_set *set = NULL; int r, rc = -1; uint32_t i, j; @@ -128,7 +129,7 @@ np2srv_cert_list_cb(const char *name, void *UNUSED(user_data), char ***UNUSED(ce } /* find all certificates */ - if (lyd_find_xpath(data, "certificate/cert", &set)) { + if (lyd_find_xpath(data->tree, "certificate/cert", &set)) { /* libyang error printed */ goto cleanup; } else if (!set->count) { @@ -161,7 +162,7 @@ np2srv_cert_list_cb(const char *name, void *UNUSED(user_data), char ***UNUSED(ce rc = 0; cleanup: - lyd_free_siblings(data); + sr_release_data(data); ly_set_free(set, NULL); sr_session_stop(sr_sess); return rc; diff --git a/src/netconf_subscribed_notifications.c b/src/netconf_subscribed_notifications.c index a2ecfb2ab..dcde64c1c 100644 --- a/src/netconf_subscribed_notifications.c +++ b/src/netconf_subscribed_notifications.c @@ -450,11 +450,13 @@ sub_ntf_send_notif_modified(const struct np2srv_sub_ntf *sub) { int rc = SR_ERR_OK; char buf[11], *datetime = NULL; + const struct ly_ctx *ly_ctx; struct lyd_node *ly_ntf = NULL; struct nc_session *ncs; - if (lyd_new_path(NULL, sr_get_context(np2srv.sr_conn), "/ietf-subscribed-notifications:subscription-modified", NULL, - 0, &ly_ntf)) { + ly_ctx = sr_acquire_context(np2srv.sr_conn); + + if (lyd_new_path(NULL, ly_ctx, "/ietf-subscribed-notifications:subscription-modified", NULL, 0, &ly_ntf)) { rc = SR_ERR_LY; goto cleanup; } @@ -502,6 +504,7 @@ sub_ntf_send_notif_modified(const struct np2srv_sub_ntf *sub) cleanup: free(datetime); lyd_free_tree(ly_ntf); + sr_release_context(np2srv.sr_conn); return rc; } @@ -584,6 +587,7 @@ sub_ntf_terminate_sub(struct np2srv_sub_ntf *sub, struct nc_session *ncs) { int r, rc = SR_ERR_OK; struct lyd_node *ly_ntf; + const struct ly_ctx *ly_ctx; char buf[11]; uint32_t idx, sub_id_count, sub_id; enum sub_ntf_type sub_type = sub->type; @@ -641,16 +645,19 @@ sub_ntf_terminate_sub(struct np2srv_sub_ntf *sub, struct nc_session *ncs) INFO_WLOCK; if (nc_session_get_status(ncs) == NC_STATUS_RUNNING) { + ly_ctx = sr_acquire_context(np2srv.sr_conn); + /* send the subscription-terminated notification */ sprintf(buf, "%" PRIu32, sub->nc_sub_id); - lyd_new_path(NULL, sr_get_context(np2srv.sr_conn), "/ietf-subscribed-notifications:subscription-terminated/id", - buf, 0, &ly_ntf); + lyd_new_path(NULL, ly_ctx, "/ietf-subscribed-notifications:subscription-terminated/id", buf, 0, &ly_ntf); lyd_new_path(ly_ntf, NULL, "reason", sub->term_reason, 0, NULL); r = sub_ntf_send_notif(ncs, sub->nc_sub_id, np_gettimespec(1), &ly_ntf, 1); if (r != SR_ERR_OK) { rc = r; } + + sr_release_context(np2srv.sr_conn); } /* subscription terminated */ @@ -848,16 +855,19 @@ np2srv_oper_sub_ntf_streams_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id const char *UNUSED(path), const char *UNUSED(request_xpath), uint32_t UNUSED(request_id), struct lyd_node **parent, void *UNUSED(private_data)) { - struct lyd_node *root, *stream, *sr_data = NULL, *sr_mod, *rep_sup; + struct lyd_node *root, *stream; sr_conn_ctx_t *conn; const struct ly_ctx *ly_ctx; - const struct lys_module *mod; - const char *mod_name; + const struct lys_module *ly_mod; + uint32_t idx = 0; char *buf; - int rc; + int enabled; + struct timespec earliest_notif; + /* context locked while the callback is executing */ conn = sr_session_get_connection(session); - ly_ctx = sr_get_context(conn); + ly_ctx = sr_acquire_context(conn); + sr_release_context(conn); if (lyd_new_path(NULL, ly_ctx, "/ietf-subscribed-notifications:streams", NULL, 0, &root)) { goto error; @@ -876,42 +886,30 @@ np2srv_oper_sub_ntf_streams_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id goto error; } - /* go through all the sysrepo modules for individual streams */ - rc = sr_get_module_info(conn, &sr_data); - if (rc != SR_ERR_OK) { - ERR("Failed to get sysrepo module info data (%s).", sr_strerror(rc)); - goto error; - } - LY_LIST_FOR(lyd_child(sr_data), sr_mod) { - if (strcmp(sr_mod->schema->name, "module")) { - continue; - } - - mod_name = lyd_get_value(lyd_child(sr_mod)); - - /* get the module */ - mod = ly_ctx_get_module_implemented(ly_ctx, mod_name); - assert(mod); - - if (!np_ly_mod_has_notif(mod)) { - /* no notifications in the module so do not consider it a stream */ + /* go through all the modules */ + while ((ly_mod = ly_ctx_get_module_iter(ly_ctx, &idx))) { + if (!ly_mod->implemented || !np_ly_mod_has_notif(ly_mod)) { + /* not implemented or no notifications in the module so do not consider it a stream */ continue; } /* generate information about the stream/module */ - if (lyd_new_list(root, NULL, "stream", 0, &stream, mod_name)) { + if (lyd_new_list(root, NULL, "stream", 0, &stream, ly_mod->name)) { goto error; } if (lyd_new_term(stream, NULL, "description", "Stream with all notifications of a module.", 0, NULL)) { goto error; } - lyd_find_path(sr_mod, "replay-support", 0, &rep_sup); - if (rep_sup) { + /* learn whether replay is supported */ + if (sr_get_module_replay_support(conn, ly_mod->name, &earliest_notif, &enabled)) { + goto error; + } + if (enabled) { if (lyd_new_term(stream, NULL, "replay-support", NULL, 0, NULL)) { goto error; } - ly_time_time2str(((struct lyd_node_term *)rep_sup)->value.uint64, NULL, &buf); + ly_time_ts2str(&earliest_notif, &buf); if (lyd_new_term(stream, NULL, "replay-log-creation-time", buf, 0, NULL)) { free(buf); goto error; @@ -920,13 +918,11 @@ np2srv_oper_sub_ntf_streams_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id } } - lyd_free_siblings(sr_data); *parent = root; return SR_ERR_OK; error: lyd_free_tree(root); - lyd_free_siblings(sr_data); return SR_ERR_INTERNAL; } @@ -942,7 +938,9 @@ np2srv_oper_sub_ntf_subscriptions_cb(sr_session_ctx_t *session, uint32_t UNUSED( uint32_t i, excluded_count = 0; int r, rc = SR_ERR_OK; - ly_ctx = sr_get_context(sr_session_get_connection(session)); + /* context is locked while the callback is executing */ + ly_ctx = sr_session_acquire_context(session); + sr_session_release_context(session); /* READ LOCK */ INFO_RLOCK; diff --git a/src/subscribed_notifications.c b/src/subscribed_notifications.c index e5047ff81..2d29a0640 100644 --- a/src/subscribed_notifications.c +++ b/src/subscribed_notifications.c @@ -85,6 +85,7 @@ np2srv_rpc_establish_sub_ntf_cb(sr_session_ctx_t *UNUSED(session), uint32_t sub_ { struct sub_ntf_cb_arg *arg = private_data; struct lyd_node *ly_ntf = NULL; + const struct ly_ctx *ly_ctx; struct np2srv_sub_ntf *sub; char buf[26]; @@ -95,9 +96,12 @@ np2srv_rpc_establish_sub_ntf_cb(sr_session_ctx_t *UNUSED(session), uint32_t sub_ goto cleanup; } + /* context lock is held while the callback is executing */ + ly_ctx = sr_acquire_context(np2srv.sr_conn); + sr_release_context(np2srv.sr_conn); + sprintf(buf, "%" PRIu32, arg->nc_sub_id); - lyd_new_path(NULL, sr_get_context(np2srv.sr_conn), "/ietf-subscribed-notifications:replay-completed/id", - buf, 0, &ly_ntf); + lyd_new_path(NULL, ly_ctx, "/ietf-subscribed-notifications:replay-completed/id", buf, 0, &ly_ntf); notif = ly_ntf; } else if (notif_type == SR_EV_NOTIF_TERMINATED) { /* WRITE LOCK on sub */ @@ -157,13 +161,15 @@ sub_ntf_sr_subscribe(sr_session_ctx_t *user_sess, const char *stream, const char const struct timespec *stop, struct sub_ntf_cb_arg *cb_arg, sr_session_ctx_t *ev_sess, uint32_t **sub_ids, uint32_t *sub_id_count) { - const struct ly_ctx *ly_ctx = sr_get_context(sr_session_get_connection(user_sess)); + const struct ly_ctx *ly_ctx; const struct lys_module *ly_mod; int rc, suspended = 0; const sr_error_info_t *err_info; struct ly_set mod_set = {0}; uint32_t idx; + ly_ctx = sr_session_acquire_context(user_sess); + *sub_ids = NULL; *sub_id_count = 0; @@ -201,7 +207,7 @@ sub_ntf_sr_subscribe(sr_session_ctx_t *user_sess, const char *stream, const char /* subscribe to the module */ rc = sr_notif_subscribe_tree(user_sess, ly_mod->name, xpath, start, stop, np2srv_rpc_establish_sub_ntf_cb, - cb_arg, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_THREAD_SUSPEND, &np2srv.sr_notif_sub); + cb_arg, SR_SUBSCR_THREAD_SUSPEND, &np2srv.sr_notif_sub); if (rc != SR_ERR_OK) { sr_session_get_error(user_sess, &err_info); sr_session_set_error_message(ev_sess, err_info->err[0].message); @@ -227,7 +233,7 @@ sub_ntf_sr_subscribe(sr_session_ctx_t *user_sess, const char *stream, const char /* subscribe to the specific module (stream) */ rc = sr_notif_subscribe_tree(user_sess, stream, xpath, start, stop, np2srv_rpc_establish_sub_ntf_cb, - cb_arg, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_THREAD_SUSPEND, &np2srv.sr_notif_sub); + cb_arg, SR_SUBSCR_THREAD_SUSPEND, &np2srv.sr_notif_sub); if (rc != SR_ERR_OK) { sr_session_get_error(user_sess, &err_info); sr_session_set_error_message(ev_sess, err_info->err[0].message); @@ -251,6 +257,7 @@ sub_ntf_sr_subscribe(sr_session_ctx_t *user_sess, const char *stream, const char *sub_id_count = 0; cleanup: + sr_session_release_context(user_sess); if (suspended) { /* resume subscription thread */ sr_subscription_thread_resume(np2srv.sr_notif_sub); @@ -276,7 +283,8 @@ sub_ntf_rpc_filter2xpath(sr_session_ctx_t *user_sess, const struct lyd_node *rpc char **xpath, const char **stream_filter_name, struct lyd_node **stream_subtree_filter, const char **stream_xpath_filter) { - struct lyd_node *node = NULL, *subtree = NULL; + struct lyd_node *node = NULL; + sr_data_t *subtree = NULL; struct ly_set *nodeset; const sr_error_info_t *err_info; struct np2_filter filter = {0}; @@ -341,12 +349,12 @@ sub_ntf_rpc_filter2xpath(sr_session_ctx_t *user_sess, const struct lyd_node *rpc goto cleanup; } - if (!lyd_child(subtree)->next) { + if (!lyd_child(subtree->tree)->next) { ERR("Stream filter \"%s\" does not define any actual filter.", lyd_get_value(node)); rc = SR_ERR_INVAL_ARG; goto cleanup; } - node = lyd_child(subtree)->next; + node = lyd_child(subtree->tree)->next; } if (!strcmp(node->schema->name, "stream-subtree-filter")) { @@ -374,7 +382,7 @@ sub_ntf_rpc_filter2xpath(sr_session_ctx_t *user_sess, const struct lyd_node *rpc } cleanup: - lyd_free_tree(subtree); + sr_release_data(subtree); op_filter_erase(&filter); return rc; } @@ -524,7 +532,7 @@ sub_ntf_rpc_modify_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, st for (i = 0; i < sub->sub_id_count; ++i) { /* "pass" the lock to the callback */ sub_ntf_cb_lock_pass(sub->sub_ids[i]); - rc = sr_event_notif_sub_modify_xpath(np2srv.sr_notif_sub, sub->sub_ids[i], xp); + rc = sr_notif_sub_modify_xpath(np2srv.sr_notif_sub, sub->sub_ids[i], xp); sub_ntf_cb_lock_clear(sub->sub_ids[i]); if (rc != SR_ERR_OK) { goto cleanup; @@ -637,7 +645,7 @@ sub_ntf_config_filters(const struct lyd_node *filter, sr_change_oper_t op) /* modify the filter of the subscription(s) */ for (i = 0; i < sub->sub_id_count; ++i) { /* callback ignores this event */ - r = sr_event_notif_sub_modify_xpath(np2srv.sr_notif_sub, sub->sub_ids[i], xp); + r = sr_notif_sub_modify_xpath(np2srv.sr_notif_sub, sub->sub_ids[i], xp); if (r != SR_ERR_OK) { rc = r; } diff --git a/src/yang_push.c b/src/yang_push.c index 54afb543b..26bff18d5 100644 --- a/src/yang_push.c +++ b/src/yang_push.c @@ -308,10 +308,11 @@ yang_push_notif_change_send(struct nc_session *ncs, struct yang_push_data *yp_da int all_removed = 0; int rc = SR_ERR_OK; - assert(yp_data->ly_change_ntf); + assert(yp_data->change_ntf); /* NACM filtering */ - if (lyd_find_xpath(yp_data->ly_change_ntf, "/ietf-yang-push:push-change-update/datastore-changes/yang-patch/edit", &set)) { + if (lyd_find_xpath(yp_data->change_ntf->tree, "/ietf-yang-push:push-change-update/datastore-changes/yang-patch/edit", + &set)) { rc = SR_ERR_LY; goto cleanup; } @@ -324,7 +325,7 @@ yang_push_notif_change_send(struct nc_session *ncs, struct yang_push_data *yp_da } /* send the notification */ - rc = sub_ntf_send_notif(ncs, nc_sub_id, np_gettimespec(1), &yp_data->ly_change_ntf, 1); + rc = sub_ntf_send_notif(ncs, nc_sub_id, np_gettimespec(1), &yp_data->change_ntf->tree, 1); if (rc == SR_ERR_OK) { /* set last_notif timestamp */ @@ -333,8 +334,8 @@ yang_push_notif_change_send(struct nc_session *ncs, struct yang_push_data *yp_da cleanup: ly_set_free(set, NULL); - lyd_free_tree(yp_data->ly_change_ntf); - yp_data->ly_change_ntf = NULL; + sr_release_data(yp_data->change_ntf); + yp_data->change_ntf = NULL; return rc; } @@ -428,6 +429,7 @@ np2srv_change_yang_push_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), c sr_change_iter_t *iter = NULL; sr_change_oper_t op; const struct lyd_node *node; + const struct ly_ctx *ly_ctx = NULL; struct lyd_node *ly_yp = NULL; const char *prev_value, *prev_list; enum yang_push_op yp_op; @@ -462,18 +464,24 @@ np2srv_change_yang_push_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), c } /* there is a change */ - if (!arg->yp_data->ly_change_ntf) { + if (!arg->yp_data->change_ntf) { + /* store as SR data with context lock, is unlocked on error */ + ly_ctx = sr_acquire_context(np2srv.sr_conn); + if (sr_acquire_data(np2srv.sr_conn, NULL, &arg->yp_data->change_ntf)) { + goto cleanup_unlock; + } + /* create basic structure for push-change-update notification */ sprintf(buf, "%" PRIu32, arg->nc_sub_id); - if (lyd_new_path(NULL, sr_get_context(np2srv.sr_conn), "/ietf-yang-push:push-change-update/id", buf, 0, - &arg->yp_data->ly_change_ntf)) { + if (lyd_new_path(NULL, ly_ctx, "/ietf-yang-push:push-change-update/id", buf, 0, + &arg->yp_data->change_ntf->tree)) { goto cleanup_unlock; } /* generate a new patch-id */ patch_id = ATOMIC_INC_RELAXED(arg->yp_data->patch_id); sprintf(buf, "patch-%" PRIu32, patch_id); - if (lyd_new_path(arg->yp_data->ly_change_ntf, NULL, "datastore-changes/yang-patch/patch-id", buf, 0, NULL)) { + if (lyd_new_path(arg->yp_data->change_ntf->tree, NULL, "datastore-changes/yang-patch/patch-id", buf, 0, NULL)) { goto cleanup_unlock; } @@ -481,7 +489,7 @@ np2srv_change_yang_push_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), c ATOMIC_STORE_RELAXED(arg->yp_data->edit_id, 1); } if (!ly_yp) { - ly_yp = lyd_child(lyd_child(arg->yp_data->ly_change_ntf)->next); + ly_yp = lyd_child(lyd_child(arg->yp_data->change_ntf->tree)->next); } /* append a new edit */ @@ -490,7 +498,7 @@ np2srv_change_yang_push_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), c } } - if (!arg->yp_data->ly_change_ntf) { + if (!arg->yp_data->change_ntf) { /* there are actually no changes */ goto cleanup_unlock; } @@ -547,7 +555,7 @@ yang_push_sr_subscribe_mod(const struct lys_module *ly_mod, sr_session_ctx_t *us /* subscribe to the module */ rc = sr_module_change_subscribe(user_sess, ly_mod->name, xpath, np2srv_change_yang_push_cb, private_data, - 0, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_PASSIVE | SR_SUBSCR_DONE_ONLY, &np2srv.sr_data_sub); + 0, SR_SUBSCR_PASSIVE | SR_SUBSCR_DONE_ONLY, &np2srv.sr_data_sub); if (rc != SR_ERR_OK) { sr_session_get_error(user_sess, &err_info); sr_session_set_error_message(ev_sess, err_info->err[0].message); @@ -639,12 +647,14 @@ static int yang_push_sr_subscribe(sr_session_ctx_t *user_sess, sr_datastore_t ds, const char *xpath, void *private_data, sr_session_ctx_t *ev_sess, uint32_t **sub_ids, uint32_t *sub_id_count) { - const struct ly_ctx *ly_ctx = sr_get_context(sr_session_get_connection(user_sess)); + const struct ly_ctx *ly_ctx; const struct lys_module *ly_mod; struct ly_set *mod_set = NULL; int rc; uint32_t idx, config_mask = (ds == SR_DS_OPERATIONAL) ? LYS_CONFIG_MASK : LYS_CONFIG_W; + ly_ctx = sr_session_acquire_context(user_sess); + *sub_ids = NULL; *sub_id_count = 0; @@ -684,10 +694,12 @@ yang_push_sr_subscribe(sr_session_ctx_t *user_sess, sr_datastore_t ds, const cha } } + sr_session_release_context(user_sess); ly_set_free(mod_set, NULL); return SR_ERR_OK; error: + sr_session_release_context(user_sess); ly_set_free(mod_set, NULL); for (idx = 0; idx < *sub_id_count; ++idx) { @@ -716,7 +728,8 @@ yang_push_rpc_filter2xpath(sr_session_ctx_t *user_sess, const struct lyd_node *r char **xpath, const char **selection_filter_ref, struct lyd_node **datastore_subtree_filter, const char **datastore_xpath_filter) { - struct lyd_node *node = NULL, *subtree = NULL; + struct lyd_node *node = NULL; + sr_data_t *subtree = NULL; struct ly_set *nodeset; const sr_error_info_t *err_info; struct np2_filter filter = {0}; @@ -783,12 +796,12 @@ yang_push_rpc_filter2xpath(sr_session_ctx_t *user_sess, const struct lyd_node *r goto cleanup; } - if (!lyd_child(subtree)->next) { + if (!lyd_child(subtree->tree)->next) { ERR("Selection filter \"%s\" does not define any actual filter.", lyd_get_value(node)); rc = SR_ERR_INVAL_ARG; goto cleanup; } - node = lyd_child(subtree)->next; + node = lyd_child(subtree->tree)->next; } if (!strcmp(node->schema->name, "datastore-subtree-filter")) { @@ -816,7 +829,7 @@ yang_push_rpc_filter2xpath(sr_session_ctx_t *user_sess, const struct lyd_node *r } cleanup: - lyd_free_tree(subtree); + sr_release_data(subtree); op_filter_erase(&filter); return rc; } @@ -833,7 +846,9 @@ static int yang_push_notif_update_send(struct nc_session *ncs, struct yang_push_data *yp_data, uint32_t nc_sub_id) { struct np2_user_sess *user_sess; - struct lyd_node *data = NULL, *ly_ntf = NULL; + struct lyd_node *ly_ntf = NULL; + const struct ly_ctx *ly_ctx; + sr_data_t *data = NULL; char buf[11]; int rc = SR_ERR_OK; @@ -851,21 +866,29 @@ yang_push_notif_update_send(struct nc_session *ncs, struct yang_push_data *yp_da } /* NACM filter */ - ncac_check_data_read_filter(&data, nc_session_get_username(ncs)); + if (data) { + ncac_check_data_read_filter(&data->tree, nc_session_get_username(ncs)); + } + + /* context lock is already held by data */ + ly_ctx = sr_acquire_context(np2srv.sr_conn); + sr_release_context(np2srv.sr_conn); /* create the notification */ sprintf(buf, "%" PRIu32, nc_sub_id); - if (lyd_new_path(NULL, sr_get_context(np2srv.sr_conn), "/ietf-yang-push:push-update/id", buf, 0, &ly_ntf)) { + if (lyd_new_path(NULL, ly_ctx, "/ietf-yang-push:push-update/id", buf, 0, &ly_ntf)) { rc = SR_ERR_LY; goto cleanup; } /* datastore-contents */ - if (lyd_new_any(ly_ntf, NULL, "datastore-contents", data, 1, LYD_ANYDATA_DATATREE, 0, NULL)) { + if (lyd_new_any(ly_ntf, NULL, "datastore-contents", data ? data->tree : NULL, 1, LYD_ANYDATA_DATATREE, 0, NULL)) { rc = SR_ERR_LY; goto cleanup; } - data = NULL; + if (data) { + data->tree = NULL; + } /* send the notification */ rc = sub_ntf_send_notif(ncs, nc_sub_id, np_gettimespec(1), &ly_ntf, 1); @@ -874,7 +897,7 @@ yang_push_notif_update_send(struct nc_session *ncs, struct yang_push_data *yp_da } cleanup: - lyd_free_siblings(data); + sr_release_data(data); lyd_free_tree(ly_ntf); np_release_user_sess(user_sess); return rc; @@ -1685,7 +1708,7 @@ yang_push_data_destroy(void *data) timer_delete(yp_data->update_timer); } else { pthread_mutex_destroy(&yp_data->notif_lock); - lyd_free_tree(yp_data->ly_change_ntf); + sr_release_data(yp_data->change_ntf); if (yp_data->dampening_period_ms) { timer_delete(yp_data->damp_timer); } diff --git a/src/yang_push.h b/src/yang_push.h index 7513508ce..3fddb5e63 100644 --- a/src/yang_push.h +++ b/src/yang_push.h @@ -71,7 +71,7 @@ struct yang_push_data { /* internal data */ pthread_mutex_t notif_lock; - struct lyd_node *ly_change_ntf; + sr_data_t *change_ntf; ATOMIC_T patch_id; ATOMIC_T edit_id; struct timespec last_notif; diff --git a/tests/np_test.c b/tests/np_test.c index e3253d466..e6860604c 100644 --- a/tests/np_test.c +++ b/tests/np_test.c @@ -129,12 +129,13 @@ np_glob_setup_env(const char *test_name) } int -np_glob_setup_np2(void **state, const char *test_name) +np_glob_setup_np2(void **state, const char *test_name, const char *modules[], uint32_t mod_count) { struct np_test *st; pid_t pid; char str[256], serverdir[256], sockparam[128]; int fd, pipefd[2], buf; + uint32_t i; if (!getcwd(str, 256)) { SETUP_FAIL_LOG; @@ -253,6 +254,28 @@ np_glob_setup_np2(void **state, const char *test_name) strncpy(st->socket_path, sockparam + 2, sizeof st->socket_path - 1); strncpy(st->test_name, test_name, sizeof st->test_name - 1); + /* create connection and install modules */ + if (sr_connect(SR_CONN_DEFAULT, &st->conn)) { + SETUP_FAIL_LOG; + return 1; + } + for (i = 0; i < mod_count; ++i) { + if (sr_install_module(st->conn, modules[i], NULL, NULL)) { + SETUP_FAIL_LOG; + return 1; + } + } + + /* start session and acquire context */ + if (sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess)) { + SETUP_FAIL_LOG; + return 1; + } + if (!(st->ctx = sr_acquire_context(st->conn))) { + SETUP_FAIL_LOG; + return 1; + } + /* create NETCONF sessions */ st->nc_sess = nc_connect_unix(st->socket_path, NULL); if (!st->nc_sess) { @@ -270,19 +293,37 @@ np_glob_setup_np2(void **state, const char *test_name) } int -np_glob_teardown(void **state) +np_glob_teardown(void **state, const char *modules[], uint32_t mod_count) { struct np_test *st = *state; - int ret = 0, wstatus; + int ret = 0, wstatus, rc; + uint32_t i; if (!st) { return 0; } - /* stop the NETCONF session */ + /* stop NETCONF sessions */ nc_session_free(st->nc_sess, NULL); nc_session_free(st->nc_sess2, NULL); + /* release context */ + sr_release_context(st->conn); + + /* uninstall modules */ + for (i = 0; i < mod_count; ++i) { + if ((rc = sr_remove_module(st->conn, modules[i], 0))) { + printf("sr_remove_module() failed (%s)\n", sr_strerror(rc)); + ret = 1; + } + } + + /* disconnect */ + if ((rc = sr_disconnect(st->conn))) { + printf("sr_disconnect() failed (%s)\n", sr_strerror(rc)); + ret = 1; + } + /* terminate the server */ if (kill(st->server_pid, SIGTERM)) { printf("kill() failed (%s)\n", strerror(errno)); diff --git a/tests/np_test.h b/tests/np_test.h index a4b2f4b3e..d0ac1c008 100644 --- a/tests/np_test.h +++ b/tests/np_test.h @@ -214,9 +214,9 @@ void np_glob_setup_test_name(char *buf); int np_glob_setup_env(const char *test_name); -int np_glob_setup_np2(void **state, const char *test_name); +int np_glob_setup_np2(void **state, const char *test_name, const char *modules[], uint32_t mod_count); -int np_glob_teardown(void **state); +int np_glob_teardown(void **state, const char *modules[], uint32_t mod_count); void parse_arg(int argc, char **argv); diff --git a/tests/test_candidate.c b/tests/test_candidate.c index 2f4c84138..e3450f59b 100644 --- a/tests/test_candidate.c +++ b/tests/test_candidate.c @@ -33,63 +33,43 @@ static int local_setup(void **state) { struct np_test *st = *state; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/edit1.yang"; - const char *module2 = NP_TEST_MODULE_DIR "/edit2.yang"; - int rv; + const char *modules[] = {NP_TEST_MODULE_DIR "/edit1.yang", NP_TEST_MODULE_DIR "/edit2.yang"}; + int rc; /* get test name */ np_glob_setup_test_name(test_name); - /* setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module2, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); + /* setup environment */ + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); /* setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - st = *state; - /* Open two connections to start a session for the tests - * One for Candidate and other for running - */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - assert_int_equal(sr_session_start(st->conn, SR_DS_CANDIDATE, &st->sr_sess2), SR_ERR_OK); - rv |= setup_nacm(state); - } - return rv; + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + st = *state; + + /* candidate session */ + assert_int_equal(sr_session_start(st->conn, SR_DS_CANDIDATE, &st->sr_sess2), SR_ERR_OK); + + return 0; } static int local_teardown(void **state) { struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"edit1", "edit2"}; if (!st) { return 0; } - /* Close the sessions and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); + /* close the session */ assert_int_equal(sr_session_stop(st->sr_sess2), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit2"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); /* close netopeer2 server */ - return np_glob_teardown(state); + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } static int diff --git a/tests/test_confirmed_commit.c b/tests/test_confirmed_commit.c index c05cb2152..42d35a689 100644 --- a/tests/test_confirmed_commit.c +++ b/tests/test_confirmed_commit.c @@ -34,67 +34,59 @@ static int local_setup(void **state) { - struct np_test *st = *state; - sr_conn_ctx_t *conn; + struct np_test *st; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/edit1.yang"; - int rv; + const char *modules[] = {NP_TEST_MODULE_DIR "/edit1.yang"}; + int rc; /* get test name */ np_glob_setup_test_name(test_name); /* setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); /* setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - st = *state; - /* Open two connections to start a session for the tests - * One for Candidate and other for running - */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - assert_int_equal(sr_session_start(st->conn, SR_DS_CANDIDATE, &st->sr_sess2), SR_ERR_OK); - /* - * The use of st->path is a little overriden until test_failed_file is called it stores test_name after that - * the path to the test server file directory - */ - st->path = strdup(test_name); - if (!st->path) { - return 1; - } - rv |= setup_nacm(state); + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + st = *state; + + /* start candidate session */ + assert_int_equal(sr_session_start(st->conn, SR_DS_CANDIDATE, &st->sr_sess2), SR_ERR_OK); + + /* + * The use of st->path is a little overriden until test_failed_file is called it stores test_name after that + * the path to the test server file directory + */ + st->path = strdup(test_name); + if (!st->path) { + return 1; } - return rv; + + /* setup NACM */ + rc = setup_nacm(state); + assert_int_equal(rc, 0); + + return 0; } static int local_teardown(void **state) { struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"edit1"}; + + if (!st) { + return 0; + } free(st->path); - /* Close the sessions and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); + /* close the candidate session */ assert_int_equal(sr_session_stop(st->sr_sess2), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit1"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); /* close netopeer2 server */ - return np_glob_teardown(state); + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } static int diff --git a/tests/test_edit.c b/tests/test_edit.c index b98b7cbbb..e94d9388b 100644 --- a/tests/test_edit.c +++ b/tests/test_edit.c @@ -32,72 +32,43 @@ static int local_setup(void **state) { - struct np_test *st; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/edit1.yang"; - const char *module2 = NP_TEST_MODULE_DIR "/edit2.yang"; - const char *module3 = NP_TEST_MODULE_DIR "/edit3.yang"; - const char *module4 = NP_TEST_MODULE_DIR "/edit4.yang"; - const char *module5 = NP_TEST_MODULE_DIR "/example1.yang"; - const char *module6 = NP_TEST_MODULE_DIR "/example2.yang"; - int rv; + const char *modules[] = { + NP_TEST_MODULE_DIR "/edit1.yang", NP_TEST_MODULE_DIR "/edit2.yang", + NP_TEST_MODULE_DIR "/edit3.yang", NP_TEST_MODULE_DIR "/edit4.yang", NP_TEST_MODULE_DIR "/example1.yang", + NP_TEST_MODULE_DIR "/example2.yang" + }; + int rc; /* get test name */ np_glob_setup_test_name(test_name); - /* Setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* Connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module2, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module3, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module4, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module5, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module6, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - - /* Setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - st = *state; - /* Open the connection to start a session for the tests */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - rv |= setup_nacm(state); - } - return rv; + /* setup environment */ + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); + + /* setup netopeer2 server */ + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + + /* setup NACM */ + rc = setup_nacm(state); + assert_int_equal(rc, 0); + + return 0; } static int local_teardown(void **state) { - struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"edit1", "edit2", "edit3", "edit4", "example1", "example2"}; - if (!st) { + if (!*state) { return 0; } - /* Close the session and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* Connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit2"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit3"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit4"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "example1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "example2"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - - /* Close netopeer2 server */ - return np_glob_teardown(state); + /* close netopeer2 server */ + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } static int diff --git a/tests/test_filter.c b/tests/test_filter.c index 835cf9a76..dd0511c57 100644 --- a/tests/test_filter.c +++ b/tests/test_filter.c @@ -192,78 +192,51 @@ static int local_setup(void **state) { struct np_test *st; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/example2.yang"; - const char *module2 = NP_TEST_MODULE_DIR "/filter1.yang"; - const char *module3 = NP_TEST_MODULE_DIR "/xpath.yang"; - const char *module4 = NP_TEST_MODULE_DIR "/issue1.yang"; - const char *module5 = NP_TEST_MODULE_DIR "/edit1.yang"; - int rv; + const char *modules[] = { + NP_TEST_MODULE_DIR "/example2.yang", NP_TEST_MODULE_DIR "/filter1.yang", + NP_TEST_MODULE_DIR "/xpath.yang", NP_TEST_MODULE_DIR "/issue1.yang", NP_TEST_MODULE_DIR "/edit1.yang" + }; + int rc; /* get test name */ np_glob_setup_test_name(test_name); - /* setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module2, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module3, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module4, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module5, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); + /* setup environment */ + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); /* setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - /* state is allocated in np_glob_setup_np2 have to set here */ - st = *state; - /* Open connection to start a session for the tests */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - setup_data(state); - - assert_int_equal(SR_ERR_OK, sr_oper_get_items_subscribe(st->sr_sess, "issue1", - "/issue1:hardware/component/serial-num", change_serial_num, NULL, SR_SUBSCR_DEFAULT, &st->sub)); - - assert_int_equal(SR_ERR_OK, sr_module_change_subscribe(st->sr_sess, "issue1", NULL, change_cb, NULL, 0, - SR_SUBSCR_CTX_REUSE, &st->sub)); - } - return rv; + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + st = *state; + + /* setup data */ + setup_data(state); + + /* setup subscriptions */ + assert_int_equal(SR_ERR_OK, sr_oper_get_subscribe(st->sr_sess, "issue1", + "/issue1:hardware/component/serial-num", change_serial_num, NULL, 0, &st->sub)); + assert_int_equal(SR_ERR_OK, sr_module_change_subscribe(st->sr_sess, "issue1", NULL, change_cb, NULL, 0, 0, &st->sub)); + + return 0; } static int local_teardown(void **state) { struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"example2", "filter1", "xpath", "issue1", "edit1"}; if (!st) { return 0; } - /* Unsubscribe */ + /* unsubscribe */ sr_unsubscribe(st->sub); - /* Close the session and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "example2"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "xpath"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "filter1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "issue1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit1"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - /* close netopeer2 server */ - return np_glob_teardown(state); + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } static void diff --git a/tests/test_nacm.c b/tests/test_nacm.c index 9c776fdc7..4cf7d81e2 100644 --- a/tests/test_nacm.c +++ b/tests/test_nacm.c @@ -35,69 +35,50 @@ static int local_setup(void **state) { struct np_test *st; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/edit1.yang"; - const char *module2 = NP_TEST_MODULE_DIR "/example2.yang"; - const char *module3 = NP_TEST_MODULE_DIR "/nacm-test1.yang"; - const char *module4 = NP_TEST_MODULE_DIR "/nacm-test2.yang"; - int rv; + const char *modules[] = { + NP_TEST_MODULE_DIR "/edit1.yang", NP_TEST_MODULE_DIR "/example2.yang", + NP_TEST_MODULE_DIR "/nacm-test1.yang", NP_TEST_MODULE_DIR "/nacm-test2.yang" + }; + int rc; /* get test name */ np_glob_setup_test_name(test_name); - /* setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module2, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module3, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module4, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); + /* setup environment */ + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); /* setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - st = *state; - /* Open two connections to start a session for the tests - * One for Candidate and other for running - */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - assert_int_equal(sr_session_start(st->conn, SR_DS_CANDIDATE, &st->sr_sess2), SR_ERR_OK); - rv |= setup_nacm(state); - } - return rv; + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + st = *state; + + /* candidate session */ + assert_int_equal(sr_session_start(st->conn, SR_DS_CANDIDATE, &st->sr_sess2), SR_ERR_OK); + + /* setup NACM */ + rc = setup_nacm(state); + assert_int_equal(rc, 0); + + return 0; } static int local_teardown(void **state) { struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"edit1", "example2", "nacm-test1", "nacm-test2"}; if (!st) { return 0; } - /* Close the sessions and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); + /* close the session */ assert_int_equal(sr_session_stop(st->sr_sess2), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "example2"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "nacm-test1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "nacm-test2"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); /* close netopeer2 server */ - return np_glob_teardown(state); + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } static int diff --git a/tests/test_parallel_sessions.c b/tests/test_parallel_sessions.c index b0e975bad..fb2d6ec30 100644 --- a/tests/test_parallel_sessions.c +++ b/tests/test_parallel_sessions.c @@ -38,16 +38,27 @@ static int local_setup(void **state) { char test_name[256]; - int rv; + int rc; /* get test name */ np_glob_setup_test_name(test_name); /* setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); - return np_glob_setup_np2(state, test_name); + return np_glob_setup_np2(state, test_name, NULL, 0); +} + +static int +local_teardown(void **state) +{ + if (!*state) { + return 0; + } + + /* close netopeer2 server */ + return np_glob_teardown(state, NULL, 0); } struct thread_arg { @@ -157,5 +168,5 @@ main(int argc, char **argv) nc_verbosity(NC_VERB_WARNING); parse_arg(argc, argv); - return cmocka_run_group_tests(tests, local_setup, np_glob_teardown); + return cmocka_run_group_tests(tests, local_setup, local_teardown); } diff --git a/tests/test_rpc.c b/tests/test_rpc.c index 00cfe5d03..68df8bd0e 100644 --- a/tests/test_rpc.c +++ b/tests/test_rpc.c @@ -32,57 +32,39 @@ static int local_setup(void **state) { - struct np_test *st = *state; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/edit1.yang"; - int rv; + const char *modules[] = {NP_TEST_MODULE_DIR "/edit1.yang"}; + int rc; /* get test name */ np_glob_setup_test_name(test_name); /* setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* Connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - - /* Setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - st = *state; - /* Open the connection to start a session for the tests */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - rv |= setup_nacm(state); - } - return rv; + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); + + /* setup netopeer2 server */ + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + + /* setup NACM */ + rc = setup_nacm(state); + assert_int_equal(rc, 0); + + return 0; } static int local_teardown(void **state) { - struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"edit1"}; - if (!st) { - return 0; + /* close netopeer2 server */ + if (*state) { + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } - /* Close the session and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* Connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit1"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - - /* Close netopeer2 server */ - return np_glob_teardown(state); + return 0; } static int diff --git a/tests/test_sub_ntf.c b/tests/test_sub_ntf.c index 86554ae19..db09556c5 100644 --- a/tests/test_sub_ntf.c +++ b/tests/test_sub_ntf.c @@ -35,39 +35,29 @@ static int local_setup(void **state) { struct np_test *st; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/notif1.yang"; - const char *module2 = NP_TEST_MODULE_DIR "/notif2.yang"; - int rv; + const char *modules[] = {NP_TEST_MODULE_DIR "/notif1.yang", NP_TEST_MODULE_DIR "/notif2.yang"}; + int rc; /* get test name */ np_glob_setup_test_name(test_name); - /* setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module2, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); + /* setup environment */ + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); /* setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - /* state is allocated in np_glob_setup_np2 have to set here */ - st = *state; - /* Open connection to start a session for the tests */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess2), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - - /* Enable replay support */ - assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif1", 1)); - } - return rv; + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + st = *state; + + /* start second session for the tests */ + assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess2), SR_ERR_OK); + + /* enable replay support */ + assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif1", 1)); + + return 0; } static int @@ -103,32 +93,24 @@ static int local_teardown(void **state) { struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"notif1", "notif2"}; if (!st) { return 0; } - /* Disable replay support */ + /* disable replay support */ assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif1", 0)); assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif2", 0)); - /* Close the sessions and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); + /* close the session */ assert_int_equal(sr_session_stop(st->sr_sess2), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - /* connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "notif1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "notif2"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - - /* Remove the notifications */ + /* remove the notifications */ teardown_common(state); /* close netopeer2 server */ - return np_glob_teardown(state); + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } static void @@ -233,7 +215,7 @@ test_basic_sub(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Check for notification content */ RECV_NOTIF(st); @@ -260,7 +242,7 @@ test_replay_sub(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); SEND_RPC_ESTABSUB(st, NULL, "notif1", timestr, NULL); free(timestr); @@ -307,7 +289,7 @@ test_replay_real_time(void **state) "\n"; expected = data; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Subscribe to notifications */ SEND_RPC_ESTABSUB(st, NULL, "notif1", start_time, NULL); @@ -321,7 +303,7 @@ test_replay_real_time(void **state) " Second\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Receive the notification and test the contents */ RECV_NOTIF(st); @@ -374,7 +356,7 @@ test_stop_time(void **state) "\n"; expected = data; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); /* To subscribe to replay of notifications until time was called, should not include any called after */ @@ -420,7 +402,7 @@ test_stop_time(void **state) " Another\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); /* No other notification should arrive */ @@ -490,7 +472,7 @@ test_stream_no_pass(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); ASSERT_NO_NOTIF(st); FREE_TEST_VARS(st); } diff --git a/tests/test_sub_ntf_advanced.c b/tests/test_sub_ntf_advanced.c index 7501a1f5b..cb50fc6c5 100644 --- a/tests/test_sub_ntf_advanced.c +++ b/tests/test_sub_ntf_advanced.c @@ -35,40 +35,33 @@ static int local_setup(void **state) { struct np_test *st; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/notif1.yang"; - const char *module2 = NP_TEST_MODULE_DIR "/notif2.yang"; - int rv; + const char *modules[] = {NP_TEST_MODULE_DIR "/notif1.yang", NP_TEST_MODULE_DIR "/notif2.yang"}; + int rc; /* get test name */ np_glob_setup_test_name(test_name); - /* setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module2, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); + /* setup environment */ + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); /* setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - /* state is allocated in np_glob_setup_np2 have to set here */ - st = *state; - /* Open connection to start a sessions for the tests */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_OPERATIONAL, &st->sr_sess2), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - - /* Enable replay support */ - assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif1", 1)); - rv |= setup_nacm(state); - } - return rv; + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + st = *state; + + /* second session */ + assert_int_equal(sr_session_start(st->conn, SR_DS_OPERATIONAL, &st->sr_sess2), SR_ERR_OK); + + /* enable replay support */ + assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif1", 1)); + + /* setup NACM */ + rc = setup_nacm(state); + assert_int_equal(rc, 0); + + return 0; } static int @@ -104,31 +97,23 @@ static int local_teardown(void **state) { struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"notif1", "notif2"}; if (!st) { return 0; } - /* Disable replay support */ + /* disable replay support */ assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif1", 0)); - /* Close the sessions and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); + /* close the session */ assert_int_equal(sr_session_stop(st->sr_sess2), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "notif1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "notif2"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); /* Remove the notifications */ teardown_common(state); /* close netopeer2 server */ - return np_glob_teardown(state); + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } static void @@ -150,7 +135,7 @@ test_filter_pass(void **state) NOTIF_PARSE(st, data); /* Send the notification */ - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); RECV_NOTIF(st); assert_string_equal(data, st->str); FREE_TEST_VARS(st); @@ -175,7 +160,7 @@ test_filter_no_pass(void **state) NOTIF_PARSE(st, data); /* Notification should not pass */ - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); ASSERT_NO_NOTIF(st); FREE_TEST_VARS(st); } @@ -198,7 +183,7 @@ test_modifysub_filter(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); RECV_NOTIF(st); assert_string_equal(data, st->str); FREE_TEST_VARS(st); @@ -230,7 +215,7 @@ test_modifysub_filter(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); ASSERT_NO_NOTIF(st); FREE_TEST_VARS(st); } @@ -281,7 +266,7 @@ test_modifysub_stop_time(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); ASSERT_NO_NOTIF(st); FREE_TEST_VARS(st); } @@ -297,7 +282,7 @@ test_modifysub_fail_no_such_sub(void **state) /* Check if correct error-tag */ assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next), "invalid-value"); /* Check if correct error-app-tag */ - assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next->next->next->next), + assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next->next->next), "ietf-subscribed-notifications:no-such-subscription"); FREE_TEST_VARS(st); } @@ -334,7 +319,7 @@ test_deletesub(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); ASSERT_NO_NOTIF(st); FREE_TEST_VARS(st); } @@ -350,7 +335,7 @@ test_deletesub_fail(void **state) /* Check if correct error-tag */ assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next), "invalid-value"); /* Check if correct error-app-tag */ - assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next->next->next->next), + assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next->next->next), "ietf-subscribed-notifications:no-such-subscription"); FREE_TEST_VARS(st); } @@ -373,7 +358,7 @@ test_deletesub_fail_diff_sess(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); RECV_NOTIF(st); assert_string_equal(st->str, data); @@ -395,7 +380,7 @@ test_deletesub_fail_diff_sess(void **state) /* Check if correct error-tag */ assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next), "invalid-value"); /* Check if correct error-app-tag */ - assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next->next->next->next), + assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next->next->next), "ietf-subscribed-notifications:no-such-subscription"); FREE_TEST_VARS(st); @@ -475,7 +460,7 @@ test_ds_subscriptions_sent_event(void **state) "\n"; for (uint8_t i = 0; i < 3; i++) { NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); RECV_NOTIF(st); FREE_TEST_VARS(st); } @@ -523,7 +508,7 @@ test_ds_subscriptions_excluded_event(void **state) " Different\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); RECV_NOTIF(st); FREE_TEST_VARS(st); data = @@ -531,7 +516,7 @@ test_ds_subscriptions_excluded_event(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); ASSERT_NO_NOTIF(st); FREE_TEST_VARS(st); @@ -658,7 +643,7 @@ test_multiple_subscriptions_notif(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); /* Receive three notifications */ @@ -711,7 +696,7 @@ test_multiple_subscriptions_notif_interlaced(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); RECV_NOTIF(st); assert_string_equal(st->str, data); @@ -723,7 +708,7 @@ test_multiple_subscriptions_notif_interlaced(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); /* Establish second sub for a different stream */ @@ -745,7 +730,7 @@ test_multiple_subscriptions_notif_interlaced(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); /* Send notification to the second session */ @@ -759,7 +744,7 @@ test_multiple_subscriptions_notif_interlaced(void **state) " \n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); /* Receive the notification from first sub */ @@ -863,7 +848,7 @@ test_killsub_fail_no_such_sub(void **state) /* Check if correct error-tag */ assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next), "invalid-value"); /* Check if correct error-app-tag */ - assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next->next->next->next), + assert_string_equal(lyd_get_value(lyd_child(lyd_child(st->envp))->next->next->next), "ietf-subscribed-notifications:no-such-subscription"); FREE_TEST_VARS(st); } @@ -886,7 +871,7 @@ test_killsub_same_sess(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); RECV_NOTIF(st); assert_string_equal(st->str, data); @@ -915,7 +900,7 @@ test_killsub_same_sess(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); ASSERT_NO_NOTIF(st); FREE_TEST_VARS(st); @@ -940,7 +925,7 @@ test_killsub_diff_sess(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); RECV_NOTIF(st); assert_string_equal(st->str, data); @@ -979,7 +964,7 @@ test_killsub_diff_sess(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); ASSERT_NO_NOTIF(st); FREE_TEST_VARS(st); diff --git a/tests/test_sub_ntf_filter.c b/tests/test_sub_ntf_filter.c index a8ab7c516..0bea484d1 100644 --- a/tests/test_sub_ntf_filter.c +++ b/tests/test_sub_ntf_filter.c @@ -35,39 +35,29 @@ static int local_setup(void **state) { struct np_test *st; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/notif1.yang"; - const char *module2 = NP_TEST_MODULE_DIR "/notif2.yang"; - int rv; + const char *modules[] = {NP_TEST_MODULE_DIR "/notif1.yang", NP_TEST_MODULE_DIR "/notif2.yang"}; + int rc; /* get test name */ np_glob_setup_test_name(test_name); - /* setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module2, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); + /* setup environment*/ + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); /* setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - /* state is allocated in np_glob_setup_np2 have to set here */ - st = *state; - /* Open connection to start a session for the tests */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess2), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - - /* Enable replay support */ - assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif1", 1)); - } - return rv; + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + st = *state; + + /* second session */ + assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess2), SR_ERR_OK); + + /* enable replay support */ + assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif1", 1)); + + return 0; } static int @@ -124,32 +114,23 @@ static int local_teardown(void **state) { struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"notif1", "notif2"}; if (!st) { return 0; } - /* Disable replay support */ + /* disable replay support */ assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif1", 0)); - assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif2", 0)); - /* Close the sessions and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); + /* close the session */ assert_int_equal(sr_session_stop(st->sr_sess2), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "notif1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "notif2"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - /* Remove the notifications */ + /* remove the notifications */ teardown_common(state); /* close netopeer2 server */ - return np_glob_teardown(state); + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } static void @@ -169,7 +150,7 @@ test_basic_xpath_pass(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Check for notification content */ RECV_NOTIF(st); @@ -184,7 +165,7 @@ test_basic_xpath_no_pass(void **state) const char *data; /* Establish subscription */ - SEND_RPC_ESTABSUB(st, "/notif1:n1/first[.=Alt]", "notif1", NULL, NULL); + SEND_RPC_ESTABSUB(st, "/notif1:n1/first[.='Alt']", "notif1", NULL, NULL); ASSERT_OK_SUB_NTF(st); FREE_TEST_VARS(st); @@ -194,7 +175,7 @@ test_basic_xpath_no_pass(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* No notification should pass the filter */ ASSERT_NO_NOTIF(st); @@ -219,7 +200,7 @@ test_basic_subtree_pass(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Check for notification content */ RECV_NOTIF(st); @@ -248,7 +229,7 @@ test_basic_subtree_no_pass(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* No notification should pass the filter */ ASSERT_NO_NOTIF(st); @@ -269,7 +250,7 @@ setup_filter(void **state) " \n" " \n" " xpath-no-pass\n" - " /n1/first[.=Alt]\n" + " /n1/first[.='Alt']\n" " \n" " \n" " subtree-pass\n" @@ -302,7 +283,7 @@ test_ref_xpath_pass(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Check for notification content */ RECV_NOTIF(st); @@ -327,7 +308,7 @@ test_ref_xpath_no_pass(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* No notification should pass the filter */ ASSERT_NO_NOTIF(st); @@ -351,7 +332,7 @@ test_ref_subtree_pass(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Check for notification content */ RECV_NOTIF(st); @@ -376,7 +357,7 @@ test_ref_subtree_no_pass(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* No notification should pass the filter */ ASSERT_NO_NOTIF(st); @@ -401,7 +382,7 @@ test_filter_change(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Check for notification content */ RECV_NOTIF(st); @@ -450,7 +431,7 @@ test_filter_remove(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Check for notification content */ RECV_NOTIF(st); diff --git a/tests/test_subscribe_filter.c b/tests/test_subscribe_filter.c index 98040ea5c..5402eb444 100644 --- a/tests/test_subscribe_filter.c +++ b/tests/test_subscribe_filter.c @@ -78,60 +78,40 @@ static int local_setup(void **state) { struct np_test *st; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/notif1.yang"; - const char *module2 = NP_TEST_MODULE_DIR "/notif2.yang"; - int rv; + const char *modules[] = {NP_TEST_MODULE_DIR "/notif1.yang", NP_TEST_MODULE_DIR "/notif2.yang"}; + int rc; /* get test name */ np_glob_setup_test_name(test_name); - /* Setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* Connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module2, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - - /* Setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - /* State is allocated in np_glob_setup_np2 have to set here */ - st = *state; - /* Open connection to start a session for the tests */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_OPERATIONAL, &st->sr_sess), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - setup_data(state); - } - return rv; + /* setup environment */ + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); + + /* setup netopeer2 server */ + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + st = *state; + + /* use operational DS */ + assert_int_equal(sr_session_switch_ds(st->sr_sess, SR_DS_OPERATIONAL), SR_ERR_OK); + setup_data(state); + + return 0; } static int local_teardown(void **state) { - struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"notif1", "notif2"}; - if (!st) { + if (!*state) { return 0; } - /* Close the session and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* Connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "notif1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "notif2"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - - /* Close netopeer2 server */ - return np_glob_teardown(state); + /* close netopeer2 server */ + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } static void @@ -147,7 +127,7 @@ test_basic_notif(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Receive the notification and test the contents */ RECV_NOTIF(st); @@ -173,7 +153,7 @@ test_list_notif(void **state) " \n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Receive the notification and test the contents */ RECV_NOTIF(st); @@ -230,7 +210,7 @@ test_subtree_filter_notif_selection_node_no_pass(void **state) " \n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* No notification should pass due to the filter */ ASSERT_NO_NOTIF(st); @@ -250,7 +230,7 @@ test_subtree_filter_notif_selection_node_pass(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Notification should pass the filter */ RECV_NOTIF(st); @@ -282,7 +262,7 @@ test_subtree_filter_notif_content_match_node_no_pass(void **state) " \n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* No notification should pass due to the filter */ ASSERT_NO_NOTIF(st); @@ -313,7 +293,7 @@ test_subtree_filter_notif_content_match_node_pass(void **state) " \n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Notification should pass the filter */ RECV_NOTIF(st); @@ -365,7 +345,7 @@ test_xpath_filter_notif_selection_node_no_pass(void **state) " \n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* No notification should pass due to the filter */ ASSERT_NO_NOTIF(st); @@ -385,7 +365,7 @@ test_xpath_filter_notif_selection_node_pass(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Notification should pass the filter */ RECV_NOTIF(st); @@ -411,7 +391,7 @@ test_xpath_filter_notif_content_match_node_no_pass(void **state) " \n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* No notification should pass due to the filter */ ASSERT_NO_NOTIF(st); @@ -436,7 +416,7 @@ test_xpath_filter_notif_content_match_node_pass(void **state) " \n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Notification should pass the filter */ RECV_NOTIF(st); @@ -463,7 +443,7 @@ test_xpath_boolean_no_pass(void **state) " \n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* No notification should pass due to the filter */ ASSERT_NO_NOTIF(st); @@ -489,7 +469,7 @@ test_xpath_boolean_pass(void **state) " \n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Notification should pass the filter */ RECV_NOTIF(st); diff --git a/tests/test_subscribe_param.c b/tests/test_subscribe_param.c index a54fd09b8..f8862fc4d 100644 --- a/tests/test_subscribe_param.c +++ b/tests/test_subscribe_param.c @@ -67,39 +67,29 @@ static int local_setup(void **state) { struct np_test *st; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/notif1.yang"; - const char *module2 = NP_TEST_MODULE_DIR "/notif2.yang"; - int rv; + const char *modules[] = {NP_TEST_MODULE_DIR "/notif1.yang", NP_TEST_MODULE_DIR "/notif2.yang"}; + int rc; /* get test name */ np_glob_setup_test_name(test_name); - /* Setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module2, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - - /* Setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - /* State is allocated in np_glob_setup_np2 have to set here */ - st = *state; - /* Open connection to start a session for the tests */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess2), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - - /* Enable replay support */ - assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif1", 1)); - } - return rv; + /* setup environment */ + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); + + /* setup netopeer2 server */ + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + st = *state; + + /* second running session */ + assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess2), SR_ERR_OK); + + /* rnable replay support */ + assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif1", 1)); + + return 0; } static int @@ -129,31 +119,23 @@ static int local_teardown(void **state) { struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"notif1", "notif2"}; if (!st) { return 0; } - /* Disable replay support */ + /* disable replay support */ assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "notif1", 0)); - /* Close the sessions and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); + /* close the session */ assert_int_equal(sr_session_stop(st->sr_sess2), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* Connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "notif1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "notif2"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - /* Remove the notfications */ + /* remove the notfications */ clear_notif(state); /* close netopeer2 server */ - return np_glob_teardown(state); + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } static void @@ -312,7 +294,7 @@ test_basic_replay(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Subscribe to notfications */ reestablish_sub(state, NULL, start_time, NULL); @@ -353,7 +335,7 @@ test_replay_real_time(void **state) "\n"; expected = data; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Subscribe to notfications */ reestablish_sub(state, NULL, timestr, NULL); @@ -365,7 +347,7 @@ test_replay_real_time(void **state) " Second\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Receive the notification and test the contents */ RECV_NOTIF(st); @@ -413,7 +395,7 @@ test_stop_time(void **state) NOTIF_PARSE(st, data); /* Send the notification */ - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); /* To subscribe to replay of notifications until time was called, should not include any called after */ @@ -448,7 +430,7 @@ test_stop_time(void **state) " Another\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); /* No other notification should arrive */ @@ -511,7 +493,7 @@ test_stop_time_sub_end(void **state) " Second\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); /* Receive the notification and test the contents */ @@ -570,7 +552,7 @@ test_stream_no_pass(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* It should no be recieved since it is in a a different stream than subscribed to */ ASSERT_NO_NOTIF(st); @@ -592,7 +574,7 @@ test_stream_pass(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); /* Receive the notification and test the contents */ RECV_NOTIF(st); @@ -618,7 +600,7 @@ test_stream_no_pass_start_time(void **state) " Test\n" "\n"; NOTIF_PARSE(st, data); - assert_int_equal(sr_event_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); + assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); /* Subscribe to notfications from a different stream */ diff --git a/tests/test_url.c b/tests/test_url.c index ef317294c..5f5dfb6e6 100644 --- a/tests/test_url.c +++ b/tests/test_url.c @@ -67,59 +67,41 @@ setup_nacm_rules(void **state) static int local_setup(void **state) { - struct np_test *st = *state; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/edit1.yang"; - int rv; + const char *modules[] = {NP_TEST_MODULE_DIR "/edit1.yang"}; + int rc; /* get test name */ np_glob_setup_test_name(test_name); - /* setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); + /* setup environment */ + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); /* setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - /* state is allocated in np_glob_setup_np2 have to set here */ - st = *state; - /* Open connection to start a session for the tests */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - rv |= setup_nacm(state); - rv |= setup_nacm_rules(state); - } - return rv; + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + + /* setup NACM */ + rc = setup_nacm(state); + assert_int_equal(rc, 0); + rc = setup_nacm_rules(state); + assert_int_equal(rc, 0); + + return 0; } static int local_teardown(void **state) { - struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"edit1"}; - if (!st) { - return 0; + /* close netopeer2 server */ + if (*state) { + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } - /* Close the session and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit1"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - - /* close netopeer2 server */ - return np_glob_teardown(state); + return 0; } static void diff --git a/tests/test_with_defaults.c b/tests/test_with_defaults.c index 1bcb28edc..fbe9c0fdb 100644 --- a/tests/test_with_defaults.c +++ b/tests/test_with_defaults.c @@ -32,57 +32,35 @@ static int local_setup(void **state) { - struct np_test *st = *state; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/defaults1.yang"; - int rv; + const char *modules[] = {NP_TEST_MODULE_DIR "/defaults1.yang"}; + int rc; /* get test name */ np_glob_setup_test_name(test_name); - /* setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); + /* setup environment */ + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); /* setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - /* state is allocated in np_glob_setup_np2 have to set here */ - st = *state; - /* Open connection to start a session for the tests */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - } - return rv; + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + + return 0; } static int local_teardown(void **state) { - struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"defaults1"}; - if (!st) { + if (!*state) { return 0; } - /* Close the session and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "defaults1"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - /* close netopeer2 server */ - return np_glob_teardown(state); + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } static void diff --git a/tests/test_yang_push.c b/tests/test_yang_push.c index f9da5529a..0ec5c72c2 100644 --- a/tests/test_yang_push.c +++ b/tests/test_yang_push.c @@ -35,38 +35,26 @@ static int local_setup(void **state) { struct np_test *st; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/edit1.yang"; - const char *module2 = NP_TEST_MODULE_DIR "/edit2.yang"; - int rv; + const char *modules[] = {NP_TEST_MODULE_DIR "/edit1.yang", NP_TEST_MODULE_DIR "/edit2.yang"}; + int rc; /* get test name */ np_glob_setup_test_name(test_name); - /* setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module2, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); + /* setup environment */ + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); /* setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - /* state is allocated in np_glob_setup_np2 have to set here */ - st = *state; - /* Open connection to start a session for the tests */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - - /* Enable replay support */ - assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "edit1", 1)); - } - return rv; + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + st = *state; + + /* enable replay support */ + assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "edit1", 1)); + + return 0; } static int @@ -114,28 +102,17 @@ static int local_teardown(void **state) { struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"edit1", "edit2"}; if (!st) { return 0; } - /* Disable replay support */ + /* disable replay support */ assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "edit1", 0)); - /* Close the sessions and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); - assert_int_equal(sr_session_stop(st->sr_sess2), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit2"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - /* close netopeer2 server */ - return np_glob_teardown(state); + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } static void diff --git a/tests/test_yang_push_advanced.c b/tests/test_yang_push_advanced.c index 6904e22c6..726082409 100644 --- a/tests/test_yang_push_advanced.c +++ b/tests/test_yang_push_advanced.c @@ -35,38 +35,26 @@ static int local_setup(void **state) { struct np_test *st; - sr_conn_ctx_t *conn; char test_name[256]; - const char *module1 = NP_TEST_MODULE_DIR "/edit1.yang"; - const char *module2 = NP_TEST_MODULE_DIR "/edit2.yang"; - int rv; + const char *modules[] = {NP_TEST_MODULE_DIR "/edit1.yang", NP_TEST_MODULE_DIR "/edit2.yang"}; + int rc; /* get test name */ np_glob_setup_test_name(test_name); - /* setup environment necessary for installing module */ - rv = np_glob_setup_env(test_name); - assert_int_equal(rv, 0); - - /* connect to server and install test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module1, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_install_module(conn, module2, NULL, NULL), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); + /* setup environment */ + rc = np_glob_setup_env(test_name); + assert_int_equal(rc, 0); /* setup netopeer2 server */ - if (!(rv = np_glob_setup_np2(state, test_name))) { - /* state is allocated in np_glob_setup_np2 have to set here */ - st = *state; - /* Open connection to start a session for the tests */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &st->conn), SR_ERR_OK); - assert_int_equal(sr_session_start(st->conn, SR_DS_RUNNING, &st->sr_sess), SR_ERR_OK); - assert_non_null(st->ctx = sr_get_context(st->conn)); - - /* Enable replay support */ - assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "edit1", 1)); - } - return rv; + rc = np_glob_setup_np2(state, test_name, modules, sizeof modules / sizeof *modules); + assert_int_equal(rc, 0); + st = *state; + + /* enable replay support */ + assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "edit1", 1)); + + return 0; } static int @@ -112,28 +100,17 @@ static int local_teardown(void **state) { struct np_test *st = *state; - sr_conn_ctx_t *conn; + const char *modules[] = {"edit1", "edit2"}; if (!st) { return 0; } - /* Disable replay support */ + /* disable replay support */ assert_int_equal(SR_ERR_OK, sr_set_module_replay_support(st->conn, "edit1", 0)); - /* Close the sessions and connection needed for tests */ - assert_int_equal(sr_session_stop(st->sr_sess), SR_ERR_OK); - assert_int_equal(sr_session_stop(st->sr_sess2), SR_ERR_OK); - assert_int_equal(sr_disconnect(st->conn), SR_ERR_OK); - - /* connect to server and remove test modules */ - assert_int_equal(sr_connect(SR_CONN_DEFAULT, &conn), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit1"), SR_ERR_OK); - assert_int_equal(sr_remove_module(conn, "edit2"), SR_ERR_OK); - assert_int_equal(sr_disconnect(conn), SR_ERR_OK); - /* close netopeer2 server */ - return np_glob_teardown(state); + return np_glob_teardown(state, modules, sizeof modules / sizeof *modules); } static void From c3d1dd1ce71f8f71133327394ef8a20857ca013f Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Tue, 9 Nov 2021 13:18:04 +0100 Subject: [PATCH 02/70] VERSION bump to version 2.1.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e53661ce4..67f644c21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.0.35) +set(NP2SRV_VERSION 2.1.0) # libyang required SO version set(LIBYANG_DEP_SOVERSION_MAJOR 2) From 112f35375cf5bdd1c3c9da32cbd08df8d581a516 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 12 Nov 2021 15:36:07 +0100 Subject: [PATCH 03/70] actions UPDATE enable_tests var renamed --- .github/workflows/devel-push.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/devel-push.yml b/.github/workflows/devel-push.yml index 868b1e35b..dbd13d8bf 100644 --- a/.github/workflows/devel-push.yml +++ b/.github/workflows/devel-push.yml @@ -88,7 +88,7 @@ jobs: cd libyang mkdir build cd build - CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.dep-build-type }} -DENABLE_BUILD_TESTS=OFF .. + CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.dep-build-type }} -DENABLE_TESTS=OFF .. make -j2 sudo make install @@ -110,7 +110,7 @@ jobs: cd libnetconf2 mkdir build cd build - CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.build-type }} -DENABLE_BUILD_TESTS=OFF .. + CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.build-type }} -DENABLE_TESTS=OFF .. make -j2 sudo make install From 7324800158cab9a77712e7d31671e2df785698e0 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Sat, 13 Nov 2021 09:55:09 +0100 Subject: [PATCH 04/70] netconf acm MAINTENANCE existing groups check For coverity. --- src/netconf_acm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/netconf_acm.c b/src/netconf_acm.c index 624b69c24..3149c46b8 100644 --- a/src/netconf_acm.c +++ b/src/netconf_acm.c @@ -223,6 +223,7 @@ ncac_group_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UN case SR_OP_DELETED: /* find it */ group = ncac_group_find(group_name); + assert(group && nacm.group_count); /* delete it */ free(group->name); From b5b47119cacb3213dfdf17951add771384be6778 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Sat, 13 Nov 2021 09:55:38 +0100 Subject: [PATCH 05/70] test BUGFIX missing nacm setup --- tests/test_candidate.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_candidate.c b/tests/test_candidate.c index e3450f59b..09c7c7f92 100644 --- a/tests/test_candidate.c +++ b/tests/test_candidate.c @@ -52,6 +52,10 @@ local_setup(void **state) /* candidate session */ assert_int_equal(sr_session_start(st->conn, SR_DS_CANDIDATE, &st->sr_sess2), SR_ERR_OK); + /* setup NACM */ + rc = setup_nacm(state); + assert_int_equal(rc, 0); + return 0; } From a2f1532c82ebb2f18a759b7413f607160af459d8 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 22 Nov 2021 15:08:47 +0100 Subject: [PATCH 06/70] common BUGFIX handle no returned data --- src/common.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/common.c b/src/common.c index 26d15c28b..72df49ffb 100644 --- a/src/common.c +++ b/src/common.c @@ -1151,6 +1151,11 @@ op_filter_data_get(sr_session_ctx_t *session, uint32_t max_depth, sr_get_oper_op return rc; } + if (!sr_data) { + /* no data */ + continue; + } + /* merge */ lyrc = lyd_merge_siblings(data, sr_data->tree, LYD_MERGE_DESTRUCT); sr_data->tree = NULL; From 8002a86802f8c0751833c43d18585f56ead9405e Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 22 Nov 2021 15:09:17 +0100 Subject: [PATCH 07/70] test UPDATE try to wait a bit to see server output --- tests/test_parallel_sessions.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_parallel_sessions.c b/tests/test_parallel_sessions.c index fb2d6ec30..b6294bc82 100644 --- a/tests/test_parallel_sessions.c +++ b/tests/test_parallel_sessions.c @@ -87,6 +87,9 @@ recv_reply_error_print(struct np_test *st, const struct lyd_node *op, const stru } printf("\n"); + /* wait until all output is written to the file */ + sleep(2); + /* print netopeer2 log */ printf("np2 log:\n"); assert_int_not_equal(-1, asprintf(&path, "%s/%s/%s", NP_SR_REPOS_DIR, st->test_name, NP_LOG_FILE)); From 436e2aea11b15e2cc28be4e36ae9cb86aa19d3a3 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 22 Nov 2021 15:25:53 +0100 Subject: [PATCH 08/70] common BUGFIX subtree opaque node with identityref Fixes #1056 --- src/common.c | 61 ++++++++++++++++++++++++++++++++++-- tests/modules/filter1.yang | 18 ++++++++++- tests/test_filter.c | 63 +++++++++++++++++++++++++++++--------- 3 files changed, 124 insertions(+), 18 deletions(-) diff --git a/src/common.c b/src/common.c index 72df49ffb..81efa9202 100644 --- a/src/common.c +++ b/src/common.c @@ -38,6 +38,7 @@ #endif #include +#include #include #include "common.h" @@ -821,6 +822,53 @@ filter_xpath_print_node_module(const struct lyd_node *node) return mod; } +/** + * @brief Get value of a node to use in XPath filter. + * + * @param[in] node Subtree filter node. + * @param[out] dynamic Whether the value eneds to be freed. + * @return String value to use; + * @return NULL on error. + */ +static char * +filter_xpath_buf_get_value(const struct lyd_node *node, int *dynamic) +{ + struct lyd_node_opaq *opaq; + const char *ptr; + const struct lys_module *mod; + char *val_str; + + *dynamic = 0; + + if (node->schema) { + /* data node, canonical value should be fine */ + return (char *)lyd_get_value(node); + } + + opaq = (struct lyd_node_opaq *)node; + + if (!(ptr = strchr(opaq->value, ':'))) { + /* no prefix, use it directly */ + return (char *)opaq->value; + } + + /* assume identity, try to get its module */ + mod = lyplg_type_identity_module(LYD_CTX(node), NULL, opaq->value, ptr - opaq->value, opaq->format, + opaq->val_prefix_data); + + if (!mod) { + /* unknown module, use as is */ + return (char *)opaq->value; + } + + /* print the module name instead of the prefix */ + if (asprintf(&val_str, "%s:%s", mod->name, ptr + 1) == -1) { + return NULL; + } + *dynamic = 1; + return val_str; +} + /** * @brief Append subtree filter node to XPath filter string buffer. * @@ -836,8 +884,8 @@ static int filter_xpath_buf_append_content(const struct lyd_node *node, char **buf, int size) { const struct lys_module *mod = NULL; - int new_size; - char *buf_new, quot; + int new_size, dynamic; + char *buf_new, *val_str, quot; assert(!node->schema || (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))); @@ -867,12 +915,19 @@ filter_xpath_buf_append_content(const struct lyd_node *node, char **buf, int siz } *buf = buf_new; + /* learn which quotes are safe to use */ if (strchr(lyd_get_value(node), '\'')) { quot = '\"'; } else { quot = '\''; } - sprintf((*buf) + (size - 1), "=%c%s%c]", quot, lyd_get_value(node), quot); + + /* get proper value */ + val_str = filter_xpath_buf_get_value(node, &dynamic); + sprintf((*buf) + (size - 1), "=%c%s%c]", quot, val_str, quot); + if (dynamic) { + free(val_str); + } return new_size; } diff --git a/tests/modules/filter1.yang b/tests/modules/filter1.yang index 9b2e8d206..7520ee6e9 100644 --- a/tests/modules/filter1.yang +++ b/tests/modules/filter1.yang @@ -6,6 +6,10 @@ module filter1 { prefix "inet"; } + import filter1-imp { + prefix f1i; + } + container top { container devices { container desktops { @@ -34,5 +38,17 @@ module filter1 { } } } + + list some-list { + key "k"; + leaf k { + type string; + } + leaf val { + type identityref { + base f1i:ident-base; + } + } + } } -} \ No newline at end of file +} diff --git a/tests/test_filter.c b/tests/test_filter.c index dd0511c57..46caeafad 100644 --- a/tests/test_filter.c +++ b/tests/test_filter.c @@ -68,7 +68,6 @@ setup_data(void **state) " \n" " \n" "\n"; - SR_EDIT(st, data); FREE_TEST_VARS(st); @@ -93,7 +92,6 @@ setup_data(void **state) " 13\n" " \n" "\n"; - SR_EDIT(st, data); FREE_TEST_VARS(st); @@ -107,7 +105,6 @@ setup_data(void **state) " \n" " \n" "\n"; - SR_EDIT(st, data); FREE_TEST_VARS(st); @@ -152,35 +149,40 @@ setup_data(void **state) " \n" " \n" " \n" + " \n" + " a\n" + " f1i:ident-val1\n" + " \n" + " \n" + " b\n" + " f1i:ident-val2\n" + " \n" "\n"; - SR_EDIT(st, data); FREE_TEST_VARS(st); data = "Test\n"; - SR_EDIT(st, data); FREE_TEST_VARS(st); } static int -change_cb(sr_session_ctx_t *session, uint32_t sub_id, - const char *module_name, const char *xpath, - sr_event_t event, uint32_t request_id, void *private_data) +change_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *xpath, sr_event_t event, + uint32_t request_id, void *private_data) { (void) session; (void) sub_id; (void) module_name; (void) xpath; (void) event; (void) request_id; (void) private_data; + return SR_ERR_OK; } static int -change_serial_num(sr_session_ctx_t *session, uint32_t sub_id, - const char *module_name, const char *path, - const char *request_xpath, uint32_t request_id, - struct lyd_node **parent, void *private_data) +change_serial_num(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *path, + const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data) { (void) session; (void) sub_id; (void) module_name; (void) path; (void) request_xpath; (void) request_id; (void) private_data; + if (!lyd_new_path(*parent, NULL, "serial-num", "1234", 0, NULL)) { return SR_ERR_OK; } else { @@ -194,7 +196,7 @@ local_setup(void **state) struct np_test *st; char test_name[256]; const char *modules[] = { - NP_TEST_MODULE_DIR "/example2.yang", NP_TEST_MODULE_DIR "/filter1.yang", + NP_TEST_MODULE_DIR "/example2.yang", NP_TEST_MODULE_DIR "/filter1-imp.yang", NP_TEST_MODULE_DIR "/filter1.yang", NP_TEST_MODULE_DIR "/xpath.yang", NP_TEST_MODULE_DIR "/issue1.yang", NP_TEST_MODULE_DIR "/edit1.yang" }; int rc; @@ -226,7 +228,7 @@ static int local_teardown(void **state) { struct np_test *st = *state; - const char *modules[] = {"example2", "filter1", "xpath", "issue1", "edit1"}; + const char *modules[] = {"example2", "filter1-imp", "filter1", "xpath", "issue1", "edit1"}; if (!st) { return 0; @@ -521,6 +523,14 @@ test_xpath_namespaces(void **state) " \n" " \n" " \n" + " \n" + " a\n" + " f1i:ident-val1\n" + " \n" + " \n" + " b\n" + " f1i:ident-val2\n" + " \n" " \n" " \n" " \n" @@ -614,6 +624,31 @@ test_subtree_content_match(void **state) assert_string_equal(st->str, expected); FREE_TEST_VARS(st); + + filter = + "\n" + " \n" + " f1i:ident-val1\n" + " \n" + "\n"; + + GET_CONFIG_FILTER(st, filter); + + expected = + "\n" + " \n" + " \n" + " \n" + " a\n" + " f1i:ident-val1\n" + " \n" + " \n" + " \n" + "\n"; + + assert_string_equal(st->str, expected); + + FREE_TEST_VARS(st); } static void From b678b3ec8117ef18bf8c9846d6a68f55730aca30 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 22 Nov 2021 15:26:21 +0100 Subject: [PATCH 09/70] VERSION bump to version 2.1.1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 67f644c21..6b8e72214 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.0) +set(NP2SRV_VERSION 2.1.1) # libyang required SO version set(LIBYANG_DEP_SOVERSION_MAJOR 2) From 56e75065e7db75f3f5b0bc170c077ab03b4b7b2c Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 22 Nov 2021 15:35:34 +0100 Subject: [PATCH 10/70] test BUGFIX missing file --- tests/modules/filter1-imp.yang | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/modules/filter1-imp.yang diff --git a/tests/modules/filter1-imp.yang b/tests/modules/filter1-imp.yang new file mode 100644 index 000000000..0716bd929 --- /dev/null +++ b/tests/modules/filter1-imp.yang @@ -0,0 +1,14 @@ +module filter1-imp { + namespace "urn:f1i"; + prefix f1i; + + identity ident-base; + + identity ident-val1 { + base ident-base; + } + + identity ident-val2 { + base ident-base; + } +} From fe9cb0608de5793a98358d693677f17a75b0587c Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Tue, 23 Nov 2021 10:47:06 +0100 Subject: [PATCH 11/70] actions UPDATE run tests in parallel --- .github/workflows/ci.yml | 2 +- .github/workflows/devel-push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14fdd551e..96e1771b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -179,4 +179,4 @@ jobs: - name: Test shell: bash working-directory: ${{ github.workspace }}/build - run: ctest --output-on-failure + run: ctest --output-on-failure -j4 diff --git a/.github/workflows/devel-push.yml b/.github/workflows/devel-push.yml index dbd13d8bf..33ea391a4 100644 --- a/.github/workflows/devel-push.yml +++ b/.github/workflows/devel-push.yml @@ -137,7 +137,7 @@ jobs: - name: Test shell: bash working-directory: ${{ github.workspace }}/build - run: ctest --output-on-failure + run: ctest --output-on-failure -j4 - name: Upload to Coverity.com shell: bash From 89896e8b97aa11564954977184e7e638240ef8e4 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Tue, 23 Nov 2021 10:47:33 +0100 Subject: [PATCH 12/70] common BUGFIX allocate for the actual value --- src/common.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/common.c b/src/common.c index 81efa9202..ae2ca8d2c 100644 --- a/src/common.c +++ b/src/common.c @@ -884,7 +884,7 @@ static int filter_xpath_buf_append_content(const struct lyd_node *node, char **buf, int size) { const struct lys_module *mod = NULL; - int new_size, dynamic; + int new_size, dynamic = 0; char *buf_new, *val_str, quot; assert(!node->schema || (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))); @@ -895,8 +895,7 @@ filter_xpath_buf_append_content(const struct lyd_node *node, char **buf, int siz new_size = size + 1 + (mod ? strlen(mod->name) + 1 : 0) + strlen(LYD_NAME(node)); buf_new = realloc(*buf, new_size); if (!buf_new) { - EMEM; - return -1; + goto error; } *buf = buf_new; sprintf((*buf) + (size - 1), "[%s%s%s", (mod ? mod->name : ""), (mod ? ":" : ""), LYD_NAME(node)); @@ -904,32 +903,43 @@ filter_xpath_buf_append_content(const struct lyd_node *node, char **buf, int siz size = filter_xpath_buf_append_attrs(node->meta, buf, size); if (size < 1) { - return size; + goto error; } - new_size = size + 2 + strlen(lyd_get_value(node)) + 2; + /* get proper value */ + val_str = filter_xpath_buf_get_value(node, &dynamic); + if (!val_str) { + goto error; + } + + new_size = size + 2 + strlen(val_str) + 2; buf_new = realloc(*buf, new_size); if (!buf_new) { - EMEM; - return -1; + goto error; } *buf = buf_new; /* learn which quotes are safe to use */ - if (strchr(lyd_get_value(node), '\'')) { + if (strchr(val_str, '\'')) { quot = '\"'; } else { quot = '\''; } - /* get proper value */ - val_str = filter_xpath_buf_get_value(node, &dynamic); + /* append */ sprintf((*buf) + (size - 1), "=%c%s%c]", quot, val_str, quot); + if (dynamic) { free(val_str); } - return new_size; + +error: + EMEM; + if (dynamic) { + free(val_str); + } + return -1; } /** From 65c285aa2262604a8a47333f86c35da9645f9961 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Tue, 23 Nov 2021 10:48:49 +0100 Subject: [PATCH 13/70] VERSION bump to version 2.1.2 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b8e72214..ca40b6e0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.1) +set(NP2SRV_VERSION 2.1.2) # libyang required SO version set(LIBYANG_DEP_SOVERSION_MAJOR 2) From ed157328878565f6e3e2541ed17bd283502127cf Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Tue, 23 Nov 2021 11:04:32 +0100 Subject: [PATCH 14/70] test UPDATE account for really slow test run When using valgrind. --- tests/test_subscribe_param.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/test_subscribe_param.c b/tests/test_subscribe_param.c index f8862fc4d..88f7502b1 100644 --- a/tests/test_subscribe_param.c +++ b/tests/test_subscribe_param.c @@ -197,7 +197,7 @@ test_start_time_invalid(void **state) /* startTime is in the future */ assert_int_not_equal(-1, clock_gettime(CLOCK_REALTIME, &ts)); - ts.tv_sec += 10; + ts.tv_sec += 30; assert_int_equal(LY_SUCCESS, ly_time_ts2str(&ts, &start_time)); /* Reestablish NETCONF connection */ @@ -211,10 +211,9 @@ test_start_time_invalid(void **state) assert_int_equal(NC_MSG_RPC, st->msgtype); /* Check reply */ - st->msgtype = NC_MSG_NOTIF; - while (st->msgtype == NC_MSG_NOTIF) { + do { st->msgtype = nc_recv_reply(st->nc_sess, st->rpc, st->msgid, 1000, &st->envp, &st->op); - } + } while (st->msgtype == NC_MSG_NOTIF); assert_int_equal(NC_MSG_REPLY, st->msgtype); assert_null(st->op); assert_string_equal(LYD_NAME(lyd_child(st->envp)), "rpc-error"); From 0040808a6e193571af07d68ecaf1232349063f0a Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 26 Nov 2021 11:33:44 +0100 Subject: [PATCH 15/70] tests BUGFIX handle rare data race --- tests/test_yang_push.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/test_yang_push.c b/tests/test_yang_push.c index 0ec5c72c2..5f307852e 100644 --- a/tests/test_yang_push.c +++ b/tests/test_yang_push.c @@ -122,14 +122,14 @@ test_periodic_basic(void **state) const char *template, *data; char *ntf; - /* Establish periodic push */ + /* establish periodic push */ st->rpc = nc_rpc_establishpush_periodic("ietf-datastores:running", NULL, NULL, NULL, 10, NULL, NC_PARAMTYPE_CONST); st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); assert_int_equal(st->msgtype, NC_MSG_RPC); ASSERT_OK_SUB_NTF(st); FREE_TEST_VARS(st); - /* Receive a notification */ + /* receive a notification */ RECV_NOTIF(st); template = "\n" @@ -141,13 +141,21 @@ test_periodic_basic(void **state) free(ntf); FREE_TEST_VARS(st); - /* Put some data into the datastore */ + /* put some data into the datastore */ data = "TestFirst"; SR_EDIT(st, data); FREE_TEST_VARS(st); - /* Receive a notification with the new data */ + /* receive a notification */ RECV_NOTIF(st); + if (!strcmp(st->str, ntf)) { + /* rare result of a data race when a notification arrived still with the previous data */ + FREE_TEST_VARS(st); + + /* receive next notification, now it must have the new data (unless the edit took longer than is + * the subscription period, hope not) */ + RECV_NOTIF(st); + } template = "\n" " %d\n" @@ -156,10 +164,9 @@ test_periodic_basic(void **state) " \n" "\n"; assert_int_not_equal(-1, asprintf(&ntf, template, st->ntf_id)); - assert_string_equal(st->str, ntf); FREE_TEST_VARS(st); - /* Test yet again if arives with the same data */ + /* test yet again if arives with the same data */ RECV_NOTIF(st); assert_string_equal(st->str, ntf); FREE_TEST_VARS(st); From 0fd5ee15f93dca42f22876d98cdbbd25fe02f603 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 26 Nov 2021 11:47:39 +0100 Subject: [PATCH 16/70] tests BUGFIX reading freed memory --- tests/test_yang_push.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_yang_push.c b/tests/test_yang_push.c index 5f307852e..453f411e1 100644 --- a/tests/test_yang_push.c +++ b/tests/test_yang_push.c @@ -138,7 +138,6 @@ test_periodic_basic(void **state) "\n"; assert_int_not_equal(-1, asprintf(&ntf, template, st->ntf_id)); assert_string_equal(st->str, ntf); - free(ntf); FREE_TEST_VARS(st); /* put some data into the datastore */ @@ -163,6 +162,7 @@ test_periodic_basic(void **state) " TestFirst\n" " \n" "\n"; + free(ntf); assert_int_not_equal(-1, asprintf(&ntf, template, st->ntf_id)); FREE_TEST_VARS(st); From 288fbca91bdf939e4958234e4737ead453d04706 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 26 Nov 2021 13:41:42 +0100 Subject: [PATCH 17/70] scripts FEATURE allow openssl path override Refs #1081 --- scripts/merge_hostkey.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/merge_hostkey.sh b/scripts/merge_hostkey.sh index 791ea6194..efd74b31f 100755 --- a/scripts/merge_hostkey.sh +++ b/scripts/merge_hostkey.sh @@ -12,8 +12,9 @@ else SYSREPOCFG=`which sysrepocfg` fi -# avoid problems with sudo PATH -if [ `id -u` -eq 0 ]; then +if [ -n "$OPENSSL_EXECUTABLE" ]; then + OPENSSL="$OPENSSL_EXECUTABLE" +elif [ `id -u` -eq 0 ]; then OPENSSL=`su -c 'which openssl' -l $USER` else OPENSSL=`which openssl` From bea89f7ae1adac3658118e4f44342c3083d8aaaa Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 26 Nov 2021 14:15:28 +0100 Subject: [PATCH 18/70] test UPDATE print unexpected replies --- tests/np_test.h | 30 ++++++++++++++++++++++++++---- tests/test_subscribe_param.c | 8 +------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/tests/np_test.h b/tests/np_test.h index a041b6e89..1aa8de939 100644 --- a/tests/np_test.h +++ b/tests/np_test.h @@ -46,8 +46,19 @@ state->msgtype = nc_recv_reply(state->nc_sess, state->rpc, state->msgid, 3000, &state->envp, &state->op); \ } while (state->msgtype == NC_MSG_NOTIF); \ assert_int_equal(state->msgtype, NC_MSG_REPLY); \ - assert_null(state->op); \ - assert_string_equal(LYD_NAME(lyd_child(state->envp)), "ok"); + if (strcmp(LYD_NAME(lyd_child(state->envp)), "ok")) { \ + printf("Expected \"ok\" reply, received \"%s\" instead.\n", LYD_NAME(lyd_child(state->envp))); \ + printf("op:\n"); \ + if (state->op) { \ + lyd_print_file(stdout, state->op, LYD_XML, 0); \ + } \ + printf("\nenvp:\n"); \ + if (state->envp) { \ + lyd_print_file(stdout, state->envp, LYD_XML, 0); \ + } \ + fail(); \ + } \ + assert_null(state->op); #define ASSERT_OK_REPLY_SESS2(state) \ state->msgtype = nc_recv_reply(state->nc_sess2, state->rpc, state->msgid, 3000, &state->envp, &state->op); \ @@ -58,8 +69,19 @@ #define ASSERT_RPC_ERROR(state) \ state->msgtype = nc_recv_reply(state->nc_sess, state->rpc, state->msgid, 3000, &state->envp, &state->op); \ assert_int_equal(state->msgtype, NC_MSG_REPLY); \ - assert_null(state->op); \ - assert_string_equal(LYD_NAME(lyd_child(state->envp)), "rpc-error"); + if (strcmp(LYD_NAME(lyd_child(state->envp)), "rpc-error")) { \ + printf("Expected \"rpc-error\" reply, received \"%s\" instead.\n", LYD_NAME(lyd_child(state->envp))); \ + printf("op:\n"); \ + if (state->op) { \ + lyd_print_file(stdout, state->op, LYD_XML, 0); \ + } \ + printf("\nenvp:\n"); \ + if (state->envp) { \ + lyd_print_file(stdout, state->envp, LYD_XML, 0); \ + } \ + fail(); \ + } \ + assert_null(state->op); #define ASSERT_RPC_ERROR_SESS2(state) \ state->msgtype = nc_recv_reply(state->nc_sess2, state->rpc, state->msgid, 3000, &state->envp, &state->op); \ diff --git a/tests/test_subscribe_param.c b/tests/test_subscribe_param.c index 88f7502b1..879670a83 100644 --- a/tests/test_subscribe_param.c +++ b/tests/test_subscribe_param.c @@ -476,13 +476,7 @@ test_stop_time_sub_end(void **state) assert_int_equal(NC_MSG_RPC, st->msgtype); /* Check reply */ - st->msgtype = NC_MSG_NOTIF; - while (st->msgtype == NC_MSG_NOTIF) { - st->msgtype = nc_recv_reply(st->nc_sess, st->rpc, st->msgid, 1000, &st->envp, &st->op); - } - assert_int_equal(NC_MSG_REPLY, st->msgtype); - assert_null(st->op); - assert_string_equal(LYD_NAME(lyd_child(st->envp)), "ok"); + ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); /* Try sending a notfication real time on new session */ From 56c9e65afd2f70aba10eb20b2845424a8f774371 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 26 Nov 2021 14:47:38 +0100 Subject: [PATCH 19/70] tests UPDATE print np2 log backwards on error --- tests/test_parallel_sessions.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/tests/test_parallel_sessions.c b/tests/test_parallel_sessions.c index b6294bc82..3dca75824 100644 --- a/tests/test_parallel_sessions.c +++ b/tests/test_parallel_sessions.c @@ -69,8 +69,8 @@ struct thread_arg { static void recv_reply_error_print(struct np_test *st, const struct lyd_node *op, const struct lyd_node *envp) { - char *path, *line = NULL; - size_t line_len = 0; + char *path, *line = NULL, **lines = NULL; + size_t i, line_len = 0, line_count = 0; FILE *f; /* print op */ @@ -87,11 +87,7 @@ recv_reply_error_print(struct np_test *st, const struct lyd_node *op, const stru } printf("\n"); - /* wait until all output is written to the file */ - sleep(2); - - /* print netopeer2 log */ - printf("np2 log:\n"); + /* open netopeer2 log */ assert_int_not_equal(-1, asprintf(&path, "%s/%s/%s", NP_SR_REPOS_DIR, st->test_name, NP_LOG_FILE)); f = fopen(path, "r"); free(path); @@ -99,11 +95,26 @@ recv_reply_error_print(struct np_test *st, const struct lyd_node *op, const stru printf("Opening netopeer2 log file failed.\n"); return; } + + /* store all the lines */ while (getline(&line, &line_len, f) != -1) { - fputs(line, stdout); + lines = realloc(lines, (line_count + 1) * sizeof *lines); + lines[line_count] = line; + line = NULL; + ++line_count; } - free(line); fclose(f); + + /* print from the end in case the log is too long and would not get printed */ + printf("np2 log backwards:\n"); + assert_non_null(line_count); + i = line_count; + do { + --i; + puts(lines[i]); + free(lines[i]); + } while (i); + free(lines); } static void * From 76f2468bd032ce3c0d4b09975a741967e2c6bb54 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 26 Nov 2021 14:48:22 +0100 Subject: [PATCH 20/70] tests UPDATE increase server timeout to 10 --- tests/np_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/np_test.c b/tests/np_test.c index e6860604c..05faf943d 100644 --- a/tests/np_test.c +++ b/tests/np_test.c @@ -218,7 +218,7 @@ np_glob_setup_np2(void **state, const char *test_name, const char *modules[], ui /* exec server listening on a unix socket */ sprintf(str, "-p%s/%s/%s", NP_TEST_DIR, test_name, NP_PID_FILE); - execl(NP_BINARY_DIR "/netopeer2-server", NP_BINARY_DIR "/netopeer2-server", "-d", "-v3", str, sockparam, + execl(NP_BINARY_DIR "/netopeer2-server", NP_BINARY_DIR "/netopeer2-server", "-d", "-v3", "-t10", str, sockparam, "-m 600", "-f", serverdir, (char *)NULL); child_error: From 9e1970aaa0ef0836c70eaf968655cdb205297c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= Date: Mon, 29 Nov 2021 10:41:37 +0100 Subject: [PATCH 21/70] Fix build when not using CMake's make generator CMake is a generator which can be used with other tools than just `make` -- `ninja` is a good candidate which tends to build a little faster. It's therefore a bug to try invoking `make` on some target directly. See-also: https://github.com/sysrepo/sysrepo/pull/2647 Fixes: e70b9c9 tests UPDATE cleanup SHM files after tests --- tests/CMakeLists.txt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 89f9067b9..54130434d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -52,10 +52,14 @@ foreach(test_name IN LISTS tests) set_property(TARGET ${test_name} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) endforeach(test_name) +set(TEST_KILL_SERVER_COMMAND ${CMAKE_CURRENT_BINARY_DIR}/scripts/kill_np_server.sh) +set(TEST_CLEAR_STATE_COMMAND rm -rf /dev/shm/_tests_np_*) if(${CMAKE_VERSION} VERSION_GREATER "3.7") - # tests cleanup fixture, keep repos with server log files - add_test(NAME tests_done COMMAND make test_clean_keep_repos) - set_tests_properties(tests_done PROPERTIES FIXTURES_CLEANUP tests_cleanup) + # tests cleanup fixtures, keep repos with server log files + add_test(NAME tests_kill_server COMMAND ${TEST_KILL_SERVER_COMMAND}) + add_test(NAME tests_clear_state COMMAND ${TEST_CLEAR_STATE_COMMAND}) + set_tests_properties(tests_kill_server PROPERTIES FIXTURES_CLEANUP tests_cleanup) + set_tests_properties(tests_clear_state PROPERTIES FIXTURES_CLEANUP tests_cleanup DEPENDS tests_kill_server) endif() # add tests with their attributes @@ -81,11 +85,8 @@ if(ENABLE_VALGRIND_TESTS) endif() # phony target for clearing all sysrepo test data -add_custom_target(test_clean_keep_repos - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/scripts/kill_np_server.sh - COMMAND rm -rf /dev/shm/_tests_np_* -) add_custom_target(test_clean - DEPENDS test_clean_keep_repos + COMMAND ${TEST_KILL_SERVER_COMMAND} + COMMAND ${TEST_CLEAR_STATE_COMMAND} COMMAND rm -rf ${CMAKE_CURRENT_BINARY_DIR}/repositories ) From 0ca0aefb0cd132e9243cf473e3de6836d393196b Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 13 Dec 2021 09:58:14 +0100 Subject: [PATCH 22/70] readme DOC mention netconf-cli --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a7f06875c..bccb60f46 100644 --- a/README.md +++ b/README.md @@ -110,12 +110,15 @@ to create it. ### CLI -A command-line NETCONF client `netopeer2-cli` is included and build/installed by default. This can be +A simple command-line NETCONF client `netopeer2-cli` is included and build/installed by default. This can be adjusted by an option: ``` BUILD_CLI:ON ``` +There is also a separate [netconf-cli](https://github.com/CESNET/netconf-cli) project that you may want to +give a try if you need an advanced and more user-friendly command-line NETCONF client. + ### Tests There are several tests included and built with [cmocka](https://cmocka.org/). The tests From cdc1ad2f6b841f55de27ec996ccb0a8e4085030c Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 13 Dec 2021 10:27:57 +0100 Subject: [PATCH 23/70] tests UPDATE skip non-root-only tests when root Refs #1094 --- tests/test_confirmed_commit.c | 5 +++++ tests/test_edit.c | 5 +++++ tests/test_rpc.c | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/tests/test_confirmed_commit.c b/tests/test_confirmed_commit.c index 42d35a689..5b7856e94 100644 --- a/tests/test_confirmed_commit.c +++ b/tests/test_confirmed_commit.c @@ -550,6 +550,11 @@ teardown_test_failed_file(void **state) int main(int argc, char **argv) { + if (is_nacm_rec_uid()) { + puts("Running as NACM_RECOVERY_UID. Tests will not run correctly as this user bypases NACM. Skipping."); + return 0; + } + const struct CMUnitTest tests[] = { cmocka_unit_test(test_basic), cmocka_unit_test_setup_teardown(test_sameas_commit, setup_common, teardown_common), diff --git a/tests/test_edit.c b/tests/test_edit.c index e94d9388b..8807f67ef 100644 --- a/tests/test_edit.c +++ b/tests/test_edit.c @@ -717,6 +717,11 @@ test_autodel_case(void **state) int main(int argc, char **argv) { + if (is_nacm_rec_uid()) { + puts("Running as NACM_RECOVERY_UID. Tests will not run correctly as this user bypases NACM. Skipping."); + return 0; + } + const struct CMUnitTest tests[] = { cmocka_unit_test_teardown(test_merge_edit1, teardown_common), cmocka_unit_test_teardown(test_merge_edit2, teardown_common), diff --git a/tests/test_rpc.c b/tests/test_rpc.c index 68df8bd0e..c64747514 100644 --- a/tests/test_rpc.c +++ b/tests/test_rpc.c @@ -365,6 +365,11 @@ test_getconfig(void **state) const char *expected; char *configuration; + if (is_nacm_rec_uid()) { + puts("Skipping the test."); + return; + } + /* Try getting configuration */ st->rpc = nc_rpc_getconfig(NC_DATASTORE_RUNNING, NULL, NC_WD_ALL, NC_PARAMTYPE_CONST); st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); From 331cf347a15f4c8e91d89113360ba3128afbdd97 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 13 Dec 2021 17:14:32 +0100 Subject: [PATCH 24/70] common BUGFIX root context node when filtering Refs #1097 --- CMakeLists.txt | 4 +++- src/common.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ca40b6e0a..286f877ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,8 @@ endif() set(NP2SRV_VERSION 2.1.2) # libyang required SO version +set(LIBYANG_DEP_VERSION 2.0.128) +set(LIBYANG_DEP_SOVERSION 2.14.0) set(LIBYANG_DEP_SOVERSION_MAJOR 2) # libnetconf2 required SO version @@ -281,7 +283,7 @@ target_link_libraries(netopeer2-server ${CMAKE_THREAD_LIBS_INIT}) list(APPEND CMAKE_REQUIRED_FLAGS ${CMAKE_THREAD_LIBS_INIT}) # libyang -find_package(LibYANG ${LIBYANG_DEP_SOVERSION_MAJOR} REQUIRED) +find_package(LibYANG ${LIBYANG_DEP_SOVERSION} REQUIRED) target_link_libraries(netopeer2-server ${LIBYANG_LIBRARIES}) include_directories(${LIBYANG_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBYANG_INCLUDE_DIRS}) diff --git a/src/common.c b/src/common.c index ae2ca8d2c..79d18b49e 100644 --- a/src/common.c +++ b/src/common.c @@ -1254,7 +1254,7 @@ op_filter_data_filter(struct lyd_node **data, const struct np2_filter *filter, i has_filter = 1; /* apply content (or even selection) filter */ - if (lyd_find_xpath(*data, filter->filters[i].str, &set)) { + if (lyd_find_xpath3(NULL, *data, filter->filters[i].str, NULL, &set)) { rc = SR_ERR_LY; goto cleanup; } From 66583823c6957107424598f4cd70ac0cf374b4da Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 13 Dec 2021 17:15:08 +0100 Subject: [PATCH 25/70] VERSION bump to version 2.1.3 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 286f877ba..216626e84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.2) +set(NP2SRV_VERSION 2.1.3) # libyang required SO version set(LIBYANG_DEP_VERSION 2.0.128) From b41b383d88ef4eac58bddaa02d46d4833edc096a Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Tue, 14 Dec 2021 15:12:58 +0100 Subject: [PATCH 26/70] main UPDATE all RPCs pass the global RPC callback ... so that NACM can be checked. Fixes #1103 --- CMakeLists.txt | 10 ++++++---- src/main.c | 7 +++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 216626e84..30ea5fc35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,15 +45,17 @@ endif() # micro version is changed with a set of small changes or bugfixes anywhere in the project. set(NP2SRV_VERSION 2.1.3) -# libyang required SO version +# libyang required version set(LIBYANG_DEP_VERSION 2.0.128) set(LIBYANG_DEP_SOVERSION 2.14.0) set(LIBYANG_DEP_SOVERSION_MAJOR 2) -# libnetconf2 required SO version +# libnetconf2 required version +set(LIBNETCONF2_DEP_VERSION 2.1.3) +set(LIBNETCONF2_DEP_SOVERSION 3.1.0) set(LIBNETCONF2_DEP_SOVERSION_MAJOR 3) -# Version of sysrepo that this netopeer2 version depends on +# sysrepo required version set(SYSREPO_DEP_VERSION 2.1.0) set(SYSREPO_DEP_SOVERSION 7.0.0) set(SYSREPO_DEP_SOVERSION_MAJOR 7) @@ -217,7 +219,7 @@ if(LIBSSH_FOUND) endif() # dependencies - libnetconf2 (now, because we need to configure outselves based on it) -find_package(LibNETCONF2 ${LIBNETCONF2_DEP_SOVERSION_MAJOR} REQUIRED) +find_package(LibNETCONF2 ${LIBNETCONF2_DEP_SOVERSION} REQUIRED) include_directories(${LIBNETCONF2_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBNETCONF2_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBNETCONF2_LIBRARIES}) diff --git a/src/main.c b/src/main.c index caf736469..07aa25d70 100644 --- a/src/main.c +++ b/src/main.c @@ -357,6 +357,13 @@ np2srv_rpc_cb(struct lyd_node *rpc, struct nc_session *ncs) return nc_server_reply_err(e); } + /* use default lnc2 callbacks when available */ + if (!strcmp(LYD_NAME(rpc), "get-schema") && !strcmp(lyd_owner_module(rpc)->name, "ietf-netconf-monitoring")) { + return nc_clb_default_get_schema(rpc, ncs); + } else if (!strcmp(LYD_NAME(rpc), "close-session") && !strcmp(lyd_owner_module(rpc)->name, "ietf-netconf")) { + return nc_clb_default_close_session(rpc, ncs); + } + /* get this user session with its originator data, no need to use ref-count */ user_sess = nc_session_get_data(ncs); From 03bdfd48a23627b88295fdb5fadc5e8261f2c435 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Tue, 14 Dec 2021 15:13:45 +0100 Subject: [PATCH 27/70] VERSION bump to version 2.1.4 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 30ea5fc35..e89b70b0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.3) +set(NP2SRV_VERSION 2.1.4) # libyang required version set(LIBYANG_DEP_VERSION 2.0.128) From 5eb0ba819c94950cc09a9c51efe3235afec0f2b0 Mon Sep 17 00:00:00 2001 From: Brett Bergquist Date: Tue, 14 Dec 2021 10:36:47 -0500 Subject: [PATCH 28/70] Added get-data operational test filter --- tests/np_test.h | 11 +++++++++++ tests/test_filter.c | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/tests/np_test.h b/tests/np_test.h index 1aa8de939..40ebdc587 100644 --- a/tests/np_test.h +++ b/tests/np_test.h @@ -122,6 +122,17 @@ assert_string_equal(LYD_NAME(lyd_child(state->op)), "data"); \ assert_int_equal(LY_SUCCESS, lyd_print_mem(&state->str, state->op, LYD_XML, 0)); +#define GET_DATA_FILTER(state, ds, filter, config_filter, origin_filter, origin_filter_count, neg_origin_filter, max_depth, with_origin, wd_mode) \ + state->rpc = nc_rpc_getdata(ds, filter, config_filter, origin_filter, origin_filter_count, neg_origin_filter, max_depth, with_origin, wd_mode, NC_PARAMTYPE_CONST); \ + state->msgtype = nc_send_rpc(state->nc_sess, state->rpc, 1000, &state->msgid); \ + assert_int_equal(NC_MSG_RPC, state->msgtype); \ + state->msgtype = nc_recv_reply(state->nc_sess, state->rpc, state->msgid, 2000, &state->envp, &state->op); \ + assert_int_equal(state->msgtype, NC_MSG_REPLY); \ + assert_non_null(state->op); \ + assert_non_null(state->envp); \ + assert_string_equal(LYD_NAME(lyd_child(state->op)), "data"); \ + assert_int_equal(LY_SUCCESS, lyd_print_mem(&state->str, state->op, LYD_XML, 0)); + #define SEND_EDIT_RPC_DS(state, ds, config) \ state->rpc = nc_rpc_edit(ds, NC_RPC_EDIT_DFLTOP_MERGE, NC_RPC_EDIT_TESTOPT_SET, NC_RPC_EDIT_ERROPT_ROLLBACK, \ config, NC_PARAMTYPE_CONST); \ diff --git a/tests/test_filter.c b/tests/test_filter.c index 46caeafad..c611ac366 100644 --- a/tests/test_filter.c +++ b/tests/test_filter.c @@ -869,6 +869,46 @@ test_get_operational_data(void **state) FREE_TEST_VARS(st); } +static void +test_getdata_operational_data(void **state) +{ + struct np_test *st = *state; + char *filter, *expected; + + filter = + "\n" + " \n" + " O-RAN-RADIO\n" + " 1234\n" + " \n" + " true\n" + " \n" + " \n" + "\n"; + + GET_DATA_FILTER(st, "ietf-datastores:operational", filter, NULL, NULL, 0, 0, 0, 0, NC_WD_ALL); + + expected = + "\n" + " \n" + " \n" + " \n" + " ComponentName\n" + " O-RAN-RADIO\n" + " 1234\n" + " \n" + " true\n" + " \n" + " \n" + " \n" + " \n" + "\n"; + + assert_string_equal(st->str, expected); + + FREE_TEST_VARS(st); +} + int main(int argc, char **argv) { @@ -885,6 +925,7 @@ main(int argc, char **argv) cmocka_unit_test(test_get_containment_node), cmocka_unit_test(test_get_content_match_node), cmocka_unit_test(test_get_operational_data), + cmocka_unit_test(test_getdata_operational_data), }; nc_verbosity(NC_VERB_WARNING); From 03bc8e32078184b260c33f5c270498c4475f1717 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 15 Dec 2021 16:41:24 +0100 Subject: [PATCH 29/70] tests REFACTOR use helper macro --- tests/test_subscribe_filter.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_subscribe_filter.c b/tests/test_subscribe_filter.c index 5402eb444..100ec2d18 100644 --- a/tests/test_subscribe_filter.c +++ b/tests/test_subscribe_filter.c @@ -66,10 +66,7 @@ reestablish_sub(void **state, const char *filter) assert_int_equal(NC_MSG_RPC, st->msgtype); /* Check reply */ - st->msgtype = nc_recv_reply(st->nc_sess, st->rpc, st->msgid, 1000, &st->envp, &st->op); - assert_int_equal(NC_MSG_REPLY, st->msgtype); - assert_null(st->op); - assert_string_equal(LYD_NAME(lyd_child(st->envp)), "ok"); + ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); } From 48679fd08ce299c04b893a6002ba6949050ecdad Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 15 Dec 2021 16:41:53 +0100 Subject: [PATCH 30/70] common BUGFIX proper distinction between filter kinds Refs #1104 --- src/common.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/common.c b/src/common.c index 79d18b49e..32e8960f8 100644 --- a/src/common.c +++ b/src/common.c @@ -997,7 +997,7 @@ static int filter_xpath_buf_add_r(const struct lyd_node *node, char **buf, int size, struct np2_filter *filter) { const struct lyd_node *child; - int s, only_content_match; + int s, only_content_match, selection; /* containment node or selection node */ size = filter_xpath_buf_append_node(node, buf, size); @@ -1053,7 +1053,8 @@ filter_xpath_buf_add_r(const struct lyd_node *node, char **buf, int size, struct return -1; } - if (op_filter_xpath_add_filter(*buf, 1, filter)) { + selection = (lyd_get_value(child) && !strws(lyd_get_value(child))) ? 0 : 1; + if (op_filter_xpath_add_filter(*buf, selection, filter)) { return -1; } } From 036d18d1cd86dc383c07f94ae882201de996952c Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 15 Dec 2021 16:42:42 +0100 Subject: [PATCH 31/70] common UPDATE allow several xpath filetrs of any kind --- src/common.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/common.c b/src/common.c index 32e8960f8..fef073683 100644 --- a/src/common.c +++ b/src/common.c @@ -1149,15 +1149,8 @@ op_filter_filter2xpath(const struct np2_filter *filter, char **xpath) *xpath = NULL; - /* all selection filters first */ + /* combine all filters into one */ for (i = 0; i < filter->count; ++i) { - if (!filter->filters[i].selection && (filter->count > 1)) { - ERR("Several top-level content match filters are not supported as they are redundant."); - rc = SR_ERR_UNSUPPORTED; - goto error; - } - - /* put all selection filters into parentheses */ if (!*xpath) { if (np_append_str("(", xpath)) { rc = SR_ERR_NO_MEMORY; From bff2fd38d731e7e471e31ae6cf19b059c6bc3470 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 15 Dec 2021 16:43:03 +0100 Subject: [PATCH 32/70] build UPDATE increase required sysrepo version Required for the tests to pass. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e89b70b0a..56d99a431 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,8 +56,8 @@ set(LIBNETCONF2_DEP_SOVERSION 3.1.0) set(LIBNETCONF2_DEP_SOVERSION_MAJOR 3) # sysrepo required version -set(SYSREPO_DEP_VERSION 2.1.0) -set(SYSREPO_DEP_SOVERSION 7.0.0) +set(SYSREPO_DEP_VERSION 2.1.10) +set(SYSREPO_DEP_SOVERSION 7.0.10) set(SYSREPO_DEP_SOVERSION_MAJOR 7) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=gnu99") From 3b022db9c8323ba40eedad4691e19f12ecf4aee0 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 15 Dec 2021 16:46:34 +0100 Subject: [PATCH 33/70] test REFACTOR formatting --- tests/test_filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_filter.c b/tests/test_filter.c index c611ac366..50bdbab6b 100644 --- a/tests/test_filter.c +++ b/tests/test_filter.c @@ -925,7 +925,7 @@ main(int argc, char **argv) cmocka_unit_test(test_get_containment_node), cmocka_unit_test(test_get_content_match_node), cmocka_unit_test(test_get_operational_data), - cmocka_unit_test(test_getdata_operational_data), + cmocka_unit_test(test_getdata_operational_data), }; nc_verbosity(NC_VERB_WARNING); From a09bc8a6986721b38e8bc3d9751d1337e726166c Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 15 Dec 2021 16:43:31 +0100 Subject: [PATCH 34/70] VERSION bump to version 2.1.5 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 56d99a431..c9cfb1f22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.4) +set(NP2SRV_VERSION 2.1.5) # libyang required version set(LIBYANG_DEP_VERSION 2.0.128) From 0271a30170039d8d0d6a99310831273036c628ec Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 15 Dec 2021 17:08:35 +0100 Subject: [PATCH 35/70] netconf acm REFACTOR remove redundant casts --- src/netconf_acm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/netconf_acm.c b/src/netconf_acm.c index 3149c46b8..02c361a38 100644 --- a/src/netconf_acm.c +++ b/src/netconf_acm.c @@ -338,7 +338,7 @@ ncac_remove_rules(struct ncac_rule_list *list) * @param[in] idx Index of the item to get. * @return Pointer to the item at index. */ -#define ITEM_IDX_PTR(items, item_size, idx) (char **)(((uint64_t)(uintptr_t)items) + ((idx) * (item_size))) +#define ITEM_IDX_PTR(items, item_size, idx) (char **)(((uintptr_t)items) + ((idx) * (item_size))) /** * @brief Compare callback for sorting functions like qsort(3) and bsearch(3). @@ -383,7 +383,7 @@ ncac_strarr_sort_find(const char **item, size_t item_size, char **items, uint32_ m = bsearch(item, items, item_count, item_size, ncac_sort_strcmp_cb); if (m) { - idx = ((uint64_t)(uintptr_t)m - (uint64_t)(uintptr_t)items) / item_size; + idx = ((uintptr_t)m - (uintptr_t)items) / item_size; } return idx; From f63454b0eb9a8d5077e2ca0a80ae1720f1990e30 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 15 Dec 2021 17:09:06 +0100 Subject: [PATCH 36/70] test rpc BUGFIX use proper printf flags Fixes #1106 --- tests/test_rpc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/test_rpc.c b/tests/test_rpc.c index c64747514..5f4bf6b91 100644 --- a/tests/test_rpc.c +++ b/tests/test_rpc.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -120,7 +121,7 @@ test_lock_fail(void **state) template = "\n" + "message-id=\"%" PRIu64 "\">\n" " \n" " protocol\n" " lock-denied\n" @@ -128,7 +129,7 @@ test_lock_fail(void **state) " Access to the requested lock is denied" " because the lock is currently held by another entity.\n" " \n" - " %d\n" + " %" PRIu32 "\n" " \n" " \n" "\n"; @@ -251,7 +252,7 @@ test_unlock_fail(void **state) template = "\n" + "message-id=\"%" PRIu64 "\">\n" " \n" " protocol\n" " lock-denied\n" @@ -259,7 +260,7 @@ test_unlock_fail(void **state) " Access to the requested lock is denied" " because the lock is currently held by another entity.\n" " \n" - " %d\n" + " %" PRIu32 "\n" " \n" " \n" "\n"; @@ -310,7 +311,7 @@ test_kill(void **state) get_username(&username); template = "\n" + "message-id=\"%" PRIu64 "\">\n" " \n" " application\n" " access-denied\n" From c2c34e5471c8c6a87af954a7300020f5a3bb4dd0 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 15 Dec 2021 17:09:47 +0100 Subject: [PATCH 37/70] VERSION bump to version 2.1.6 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c9cfb1f22..dec06dd48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.5) +set(NP2SRV_VERSION 2.1.6) # libyang required version set(LIBYANG_DEP_VERSION 2.0.128) From 6d99f3e3abc11fec417f09c2dd3ee0831f1e7c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= Date: Fri, 17 Dec 2021 07:11:55 +0100 Subject: [PATCH 38/70] build: format-check: invoke `make` in a portable way I've added `uncrustify` to my build environment, and as a result the build started failing for me because it tried to invoke `make format-check`. I use the ninja generator for CMake, which means that there's no `Makefile` generated in my build directory. Fix this by invoking the relevant target via CMake. --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 54130434d..b99a7ca4b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -24,7 +24,7 @@ add_test(NAME headers # format if (${SOURCE_FORMAT_ENABLED}) - add_test(NAME format WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND make format-check) + add_test(NAME format WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND cmake --build ${CMAKE_BINARY_DIR} --target format-check) endif() # include dirs From a8c604d939249c552f299a04780955ab4a6a4e03 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 17 Dec 2021 10:32:10 +0100 Subject: [PATCH 39/70] test BUGFIX sleep to switch to subscription thread --- tests/test_subscribe_param.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/test_subscribe_param.c b/tests/test_subscribe_param.c index 879670a83..227a2acf0 100644 --- a/tests/test_subscribe_param.c +++ b/tests/test_subscribe_param.c @@ -445,7 +445,7 @@ test_stop_time_sub_end(void **state) struct timespec ts; char *start_time, *stop_time; - /* Needed for stopTime */ + /* needed for stopTime */ assert_int_not_equal(-1, clock_gettime(CLOCK_REALTIME, &ts)); assert_int_equal(LY_SUCCESS, ly_time_ts2str(&ts, &start_time)); @@ -453,34 +453,37 @@ test_stop_time_sub_end(void **state) assert_int_not_equal(-1, clock_gettime(CLOCK_REALTIME, &ts)); assert_int_equal(LY_SUCCESS, ly_time_ts2str(&ts, &stop_time)); - /* Subscribe to notfications */ + /* subscribe to notfications */ reestablish_sub(state, NULL, start_time, stop_time); free(start_time); free(stop_time); - /* Receive the notification and test the contents */ + /* receive the notification and test the contents */ RECV_NOTIF(st); data = "\n"; assert_string_equal(data, st->str); FREE_TEST_VARS(st); - /* Receive the notification and test the contents */ + /* receive the notification and test the contents */ RECV_NOTIF(st); data = "\n"; assert_string_equal(data, st->str); FREE_TEST_VARS(st); - /* Subsription should have ended now due to stop time, try to create a new one */ + /* wait a bit to increase chances that the subscription thread was scheduled and the subscription finished due to stop time */ + usleep(10000); + + /* create new subscription */ st->rpc = nc_rpc_subscribe(NULL, NULL, NULL, NULL, NC_PARAMTYPE_CONST); st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); assert_int_equal(NC_MSG_RPC, st->msgtype); - /* Check reply */ + /* check reply */ ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); - /* Try sending a notfication real time on new session */ - /* Send the notification */ + /* try sending a notfication real time on new session */ + /* send the notification */ data = "\n" " Second\n" @@ -489,7 +492,7 @@ test_stop_time_sub_end(void **state) assert_int_equal(sr_notif_send_tree(st->sr_sess, st->node, 1000, 1), SR_ERR_OK); FREE_TEST_VARS(st); - /* Receive the notification and test the contents */ + /* receive the notification and test the contents */ RECV_NOTIF(st); assert_string_equal(data, st->str); FREE_TEST_VARS(st); From 0edf2f1282bbe5ded4e991e96e1a000e9a288a64 Mon Sep 17 00:00:00 2001 From: Brett Bergquist Date: Mon, 3 Jan 2022 07:57:20 -0500 Subject: [PATCH 40/70] confirmed commit BUGFIX hardcoded module name (#1120) --- src/netconf_confirmed_commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/netconf_confirmed_commit.c b/src/netconf_confirmed_commit.c index e5e009ecd..0e57d82ec 100644 --- a/src/netconf_confirmed_commit.c +++ b/src/netconf_confirmed_commit.c @@ -675,7 +675,7 @@ set_running_backup(void) } /* Check if has both read and write permission for module in sysrepo */ - if ((rc = sr_check_module_ds_access(np2srv.sr_conn, "edit1", SR_DS_RUNNING, &read, &write))) { + if ((rc = sr_check_module_ds_access(np2srv.sr_conn, module->name, SR_DS_RUNNING, &read, &write))) { ERR("Failed getting permissions of module \"%s\".", module->name); goto cleanup; } From 81b3b90857e6fcef7f097cbe5ead949ec888ad4a Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 3 Jan 2022 13:58:21 +0100 Subject: [PATCH 41/70] VERSION bump to version 2.1.7 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dec06dd48..4d018a1a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.6) +set(NP2SRV_VERSION 2.1.7) # libyang required version set(LIBYANG_DEP_VERSION 2.0.128) From 7f3c3b2b1af69fde64b64f7b76d7f253ca0027c8 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Thu, 6 Jan 2022 15:15:25 +0100 Subject: [PATCH 42/70] confirmed commit BUGFIX cancel-commit on no confirmed commit Returns an error now. Fixes #1127 --- src/netconf_confirmed_commit.c | 12 +++++++++++- tests/test_confirmed_commit.c | 9 +++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/netconf_confirmed_commit.c b/src/netconf_confirmed_commit.c index 0e57d82ec..18cd54911 100644 --- a/src/netconf_confirmed_commit.c +++ b/src/netconf_confirmed_commit.c @@ -906,11 +906,21 @@ np2srv_rpc_cancel_commit_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), if (node) { persist_id = lyd_get_value(node); } + /* LOCK */ pthread_mutex_lock(&commit_ctx.lock); + + /* check there is a confirmed commit to cancel */ + if (!commit_ctx.timer) { + np_err_invalid_value(session, "No pending confirmed commit to cancel.", NULL); + rc = SR_ERR_INVAL_ARG; + goto cleanup; + } + + /* check persist-id */ persist = commit_ctx.persist; if ((persist && !persist_id) || (!persist && persist_id) || (persist && persist_id && strcmp(persist, persist_id))) { - np_err_invalid_value(session, "Confirming commit does not match pending confirmed commit.", persist_id); + np_err_invalid_value(session, "Cancel commit does not match pending confirmed commit.", persist_id); rc = SR_ERR_INVAL_ARG; goto cleanup; } diff --git a/tests/test_confirmed_commit.c b/tests/test_confirmed_commit.c index 5b7856e94..8e0570749 100644 --- a/tests/test_confirmed_commit.c +++ b/tests/test_confirmed_commit.c @@ -315,6 +315,15 @@ test_cancel(void **state) /* Prior to the test running should be empty */ ASSERT_EMPTY_CONFIG(st); + /* Send cancel-commit rpc, should fail as there is no commit */ + st->rpc = nc_rpc_cancel(NULL, NC_PARAMTYPE_CONST); + st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); + assert_int_equal(st->msgtype, NC_MSG_RPC); + + /* Check if received an error reply */ + ASSERT_RPC_ERROR(st); + FREE_TEST_VARS(st); + /* Send a confirmed-commit rpc with 10m timeout */ st->rpc = nc_rpc_commit(1, 0, NULL, NULL, NC_PARAMTYPE_CONST); st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); From 181f3a9e4c4a7a7a99edbef63884a8b92182dbc7 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Thu, 6 Jan 2022 15:16:55 +0100 Subject: [PATCH 43/70] VERSION bump to version 2.1.8 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d018a1a9..47bb3520b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.7) +set(NP2SRV_VERSION 2.1.8) # libyang required version set(LIBYANG_DEP_VERSION 2.0.128) From fcb66eaae59d2f88d28211c7c1c8e992d1a7cba1 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 7 Jan 2022 09:05:35 +0100 Subject: [PATCH 44/70] confirmed commit BUGFIX backup whole module Fixes #1130 --- src/netconf_confirmed_commit.c | 2 +- tests/modules/edit1.yang | 9 ++++++++ tests/test_confirmed_commit.c | 42 ++++++++++++++++++++++++---------- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/netconf_confirmed_commit.c b/src/netconf_confirmed_commit.c index 18cd54911..e696567e1 100644 --- a/src/netconf_confirmed_commit.c +++ b/src/netconf_confirmed_commit.c @@ -516,7 +516,7 @@ backup_module(sr_session_ctx_t *session, const struct lys_module *module) rc = SR_ERR_NO_MEMORY; goto cleanup; } - if (lyd_print_path(path, data ? data->tree : NULL, LYD_JSON, LY_PRINT_SHRINK)) { + if (lyd_print_path(path, data ? data->tree : NULL, LYD_JSON, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK)) { ERR("Failed backing up node of module \"%s\" into file \"%s\".", module->name, path); rc = SR_ERR_LY; goto cleanup; diff --git a/tests/modules/edit1.yang b/tests/modules/edit1.yang index 57beb0a79..ec8671e48 100644 --- a/tests/modules/edit1.yang +++ b/tests/modules/edit1.yang @@ -5,4 +5,13 @@ module edit1 { leaf first { type string; } + + container cont { + leaf second { + type empty; + } + leaf third { + type uint32; + } + } } diff --git a/tests/test_confirmed_commit.c b/tests/test_confirmed_commit.c index 8e0570749..9d201fc9c 100644 --- a/tests/test_confirmed_commit.c +++ b/tests/test_confirmed_commit.c @@ -106,7 +106,8 @@ teardown_common(void **state) { struct np_test *st = *state; const char *data = - ""; + "" + ""; SR_EDIT_SESSION(st, st->sr_sess, data); FREE_TEST_VARS(st); @@ -310,30 +311,35 @@ static void test_cancel(void **state) { struct np_test *st = *state; - const char *expected; + const char *expected, *data; - /* Prior to the test running should be empty */ + /* prior to the test running should be empty */ ASSERT_EMPTY_CONFIG(st); - /* Send cancel-commit rpc, should fail as there is no commit */ + /* send cancel-commit rpc, should fail as there is no commit */ st->rpc = nc_rpc_cancel(NULL, NC_PARAMTYPE_CONST); st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); assert_int_equal(st->msgtype, NC_MSG_RPC); - /* Check if received an error reply */ + /* check if received an error reply */ ASSERT_RPC_ERROR(st); FREE_TEST_VARS(st); - /* Send a confirmed-commit rpc with 10m timeout */ + /* edit running */ + data = "val5"; + SR_EDIT_SESSION(st, st->sr_sess, data); + FREE_TEST_VARS(st); + + /* send a confirmed-commit rpc with 10m timeout */ st->rpc = nc_rpc_commit(1, 0, NULL, NULL, NC_PARAMTYPE_CONST); st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); assert_int_equal(st->msgtype, NC_MSG_RPC); - /* Check if received an OK reply */ + /* check if received an OK reply */ ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); - /* Running should now be same as candidate */ + /* running should now be same as candidate */ GET_CONFIG(st); expected = "\n" @@ -344,17 +350,29 @@ test_cancel(void **state) assert_string_equal(st->str, expected); FREE_TEST_VARS(st); - /* Send cancel-commit rpc */ + /* send cancel-commit rpc */ st->rpc = nc_rpc_cancel(NULL, NC_PARAMTYPE_CONST); st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); assert_int_equal(st->msgtype, NC_MSG_RPC); - /* Check if received an OK reply */ + /* check if received an OK reply */ ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); - /* Running should now be empty */ - ASSERT_EMPTY_CONFIG(st); + /* running should now be back how it was */ + GET_CONFIG(st); + expected = + "\n" + " \n" + " val\n" + " \n" + " \n" + " 5\n" + " \n" + " \n" + "\n"; + assert_string_equal(st->str, expected); + FREE_TEST_VARS(st); } static void From 73302d3049481a8e83d8c09fffcac1f35a029d90 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 7 Jan 2022 09:06:19 +0100 Subject: [PATCH 45/70] VERSION bump to version 2.1.9 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 47bb3520b..0f3e88802 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.8) +set(NP2SRV_VERSION 2.1.9) # libyang required version set(LIBYANG_DEP_VERSION 2.0.128) From 2d1c03cecaeb5a61d9187d44941326cfb704cee4 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 12 Jan 2022 14:08:20 +0100 Subject: [PATCH 46/70] modules UPDATE use published ietf-datastores revision No actual content changes compared to the one used before. --- ...7.yang => ietf-datastores@2018-02-14.yang} | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) rename modules/{ietf-datastores@2017-08-17.yang => ietf-datastores@2018-02-14.yang} (66%) diff --git a/modules/ietf-datastores@2017-08-17.yang b/modules/ietf-datastores@2018-02-14.yang similarity index 66% rename from modules/ietf-datastores@2017-08-17.yang rename to modules/ietf-datastores@2018-02-14.yang index 0a5a0fd83..9e875ab6a 100644 --- a/modules/ietf-datastores@2017-08-17.yang +++ b/modules/ietf-datastores@2018-02-14.yang @@ -27,11 +27,10 @@ module ietf-datastores { "; description - "This YANG module defines two sets of identities for datastores. - The first identifies the datastores themselves, the second - identifies datastore properties. + "This YANG module defines a set of identities for identifying + datastores. - Copyright (c) 2017 IETF Trust and the persons identified as + Copyright (c) 2018 IETF Trust and the persons identified as authors of the code. All rights reserved. Redistribution and use in source and binary forms, with or @@ -39,17 +38,17 @@ module ietf-datastores { the license terms contained in, the Simplified BSD License set forth in Section 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents - (http://trustee.ietf.org/license-info). + (https://trustee.ietf.org/license-info). - This version of this YANG module is part of RFC XXXX - (http://www.rfc-editor.org/info/rfcxxxx); see the RFC itself + This version of this YANG module is part of RFC 8342 + (https://www.rfc-editor.org/info/rfc8342); see the RFC itself for full legal notices."; - revision 2017-08-17 { + revision 2018-02-14 { description "Initial revision."; reference - "RFC XXXX: Network Management Datastore Architecture"; + "RFC 8342: Network Management Datastore Architecture (NMDA)"; } /* @@ -58,50 +57,50 @@ module ietf-datastores { identity datastore { description - "Abstract base identity for datastore identities."; + "Abstract base identity for datastore identities."; } identity conventional { base datastore; description - "Abstract base identity for conventional configuration - datastores."; + "Abstract base identity for conventional configuration + datastores."; } identity running { base conventional; description - "The running configuration datastore."; + "The running configuration datastore."; } identity candidate { base conventional; description - "The candidate configuration datastore."; + "The candidate configuration datastore."; } identity startup { base conventional; description - "The startup configuration datastore."; + "The startup configuration datastore."; } identity intended { base conventional; description - "The intended configuration datastore."; + "The intended configuration datastore."; } identity dynamic { base datastore; description - "Abstract base identity for dynamic configuration datastores."; + "Abstract base identity for dynamic configuration datastores."; } identity operational { base datastore; description - "The operational state datastore."; + "The operational state datastore."; } /* @@ -115,5 +114,4 @@ module ietf-datastores { description "A datastore identity reference."; } - } From 1033da2b113ff467533ccaf01b04ceac52893be9 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 14 Jan 2022 14:01:28 +0100 Subject: [PATCH 47/70] sub ntf BUGFIX lock order inversion When adding a subscription at the same time as removing another, there could a dead lock occur caused by lock order inversion. Solved by splitting adding new subscriptions into 2 functions when the first one does not hold subscription lock. --- src/netconf_subscribed_notifications.c | 65 ++++++++++++++------------ src/subscribed_notifications.c | 19 ++++++-- src/subscribed_notifications.h | 16 +++++-- src/yang_push.c | 56 ++++++++++++++++------ src/yang_push.h | 4 +- 5 files changed, 105 insertions(+), 55 deletions(-) diff --git a/src/netconf_subscribed_notifications.c b/src/netconf_subscribed_notifications.c index dcde64c1c..c1e296b34 100644 --- a/src/netconf_subscribed_notifications.c +++ b/src/netconf_subscribed_notifications.c @@ -244,41 +244,27 @@ sub_ntf_inc_denied(uint32_t nc_sub_id) } /** - * @brief Add a subscription into internal subscriptions. + * @brief Add a prepared and valid subscription into internal subscriptions. * - * @param[in] nc_id NETCONF SID of the session creating this subscription. - * @param[in] nc_sub_id NETCONF subscription ID. - * @param[in] term_reason Default termination reason. - * @param[in] stop_time Subscription stop time. - * @param[in] type Subscription type. - * @param[out] sub_p Created subscription. + * @param[in] sub Subscription to add. + * @param[out] sub_p Pointer to the stored subscription. * @return 0 on success. * @return -1 on error. */ static int -sub_ntf_new(uint32_t nc_id, uint32_t nc_sub_id, const char *term_reason, struct timespec stop_time, enum sub_ntf_type type, - struct np2srv_sub_ntf **sub_p) +sub_ntf_add(const struct np2srv_sub_ntf *sub, struct np2srv_sub_ntf **sub_p) { void *mem; - struct np2srv_sub_ntf *sub; mem = realloc(info.subs, (info.count + 1) * sizeof *info.subs); if (!mem) { return -1; } info.subs = mem; - sub = &info.subs[info.count]; - memset(sub, 0, sizeof *sub); - - /* fill known members */ - sub->nc_id = nc_id; - sub->nc_sub_id = nc_sub_id; - sub->term_reason = term_reason; - sub->stop_time = stop_time; - sub->type = type; + info.subs[info.count] = *sub; + *sub_p = &info.subs[info.count]; ++info.count; - *sub_p = sub; return 0; } @@ -347,7 +333,7 @@ np2srv_rpc_establish_sub_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), { struct lyd_node *node; struct nc_session *ncs; - struct np2srv_sub_ntf *sub; + struct np2srv_sub_ntf sub = {0}, *sub_p; char id_str[11]; struct timespec stop = {0}; int r, rc, ntf_status = 0; @@ -396,26 +382,43 @@ np2srv_rpc_establish_sub_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), /* get new NC sub ID */ nc_sub_id = ATOMIC_INC_RELAXED(new_nc_sub_id); + /* prepare a new subscription */ + sr_session_get_orig_data(session, 0, NULL, (const void **)&nc_id); + sub.nc_id = *nc_id; + sub.nc_sub_id = nc_sub_id; + sub.term_reason = "ietf-subscribed-notifications:no-such-subscription"; + sub.stop_time = stop; + sub.type = type; + + /* create sysrepo subscriptions and type-specific data */ + switch (type) { + case SUB_TYPE_SUB_NTF: + rc = sub_ntf_rpc_establish_sub_prepare(session, input, &sub); + break; + case SUB_TYPE_YANG_PUSH: + rc = yang_push_rpc_establish_sub_prepare(session, input, &sub); + break; + } + if (rc) { + goto error; + } + /* WRITE LOCK */ INFO_WLOCK; - /* allocate a new subscription */ - sr_session_get_orig_data(session, 0, NULL, (const void **)&nc_id); - if (sub_ntf_new(*nc_id, nc_sub_id, "ietf-subscribed-notifications:no-such-subscription", stop, type, &sub)) { - rc = SR_ERR_INTERNAL; - goto error_unlock; - } + /* add into subscriptions, is not accessible before */ + sub_ntf_add(&sub, &sub_p); - /* create sysrepo subscriptions and type-specific data */ + /* start even asynchronous tasks that may access subscriptions and require lock to be held now */ switch (type) { case SUB_TYPE_SUB_NTF: - rc = sub_ntf_rpc_establish_sub(session, input, sub); + rc = sub_ntf_rpc_establish_sub_start_async(session, sub_p); break; case SUB_TYPE_YANG_PUSH: - rc = yang_push_rpc_establish_sub(session, input, sub); + rc = yang_push_rpc_establish_sub_start_async(session, sub_p); break; } - if (rc != SR_ERR_OK) { + if (rc) { goto error_unlock; } diff --git a/src/subscribed_notifications.c b/src/subscribed_notifications.c index 2d29a0640..aea7580cc 100644 --- a/src/subscribed_notifications.c +++ b/src/subscribed_notifications.c @@ -252,16 +252,16 @@ sub_ntf_sr_subscribe(sr_session_ctx_t *user_sess, const char *stream, const char for (idx = 0; idx < *sub_id_count; ++idx) { sr_unsubscribe_sub(np2srv.sr_notif_sub, (*sub_ids)[idx]); } + if (suspended) { + /* resume subscription thread */ + sr_subscription_thread_resume(np2srv.sr_notif_sub); + } free(*sub_ids); *sub_ids = NULL; *sub_id_count = 0; cleanup: sr_session_release_context(user_sess); - if (suspended) { - /* resume subscription thread */ - sr_subscription_thread_resume(np2srv.sr_notif_sub); - } ly_set_erase(&mod_set, NULL); return rc; } @@ -388,7 +388,7 @@ sub_ntf_rpc_filter2xpath(sr_session_ctx_t *user_sess, const struct lyd_node *rpc } int -sub_ntf_rpc_establish_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, struct np2srv_sub_ntf *sub) +sub_ntf_rpc_establish_sub_prepare(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, struct np2srv_sub_ntf *sub) { struct lyd_node *node, *stream_subtree_filter = NULL; struct nc_session *ncs; @@ -486,6 +486,15 @@ sub_ntf_rpc_establish_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, return rc; } +int +sub_ntf_rpc_establish_sub_start_async(sr_session_ctx_t *UNUSED(ev_sess), struct np2srv_sub_ntf *UNUSED(sub)) +{ + /* resume subscription thread */ + sr_subscription_thread_resume(np2srv.sr_notif_sub); + + return 0; +} + int sub_ntf_rpc_modify_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, struct timespec stop, struct np2srv_sub_ntf *sub) diff --git a/src/subscribed_notifications.h b/src/subscribed_notifications.h index 785244328..ac279449e 100644 --- a/src/subscribed_notifications.h +++ b/src/subscribed_notifications.h @@ -55,14 +55,24 @@ struct sub_ntf_data { /** * @brief Called on establish-subscription RPC, should create any required sysrepo subscriptions and type-specific data. - * sub-ntf lock held. + * sub-ntf lock NOT held, @p sub not yet in subscriptions. * * @param[in] ev_sess Event session. * @param[in] rpc RPC data. - * @param[in,out] sub Subscription structure to fill. + * @param[in,out] sub Subscription structure to prepare. + * @return Sysrepo error value. + */ +int sub_ntf_rpc_establish_sub_prepare(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, struct np2srv_sub_ntf *sub); + +/** + * @brief Called on establish-subscription RPC, should start any asynchronous tasks. + * sub-ntf lock held. + * + * @param[in] ev_sess Event session. + * @param[in,out] sub Prepared subscription structure. * @return Sysrepo error value. */ -int sub_ntf_rpc_establish_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, struct np2srv_sub_ntf *sub); +int sub_ntf_rpc_establish_sub_start_async(sr_session_ctx_t *ev_sess, struct np2srv_sub_ntf *sub); /** * @brief Called on modify-subscription RPC, should update sysrepo subscriptions and type-specific data accordingly. diff --git a/src/yang_push.c b/src/yang_push.c index 26bff18d5..19eff96e0 100644 --- a/src/yang_push.c +++ b/src/yang_push.c @@ -976,7 +976,7 @@ yang_push_create_timer(void (*cb)(union sigval), void *arg, int force_real, time } int -yang_push_rpc_establish_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, struct np2srv_sub_ntf *sub) +yang_push_rpc_establish_sub_prepare(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, struct np2srv_sub_ntf *sub) { struct lyd_node *node, *child, *datastore_subtree_filter = NULL; struct nc_session *ncs; @@ -986,10 +986,8 @@ yang_push_rpc_establish_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rp const char *selection_filter_ref = NULL, *datastore_xpath_filter = NULL; sr_datastore_t datastore; char *xp = NULL; - uint32_t i, period, dampening_period, sub_id_count; - int64_t anchor_msec; + uint32_t i, period, dampening_period; int rc = SR_ERR_OK, periodic, sync_on_start, excluded_change[YP_OP_OPERATION_COUNT] = {0}; - struct itimerspec trspec = {0}; struct timespec anchor_time = {0}; /* get the NETCONF session and user session */ @@ -1076,7 +1074,7 @@ yang_push_rpc_establish_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rp yp_data->cb_arg.yp_data = yp_data; yp_data->cb_arg.nc_sub_id = sub->nc_sub_id; - if (periodic) { + if (yp_data->periodic) { yp_data->period_ms = period * 10; yp_data->anchor_time = anchor_time; } else { @@ -1107,7 +1105,42 @@ yang_push_rpc_establish_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rp if (rc != SR_ERR_OK) { goto cleanup; } + } + if (yp_data->periodic) { + /* create update timer */ + rc = yang_push_create_timer(yang_push_update_timer_cb, &yp_data->cb_arg, 1, &yp_data->update_timer); + if (rc != SR_ERR_OK) { + goto cleanup; + } + } + +cleanup: + free(xp); + np_release_user_sess(user_sess); + if (rc) { + yang_push_data_destroy(yp_data); + } + return rc; +} + +int +yang_push_rpc_establish_sub_start_async(sr_session_ctx_t *ev_sess, struct np2srv_sub_ntf *sub) +{ + struct np2_user_sess *user_sess = NULL; + struct nc_session *ncs; + struct yang_push_data *yp_data = sub->data; + uint32_t sub_id_count; + int64_t anchor_msec; + int rc = SR_ERR_OK; + struct itimerspec trspec = {0}; + + /* get the NETCONF session and user session */ + if ((rc = np_get_user_sess(ev_sess, &ncs, &user_sess))) { + goto cleanup; + } + + if (sub->stop_time.tv_sec) { /* schedule subscription stop */ trspec.it_value = sub->stop_time; if (timer_settime(yp_data->stop_timer, TIMER_ABSTIME, &trspec, NULL) == -1) { @@ -1116,13 +1149,7 @@ yang_push_rpc_establish_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rp } } - if (periodic) { - /* create update timer */ - rc = yang_push_create_timer(yang_push_update_timer_cb, &yp_data->cb_arg, 1, &yp_data->update_timer); - if (rc != SR_ERR_OK) { - goto cleanup; - } - + if (yp_data->periodic) { /* schedule the periodic updates */ trspec.it_value = np_gettimespec(1); if (yp_data->anchor_time.tv_sec) { @@ -1152,8 +1179,8 @@ yang_push_rpc_establish_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rp /* subscribe to sysrepo module data changes */ sub_id_count = 0; - rc = yang_push_sr_subscribe(user_sess->sess, datastore, yp_data->xpath, &yp_data->cb_arg, ev_sess, &sub->sub_ids, - &sub_id_count); + rc = yang_push_sr_subscribe(user_sess->sess, yp_data->datastore, yp_data->xpath, &yp_data->cb_arg, ev_sess, + &sub->sub_ids, &sub_id_count); ATOMIC_STORE_RELAXED(sub->sub_id_count, sub_id_count); if (rc != SR_ERR_OK) { goto cleanup; @@ -1161,7 +1188,6 @@ yang_push_rpc_establish_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rp } cleanup: - free(xp); np_release_user_sess(user_sess); if (rc) { yang_push_data_destroy(yp_data); diff --git a/src/yang_push.h b/src/yang_push.h index 3fddb5e63..9cfdc8927 100644 --- a/src/yang_push.h +++ b/src/yang_push.h @@ -87,7 +87,9 @@ struct yang_push_data { }; /* for documentation, see subscribed_notifications.h */ -int yang_push_rpc_establish_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, struct np2srv_sub_ntf *sub); +int yang_push_rpc_establish_sub_prepare(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, struct np2srv_sub_ntf *sub); + +int yang_push_rpc_establish_sub_start_async(sr_session_ctx_t *ev_sess, struct np2srv_sub_ntf *sub); int yang_push_rpc_modify_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, struct timespec stop, struct np2srv_sub_ntf *sub); From f939b49b41d545c1ee75d7cde098ed12294c7fe0 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 14 Jan 2022 14:04:43 +0100 Subject: [PATCH 48/70] VERSION bump to version 2.1.10 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f3e88802..20c4ba17d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.9) +set(NP2SRV_VERSION 2.1.10) # libyang required version set(LIBYANG_DEP_VERSION 2.0.128) From 9b703c58d1c905fd6f63b447e596277eb7286dab Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 14 Jan 2022 14:21:41 +0100 Subject: [PATCH 49/70] test UPDATE do not edit list keys --- tests/test_nacm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_nacm.c b/tests/test_nacm.c index 4cf7d81e2..7a9372798 100644 --- a/tests/test_nacm.c +++ b/tests/test_nacm.c @@ -688,9 +688,8 @@ test_edit_config_subtree(void **state) "\n" " \n" " \n" - " \n" - " 0.0.0.0\n" + " \n" + " 0.0.0.0\n" " \n" " \n" " \n" From 5015ecd922b28bc7a5b0c4b3d05e91b5dbf2e0f5 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 19 Jan 2022 09:52:57 +0100 Subject: [PATCH 50/70] yang push BUGFIX edit targets may include quotes Fixes cesnet/libnetconf2#361 --- src/yang_push.c | 60 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/src/yang_push.c b/src/yang_push.c index 19eff96e0..93586555c 100644 --- a/src/yang_push.c +++ b/src/yang_push.c @@ -166,6 +166,44 @@ yang_push_op_sr2yp(sr_change_oper_t op, const struct lyd_node *node) return 0; } +/** + * @brief Remove any previous edits in a YANG patch of a target. + * + * @param[in] ly_yp YANG patch node. + * @param[in] target Target edits to remove. + * @return Sysrepo error value. + */ +static int +yang_push_notif_change_edit_clear_target(struct lyd_node *ly_yp, const char *target) +{ + int rc = SR_ERR_OK; + struct ly_set *set = NULL; + char quot, *xpath = NULL; + + /* find the edit of this target, be careful with the quotes in the XPath */ + quot = strchr(target, '\'') ? '\"' : '\''; + if (asprintf(&xpath, "/ietf-yang-push:push-change-update/datastore-changes/yang-patch/edit[target=%c%s%c]", + quot, target, quot) == -1) { + rc = SR_ERR_NO_MEMORY; + goto cleanup; + } + if (lyd_find_xpath(ly_yp, xpath, &set)) { + rc = SR_ERR_LY; + goto cleanup; + } + assert((set->count == 0) || (set->count == 1)); + + /* remove the previous change of this target */ + if (set->count) { + lyd_free_tree(set->dnodes[0]); + } + +cleanup: + free(xpath); + ly_set_free(set, NULL); + return rc; +} + /** * @brief Append a new edit (change) to a YANG patch. * @@ -182,8 +220,7 @@ yang_push_notif_change_edit_append(struct lyd_node *ly_yp, enum yang_push_op yp_ const char *prev_value, const char *prev_list, struct yang_push_data *yp_data) { struct lyd_node *ly_edit, *ly_target, *value_tree; - struct ly_set *set = NULL; - char buf[26], *path = NULL, *point = NULL, *xpath = NULL; + char buf[26], *path = NULL, *point = NULL, quot; uint32_t edit_id; int rc = SR_ERR_OK; @@ -195,18 +232,9 @@ yang_push_notif_change_edit_append(struct lyd_node *ly_yp, enum yang_push_op yp_ } /* remove any previous change of this target */ - if (asprintf(&xpath, "/ietf-yang-push:push-change-update/datastore-changes/yang-patch/edit[target='%s']", path) == -1) { - rc = SR_ERR_NO_MEMORY; + if ((rc = yang_push_notif_change_edit_clear_target(ly_yp, path))) { goto cleanup; } - if (lyd_find_xpath(ly_yp, xpath, &set)) { - rc = SR_ERR_LY; - goto cleanup; - } - assert((set->count == 0) || (set->count == 1)); - if (set->count) { - lyd_free_tree(set->dnodes[0]); - } /* generate new edit ID */ edit_id = ATOMIC_INC_RELAXED(yp_data->edit_id); @@ -238,7 +266,8 @@ yang_push_notif_change_edit_append(struct lyd_node *ly_yp, enum yang_push_op yp_ if (node->schema->nodetype == LYS_LEAFLIST) { assert(prev_value); if (prev_value[0]) { - if (asprintf(&point, "%s[.='%s']", path, prev_value) == -1) { + quot = strchr(prev_value, '\'') ? '\"' : '\''; + if (asprintf(&point, "%s[.=%c%s%c]", path, quot, prev_value, quot) == -1) { rc = SR_ERR_NO_MEMORY; goto cleanup; } @@ -286,10 +315,11 @@ yang_push_notif_change_edit_append(struct lyd_node *ly_yp, enum yang_push_op yp_ } cleanup: - ly_set_free(set, NULL); - free(xpath); free(path); free(point); + if (rc) { + ERR("Failed to store data edit for an on-change notification."); + } return rc; } From ebe2529e4f5d625ff2a30998490119f8565f4205 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 19 Jan 2022 09:54:56 +0100 Subject: [PATCH 51/70] VERSION bump to version 2.1.11 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 20c4ba17d..943bcd6fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.10) +set(NP2SRV_VERSION 2.1.11) # libyang required version set(LIBYANG_DEP_VERSION 2.0.128) From 79fbd3d1912095d349b764f0ff735c72b86b4bb8 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 19 Jan 2022 10:28:10 +0100 Subject: [PATCH 52/70] main BUGFIX invalid free First stop all sessions and only then unsubscribe all server subscriptions. Otherwise on-change YANG Push subscriptions are unsubscribed twice. Refs cesnet/libnetconf2#361 --- src/main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main.c b/src/main.c index 07aa25d70..e0433fd05 100644 --- a/src/main.c +++ b/src/main.c @@ -658,11 +658,6 @@ server_destroy(void) { struct nc_session *sess; - /* stop subscriptions */ - sr_unsubscribe(np2srv.sr_rpc_sub); - sr_unsubscribe(np2srv.sr_data_sub); - sr_unsubscribe(np2srv.sr_notif_sub); - #if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS) /* remove all CH clients so they do not reconnect */ nc_server_ch_del_client(NULL); @@ -679,6 +674,11 @@ server_destroy(void) nc_ps_free(np2srv.nc_ps); } + /* stop subscriptions */ + sr_unsubscribe(np2srv.sr_rpc_sub); + sr_unsubscribe(np2srv.sr_data_sub); + sr_unsubscribe(np2srv.sr_notif_sub); + /* libnetconf2 cleanup */ nc_server_destroy(); From 149b9cc5172b51c7bd8b299cf1a701d30d01cb10 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 19 Jan 2022 10:54:38 +0100 Subject: [PATCH 53/70] yang push BUGFIX internal modules special handling Refs cesnet/libnetconf2#361 --- src/yang_push.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/yang_push.c b/src/yang_push.c index 93586555c..7cf236a95 100644 --- a/src/yang_push.c +++ b/src/yang_push.c @@ -646,8 +646,8 @@ yang_push_sr_subscribe_filter_collect_mods(const struct ly_ctx *ly_ctx, const ch } ly_mod = snode->module; - if (!ly_mod->implemented || !strcmp(ly_mod->name, "ietf-netconf")) { - /* skip import-only modules and ietf-netconf (as it has no data, only in libyang) */ + if (!ly_mod->implemented || !strcmp(ly_mod->name, "sysrepo") || !strcmp(ly_mod->name, "ietf-netconf")) { + /* skip import-only modules, sysrepo, and ietf-netconf (as it has no data, only in libyang) */ continue; } @@ -695,7 +695,7 @@ yang_push_sr_subscribe(sr_session_ctx_t *user_sess, sr_datastore_t ds, const cha /* subscribe to all modules with (configuration) data */ idx = 0; while ((ly_mod = ly_ctx_get_module_iter(ly_ctx, &idx))) { - if (!ly_mod->implemented) { + if (!ly_mod->implemented || !strcmp(ly_mod->name, "sysrepo") || !strcmp(ly_mod->name, "ietf-netconf")) { continue; } From c47db819d437192c396f1a221648e6f3f929cac9 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 19 Jan 2022 10:55:32 +0100 Subject: [PATCH 54/70] VERSION bump to version 2.1.12 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 943bcd6fa..fd1ccf53b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.11) +set(NP2SRV_VERSION 2.1.12) # libyang required version set(LIBYANG_DEP_VERSION 2.0.128) From b3e7c013f35d942c3e3f9d783d7ce7ea61995170 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 19 Jan 2022 15:57:55 +0100 Subject: [PATCH 55/70] common BUGFIX wrong loop var Fixes cesnet/libnetconf2#361 --- src/common.c | 4 ++-- tests/modules/edit1.yang | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/common.c b/src/common.c index fef073683..cb9858266 100644 --- a/src/common.c +++ b/src/common.c @@ -232,12 +232,12 @@ np_ly_mod_has_data(const struct lys_module *mod, uint32_t config_mask) const struct lysc_node *root, *node; LY_LIST_FOR(mod->compiled->data, root) { - LYSC_TREE_DFS_BEGIN(mod->compiled->data, node) { + LYSC_TREE_DFS_BEGIN(root, node) { if (node->flags & config_mask) { return 1; } - LYSC_TREE_DFS_END(mod->compiled->data, node); + LYSC_TREE_DFS_END(root, node); } } diff --git a/tests/modules/edit1.yang b/tests/modules/edit1.yang index ec8671e48..c41b2c2d4 100644 --- a/tests/modules/edit1.yang +++ b/tests/modules/edit1.yang @@ -2,6 +2,11 @@ module edit1 { namespace ed1; prefix ed1; + leaf stateLeaf { + type int32; + config false; + } + leaf first { type string; } From 02309ff196aab507a33c707699b223600b1bb83c Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 19 Jan 2022 15:58:51 +0100 Subject: [PATCH 56/70] VERSION bump to version 2.1.13 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fd1ccf53b..173912da6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.12) +set(NP2SRV_VERSION 2.1.13) # libyang required version set(LIBYANG_DEP_VERSION 2.0.128) From af7bbe7c3747e5ddf38ba2570c3d8e856ebe16a6 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 26 Jan 2022 08:27:36 +0100 Subject: [PATCH 57/70] distro BUGFIX silent removal of created group It could have already been removed by user. --- distro/pkg/deb/postrm | 2 +- distro/pkg/rpm/netopeer2.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distro/pkg/deb/postrm b/distro/pkg/deb/postrm index 972bf57e8..42b8af175 100755 --- a/distro/pkg/deb/postrm +++ b/distro/pkg/deb/postrm @@ -2,4 +2,4 @@ {% include 'scripts/remove.sh' %} -groupdel netconf +groupdel netconf &> /dev/null diff --git a/distro/pkg/rpm/netopeer2.spec b/distro/pkg/rpm/netopeer2.spec index c8dd22eff..f93b98662 100644 --- a/distro/pkg/rpm/netopeer2.spec +++ b/distro/pkg/rpm/netopeer2.spec @@ -66,7 +66,7 @@ NP2_MODULE_GROUP=netconf %postun {% include 'scripts/remove.sh' %} -groupdel netconf +groupdel netconf &> /dev/null %files %license LICENSE From 2418667743f0736fce3a81483411ddefbf912800 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 2 Feb 2022 09:25:25 +0100 Subject: [PATCH 58/70] main UPDATE check confirmed-commit feature --- src/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.c b/src/main.c index e0433fd05..6ebe6bc98 100644 --- a/src/main.c +++ b/src/main.c @@ -500,6 +500,7 @@ np2srv_check_schemas(sr_session_ctx_t *sr_sess) NP2_CHECK_FEATURE("url"); #endif NP2_CHECK_FEATURE("xpath"); + NP2_CHECK_FEATURE("confirmed-commit"); /* ... ietf-netconf-acm, */ mod_name = "ietf-netconf-acm"; From fb40dd097d381b928a2944d64759cf4b8eb85b17 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 2 Feb 2022 09:25:56 +0100 Subject: [PATCH 59/70] netconf UPDATE check that node was found Refs #1141 --- src/netconf.c | 24 ++++++++---------------- src/netconf_confirmed_commit.c | 16 ++++++---------- src/netconf_nmda.c | 12 ++++-------- src/netconf_subscribed_notifications.c | 10 ++++------ src/subscribed_notifications.c | 6 ++---- src/yang_push.c | 6 ++---- 6 files changed, 26 insertions(+), 48 deletions(-) diff --git a/src/netconf.c b/src/netconf.c index c2a665607..f01134ce4 100644 --- a/src/netconf.c +++ b/src/netconf.c @@ -231,8 +231,7 @@ np2srv_rpc_get_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char } /* create filters */ - lyd_find_path(input, "filter", 0, &node); - if (node) { + if (!lyd_find_path(input, "filter", 0, &node)) { /* learn filter type */ meta = lyd_find_meta(node->meta, NULL, "ietf-netconf:type"); if (meta && !strcmp(lyd_get_meta_value(meta), "xpath")) { @@ -345,14 +344,12 @@ np2srv_rpc_editconfig_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), con ly_set_free(nodeset, NULL); /* default-operation */ - lyd_find_path(input, "default-operation", 0, &node); - if (node) { + if (!lyd_find_path(input, "default-operation", 0, &node)) { defop = lyd_get_value(node); } /* test-option */ - lyd_find_path(input, "test-option", 0, &node); - if (node) { + if (!lyd_find_path(input, "test-option", 0, &node)) { testop = lyd_get_value(node); if (!strcmp(testop, "set")) { VRB("edit-config test-option \"set\" not supported, validation will be performed."); @@ -361,8 +358,7 @@ np2srv_rpc_editconfig_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), con } /* error-option */ - lyd_find_path(input, "error-option", 0, &node); - if (node) { + if (!lyd_find_path(input, "error-option", 0, &node)) { if (strcmp(lyd_get_value(node), "rollback-on-error")) { VRB("edit-config error-option \"%s\" not supported, rollback-on-error will be performed.", lyd_get_value(node)); } @@ -568,9 +564,8 @@ np2srv_rpc_copyconfig_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), con struct lyd_node *node; /* we need with-defaults flag in this case */ - lyd_find_path(input, "ietf-netconf-with-defaults:with-defaults", 0, &node); lyp_wd_flag = 0; - if (node) { + if (!lyd_find_path(input, "ietf-netconf-with-defaults:with-defaults", 0, &node)) { if (!strcmp(lyd_get_value(node), "report-all")) { lyp_wd_flag = LYD_PRINT_WD_ALL; } else if (!strcmp(lyd_get_value(node), "report-all-tagged")) { @@ -1057,8 +1052,7 @@ np2srv_rpc_subscribe_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), cons stream = lyd_get_value(node); /* filter */ - lyd_find_path(input, "filter", 0, &node); - if (node) { + if (!lyd_find_path(input, "filter", 0, &node)) { /* learn filter type */ meta = lyd_find_meta(node->meta, NULL, "ietf-netconf:type"); if (meta && !strcmp(lyd_get_meta_value(meta), "xpath")) { @@ -1097,14 +1091,12 @@ np2srv_rpc_subscribe_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), cons } /* start time */ - lyd_find_path(input, "startTime", 0, &node); - if (node) { + if (!lyd_find_path(input, "startTime", 0, &node)) { ly_time_str2ts(lyd_get_value(node), &start); } /* stop time */ - lyd_find_path(input, "stopTime", 0, &node); - if (node) { + if (!lyd_find_path(input, "stopTime", 0, &node)) { ly_time_str2ts(lyd_get_value(node), &stop); } diff --git a/src/netconf_confirmed_commit.c b/src/netconf_confirmed_commit.c index e696567e1..de78e6dc4 100644 --- a/src/netconf_confirmed_commit.c +++ b/src/netconf_confirmed_commit.c @@ -760,14 +760,12 @@ np2srv_confirmed_commit_cb(sr_session_ctx_t *session, const struct lyd_node *inp } /* persist */ - lyd_find_path(input, "persist", 0, &node); - if (node) { + if (!lyd_find_path(input, "persist", 0, &node)) { persist = lyd_get_value(node); } /* persist-id */ - lyd_find_path(input, "persist-id", 0, &node); - if (node) { + if (!lyd_find_path(input, "persist-id", 0, &node)) { ERR("Persist-id given in confirmed commit rpc."); rc = SR_ERR_INVAL_ARG; goto cleanup; @@ -836,16 +834,15 @@ np2srv_rpc_commit_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const c /* LOCK */ pthread_mutex_lock(&commit_ctx.lock); + /* check if confirmed-commit */ - lyd_find_path(input, "confirmed", 0, &node); - if (node) { + if (!lyd_find_path(input, "confirmed", 0, &node)) { rc = np2srv_confirmed_commit_cb(session, input); goto cleanup; } /* persist-id */ - lyd_find_path(input, "persist-id", 0, &node); - if (node) { + if (!lyd_find_path(input, "persist-id", 0, &node)) { persist_id = lyd_get_value(node); } @@ -902,8 +899,7 @@ np2srv_rpc_cancel_commit_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), } /* persist-id */ - lyd_find_path(input, "persist-id", 0, &node); - if (node) { + if (!lyd_find_path(input, "persist-id", 0, &node)) { persist_id = lyd_get_value(node); } diff --git a/src/netconf_nmda.c b/src/netconf_nmda.c index 4541570d9..3c1088527 100644 --- a/src/netconf_nmda.c +++ b/src/netconf_nmda.c @@ -162,8 +162,7 @@ np2srv_rpc_getdata_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const } /* config filter */ - lyd_find_path(input, "config-filter", 0, &node); - if (node) { + if (!lyd_find_path(input, "config-filter", 0, &node)) { if (!strcmp(lyd_get_value(node), "false")) { get_opts |= SR_OPER_NO_CONFIG; } else { @@ -172,20 +171,17 @@ np2srv_rpc_getdata_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const } /* depth */ - lyd_find_path(input, "max-depth", 0, &node); - if (node && strcmp(lyd_get_value(node), "unbounded")) { + if (!lyd_find_path(input, "max-depth", 0, &node) && strcmp(lyd_get_value(node), "unbounded")) { max_depth = ((struct lyd_node_term *)node)->value.uint16; } /* origin */ - lyd_find_path(input, "with-origin", 0, &node); - if (node) { + if (!lyd_find_path(input, "with-origin", 0, &node)) { get_opts |= SR_OPER_WITH_ORIGIN; } /* get with-defaults mode */ - lyd_find_path(input, "with-defaults", 0, &node); - if (node) { + if (!lyd_find_path(input, "with-defaults", 0, &node)) { if (!strcmp(lyd_get_value(node), "report-all")) { nc_wd = NC_WD_ALL; } else if (!strcmp(lyd_get_value(node), "report-all-tagged")) { diff --git a/src/netconf_subscribed_notifications.c b/src/netconf_subscribed_notifications.c index c1e296b34..1c2ae80d0 100644 --- a/src/netconf_subscribed_notifications.c +++ b/src/netconf_subscribed_notifications.c @@ -351,14 +351,13 @@ np2srv_rpc_establish_sub_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), } /* stop time */ - lyd_find_path(input, "stop-time", 0, &node); - if (node) { + if (!lyd_find_path(input, "stop-time", 0, &node)) { ly_time_str2ts(lyd_get_value(node), &stop); } /* encoding */ - lyd_find_path(input, "encoding", 0, &node); - if (node && strcmp(((struct lyd_node_term *)node)->value.ident->name, "encode-xml")) { + if (!lyd_find_path(input, "encoding", 0, &node) && + strcmp(((struct lyd_node_term *)node)->value.ident->name, "encode-xml")) { ERR("Unsupported encoding \"%s\".", lyd_get_value(node)); rc = SR_ERR_INTERNAL; goto error; @@ -533,8 +532,7 @@ np2srv_rpc_modify_sub_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), con nc_sub_id = ((struct lyd_node_term *)node)->value.uint32; /* stop time */ - lyd_find_path(input, "stop-time", 0, &node); - if (node) { + if (!lyd_find_path(input, "stop-time", 0, &node)) { ly_time_str2ts(lyd_get_value(node), &stop); } diff --git a/src/subscribed_notifications.c b/src/subscribed_notifications.c index aea7580cc..fb87b2370 100644 --- a/src/subscribed_notifications.c +++ b/src/subscribed_notifications.c @@ -418,8 +418,7 @@ sub_ntf_rpc_establish_sub_prepare(sr_session_ctx_t *ev_sess, const struct lyd_no stream = lyd_get_value(node); /* replay start time */ - lyd_find_path(rpc, "replay-start-time", 0, &node); - if (node) { + if (!lyd_find_path(rpc, "replay-start-time", 0, &node)) { ly_time_str2ts(lyd_get_value(node), &start); } @@ -515,8 +514,7 @@ sub_ntf_rpc_modify_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, st } /* datastore */ - lyd_find_path(rpc, "ietf-yang-push:datastore", 0, &node); - if (node) { + if (!lyd_find_path(rpc, "ietf-yang-push:datastore", 0, &node)) { sr_session_set_error_message(ev_sess, "Subscription with ID %" PRIu32 " is not yang-push but \"datastore\"" " is set.", sub->nc_sub_id); rc = SR_ERR_UNSUPPORTED; diff --git a/src/yang_push.c b/src/yang_push.c index 7cf236a95..827c2ba6c 100644 --- a/src/yang_push.c +++ b/src/yang_push.c @@ -1268,8 +1268,7 @@ yang_push_rpc_modify_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, /* * periodic */ - lyd_find_path(rpc, "ietf-yang-push:periodic", 0, &cont); - if (cont) { + if (!lyd_find_path(rpc, "ietf-yang-push:periodic", 0, &cont)) { if (!yp_data->periodic) { sr_session_set_error_message(ev_sess, "Subscription with ID %" PRIu32 " is not \"periodic\".", sub->nc_sub_id); @@ -1319,8 +1318,7 @@ yang_push_rpc_modify_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, /* * on-change */ - lyd_find_path(rpc, "ietf-yang-push:on-change", 0, &cont); - if (cont) { + if (!lyd_find_path(rpc, "ietf-yang-push:on-change", 0, &cont)) { if (yp_data->periodic) { sr_session_set_error_message(ev_sess, "Subscription with ID %" PRIu32 " is not \"on-change\".", sub->nc_sub_id); From 37c1de80985baddc8ab6e3fb77e958315728037c Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Wed, 2 Feb 2022 09:26:49 +0100 Subject: [PATCH 60/70] VERSION bump to version 2.1.14 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 173912da6..f432aaa9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.13) +set(NP2SRV_VERSION 2.1.14) # libyang required version set(LIBYANG_DEP_VERSION 2.0.128) From 1d5ddc80f3b2b1c37d91f5ea75c16000725bc11b Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 21 Feb 2022 13:36:54 +0100 Subject: [PATCH 61/70] server FEATURE systemd service file --- CMakeLists.txt | 18 +++++++ CMakeModules/FindLibSystemd.cmake | 83 +++++++++++++++++++++++++++++ README.md | 1 + distro/pkg/deb/control | 1 + distro/pkg/deb/netopeer2.install | 1 + distro/pkg/rpm/netopeer2.spec | 3 ++ service/netopeer2-server.service.in | 13 +++++ src/config.h.in | 4 ++ src/main.c | 14 +++++ 9 files changed, 138 insertions(+) create mode 100644 CMakeModules/FindLibSystemd.cmake create mode 100644 service/netopeer2-server.service.in diff --git a/CMakeLists.txt b/CMakeLists.txt index f432aaa9f..64d8a1da3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -278,6 +278,20 @@ if(ENABLE_URL) endif() endif() +# libsystemd +if(NOT PKG_CONFIG_FOUND AND NOT SYSTEMD_UNIT_DIR) + set(SYSTEMD_UNIT_DIR "/usr/lib/systemd/system") +endif() +find_package(LibSystemd) +if(LIBSYSTEMD_FOUND) + set(NP2SRV_HAVE_SYSTEMD 1) + target_link_libraries(netopeer2-server ${LIBSYSTEMD_LIBRARIES}) + include_directories(${LIBSYSTEMD_INCLUDE_DIRS}) + message(STATUS "systemd system service unit path: ${SYSTEMD_UNIT_DIR}") +else() + message(WARNING "Disabling netopeer2-server systemd support because libsystemd was not found.") +endif() + # pthread set(CMAKE_THREAD_PREFER_PTHREAD TRUE) find_package(Threads REQUIRED) @@ -300,6 +314,7 @@ list(APPEND CMAKE_REQUIRED_LIBRARIES ${SYSREPO_LIBRARIES}) # generate files configure_file("${PROJECT_SOURCE_DIR}/src/config.h.in" "${PROJECT_BINARY_DIR}/config.h" ESCAPE_QUOTES @ONLY) +configure_file("${PROJECT_SOURCE_DIR}/service/netopeer2-server.service.in" "${PROJECT_BINARY_DIR}/netopeer2-server.service" @ONLY) include_directories(${PROJECT_BINARY_DIR}) # set script dir @@ -311,6 +326,9 @@ install(DIRECTORY "${PROJECT_SOURCE_DIR}/modules/" DESTINATION ${YANG_MODULE_DIR # install the binary, required modules, and default configuration install(TARGETS netopeer2-server DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${PROJECT_SOURCE_DIR}/doc/netopeer2-server.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8) +if(NP2SRV_HAVE_SYSTEMD) + install(FILES ${PROJECT_BINARY_DIR}/netopeer2-server.service DESTINATION ${SYSTEMD_UNIT_DIR}) +endif() if(INSTALL_MODULES) install(CODE " message(STATUS \"Installing missing sysrepo modules...\") diff --git a/CMakeModules/FindLibSystemd.cmake b/CMakeModules/FindLibSystemd.cmake new file mode 100644 index 000000000..8647e0982 --- /dev/null +++ b/CMakeModules/FindLibSystemd.cmake @@ -0,0 +1,83 @@ +# - Try to find LibSystemd, it is expected find_package(PkgConfig) was called before +# +# Once done this will define +# +# LIBSYSTEMD_FOUND - system has LibSystemd +# LIBSYSTEMD_INCLUDE_DIRS - the LibSystemd include directory +# LIBSYSTEMD_LIBRARIES - link these to use LibSystemd +# SYSTEMD_UNIT_DIR - directory with systemd system unit files +# +# Author Michal Vasko +# Copyright (c) 2022 CESNET, z.s.p.o. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +include(FindPackageHandleStandardArgs) + +if(LIBSYSTEMD_LIBRARIES AND LIBSYSTEMD_INCLUDE_DIRS AND SYSTEMD_UNIT_DIR) + # in cache already + set(LIBSYSTEMD_FOUND TRUE) +else() + find_path(LIBSYSTEMD_INCLUDE_DIR + NAMES + systemd/sd-daemon.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + /sw/include + ${CMAKE_INCLUDE_PATH} + ${CMAKE_INSTALL_PREFIX}/include + ) + + find_library(LIBSYSTEMD_LIBRARY + NAMES + systemd + libsystemd + PATHS + /usr/lib + /usr/lib64 + /usr/local/lib + /usr/local/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + if(NOT SYSTEMD_UNIT_DIR) + execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --define-variable=rootprefix=${CMAKE_INSTALL_PREFIX} --variable=systemdsystemunitdir systemd + OUTPUT_VARIABLE SYSTEMD_UNIT_DIR) + string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_UNIT_DIR "${SYSTEMD_UNIT_DIR}") + endif() + + set(LIBSYSTEMD_INCLUDE_DIRS ${LIBSYSTEMD_INCLUDE_DIR}) + set(LIBSYSTEMD_LIBRARIES ${LIBSYSTEMD_LIBRARY}) + mark_as_advanced(LIBSYSTEMD_INCLUDE_DIRS LIBSYSTEMD_LIBRARIES) + + # handle the QUIETLY and REQUIRED arguments and set LIBSYSTEMD_FOUND to TRUE + # if all listed variables are TRUE + find_package_handle_standard_args(LibSystemd FOUND_VAR LIBSYSTEMD_FOUND + REQUIRED_VARS LIBSYSTEMD_LIBRARY LIBSYSTEMD_INCLUDE_DIR SYSTEMD_UNIT_DIR) +endif() diff --git a/README.md b/README.md index bccb60f46..ccd300f17 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ the `distro` directory. ### Optional +* pkg-config & libsystemd (to support `netopeer2-server` systemd service) * cmocka >= 1.0.1 (for [tests](#Tests)) * valgrind (for enhanced testing) * gcov (for code coverage) diff --git a/distro/pkg/deb/control b/distro/pkg/deb/control index 029f35f4b..d75193dcb 100644 --- a/distro/pkg/deb/control +++ b/distro/pkg/deb/control @@ -9,6 +9,7 @@ Build-Depends: cmake, libyang2-dev (>= 2), libnetconf2-dev (>= 2), libsysrepo-dev (>= 2), + libsystemd-dev, pkg-config Vcs-Browser: https://github.com/CESNET/netopeer2/tree/master Vcs-Git: https://github.com/CESNET/netopeer2.git diff --git a/distro/pkg/deb/netopeer2.install b/distro/pkg/deb/netopeer2.install index e3d7cfd2a..a3bb10290 100644 --- a/distro/pkg/deb/netopeer2.install +++ b/distro/pkg/deb/netopeer2.install @@ -2,4 +2,5 @@ usr/bin/netopeer2-server usr/bin/netopeer2-cli usr/share/man/man1 usr/share/man/man8 +usr/lib/systemd/system/netopeer2-server.service usr/share/yang/modules/netopeer2 diff --git a/distro/pkg/rpm/netopeer2.spec b/distro/pkg/rpm/netopeer2.spec index f93b98662..5e6038460 100644 --- a/distro/pkg/rpm/netopeer2.spec +++ b/distro/pkg/rpm/netopeer2.spec @@ -12,6 +12,8 @@ BuildRequires: cmake BuildRequires: pkgconfig(libyang) >= 2 BuildRequires: pkgconfig(libnetconf2) >= 2 BuildRequires: pkgconfig(sysrepo) >= 2 +BuildRequires: systemd-devel +BuildRequires: systemd # needed by scripts/setup.sh (run in post) Requires: sysrepo-tools @@ -74,6 +76,7 @@ groupdel netconf &> /dev/null %{_bindir}/netopeer2-server %{_datadir}/man/man1/netopeer2-cli.1.gz %{_datadir}/man/man8/netopeer2-server.8.gz +%{_unitdir}/netopeer2-server.service %{_datadir}/yang/modules/netopeer2/*.yang %dir %{_datadir}/yang/modules/netopeer2/ diff --git a/service/netopeer2-server.service.in b/service/netopeer2-server.service.in new file mode 100644 index 000000000..04bfe9caf --- /dev/null +++ b/service/netopeer2-server.service.in @@ -0,0 +1,13 @@ +[Unit] +Description=netopeer2 NETCONF server +#After= + +[Service] +Type=notify +ExecStart=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/netopeer2-server -d -v2 +Restart=always +SystemCallArchitectures=native +KillMode=control-group + +[Install] +WantedBy=multi-user.target diff --git a/src/config.h.in b/src/config.h.in index 01e104002..75aa4a02d 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -70,6 +70,10 @@ */ #cmakedefine NP2SRV_URL_CAPAB +/** @brief Whether libsystemd is installed, decides general support for systemd + */ +#cmakedefine NP2SRV_HAVE_SYSTEMD + /** @brief printf-like pattern for path to the authorized_keys file */ #define NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN "@NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN@" /** @brief Replace %s in NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN by username (1), or by the home dir (0) */ diff --git a/src/main.c b/src/main.c index 6ebe6bc98..4e1cd4425 100644 --- a/src/main.c +++ b/src/main.c @@ -55,6 +55,10 @@ #include "netconf_subscribed_notifications.h" #include "yang_push.h" +#ifdef NP2SRV_HAVE_SYSTEMD +# include +#endif + /** @brief flag for main loop */ ATOMIC_T loop_continue = 1; @@ -1364,6 +1368,11 @@ main(int argc, char *argv[]) goto cleanup; } +#ifdef NP2SRV_HAVE_SYSTEMD + /* notify systemd */ + sd_notify(0, "READY=1"); +#endif + /* start additional worker threads */ for (i = 1; i < NP2SRV_THREAD_COUNT; ++i) { idx = malloc(sizeof *idx); @@ -1377,6 +1386,11 @@ main(int argc, char *argv[]) *idx = 0; worker_thread(idx); +#ifdef NP2SRV_HAVE_SYSTEMD + /* notify systemd */ + sd_notify(0, "STOPPING=1"); +#endif + /* wait for other worker threads to finish */ for (i = 1; i < NP2SRV_THREAD_COUNT; ++i) { c = pthread_join(np2srv.workers[i], NULL); From 7ea0f251e284124f679cde33032693822b69bed8 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 21 Feb 2022 14:29:00 +0100 Subject: [PATCH 62/70] server & cli BUGFIX memory leaks --- cli/commands.c | 152 ++++++++++++++------------------- cli/configuration.c | 6 +- src/netconf_confirmed_commit.c | 1 + 3 files changed, 67 insertions(+), 92 deletions(-) diff --git a/cli/commands.c b/cli/commands.c index 5855c2e7f..5e7017b2c 100644 --- a/cli/commands.c +++ b/cli/commands.c @@ -1998,8 +1998,8 @@ cmd_cert(const char *arg, char **UNUSED(tmp_config_file)) { int ret; char *args = strdupa(arg); - char *cmd = NULL, *ptr = NULL, *path, *path2, *dest; - char *trusted_dir, *netconf_dir, *c_rehash_cmd; + char *cmd = NULL, *ptr = NULL, *path, *path2, *dest = NULL; + char *trusted_dir = NULL, *netconf_dir = NULL, *c_rehash_cmd = NULL; DIR *dir = NULL; struct dirent *d; @@ -2014,7 +2014,7 @@ cmd_cert(const char *arg, char **UNUSED(tmp_config_file)) if (!(trusted_dir = get_default_trustedCA_dir(NULL))) { ERROR("cert display", "Could not get the default trusted CA directory"); - return EXIT_FAILURE; + goto error; } dir = opendir(trusted_dir); @@ -2036,32 +2036,29 @@ cmd_cert(const char *arg, char **UNUSED(tmp_config_file)) if (none) { printf("No certificates found in the default trusted CA directory.\n"); } - free(trusted_dir); } else if (!strcmp(cmd, "add")) { path = strtok_r(NULL, " ", &ptr); if (!path || (strlen(path) < 5)) { ERROR("cert add", "Missing or wrong path to the certificate"); - return EXIT_FAILURE; + goto error; } if (eaccess(path, R_OK)) { ERROR("cert add", "Cannot access certificate \"%s\": %s", path, strerror(errno)); - return EXIT_FAILURE; + goto error; } trusted_dir = get_default_trustedCA_dir(NULL); if (!trusted_dir) { ERROR("cert add", "Could not get the default trusted CA directory"); - return EXIT_FAILURE; + goto error; } if ((asprintf(&dest, "%s/%s", trusted_dir, strrchr(path, '/') + 1) == -1) || (asprintf(&c_rehash_cmd, "c_rehash %s &> /dev/null", trusted_dir) == -1)) { ERROR("cert add", "Memory allocation failed"); - free(trusted_dir); - return EXIT_FAILURE; + goto error; } - free(trusted_dir); if (strcmp(dest + strlen(dest) - 4, ".pem")) { ERROR("cert add", "CA certificates are expected to be in *.pem format"); @@ -2070,25 +2067,19 @@ cmd_cert(const char *arg, char **UNUSED(tmp_config_file)) if (cp(dest, path)) { ERROR("cert add", "Could not copy the certificate: %s", strerror(errno)); - free(dest); - free(c_rehash_cmd); - return EXIT_FAILURE; + goto error; } - free(dest); if (((ret = system(c_rehash_cmd)) == -1) || WEXITSTATUS(ret)) { ERROR("cert add", "c_rehash execution failed"); - free(c_rehash_cmd); - return EXIT_FAILURE; + goto error; } - free(c_rehash_cmd); - } else if (!strcmp(cmd, "remove")) { path = strtok_r(NULL, " ", &ptr); if (!path) { ERROR("cert remove", "Missing the certificate name"); - return EXIT_FAILURE; + goto error; } /* delete ".pem" if the user unnecessarily included it */ @@ -2099,49 +2090,39 @@ cmd_cert(const char *arg, char **UNUSED(tmp_config_file)) trusted_dir = get_default_trustedCA_dir(NULL); if (!trusted_dir) { ERROR("cert remove", "Could not get the default trusted CA directory"); - return EXIT_FAILURE; + goto error; } if ((asprintf(&dest, "%s/%s.pem", trusted_dir, path) == -1) || (asprintf(&c_rehash_cmd, "c_rehash %s &> /dev/null", trusted_dir) == -1)) { ERROR("cert remove", "Memory allocation failed"); - free(trusted_dir); - return EXIT_FAILURE; + goto error; } - free(trusted_dir); if (remove(dest)) { ERROR("cert remove", "Cannot remove certificate \"%s\": %s (use the name from \"cert display\" output)", path, strerror(errno)); - free(dest); - free(c_rehash_cmd); - return EXIT_FAILURE; + goto error; } - free(dest); if (((ret = system(c_rehash_cmd)) == -1) || WEXITSTATUS(ret)) { ERROR("cert remove", "c_rehash execution failed"); - free(c_rehash_cmd); - return EXIT_FAILURE; + goto error; } - free(c_rehash_cmd); - } else if (!strcmp(cmd, "displayown")) { int crt = 0, key = 0, pem = 0; netconf_dir = get_netconf_dir(); if (!netconf_dir) { ERROR("cert displayown", "Could not get the client home directory"); - return EXIT_FAILURE; + goto error; } if (asprintf(&dest, "%s/client.pem", netconf_dir) == -1) { ERROR("cert displayown", "Memory allocation failed"); - free(netconf_dir); - return EXIT_FAILURE; + goto error; } - free(netconf_dir); if (!eaccess(dest, R_OK)) { pem = 1; } @@ -2181,42 +2162,39 @@ cmd_cert(const char *arg, char **UNUSED(tmp_config_file)) strcpy(dest + strlen(dest) - 4, ".pem"); parse_cert("PEM", dest); } - free(dest); } else if (!strcmp(cmd, "replaceown")) { path = strtok_r(NULL, " ", &ptr); if (!path || (strlen(path) < 5)) { ERROR("cert replaceown", "Missing the certificate or invalid path."); - return EXIT_FAILURE; + goto error; } if (eaccess(path, R_OK)) { ERROR("cert replaceown", "Cannot access the certificate \"%s\": %s", path, strerror(errno)); - return EXIT_FAILURE; + goto error; } path2 = strtok_r(NULL, " ", &ptr); if (path2) { if (strlen(path2) < 5) { ERROR("cert replaceown", "Invalid private key path."); - return EXIT_FAILURE; + goto error; } if (eaccess(path2, R_OK)) { ERROR("cert replaceown", "Cannot access the private key \"%s\": %s", path2, strerror(errno)); - return EXIT_FAILURE; + goto error; } } netconf_dir = get_netconf_dir(); if (!netconf_dir) { ERROR("cert replaceown", "Could not get the client home directory"); - return EXIT_FAILURE; + goto error; } if (asprintf(&dest, "%s/client.XXX", netconf_dir) == -1) { ERROR("cert replaceown", "Memory allocation failed"); - free(netconf_dir); - return EXIT_FAILURE; + goto error; } - free(netconf_dir); if (path2) { /* CRT & KEY */ @@ -2229,14 +2207,12 @@ cmd_cert(const char *arg, char **UNUSED(tmp_config_file)) strcpy(dest + strlen(dest) - 4, ".crt"); if (cp(dest, path)) { ERROR("cert replaceown", "Could not copy the certificate \"%s\": %s", path, strerror(errno)); - free(dest); - return EXIT_FAILURE; + goto error; } strcpy(dest + strlen(dest) - 4, ".key"); if (cp(dest, path2)) { ERROR("cert replaceown", "Could not copy the private key \"%s\": %s", path, strerror(errno)); - free(dest); - return EXIT_FAILURE; + goto error; } } else { /* PEM */ @@ -2253,19 +2229,27 @@ cmd_cert(const char *arg, char **UNUSED(tmp_config_file)) strcpy(dest + strlen(dest) - 4, ".pem"); if (cp(dest, path)) { ERROR("cert replaceown", "Could not copy the certificate \"%s\": %s", path, strerror(errno)); - free(dest); - return EXIT_FAILURE; + goto error; } } - free(dest); - } else { ERROR("cert", "Unknown argument %s", cmd); - return EXIT_FAILURE; + goto error; } + free(dest); + free(trusted_dir); + free(netconf_dir); + free(c_rehash_cmd); return EXIT_SUCCESS; + +error: + free(dest); + free(trusted_dir); + free(netconf_dir); + free(c_rehash_cmd); + return EXIT_FAILURE; } static int @@ -2273,8 +2257,8 @@ cmd_crl(const char *arg, char **UNUSED(tmp_config_file)) { int ret; char *args = strdupa(arg); - char *cmd = NULL, *ptr = NULL, *path, *dest; - char *crl_dir, *c_rehash_cmd; + char *cmd = NULL, *ptr = NULL, *path, *dest = NULL; + char *crl_dir = NULL, *c_rehash_cmd = NULL; DIR *dir = NULL; struct dirent *d; @@ -2289,7 +2273,7 @@ cmd_crl(const char *arg, char **UNUSED(tmp_config_file)) if (!(crl_dir = get_default_CRL_dir(NULL))) { ERROR("crl display", "Could not get the default CRL directory"); - return EXIT_FAILURE; + goto error; } dir = opendir(crl_dir); @@ -2311,32 +2295,29 @@ cmd_crl(const char *arg, char **UNUSED(tmp_config_file)) if (none) { printf("No CRLs found in the default CRL directory.\n"); } - free(crl_dir); } else if (!strcmp(cmd, "add")) { path = strtok_r(NULL, " ", &ptr); if (!path || (strlen(path) < 5)) { ERROR("crl add", "Missing or wrong path to the certificate"); - return EXIT_FAILURE; + goto error; } if (eaccess(path, R_OK)) { ERROR("crl add", "Cannot access certificate \"%s\": %s", path, strerror(errno)); - return EXIT_FAILURE; + goto error; } crl_dir = get_default_CRL_dir(NULL); if (!crl_dir) { ERROR("crl add", "Could not get the default CRL directory"); - return EXIT_FAILURE; + goto error; } if ((asprintf(&dest, "%s/%s", crl_dir, strrchr(path, '/') + 1) == -1) || (asprintf(&c_rehash_cmd, "c_rehash %s &> /dev/null", crl_dir) == -1)) { ERROR("crl add", "Memory allocation failed"); - free(crl_dir); - return EXIT_FAILURE; + goto error; } - free(crl_dir); if (strcmp(dest + strlen(dest) - 4, ".pem")) { ERROR("crl add", "CRLs are expected to be in *.pem format"); @@ -2345,25 +2326,19 @@ cmd_crl(const char *arg, char **UNUSED(tmp_config_file)) if (cp(dest, path)) { ERROR("crl add", "Could not copy the CRL \"%s\": %s", path, strerror(errno)); - free(dest); - free(c_rehash_cmd); - return EXIT_FAILURE; + goto error; } - free(dest); if (((ret = system(c_rehash_cmd)) == -1) || WEXITSTATUS(ret)) { ERROR("crl add", "c_rehash execution failed"); - free(c_rehash_cmd); - return EXIT_FAILURE; + goto error; } - free(c_rehash_cmd); - } else if (!strcmp(cmd, "remove")) { path = strtok_r(NULL, " ", &ptr); if (!path) { ERROR("crl remove", "Missing the certificate name"); - return EXIT_FAILURE; + goto error; } // delete ".pem" if the user unnecessarily included it @@ -2374,40 +2349,41 @@ cmd_crl(const char *arg, char **UNUSED(tmp_config_file)) crl_dir = get_default_CRL_dir(NULL); if (!crl_dir) { ERROR("crl remove", "Could not get the default CRL directory"); - return EXIT_FAILURE; + goto error; } if ((asprintf(&dest, "%s/%s.pem", crl_dir, path) == -1) || (asprintf(&c_rehash_cmd, "c_rehash %s &> /dev/null", crl_dir) == -1)) { ERROR("crl remove", "Memory allocation failed"); - free(crl_dir); - return EXIT_FAILURE; + goto error; } - free(crl_dir); if (remove(dest)) { ERROR("crl remove", "Cannot remove CRL \"%s\": %s (use the name from \"crl display\" output)", path, strerror(errno)); - free(dest); - free(c_rehash_cmd); - return EXIT_FAILURE; + goto error; } - free(dest); if (((ret = system(c_rehash_cmd)) == -1) || WEXITSTATUS(ret)) { ERROR("crl remove", "c_rehash execution failed"); - free(c_rehash_cmd); - return EXIT_FAILURE; + goto error; } - free(c_rehash_cmd); - } else { ERROR("crl", "Unknown argument %s", cmd); - return EXIT_FAILURE; + goto error; } + free(dest); + free(c_rehash_cmd); + free(crl_dir); return EXIT_SUCCESS; + +error: + free(dest); + free(c_rehash_cmd); + free(crl_dir); + return EXIT_FAILURE; } static int @@ -2460,12 +2436,12 @@ cmd_connect_listen_tls(struct arglist *cmd, int is_connect) break; case 'c': if (asprintf(&cert, "%s", optarg) == -1) { - return EXIT_FAILURE; + goto error_cleanup; } break; case 'k': if (asprintf(&key, "%s", optarg) == -1) { - return EXIT_FAILURE; + goto error_cleanup; } break; case 'r': @@ -2478,7 +2454,7 @@ cmd_connect_listen_tls(struct arglist *cmd, int is_connect) } else { cmd_listen_help(); } - return EXIT_FAILURE; + goto error_cleanup; } } diff --git a/cli/configuration.c b/cli/configuration.c index 9277c1e90..e88395b7f 100644 --- a/cli/configuration.c +++ b/cli/configuration.c @@ -178,9 +178,8 @@ get_default_trustedCA_dir(DIR **ret_dir) if (ret_dir) { if (!(*ret_dir = opendir(cert_dir))) { ERROR(__func__, "Unable to open the default trusted CA directory (%s).", strerror(errno)); - free(cert_dir); - return NULL; } + free(cert_dir); return NULL; } @@ -223,9 +222,8 @@ get_default_CRL_dir(DIR **ret_dir) if (ret_dir) { if (!(*ret_dir = opendir(crl_dir))) { ERROR(__func__, "Unable to open the default CRL directory (%s).", strerror(errno)); - free(crl_dir); - return NULL; } + free(crl_dir); return NULL; } diff --git a/src/netconf_confirmed_commit.c b/src/netconf_confirmed_commit.c index de78e6dc4..54781fea6 100644 --- a/src/netconf_confirmed_commit.c +++ b/src/netconf_confirmed_commit.c @@ -524,6 +524,7 @@ backup_module(sr_session_ctx_t *session, const struct lys_module *module) cleanup: sr_release_data(data); + free(ncc_path); free(path); free(xpath); return rc; From 65e3ba279ea3f3135dbce0412efaea831f8ff24c Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 21 Feb 2022 14:29:24 +0100 Subject: [PATCH 63/70] VERSION bump to version 2.1.15 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64d8a1da3..f69db84c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.14) +set(NP2SRV_VERSION 2.1.15) # libyang required version set(LIBYANG_DEP_VERSION 2.0.128) From a74c2c5977a898225fb40955bf7d587e0bddb805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Thu, 24 Feb 2022 09:33:02 +0100 Subject: [PATCH 64/70] tests UPDATE print to stderr (#1154) When running tests via ctest, setup failure messages are not shown for some reason (they are shown when running the test directly). Fix this by changing the macro to print to stderr. --- tests/np_test.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/np_test.h b/tests/np_test.h index 40ebdc587..76969c27b 100644 --- a/tests/np_test.h +++ b/tests/np_test.h @@ -25,7 +25,7 @@ #include #define SETUP_FAIL_LOG \ - printf("Setup fail in %s:%d.\n", __FILE__, __LINE__) + fprintf(stderr, "Setup fail in %s:%d.\n", __FILE__, __LINE__) #define FREE_TEST_VARS(state) \ nc_rpc_free(state->rpc); \ From 05d9ac47195345dcef35443980aa2a4390257f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Thu, 24 Feb 2022 10:56:50 +0100 Subject: [PATCH 65/70] Save sysrepo executable env vars after first cmake Currently, if you're using SYSREPO{CTL,CFG}_EXECUTABLE env vars, you have to specify it manually when installing Netopeer2 and also when running tests. The new code saves the env variable, so that you only have to specify it when running cmake for the first time. --- CMakeLists.txt | 22 ++++++++++++++++++++++ tests/CMakeLists.txt | 2 ++ 2 files changed, 24 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f69db84c9..f76ae0de6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -312,6 +312,22 @@ include_directories(${SYSREPO_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_INCLUDES ${SYSREPO_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${SYSREPO_LIBRARIES}) +# find sysrepoctl to be used for installation and tests +if (NOT SYSREPOCTL_EXECUTABLE) + find_program(SYSREPOCTL_EXECUTABLE sysrepoctl) +endif() +if (NOT SYSREPOCTL_EXECUTABLE) + message(FATAL_ERROR "Unable to find sysrepoctl, set SYSREPOCTL_EXECUTABLE manually.") +endif() + +# find sysrepocfg to be used for installation and tests +if (NOT SYSREPOCFG_EXECUTABLE) + find_program(SYSREPOCFG_EXECUTABLE sysrepocfg) +endif() +if (NOT SYSREPOCFG_EXECUTABLE) + message(FATAL_ERROR "Unable to find sysrepocfg, set SYSREPOCFG_EXECUTABLE manually.") +endif() + # generate files configure_file("${PROJECT_SOURCE_DIR}/src/config.h.in" "${PROJECT_BINARY_DIR}/config.h" ESCAPE_QUOTES @ONLY) configure_file("${PROJECT_SOURCE_DIR}/service/netopeer2-server.service.in" "${PROJECT_BINARY_DIR}/netopeer2-server.service" @ONLY) @@ -336,6 +352,8 @@ if(INSTALL_MODULES) set(ENV{NP2_MODULE_PERMS} \"${MODULES_PERMS}\") set(ENV{NP2_MODULE_OWNER} \"${MODULES_OWNER}\") set(ENV{NP2_MODULE_GROUP} \"${MODULES_GROUP}\") + set(ENV{SYSREPOCTL_EXECUTABLE} \"${SYSREPOCTL_EXECUTABLE}\") + set(ENV{SYSREPOCFG_EXECUTABLE} \"${SYSREPOCFG_EXECUTABLE}\") execute_process(COMMAND \"${SCRIPT_DIR}/setup.sh\" RESULT_VARIABLE SETUP_RES) if(NOT SETUP_RES EQUAL \"0\") message(FATAL_ERROR \" scripts/setup.sh failed: \${SETUP_RES}\") @@ -347,6 +365,8 @@ endif() if(GENERATE_HOSTKEY) install(CODE " message(STATUS \"Generating a new RSA host key \\\"genkey\\\" if not already added...\") + set(ENV{SYSREPOCTL_EXECUTABLE} \"${SYSREPOCTL_EXECUTABLE}\") + set(ENV{SYSREPOCFG_EXECUTABLE} \"${SYSREPOCFG_EXECUTABLE}\") execute_process(COMMAND ${SCRIPT_DIR}/merge_hostkey.sh RESULT_VARIABLE MERGE_HOSTKEY_RES) if(NOT MERGE_HOSTKEY_RES EQUAL \"0\") message(FATAL_ERROR \" scripts/merge_hostkey.sh failed: \${MERGE_HOSTKEY_RES}\") @@ -356,6 +376,8 @@ endif() if(MERGE_LISTEN_CONFIG) install(CODE " message(STATUS \"Merging default server listen configuration if there is none...\") + set(ENV{SYSREPOCTL_EXECUTABLE} \"${SYSREPOCTL_EXECUTABLE}\") + set(ENV{SYSREPOCFG_EXECUTABLE} \"${SYSREPOCFG_EXECUTABLE}\") execute_process(COMMAND ${SCRIPT_DIR}/merge_config.sh RESULT_VARIABLE MERGE_CONFIG_RES) if(NOT MERGE_CONFIG_RES EQUAL \"0\") message(FATAL_ERROR \" scripts/merge_config.sh failed: \${MERGE_CONFIG_RES}\") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b99a7ca4b..009cf18e5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -67,6 +67,7 @@ foreach(test_name IN LISTS tests) add_test(NAME ${test_name} COMMAND $ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST ${test_name} APPEND PROPERTY ENVIRONMENT "MALLOC_CHECK_=3") set_property(TEST ${test_name} APPEND PROPERTY ENVIRONMENT "TEST_NAME=${test_name}") + set_property(TEST ${test_name} APPEND PROPERTY ENVIRONMENT "SYSREPOCTL_EXECUTABLE=${SYSREPOCTL_EXECUTABLE}") if(${CMAKE_VERSION} VERSION_GREATER "3.7") set_tests_properties(${test_name} PROPERTIES FIXTURES_REQUIRED tests_cleanup) endif() @@ -78,6 +79,7 @@ if(ENABLE_VALGRIND_TESTS) add_test(NAME ${test_name}_valgrind COMMAND valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 ${CMAKE_CURRENT_BINARY_DIR}/${test_name} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set(test_name "${test_name}_valgrind") set_property(TEST ${test_name} APPEND PROPERTY ENVIRONMENT "TEST_NAME=${test_name}") + set_property(TEST ${test_name} APPEND PROPERTY ENVIRONMENT "SYSREPOCTL_EXECUTABLE=${SYSREPOCTL_EXECUTABLE}") if(${CMAKE_VERSION} VERSION_GREATER "3.7") set_tests_properties(${test_name} PROPERTIES FIXTURES_REQUIRED tests_cleanup) endif() From 1269b40448bf2dca74d46c1541d97acb53243919 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Mon, 28 Feb 2022 11:21:56 +0100 Subject: [PATCH 66/70] server UPDATE use sysrepo NACM NACM moved from the server to sysrepo. --- CMakeLists.txt | 2 - README.md | 9 +- scripts/remove.sh | 1 - scripts/setup.sh | 1 - src/common.c | 7 +- src/common.h | 1 - src/err_netconf.c | 17 - src/err_netconf.h | 2 - src/main.c | 101 +- src/netconf.c | 58 +- src/netconf_acm.c | 1704 ------------------------ src/netconf_acm.h | 172 --- src/netconf_nmda.c | 8 - src/netconf_subscribed_notifications.c | 27 - src/netconf_subscribed_notifications.h | 8 - src/subscribed_notifications.c | 1 - src/yang_push.c | 96 +- tests/np_test.c | 53 +- tests/np_test.h | 6 +- tests/test_candidate.c | 5 +- tests/test_confirmed_commit.c | 5 +- tests/test_edit.c | 6 +- tests/test_nacm.c | 5 +- tests/test_rpc.c | 36 +- tests/test_sub_ntf_advanced.c | 7 +- tests/test_url.c | 17 +- tests/test_with_defaults.c | 5 +- 27 files changed, 138 insertions(+), 2222 deletions(-) delete mode 100644 src/netconf_acm.c delete mode 100644 src/netconf_acm.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f76ae0de6..8a05e62f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,6 @@ option(ENABLE_COVERAGE "Build code coverage report from tests" OFF) option(BUILD_CLI "Build and install neotpeer2-cli" ON) option(ENABLE_URL "Enable URL capability" ON) set(THREAD_COUNT 5 CACHE STRING "Number of threads accepting new sessions and handling requests") -set(NACM_RECOVERY_UID 0 CACHE STRING "NACM recovery session UID that has unrestricted access") set(POLL_IO_TIMEOUT 10 CACHE STRING "Timeout in milliseconds of polling sessions for new data. It is also used for synchronization of low level IO such as sending a reply while a notification is being sent") set(YANG_MODULE_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/yang/modules/netopeer2" CACHE STRING "Directory where to copy the YANG modules to") @@ -129,7 +128,6 @@ set(SERVER_SRC src/common.c src/netconf.c src/netconf_monitoring.c - src/netconf_acm.c src/netconf_nmda.c src/netconf_subscribed_notifications.c src/netconf_confirmed_commit.c diff --git a/README.md b/README.md index ccd300f17..c02445029 100644 --- a/README.md +++ b/README.md @@ -158,11 +158,10 @@ $ make coverage ## NACM -This NETCONF server implements full *ietf-netconf-acm* access control that **bypasses** *sysrepo* -file system access control. NACM is enabled by default, so users other than `root` will not be -allowed to *write* any data but should be granted *read* and *execute* permissions unless -the access was modified by a NACM extension. When deploying this server, it is strongly advised -to configure NACM properly. +This NETCONF server uses *ietf-netconf-acm* access control of *sysrepo*. NACM is enabled by default, +so except for the recovery user, no others will be allowed to *write* any data but should be granted +*read* and *execute* permissions unless the access was modified by a NACM extension. When deploying +this server, it is strongly advised to configure NACM properly. ## Server configuration diff --git a/scripts/remove.sh b/scripts/remove.sh index d229a3534..381d10b94 100755 --- a/scripts/remove.sh +++ b/scripts/remove.sh @@ -13,7 +13,6 @@ fi # array of modules to remove, exact same as setup.sh MODULES=( -"ietf-netconf-acm@2018-02-14.yang" "ietf-netconf@2013-09-29.yang -e writable-running -e candidate -e rollback-on-error -e validate -e startup -e url -e xpath -e confirmed-commit" "ietf-netconf-monitoring@2010-10-04.yang" "ietf-netconf-nmda@2019-01-07.yang -e origin -e with-defaults" diff --git a/scripts/setup.sh b/scripts/setup.sh index 4790e4a37..0b155a641 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -23,7 +23,6 @@ GROUP=${NP2_MODULE_GROUP} # array of modules to install MODULES=( -"ietf-netconf-acm@2018-02-14.yang" "ietf-netconf@2013-09-29.yang -e writable-running -e candidate -e rollback-on-error -e validate -e startup -e url -e xpath -e confirmed-commit" "ietf-netconf-monitoring@2010-10-04.yang" "ietf-netconf-nmda@2019-01-07.yang -e origin -e with-defaults" diff --git a/src/common.c b/src/common.c index cb9858266..b77fe2aad 100644 --- a/src/common.c +++ b/src/common.c @@ -40,11 +40,11 @@ #include #include #include +#include #include "common.h" #include "compat.h" #include "log.h" -#include "netconf_acm.h" #include "netconf_monitoring.h" struct np2srv np2srv = {.unix_mode = -1, .unix_uid = -1, .unix_gid = -1}; @@ -297,6 +297,11 @@ np2srv_new_session_cb(const char *UNUSED(client_name), struct nc_session *new_se username = nc_session_get_username(new_session); sr_session_push_orig_data(sr_sess, strlen(username) + 1, username); + /* set NACM username for it to be applied */ + if (sr_nacm_set_user(sr_sess, username)) { + goto error; + } + c = 0; while ((c < 3) && nc_ps_add_session(np2srv.nc_ps, new_session)) { /* presumably timeout, give it a shot 2 times */ diff --git a/src/common.h b/src/common.h index 87cf6188f..82ceb3586 100644 --- a/src/common.h +++ b/src/common.h @@ -69,7 +69,6 @@ struct np2srv { }; extern struct np2srv np2srv; -extern ATOMIC_T skip_nacm_nc_sid; /** * @brief Sleep in milliseconds. diff --git a/src/err_netconf.c b/src/err_netconf.c index cb79cff06..0fa3f10eb 100644 --- a/src/err_netconf.c +++ b/src/err_netconf.c @@ -24,23 +24,6 @@ #include "common.h" #include "compat.h" -void -np_err_nacm_access_denied(sr_session_ctx_t *ev_sess, const char *module_name, const char *user, const char *path) -{ - char *msg; - - /* generate message */ - if (asprintf(&msg, "Access to the data model \"%s\" is denied because \"%s\" NACM authorization failed.", - module_name, user) == -1) { - return; - } - - /* set error */ - sr_session_set_netconf_error(ev_sess, "protocol", "access-denied", NULL, path, msg, 0); - - free(msg); -} - void np_err_sr2nc_lock_denied(sr_session_ctx_t *ev_sess, const sr_error_info_t *err_info) { diff --git a/src/err_netconf.h b/src/err_netconf.h index 424dfce76..400fe0535 100644 --- a/src/err_netconf.h +++ b/src/err_netconf.h @@ -19,8 +19,6 @@ #include -void np_err_nacm_access_denied(sr_session_ctx_t *ev_sess, const char *module_name, const char *user, const char *path); - void np_err_sr2nc_lock_denied(sr_session_ctx_t *ev_sess, const sr_error_info_t *err_info); void np_err_sr2nc_in_use(sr_session_ctx_t *ev_sess, const sr_error_info_t *err_info); diff --git a/src/main.c b/src/main.c index 4e1cd4425..69d094e33 100644 --- a/src/main.c +++ b/src/main.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "common.h" #include "compat.h" @@ -48,7 +49,6 @@ #ifdef NC_ENABLED_TLS # include "netconf_server_tls.h" #endif -#include "netconf_acm.h" #include "netconf_confirmed_commit.h" #include "netconf_monitoring.h" #include "netconf_nmda.h" @@ -62,9 +62,6 @@ /** @brief flag for main loop */ ATOMIC_T loop_continue = 1; -/* NETCONF SID of session to skip diff check for */ -ATOMIC_T skip_nacm_nc_sid; - static void *worker_thread(void *arg); /** @@ -331,36 +328,14 @@ static struct nc_server_reply * np2srv_rpc_cb(struct lyd_node *rpc, struct nc_session *ncs) { struct np2_user_sess *user_sess; - const struct lyd_node *denied; struct lyd_node *node; const sr_error_info_t *err_info; struct nc_server_reply *reply = NULL; struct lyd_node *child = NULL; sr_data_t *output; NC_WD_MODE nc_wd; - struct lyd_node *e; - char *str; int rc; - /* check NACM */ - if ((denied = ncac_check_operation(rpc, nc_session_get_username(ncs)))) { - e = nc_err(LYD_CTX(rpc), NC_ERR_ACCESS_DENIED, NC_ERR_TYPE_APP); - - /* set path */ - str = lysc_path(denied->schema, LYSC_PATH_LOG, NULL, 0); - nc_err_set_path(e, str); - free(str); - - /* set message */ - if (asprintf(&str, "Executing the operation is denied because \"%s\" NACM authorization failed.", - nc_session_get_username(ncs)) > -1) { - nc_err_set_msg(e, str, "en"); - free(str); - } - - return nc_server_reply_err(e); - } - /* use default lnc2 callbacks when available */ if (!strcmp(LYD_NAME(rpc), "get-schema") && !strcmp(lyd_owner_module(rpc)->name, "ietf-netconf-monitoring")) { return nc_clb_default_get_schema(rpc, ncs); @@ -425,44 +400,6 @@ np2srv_rpc_cb(struct lyd_node *rpc, struct nc_session *ncs) return reply; } -/** - * @brief Sysrepo callback for checking NACM of data diff. - * - * @param[in] session SR session. - * @param[in] diff Diff to check. - * @return SR error value. - */ -static int -np2srv_diff_check_cb(sr_session_ctx_t *session, const struct lyd_node *diff) -{ - const struct lyd_node *node; - char *path; - const char *user, *name; - uint32_t *nc_sid; - - name = sr_session_get_orig_name(session); - if (!name || strcmp(name, "netopeer2")) { - EINT; - return SR_ERR_INTERNAL; - } - sr_session_get_orig_data(session, 0, NULL, (const void **)&nc_sid); - if (ATOMIC_LOAD_RELAXED(skip_nacm_nc_sid) == *nc_sid) { - /* skip the NACM check */ - return SR_ERR_OK; - } - - sr_session_get_orig_data(session, 1, NULL, (const void **)&user); - if ((node = ncac_check_diff(diff, user))) { - /* access denied */ - path = lysc_path(node->schema, LYSC_PATH_LOG, NULL, 0); - np_err_nacm_access_denied(session, node->schema->module->name, user, path); - free(path); - return SR_ERR_UNAUTHORIZED; - } - - return SR_ERR_OK; -} - /** * @brief Check SR schema context for all the required schemas and features. * @@ -568,12 +505,6 @@ server_init(void) goto error; } - /* set edit-config NACM diff check callback */ - rc = sr_set_diff_check_callback(np2srv.sr_conn, np2srv_diff_check_cb); - if (rc != SR_ERR_OK) { - WRN("Unable to set diff check callback (%s), NACM will not be applied when editing data.", sr_strerror(rc)); - } - /* set the content-id callback */ nc_server_set_content_id_clb(np2srv_content_id_cb, NULL, NULL); @@ -592,9 +523,6 @@ server_init(void) /* init monitoring */ ncm_init(); - /* init NACM */ - ncac_init(); - /* init libnetconf2 */ if (nc_server_init()) { goto error; @@ -696,7 +624,7 @@ server_destroy(void) ncm_destroy(); /* NACM cleanup */ - ncac_destroy(); + sr_nacm_destroy(); /* confirmed commit cleanup */ ncc_commit_ctx_destroy(); @@ -946,28 +874,9 @@ server_data_subscribe(void) /* * ietf-netconf-acm */ - mod_name = "ietf-netconf-acm"; - xpath = "/ietf-netconf-acm:nacm"; - SR_CONFIG_SUBSCR(mod_name, xpath, ncac_nacm_params_cb); - - xpath = "/ietf-netconf-acm:nacm/groups/group"; - SR_CONFIG_SUBSCR(mod_name, xpath, ncac_group_cb); - - xpath = "/ietf-netconf-acm:nacm/rule-list"; - SR_CONFIG_SUBSCR(mod_name, xpath, ncac_rule_list_cb); - - xpath = "/ietf-netconf-acm:nacm/rule-list/rule"; - SR_CONFIG_SUBSCR(mod_name, xpath, ncac_rule_cb); - - /* state data */ - xpath = "/ietf-netconf-acm:nacm/denied-operations"; - SR_OPER_SUBSCR(mod_name, xpath, ncac_oper_cb); - - xpath = "/ietf-netconf-acm:nacm/denied-data-writes"; - SR_OPER_SUBSCR(mod_name, xpath, ncac_oper_cb); - - xpath = "/ietf-netconf-acm:nacm/denied-notifications"; - SR_OPER_SUBSCR(mod_name, xpath, ncac_oper_cb); + if (sr_nacm_init(np2srv.sr_sess, 0, &np2srv.sr_data_sub)) { + goto error; + } return 0; diff --git a/src/netconf.c b/src/netconf.c index f01134ce4..923906c4d 100644 --- a/src/netconf.c +++ b/src/netconf.c @@ -32,12 +32,12 @@ #include #include +#include #include "common.h" #include "compat.h" #include "err_netconf.h" #include "log.h" -#include "netconf_acm.h" #include "netconf_confirmed_commit.h" #include "netconf_monitoring.h" @@ -208,7 +208,7 @@ np2srv_rpc_get_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char struct np2_user_sess *user_sess = NULL; struct ly_set *nodeset = NULL; sr_datastore_t ds = 0; - const char *single_filter, *username; + const char *single_filter; if (NP_IGNORE_RPC(session, event)) { /* ignore in this case */ @@ -297,18 +297,12 @@ np2srv_rpc_get_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char goto cleanup; } - /* perform correct NACM filtering */ - sr_session_get_orig_data(session, 1, NULL, (const void **)&username); - ncac_check_data_read_filter(&data_get, username); - /* add output */ if (lyd_new_any(output, NULL, "data", data_get, 1, LYD_ANYDATA_DATATREE, 1, &node)) { goto cleanup; } data_get = NULL; - /* success */ - cleanup: op_filter_erase(&filter); lyd_free_siblings(data_get); @@ -441,8 +435,7 @@ np2srv_rpc_copyconfig_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), con sr_data_t *sr_data; int rc = SR_ERR_OK, run_to_start = 0, source_is_config = 0; struct np2_user_sess *user_sess = NULL; - const char *username; - uint32_t *nc_sid; + struct nc_session *nc_sess; #ifdef NP2SRV_URL_CAPAB const char *trg_url = NULL; @@ -534,24 +527,19 @@ np2srv_rpc_copyconfig_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), con goto cleanup; } - /* NACM checks */ if (!source_is_config && !run_to_start) { - /* get source datastore data and filter them */ + /* get source datastore data */ sr_session_switch_ds(session, sds); - rc = sr_get_data(session, "/*", 0, np2srv.sr_timeout, 0, &sr_data); - if (rc != SR_ERR_OK) { + if ((rc = sr_get_data(session, "/*", 0, np2srv.sr_timeout, 0, &sr_data))) { goto cleanup; } config = sr_data->tree; sr_data->tree = NULL; sr_release_data(sr_data); - - sr_session_get_orig_data(session, 1, NULL, (const void **)&username); - ncac_check_data_read_filter(&config, username); } /* get the user session */ - if ((rc = np_get_user_sess(session, NULL, &user_sess))) { + if ((rc = np_get_user_sess(session, &nc_sess, &user_sess))) { goto cleanup; } @@ -588,23 +576,30 @@ np2srv_rpc_copyconfig_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), con /* config is spent */ rc = sr_replace_config(user_sess->sess, NULL, config, np2srv.sr_timeout); config = NULL; + if (rc) { + sr_session_dup_error(user_sess->sess, session); + goto cleanup; + } } else { if (run_to_start) { - /* set SID to skip NACM check, only one copy-config can be executed at once */ - sr_session_get_orig_data(session, 0, NULL, (const void **)&nc_sid); - ATOMIC_STORE_RELAXED(skip_nacm_nc_sid, *nc_sid); + /* skip NACM check */ + sr_nacm_set_user(user_sess->sess, NULL); + } + + if ((rc = sr_copy_config(user_sess->sess, NULL, sds, np2srv.sr_timeout))) { + /* prevent the error info being overwritten */ + sr_session_dup_error(user_sess->sess, session); + } + + /* set NACM username back */ + sr_nacm_set_user(user_sess->sess, nc_session_get_username(nc_sess)); + + if (rc) { + goto cleanup; } - rc = sr_copy_config(user_sess->sess, NULL, sds, np2srv.sr_timeout); - ATOMIC_STORE_RELAXED(skip_nacm_nc_sid, 0); - } - if (rc) { - sr_session_dup_error(user_sess->sess, session); - goto cleanup; } } - /* success */ - cleanup: lyd_free_siblings(config); np_release_user_sess(user_sess); @@ -980,11 +975,6 @@ np2srv_rpc_subscribe_ntf_cb(sr_session_ctx_t *UNUSED(session), uint32_t sub_id, notif = lyd_parent(notif); } - /* check NACM */ - if (ncac_check_operation(notif, nc_session_get_username(cb_data->nc_sess))) { - goto cleanup; - } - /* create the notification object, all the passed arguments must exist until it is sent */ ly_time_ts2str(timestamp, &datetime); nc_ntf = nc_server_notif_new((struct lyd_node *)notif, datetime, NC_PARAMTYPE_CONST); diff --git a/src/netconf_acm.c b/src/netconf_acm.c deleted file mode 100644 index 02c361a38..000000000 --- a/src/netconf_acm.c +++ /dev/null @@ -1,1704 +0,0 @@ -/** - * @file netconf_acm.c - * @author Michal Vasko - * @brief NACM and ietf-netconf-acm callbacks - * - * @copyright - * Copyright (c) 2019 - 2021 Deutsche Telekom AG. - * Copyright (c) 2017 - 2021 CESNET, z.s.p.o. - * - * This source code is licensed under BSD 3-Clause License (the "License"). - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - */ -#define _GNU_SOURCE -#define _DEFAULT_SOURCE - -#include "netconf_acm.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "common.h" -#include "compat.h" -#include "log.h" - -static struct ncac nacm; - -/* /ietf-netconf-acm:nacm */ -int -ncac_nacm_params_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNUSED(module_name), const char *xpath, - sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) -{ - sr_change_iter_t *iter; - sr_change_oper_t op; - const struct lyd_node *node; - const struct lyd_node_term *term; - char *xpath2; - int rc; - - if (asprintf(&xpath2, "%s/*", xpath) == -1) { - EMEM; - return SR_ERR_NO_MEMORY; - } - rc = sr_get_changes_iter(session, xpath2, &iter); - free(xpath2); - if (rc != SR_ERR_OK) { - ERR("Getting changes iter failed (%s).", sr_strerror(rc)); - return rc; - } - - /* NACM LOCK */ - pthread_mutex_lock(&nacm.lock); - - while ((rc = sr_get_change_tree_next(session, iter, &op, &node, NULL, NULL, NULL)) == SR_ERR_OK) { - term = (struct lyd_node_term *)node; - if (!strcmp(node->schema->name, "enable-nacm")) { - if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { - if (term->value.boolean) { - nacm.enabled = 1; - } else { - nacm.enabled = 0; - } - } - } else if (!strcmp(node->schema->name, "read-default")) { - if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { - if (!strcmp(lyd_get_value(node), "permit")) { - nacm.default_read_deny = 0; - } else { - nacm.default_read_deny = 1; - } - } - } else if (!strcmp(node->schema->name, "write-default")) { - if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { - if (!strcmp(lyd_get_value(node), "permit")) { - nacm.default_write_deny = 0; - } else { - nacm.default_write_deny = 1; - } - } - } else if (!strcmp(node->schema->name, "exec-default")) { - if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { - if (!strcmp(lyd_get_value(node), "permit")) { - nacm.default_exec_deny = 0; - } else { - nacm.default_exec_deny = 1; - } - } - } else if (!strcmp(node->schema->name, "enable-external-groups")) { - if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { - if (term->value.boolean) { - nacm.enable_external_groups = 1; - } else { - nacm.enable_external_groups = 0; - } - } - } - } - - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - - sr_free_change_iter(iter); - if (rc != SR_ERR_NOT_FOUND) { - ERR("Getting next change failed (%s).", sr_strerror(rc)); - return rc; - } - - return SR_ERR_OK; -} - -/* /ietf-netconf-acm:nacm/denied-* */ -int -ncac_oper_cb(sr_session_ctx_t *UNUSED(session), uint32_t UNUSED(sub_id), const char *UNUSED(module_name), const char *path, - const char *UNUSED(request_xpath), uint32_t UNUSED(request_id), struct lyd_node **parent, void *UNUSED(private_data)) -{ - LY_ERR lyrc; - char num_str[11]; - - assert(*parent); - - /* NACM LOCK */ - pthread_mutex_lock(&nacm.lock); - - if (!strcmp(path, "/ietf-netconf-acm:nacm/denied-operations")) { - sprintf(num_str, "%u", nacm.denied_operations); - lyrc = lyd_new_path(*parent, NULL, "denied-operations", num_str, 0, NULL); - } else if (!strcmp(path, "/ietf-netconf-acm:nacm/denied-data-writes")) { - sprintf(num_str, "%u", nacm.denied_data_writes); - lyrc = lyd_new_path(*parent, NULL, "denied-data-writes", num_str, 0, NULL); - } else { - assert(!strcmp(path, "/ietf-netconf-acm:nacm/denied-notifications")); - sprintf(num_str, "%u", nacm.denied_notifications); - lyrc = lyd_new_path(*parent, NULL, "denied-notifications", num_str, 0, NULL); - } - - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - - if (lyrc) { - return SR_ERR_INTERNAL; - } - - return SR_ERR_OK; -} - -static struct ncac_group * -ncac_group_find(const char *group_name) -{ - uint32_t i; - - for (i = 0; i < nacm.group_count; ++i) { - if (!strcmp(nacm.groups[i].name, group_name)) { - return &nacm.groups[i]; - } - } - - return NULL; -} - -/* /ietf-netconf-acm:nacm/groups/group */ -int -ncac_group_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNUSED(module_name), const char *xpath, - sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) -{ - sr_change_iter_t *iter; - sr_change_oper_t op; - const struct lyd_node *node; - const char *group_name, *user_name; - struct ncac_group *group = NULL; - uint32_t i; - char *xpath2; - int rc; - void *mem; - - if (asprintf(&xpath2, "%s//.", xpath) == -1) { - EMEM; - return SR_ERR_NO_MEMORY; - } - rc = sr_get_changes_iter(session, xpath2, &iter); - free(xpath2); - if (rc != SR_ERR_OK) { - ERR("Getting changes iter failed (%s).", sr_strerror(rc)); - return rc; - } - - /* NACM LOCK */ - pthread_mutex_lock(&nacm.lock); - - while ((rc = sr_get_change_tree_next(session, iter, &op, &node, NULL, NULL, NULL)) == SR_ERR_OK) { - if (!strcmp(node->schema->name, "group")) { - /* name must be present */ - assert(!strcmp(lyd_child(node)->schema->name, "name")); - group_name = lyd_get_value(lyd_child(node)); - - switch (op) { - case SR_OP_CREATED: - /* add new group */ - mem = realloc(nacm.groups, (nacm.group_count + 1) * sizeof *nacm.groups); - if (!mem) { - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - - EMEM; - return SR_ERR_NO_MEMORY; - } - nacm.groups = mem; - group = &nacm.groups[nacm.group_count]; - ++nacm.group_count; - - group->name = strdup(group_name); - group->users = NULL; - group->user_count = 0; - break; - case SR_OP_DELETED: - /* find it */ - group = ncac_group_find(group_name); - assert(group && nacm.group_count); - - /* delete it */ - free(group->name); - for (i = 0; i < group->user_count; ++i) { - free(group->users[i]); - } - free(group->users); - - --nacm.group_count; - if (i < nacm.group_count) { - memcpy(group, &nacm.groups[nacm.group_count], sizeof *group); - } - if (!nacm.group_count) { - free(nacm.groups); - nacm.groups = NULL; - } - group = NULL; - break; - default: - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - - EINT; - return SR_ERR_INTERNAL; - } - } else { - /* name must be present */ - assert(!strcmp(node->parent->child->schema->name, "name")); - group = ncac_group_find(lyd_get_value(node->parent->child)); - - if (!strcmp(node->schema->name, "user-name")) { - if ((op == SR_OP_DELETED) && !group) { - continue; - } - - assert(group); - user_name = lyd_get_value(node); - - if (op == SR_OP_CREATED) { - mem = realloc(group->users, (group->user_count + 1) * sizeof *group->users); - if (!mem) { - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - - EMEM; - return SR_ERR_NO_MEMORY; - } - group->users = mem; - group->users[group->user_count] = strdup(user_name); - ++group->user_count; - } else { - assert(op == SR_OP_DELETED); - for (i = 0; i < group->user_count; ++i) { - if (!strcmp(group->users[i], user_name)) { - break; - } - } - assert(i < group->user_count); - - /* delete it */ - free(group->users[i]); - --group->user_count; - if (i < group->user_count) { - group->users[i] = group->users[group->user_count]; - } - if (!group->user_count) { - free(group->users); - group->users = NULL; - } - } - } - } - } - - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - - sr_free_change_iter(iter); - if (rc != SR_ERR_NOT_FOUND) { - ERR("Getting next change failed (%s).", sr_strerror(rc)); - return rc; - } - - return SR_ERR_OK; -} - -/** - * @brief Remove all rules from a rule list. - * - * @param[in,out] list Rule list to remove from. - */ -static void -ncac_remove_rules(struct ncac_rule_list *list) -{ - struct ncac_rule *rule, *tmp; - - LY_LIST_FOR_SAFE(list->rules, tmp, rule) { - free(rule->name); - free(rule->module_name); - free(rule->target); - free(rule->comment); - free(rule); - } - list->rules = NULL; -} - -/** - * @brief Get pointer to an item on a specific index. - * - * @param[in] items Array of items. - * @param[in] item_size Size of each item. - * @param[in] idx Index of the item to get. - * @return Pointer to the item at index. - */ -#define ITEM_IDX_PTR(items, item_size, idx) (char **)(((uintptr_t)items) + ((idx) * (item_size))) - -/** - * @brief Compare callback for sorting functions like qsort(3) and bsearch(3). - * - * @param[in] ptr1 Pointer to the first value. - * @param[in] ptr2 Pointer to the second value. - * @return < 0 if ptr1 < ptr2. - * @return 0 if ptr1 == ptr2. - * @return > 0 if ptr1 > ptr2. - */ -static int -ncac_sort_strcmp_cb(const void *ptr1, const void *ptr2) -{ - const char **str1, **str2; - - str1 = (const char **)ptr1; - str2 = (const char **)ptr2; - - return strcmp(*str1, *str2); -} - -/** - * @brief Find an item in a sorted array. - * - * @param[in] item Pointer to item to find. - * @param[in] item_size Size of an item. - * @param[in] items Item array. - * @param[in] item_count Number of @p items. - * @param[out] match Optional pointer to the found item. - * @return Index of the item in @p items. - * @return -1 if no matching item was found. - */ -static int32_t -ncac_strarr_sort_find(const char **item, size_t item_size, char **items, uint32_t item_count) -{ - const char **m; - int32_t idx = -1; - - if (!items) { - return idx; - } - - m = bsearch(item, items, item_count, item_size, ncac_sort_strcmp_cb); - if (m) { - idx = ((uintptr_t)m - (uintptr_t)items) / item_size; - } - - return idx; -} - -/** - * @brief Add an item into a sorted array. - * - * @param[in] item Pointer to item to add. - * @param[in] item_size Size of an item. - * @param[in] check_dup Whether to check for duplicates before adding, returns SR_ERR_OK if duplicate found. - * @param[in,out] items Pointer to the item array. - * @param[in,out] item_count Pointer to the number of @p items. - * @return Sysrepo err value. - */ -static int -ncac_strarr_sort_add(const char **item, size_t item_size, int check_dup, char ***items, uint32_t *item_count) -{ - void *mem; - uint32_t i; - - if (check_dup && (ncac_strarr_sort_find(item, item_size, *items, *item_count) > -1)) { - /* already added */ - return SR_ERR_OK; - } - - /* starting index, assume normal distribution and names starting with lowercase letters */ - if ((*item)[0] < 'a') { - i = 0; - } else if ((*item)[0] > 'z') { - i = *item_count ? *item_count - 1 : 0; - } else { - i = ((*item)[0] - 'a') * ((double)*item_count / 26.0); - } - - /* find the index to add it on */ - if (*item_count && (strcmp(*ITEM_IDX_PTR(*items, item_size, i), *item) > 0)) { - while (i && (strcmp(*ITEM_IDX_PTR(*items, item_size, i - 1), *item) > 0)) { - --i; - } - } else if (*item_count && (strcmp(*ITEM_IDX_PTR(*items, item_size, i), *item) < 0)) { - while ((i < *item_count) && (strcmp(*ITEM_IDX_PTR(*items, item_size, i), *item) < 0)) { - ++i; - } - } - - /* realloc */ - mem = realloc(*items, (*item_count + 1) * item_size); - if (!mem) { - EMEM; - return SR_ERR_NO_MEMORY; - } - *items = mem; - - /* move all following items */ - if (i < *item_count) { - memmove(ITEM_IDX_PTR(*items, item_size, i + 1), ITEM_IDX_PTR(*items, item_size, i), (*item_count - i) * item_size); - } - - /* insert new item */ - *ITEM_IDX_PTR(*items, item_size, i) = strdup(*item); - ++(*item_count); - return SR_ERR_OK; -} - -/** - * @brief Remove an item from a sorted array. - * - * @param[in] item Pointer to item to remove. - * @param[in] item_size Size of an item. - * @param[in,out] items Pointer to the item array. - * @param[in,out] item_count Pointer to the number of @p items. - */ -static void -ncac_strarr_sort_del(const char **item, size_t item_size, char ***items, uint32_t *item_count) -{ - int32_t i; - - /* find the item, get its index */ - i = ncac_strarr_sort_find(item, item_size, *items, *item_count); - assert(i > -1); - - /* delete it, keep the order */ - free(*ITEM_IDX_PTR(*items, item_size, i)); - --(*item_count); - if ((uint32_t)i < *item_count) { - memmove(ITEM_IDX_PTR(*items, item_size, i), ITEM_IDX_PTR(*items, item_size, i + 1), (*item_count - i) * item_size); - } - if (!*item_count) { - free(*items); - *items = NULL; - } -} - -/* /ietf-netconf-acm:nacm/rule-list */ -int -ncac_rule_list_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNUSED(module_name), const char *xpath, - sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) -{ - sr_change_iter_t *iter; - sr_change_oper_t op; - const struct lyd_node *node; - const char *prev_list, *rlist_name, *group_name; - struct ncac_rule_list *rlist = NULL, *prev_rlist; - char *xpath2; - int rc, len; - uint32_t i; - - if (asprintf(&xpath2, "%s//.", xpath) == -1) { - EMEM; - return SR_ERR_NO_MEMORY; - } - rc = sr_get_changes_iter(session, xpath2, &iter); - free(xpath2); - if (rc != SR_ERR_OK) { - ERR("Getting changes iter failed (%s).", sr_strerror(rc)); - return rc; - } - - /* NACM LOCK */ - pthread_mutex_lock(&nacm.lock); - - while ((rc = sr_get_change_tree_next(session, iter, &op, &node, NULL, &prev_list, NULL)) == SR_ERR_OK) { - if (!strcmp(node->schema->name, "rule-list")) { - /* name must be present */ - assert(!strcmp(lyd_child(node)->schema->name, "name")); - rlist_name = lyd_get_value(lyd_child(node)); - - switch (op) { - case SR_OP_MOVED: - /* find it */ - prev_rlist = NULL; - for (rlist = nacm.rule_lists; rlist && strcmp(rlist->name, rlist_name); rlist = rlist->next) { - prev_rlist = rlist; - } - assert(rlist); - - /* unlink it */ - if (prev_rlist) { - prev_rlist->next = rlist->next; - } else { - nacm.rule_lists = rlist->next; - } - /* fallthrough */ - case SR_OP_CREATED: - if (op == SR_OP_CREATED) { - /* create new rule list */ - rlist = calloc(1, sizeof *rlist); - if (!rlist) { - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - - EMEM; - return SR_ERR_NO_MEMORY; - } - rlist->name = strdup(rlist_name); - } - - /* find previous list */ - assert(prev_list); - if (prev_list[0]) { - assert(strchr(prev_list, '\'')); - prev_list = strchr(prev_list, '\'') + 1; - len = strchr(prev_list, '\'') - prev_list; - prev_rlist = nacm.rule_lists; - while (prev_rlist && strncmp(prev_rlist->name, prev_list, len)) { - prev_rlist = prev_rlist->next; - } - assert(prev_rlist); - } else { - prev_rlist = NULL; - } - - /* insert after previous list */ - if (prev_rlist) { - rlist->next = prev_rlist->next; - prev_rlist->next = rlist; - } else { - rlist->next = nacm.rule_lists; - nacm.rule_lists = rlist; - } - break; - case SR_OP_DELETED: - /* find it */ - prev_rlist = NULL; - for (rlist = nacm.rule_lists; rlist && strcmp(rlist->name, rlist_name); rlist = rlist->next) { - prev_rlist = rlist; - } - assert(rlist); - - /* delete it */ - free(rlist->name); - for (i = 0; i < rlist->group_count; ++i) { - free(rlist->groups[i]); - } - free(rlist->groups); - ncac_remove_rules(rlist); - if (prev_rlist) { - prev_rlist->next = rlist->next; - } else { - nacm.rule_lists = rlist->next; - } - free(rlist); - rlist = NULL; - break; - default: - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - - EINT; - return SR_ERR_INTERNAL; - } - } else { - /* name must be present */ - assert(!strcmp(node->parent->child->schema->name, "name")); - rlist_name = lyd_get_value(node->parent->child); - for (rlist = nacm.rule_lists; rlist && strcmp(rlist->name, rlist_name); rlist = rlist->next) {} - - if (!strcmp(node->schema->name, "group")) { - if ((op == SR_OP_DELETED) && !rlist) { - continue; - } - - assert(rlist); - group_name = lyd_get_value(node); - - if (op == SR_OP_CREATED) { - if ((rc = ncac_strarr_sort_add(&group_name, sizeof rlist->groups, 0, &rlist->groups, - &rlist->group_count))) { - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - return rc; - } - } else { - assert(op == SR_OP_DELETED); - ncac_strarr_sort_del(&group_name, sizeof rlist->groups, &rlist->groups, &rlist->group_count); - } - } - } - } - - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - - sr_free_change_iter(iter); - if (rc != SR_ERR_NOT_FOUND) { - ERR("Getting next change failed (%s).", sr_strerror(rc)); - return rc; - } - - return SR_ERR_OK; -} - -/* /ietf-netconf-acm:nacm/rule-list/rule */ -int -ncac_rule_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *UNUSED(module_name), const char *xpath, - sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) -{ - sr_change_iter_t *iter; - sr_change_oper_t op; - const struct lyd_node *node; - const char *prev_list, *rule_name, *rlist_name, *str; - struct ncac_rule_list *rlist; - struct ncac_rule *rule = NULL, *prev_rule; - char *xpath2; - int rc, len; - - if (asprintf(&xpath2, "%s//.", xpath) == -1) { - EMEM; - return SR_ERR_NO_MEMORY; - } - rc = sr_get_changes_iter(session, xpath2, &iter); - free(xpath2); - if (rc != SR_ERR_OK) { - ERR("Getting changes iter failed (%s).", sr_strerror(rc)); - return rc; - } - - /* NACM LOCK */ - pthread_mutex_lock(&nacm.lock); - - while ((rc = sr_get_change_tree_next(session, iter, &op, &node, NULL, &prev_list, NULL)) == SR_ERR_OK) { - if (!strcmp(node->schema->name, "rule")) { - /* find parent rule list */ - assert(!strcmp(node->parent->child->schema->name, "name")); - rlist_name = lyd_get_value(node->parent->child); - for (rlist = nacm.rule_lists; rlist && strcmp(rlist->name, rlist_name); rlist = rlist->next) {} - if ((op == SR_OP_DELETED) && !rlist) { - /* even parent rule-list was deleted */ - continue; - } - assert(rlist); - - /* name must be present */ - assert(!strcmp(lyd_child(node)->schema->name, "name")); - rule_name = lyd_get_value(lyd_child(node)); - - switch (op) { - case SR_OP_MOVED: - /* find it */ - prev_rule = NULL; - for (rule = rlist->rules; rule && strcmp(rule->name, rule_name); rule = rule->next) { - prev_rule = rule; - } - assert(rule); - - /* unlink it */ - if (prev_rule) { - prev_rule->next = rule->next; - } else { - rlist->rules = rule->next; - } - /* fallthrough */ - case SR_OP_CREATED: - if (op == SR_OP_CREATED) { - /* create new rule */ - rule = calloc(1, sizeof *rule); - if (!rule) { - EMEM; - - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - return SR_ERR_NO_MEMORY; - } - rule->name = strdup(rule_name); - rule->target_type = NCAC_TARGET_ANY; - } - assert(rule); - - /* find previous rule */ - assert(prev_list); - if (prev_list[0]) { - assert(strchr(prev_list, '\'')); - prev_list = strchr(prev_list, '\'') + 1; - len = strchr(prev_list, '\'') - prev_list; - prev_rule = rlist->rules; - while (prev_rule && strncmp(prev_rule->name, prev_list, len)) { - prev_rule = prev_rule->next; - } - assert(prev_rule); - } else { - prev_rule = NULL; - } - - /* insert after previous rule */ - if (prev_rule) { - rule->next = prev_rule->next; - prev_rule->next = rule; - } else { - rule->next = rlist->rules; - rlist->rules = rule; - } - break; - case SR_OP_DELETED: - /* find it */ - prev_rule = NULL; - for (rule = rlist->rules; rule && strcmp(rule->name, rule_name); rule = rule->next) { - prev_rule = rule; - } - assert(rule); - - /* delete it */ - free(rule->name); - free(rule->module_name); - free(rule->target); - free(rule->comment); - if (prev_rule) { - prev_rule->next = rule->next; - } else { - rlist->rules = rule->next; - } - free(rule); - break; - default: - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - - EINT; - return SR_ERR_INTERNAL; - } - } else { - /* find parent rule list */ - assert(!strcmp(node->parent->parent->child->schema->name, "name")); - rlist_name = lyd_get_value(node->parent->parent->child); - for (rlist = nacm.rule_lists; rlist && strcmp(rlist->name, rlist_name); rlist = rlist->next) {} - if ((op == SR_OP_DELETED) && !rlist) { - /* even parent rule-list was deleted */ - continue; - } - assert(rlist); - - /* name must be present */ - assert(!strcmp(node->parent->child->schema->name, "name")); - rule_name = lyd_get_value(node->parent->child); - for (rule = rlist->rules; rule && strcmp(rule->name, rule_name); rule = rule->next) {} - if ((op == SR_OP_DELETED) && !rule) { - /* even parent rule was deleted */ - continue; - } - assert(rule); - - if (!strcmp(node->schema->name, "module-name")) { - str = lyd_get_value(node); - free(rule->module_name); - if (!strcmp(str, "*")) { - rule->module_name = NULL; - } else { - rule->module_name = strdup(str); - } - } else if (!strcmp(node->schema->name, "rpc-name") || !strcmp(node->schema->name, "notification-name") || - !strcmp(node->schema->name, "path")) { - if (op == SR_OP_DELETED) { - free(rule->target); - rule->target = NULL; - rule->target_type = NCAC_TARGET_ANY; - } else { - str = lyd_get_value(node); - free(rule->target); - if (!strcmp(str, "*")) { - rule->target = NULL; - } else { - rule->target = strdup(str); - } - if (!strcmp(node->schema->name, "rpc-name")) { - rule->target_type = NCAC_TARGET_RPC; - } else if (!strcmp(node->schema->name, "notification-name")) { - rule->target_type = NCAC_TARGET_NOTIF; - } else { - assert(!strcmp(node->schema->name, "path")); - rule->target_type = NCAC_TARGET_DATA; - } - } - } else if (!strcmp(node->schema->name, "access-operations")) { - str = lyd_get_value(node); - rule->operations = 0; - if (!strcmp(str, "*")) { - rule->operations = NCAC_OP_ALL; - } else { - if (strstr(str, "create")) { - rule->operations |= NCAC_OP_CREATE; - } - if (strstr(str, "read")) { - rule->operations |= NCAC_OP_READ; - } - if (strstr(str, "update")) { - rule->operations |= NCAC_OP_UPDATE; - } - if (strstr(str, "delete")) { - rule->operations |= NCAC_OP_DELETE; - } - if (strstr(str, "exec")) { - rule->operations |= NCAC_OP_EXEC; - } - } - } else if (!strcmp(node->schema->name, "action")) { - if (!strcmp(lyd_get_value(node), "permit")) { - rule->action_deny = 0; - } else { - rule->action_deny = 1; - } - } else if (!strcmp(node->schema->name, "comment")) { - if (op == SR_OP_DELETED) { - free(rule->comment); - rule->comment = NULL; - } else { - assert((op == SR_OP_MODIFIED) || (op == SR_OP_CREATED)); - free(rule->comment); - rule->comment = strdup(lyd_get_value(node)); - } - } - } - } - - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - - sr_free_change_iter(iter); - if (rc != SR_ERR_NOT_FOUND) { - ERR("Getting next change failed (%s).", sr_strerror(rc)); - return rc; - } - - return SR_ERR_OK; -} - -void -ncac_init(void) -{ - pthread_mutex_init(&nacm.lock, NULL); -} - -void -ncac_destroy(void) -{ - struct ncac_group *group; - struct ncac_rule_list *rule_list, *tmp; - uint32_t i, j; - - for (i = 0; i < nacm.group_count; ++i) { - group = &nacm.groups[i]; - free(group->name); - for (j = 0; j < group->user_count; ++j) { - free(group->users[j]); - } - free(group->users); - } - free(nacm.groups); - - LY_LIST_FOR_SAFE(nacm.rule_lists, tmp, rule_list) { - free(rule_list->name); - for (i = 0; i < rule_list->group_count; ++i) { - free(rule_list->groups[i]); - } - free(rule_list->groups); - ncac_remove_rules(rule_list); - free(rule_list); - } - - pthread_mutex_destroy(&nacm.lock); -} - -/** - * @brief Get passwd entry of a user, specifically its UID and GID. - * - * @param[in] user User to learn about. - * @param[out] uid User UID, if set. - * @param[out] gid User GID, if set. - * @return 0 on success, 1 on user not found, -1 on error. - */ -static int -ncac_getpwnam(const char *user, uid_t *uid, gid_t *gid) -{ - struct passwd pwd, *pwd_p; - char *buf = NULL; - ssize_t buflen; - int ret; - - assert(user); - - buflen = sysconf(_SC_GETPW_R_SIZE_MAX); - if (buflen == -1) { - buflen = 2048; - } - buf = malloc(buflen); - if (!buf) { - EMEM; - return -1; - } - ret = getpwnam_r(user, &pwd, buf, buflen, &pwd_p); - if (ret) { - ERR("Getting user \"%s\" pwd entry failed (%s).", user, strerror(ret)); - free(buf); - return -1; - } else if (!pwd_p) { - free(buf); - return 1; - } - - if (uid) { - *uid = pwd.pw_uid; - } - if (gid) { - *gid = pwd.pw_gid; - } - free(buf); - return 0; -} - -/** - * @brief Check NACM acces for the data tree. If this check passes, no other check is necessary. - * If not, each node must be checked separately to decide. - * - * @param[in] root Root schema node of the data subtree. - * @param[in] user User, whose access to check. - * @return non-zero if access allowed, 0 if more checks are required. - */ -static int -ncac_allowed_tree(const struct lysc_node *root, const char *user) -{ - uid_t user_uid; - - /* 1) NACM is off */ - if (!nacm.enabled) { - return 1; - } - - /* 2) recovery session allowed */ - if (!ncac_getpwnam(user, &user_uid, NULL) && (user_uid == NP2SRV_NACM_RECOVERY_UID)) { - return 1; - } - - /* 3) and notifications , always allowed */ - if ((root->nodetype == LYS_RPC) && !strcmp(root->name, "close-session") && - !strcmp(root->module->name, "ietf-netconf")) { - return 1; - } else if ((root->nodetype == LYS_NOTIF) && !strcmp(root->module->name, "nc-notifications")) { - return 1; - } - - /* 4) , , and not checked for execute permission - RFC 8341 section 3.2.4 - * (assume it is the same for ) */ - if ((root->nodetype == LYS_RPC) && (((!strcmp(root->name, "get") || !strcmp(root->name, "get-config")) && - !strcmp(root->module->name, "ietf-netconf")) || (!strcmp(root->name, "get-data") && - !strcmp(root->module->name, "ietf-netconf-nmda")))) { - return 1; - } - - return 0; -} - -/** - * @brief Collect all NACM groups for a user. If enabled, even system ones. - * - * @param[in] user User to collect groups for. - * @param[out] groups Sorted array of collected groups. - * @param[out] group_count Number of @p groups. - * @return 0 on success, -1 on error. - */ -static int -ncac_collect_groups(const char *user, char ***groups, uint32_t *group_count) -{ - struct group grp, *grp_p; - gid_t user_gid; - char *buf = NULL; - gid_t *gids = NULL; - ssize_t buflen; - uint32_t i, j; - int gid_count = 0, ret, rc = -1; - - *groups = NULL; - *group_count = 0; - - /* collect NACM groups */ - for (i = 0; i < nacm.group_count; ++i) { - for (j = 0; j < nacm.groups[i].user_count; ++j) { - if (!strcmp(nacm.groups[i].users[j], user)) { - if (ncac_strarr_sort_add((const char **)&nacm.groups[i].name, sizeof **groups, 0, groups, group_count)) { - goto cleanup; - } - } - } - } - - /* collect system groups */ - if (nacm.enable_external_groups) { - ret = ncac_getpwnam(user, NULL, &user_gid); - if (ret) { - if (ret == 1) { - /* no user, no more groups */ - rc = 0; - } - goto cleanup; - } - - /* get all GIDs */ - getgrouplist(user, user_gid, gids, &gid_count); - gids = malloc(gid_count * sizeof *gids); - if (!gids) { - EMEM; - goto cleanup; - } - ret = getgrouplist(user, user_gid, gids, &gid_count); - if (ret == -1) { - ERR("Getting system groups of user \"%s\" failed.", user); - goto cleanup; - } - - /* add all GIDs group names */ - buflen = sysconf(_SC_GETGR_R_SIZE_MAX); - if (buflen == -1) { - buflen = 2048; - } - free(buf); - buf = malloc(buflen); - if (!buf) { - EMEM; - goto cleanup; - } - for (i = 0; i < (unsigned)gid_count; ++i) { - ret = getgrgid_r(gids[i], &grp, buf, buflen, &grp_p); - if (ret) { - ERR("Getting GID grp entry failed (%s).", strerror(ret)); - goto cleanup; - } else if (!grp_p) { - ERR("Getting GID grp entry failed (Group not found)."); - goto cleanup; - } - - /* add, if not already there */ - if (ncac_strarr_sort_add((const char **)&grp.gr_name, sizeof **groups, 1, groups, group_count)) { - goto cleanup; - } - } - } - - /* success */ - rc = 0; - -cleanup: - free(gids); - free(buf); - return rc; -} - -/** - * @brief Check NACM match of a node path and specific rule target. - * - * Details on matching in description of typedef ietf-netconf-acm:node-instance-identifier. - * - * @param[in] rule_target Rule target instance-identifier. - * @param[in] node_path Node data path. - * @return 0 if does not match. - * @return 1 if the rule path matches. - * @return 2 if the path is a partial match. - */ -static int -ncac_allowed_path(const char *rule_target, const char *node_path) -{ - const char *rule_ptr, *node_ptr; - - rule_ptr = rule_target; - node_ptr = node_path; - - while (rule_ptr[0] && node_ptr[0]) { - if (rule_ptr[0] == node_ptr[0]) { - ++rule_ptr; - ++node_ptr; - } else if ((rule_ptr[0] == '/') && (node_ptr[0] == '[')) { - /* target has no predicate, skip it in path as well because it matches any value */ - while (node_ptr[0] != ']') { - if (node_ptr[0] == '\'') { - do { - ++node_ptr; - } while (node_ptr[0] != '\''); - } - - ++node_ptr; - } - - ++node_ptr; - } else { - /* not a match */ - return 0; - } - } - - if (!rule_ptr[0] && !node_ptr[0]) { - /* full match */ - return 1; - } else if (rule_ptr[0]) { - assert(!node_ptr[0]); - /* rule continues, it is a partial match */ - return 2; - } else { - assert(!rule_ptr[0]); - /* node continues, prefix (descendant) match */ - return 1; - } -} - -/** - * @brief Check whether any group from a rule list matches one of the user groups. - * - * @param[in] rlist Rule list with sorted groups. - * @param[in] groups User group sorted array. - * @param[in] group_count Count of @p groups. - * @return 1 if a match is found. - * @return 0 if no matching group is found. - */ -static int -ncac_rule_group_match(struct ncac_rule_list *rlist, char **groups, uint32_t group_count) -{ - uint32_t i = 0, j = 0; - int r; - - while ((i < rlist->group_count) && (j < group_count)) { - if (!strcmp(rlist->groups[i], "*")) { - /* match for all groups */ - return 1; - } - - r = strcmp(rlist->groups[i], groups[j]); - if (r > 0) { - ++j; - } else if (r < 0) { - ++i; - } else { - /* match */ - return 1; - } - } - - /* no match */ - return 0; -} - -/** - * @brief Free all NACM groups. Supposed to be called after @ref ncac_collect_group. - * - * @param[out] groups Sorted array of collected groups to free - * @param[out] group_count Number of @p groups. - */ -static void -ncac_free_groups(char **groups, uint32_t group_count) -{ - uint32_t i; - - if (!groups) { - return; - } - - for (i = 0; i < group_count; ++i) { - free(groups[i]); - } - free(groups); -} - -/** - * @brief Check NACM access for a single node. - * - * @param[in] node Node to check. Can be NULL if @p node_path and @p node_schema are set. - * @param[in] node_path Node path of the node to check. Can be NULL if @p node is set. - * @param[in] node_schema Schema of the node to check. Can be NULL if @p node is set. - * @param[in] oper Operation to check. - * @param[in] groups Array of groups name to be checked for permissions - * @param[in] group_count Length of @p groups - * @return NCAC access enum. - */ -static enum ncac_access -ncac_allowed_node(const struct lyd_node *node, const char *node_path, const struct lysc_node *node_schema, - uint8_t oper, char **groups, uint32_t group_count) -{ - struct ncac_rule_list *rlist; - struct ncac_rule *rule; - char *path; - enum ncac_access access = NCAC_ACCESS_DENY; - - enum { - RULE_PARTIAL_MATCH_NONE = 0, - RULE_PARTIAL_MATCH_PERMIT = 1, - RULE_PARTIAL_MATCH_DENY = 2 - } partial_access = RULE_PARTIAL_MATCH_NONE; - int path_match; - LY_ARRAY_COUNT_TYPE u; - - assert(node || (node_path && node_schema)); - assert(oper); - - if (!node_schema) { - node_schema = node->schema; - } - - /* - * ref https://tools.ietf.org/html/rfc8341#section-3.4.4 - */ - - /* 4) collected groups passed as argument */ - - /* 5) no groups */ - if (!group_count) { - goto step10; - } - - /* 6) find matching rule lists */ - for (rlist = nacm.rule_lists; rlist; rlist = rlist->next) { - if (!ncac_rule_group_match(rlist, groups, group_count)) { - /* no group match */ - continue; - } - - /* 7) find matching rules */ - for (rule = rlist->rules; rule; rule = rule->next) { - /* access operation matching */ - if (!(rule->operations & oper)) { - continue; - } - - /* target (rule) type matching */ - switch (rule->target_type) { - case NCAC_TARGET_RPC: - if (node_schema->nodetype != LYS_RPC) { - continue; - } - if (rule->target && strcmp(rule->target, node_schema->name)) { - /* exact match needed */ - continue; - } - break; - case NCAC_TARGET_NOTIF: - /* only top-level notification */ - if (node_schema->parent || (node_schema->nodetype != LYS_NOTIF)) { - continue; - } - if (rule->target && strcmp(rule->target, node_schema->name)) { - /* exact match needed */ - continue; - } - break; - case NCAC_TARGET_DATA: - if (node_schema->nodetype & (LYS_RPC | LYS_NOTIF)) { - continue; - } - /* fallthrough */ - case NCAC_TARGET_ANY: - if (rule->target) { - /* exact match or is a descendant (specified in RFC 8341 page 27) for full tree access */ - if (!node_path) { - path = lyd_path(node, LYD_PATH_STD, NULL, 0); - path_match = ncac_allowed_path(rule->target, path); - free(path); - } else { - path_match = ncac_allowed_path(rule->target, node_path); - } - - if (!path_match) { - continue; - } else if (path_match == 2) { - /* partial match, continue searching for a full match */ - partial_access |= rule->action_deny ? RULE_PARTIAL_MATCH_DENY : RULE_PARTIAL_MATCH_PERMIT; - continue; - } - } - break; - } - - /* module name matching, after partial path matches */ - if (rule->module_name && strcmp(rule->module_name, node_schema->module->name)) { - continue; - } - - /* 8) rule matched */ - access = rule->action_deny ? NCAC_ACCESS_DENY : NCAC_ACCESS_PERMIT; - goto cleanup; - } - } - - /* 9) no matching rule found */ - -step10: - /* 10) check default-deny-all extension */ - LY_ARRAY_FOR(node_schema->exts, u) { - if (!strcmp(node_schema->exts[u].def->module->name, "ietf-netconf-acm")) { - if (!strcmp(node_schema->exts[u].def->name, "default-deny-all")) { - goto cleanup; - } - if ((oper & (NCAC_OP_CREATE | NCAC_OP_UPDATE | NCAC_OP_DELETE)) && - !strcmp(node_schema->exts[u].def->name, "default-deny-write")) { - goto cleanup; - } - } - } - - /* 11) was already covered in 10) */ - - /* 12) check defaults */ - switch (oper) { - case NCAC_OP_READ: - if (nacm.default_read_deny) { - access = NCAC_ACCESS_DENY; - } else { - /* permit, but not by an explicit rule */ - access = NCAC_ACCESS_PARTIAL_PERMIT; - } - break; - case NCAC_OP_CREATE: - case NCAC_OP_UPDATE: - case NCAC_OP_DELETE: - if (nacm.default_write_deny) { - access = NCAC_ACCESS_DENY; - } else { - /* permit, but not by an explicit rule */ - access = NCAC_ACCESS_PARTIAL_PERMIT; - } - break; - case NCAC_OP_EXEC: - if (nacm.default_exec_deny) { - access = NCAC_ACCESS_DENY; - } else { - /* permit, but not by an explicit rule */ - access = NCAC_ACCESS_PARTIAL_PERMIT; - } - break; - default: - EINT; - goto cleanup; - } - -cleanup: - if ((access == NCAC_ACCESS_DENY) && (partial_access & RULE_PARTIAL_MATCH_PERMIT)) { - /* node itself is not allowed but a rule allows access to some descendants so it may be allowed at the end */ - access = NCAC_ACCESS_PARTIAL_DENY; - } else if ((access == NCAC_ACCESS_PERMIT) && (partial_access & RULE_PARTIAL_MATCH_DENY)) { - /* node itself is allowed but a rule denies access to some descendants */ - access = NCAC_ACCESS_PARTIAL_PERMIT; - } - return access; -} - -const struct lyd_node * -ncac_check_operation(const struct lyd_node *data, const char *user) -{ - const struct lyd_node *op = NULL; - char **groups = NULL; - uint32_t group_count = 0; - int allowed = 0; - - /* NACM LOCK */ - pthread_mutex_lock(&nacm.lock); - - /* check access for the whole data tree first */ - if (ncac_allowed_tree(data->schema, user)) { - allowed = 1; - goto cleanup; - } - - if (ncac_collect_groups(user, &groups, &group_count)) { - goto cleanup; - } - - op = data; - while (op) { - if (op->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) { - /* we found the desired node */ - break; - } - - switch (op->schema->nodetype) { - case LYS_CONTAINER: - case LYS_LIST: - if (!lyd_child(op)) { - /* list/container without children, invalid */ - op = NULL; - } else { - op = lyd_child(op); - } - break; - case LYS_LEAF: - assert(lysc_is_key(op->schema)); - if (!op->next) { - /* last key of the last in-depth list, invalid */ - op = NULL; - } else { - op = op->next; - } - break; - default: - op = NULL; - break; - } - } - if (!op) { - EINT; - goto cleanup; - } - - if (op->schema->nodetype & (LYS_RPC | LYS_ACTION)) { - /* check X access on the RPC/action */ - if (!NCAC_ACCESS_IS_NODE_PERMIT(ncac_allowed_node(op, NULL, NULL, NCAC_OP_EXEC, groups, group_count))) { - goto cleanup; - } - } else { - assert(op->schema->nodetype == LYS_NOTIF); - - /* check R access on the notification */ - if (!NCAC_ACCESS_IS_NODE_PERMIT(ncac_allowed_node(op, NULL, NULL, NCAC_OP_READ, groups, group_count))) { - goto cleanup; - } - } - - if (op->parent) { - /* check R access on the parents, the last parent must be enough */ - if (!NCAC_ACCESS_IS_NODE_PERMIT(ncac_allowed_node(lyd_parent(op), NULL, NULL, NCAC_OP_READ, groups, - group_count))) { - goto cleanup; - } - } - - allowed = 1; - -cleanup: - ncac_free_groups(groups, group_count); - if (allowed) { - op = NULL; - } else if (op) { - if (op->schema->nodetype & (LYS_RPC | LYS_ACTION)) { - ++nacm.denied_operations; - } else { - ++nacm.denied_notifications; - } - } - - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - return op; -} - -/** - * @brief Filter out any siblings for which the user does not have R access, recursively. - * - * @param[in,out] first First sibling to filter. - * @param[in] user User for the NACM filtering. - * @param[in] groups Array of collected groups. - * @param[in] group_count Number of @p groups. - * @return Highest access among descendants (recursively), permit is the highest. - */ -static enum ncac_access -ncac_check_data_read_filter_r(struct lyd_node **first, const char *user, char **groups, uint32_t group_count) -{ - struct lyd_node *next, *elem; - enum ncac_access node_access, ret_access = NCAC_ACCESS_DENY; - - LY_LIST_FOR_SAFE(*first, next, elem) { - /* check access of the node */ - node_access = ncac_allowed_node(elem, NULL, NULL, NCAC_OP_READ, groups, group_count); - - if (node_access == NCAC_ACCESS_PARTIAL_DENY) { - /* only partial deny access, we must check children recursively to learn whether this node is allowed or not */ - if (elem->schema->nodetype & LYD_NODE_INNER) { - node_access = ncac_check_data_read_filter_r(&((struct lyd_node_inner *)elem)->child, user, groups, group_count); - } - - if (node_access != NCAC_ACCESS_PERMIT) { - /* none of the descendants are actually permitted, access denied */ - node_access = NCAC_ACCESS_DENY; - } - } else if (node_access == NCAC_ACCESS_PARTIAL_PERMIT) { - /* partial permit, the node will be included in the reply but we must check children as well */ - if (elem->schema->nodetype & LYD_NODE_INNER) { - ncac_check_data_read_filter_r(&((struct lyd_node_inner *)elem)->child, user, groups, group_count); - } - node_access = NCAC_ACCESS_PERMIT; - } - - /* access denied, free the subtree */ - if (node_access == NCAC_ACCESS_DENY) { - /* never free keys */ - if (!lysc_is_key(elem->schema)) { - if ((elem == *first) && !(*first)->parent) { - *first = (*first)->next; - } - lyd_free_tree(elem); - } - continue; - } - - /* access is permitted, update return access and check the next sibling */ - ret_access = NCAC_ACCESS_PERMIT; - } - - return ret_access; -} - -void -ncac_check_data_read_filter(struct lyd_node **data, const char *user) -{ - char **groups = NULL; - uint32_t group_count; - - assert(data); - - /* NACM LOCK */ - pthread_mutex_lock(&nacm.lock); - - if (ncac_collect_groups(user, &groups, &group_count)) { - goto cleanup; - } - - if (*data && !ncac_allowed_tree((*data)->schema, user)) { - ncac_check_data_read_filter_r(data, user, groups, group_count); - } - -cleanup: - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - ncac_free_groups(groups, group_count); -} - -/** - * @brief Check whether diff node siblings can be applied by a user, recursively with children. - * - * @param[in] diff First diff sibling. - * @param[in] user User for the NACM check. - * @param[in] parent_op Inherited parent operation. - * @param[in] groups Array of collected groups. - * @param[in] group_count Number of @p groups. - * @return NULL if access allowed, otherwise the denied access data node. - */ -static const struct lyd_node * -ncac_check_diff_r(const struct lyd_node *diff, const char *user, const char *parent_op, char **groups, uint32_t group_count) -{ - const char *op; - struct lyd_meta *meta; - const struct lyd_node *node = NULL; - uint8_t oper; - - LY_LIST_FOR(diff, diff) { - /* find operation */ - LY_LIST_FOR(diff->meta, meta) { - if (!strcmp(meta->name, "operation")) { - assert(!strcmp(meta->annotation->module->name, "yang")); - break; - } - } - if (meta) { - op = lyd_get_meta_value(meta); - } else { - op = parent_op; - } - assert(op); - - /* get required access operation */ - switch (op[0]) { - case 'n': - /* "none" */ - oper = 0; - break; - case 'r': - /* "replace" */ - assert(!strcmp(op, "replace")); - oper = NCAC_OP_UPDATE; - break; - case 'c': - /* "create" */ - oper = NCAC_OP_CREATE; - break; - case 'd': - /* "delete" */ - oper = NCAC_OP_DELETE; - break; - default: - EINT; - return NULL; - } - - /* check access for the node, none operation is always allowed, and partial access is relevant only for - * read operation */ - if (oper && !NCAC_ACCESS_IS_NODE_PERMIT(ncac_allowed_node(diff, NULL, NULL, oper, groups, group_count))) { - node = diff; - break; - } - - /* go recursively */ - if (lyd_child(diff)) { - node = ncac_check_diff_r(lyd_child(diff), user, op, groups, group_count); - } - } - - return node; -} - -const struct lyd_node * -ncac_check_diff(const struct lyd_node *diff, const char *user) -{ - const struct lyd_node *node = NULL; - char **groups = NULL; - uint32_t group_count; - - /* NACM LOCK */ - pthread_mutex_lock(&nacm.lock); - - if (ncac_collect_groups(user, &groups, &group_count)) { - goto cleanup; - } - - /* any node can be used in this case */ - if (!ncac_allowed_tree(diff->schema, user)) { - node = ncac_check_diff_r(diff, user, NULL, groups, group_count); - if (node) { - ++nacm.denied_data_writes; - } - } - -cleanup: - /* NACM UNLOCK */ - pthread_mutex_unlock(&nacm.lock); - ncac_free_groups(groups, group_count); - return node; -} - -void -ncac_check_yang_push_update_notif(const char *user, struct ly_set *set, int *all_removed) -{ - struct lyd_node_any *ly_value; - struct lyd_node *ly_target, *next, *iter; - uint32_t i, group_count, removed = 0; - char **groups; - - if (ncac_collect_groups(user, &groups, &group_count)) { - return; - } - - for (i = 0; i < set->count; ++i) { - /* check the change itself */ - lyd_find_path(set->dnodes[i], "target", 0, &ly_target); - if (!NCAC_ACCESS_IS_NODE_PERMIT(ncac_allowed_node(NULL, lyd_get_value(ly_target), ly_target->priv, - NCAC_OP_READ, groups, group_count))) { - /* not allowed, remove this change */ - lyd_free_tree(set->dnodes[i]); - ++removed; - continue; - } - - if (!lyd_find_path(set->dnodes[i], "value", 0, (struct lyd_node **)&ly_value)) { - assert(ly_value->value_type == LYD_ANYDATA_DATATREE); - - /* filter out any nested nodes */ - LY_LIST_FOR_SAFE(lyd_child(ly_value->value.tree), next, iter) { - ncac_check_data_read_filter(&iter, user); - } - } - } - ncac_free_groups(groups, group_count); - if (removed == set->count) { - *all_removed = 1; - } else { - *all_removed = 0; - } -} diff --git a/src/netconf_acm.h b/src/netconf_acm.h deleted file mode 100644 index 7386874be..000000000 --- a/src/netconf_acm.h +++ /dev/null @@ -1,172 +0,0 @@ -/** - * @file netconf_acm.h - * @author Michal Vasko - * @brief NACM and ietf-netconf-acm callbacks header - * - * @copyright - * Copyright (c) 2019 - 2021 Deutsche Telekom AG. - * Copyright (c) 2017 - 2021 CESNET, z.s.p.o. - * - * This source code is licensed under BSD 3-Clause License (the "License"). - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - */ - -#ifndef NP2SRV_NETCONF_ACM_H_ -#define NP2SRV_NETCONF_ACM_H_ - -#include -#include - -#include -#include - -#define NCAC_OP_CREATE 0x01 /**< NACM operation create */ -#define NCAC_OP_READ 0x02 /**< NACM operation read */ -#define NCAC_OP_UPDATE 0x04 /**< NACM operation update */ -#define NCAC_OP_DELETE 0x08 /**< NACM operation delete */ -#define NCAC_OP_EXEC 0x10 /**< NACM operation exec */ -#define NCAC_OP_ALL 0x1F /**< All NACM operations */ - -/** - * @brief Rule target node type. - */ -typedef enum ncac_target_type { - NCAC_TARGET_RPC, /**< Rule target is an RPC. */ - NCAC_TARGET_NOTIF, /**< Rule target is a notification. */ - NCAC_TARGET_DATA, /**< Rule target is a data node, action, or a nested notification. */ - NCAC_TARGET_ANY /**< Rule target is any node. */ -} NCAC_TARGET_TYPE; - -/** - * @brief Main NACM container structure. - */ -struct ncac { - char enabled; /**< Whether NACM is enabled. */ - char default_read_deny; /**< Whether default NACM read action is "deny" (otherwise "permit"). */ - char default_write_deny; /**< Whether default NACM write action is "deny" (otherwise "permit"). */ - char default_exec_deny; /**< Whether default NACM exec action is "deny" (otherwise "permit"). */ - char enable_external_groups; /**< Whether external (system) groups are taken into consideration for NACM. */ - - uint32_t denied_operations; /**< Counter of denied operations (RPC or action). */ - uint32_t denied_data_writes; /**< Counter of denied data writes. */ - uint32_t denied_notifications; /**< Counter of denied notifications. */ - - /** - * @brief NACM group. - */ - struct ncac_group { - char *name; /**< Group name. */ - char **users; /**< Array of users belonging to this group. */ - uint32_t user_count; /**< Number of users. */ - } *groups; /**< Sorted array of existing groups. */ - uint32_t group_count; /**< Number of groups. */ - - /** - * @brief NACM rule list. - */ - struct ncac_rule_list { - char *name; /**< Rule list name. */ - char **groups; /**< Sorted all groups associated with this rule list. */ - uint32_t group_count; /**< Number of groups. */ - - /** - * @brief NACM rule. - */ - struct ncac_rule { - char *name; /**< Rule name. */ - char *module_name; /**< Rule module name. */ - char *target; /**< Rule target. */ - NCAC_TARGET_TYPE target_type; /**< Rule target type. */ - uint8_t operations; /**< Rule operations associated with it. */ - char action_deny; /**< Whether the rule action is "deny" (otherwise "permit"). */ - char *comment; /**< Rule comment. */ - struct ncac_rule *next; /**< Pointer to the next rule. */ - } *rules; /**< List of rules in the rule list. */ - - struct ncac_rule_list *next; /**< Pointer to the next rule list. */ - } *rule_lists; /**< List of all the rule lists. */ - - pthread_mutex_t lock; /**< Lock for accessing all the NACM members. */ -}; - -enum ncac_access { - NCAC_ACCESS_DENY = 1, /**< access to the node is denied */ - NCAC_ACCESS_PARTIAL_DENY = 2, /**< access to the node is denied but it is a prefix of a matching rule */ - NCAC_ACCESS_PARTIAL_PERMIT = 3, /**< access to the node is permitted but any children must still be checked */ - NCAC_ACCESS_PERMIT = 4 /**< access to the node is permitted with any children */ -}; - -#define NCAC_ACCESS_IS_NODE_PERMIT(x) ((x) > 2) - -int ncac_nacm_params_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *xpath, - sr_event_t event, uint32_t request_id, void *private_data); - -int ncac_oper_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *path, - const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data); - -int ncac_group_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *xpath, - sr_event_t event, uint32_t request_id, void *private_data); - -int ncac_rule_list_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *xpath, - sr_event_t event, uint32_t request_id, void *private_data); - -int ncac_rule_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *xpath, sr_event_t event, - uint32_t request_id, void *private_data); - -void ncac_init(void); -void ncac_destroy(void); - -/** - * @brief Check whether an operation is allowed for a user. - * - * According to https://tools.ietf.org/html/rfc8341#section-3.1.3 - * RPC must have X access (except close-session), action additional R access on parent nodes. - * Notification must have R access on itself and any parent nodes. - * Recovery session is allowed by default. - * - * @param[in] data Top-level node of the operation. - * @param[in] user User for the NACM check. - * @return NULL if access allowed, otherwise the denied access data node. - */ -const struct lyd_node *ncac_check_operation(const struct lyd_node *data, const char *user); - -/** - * @brief Filter out any data for which the user does not have R access. - * - * According to https://tools.ietf.org/html/rfc8341#section-3.2.4 - * Recovery session is allowed all nodes by default. - * - * @param[in,out] data Data to filter. - * @param[in] user User for the NACM filtering. - */ -void ncac_check_data_read_filter(struct lyd_node **data, const char *user); - -/** - * @brief Check whether a diff (simplified edit-config tree) can be - * applied by a user. - * - * According to https://tools.ietf.org/html/rfc8341#section-3.2.5 - * Check C access for created nodes, D access for deleted nodes, - * and U access for changed nodes. - * Recovery session is allowed by default. - * - * @param[in] diff Diff tree to check. - * @param[in] user User for the NACM check. - * @return NULL if access allowed, otherwise the denied access data node. - */ -const struct lyd_node *ncac_check_diff(const struct lyd_node *diff, const char *user); - -/** - * @brief Filter out any data in the notification the user does not have R access to - * - * @param[in] user Name of the user to check. - * @param[in] set Set of the notification data. - * @param[out] all_removed Whether or not all nodes have been removed. - * @return NULL if access allowed, otherwise the denied access data node. - */ -void ncac_check_yang_push_update_notif(const char *user, struct ly_set *set, int *all_removed); - -#endif /* NP2SRV_NETCONF_ACM_H_ */ diff --git a/src/netconf_nmda.c b/src/netconf_nmda.c index 3c1088527..5464d40dd 100644 --- a/src/netconf_nmda.c +++ b/src/netconf_nmda.c @@ -31,7 +31,6 @@ #include "compat.h" #include "config.h" #include "log.h" -#include "netconf_acm.h" /** * @brief Perform origin filtering. @@ -109,7 +108,6 @@ np2srv_rpc_getdata_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const sr_datastore_t ds; NC_WD_MODE nc_wd; sr_get_oper_options_t get_opts = 0; - const char *username; if (NP_IGNORE_RPC(session, event)) { /* ignore in this case */ @@ -220,18 +218,12 @@ np2srv_rpc_getdata_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const } ly_set_free(nodeset, NULL); - /* perform correct NACM filtering */ - sr_session_get_orig_data(session, 1, NULL, (const void **)&username); - ncac_check_data_read_filter(&data, username); - /* add output */ if (lyd_new_any(output, NULL, "data", data, 1, LYD_ANYDATA_DATATREE, 1, NULL)) { goto cleanup; } data = NULL; - /* success */ - cleanup: op_filter_erase(&filter); lyd_free_siblings(select_data); diff --git a/src/netconf_subscribed_notifications.c b/src/netconf_subscribed_notifications.c index 1c2ae80d0..c38a974cd 100644 --- a/src/netconf_subscribed_notifications.c +++ b/src/netconf_subscribed_notifications.c @@ -34,7 +34,6 @@ #include "compat.h" #include "err_netconf.h" #include "log.h" -#include "netconf_acm.h" #include "netconf_monitoring.h" #include "subscribed_notifications.h" #include "yang_push.h" @@ -171,15 +170,6 @@ sub_ntf_send_notif(struct nc_session *ncs, uint32_t nc_sub_id, struct timespec t goto cleanup; } - /* check NACM of the notification itself */ - if (ncac_check_operation(*ly_ntf, nc_session_get_username(ncs))) { - /* denied */ - ATOMIC_INC_RELAXED(sub->denied_count); - - /* success */ - goto cleanup; - } - /* create the notification object */ ly_time_ts2str(×tamp, &datetime); if (use_ntf) { @@ -229,20 +219,6 @@ sub_ntf_cb_lock_clear(uint32_t sub_id) ATOMIC_STORE_RELAXED(info.sub_id_lock, 0); } -void -sub_ntf_inc_denied(uint32_t nc_sub_id) -{ - struct np2srv_sub_ntf *sub; - - sub = sub_ntf_find(nc_sub_id, 0, 0, 0); - if (!sub) { - EINT; - return; - } - - ATOMIC_INC_RELAXED(sub->denied_count); -} - /** * @brief Add a prepared and valid subscription into internal subscriptions. * @@ -1015,9 +991,6 @@ np2srv_oper_sub_ntf_subscriptions_cb(sr_session_ctx_t *session, uint32_t UNUSED( break; } - /* add denied */ - excluded_count += ATOMIC_LOAD_RELAXED(sub->denied_count); - sprintf(buf, "%" PRIu32, excluded_count); if (lyd_new_term(receiver, NULL, "excluded-event-records", buf, 0, NULL)) { rc = SR_ERR_LY; diff --git a/src/netconf_subscribed_notifications.h b/src/netconf_subscribed_notifications.h index 078187260..a9bbd72b5 100644 --- a/src/netconf_subscribed_notifications.h +++ b/src/netconf_subscribed_notifications.h @@ -48,7 +48,6 @@ struct np2srv_sub_ntf_info { int terminating; /* set flag means the WRITE lock for this subscription will not be granted */ ATOMIC_T sent_count; /* sent notifications counter */ - ATOMIC_T denied_count; /* counter of notifications denied by NACM */ enum sub_ntf_type type; void *data; @@ -119,13 +118,6 @@ void sub_ntf_cb_lock_pass(uint32_t sub_id); */ void sub_ntf_cb_lock_clear(uint32_t sub_id); -/** - * @brief Increase denied notification count for a subscription. - * - * @param[in] nc_sub_id NETCONF sub ID of the subscription. - */ -void sub_ntf_inc_denied(uint32_t nc_sub_id); - /** * @brief Correctly terminate a ntf-sub subscription. * ntf-sub lock is expected to be held. diff --git a/src/subscribed_notifications.c b/src/subscribed_notifications.c index fb87b2370..ecaf4276b 100644 --- a/src/subscribed_notifications.c +++ b/src/subscribed_notifications.c @@ -33,7 +33,6 @@ #include "compat.h" #include "err_netconf.h" #include "log.h" -#include "netconf_acm.h" #include "netconf_monitoring.h" #include "netconf_subscribed_notifications.h" diff --git a/src/yang_push.c b/src/yang_push.c index 827c2ba6c..70d094e07 100644 --- a/src/yang_push.c +++ b/src/yang_push.c @@ -31,7 +31,6 @@ #include "common.h" #include "compat.h" #include "log.h" -#include "netconf_acm.h" #include "netconf_subscribed_notifications.h" /** @@ -258,9 +257,6 @@ yang_push_notif_change_edit_append(struct lyd_node *ly_yp, enum yang_push_op yp_ goto cleanup; } - /* remember the node schema */ - ly_target->priv = (void *)node->schema; - if ((yp_op == YP_OP_INSERT) || (yp_op == YP_OP_MOVE)) { /* point */ if (node->schema->nodetype == LYS_LEAFLIST) { @@ -323,52 +319,6 @@ yang_push_notif_change_edit_append(struct lyd_node *ly_yp, enum yang_push_op yp_ return rc; } -/** - * @brief Send an on-change push-change-update yang-push notification. - * - * @param[in] ncs NETCONF session. - * @param[in] yp_data yang-push data with the notification. - * @param[in] nc_sub_id NC sub ID of the subscription. - * @return Sysrepo error value. - */ -static int -yang_push_notif_change_send(struct nc_session *ncs, struct yang_push_data *yp_data, uint32_t nc_sub_id) -{ - struct ly_set *set = NULL; - int all_removed = 0; - int rc = SR_ERR_OK; - - assert(yp_data->change_ntf); - - /* NACM filtering */ - if (lyd_find_xpath(yp_data->change_ntf->tree, "/ietf-yang-push:push-change-update/datastore-changes/yang-patch/edit", - &set)) { - rc = SR_ERR_LY; - goto cleanup; - } - ncac_check_yang_push_update_notif(nc_session_get_username(ncs), set, &all_removed); - - if (all_removed) { - /* no change is actually readable, notification denied */ - sub_ntf_inc_denied(nc_sub_id); - goto cleanup; - } - - /* send the notification */ - rc = sub_ntf_send_notif(ncs, nc_sub_id, np_gettimespec(1), &yp_data->change_ntf->tree, 1); - - if (rc == SR_ERR_OK) { - /* set last_notif timestamp */ - yp_data->last_notif = np_gettimespec(1); - } - -cleanup: - ly_set_free(set, NULL); - sr_release_data(yp_data->change_ntf); - yp_data->change_ntf = NULL; - return rc; -} - /** * @brief Timer callback for dampened on-change yang-push changes. */ @@ -386,7 +336,13 @@ yang_push_damp_timer_cb(union sigval sval) pthread_mutex_lock(&arg->yp_data->notif_lock); /* send the postponed on-change notification */ - yang_push_notif_change_send(arg->ncs, arg->yp_data, arg->nc_sub_id); + if (!sub_ntf_send_notif(arg->ncs, arg->nc_sub_id, np_gettimespec(1), &arg->yp_data->change_ntf->tree, 1)) { + /* set last_notif timestamp */ + arg->yp_data->last_notif = np_gettimespec(1); + } + assert(!arg->yp_data->change_ntf->tree); + sr_release_data(arg->yp_data->change_ntf); + arg->yp_data->change_ntf = NULL; /* NOTIF UNLOCK */ pthread_mutex_unlock(&arg->yp_data->notif_lock); @@ -454,6 +410,7 @@ static int np2srv_change_yang_push_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char *module_name, const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *private_data) { + int rc = SR_ERR_OK; struct yang_push_cb_arg *arg = private_data; char *xp = NULL, buf[26]; sr_change_iter_t *iter = NULL; @@ -475,9 +432,10 @@ np2srv_change_yang_push_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), c } if (r == -1) { EMEM; + rc = SR_ERR_NO_MEMORY; goto cleanup; } - if (sr_get_changes_iter(session, xp, &iter) != SR_ERR_OK) { + if ((rc = sr_get_changes_iter(session, xp, &iter))) { goto cleanup; } @@ -493,11 +451,13 @@ np2srv_change_yang_push_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), c continue; } + assert(!arg->yp_data->change_ntf || arg->yp_data->change_ntf->tree); + /* there is a change */ if (!arg->yp_data->change_ntf) { /* store as SR data with context lock, is unlocked on error */ ly_ctx = sr_acquire_context(np2srv.sr_conn); - if (sr_acquire_data(np2srv.sr_conn, NULL, &arg->yp_data->change_ntf)) { + if ((rc = sr_acquire_data(np2srv.sr_conn, NULL, &arg->yp_data->change_ntf))) { goto cleanup_unlock; } @@ -505,6 +465,7 @@ np2srv_change_yang_push_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), c sprintf(buf, "%" PRIu32, arg->nc_sub_id); if (lyd_new_path(NULL, ly_ctx, "/ietf-yang-push:push-change-update/id", buf, 0, &arg->yp_data->change_ntf->tree)) { + rc = SR_ERR_LY; goto cleanup_unlock; } @@ -512,6 +473,7 @@ np2srv_change_yang_push_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), c patch_id = ATOMIC_INC_RELAXED(arg->yp_data->patch_id); sprintf(buf, "patch-%" PRIu32, patch_id); if (lyd_new_path(arg->yp_data->change_ntf->tree, NULL, "datastore-changes/yang-patch/patch-id", buf, 0, NULL)) { + rc = SR_ERR_LY; goto cleanup_unlock; } @@ -523,7 +485,7 @@ np2srv_change_yang_push_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), c } /* append a new edit */ - if (yang_push_notif_change_edit_append(ly_yp, yp_op, node, prev_value, prev_list, arg->yp_data)) { + if ((rc = yang_push_notif_change_edit_append(ly_yp, yp_op, node, prev_value, prev_list, arg->yp_data))) { goto cleanup_unlock; } } @@ -534,16 +496,29 @@ np2srv_change_yang_push_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), c } /* check whether the notification can be sent now */ - if (yang_push_notif_change_ready(arg->yp_data, &ready)) { + if ((rc = yang_push_notif_change_ready(arg->yp_data, &ready))) { goto cleanup_unlock; } /* send the notification */ - if (ready && yang_push_notif_change_send(arg->ncs, arg->yp_data, arg->nc_sub_id)) { - goto cleanup_unlock; + if (ready) { + if ((rc = sub_ntf_send_notif(arg->ncs, arg->nc_sub_id, np_gettimespec(1), &arg->yp_data->change_ntf->tree, 1))) { + goto cleanup_unlock; + } + assert(!arg->yp_data->change_ntf->tree); + sr_release_data(arg->yp_data->change_ntf); + arg->yp_data->change_ntf = NULL; + + /* set last_notif timestamp */ + arg->yp_data->last_notif = np_gettimespec(1); } cleanup_unlock: + if (rc && arg->yp_data->change_ntf) { + sr_release_data(arg->yp_data->change_ntf); + arg->yp_data->change_ntf = NULL; + } + /* NOTIF UNLOCK */ pthread_mutex_unlock(&arg->yp_data->notif_lock); @@ -552,7 +527,7 @@ np2srv_change_yang_push_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), c sr_free_change_iter(iter); /* return value is ignored anyway */ - return SR_ERR_OK; + return rc; } /** @@ -895,11 +870,6 @@ yang_push_notif_update_send(struct nc_session *ncs, struct yang_push_data *yp_da goto cleanup; } - /* NACM filter */ - if (data) { - ncac_check_data_read_filter(&data->tree, nc_session_get_username(ncs)); - } - /* context lock is already held by data */ ly_ctx = sr_acquire_context(np2srv.sr_conn); sr_release_context(np2srv.sr_conn); diff --git a/tests/np_test.c b/tests/np_test.c index 05faf943d..c8ae14795 100644 --- a/tests/np_test.c +++ b/tests/np_test.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include +#include #include "np_test_config.h" @@ -365,32 +367,27 @@ np_glob_teardown(void **state, const char *modules[], uint32_t mod_count) return ret; } -int -get_username(char **name) +const char * +np_get_user(void) { - FILE *file; + struct passwd *pw; - *name = NULL; - size_t size = 0; + pw = getpwuid(geteuid()); - /* Get user name */ - file = popen("whoami", "r"); - if (!file) { - return 1; - } - if (getline(name, &size, file) == -1) { - return 1; - } - (*name)[strlen(*name) - 1] = '\0'; /* Remove the newline */ - pclose(file); - return 0; + return pw ? pw->pw_name : NULL; +} + +int +np_is_nacm_recovery(void) +{ + return !strcmp(sr_nacm_get_recovery_user(), np_get_user()); } int setup_nacm(void **state) { struct np_test *st = *state; - char *user, *data; + char *data; const char *template = "\n" " false\n" @@ -403,15 +400,10 @@ setup_nacm(void **state) " \n" "\n"; - if (get_username(&user)) { - return 1; - } - /* Put user and message id into error template */ - if (asprintf(&data, template, user) == -1) { + if (asprintf(&data, template, np_get_user()) == -1) { return 1; } - free(user); /* Parse and merge the config */ if (lyd_parse_data_mem(st->ctx, data, LYD_XML, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, &st->node)) { @@ -432,18 +424,3 @@ setup_nacm(void **state) return 0; } - -int -is_nacm_rec_uid() -{ - uid_t uid; - char streuid[10]; - - /* Get UID */ - uid = geteuid(); - sprintf(streuid, "%d", (int) uid); - if (!strcmp(streuid, NACM_RECOVERY_UID)) { - return 1; - } - return 0; -} diff --git a/tests/np_test.h b/tests/np_test.h index 76969c27b..d36d483df 100644 --- a/tests/np_test.h +++ b/tests/np_test.h @@ -253,10 +253,10 @@ int np_glob_teardown(void **state, const char *modules[], uint32_t mod_count); void parse_arg(int argc, char **argv); -int get_username(char **name); +const char *np_get_user(void); -int setup_nacm(void **state); +int np_is_nacm_recovery(void); -int is_nacm_rec_uid(); +int setup_nacm(void **state); #endif /* _NP_TEST_H_ */ diff --git a/tests/test_candidate.c b/tests/test_candidate.c index 09c7c7f92..d23089069 100644 --- a/tests/test_candidate.c +++ b/tests/test_candidate.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "np_test.h" #include "np_test_config.h" @@ -530,8 +531,8 @@ main(int argc, char **argv) teardown_discard_changes_advanced), }; - if (is_nacm_rec_uid()) { - puts("Running as NACM_RECOVERY_UID. Tests will not run correctly as this user bypases NACM. Skipping."); + if (np_is_nacm_recovery()) { + puts("Running as NACM_RECOVERY_USER. Tests will not run correctly as this user bypases NACM. Skipping."); return 0; } diff --git a/tests/test_confirmed_commit.c b/tests/test_confirmed_commit.c index 9d201fc9c..a694a0eee 100644 --- a/tests/test_confirmed_commit.c +++ b/tests/test_confirmed_commit.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "np_test.h" #include "np_test_config.h" @@ -577,8 +578,8 @@ teardown_test_failed_file(void **state) int main(int argc, char **argv) { - if (is_nacm_rec_uid()) { - puts("Running as NACM_RECOVERY_UID. Tests will not run correctly as this user bypases NACM. Skipping."); + if (np_is_nacm_recovery()) { + puts("Running as NACM_RECOVERY_USER. Tests will not run correctly as this user bypases NACM. Skipping."); return 0; } diff --git a/tests/test_edit.c b/tests/test_edit.c index 8807f67ef..e9580a533 100644 --- a/tests/test_edit.c +++ b/tests/test_edit.c @@ -26,6 +26,8 @@ #include #include #include +#include + #include "np_test.h" #include "np_test_config.h" @@ -717,8 +719,8 @@ test_autodel_case(void **state) int main(int argc, char **argv) { - if (is_nacm_rec_uid()) { - puts("Running as NACM_RECOVERY_UID. Tests will not run correctly as this user bypases NACM. Skipping."); + if (np_is_nacm_recovery()) { + puts("Running as NACM_RECOVERY_USER. Tests will not run correctly as this user bypases NACM. Skipping."); return 0; } diff --git a/tests/test_nacm.c b/tests/test_nacm.c index 7a9372798..21bf4eb3b 100644 --- a/tests/test_nacm.c +++ b/tests/test_nacm.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "np_test.h" #include "np_test_config.h" @@ -1079,8 +1080,8 @@ test_kill_session(void **state) int main(int argc, char **argv) { - if (is_nacm_rec_uid()) { - puts("Running as NACM_RECOVERY_UID. Tests will not run correctly as this user bypases NACM. Skipping."); + if (np_is_nacm_recovery()) { + puts("Running as NACM_RECOVERY_USER. Tests will not run correctly as this user bypases NACM. Skipping."); return 0; } diff --git a/tests/test_rpc.c b/tests/test_rpc.c index 5f4bf6b91..ce225f48f 100644 --- a/tests/test_rpc.c +++ b/tests/test_rpc.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "np_test.h" #include "np_test_config.h" @@ -276,7 +277,6 @@ test_get(void **state) struct np_test *st = *state; /* Check if get RPC succeeds */ - /* TODO: get crashes the server on a locked session */ st->rpc = nc_rpc_get(NULL, NC_WD_ALL, NC_PARAMTYPE_CONST); st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); assert_int_equal(NC_MSG_RPC, st->msgtype); @@ -292,28 +292,27 @@ static void test_kill(void **state) { struct np_test *st = *state; - char *username, *error, *expected; + char *error, *expected; const char *template; - if (is_nacm_rec_uid()) { + if (np_is_nacm_recovery()) { puts("Skipping the test."); return; } - /* Try to close a session, should fail due to wrong permissions */ + /* try to close a session, should fail due to wrong permissions */ st->rpc = nc_rpc_kill(nc_session_get_id(st->nc_sess)); st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); assert_int_equal(NC_MSG_RPC, st->msgtype); ASSERT_RPC_ERROR(st); - /* Check the error message */ + /* check the error message */ lyd_print_mem(&error, st->envp, LYD_XML, 0); - get_username(&username); template = "\n" " \n" - " application\n" + " protocol\n" " access-denied\n" " error\n" " /ietf-netconf:kill-session\n" @@ -321,14 +320,14 @@ test_kill(void **state) "because \"%s\" NACM authorization failed.\n" " \n" "\n"; - assert_int_not_equal(-1, asprintf(&expected, template, st->msgid, username)); + assert_int_not_equal(-1, asprintf(&expected, template, st->msgid, np_get_user())); assert_string_equal(error, expected); - free(username); free(error); free(expected); FREE_TEST_VARS(st); - /* Functionality tested in test_nacm.c */ + + /* functionality tested in test_nacm.c */ } static void @@ -336,13 +335,19 @@ test_commit(void **state) { struct np_test *st = *state; - /* Check if commit RPC succeeds */ + /*if (sr_nacm_get_recovery_uid() == geteuid()) { + puts("Skipping the test."); + return; + }*/ + + /* check if commit RPC succeeds */ st->rpc = nc_rpc_commit(0, 0, NULL, NULL, NC_PARAMTYPE_CONST); st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); assert_int_equal(NC_MSG_RPC, st->msgtype); ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); - /* Functionality tested in test_candidate.c */ + + /* functionality tested in test_candidate.c */ } static void @@ -350,13 +355,14 @@ test_discard(void **state) { struct np_test *st = *state; - /* Check if discard RPC succeeds */ + /* check if discard RPC succeeds */ st->rpc = nc_rpc_discard(); st->msgtype = nc_send_rpc(st->nc_sess, st->rpc, 1000, &st->msgid); assert_int_equal(NC_MSG_RPC, st->msgtype); ASSERT_OK_REPLY(st); FREE_TEST_VARS(st); - /* Functionality tested in test_candidate.c */ + + /* functionality tested in test_candidate.c */ } static void @@ -366,7 +372,7 @@ test_getconfig(void **state) const char *expected; char *configuration; - if (is_nacm_rec_uid()) { + if (np_is_nacm_recovery()) { puts("Skipping the test."); return; } diff --git a/tests/test_sub_ntf_advanced.c b/tests/test_sub_ntf_advanced.c index cb50fc6c5..b32b1dbe0 100644 --- a/tests/test_sub_ntf_advanced.c +++ b/tests/test_sub_ntf_advanced.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "np_test.h" #include "np_test_config.h" @@ -800,9 +801,9 @@ test_killsub_fail_nacm(void **state) { struct np_test *st = *state; - /* Check for NACM_RECOVERY_UID */ - if (is_nacm_rec_uid()) { - puts("Running as NACM_RECOVERY_UID. Tests will not run correctly as this user bypases NACM. Skipping."); + /* check for NACM_RECOVERY_USER */ + if (np_is_nacm_recovery()) { + puts("Running as NACM_RECOVERY_USER. Tests will not run correctly as this user bypases NACM. Skipping."); return; } diff --git a/tests/test_url.c b/tests/test_url.c index 5f5dfb6e6..955e44721 100644 --- a/tests/test_url.c +++ b/tests/test_url.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "np_test.h" #include "np_test_config.h" @@ -213,7 +214,7 @@ test_copy_config_into_file(void **state) { struct np_test *st = *state; const char *path, *url, *template; - char *expected, *config, *user; + char *expected, *config; long size; FILE *file; @@ -289,11 +290,9 @@ test_copy_config_into_file(void **state) " \n" "\n"; - assert_int_equal(0, get_username(&user)); - assert_int_not_equal(-1, asprintf(&expected, template, user) == -1); + assert_int_not_equal(-1, asprintf(&expected, template, np_get_user()) == -1); assert_string_equal(config, expected); free(expected); - free(user); free(config); fclose(file); @@ -357,7 +356,7 @@ test_edit_config(void **state) { struct np_test *st = *state; const char *url, *template; - char *expected, *user; + char *expected; url = "file://" NP_TEST_MODULE_DIR "/edit1.xml"; @@ -419,11 +418,9 @@ test_edit_config(void **state) " \n" "\n"; - assert_int_equal(0, get_username(&user)); - assert_int_not_equal(-1, asprintf(&expected, template, user) == -1); + assert_int_not_equal(-1, asprintf(&expected, template, np_get_user()) == -1); assert_string_equal(st->str, expected); free(expected); - free(user); FREE_TEST_VARS(st); } @@ -440,8 +437,8 @@ main(int argc, char **argv) cmocka_unit_test_teardown(test_edit_config, teardown_data), }; - if (is_nacm_rec_uid()) { - puts("Running as NACM_RECOVERY_UID. Tests will not run correctly as this user bypases NACM. Skipping."); + if (np_is_nacm_recovery()) { + puts("Running as NACM_RECOVERY_USER. Tests will not run correctly as this user bypases NACM. Skipping."); return 0; } diff --git a/tests/test_with_defaults.c b/tests/test_with_defaults.c index fbe9c0fdb..d4a15f642 100644 --- a/tests/test_with_defaults.c +++ b/tests/test_with_defaults.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "np_test.h" #include "np_test_config.h" @@ -381,8 +382,8 @@ main(int argc, char **argv) cmocka_unit_test_setup_teardown(test_explicit_all_set_default, setup_data_all_default, teardown_data), }; - if (is_nacm_rec_uid()) { - puts("Running as NACM_RECOVERY_UID. Tests will not run correctly as this user bypases NACM. Skipping."); + if (np_is_nacm_recovery()) { + puts("Running as NACM_RECOVERY_USER. Tests will not run correctly as this user bypases NACM. Skipping."); return 0; } From 440a28e0051abf8a776b35ea26a7d1a18ff38f60 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 4 Mar 2022 15:46:07 +0100 Subject: [PATCH 67/70] build UPDATE increase required sysrepo version --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a05e62f2..d622e4ab6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,8 +56,8 @@ set(LIBNETCONF2_DEP_SOVERSION 3.1.0) set(LIBNETCONF2_DEP_SOVERSION_MAJOR 3) # sysrepo required version -set(SYSREPO_DEP_VERSION 2.1.10) -set(SYSREPO_DEP_SOVERSION 7.0.10) +set(SYSREPO_DEP_VERSION 2.1.36) +set(SYSREPO_DEP_SOVERSION 7.3.0) set(SYSREPO_DEP_SOVERSION_MAJOR 7) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=gnu99") From 84085d76bae5bd87b8aa0a323b64c241dad87e4e Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 4 Mar 2022 15:56:58 +0100 Subject: [PATCH 68/70] netconf sub ntf REFACTOR uninitialized var warning --- src/netconf_subscribed_notifications.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/netconf_subscribed_notifications.c b/src/netconf_subscribed_notifications.c index c38a974cd..31fe27553 100644 --- a/src/netconf_subscribed_notifications.c +++ b/src/netconf_subscribed_notifications.c @@ -232,6 +232,8 @@ sub_ntf_add(const struct np2srv_sub_ntf *sub, struct np2srv_sub_ntf **sub_p) { void *mem; + *sub_p = NULL; + mem = realloc(info.subs, (info.count + 1) * sizeof *info.subs); if (!mem) { return -1; From e862a2941ed90b80eac54c08d1171aea3959d8a3 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Fri, 4 Mar 2022 15:46:25 +0100 Subject: [PATCH 69/70] VERSION bump to version 2.1.16 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d622e4ab6..e0b88fa57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(NP2SRV_VERSION 2.1.15) +set(NP2SRV_VERSION 2.1.16) # libyang required version set(LIBYANG_DEP_VERSION 2.0.128) From 8ca03946a72653f87578f56404b31a4ad897c8f4 Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Tue, 15 Mar 2022 10:41:17 +0100 Subject: [PATCH 70/70] scripts UPDATE remove obsolete param --- scripts/remove.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/remove.sh b/scripts/remove.sh index 381d10b94..c0294d231 100755 --- a/scripts/remove.sh +++ b/scripts/remove.sh @@ -35,7 +35,7 @@ MODULES=( # functions UNINSTALL_MODULE() { - "$SYSREPOCTL" -a -u $1 -v2 + "$SYSREPOCTL" -u $1 -v2 local rc=$? if [ $rc -ne 0 ]; then exit $rc @@ -43,7 +43,7 @@ UNINSTALL_MODULE() { } DISABLE_FEATURE() { - "$SYSREPOCTL" -a -c $1 -d $2 -v2 + "$SYSREPOCTL" -c $1 -d $2 -v2 local rc=$? if [ $rc -ne 0 ]; then exit $rc