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 868b1e35b..33ea391a4 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 @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bc206860..e0b88fa57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,17 +43,24 @@ 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.16) -# 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) -# 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) +# libnetconf2 required version +set(LIBNETCONF2_DEP_VERSION 2.1.3) +set(LIBNETCONF2_DEP_SOVERSION 3.1.0) +set(LIBNETCONF2_DEP_SOVERSION_MAJOR 3) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=gnu99") +# sysrepo required version +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") # # options @@ -68,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") @@ -122,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 @@ -212,7 +217,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} REQUIRED) include_directories(${LIBNETCONF2_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBNETCONF2_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBNETCONF2_LIBRARIES}) @@ -271,6 +276,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) @@ -278,7 +297,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}) @@ -291,8 +310,25 @@ 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) include_directories(${PROJECT_BINARY_DIR}) # set script dir @@ -304,6 +340,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...\") @@ -311,6 +350,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}\") @@ -322,6 +363,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}\") @@ -331,6 +374,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/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/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 187694cf8..c02445029 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) @@ -105,29 +106,20 @@ 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 -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 @@ -166,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/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/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/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..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 @@ -66,7 +68,7 @@ NP2_MODULE_GROUP=netconf %postun {% include 'scripts/remove.sh' %} -groupdel netconf +groupdel netconf &> /dev/null %files %license LICENSE @@ -74,6 +76,7 @@ groupdel netconf %{_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/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."; } - } diff --git a/scripts/remove.sh b/scripts/remove.sh index d229a3534..c0294d231 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" @@ -36,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 @@ -44,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 diff --git a/scripts/setup.sh b/scripts/setup.sh index 68ea05a3a..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" @@ -46,7 +45,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 +60,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 +90,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 +121,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/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/common.c b/src/common.c index f5250c3b9..b77fe2aad 100644 --- a/src/common.c +++ b/src/common.c @@ -38,12 +38,13 @@ #endif #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}; @@ -231,18 +232,30 @@ 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); } } 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 +263,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; @@ -283,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 */ @@ -296,7 +315,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 +333,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 +342,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 +491,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 +523,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 +544,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 +586,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 @@ -797,6 +827,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. * @@ -812,8 +889,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 = 0; + char *buf_new, *val_str, quot; assert(!node->schema || (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))); @@ -823,8 +900,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)); @@ -832,25 +908,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; - if (strchr(lyd_get_value(node), '\'')) { + /* learn which quotes are safe to use */ + if (strchr(val_str, '\'')) { quot = '\"'; } else { quot = '\''; } - sprintf((*buf) + (size - 1), "=%c%s%c]", quot, lyd_get_value(node), quot); + /* 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; } /** @@ -908,7 +1002,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); @@ -964,7 +1058,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; } } @@ -1059,15 +1154,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; @@ -1112,13 +1200,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); @@ -1126,9 +1215,16 @@ 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 */ - 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; } } @@ -1157,7 +1253,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; } diff --git a/src/common.h b/src/common.h index 19420fba6..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. @@ -164,6 +163,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/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/err_netconf.c b/src/err_netconf.c index 56096d8ca..0fa3f10eb 100644 --- a/src/err_netconf.c +++ b/src/err_netconf.c @@ -19,72 +19,22 @@ #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); - - /* error-tag */ - str = "access-denied"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* 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 +43,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 +67,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"); - - /* error-type */ - str = "protocol"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); - - /* error-tag */ - str = "missing-element"; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); + const char *msg; - /* error-message */ - str = "An expected element is missing."; - sr_session_push_error_data(ev_sess, strlen(str) + 1, str); + /* message */ + msg = "An expected element is missing."; - /* 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/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 19312873c..69d094e33 100644 --- a/src/main.c +++ b/src/main.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include "common.h" #include "compat.h" @@ -47,19 +49,19 @@ #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" #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; -/* NETCONF SID of session to skip diff check for */ -ATOMIC_T skip_nacm_nc_sid; - static void *worker_thread(void *arg); /** @@ -103,6 +105,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 +122,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 +166,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 +175,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 +192,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 +279,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 +299,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 +312,7 @@ np2srv_err_reply_sr(const sr_error_info_t *err_info) } e = NULL; } + sr_release_context(np2srv.sr_conn); return reply; } @@ -318,33 +328,19 @@ 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 *output, *child = 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); + } 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 */ @@ -362,7 +358,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,53 +390,16 @@ 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; } -/** - * @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. * @@ -468,7 +427,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"; @@ -482,6 +441,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"; @@ -512,6 +472,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 +496,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 */ @@ -545,14 +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)); - } - - 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); @@ -571,11 +523,8 @@ server_init(void) /* init monitoring */ ncm_init(); - /* 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; } @@ -642,11 +591,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); @@ -658,10 +602,16 @@ 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); } + /* 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(); @@ -674,7 +624,7 @@ server_destroy(void) ncm_destroy(); /* NACM cleanup */ - ncac_destroy(); + sr_nacm_destroy(); /* confirmed commit cleanup */ ncc_commit_ctx_destroy(); @@ -682,7 +632,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 +659,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 +720,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 +728,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; \ @@ -924,34 +874,48 @@ 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); + if (sr_nacm_init(np2srv.sr_sess, 0, &np2srv.sr_data_sub)) { + goto error; + } - xpath = "/ietf-netconf-acm:nacm/rule-list"; - SR_CONFIG_SUBSCR(mod_name, xpath, ncac_rule_list_cb); + return 0; - xpath = "/ietf-netconf-acm:nacm/rule-list/rule"; - SR_CONFIG_SUBSCR(mod_name, xpath, ncac_rule_cb); +error: + ERR("Server data subscribe failed."); + return -1; +} - /* state data */ - xpath = "/ietf-netconf-acm:nacm/denied-operations"; - SR_OPER_SUBSCR(mod_name, xpath, ncac_oper_cb); +/** + * @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; - xpath = "/ietf-netconf-acm:nacm/denied-data-writes"; - SR_OPER_SUBSCR(mod_name, xpath, ncac_oper_cb); + if (!nc_server_endpt_count()) { + /* no listening endpoints */ + return; + } - xpath = "/ietf-netconf-acm:nacm/denied-notifications"; - SR_OPER_SUBSCR(mod_name, xpath, ncac_oper_cb); + ly_ctx = sr_acquire_context(np2srv.sr_conn); + if (!ly_ctx) { + ERR("Failed to acquire SR connection context."); + return; + } - return 0; + /* 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; + } -error: - ERR("Server data subscribe failed."); - return -1; + /* no new session or callback fail, free the session, release context */ + nc_session_free(ncs, NULL); + sr_release_context(np2srv.sr_conn); } /** @@ -973,15 +937,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 +964,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 +976,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); } @@ -1317,6 +1277,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); @@ -1330,6 +1295,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); diff --git a/src/netconf.c b/src/netconf.c index 2c5235807..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 */ @@ -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")) { @@ -298,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); @@ -345,14 +338,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 +352,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)); } @@ -442,10 +432,10 @@ 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; - uint32_t *nc_sid; + struct nc_session *nc_sess; #ifdef NP2SRV_URL_CAPAB const char *trg_url = NULL; @@ -537,21 +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, &config); - if (rc != SR_ERR_OK) { + if ((rc = sr_get_data(session, "/*", 0, np2srv.sr_timeout, 0, &sr_data))) { goto cleanup; } - - sr_session_get_orig_data(session, 1, NULL, (const void **)&username); - ncac_check_data_read_filter(&config, username); + config = sr_data->tree; + sr_data->tree = NULL; + sr_release_data(sr_data); } /* 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; } @@ -564,9 +552,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")) { @@ -589,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); @@ -726,7 +720,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 +931,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 +946,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 +962,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)) { @@ -978,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); @@ -1006,6 +998,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 @@ -1049,8 +1042,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")) { @@ -1089,14 +1081,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); } @@ -1152,8 +1142,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 +1153,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 +1183,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 +1212,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 +1244,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 deleted file mode 100644 index 0fd249392..000000000 --- a/src/netconf_acm.c +++ /dev/null @@ -1,1727 +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; -} - -/* /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; - struct ly_ctx *ly_ctx; - uint32_t i, j; - 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; - } - 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; - - lydict_insert(ly_ctx, group_name, 0, &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); - - /* 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->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_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; - } - } - - 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; - lydict_insert(ly_ctx, user_name, 0, (const char **)&group->users[group->user_count]); - ++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) { - break; - } - } - assert(i < group->user_count); - - /* delete it */ - lydict_remove(ly_ctx, 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; - 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); - } - 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 **)(((uint64_t)(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. The item structure first member must be (const char *) in the dictionary. - * - * @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 = ((uint64_t)(uintptr_t)m - (uint64_t)(uintptr_t)items) / item_size; - } - - return idx; -} - -/** - * @brief Add an item into a sorted array. The item structure first member must be (const char *) in the dictionary. - * - * @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_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 struct ly_ctx *ly_ctx, 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 */ - lydict_insert(ly_ctx, *item, 0, (const char **)ITEM_IDX_PTR(*items, item_size, i)); - ++(*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. - * - * @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) -{ - 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 */ - lydict_remove(ly_ctx, *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; - 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; - } - 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 && (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; - } - lydict_insert(ly_ctx, rlist_name, 0, &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 && (rlist->name != rlist_name); rlist = rlist->next) { - prev_rlist = rlist; - } - assert(rlist); - - /* delete it */ - lydict_remove(ly_ctx, rlist->name); - for (i = 0; i < rlist->group_count; ++i) { - lydict_remove(ly_ctx, 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 && (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(ly_ctx, &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(ly_ctx, &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; - 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; - } - 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 && (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 && (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; - } - lydict_insert(ly_ctx, rule_name, 0, &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 && (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); - 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 && (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 && (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); - lydict_remove(ly_ctx, rule->module_name); - if (!strcmp(str, "*")) { - rule->module_name = NULL; - } else { - lydict_insert(ly_ctx, str, 0, &rule->module_name); - } - } 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); - rule->target = NULL; - rule->target_type = NCAC_TARGET_ANY; - } else { - str = lyd_get_value(node); - lydict_remove(ly_ctx, rule->target); - if (!strcmp(str, "*")) { - rule->target = NULL; - } else { - lydict_insert(ly_ctx, str, 0, &rule->target); - } - 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) { - lydict_remove(ly_ctx, 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); - } - } - } - } - - /* 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; - 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); - for (j = 0; j < group->user_count; ++j) { - lydict_remove(ly_ctx, 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); - for (i = 0; i < rule_list->group_count; ++i) { - lydict_remove(ly_ctx, 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] 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) -{ - 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)) { - 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(ly_ctx, (const char **)&grp.gr_name, sizeof **groups, 1, groups, group_count)) { - goto cleanup; - } - } - } - - /* success */ - rc = 0; - -cleanup: - free(gids); - free(buf); - lydict_remove(ly_ctx, user_dict); - 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) { - lydict_remove(sr_get_context(np2srv.sr_conn), 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 && (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 && (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 && (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(sr_get_context(np2srv.sr_conn), 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(sr_get_context(np2srv.sr_conn), 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(sr_get_context(np2srv.sr_conn), 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(sr_get_context(np2srv.sr_conn), 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 afff4b73c..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 { - const 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 { - const 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 { - const char *name; /**< Rule name. */ - const char *module_name; /**< Rule module name. */ - const 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. */ - 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_confirmed_commit.c b/src/netconf_confirmed_commit.c index 089c0fc2b..54781fea6 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,15 @@ 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, 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; } cleanup: - lyd_free_tree(node); + sr_release_data(data); + free(ncc_path); free(path); free(xpath); return rc; @@ -649,24 +650,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 +676,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, module->name, SR_DS_RUNNING, &read, &write))) { ERR("Failed getting permissions of module \"%s\".", module->name); goto cleanup; } @@ -692,6 +692,7 @@ set_running_backup(void) } cleanup: + sr_release_context(np2srv.sr_conn); sr_session_stop(session); return rc; } @@ -760,14 +761,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 +835,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,15 +900,24 @@ 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); } + /* 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/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_nmda.c b/src/netconf_nmda.c index 4541570d9..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 */ @@ -162,8 +160,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 +169,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")) { @@ -224,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_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..31fe27553 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,56 +219,30 @@ 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 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; + + *sub_p = NULL; 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 +311,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; @@ -365,14 +329,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; @@ -396,26 +359,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; } @@ -450,11 +430,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 +484,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; } @@ -527,8 +510,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); } @@ -584,6 +566,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 +624,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 +834,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 +865,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 +897,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 +917,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; @@ -1016,9 +993,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 e5047ff81..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" @@ -85,6 +84,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 +95,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 +160,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 +206,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 +232,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); @@ -246,15 +251,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: - if (suspended) { - /* resume subscription thread */ - sr_subscription_thread_resume(np2srv.sr_notif_sub); - } + sr_session_release_context(user_sess); ly_set_erase(&mod_set, NULL); return rc; } @@ -276,7 +282,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 +348,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,13 +381,13 @@ 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; } 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; @@ -410,8 +417,7 @@ sub_ntf_rpc_establish_sub(sr_session_ctx_t *ev_sess, const struct lyd_node *rpc, 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); } @@ -478,6 +484,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) @@ -498,8 +513,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; @@ -524,7 +538,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 +651,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/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 54afb543b..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" /** @@ -166,6 +165,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 +219,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 +231,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; - goto cleanup; - } - if (lyd_find_xpath(ly_yp, xpath, &set)) { - rc = SR_ERR_LY; + if ((rc = yang_push_notif_change_edit_clear_target(ly_yp, path))) { 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); @@ -230,15 +257,13 @@ 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) { 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,55 +311,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); - 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->ly_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)) { - 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->ly_change_ntf, 1); - - if (rc == SR_ERR_OK) { - /* set last_notif timestamp */ - yp_data->last_notif = np_gettimespec(1); + if (rc) { + ERR("Failed to store data edit for an on-change notification."); } - -cleanup: - ly_set_free(set, NULL); - lyd_free_tree(yp_data->ly_change_ntf); - yp_data->ly_change_ntf = NULL; return rc; } @@ -355,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); @@ -423,11 +410,13 @@ 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; 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; @@ -443,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; } @@ -461,19 +451,29 @@ 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->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 ((rc = 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)) { + rc = SR_ERR_LY; 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)) { + rc = SR_ERR_LY; goto cleanup_unlock; } @@ -481,31 +481,44 @@ 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 */ - 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; } } - if (!arg->yp_data->ly_change_ntf) { + if (!arg->yp_data->change_ntf) { /* there are actually no changes */ goto cleanup_unlock; } /* 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); @@ -514,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; } /** @@ -547,7 +560,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); @@ -608,8 +621,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; } @@ -639,12 +652,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; @@ -655,7 +670,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; } @@ -684,10 +699,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 +733,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 +801,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 +834,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 +851,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; @@ -850,22 +870,25 @@ yang_push_notif_update_send(struct nc_session *ncs, struct yang_push_data *yp_da goto cleanup; } - /* NACM filter */ - ncac_check_data_read_filter(&data, 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; @@ -953,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; @@ -963,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 */ @@ -1053,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 { @@ -1084,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) { @@ -1093,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) { @@ -1129,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; @@ -1138,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); @@ -1189,8 +1238,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); @@ -1240,8 +1288,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); @@ -1685,7 +1732,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..9cfdc8927 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; @@ -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); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 89f9067b9..009cf18e5 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 @@ -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 @@ -63,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() @@ -74,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() @@ -81,11 +87,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 ) diff --git a/tests/modules/edit1.yang b/tests/modules/edit1.yang index 57beb0a79..c41b2c2d4 100644 --- a/tests/modules/edit1.yang +++ b/tests/modules/edit1.yang @@ -2,7 +2,21 @@ module edit1 { namespace ed1; prefix ed1; + leaf stateLeaf { + type int32; + config false; + } + leaf first { type string; } + + container cont { + leaf second { + type empty; + } + leaf third { + type uint32; + } + } } 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; + } +} 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/np_test.c b/tests/np_test.c index e3253d466..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" @@ -129,12 +131,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; @@ -217,7 +220,7 @@ np_glob_setup_np2(void **state, const char *test_name) /* 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: @@ -253,6 +256,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 +295,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)); @@ -324,32 +367,27 @@ np_glob_teardown(void **state) 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" @@ -362,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)) { @@ -391,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 fd27a7383..d36d483df 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); \ @@ -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); \ @@ -100,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); \ @@ -214,16 +247,16 @@ 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); -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 2f4c84138..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" @@ -33,63 +34,47 @@ 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); + + /* 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"}; 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 @@ -546,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 c05cb2152..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" @@ -34,67 +35,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 @@ -114,7 +107,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); @@ -318,21 +312,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 a confirmed-commit rpc with 10m timeout */ + /* 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); + + /* 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" @@ -343,17 +351,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 @@ -558,6 +578,11 @@ teardown_test_failed_file(void **state) int main(int argc, char **argv) { + if (np_is_nacm_recovery()) { + puts("Running as NACM_RECOVERY_USER. 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 b98b7cbbb..e9580a533 100644 --- a/tests/test_edit.c +++ b/tests/test_edit.c @@ -26,78 +26,51 @@ #include #include #include +#include + #include "np_test.h" #include "np_test_config.h" 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 @@ -746,6 +719,11 @@ test_autodel_case(void **state) int main(int argc, char **argv) { + if (np_is_nacm_recovery()) { + puts("Running as NACM_RECOVERY_USER. 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_filter.c b/tests/test_filter.c index 835cf9a76..50bdbab6b 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 { @@ -192,78 +194,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-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; /* 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-imp", "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 @@ -548,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" @@ -641,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 @@ -861,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) { @@ -877,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); diff --git a/tests/test_nacm.c b/tests/test_nacm.c index 9c776fdc7..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" @@ -35,69 +36,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 @@ -707,9 +689,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" @@ -1099,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_parallel_sessions.c b/tests/test_parallel_sessions.c index b0e975bad..3dca75824 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 { @@ -58,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 */ @@ -76,8 +87,7 @@ recv_reply_error_print(struct np_test *st, const struct lyd_node *op, const stru } printf("\n"); - /* 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); @@ -85,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 * @@ -157,5 +182,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..ce225f48f 100644 --- a/tests/test_rpc.c +++ b/tests/test_rpc.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include "np_test.h" #include "np_test_config.h" @@ -32,57 +34,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 @@ -138,7 +122,7 @@ test_lock_fail(void **state) template = "\n" + "message-id=\"%" PRIu64 "\">\n" " \n" " protocol\n" " lock-denied\n" @@ -146,7 +130,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"; @@ -269,7 +253,7 @@ test_unlock_fail(void **state) template = "\n" + "message-id=\"%" PRIu64 "\">\n" " \n" " protocol\n" " lock-denied\n" @@ -277,7 +261,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"; @@ -293,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); @@ -309,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" + "message-id=\"%" PRIu64 "\">\n" " \n" - " application\n" + " protocol\n" " access-denied\n" " error\n" " /ietf-netconf:kill-session\n" @@ -338,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 @@ -353,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 @@ -367,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 @@ -383,6 +372,11 @@ test_getconfig(void **state) const char *expected; char *configuration; + if (np_is_nacm_recovery()) { + 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); 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..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" @@ -35,40 +36,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 +98,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 +136,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 +161,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 +184,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 +216,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 +267,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 +283,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 +320,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 +336,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 +359,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 +381,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 +461,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 +509,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 +517,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 +644,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 +697,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 +709,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 +731,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 +745,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 */ @@ -815,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; } @@ -863,7 +849,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 +872,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 +901,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 +926,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 +965,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..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); } @@ -78,60 +75,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 +124,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 +150,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 +207,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 +227,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 +259,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 +290,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 +342,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 +362,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 +388,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 +413,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 +440,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 +466,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..227a2acf0 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 @@ -215,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 */ @@ -229,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"); @@ -312,7 +293,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 +334,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 +346,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 +394,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 +429,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 */ @@ -464,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)); @@ -472,49 +453,46 @@ 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 */ - 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"); + /* 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" "\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 */ + /* receive the notification and test the contents */ RECV_NOTIF(st); assert_string_equal(data, st->str); FREE_TEST_VARS(st); @@ -570,7 +548,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 +570,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 +596,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..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" @@ -67,59 +68,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 @@ -231,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; @@ -307,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); @@ -375,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"; @@ -437,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); } @@ -458,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 1bcb28edc..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" @@ -32,57 +33,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 @@ -403,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; } diff --git a/tests/test_yang_push.c b/tests/test_yang_push.c index f9da5529a..453f411e1 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 @@ -145,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" @@ -161,16 +138,23 @@ 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 */ + /* 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" @@ -178,11 +162,11 @@ test_periodic_basic(void **state) " TestFirst\n" " \n" "\n"; + free(ntf); 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); 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