From 39bf57d5dfda4b845fbb02261119f97f5818fd2a Mon Sep 17 00:00:00 2001 From: Michal Vasko Date: Tue, 30 Apr 2024 16:07:46 +0200 Subject: [PATCH] np2 sr setup NEW library for setup.sh ... installing all the required SR modules. Useful for cross-compilation. --- CMakeLists.txt | 3 + lib/CMakeLists.txt | 28 +++++++ lib/generate.sh | 112 ++++++++++++++++++++++++++ lib/np2_sr_setup.c | 192 +++++++++++++++++++++++++++++++++++++++++++++ lib/np2_sr_setup.h | 35 +++++++++ 5 files changed, 370 insertions(+) create mode 100644 lib/CMakeLists.txt create mode 100755 lib/generate.sh create mode 100644 lib/np2_sr_setup.c create mode 100644 lib/np2_sr_setup.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d0b1895..e2c3d6e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -421,6 +421,9 @@ if(BUILD_CLI) add_subdirectory(cli) endif() +# np2 sysrepo setup lib +add_subdirectory(lib) + # source files to be covered by the 'format' target and a test with 'format-check' target source_format(${FORMAT_SRC}) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 00000000..1908012e --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,28 @@ +if(NOT NP2SRV_VERSION) + message(FATAL_ERROR "Please use the root CMakeLists file instead.") +endif() + +project(np2_sr_setup C) + +# source files +set(LIB_SRC + np2_sr_setup.c) + +# generate YANG header files +add_custom_command(OUTPUT np2_sr_yang.h + COMMAND ${CMAKE_COMMAND} -E env + NP2_MODULE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/../modules + LN2_MODULE_DIR=${LN2_YANG_MODULE_DIR} + NP2_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/generate.sh + COMMENT "Generating YANG header files (generate.sh)..." +) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +# lib target +add_library(np2_sr_setup ${LIB_SRC} np2_sr_yang.h) + +# reuse server variables +target_link_libraries(np2_sr_setup ${LIBYANG_LIBRARIES}) +target_link_libraries(np2_sr_setup ${SYSREPO_LIBRARIES}) diff --git a/lib/generate.sh b/lib/generate.sh new file mode 100755 index 00000000..b3b8bb86 --- /dev/null +++ b/lib/generate.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash + +if [ -z "$NP2_MODULE_DIR" -o -z "$LN2_MODULE_DIR" -o -z "$NP2_BINARY_DIR" ]; then + echo "Required environment variables not defined!" + exit 1 +fi + + +# start the YANG array +MAIN_YANG_ARRAY=" +struct { + const char *file; + const char *data; + int len; +} yang_files[] = { +" + +# generate headers from all the YANG modules +NP2_MODDIR=${DESTDIR}${NP2_MODULE_DIR} +LN2_MODDIR=${DESTDIR}${LN2_MODULE_DIR} +BINDIR=${DESTDIR}${NP2_BINARY_DIR} +for YANG_PATH in ${NP2_MODDIR}/*.yang ${LN2_MODDIR}/*.yang; do + # get module name + YANG_FILE="$(basename "${YANG_PATH}")" + + if [[ "$MAIN_YANG_ARRAY" =~ "\"$YANG_FILE\"" ]]; then + # duplicate module + continue + fi + + # generate HEX + HEX=$(echo "$(cat "${YANG_PATH}")" | xxd -i -c1) + LENGTH=$((${#HEX}/8)) + + # generate array name + ARRAY_NAME="$(echo "${YANG_FILE}" | tr -- "-@." "_")" + + # generate header file name without the revision + HEADER_FILE="${ARRAY_NAME}.h" + + # print into a C header file + echo -e "const char ${ARRAY_NAME}[] = {\n$HEX\n};\nconst int ${ARRAY_NAME}_l = ${LENGTH};" > "${BINDIR}/${HEADER_FILE}" + + # build all the include lines in the main header + MAIN_INCLUDE_LINES="${MAIN_INCLUDE_LINES}#include \"${HEADER_FILE}\"\n" + + # build the array of modules + MAIN_YANG_ARRAY="${MAIN_YANG_ARRAY} {.file = \"${YANG_FILE}\", .data = ${ARRAY_NAME}, .len = ${ARRAY_NAME}_l},\n" +done + +# end the YANG array +MAIN_YANG_ARRAY="${MAIN_YANG_ARRAY} {.file = NULL, .data = NULL}\n};\n\n" + + +# import module arrays +cur_dir=$(dirname "$0") +source "${cur_dir}/../scripts/common.sh" + +# start install and feature arrays +MAIN_FEATURE_ARRAY="const char **yang_features[] = {\n" +MAIN_INSTALL_ARRAY="const char *yang_install[] = {\n" +COUNT=0 + +# generate install and features arrays +for LINE in "${NP2_MODULES[@]}" "${LN2_MODULES[@]}"; do + ((COUNT+=1)) + + # get file and array name + FILE="$(echo "$LINE" | sed 's/\([^ ]*\).*/\1/')" + ARRAY_NAME="$(echo "${FILE}" | tr -- "-@." "_")_f" + + # generate feature array + HAS_FEATURES=$(echo "$LINE" | grep " -e ") + if [ -z "$HAS_FEATURES" ]; then + FEATURES="NULL" + else + FEATURES="${ARRAY_NAME}" + MOD_FEATURES="const char *${ARRAY_NAME}[] = {" + LINE=$(echo "$LINE" | sed 's/[^ ]* \(.*\)/\1/') + while [ "${LINE:0:3}" = "-e " ]; do + # skip "-e " + LINE=${LINE:3} + # parse feature + FEATURE=$(echo "$LINE" | sed 's/\([^ ]*\).*/\1/') + + MOD_FEATURES="$MOD_FEATURES\"$FEATURE\", " + + # next iteration, skip this feature + LINE=$(echo "$LINE" | sed 's/[^ ]* \(.*\)/\1/') + done + MOD_FEATURES="${MOD_FEATURES}NULL}" + + MAIN_FEATURE_MODS="${MAIN_FEATURE_MODS}${MOD_FEATURES};\n" + fi + + MAIN_FEATURE_ARRAY="${MAIN_FEATURE_ARRAY} ${FEATURES},\n" + MAIN_INSTALL_ARRAY="${MAIN_INSTALL_ARRAY} \"${FILE}\",\n" +done + +# end install and feature array +MAIN_FEATURE_ARRAY="${MAIN_FEATURE_ARRAY}};\n\n" +MAIN_INSTALL_ARRAY="${MAIN_INSTALL_ARRAY}};\n" + +MAIN_INSTALL_COUNT="int yang_install_count = ${COUNT};" + +# generate the main header +echo -e "#include \n\n${MAIN_INCLUDE_LINES}\ +${MAIN_YANG_ARRAY}\ +${MAIN_FEATURE_MODS}\ +${MAIN_FEATURE_ARRAY}\ +${MAIN_INSTALL_ARRAY}\ +${MAIN_INSTALL_COUNT}" > "${BINDIR}/np2_sr_yang.h" diff --git a/lib/np2_sr_setup.c b/lib/np2_sr_setup.c new file mode 100644 index 00000000..f399e96b --- /dev/null +++ b/lib/np2_sr_setup.c @@ -0,0 +1,192 @@ +/** + * @file np2_sr_setup.c + * @author Michal Vasko + * @brief netopeer2-server sysrepo YANG module setup library + * + * @copyright + * Copyright (c) 2024 Deutsche Telekom AG. + * Copyright (c) 2024 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 "np2_sr_yang.h" + +#define ERR(msg, ...) fprintf(stderr, msg "\n", __VA_ARGS__) +#define ERRMSG(msg) fprintf(stderr, msg "\n") + +/** + * @brief Check a module in the specific revision and all the features is not already installed. + * + * @param[in] file YANG module file name. + * @param[in] features Array of enabled features. + * @param[in] conn Sysrepo connection to use. + * @param[out] processed Whether the module needs tobe installed or was processed. + * @return 0 on success. + * @return non-zero on error. + */ +static int +np2_sr_setup_mod_check(const char *file, const char **features, sr_conn_ctx_t *conn, int *processed) +{ + int rc = 0, i, r; + const struct ly_ctx *ctx = sr_acquire_context(conn); + const struct lys_module *mod; + const char *ptr; + char *name = NULL, *revision = NULL; + LY_ERR lyrc; + + *processed = 0; + + /* parse name and revision */ + ptr = strchr(file, '@'); + name = strndup(file, ptr - file); + ++ptr; + revision = strndup(ptr, strlen(ptr) - 5); + if (!name || !revision) { + ERRMSG("Failed to allocate memory."); + rc = 1; + goto cleanup; + } + + /* check the file is installed */ + mod = ly_ctx_get_module_implemented(ctx, name); + if (!mod) { + goto cleanup; + } + if (!revision || strcmp(mod->revision, revision)) { + /* different revision, will fail during installation */ + goto cleanup; + } + + /* we will adjust enabled features */ + *processed = 1; + + if (!features) { + goto cleanup; + } + + /* check/enable all the features */ + for (i = 0; features[i]; ++i) { + lyrc = lys_feature_value(mod, features[i]); + if (lyrc == LY_ENOTFOUND) { + ERR("Failed to find feature \"%s\" in \"%s\".", features[i], name); + rc = 1; + goto cleanup; + } + + if (lyrc == LY_ENOT) { + /* enable feature, context will be changed */ + sr_release_context(conn); + r = sr_enable_module_feature(conn, name, features[i]); + ctx = sr_acquire_context(conn); + + if (r) { + ERR("Failed to enable feature \"%s\" in \"%s\".", features[i], name); + rc = 1; + goto cleanup; + } + + mod = ly_ctx_get_module_implemented(ctx, name); + } + } + +cleanup: + sr_release_context(conn); + free(name); + free(revision); + return rc; +} + +int +np2_sr_setup(const char *owner, const char *group, mode_t perm) +{ + int rc = 0, fd, r, i, mod_count; + sr_conn_ctx_t *conn = NULL; + sr_install_mod_t *mods = NULL; + + /* log */ + sr_log_stderr(SR_LL_WRN); + + /* connect */ + if (sr_connect(0, &conn)) { + ERRMSG("Failed to connect to sysrepo."); + rc = 1; + goto cleanup; + } + + /* print all the modules into files */ + for (i = 0; yang_files[i].file; ++i) { + fd = open(yang_files[i].file, O_WRONLY | O_CREAT | O_TRUNC, 00600); + if (fd < 0) { + ERR("Failed to create \"%s\".", yang_files[i].file); + rc = 1; + goto cleanup; + } + + r = write(fd, yang_files[i].data, yang_files[i].len); + close(fd); + + if (r != yang_files[i].len) { + ERR("Failed to write \"%s\".", yang_files[i].file); + rc = 1; + goto cleanup; + } + } + + /* prepare modules to install */ + mods = calloc(yang_install_count, sizeof *mods); + if (!mods) { + ERRMSG("Failed to allocate memory."); + rc = 1; + goto cleanup; + } + mod_count = 0; + for (i = 0; i < yang_install_count; ++i) { + /* first check that the module is not installed already and all its features enabled */ + if (np2_sr_setup_mod_check(yang_install[i], yang_features[i], conn, &r)) { + rc = 1; + goto cleanup; + } + if (r) { + continue; + } + + mods[mod_count].schema_path = yang_install[i]; + mods[mod_count].features = yang_features[i]; + mods[mod_count].owner = owner; + mods[mod_count].group = group; + mods[mod_count].perm = perm; + + ++mod_count; + } + + /* install modules */ + if (sr_install_modules2(conn, mods, mod_count, ".", NULL, NULL, 0)) { + ERRMSG("Failed to install modules."); + rc = 1; + goto cleanup; + } + +cleanup: + free(mods); + sr_disconnect(conn); + for (i = 0; yang_files[i].file; ++i) { + unlink(yang_files[i].file); + } + return rc; +} diff --git a/lib/np2_sr_setup.h b/lib/np2_sr_setup.h new file mode 100644 index 00000000..0fc8eab2 --- /dev/null +++ b/lib/np2_sr_setup.h @@ -0,0 +1,35 @@ +/** + * @file np2_sr_setup.h + * @author Michal Vasko + * @brief netopeer2-server sysrepo YANG module setup library header + * + * @copyright + * Copyright (c) 2024 Deutsche Telekom AG. + * Copyright (c) 2024 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 NP2_SR_SETUP_H_ +#define NP2_SR_SETUP_H_ + +#include + +/** + * @brief Install all YANG modules required by netopeer2-server into sysrepo. + * + * Logs to stderr. + * + * @param[in] owner Optional owner of the installed modules, process user by default. + * @param[in] group Optional group of the installed modules, process group or configured sysrepo group by default. + * @param[in] perm Optional specific permissions of the installed modules. + * @return 0 on success. + * @return non-zero on error. + */ +int np2_sr_setup(const char *owner, const char *group, mode_t perm); + +#endif /* NP2_SR_SETUP_H_ */