pax_global_header00006660000000000000000000000064141427411460014516gustar00rootroot0000000000000052 comment=9a055a0cf69f8b7005b0061533fe4907762861a2 netopeer2-2.0.35/000077500000000000000000000000001414274114600135105ustar00rootroot00000000000000netopeer2-2.0.35/.github/000077500000000000000000000000001414274114600150505ustar00rootroot00000000000000netopeer2-2.0.35/.github/workflows/000077500000000000000000000000001414274114600171055ustar00rootroot00000000000000netopeer2-2.0.35/.github/workflows/ci.yml000066400000000000000000000125501414274114600202260ustar00rootroot00000000000000name: netopeer2 CI on: push: branches: - master - devel pull_request: branches: - master - devel env: DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev jobs: git-branch: name: Get git branch runs-on: ubuntu-18.04 outputs: branch-name: ${{ steps.get-git-branch.outputs.branch-name }} steps: - id: get-git-branch run: | if ${{ github.event_name == 'push' }} then export GIT_BRANCH=`echo ${{ github.ref }} | cut -d'/' -f 3` else export GIT_BRANCH=${{ github.base_ref }} fi echo "::set-output name=branch-name::$GIT_BRANCH" build: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} needs: git-branch strategy: fail-fast: false matrix: config: - { name: "Release, Ubuntu 18.04, gcc", os: "ubuntu-18.04", build-type: "Release", dep-build-type: "Release", cc: "gcc", options: "", packages: "", snaps: "", make-prepend: "", make-target: "" } - { name: "Release, Ubuntu 18.04, clang", os: "ubuntu-18.04", build-type: "Release", dep-build-type: "Release", cc: "clang", options: "", packages: "", snaps: "", make-prepend: "", make-target: "" } - { name: "Debug, Ubuntu 18.04, gcc, with URL", os: "ubuntu-18.04", build-type: "Debug", dep-build-type: "Debug", cc: "gcc", options: "", packages: "libcurl4-openssl-dev valgrind", snaps: "", make-prepend: "", make-target: "" } - { name: "Debug, Ubuntu 18.04, clang", os: "ubuntu-18.04", build-type: "Debug", dep-build-type: "Debug", cc: "clang", options: "", packages: "valgrind", snaps: "", make-prepend: "", make-target: "" } - { name: "ASAN and UBSAN", os: "ubuntu-18.04", build-type: "Debug", dep-build-type: "Debug", cc: "clang", options: "-DCMAKE_C_FLAGS=-fsanitize=address,undefined -DENABLE_VALGRIND_TESTS=OFF", packages: "", snaps: "", make-prepend: "", make-target: "" } steps: - uses: actions/checkout@v2 - name: Deps-packages shell: bash run: | sudo add-apt-repository ppa:kedazo/libssh-0.7.x -y sudo apt-get update sudo apt-get install $DEFAULT_PACKAGES ${{ matrix.config.packages }} if ${{ matrix.config.snaps != '' }} then sudo snap install ${{ matrix.config.snaps }} fi - name: Deps-uncrustify shell: bash working-directory: ${{ github.workspace }} run: | git clone --branch uncrustify-0.71.0 https://github.com/uncrustify/uncrustify cd uncrustify mkdir build cd build CC=${{ matrix.config.cc }} cmake .. make sudo make install if: ${{ matrix.config.name == 'Debug, Ubuntu 18.04, gcc' }} - name: Deps-libyang shell: bash run: | git clone -b ${{needs.git-branch.outputs.branch-name}} https://github.com/CESNET/libyang.git cd libyang mkdir build cd build CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.dep-build-type }} -DENABLE_TESTS=OFF .. make -j2 sudo make install - name: Deps-sysrepo shell: bash run: | git clone -b ${{needs.git-branch.outputs.branch-name}} https://github.com/sysrepo/sysrepo.git cd sysrepo mkdir build cd build CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.build-type }} -DSYSREPO_SUPERUSER_UID=`id -u` -DENABLE_TESTS=OFF .. make -j2 sudo make install - name: Deps-libnetconf2 shell: bash run: | git clone -b ${{needs.git-branch.outputs.branch-name}} https://github.com/CESNET/libnetconf2.git cd libnetconf2 mkdir build cd build CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.build-type }} -DENABLE_TESTS=OFF .. make -j2 sudo make install - name: Deps-update-ld-cache shell: bash run: sudo ldconfig - name: Configure shell: bash working-directory: ${{ github.workspace }} run: | mkdir build cd build CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.build-type }} ${{ matrix.config.options }} .. - name: Build shell: bash working-directory: ${{ github.workspace }}/build run: | export LC_ALL=C.UTF-8 export PATH=/snap/bin:${{ github.workspace }}/coverity-tools/bin:$PATH ${{ matrix.config.make-prepend }} make ${{ matrix.config.make-target }} - name: Test shell: bash working-directory: ${{ github.workspace }}/build run: ctest --output-on-failure netopeer2-2.0.35/.github/workflows/devel-push.yml000066400000000000000000000121741414274114600217110ustar00rootroot00000000000000name: netopeer2 devel push on: push: branches: - devel env: DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev COVERITY_PROJECT: CESNET%2FNetopeer2 jobs: git-branch: name: Get git branch runs-on: ubuntu-18.04 outputs: branch-name: ${{ steps.get-git-branch.outputs.branch-name }} steps: - id: get-git-branch run: | if ${{ github.event_name == 'push' }} then export GIT_BRANCH=`echo ${{ github.ref }} | cut -d'/' -f 3` else export GIT_BRANCH=${{ github.base_ref }} fi echo "::set-output name=branch-name::$GIT_BRANCH" build: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} needs: git-branch strategy: fail-fast: false matrix: config: - { name: "Coverity", os: "ubuntu-latest", build-type: "Debug", dep-build-type: "Debug", cc: "clang", options: "", packages: "", snaps: "", make-prepend: "cov-build --dir cov-int", make-target: "" } - { name: "Codecov", os: "ubuntu-latest", build-type: "Debug", dep-build-type: "Debug", cc: "gcc", options: "-DENABLE_COVERAGE=ON", packages: "libcmocka-dev lcov", snaps: "", make-prepend: "", make-target: "" } steps: - uses: actions/checkout@v2 - name: Deps-packages shell: bash run: | sudo add-apt-repository ppa:kedazo/libssh-0.7.x -y sudo apt-get update sudo apt-get install $DEFAULT_PACKAGES ${{ matrix.config.packages }} if ${{ matrix.config.snaps != '' }} then sudo snap install ${{ matrix.config.snaps }} fi - name: Deps-coverity shell: bash working-directory: ${{ github.workspace }} run: | wget -q https://scan.coverity.com/download/linux64 --post-data "token=$TOKEN&project=$COVERITY_PROJECT" -O coverity-tools.tar.gz mkdir coverity-tools tar xzf coverity-tools.tar.gz --strip 1 -C coverity-tools env: TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} if: ${{ matrix.config.name == 'Coverity' }} - name: Deps-libyang shell: bash run: | git clone -b ${{needs.git-branch.outputs.branch-name}} https://github.com/CESNET/libyang.git cd libyang mkdir build cd build CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.dep-build-type }} -DENABLE_BUILD_TESTS=OFF .. make -j2 sudo make install - name: Deps-sysrepo shell: bash run: | git clone -b ${{needs.git-branch.outputs.branch-name}} https://github.com/sysrepo/sysrepo.git cd sysrepo mkdir build cd build CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.build-type }} -DENABLE_TESTS=OFF .. make -j2 sudo make install - name: Deps-libnetconf2 shell: bash run: | git clone -b ${{needs.git-branch.outputs.branch-name}} https://github.com/CESNET/libnetconf2.git cd libnetconf2 mkdir build cd build CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.build-type }} -DENABLE_BUILD_TESTS=OFF .. make -j2 sudo make install - name: Deps-update-ld-cache shell: bash run: sudo ldconfig - name: Configure shell: bash working-directory: ${{ github.workspace }} run: | mkdir build cd build CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.build-type }} ${{ matrix.config.options }} .. - name: Build shell: bash working-directory: ${{ github.workspace }}/build run: | export LC_ALL=C.UTF-8 export PATH=/snap/bin:${{ github.workspace }}/coverity-tools/bin:$PATH ${{ matrix.config.make-prepend }} make ${{ matrix.config.make-target }} - name: Test shell: bash working-directory: ${{ github.workspace }}/build run: ctest --output-on-failure - name: Upload to Coverity.com shell: bash working-directory: ${{ github.workspace }}/build run: | tar czvf netopeer2.tgz cov-int curl \ --form token=$TOKEN \ --form email=mvasko@cesnet.cz \ --form file=@netopeer2.tgz \ --form version="`./netopeer2-server -V | head -n1 | cut -d' ' -f 2`" \ --form description="netopeer2 NETCONF suite" \ https://scan.coverity.com/builds?project=$COVERITY_PROJECT env: TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} if: ${{ matrix.config.name == 'Coverity' }} - name: Upload to Codecov.io shell: bash working-directory: ${{ github.workspace }}/build run: bash <(curl -s https://codecov.io/bash) if: ${{ matrix.config.name == 'Codecov' }} netopeer2-2.0.35/.lgtm.yml000066400000000000000000000023451414274114600152600ustar00rootroot00000000000000extraction: cpp: prepare: packages: - libpcre2-dev - libssh-dev - libssl-dev after_prepare: - cd $LGTM_WORKSPACE - git clone -b devel https://github.com/CESNET/libyang.git - cd libyang; mkdir build; cd build - cmake -DCMAKE_INSTALL_PREFIX=$LGTM_WORKSPACE -DENABLE_BUILD_TESTS=OFF .. - make -j2 - make install - cd $LGTM_WORKSPACE - git clone -b devel https://github.com/sysrepo/sysrepo.git - cd sysrepo; mkdir build; cd build - cmake -DCMAKE_INSTALL_PREFIX=$LGTM_WORKSPACE -DCMAKE_INCLUDE_PATH=$LGTM_WORKSPACE/include -DCMAKE_LIBRARY_PATH=$LGTM_WORKSPACE/lib -DENABLE_TESTS=OFF .. - make -j2 - make install - cd $LGTM_WORKSPACE - git clone -b devel https://github.com/CESNET/libnetconf2.git - cd libnetconf2; mkdir build; cd build - cmake -DCMAKE_INSTALL_PREFIX=$LGTM_WORKSPACE -DCMAKE_INCLUDE_PATH=$LGTM_WORKSPACE/include -DCMAKE_LIBRARY_PATH=$LGTM_WORKSPACE/lib -DENABLE_BUILD_TESTS=OFF .. - make -j2 - make install configure: command: - mkdir build; cd build - cmake -DCMAKE_INCLUDE_PATH=$LGTM_WORKSPACE/include -DCMAKE_LIBRARY_PATH=$LGTM_WORKSPACE/lib .. index: build_command: - cd build - make -j2 netopeer2-2.0.35/CMakeLists.txt000066400000000000000000000320421414274114600162510ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.12) project(netopeer2 C) set(NETOPEER2_DESC "NETCONF tools suite including a server and command-line client") # include custom Modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules/") include(GNUInstallDirs) include(CheckFunctionExists) include(CheckIncludeFile) include(UseCompat) include(SourceFormat) include(GenCoverage) if(POLICY CMP0075) cmake_policy(SET CMP0075 NEW) endif() # set default build type if not specified by user and normalize it if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) endif() string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE_UPPER) # see https://github.com/CESNET/libyang/pull/1692 for why CMAKE_C_FLAGS_ are not used directly if("${BUILD_TYPE_UPPER}" STREQUAL "RELEASE") set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build Type" FORCE) set(CMAKE_C_FLAGS "-DNDEBUG -O2 ${CMAKE_C_FLAGS}") elseif("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG") set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build Type" FORCE) set(CMAKE_C_FLAGS "-g -O0 ${CMAKE_C_FLAGS}") elseif("${BUILD_TYPE_UPPER}" STREQUAL "RELWITHDEBINFO") set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Build Type" FORCE) elseif("${BUILD_TYPE_UPPER}" STREQUAL "RELWITHDEBUG") set(CMAKE_BUILD_TYPE "RelWithDebug" CACHE STRING "Build Type" FORCE) endif() # check the supported platform if(NOT UNIX) message(FATAL_ERROR "Only *nix like systems are supported.") endif() # Version of the project # 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) # libyang required SO version 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) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=gnu99") # # options # option(ENABLE_TESTS "Build tests" ON) if(("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG") OR ("${BUILD_TYPE_UPPER}" STREQUAL "RELWITHDEBINFO")) option(ENABLE_VALGRIND_TESTS "Build tests with valgrind" ON) else() option(ENABLE_VALGRIND_TESTS "Build tests with valgrind" OFF) endif() 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") # script options option(INSTALL_MODULES "Install required modules into sysrepo" ON) option(GENERATE_HOSTKEY "Generate a new RSA host key in the keystore named \"genkey\"" ON) option(MERGE_LISTEN_CONFIG "Merge default server configuration for listening on all IPv4 interfaces" ON) set(MODULES_PERMS 600 CACHE STRING "File access permissions set for all the server modules") if(NOT MODULES_OWNER) execute_process(COMMAND id -un RESULT_VARIABLE RET OUTPUT_VARIABLE MODULES_OWNER OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_VARIABLE ERROR_STR OUTPUT_STRIP_TRAILING_WHITESPACE) if(RET) message(WARNING "Learning server module user failed (${ERROR_STR}), the current user will be used.") endif() endif() set(MODULES_OWNER "${MODULES_OWNER}" CACHE STRING "System user that will become the owner of server modules, empty means the current user") if(NOT MODULES_GROUP AND MODULES_OWNER) execute_process(COMMAND id -gn ${MODULES_OWNER} RESULT_VARIABLE RET OUTPUT_VARIABLE MODULES_GROUP OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_VARIABLE ERROR_STR OUTPUT_STRIP_TRAILING_WHITESPACE) if(RET) message(WARNING "Learning server module group failed (${ERROR_STR}), the current user group will be used.") endif() endif() set(MODULES_GROUP "${MODULES_GROUP}" CACHE STRING "System group that the server modules will belong to, empty means the current user group") # set prefix for the PID file if(NOT PIDFILE_PREFIX) set(PIDFILE_PREFIX "/var/run") endif() set(NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN "%s/.ssh/authorized_keys" CACHE STRING "printf-like pattern for determining path to users' SSH authorized_keys file. Must contain exactly one '%s'.") set(NP2SRV_SSH_AUTHORIZED_KEYS_ARG_IS_USERNAME 0 CACHE STRING "If true, replace '%s' by username. If not set, replace '%s' by home directory. By default, unset.") if(NOT NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN MATCHES "^[^%]*%s[^%]*$") message(FATAL_ERROR "Wrong format string given for NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN: exactly one '%s' expected.") endif() if(NOT SERVER_DIR) if("${BUILD_TYPE_UPPER}" STREQUAL "RELEASE") set(SERVER_DIR "/var/netopeer2") else() set(SERVER_DIR "$ENV{HOME}/.netopeer2-server") endif() endif() # # sources # 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 src/subscribed_notifications.c src/yang_push.c src/log.c src/err_netconf.c) # source files to be covered by the 'format' target set(FORMAT_SRC compat/*.c compat/*.h* src/*.c src/*.h cli/*.c cli/*.h tests/*.c tests/*.h) # # checks # # lnc2 support for np2srv thread count find_package(PkgConfig) if(PKG_CONFIG_FOUND) execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} "--variable=LNC2_MAX_THREAD_COUNT" "libnetconf2" OUTPUT_VARIABLE LNC2_THREAD_COUNT) if(LNC2_THREAD_COUNT) string(STRIP ${LNC2_THREAD_COUNT} LNC2_THREAD_COUNT) if(LNC2_THREAD_COUNT LESS THREAD_COUNT) message(FATAL_ERROR "libnetconf2 was compiled with support up to ${LNC2_THREAD_COUNT} threads, server is configured with ${THREAD_COUNT}.") else() message(STATUS "libnetconf2 was compiled with support of up to ${LNC2_THREAD_COUNT} threads") endif() else() message(STATUS "Unable to learn libnetconf2 thread support, check skipped") endif() else() message(STATUS "pkg-config not found, so it was not possible to check if libnetconf2 supports ${THREAD_COUNT} threads") endif() if(ENABLE_VALGRIND_TESTS) find_program(VALGRIND_FOUND valgrind) if(NOT VALGRIND_FOUND) message(WARNING "valgrind executable not found! Disabling memory leaks tests.") set(ENABLE_VALGRIND_TESTS OFF) else() set(ENABLE_TESTS ON) endif() endif() if(ENABLE_TESTS) find_package(CMocka 1.0.1) if(NOT CMOCKA_FOUND) message(STATUS "Disabling tests because of missing CMocka") set(ENABLE_TESTS OFF) endif() endif() if(ENABLE_COVERAGE) gen_coverage_enable(${ENABLE_TESTS}) endif() if ("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG") source_format_enable() endif() # # targets # # put all binaries into one directory (even from subprojects) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) # dependencies - OpenSSL (required by later libnetconf2 checks and not really the server itself) find_package(OpenSSL) if(OPENSSL_FOUND) list(APPEND CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) endif() # dependencies - libssh (also required by libnetconf2 checks) find_package(LibSSH 0.7.1) if(LIBSSH_FOUND) list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBSSH_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBSSH_LIBRARIES}) endif() # dependencies - libnetconf2 (now, because we need to configure outselves based on it) find_package(LibNETCONF2 REQUIRED) include_directories(${LIBNETCONF2_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBNETCONF2_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBNETCONF2_LIBRARIES}) # at least some remote transport is enabled if(LIBNETCONF2_ENABLED_SSH OR LIBNETCONF2_ENABLED_TLS) list(APPEND SERVER_SRC src/netconf_server.c) endif() # SSH is enabled if(LIBNETCONF2_ENABLED_SSH) list(APPEND SERVER_SRC src/netconf_server_ssh.c) endif() # TLS is enabled if(LIBNETCONF2_ENABLED_TLS) list(APPEND SERVER_SRC src/netconf_server_tls.c) endif() # link compat use_compat() # netopeer2-server add_library(serverobj OBJECT ${SERVER_SRC}) add_executable(netopeer2-server $ src/main.c ${compatsrc}) # # dependencies # # librt (not required on OSX or QNX) find_library(LIBRT rt) if(LIBRT) target_link_libraries(netopeer2-server ${LIBRT}) endif() # libnetconf2 (was already found) target_link_libraries(netopeer2-server ${LIBNETCONF2_LIBRARIES}) # libssh (was already found, if exists) if(LIBSSH_FOUND AND LIBNETCONF2_ENABLED_SSH) target_link_libraries(netopeer2-server ${LIBSSH_LIBRARIES}) include_directories(${LIBSSH_INCLUDE_DIRS}) endif() # libcurl if(ENABLE_URL) find_package(CURL) if(CURL_FOUND) include_directories(${CURL_INCLUDE_DIRS}) target_link_libraries(netopeer2-server ${CURL_LIBRARIES}) set(NP2SRV_URL_CAPAB 1) else() message(STATUS "libcurl not found, url capability will not be supported") unset(NP2SRV_URL_CAPAB) endif() endif() # pthread set(CMAKE_THREAD_PREFER_PTHREAD TRUE) find_package(Threads REQUIRED) 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) target_link_libraries(netopeer2-server ${LIBYANG_LIBRARIES}) include_directories(${LIBYANG_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBYANG_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBYANG_LIBRARIES}) # sysrepo find_package(Sysrepo ${SYSREPO_DEP_SOVERSION} REQUIRED) target_link_libraries(netopeer2-server ${SYSREPO_LIBRARIES}) include_directories(${SYSREPO_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_INCLUDES ${SYSREPO_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${SYSREPO_LIBRARIES}) # generate files configure_file("${PROJECT_SOURCE_DIR}/src/config.h.in" "${PROJECT_BINARY_DIR}/config.h" ESCAPE_QUOTES @ONLY) include_directories(${PROJECT_BINARY_DIR}) # set script dir set(SCRIPT_DIR "${PROJECT_SOURCE_DIR}/scripts/") # install the module files 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(INSTALL_MODULES) install(CODE " message(STATUS \"Installing missing sysrepo modules...\") set(ENV{NP2_MODULE_DIR} \"${YANG_MODULE_DIR}\") set(ENV{NP2_MODULE_PERMS} \"${MODULES_PERMS}\") set(ENV{NP2_MODULE_OWNER} \"${MODULES_OWNER}\") set(ENV{NP2_MODULE_GROUP} \"${MODULES_GROUP}\") 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}\") endif() ") else() message(WARNING "Server will refuse to start if the modules are not installed!") endif() if(GENERATE_HOSTKEY) install(CODE " message(STATUS \"Generating a new RSA host key \\\"genkey\\\" if not already added...\") 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}\") endif() ") endif() if(MERGE_LISTEN_CONFIG) install(CODE " message(STATUS \"Merging default server listen configuration if there is none...\") 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}\") endif() ") endif() # tests if(ENABLE_TESTS) enable_testing() add_subdirectory(tests) endif() # create coverage target for generating coverage reports gen_coverage("test_.*" "test_.*_valgrind") # cli if(BUILD_CLI) add_subdirectory(cli) endif() # source files to be covered by the 'format' target and a test with 'format-check' target source_format(${FORMAT_SRC}) # clean cmake cache add_custom_target(cleancache COMMAND make clean COMMAND find . -iname '*cmake*' -not -name CMakeLists.txt -exec rm -rf {} + COMMAND rm -rf Makefile Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) # uninstall add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_MODULE_PATH}/uninstall.cmake" COMMAND ${SCRIPT_DIR}/remove.sh COMMENT "Removing netopeer2 modules from sysrepo...") netopeer2-2.0.35/CMakeModules/000077500000000000000000000000001414274114600160215ustar00rootroot00000000000000netopeer2-2.0.35/CMakeModules/FindCMocka.cmake000066400000000000000000000027261414274114600207700ustar00rootroot00000000000000# - Try to find CMocka # Once done this will define # # CMOCKA_ROOT_DIR - Set this variable to the root installation of CMocka # # Read-Only variables: # CMOCKA_FOUND - system has CMocka # CMOCKA_INCLUDE_DIR - the CMocka include directory # CMOCKA_LIBRARIES - Link these to use CMocka # CMOCKA_DEFINITIONS - Compiler switches required for using CMocka # #============================================================================= # Copyright (c) 2011-2012 Andreas Schneider # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # find_path(CMOCKA_INCLUDE_DIR NAMES cmocka.h PATHS ${CMOCKA_ROOT_DIR}/include ) find_library(CMOCKA_LIBRARY NAMES cmocka PATHS ${CMOCKA_ROOT_DIR}/include ) if (CMOCKA_LIBRARY) set(CMOCKA_LIBRARIES ${CMOCKA_LIBRARIES} ${CMOCKA_LIBRARY} ) endif (CMOCKA_LIBRARY) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(CMocka DEFAULT_MSG CMOCKA_LIBRARIES CMOCKA_INCLUDE_DIR) # show the CMOCKA_INCLUDE_DIR and CMOCKA_LIBRARIES variables only in the advanced view mark_as_advanced(CMOCKA_INCLUDE_DIR CMOCKA_LIBRARIES) netopeer2-2.0.35/CMakeModules/FindLibNETCONF2.cmake000066400000000000000000000064551414274114600214430ustar00rootroot00000000000000# - Try to find LibNETCONF2 # Once done this will define # # LIBNETCONF2_FOUND - system has LibNETCONF2 # LIBNETCONF2_INCLUDE_DIRS - the LibNETCONF2 include directory # LIBNETCONF2_LIBRARIES - Link these to use LibNETCONF2 # 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. # # 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) include(CheckSymbolExists) if(LIBNETCONF2_LIBRARIES AND LIBNETCONF2_INCLUDE_DIRS) # in cache already set(LIBNETCONF2_FOUND TRUE) else() find_path(LIBNETCONF2_INCLUDE_DIR NAMES nc_client.h nc_server.h PATHS /usr/include /usr/local/include /opt/local/include /sw/include ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include ) find_library(LIBNETCONF2_LIBRARY NAMES netconf2 libnetconf2 PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) 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) # check the configured options and make them available through cmake list(INSERT CMAKE_REQUIRED_INCLUDES 0 "${LIBNETCONF2_INCLUDE_DIR}") check_symbol_exists("NC_ENABLED_SSH" "nc_client.h" LIBNETCONF2_ENABLED_SSH) check_symbol_exists("NC_ENABLED_TLS" "nc_client.h" LIBNETCONF2_ENABLED_TLS) list(REMOVE_AT CMAKE_REQUIRED_INCLUDES 0) endif() netopeer2-2.0.35/CMakeModules/FindLibSSH.cmake000066400000000000000000000112241414274114600207100ustar00rootroot00000000000000# - Try to find LibSSH # Once done this will define # # LIBSSH_FOUND - system has LibSSH # LIBSSH_INCLUDE_DIRS - the LibSSH include directory # LIBSSH_LIBRARIES - link these to use LibSSH # LIBSSH_VERSION - # # Author Michal Vasko # Copyright (c) 2020 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(LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS) # in cache already set(LIBSSH_FOUND TRUE) else() find_path(LIBSSH_INCLUDE_DIR NAMES libssh/libssh.h PATHS /usr/include /usr/local/include /opt/local/include /sw/include ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include ) find_library(LIBSSH_LIBRARY NAMES ssh.so libssh.so libssh.dylib PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) if(LIBSSH_INCLUDE_DIR AND LIBSSH_LIBRARY) # learn libssh version if(EXISTS ${LIBSSH_INCLUDE_DIR}/libssh/libssh_version.h) set(LIBSSH_HEADER_PATH ${LIBSSH_INCLUDE_DIR}/libssh/libssh_version.h) else() set(LIBSSH_HEADER_PATH ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h) endif() file(STRINGS ${LIBSSH_HEADER_PATH} LIBSSH_VERSION_MAJOR REGEX "#define[ ]+LIBSSH_VERSION_MAJOR[ ]+[0-9]+") if(NOT LIBSSH_VERSION_MAJOR) message(STATUS "LIBSSH_VERSION_MAJOR not found, assuming libssh is too old and cannot be used!") set(LIBSSH_INCLUDE_DIR "LIBSSH_INCLUDE_DIR-NOTFOUND") set(LIBSSH_LIBRARY "LIBSSH_LIBRARY-NOTFOUND") else() string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MAJOR ${LIBSSH_VERSION_MAJOR}) file(STRINGS ${LIBSSH_HEADER_PATH} LIBSSH_VERSION_MINOR REGEX "#define[ ]+LIBSSH_VERSION_MINOR[ ]+[0-9]+") string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MINOR ${LIBSSH_VERSION_MINOR}) file(STRINGS ${LIBSSH_HEADER_PATH} LIBSSH_VERSION_PATCH REGEX "#define[ ]+LIBSSH_VERSION_MICRO[ ]+[0-9]+") string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_PATCH ${LIBSSH_VERSION_PATCH}) set(LIBSSH_VERSION ${LIBSSH_VERSION_MAJOR}.${LIBSSH_VERSION_MINOR}.${LIBSSH_VERSION_PATCH}) if(LIBSSH_VERSION VERSION_LESS 0.8.0) # libssh_threads also needs to be linked for these versions string(REPLACE "libssh.so" "libssh_threads.so" LIBSSH_THREADS_LIBRARY ${LIBSSH_LIBRARY} ) string(REPLACE "libssh.dylib" "libssh_threads.dylib" LIBSSH_THREADS_LIBRARY ${LIBSSH_THREADS_LIBRARY} ) string(REPLACE "ssh.so" "ssh_threads.so" LIBSSH_THREADS_LIBRARY ${LIBSSH_THREADS_LIBRARY} ) endif() endif() endif() set(LIBSSH_INCLUDE_DIRS ${LIBSSH_INCLUDE_DIR}) set(LIBSSH_LIBRARIES ${LIBSSH_LIBRARY} ${LIBSSH_THREADS_LIBRARY}) mark_as_advanced(LIBSSH_INCLUDE_DIRS LIBSSH_LIBRARIES) find_package_handle_standard_args(LibSSH FOUND_VAR LIBSSH_FOUND REQUIRED_VARS LIBSSH_INCLUDE_DIRS LIBSSH_LIBRARIES VERSION_VAR LIBSSH_VERSION) endif() netopeer2-2.0.35/CMakeModules/FindLibYANG.cmake000066400000000000000000000066671414274114600210300ustar00rootroot00000000000000# - Try to find LibYANG # Once done this will define # # LIBYANG_FOUND - system has LibYANG # LIBYANG_INCLUDE_DIRS - the LibYANG include directory # LIBYANG_LIBRARIES - Link these to use LibYANG # LIBYANG_VERSION - SO version of the found libyang library # # Author Michal Vasko # 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 # 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(LIBYANG_LIBRARIES AND LIBYANG_INCLUDE_DIRS) # in cache already set(LIBYANG_FOUND TRUE) else() find_path(LIBYANG_INCLUDE_DIR NAMES libyang/libyang.h PATHS /usr/include /usr/local/include /opt/local/include /sw/include ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include ) find_library(LIBYANG_LIBRARY NAMES yang libyang PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) if(LIBYANG_INCLUDE_DIR) find_path(LY_VERSION_PATH "libyang/version.h" HINTS ${LIBYANG_INCLUDE_DIR}) if(LY_VERSION_PATH) file(READ "${LY_VERSION_PATH}/libyang/version.h" LY_VERSION_FILE) else() find_path(LY_HEADER_PATH "libyang/libyang.h" HINTS ${LIBYANG_INCLUDE_DIR}) file(READ "${LY_HEADER_PATH}/libyang/libyang.h" LY_VERSION_FILE) endif() string(REGEX MATCH "#define LY_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\"" LY_VERSION_MACRO "${LY_VERSION_FILE}") string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LIBYANG_VERSION "${LY_VERSION_MACRO}") endif() set(LIBYANG_INCLUDE_DIRS ${LIBYANG_INCLUDE_DIR}) set(LIBYANG_LIBRARIES ${LIBYANG_LIBRARY}) mark_as_advanced(LIBYANG_INCLUDE_DIRS LIBYANG_LIBRARIES) # handle the QUIETLY and REQUIRED arguments and set LIBYANG_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(LibYANG FOUND_VAR LIBYANG_FOUND REQUIRED_VARS LIBYANG_LIBRARY LIBYANG_INCLUDE_DIR VERSION_VAR LIBYANG_VERSION) endif() netopeer2-2.0.35/CMakeModules/FindSysrepo.cmake000066400000000000000000000061351414274114600212750ustar00rootroot00000000000000# Find Sysrepo # Once done, it will define # # SYSREPO_FOUND - System has sysrepo # SYSREPO_INCLUDE_DIRS - The sysrepo include directories # SYSREPO_LIBRARIES - The libraries needed to use sysrepo # SYSREPO_VERSION - SO version of the found sysrepo library # # Author Michal Vasko # 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 # 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(SYSREPO_LIBRARIES AND SYSREPO_INCLUDE_DIRS) # in cache already set(SYSREPO_FOUND TRUE) else() find_path(SYSREPO_INCLUDE_DIR NAMES sysrepo.h PATHS /usr/include /usr/local/include /opt/local/include /sw/include ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include ) find_library(SYSREPO_LIBRARY NAMES sysrepo libsysrepo PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) if(SYSREPO_INCLUDE_DIR) find_path(SR_VERSION_PATH "sysrepo/version.h" HINTS ${SYSREPO_INCLUDE_DIR}) file(READ "${SR_VERSION_PATH}/sysrepo/version.h" SR_VERSION_FILE) string(REGEX MATCH "#define SR_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\"" SR_VERSION_MACRO "${SR_VERSION_FILE}") string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" SYSREPO_VERSION "${SR_VERSION_MACRO}") endif() set(SYSREPO_INCLUDE_DIRS ${SYSREPO_INCLUDE_DIR}) set(SYSREPO_LIBRARIES ${SYSREPO_LIBRARY}) mark_as_advanced(SYSREPO_INCLUDE_DIRS SYSREPO_LIBRARIES) # handle the QUIETLY and REQUIRED arguments and set SYSREPO_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(Sysrepo FOUND_VAR SYSREPO_FOUND REQUIRED_VARS SYSREPO_LIBRARY SYSREPO_INCLUDE_DIR VERSION_VAR SYSREPO_VERSION) endif() netopeer2-2.0.35/CMakeModules/FindUncrustify.cmake000066400000000000000000000015201414274114600217750ustar00rootroot00000000000000# - Find uncrustify # Find the uncrustify binary. # # UNCRUSTIFY - path ot the binary # UNCRUSTIFY_VERSION - found version # UNCRUSTIFY_FOUND - True if uncrustify found. include(FindPackageHandleStandardArgs) find_program(UNCRUSTIFY uncrustify) if(UNCRUSTIFY) execute_process(COMMAND ${UNCRUSTIFY} --version OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE VERSION) string(FIND ${VERSION} "-" START_IDX) math(EXPR START_IDX "${START_IDX} + 1") string(SUBSTRING "${VERSION}" ${START_IDX} -1 VERSION) string(FIND ${VERSION} "-" LEN) string(SUBSTRING "${VERSION}" 0 ${LEN} UNCRUSTIFY_VERSION) endif() # Handle the QUIETLY and REQUIRED arguments and set UNCRUSTIFY_FOUND to TRUE if all listed variables are TRUE. find_package_handle_standard_args(Uncrustify REQUIRED_VARS UNCRUSTIFY VERSION_VAR UNCRUSTIFY_VERSION) netopeer2-2.0.35/CMakeModules/GenCoverage.cmake000066400000000000000000000106371414274114600212170ustar00rootroot00000000000000# generate test code coverage report # check that coverage tools are available - always use before GEN_COVERAGE macro(GEN_COVERAGE_ENABLE ENABLE_TESTS) # make into normal variable set(TESTS_ENABLED ${ENABLE_TESTS}) set(GEN_COVERAGE_ENABLED ON) if(NOT TESTS_ENABLED) message(WARNING "You cannot generate coverage when tests are disabled. Enable test by additing parameter -DENABLE_BUILD_TESTS=ON or run cmake with Debug build target.") set(GEN_COVERAGE_ENABLED OFF) endif() if(GEN_COVERAGE_ENABLED) find_program(PATH_GCOV NAMES gcov) if(NOT PATH_GCOV) message(WARNING "gcov executable not found! Disabling building code coverage report.") set(GEN_COVERAGE_ENABLED OFF) endif() endif() if(GEN_COVERAGE_ENABLED) find_program(PATH_LCOV NAMES lcov) if(NOT PATH_LCOV) message(WARNING "lcov executable not found! Disabling building code coverage report.") set(GEN_COVERAGE_ENABLED OFF) endif() endif() if(GEN_COVERAGE_ENABLED) find_program(PATH_GENHTML NAMES genhtml) if(NOT PATH_GENHTML) message(WARNING "genhtml executable not found! Disabling building code coverage report.") set(GEN_COVERAGE_ENABLED OFF) endif() endif() if(GEN_COVERAGE_ENABLED) if(NOT CMAKE_COMPILER_IS_GNUCC) message(WARNING "Compiler is not gcc! Coverage may break the tests!") endif() execute_process( COMMAND bash "-c" "${CMAKE_C_COMPILER} --version | head -n1 | sed \"s/.* (.*) \\([0-9]\\+.[0-9]\\+.[0-9]\\+ .*\\)/\\1/\"" OUTPUT_VARIABLE GCC_VERSION_FULL OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND bash "-c" "${PATH_GCOV} --version | head -n1 | sed \"s/.* (.*) \\([0-9]\\+.[0-9]\\+.[0-9]\\+ .*\\)/\\1/\"" OUTPUT_VARIABLE GCOV_VERSION_FULL OUTPUT_STRIP_TRAILING_WHITESPACE ) if(NOT GCC_VERSION_FULL STREQUAL GCOV_VERSION_FULL) message(WARNING "gcc and gcov versions do not match! Generating coverage may fail with errors.") endif() # add specific required compile flags set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage") endif() endmacro() # tests are always expected to be in ${CMAKE_SOURCE_DIR}/tests function(GEN_COVERAGE MATCH_TEST_REGEX EXCLUDE_TEST_REGEX) if(NOT GEN_COVERAGE_ENABLED) return() endif() # destination set(COVERAGE_DIR "${CMAKE_BINARY_DIR}/code_coverage/") set(COVERAGE_FILE_RAW "${CMAKE_BINARY_DIR}/coverage_raw.info") set(COVERAGE_FILE_CLEAN "${CMAKE_BINARY_DIR}/coverage_clean.info") # test match/exclude if(MATCH_TEST_REGEX) set(MATCH_TEST_ARGS -R \"${MATCH_TEST_REGEX}\") endif() if(EXCLUDE_TEST_REGEX) set(EXCLUDE_TEST_ARGS -E \"${EXCLUDE_TEST_REGEX}\") endif() # coverage target add_custom_target(coverage COMMENT "Generating code coverage..." WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" # Cleanup code counters COMMAND "${PATH_LCOV}" --directory . --zerocounters --quiet # Run tests COMMAND "${CMAKE_CTEST_COMMAND}" --quiet ${MATCH_TEST_ARGS} ${EXCLUDE_TEST_ARGS} # Capture the counters COMMAND "${PATH_LCOV}" --directory . --rc lcov_branch_coverage=1 --rc 'lcov_excl_line=assert' --capture --quiet --output-file "${COVERAGE_FILE_RAW}" # Remove coverage of tests, system headers, etc. COMMAND "${PATH_LCOV}" --remove "${COVERAGE_FILE_RAW}" '${CMAKE_SOURCE_DIR}/tests/*' '${CMAKE_SOURCE_DIR}/compat/*' '*libyang*' --rc lcov_branch_coverage=1 --quiet --output-file "${COVERAGE_FILE_CLEAN}" # Generate HTML report COMMAND "${PATH_GENHTML}" --branch-coverage --function-coverage --quiet --title "${PROJECT_NAME}" --legend --show-details --output-directory "${COVERAGE_DIR}" "${COVERAGE_FILE_CLEAN}" # Delete the counters COMMAND "${CMAKE_COMMAND}" -E remove ${COVERAGE_FILE_RAW} ${COVERAGE_FILE_CLEAN} ) add_custom_command(TARGET coverage POST_BUILD WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/tests" COMMENT "To see the code coverage report, open ${COVERAGE_DIR}index.html" COMMAND ; ) endfunction() netopeer2-2.0.35/CMakeModules/SourceFormat.cmake000066400000000000000000000022251414274114600214350ustar00rootroot00000000000000# format source files with uncrustify # check that format checking is available - always use before SOURCE_FORMAT macro(SOURCE_FORMAT_ENABLE) find_package(Uncrustify 0.71) if(UNCRUSTIFY_FOUND) set(SOURCE_FORMAT_ENABLED TRUE) else() set(SOURCE_FORMAT_ENABLED FALSE) endif() endmacro() # files are expected to be a list and relative paths are resolved wtih respect to CMAKE_SOURCE DIR macro(SOURCE_FORMAT) if(NOT ${ARGC}) message(FATAL_ERROR "source_format() needs a list of files to format!") endif() if(SOURCE_FORMAT_ENABLED) add_custom_target(format COMMAND ${UNCRUSTIFY} -c ${CMAKE_SOURCE_DIR}/uncrustify.cfg --no-backup --replace ${ARGN} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMENT "Formating sources with ${UNCRUSTIFY} ...") add_custom_target(format-check COMMAND ${UNCRUSTIFY} -c ${CMAKE_SOURCE_DIR}/uncrustify.cfg --check ${ARGN} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMENT "Checking format of the sources with ${UNCRUSTIFY} ...") set(SOURCE_FORMAT_ENABLED TRUE) endif() endmacro() netopeer2-2.0.35/CMakeModules/UseCompat.cmake000066400000000000000000000041221414274114600207220ustar00rootroot00000000000000# - Use compat library providing various functions and macros that may be missing on some systems # Once done this will define # # compatsrc - sources to add to compilation # # Additionally, "compat.h" include directory is added and can be included. # # Author Michal Vasko # Copyright (c) 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 # include(CheckSymbolExists) include(CheckFunctionExists) include(CheckIncludeFile) include(TestBigEndian) if(POLICY CMP0075) cmake_policy(SET CMP0075 NEW) endif() macro(USE_COMPAT) # compatibility checks set(CMAKE_REQUIRED_DEFINITIONS -D_POSIX_C_SOURCE=200809L) list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) list(APPEND CMAKE_REQUIRED_DEFINITIONS -D__BSD_VISIBLE=1) set(CMAKE_REQUIRED_LIBRARIES pthread) check_symbol_exists(vdprintf "stdio.h;stdarg.h" HAVE_VDPRINTF) check_symbol_exists(asprintf "stdio.h" HAVE_ASPRINTF) check_symbol_exists(vasprintf "stdio.h" HAVE_VASPRINTF) check_symbol_exists(getline "stdio.h" HAVE_GETLINE) check_symbol_exists(strndup "string.h" HAVE_STRNDUP) check_symbol_exists(strnstr "string.h" HAVE_STRNSTR) check_symbol_exists(strdupa "string.h" HAVE_STRDUPA) check_symbol_exists(strchrnul "string.h" HAVE_STRCHRNUL) check_symbol_exists(get_current_dir_name "unistd.h" HAVE_GET_CURRENT_DIR_NAME) check_function_exists(pthread_mutex_timedlock HAVE_PTHREAD_MUTEX_TIMEDLOCK) TEST_BIG_ENDIAN(IS_BIG_ENDIAN) check_include_file("stdatomic.h" HAVE_STDATOMIC) unset(CMAKE_REQUIRED_DEFINITIONS) unset(CMAKE_REQUIRED_LIBRARIES) # header and source file (adding the source directly allows for hiding its symbols) configure_file(${PROJECT_SOURCE_DIR}/compat/compat.h.in ${PROJECT_BINARY_DIR}/compat/compat.h @ONLY) include_directories(${PROJECT_BINARY_DIR}/compat) set(compatsrc ${PROJECT_SOURCE_DIR}/compat/compat.c) endmacro() netopeer2-2.0.35/CMakeModules/uninstall.cmake000066400000000000000000000013371414274114600210400ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0.2) set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") if(NOT EXISTS ${MANIFEST}) message(FATAL_ERROR "Cannot find install manifest: ${MANIFEST}") endif() file(STRINGS ${MANIFEST} files) foreach(file ${files}) if(EXISTS ${file} OR IS_SYMLINK ${file}) message(STATUS "Removing: ${file}") execute_process(COMMAND rm -f ${file} RESULT_VARIABLE result OUTPUT_QUIET ERROR_VARIABLE stderr ERROR_STRIP_TRAILING_WHITESPACE ) if(NOT ${result} EQUAL 0) message(FATAL_ERROR "${stderr}") endif() else() message(STATUS "Does-not-exist: ${file}") endif() endforeach(file) netopeer2-2.0.35/CODINGSTYLE.md000066400000000000000000000112531414274114600156200ustar00rootroot00000000000000# Netopeer2 Coding Style This file describes the coding style used in most C files in the Netopeer2 tools. ## Basics - Use space instead of tabs for indentations. - There is no strict limit for the line length, However, try to keep lines in a reasonable length (120 characters). - Avoid trailing spaces on lines. - Put one blank line between function definitions. - Don't mix declarations and code within a block. Similarly, don't use declarations in iteration statements. ## Naming Use underscores to separate words in an identifier: `multi_word_name`. Use lowercase for most names. Use uppercase for macros, macro parameters and members of enumerations. Do not use names that begin with `_`. If you need a name for "internal use only", use `__` as a suffix instead of a prefix. ## Comments Avoid `//` comments. Use `/* ... */` comments, write block comments with the leading asterisk on each line. You may put the `/*` and `*/` on the same line as comment text if you prefer. ```c /* * comment text */ ``` ## Functions Put the return type, function name, and the braces that surround the function's code on separate lines, all starting in column 0. ```c static int foo(int arg) { ... } ``` When you need to put the function parameters on multiple lines, start new line at column after the opening parenthesis from the initial line. ```c static int my_function(struct my_struct *p1, struct another_struct *p2, int size) { ... } ``` In the absence of good reasons for another order, the following parameter order is preferred. One notable exception is that data parameters and their corresponding size parameters should be paired. 1. The primary object being manipulated, if any (equivalent to the "this" pointer in C++). 2. Input-only parameters. 3. Input/output parameters. 4. Output-only parameters. 5. Status parameter. Functions that destroy an instance of a dynamically-allocated type should accept and ignore a null pointer argument. Code that calls such a function (including the C standard library function `free()`) should omit a null-pointer check. We find that this usually makes code easier to read. ### Function Prototypes Put the return type and function name on the same line in a function prototype: ```c static const struct int foo(int arg); ``` ## Statements - Indent each level of code with 4 spaces. - Put single space between `if`, `while`, `for`, etc. statements and the expression that follow them. On the other hand, function calls has no space between the function name and opening parenthesis. - Opening code block brace is kept at the same line with the `if`, `while`, `for` or `switch` statements. ```c if (a) { x = exp(a); } else { return 1; } ``` - Start switch's cases at the same column as the switch. ```c switch (conn->state) { case 0: return "data found"; case 1: return "data not found"; default: return "unknown error"; } ``` - Do not put gratuitous parentheses around the expression in a return statement, that is, write `return 0;` and not `return(0);` ## Types Use typedefs sparingly. Code is clearer if the actual type is visible at the point of declaration. Do not, in general, declare a typedef for a struct, union, or enum. Do not declare a typedef for a pointer type, because this can be very confusing to the reader. Use the `int_t` and `uint_t` types from `` for exact-width integer types. Use the `PRId`, `PRIu`, and `PRIx` macros from `` for formatting them with `printf()` and related functions. Pointer declarators bind to the variable name, not the type name. Write `int *x`, not `int* x` and definitely not `int * x`. ## Expresions Put one space on each side of infix binary and ternary operators: ```c * / % + - << >> < <= > >= == != & ^ | && || ?: = += -= *= /= %= &= ^= |= <<= >>= ``` Do not put any white space around postfix, prefix, or grouping operators with one exception - `sizeof`, see the note below. ```c () [] -> . ! ~ ++ -- + - * & ``` The "sizeof" operator is unique among C operators in that it accepts two very different kinds of operands: an expression or a type. In general, prefer to specify an expression ```c int *x = calloc(1, sizeof *x); ``` When the operand of sizeof is an expression, there is no need to parenthesize that operand, and please don't. There is an exception to this rule when you need to work with partially compatible structures: ```c struct a_s { uint8_t type; } struct b_s { uint8_t type; char *str; } struct c_s { uint8_t type; uint8_t *u8; } ... struct a_s *a; switch (type) { case 1: a = (struct a_s *)calloc(1, sizeof(struct b_s)); break; case 2: a = (struct a_s *)calloc(1, sizeof(struct c_s)); break; ... ``` netopeer2-2.0.35/LICENSE000066400000000000000000000027121414274114600145170ustar00rootroot00000000000000Copyright (c) 2019, CESNET z.s.p.o. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Netopeer2 nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. netopeer2-2.0.35/README.md000066400000000000000000000225161414274114600147750ustar00rootroot00000000000000# Netopeer2 – NETCONF Server [![BSD license](https://img.shields.io/badge/License-BSD-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Build Status](https://github.com/CESNET/netopeer2/workflows/netopeer2%20CI/badge.svg)](https://github.com/CESNET/netopeer2/actions?query=workflow%3A%22netopeer2+CI%22) [![Coverity](https://scan.coverity.com/projects/8416/badge.svg)](https://scan.coverity.com/projects/8416) [![Codecov](https://codecov.io/gh/CESNET/netopeer2/branch/master/graph/badge.svg?token=ue4DTHDcuq)](https://codecov.io/gh/CESNET/netopeer2) [![Ohloh Project Status](https://www.openhub.net/p/Netopeer2/widgets/project_thin_badge.gif)](https://www.openhub.net/p/Netopeer2) **Netopeer2** is a server for implementing network configuration management based on the NETCONF Protocol. This is the second generation, originally available as the [Netopeer project](https://github.com/CESNET/netopeer). Netopeer2 is based on the new generation of the NETCONF and YANG libraries - [libyang](https://github.com/CESNET/libyang) and [libnetconf2](https://github.com/CESNET/libnetconf2). The Netopeer2 server uses [sysrepo](https://github.com/sysrepo/sysrepo) as a NETCONF datastore implementation. **Netopeer2** is maintained and further developed by the [Tools for Monitoring and Configuration](https://www.liberouter.org/) department of [CESNET](http://www.ces.net/). Any feedback, testing or feature requests are welcome. Please contact us via the [issue tracker](https://github.com/CESNET/Netopeer2/issues). ## Branches The project uses 2 main branches `master` and `devel`. Other branches should not be cloned. In `master` there are files of the last official *release*. Any latest improvements and changes (of the server), which were tested at least briefly are found in `devel`. On every new *release*, `devel` is merged into `master`. This means that when only stable official releases are to be used, either `master` can be used or specific *releases* downloaded. If all the latest bugfixes should be applied, `devel` branch is the one to be used. Note that whenever **a new issue is created** and it occurs on the `master` branch, the **first response will likely be** to use `devel` before any further provided support. ## Requirements * [libyang](https://github.com/CESNET/libyang) * [libnetconf2](https://github.com/CESNET/libnetconf2) * [sysrepo](https://github.com/sysrepo/sysrepo) ### Optional * cmocka >= 1.0.1 (for [tests](#Tests)) * valgrind (for enhanced testing) * gcov (for code coverage) * lcov (for code coverage) * genhtml (for code coverage) ## RFC Compliance * [RFC 5277](https://www.rfc-editor.org/rfc/rfc5277.html) NETCONF Event Notifications * [RFC 6022](https://datatracker.ietf.org/doc/html/rfc6022) YANG Module for NETCONF Monitoring * [RFC 6241](https://datatracker.ietf.org/doc/html/rfc6241) Network Configuration Protocol (NETCONF) * [RFC 6242](https://datatracker.ietf.org/doc/html/rfc6242) Using the NETCONF Protocol over Secure Shell (SSH) * [RFC 6243](https://datatracker.ietf.org/doc/html/rfc6243) With-defaults Capability for NETCONF * [RFC 6470](https://datatracker.ietf.org/doc/html/rfc6470) Network Configuration Protocol (NETCONF) Base Notifications * [RFC 7589](https://datatracker.ietf.org/doc/html/rfc7589) Using the NETCONF Protocol over Transport Layer Security (TLS) with Mutual X.509 Authentication * [RFC 8071](https://www.rfc-editor.org/rfc/rfc8071.html) NETCONF Call Home and RESTCONF Call Home * [RFC 8341](https://datatracker.ietf.org/doc/html/rfc8341) Network Configuration Access Control Model * [RFC 8525](https://datatracker.ietf.org/doc/html/rfc8525) YANG Library * [RFC 8526](https://datatracker.ietf.org/doc/html/rfc8526) NETCONF Extensions to Support the Network Management Datastore Architecture * [RFC 8639](https://www.rfc-editor.org/rfc/rfc8639.html) Subscription to YANG Notifications * [RFC 8640](https://www.rfc-editor.org/rfc/rfc8640.html) Dynamic Subscription to YANG Events and Datastores over NETCONF * [RFC 8641](https://www.rfc-editor.org/rfc/rfc8641.html) Subscription to YANG Notifications for Datastore Updates ## Compilation and installation ``` $ mkdir build; cd build $ cmake .. $ make # make install ``` ### Compilation options The `netopeer2-server` requires *ietf-netconf-server* and all connected YANG modules to be installed in *sysrepo* to work correctly. This is performed automatically during the installation process. Moreover, default SSH configuration listening on all IPv4 interfaces and a newly generated SSH host key are imported so that it can be connected to the server out-of-the-box. However, it may not always be desired to perform all these steps even though the executed scripts check whether the modules/some configuration already exist and do not repeat/overwrite any modules/configuration. These are the options that affect the initial setup: ``` INSTALL_MODULES:ON GENERATE_HOSTKEY:ON MERGE_LISTEN_CONFIG:ON ``` If cross-compiling for a different architecture, you will likey want to turn all these options off and then run the scripts `setup.sh`, `merge_hostkey.sh`, and `merge_config.sh` manually. ### Sysrepo callbacks When implementing a *sysrepo* application with some callbacks, in case the particular event will be generated by *netopeer2*, there will be the NETCONF session ID and NETCONF username of the originator NETCONF session provided. It can be retrieved from the event *sysrepo* session and the originator name will be `netopeer2`. Following is a table with the exact data format. | Index | Type | Meaning | |:----- |:----:|:-------:| | 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 | ### CLI A command-line NETCONF client `netopeer2-cli` is included and build/installed by default. This can be adjusted by an option: ``` BUILD_CLI:ON ``` ### Tests There are several tests included and built with [cmocka](https://cmocka.org/). The tests can be found in `tests` subdirectory and they are designed for checking library functionality after code changes. The tests are by default built in the `Debug` build mode by running ``` $ make ``` In case of the `Release` mode, the tests are not built by default (it requires additional dependency), but they can be enabled via cmake option: ``` $ cmake -DENABLE_TESTS=ON .. ``` Note that if the necessary [cmocka](https://cmocka.org/) headers are not present in the system include paths, tests are not available despite the build mode or cmake's options. Tests can be run by the make's `test` target: ``` $ make test ``` ### Code Coverage Based on the tests run, it is possible to generate code coverage report. But it must be enabled and these commands are needed to generate the report: ``` $ cmake -DENABLE_COVERAGE=ON .. $ make $ 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. ## Server configuration Right after installation SSH listen and Call Home and TLS listen and Call Home are supported. By default, only SSH listen configuration is imported so to enable any other connection methods, they need to be configured manually. Example configuration XML files can be found in the `example_configuration` directory. These files can be easily modified to create configuration specific for a particular environment and configured authentication. ### SSH Call Home To enable SSH Call Home, only `ssh_callhome.xml` file needs to be imported to *sysrepo* provided that the default SSH host key `genkey` was imported into *ietf-keystore* configuration. ### TLS listen To support clients connecting using TLS, configuration files `tls_keystore.xml`, `tls_truststore.xml`, and `tls_listen.xml` needs to be merged into *sysrepo* configuration of modules *ietf-keystore*, *ietf-truststore*, and *ietf-netconf-server*, respectively. After doing so, a NETCONF client can connect using `client.crt` certificate and `client.key` private key and having `ca.pem` CA certificate set as trusted. These example certificates can be found in `example_configuration/tls_certs`. *netopeer2-cli* can easily be configured this way and the TLS connection tested. To pass server identity check, the client must be connecting to `localhost`, which is the default server domain if left empty. Once connected, the client will be identified with `tls-test` NETCONF username. ### TLS Call Home Using the same certificates and authorization options, a TLS client can be connected to using Call Home when `tls_callhome.xml` file is imported. But `tls_keystore.xml` and `tls_truststore.xml` need to be imported first. netopeer2-2.0.35/cli/000077500000000000000000000000001414274114600142575ustar00rootroot00000000000000netopeer2-2.0.35/cli/CMakeLists.txt000066400000000000000000000033511414274114600170210ustar00rootroot00000000000000if(NOT NP2SRV_VERSION) message(FATAL_ERROR "Please use the root CMakeLists file instead.") endif() include(CheckFunctionExists) project(netopeer2-cli C) # set version set(NP2CLI_VERSION 2.0.64) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cli_version.h.in" "${PROJECT_BINARY_DIR}/cli_version.h" ESCAPE_QUOTES @ONLY) include_directories(${PROJECT_BINARY_DIR}) # source files set(CLI_SRC main.c commands.c completion.c configuration.c linenoise/linenoise.c) # netopeer2-cli target add_executable(netopeer2-cli ${CLI_SRC} ${compatsrc}) # reuse server variables target_link_libraries(netopeer2-cli ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(netopeer2-cli ${LIBYANG_LIBRARIES}) target_link_libraries(netopeer2-cli ${LIBNETCONF2_LIBRARIES}) # dependencies - libssh if(LIBNETCONF2_ENABLED_SSH) if(NOT LIBSSH_FOUND) message(FATAL_ERROR "libnetconf2 supports SSH but libssh was not found, CLI compilation failed!") endif() target_link_libraries(netopeer2-cli ${LIBSSH_LIBRARIES}) include_directories(${LIBSSH_INCLUDE_DIRS}) endif() # dependencies - libssl if(LIBNETCONF2_ENABLED_TLS) if(NOT OPENSSL_FOUND) message(FATAL_ERROR "libnetconf2 supports TLS but OpenSSL was not found, CLI compilation failed!") endif() target_link_libraries(netopeer2-cli ${OPENSSL_LIBRARIES}) include_directories(${OPENSSL_INCLUDE_DIR}) endif() check_function_exists(eaccess HAVE_EACCESS) check_function_exists(mkstemps HAVE_MKSTEMPS) if(HAVE_MKSTEMPS) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_MKSTEMPS") endif(HAVE_MKSTEMPS) # install install(TARGETS netopeer2-cli DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/doc/netopeer2-cli.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) netopeer2-2.0.35/cli/README.md000066400000000000000000000024471414274114600155450ustar00rootroot00000000000000# TLS support If TLS is enabled in libnetconf2, the TLS functionality is enabled. However, in order to make it working, you must perform a few initial configuration tasks. ## client certificate With every action that requires verification, you can specify paths to the client certificate to be used. Also, if you do not specify any certificate, the default one will be used. To set it up, use the `cert replaceown` command. ## server certificate verification In order to verify the certificate provided by the server, you need to specify the Certificate Authority certificates you find trustworthy and make them accessible to netopeer-cli. Again, you can explicitly specify the path to a Certificate Authority trusted store, or use the default directory. To add certificates to this directory, use the `cert add` command. ## Certificate Revocation Lists For netopeer-cli to check if a certificate was not revocated by its issuer, use the `crl add` command to provide CRLs of your trusted CAs for netopeer-cli. ## Certificates The `netopeer2/example_configuration/tls_certs` directory includes copies of the needed example client certificates, which will work with the server example certificates. # Scripts The CLI supports some basic scripting and an example `sample_script.sh` is included for illustration. netopeer2-2.0.35/cli/cli_version.h.in000066400000000000000000000007711414274114600173560ustar00rootroot00000000000000/** * @file cli_version.h * @author Radek Krejci * @brief netopeer2-cli version * * @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 CLI_VERSION "@NP2CLI_VERSION@" netopeer2-2.0.35/cli/commands.c000066400000000000000000006174601414274114600162420ustar00rootroot00000000000000/** * @file commands.c * @author Michal Vasko * @brief netopeer2-cli commands * * @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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NC_ENABLED_TLS # include # include #endif #ifndef HAVE_EACCESS #define eaccess access #endif #include "commands.h" #include "compat.h" #include "completion.h" #include "configuration.h" #define CLI_CH_TIMEOUT 60 /* 1 minute */ #define CLI_RPC_REPLY_TIMEOUT 5 /* 5 seconds */ #define NC_CAP_WRITABLERUNNING_ID "urn:ietf:params:netconf:capability:writable-running" #define NC_CAP_CANDIDATE_ID "urn:ietf:params:netconf:capability:candidate" #define NC_CAP_CONFIRMEDCOMMIT_ID "urn:ietf:params:netconf:capability:confirmed-commit:1.1" #define NC_CAP_ROLLBACK_ID "urn:ietf:params:netconf:capability:rollback-on-error" #define NC_CAP_VALIDATE10_ID "urn:ietf:params:netconf:capability:validate:1.0" #define NC_CAP_VALIDATE11_ID "urn:ietf:params:netconf:capability:validate:1.1" #define NC_CAP_STARTUP_ID "urn:ietf:params:netconf:capability:startup" #define NC_CAP_URL_ID "urn:ietf:params:netconf:capability:url" #define NC_CAP_XPATH_ID "urn:ietf:params:netconf:capability:xpath" #define NC_CAP_WITHDEFAULTS_ID "urn:ietf:params:netconf:capability:with-defaults" #define NC_CAP_NOTIFICATION_ID "urn:ietf:params:netconf:capability:notification" #define NC_CAP_INTERLEAVE_ID "urn:ietf:params:netconf:capability:interleave" char some_msg[4096]; COMMAND commands[]; extern int done; LYD_FORMAT output_format = LYD_XML; uint32_t output_flag; char *config_editor; struct nc_session *session; volatile int interleave; int timed; static int cmd_disconnect(const char *arg, char **tmp_config_file); struct arglist { char **list; int count; int size; }; static void init_arglist(struct arglist *args) { if (args != NULL) { args->list = NULL; args->count = 0; args->size = 0; } } static void clear_arglist(struct arglist *args) { int i = 0; if (args && args->list) { for (i = 0; i < args->count; i++) { if (args->list[i]) { free(args->list[i]); } } free(args->list); } init_arglist(args); } static int addargs(struct arglist *args, char *format, ...) { va_list arguments; char *aux = NULL, *aux1 = NULL, *prev_aux, quot; int spaces; if (args == NULL) { return EXIT_FAILURE; } /* store arguments to aux string */ va_start(arguments, format); if (vasprintf(&aux, format, arguments) == -1) { va_end(arguments); ERROR(__func__, "vasprintf() failed (%s)", strerror(errno)); return EXIT_FAILURE; } va_end(arguments); /* remember the begining of the aux string to free it after operations */ aux1 = aux; /* * get word by word from given string and store words separately into * the arglist */ prev_aux = NULL; quot = 0; for (aux = strtok(aux, " \n\t"); aux; prev_aux = aux, aux = strtok(NULL, " \n\t")) { if (!strcmp(aux, "")) { continue; } if (!args->list) { /* initial memory allocation */ if ((args->list = (char **)malloc(8 * sizeof(char *))) == NULL) { ERROR(__func__, "Memory allocation failed (%s:%d)", __FILE__, __LINE__); return EXIT_FAILURE; } args->size = 8; args->count = 0; } else if (!quot && (args->count + 2 >= args->size)) { /* * list is too short to add next to word so we have to * extend it */ args->size += 8; args->list = realloc(args->list, args->size * sizeof(char *)); } if (!quot) { /* add word at the end of the list */ if ((args->list[args->count] = malloc((strlen(aux) + 1) * sizeof(char))) == NULL) { ERROR(__func__, "Memory allocation failed (%s:%d)", __FILE__, __LINE__); return EXIT_FAILURE; } /* quoted argument */ if ((aux[0] == '\'') || (aux[0] == '\"')) { quot = aux[0]; ++aux; /* ...but without spaces */ if (aux[strlen(aux) - 1] == quot) { quot = 0; aux[strlen(aux) - 1] = '\0'; } } strcpy(args->list[args->count], aux); args->list[++args->count] = NULL; /* last argument */ } else { /* append another part of the argument */ spaces = aux - (prev_aux + strlen(prev_aux)); args->list[args->count - 1] = realloc(args->list[args->count - 1], strlen(args->list[args->count - 1]) + spaces + strlen(aux) + 1); /* end of quoted argument */ if (aux[strlen(aux) - 1] == quot) { quot = 0; aux[strlen(aux) - 1] = '\0'; } sprintf(args->list[args->count - 1] + strlen(args->list[args->count - 1]), "%*s%s", spaces, " ", aux); } } /* clean up */ free(aux1); return EXIT_SUCCESS; } static void cli_ntf_clb(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op) { FILE *output = nc_session_get_data(session); int was_rawmode = 0; if (output == stdout) { if (ls.rawmode) { was_rawmode = 1; linenoiseDisableRawMode(ls.ifd); printf("\n"); } else { was_rawmode = 0; } } fprintf(output, "notification (%s)\n", ((struct lyd_node_opaq *)lyd_child(envp))->value); lyd_print_file(output, op, output_format, LYD_PRINT_WITHSIBLINGS | output_flag); fprintf(output, "\n"); fflush(output); if ((output == stdout) && was_rawmode) { linenoiseEnableRawMode(ls.ifd); linenoiseRefreshLine(); } if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) { interleave = 1; } } static int cli_gettimespec(struct timespec *ts, int *mono) { errno = 0; #ifdef CLOCK_MONOTONIC_RAW *mono = 1; return clock_gettime(CLOCK_MONOTONIC_RAW, ts); #elif defined (CLOCK_MONOTONIC) *mono = 1; return clock_gettime(CLOCK_MONOTONIC, ts); #elif defined (CLOCK_REALTIME) /* no monotonic clock available, return realtime */ *mono = 0; return clock_gettime(CLOCK_REALTIME, ts); #else *mono = 0; int rc; struct timeval tv; rc = gettimeofday(&tv, NULL); if (!rc) { ts->tv_sec = (time_t)tv.tv_sec; ts->tv_nsec = 1000L * (long)tv.tv_usec; } return rc; #endif } /* returns milliseconds */ static int32_t cli_difftimespec(const struct timespec *ts1, const struct timespec *ts2) { int64_t nsec_diff = 0; nsec_diff += (((int64_t)ts2->tv_sec) - ((int64_t)ts1->tv_sec)) * 1000000000L; nsec_diff += ((int64_t)ts2->tv_nsec) - ((int64_t)ts1->tv_nsec); return nsec_diff ? nsec_diff / 1000000L : 0; } static int cli_send_recv(struct nc_rpc *rpc, FILE *output, NC_WD_MODE wd_mode, int timeout_s) { char *model_data; int ret = 0, mono; int32_t msec; uint32_t ly_wd; uint64_t msgid; struct lyd_node *envp, *op, *err, *node, *info; struct lyd_node_any *any; NC_MSG_TYPE msgtype; struct timespec ts_start, ts_stop; if (timed) { ret = cli_gettimespec(&ts_start, &mono); if (ret) { ERROR(__func__, "Getting current time failed (%s).", strerror(errno)); return ret; } } msgtype = nc_send_rpc(session, rpc, 1000, &msgid); if (msgtype == NC_MSG_ERROR) { ERROR(__func__, "Failed to send the RPC."); if (nc_session_get_status(session) != NC_STATUS_RUNNING) { cmd_disconnect(NULL, NULL); } return -1; } else if (msgtype == NC_MSG_WOULDBLOCK) { ERROR(__func__, "Timeout for sending the RPC expired."); return -1; } recv_reply: msgtype = nc_recv_reply(session, rpc, msgid, timeout_s * 1000, &envp, &op); if (msgtype == NC_MSG_ERROR) { ERROR(__func__, "Failed to receive a reply."); if (nc_session_get_status(session) != NC_STATUS_RUNNING) { cmd_disconnect(NULL, NULL); } return -1; } else if (msgtype == NC_MSG_WOULDBLOCK) { ERROR(__func__, "Timeout for receiving a reply expired."); return -1; } else if (msgtype == NC_MSG_NOTIF) { /* read again */ goto recv_reply; } else if (msgtype == NC_MSG_REPLY_ERR_MSGID) { /* unexpected message, try reading again to get the correct reply */ ERROR(__func__, "Unexpected reply received - ignoring and waiting for the correct reply."); lyd_free_tree(envp); lyd_free_tree(op); goto recv_reply; } if (timed) { ret = cli_gettimespec(&ts_stop, &mono); if (ret) { ERROR(__func__, "Getting current time failed (%s).", strerror(errno)); goto cleanup; } } if (op) { /* data reply */ if (nc_rpc_get_type(rpc) == NC_RPC_GETSCHEMA) { /* special case */ if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) { ERROR(__func__, "Unexpected data reply to RPC."); ret = -1; goto cleanup; } if (output == stdout) { fprintf(output, "MODULE\n"); } any = (struct lyd_node_any *)lyd_child(op); switch (any->value_type) { case LYD_ANYDATA_STRING: case LYD_ANYDATA_XML: fputs(any->value.str, output); break; case LYD_ANYDATA_DATATREE: lyd_print_mem(&model_data, any->value.tree, LYD_XML, LYD_PRINT_WITHSIBLINGS); fputs(model_data, output); free(model_data); break; default: /* none of the others can appear here */ ERROR(__func__, "Unexpected anydata value format."); ret = -1; goto cleanup; } if (output == stdout) { fprintf(output, "\n"); } } else { /* generic data */ if (output == stdout) { fprintf(output, "DATA\n"); } switch (wd_mode) { case NC_WD_ALL: ly_wd = LYD_PRINT_WD_ALL; break; case NC_WD_ALL_TAG: ly_wd = LYD_PRINT_WD_ALL_TAG; break; case NC_WD_TRIM: ly_wd = LYD_PRINT_WD_TRIM; break; case NC_WD_EXPLICIT: ly_wd = LYD_PRINT_WD_EXPLICIT; break; default: ly_wd = 0; break; } lyd_print_file(output, lyd_child(op), output_format, LYD_PRINT_WITHSIBLINGS | ly_wd | output_flag); if (output == stdout) { fprintf(output, "\n"); } } } else if (!strcmp(LYD_NAME(lyd_child(envp)), "ok")) { /* ok reply */ fprintf(output, "OK\n"); } else { assert(!strcmp(LYD_NAME(lyd_child(envp)), "rpc-error")); fprintf(output, "ERROR\n"); LY_LIST_FOR(lyd_child(envp), err) { lyd_find_sibling_opaq_next(lyd_child(err), "error-type", &node); if (node) { fprintf(output, "\ttype: %s\n", ((struct lyd_node_opaq *)node)->value); } lyd_find_sibling_opaq_next(lyd_child(err), "error-tag", &node); if (node) { fprintf(output, "\ttag: %s\n", ((struct lyd_node_opaq *)node)->value); } lyd_find_sibling_opaq_next(lyd_child(err), "error-severity", &node); if (node) { fprintf(output, "\tseverity: %s\n", ((struct lyd_node_opaq *)node)->value); } lyd_find_sibling_opaq_next(lyd_child(err), "error-app-tag", &node); if (node) { fprintf(output, "\tapp-tag: %s\n", ((struct lyd_node_opaq *)node)->value); } lyd_find_sibling_opaq_next(lyd_child(err), "error-path", &node); if (node) { fprintf(output, "\tpath: %s\n", ((struct lyd_node_opaq *)node)->value); } lyd_find_sibling_opaq_next(lyd_child(err), "error-message", &node); if (node) { fprintf(output, "\tmessage: %s\n", ((struct lyd_node_opaq *)node)->value); } info = lyd_child(err); while (!lyd_find_sibling_opaq_next(info, "error-info", &info)) { fprintf(output, "\tinfo:\n"); lyd_print_file(stdout, lyd_child(info), LYD_XML, LYD_PRINT_WITHSIBLINGS); info = info->next; } fprintf(output, "\n"); } ret = 1; } if (msgtype == NC_MSG_REPLY_ERR_MSGID) { ERROR(__func__, "Trying to receive another message...\n"); lyd_free_tree(envp); lyd_free_tree(op); goto recv_reply; } if (timed) { msec = cli_difftimespec(&ts_start, &ts_stop); fprintf(output, "%s %2dm%d.%03ds\n", mono ? "mono" : "real", msec / 60000, (msec % 60000) / 1000, msec % 1000); } cleanup: lyd_free_tree(envp); lyd_free_tree(op); return ret; } static char * trim_top_elem(char *data, const char *top_elem, const char *top_elem_ns) { char *ptr, *prefix = NULL, *buf; int pref_len = 0, state = 0, quote, rc; /* state: -2 - syntax error, * -1 - top_elem not found, * 0 - start, * 1 - parsing prefix, * 2 - prefix just parsed, * 3 - top-elem found and parsed, looking for namespace, * 4 - top_elem and top_elem_ns found (success) */ if (!data) { return NULL; } while (isspace(data[0])) { ++data; } if (data[0] != '<') { return data; } for (ptr = data + 1; (ptr[0] != '\0') && (ptr[0] != '>'); ++ptr) { switch (state) { case 0: if (!strncmp(ptr, top_elem, strlen(top_elem))) { state = 3; ptr += strlen(top_elem); } else if ((ptr[0] != ':') && !isdigit(ptr[0])) { state = 1; prefix = ptr; pref_len = 1; } else { state = -1; } break; case 1: if (ptr[0] == ':') { /* prefix parsed */ state = 2; } else if (ptr[0] != ' ') { ++pref_len; } else { state = -1; } break; case 2: if (!strncmp(ptr, top_elem, strlen(top_elem))) { state = 3; ptr += strlen(top_elem); } else { state = -1; } break; case 3: if (!strncmp(ptr, "xmlns", 5)) { ptr += 5; if (prefix) { if ((ptr[0] != ':') || strncmp(ptr + 1, prefix, pref_len) || (ptr[1 + pref_len] != '=')) { /* it's not the right prefix, look further */ break; } /* we found our prefix, does the namespace match? */ ptr += 1 + pref_len; } if (ptr[0] != '=') { if (prefix) { /* fail for sure */ state = -1; } else { /* it may not be xmlns attribute, but something longer... */ } break; } ++ptr; if ((ptr[0] != '\"') && (ptr[0] != '\'')) { state = -2; break; } quote = ptr[0]; ++ptr; if (strncmp(ptr, top_elem_ns, strlen(top_elem_ns))) { if (prefix) { state = -1; } break; } ptr += strlen(top_elem_ns); if (ptr[0] != quote) { if (prefix) { state = -1; } break; } /* success */ ptr = strchrnul(ptr, '>'); state = 4; } break; } if ((state < 0) || (state == 4)) { break; } } if ((state == -2) || (ptr[0] == '\0')) { return NULL; } else if (state != 4) { return data; } /* skip the first elem, ... */ ++ptr; while (isspace(ptr[0])) { ++ptr; } data = ptr; /* ... but also its ending tag */ if (prefix) { rc = asprintf(&buf, "", pref_len, prefix, top_elem); } else { rc = asprintf(&buf, "", top_elem); } if (rc == -1) { return NULL; } ptr = strstr(data, buf); if (!ptr) { /* syntax error */ free(buf); return NULL; } else { /* reuse it */ prefix = ptr; } ptr += strlen(buf); free(buf); while (isspace(ptr[0])) { ++ptr; } if (ptr[0] != '\0') { /* there should be nothing more */ return NULL; } /* ending tag and all syntax seems fine, so cut off the ending tag */ while (isspace(prefix[-1]) && (prefix > data)) { --prefix; } prefix[0] = '\0'; return data; } static void cmd_searchpath_help(void) { printf("searchpath []\n"); } static void cmd_outputformat_help(void) { printf("outputformat (xml | xml_noformat | json | json_noformat)\n"); } static void cmd_verb_help(void) { printf("verb (error/0 | warning/1 | verbose/2 | debug/3)\n"); } static void cmd_connect_help(void) { #if defined (NC_ENABLED_SSH) && defined (NC_ENABLED_TLS) printf("connect [--help] [--ssh] [--host ] [--port ] [--login ]\n"); printf("connect [--help] --tls [--host ] [--port ] [--cert [--key ]] [--trusted ]\n"); #elif defined (NC_ENABLED_SSH) printf("connect [--help] [--ssh] [--host ] [--port ] [--login ]\n"); #elif defined (NC_ENABLED_TLS) printf("connect [--help] [--tls] [--host ] [--port ] [--cert [--key ]] [--trusted ]\n"); #endif printf("connect [--help] --unix [--socket ]\n"); } static void cmd_listen_help(void) { #if defined (NC_ENABLED_SSH) && defined (NC_ENABLED_TLS) printf("listen [--help] [--timeout ] [--host ] [--port ]\n"); printf(" SSH [--ssh] [--login ]\n"); printf(" TLS --tls [--cert [--key ]] [--trusted ]\n"); #elif defined (NC_ENABLED_SSH) printf("listen [--help] [--ssh] [--timeout ] [--host ] [--port ] [--login ]\n"); #elif defined (NC_ENABLED_TLS) printf("listen [--help] [--tls] [--timeout ] [--host ] [--port ] [--cert [--key ]] [--trusted ]\n"); #endif } static void cmd_editor_help(void) { printf("editor [--help] []\n"); } static void cmd_cancelcommit_help(void) { if (session && !nc_session_cpblt(session, NC_CAP_CONFIRMEDCOMMIT_ID)) { printf("cancel-commit is not supported by the current session.\n"); } else { printf("cancel-commit [--help] [--persist-id ] [--rpc-timeout ]\n"); } } static void cmd_commit_help(void) { const char *confirmed; if (session && !nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { printf("commit is not supported by the current session.\n"); return; } if (!session || nc_session_cpblt(session, NC_CAP_CONFIRMEDCOMMIT_ID)) { confirmed = " [--confirmed] [--confirm-timeout ] [--persist ] [--persist-id ]"; } else { confirmed = ""; } printf("commit [--help]%s [--rpc-timeout ]\n", confirmed); } static void cmd_copyconfig_help(void) { int ds = 0; const char *running, *startup, *candidate, *url, *defaults; if (!session) { /* if session not established, print complete help for all capabilities */ running = "running"; startup = "|startup"; candidate = "|candidate"; url = "|url:"; defaults = " [--defaults report-all|report-all-tagged|trim|explicit]"; } else { if (nc_session_cpblt(session, NC_CAP_WRITABLERUNNING_ID)) { running = "running"; ds = 1; } else { running = ""; } if (nc_session_cpblt(session, NC_CAP_STARTUP_ID)) { if (ds) { startup = "|startup"; } else { startup = "startup"; ds = 1; } } else { startup = ""; } if (nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { if (ds) { candidate = "|candidate"; } else { candidate = "candidate"; ds = 1; } } else { candidate = ""; } if (nc_session_cpblt(session, NC_CAP_URL_ID)) { if (ds) { url = "|url:"; } else { url = "url:"; ds = 1; } } else { url = ""; } if (!ds) { printf("copy-config is not supported by the current session.\n"); return; } if (nc_session_cpblt(session, NC_CAP_WITHDEFAULTS_ID)) { defaults = " [--defaults report-all|report-all-tagged|trim|explicit]"; } else { defaults = ""; } } printf("copy-config [--help] --target %s%s%s%s (--source %s%s%s%s | --src-config[=])%s [--rpc-timeout ]\n", running, startup, candidate, url, running, startup, candidate, url, defaults); } static void cmd_deleteconfig_help(void) { const char *startup, *url; if (!session) { startup = "startup"; url = "|url:"; } else { if (nc_session_cpblt(session, NC_CAP_STARTUP_ID)) { startup = "startup"; } else { startup = ""; } if (nc_session_cpblt(session, NC_CAP_URL_ID)) { url = strlen(startup) ? "|url:" : "url:"; } else { url = ""; } } if ((strlen(startup) + strlen(url)) == 0) { printf("delete-config is not supported by the current session.\n"); return; } printf("delete-config [--help] --target %s%s [--rpc-timeout ]\n", startup, url); } static void cmd_discardchanges_help(void) { if (!session || nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { printf("discard-changes [--help] [--rpc-timeout ]\n"); } else { printf("discard-changes is not supported by the current session.\n"); } } static void cmd_editconfig_help(void) { const char *rollback, *validate, *running, *candidate, *url, *bracket; if (!session || nc_session_cpblt(session, NC_CAP_WRITABLERUNNING_ID)) { running = "running"; } else { running = ""; } if (!session || nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { if (running[0]) { candidate = "|candidate"; } else { candidate = "candidate"; } } else { candidate = ""; } if (!running[0] && !candidate[0]) { printf("edit-config is not supported by the current session.\n"); return; } if (!session || nc_session_cpblt(session, NC_CAP_ROLLBACK_ID)) { rollback = "|rollback"; } else { rollback = ""; } if (!session || nc_session_cpblt(session, NC_CAP_VALIDATE11_ID)) { validate = "[--test set|test-only|test-then-set] "; } else if (!session || nc_session_cpblt(session, NC_CAP_VALIDATE10_ID)) { validate = "[--test set|test-then-set] "; } else { validate = ""; } if (!session || nc_session_cpblt(session, NC_CAP_URL_ID)) { url = " | --url )"; bracket = "("; } else { url = ""; bracket = ""; } printf("edit-config [--help] --target %s%s %s--config[=]%s [--defop merge|replace|none] " "%s[--error stop|continue%s] [--rpc-timeout ]\n", running, candidate, bracket, url, validate, rollback); } static void cmd_get_help(void) { const char *defaults, *xpath; if (!session || nc_session_cpblt(session, NC_CAP_WITHDEFAULTS_ID)) { defaults = "[--defaults report-all|report-all-tagged|trim|explicit] "; } else { defaults = ""; } if (!session || nc_session_cpblt(session, NC_CAP_XPATH_ID)) { xpath = " | --filter-xpath "; } else { xpath = ""; } fprintf(stdout, "get [--help] [--filter-subtree[=]%s] %s[--out ] [--rpc-timeout ]\n", xpath, defaults); } static void cmd_getconfig_help(void) { const char *defaults, *xpath, *candidate, *startup; /* if session not established, print complete help for all capabilities */ if (!session || nc_session_cpblt(session, NC_CAP_WITHDEFAULTS_ID)) { defaults = "[--defaults report-all|report-all-tagged|trim|explicit] "; } else { defaults = ""; } if (!session || nc_session_cpblt(session, NC_CAP_XPATH_ID)) { xpath = " | --filter-xpath "; } else { xpath = ""; } if (!session || nc_session_cpblt(session, NC_CAP_STARTUP_ID)) { startup = "|startup"; } else { startup = ""; } if (!session || nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { candidate = "|candidate"; } else { candidate = ""; } printf("get-config [--help] --source running%s%s [--filter-subtree[=]%s] %s[--out ] [--rpc-timeout ]\n", startup, candidate, xpath, defaults); } static void cmd_killsession_help(void) { printf("killsession [--help] --sid [--rpc-timeout ]\n"); } static void cmd_lock_help(void) { const char *candidate, *startup; if (!session || nc_session_cpblt(session, NC_CAP_STARTUP_ID)) { startup = "|startup"; } else { startup = ""; } if (!session || nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { candidate = "|candidate"; } else { candidate = ""; } printf("lock [--help] --target running%s%s [--rpc-timeout ]\n", startup, candidate); } static void cmd_unlock_help(void) { const char *candidate, *startup; if (!session || nc_session_cpblt(session, NC_CAP_STARTUP_ID)) { startup = "|startup"; } else { startup = ""; } if (!session || nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { candidate = "|candidate"; } else { candidate = ""; } printf("unlock [--help] --target running%s%s [--rpc-timeout ]\n", startup, candidate); } static void cmd_validate_help(void) { const char *startup, *candidate, *url; if (session && !nc_session_cpblt(session, NC_CAP_VALIDATE10_ID) && !nc_session_cpblt(session, NC_CAP_VALIDATE11_ID)) { printf("validate is not supported by the current session.\n"); return; } if (!session) { /* if session not established, print complete help for all capabilities */ startup = "|startup"; candidate = "|candidate"; url = "|url:"; } else { if (nc_session_cpblt(session, NC_CAP_STARTUP_ID)) { startup = "|startup"; } else { startup = ""; } if (nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { candidate = "|candidate"; } else { candidate = ""; } if (nc_session_cpblt(session, NC_CAP_URL_ID)) { url = "|url:"; } else { url = ""; } } printf("validate [--help] (--source running%s%s%s | --src-config[=]) [--rpc-timeout ]\n", startup, candidate, url); } static void cmd_subscribe_help(void) { const char *xpath; if (session && !nc_session_cpblt(session, NC_CAP_NOTIFICATION_ID)) { printf("subscribe not supported by the current session.\n"); return; } if (!session || nc_session_cpblt(session, NC_CAP_XPATH_ID)) { xpath = " | --filter-xpath "; } else { xpath = ""; } printf("subscribe [--help] [--filter-subtree[=]%s] [--begin