diff --git a/.github/workflows/frr-checks.yml b/.github/workflows/frr-checks.yml
new file mode 100644
index 000000000..63ebad8c7
--- /dev/null
+++ b/.github/workflows/frr-checks.yml
@@ -0,0 +1,149 @@
+name: libyang+FRR HEAD CI
+run-name: libyang CI FRR ${{ github.actor }} ⚗️
+on:
+ workflow_dispatch:
+ inputs:
+ frr-versions:
+ description: 'FRRouting version'
+ required: true
+ default: 'master'
+ type: choice
+ options:
+ - master
+ - pull/15608/head
+ - frr-10.0
+ - frr-9.1
+ - frr-9.0.2
+ - frr-8.5.4
+ libyang-versions:
+ description: 'libyang version'
+ required: true
+ default: 'devel'
+ type: choice
+ options:
+ - devel
+ - v2.1.148
+ - v2.1.128
+# schedule:
+# # every night at 1.10
+# - cron: '10 1 * * *'
+# the following in pending for fixes per the comments of pr !2203
+# push:
+# branches:
+# - '**'
+# pull_request:
+# branches:
+# - '**'
+jobs:
+ build-frr:
+ name: Build FRR
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ compiler: [ gcc ]
+# frr-versions:
+# - frr-9.1
+# - frr-9.0.2
+# - frr-8.5.4
+# libyang-versions:
+# - v2.1.128
+ steps:
+ - name: add missing packages per building-frr-for-ubuntu2204
+ uses: ConorMacBride/install-package@v1
+ with:
+ apt:
+ git
+ autoconf
+ libtool
+ make
+ libreadline-dev
+ texinfo
+ pkg-config
+ libelf-dev
+ libpam0g-dev
+ libjson-c-dev
+ bison
+ flex
+ libc-ares-dev
+ python3-dev
+ python3-sphinx
+ install-info
+ build-essential
+ libsnmp-dev
+ perl
+ libcap-dev
+ libelf-dev
+ libunwind-dev
+ protobuf-c-compiler
+ libprotobuf-c-dev
+ libgrpc++-dev
+ protobuf-compiler-grpc
+ libsqlite3-dev
+ libzmq5
+ libzmq3-dev
+ python3-pytest
+ - name: libyang ${{ inputs.libyang-versions }} ${{ matrix.compiler }}
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ inputs.libyang-versions }}
+ submodules: false
+ fetch-depth: 0
+ filter: tree:0
+ fetch-tags: true
+ - name: make libyang from upstream
+ run: >-
+ git branch &&
+ mkdir build &&
+ cd build &&
+ export CC=${{ matrix.compiler }} &&
+ cmake -DCMAKE_BUILD_TYPE:String="Release" .. &&
+ make -j $(nproc) &&
+ sudo make install
+ - name: Add FRR user and groups
+ run: >-
+ sudo groupadd -r -g 92 frr &&
+ sudo groupadd -r -g 85 frrvty &&
+ sudo adduser --system --ingroup frr --home /var/run/frr/ --gecos "FRR suite" --shell /sbin/nologin frr &&
+ sudo usermod -a -G frrvty frr
+ - name: FRR github checkout
+ uses: actions/checkout@v4
+ with:
+ repository: 'FRRouting/frr.git'
+ ref: ${{ inputs.frr-versions }}
+ submodules: false
+ fetch-depth: 0
+ filter: tree:0
+ fetch-tags: true
+ - name: compile FRR with ${{ inputs.libyang-versions }} ${{ matrix.compiler }}
+ if: ${{ always() }}
+ run: >-
+ ls -la &&
+ export CC=${{ matrix.compiler }} &&
+ ./bootstrap.sh &&
+ ./configure \
+ --prefix=/usr \
+ --includedir=\${prefix}/include \
+ --bindir=\${prefix}/bin \
+ --sbindir=\${prefix}/lib/frr \
+ --libdir=\${prefix}/lib/frr \
+ --libexecdir=\${prefix}/lib/frr \
+ --sysconfdir=/etc \
+ --localstatedir=/var \
+ --with-moduledir=\${prefix}/lib/frr/modules \
+ --enable-configfile-mask=0640 \
+ --enable-logfile-mask=0640 \
+ --enable-vtysh \
+ --enable-pimd \
+ --enable-pim6d \
+ --enable-sharpd \
+ --enable-snmp=agentx \
+ --enable-multipath=64 \
+ --enable-user=frr \
+ --enable-group=frr \
+ --enable-vty-group=frrvty \
+ --with-pkg-git-version \
+ --with-pkg-extra-version=-MyOwnFRRVersion &&
+ make -j $(nproc) &&
+ sudo make install &&
+ cd tests/topotests &&
+ cd all_protocol_startup && pytest-3 -s -v
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b89fb04de..c80695197 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -60,12 +60,12 @@ set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
# set version of the project
set(LIBYANG_MAJOR_VERSION 2)
set(LIBYANG_MINOR_VERSION 2)
-set(LIBYANG_MICRO_VERSION 8)
+set(LIBYANG_MICRO_VERSION 10)
set(LIBYANG_VERSION ${LIBYANG_MAJOR_VERSION}.${LIBYANG_MINOR_VERSION}.${LIBYANG_MICRO_VERSION})
# set version of the library
set(LIBYANG_MAJOR_SOVERSION 3)
set(LIBYANG_MINOR_SOVERSION 0)
-set(LIBYANG_MICRO_SOVERSION 8)
+set(LIBYANG_MICRO_SOVERSION 10)
set(LIBYANG_SOVERSION_FULL ${LIBYANG_MAJOR_SOVERSION}.${LIBYANG_MINOR_SOVERSION}.${LIBYANG_MICRO_SOVERSION})
set(LIBYANG_SOVERSION ${LIBYANG_MAJOR_SOVERSION})
@@ -78,7 +78,7 @@ else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wpedantic -std=c11")
endif()
-include_directories(${PROJECT_BINARY_DIR}/src ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/plugins_exts)
+include_directories(${PROJECT_BINARY_DIR}/libyang ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/plugins_exts)
# type plugins are separate because they have their documentation generated
set(type_plugins
@@ -205,8 +205,8 @@ set(internal_headers
src/xpath.h)
set(gen_headers
- src/version.h
- src/ly_config.h)
+ version.h
+ ly_config.h)
# files to generate doxygen from
set(doxy_files
@@ -214,7 +214,7 @@ set(doxy_files
doc/transition_1_2.dox
doc/transition_2_3.dox
${headers}
- ${PROJECT_BINARY_DIR}/src/version.h
+ ${PROJECT_BINARY_DIR}/libyang/version.h
${type_plugins})
# project (doxygen) logo
@@ -246,6 +246,7 @@ option(ENABLE_FUZZ_TARGETS "Build target programs suitable for fuzzing with AFL"
option(ENABLE_INTERNAL_DOCS "Generate doxygen documentation also from internal headers" OFF)
option(ENABLE_YANGLINT_INTERACTIVE "Enable interactive CLI yanglint" ON)
option(ENABLE_TOOLS "Build binary tools 'yanglint' and 'yangre'" ON)
+option(ENABLE_COMMON_TARGETS "Define common custom target names such as 'doc' or 'uninstall', may cause conflicts when using add_subdirectory() to build this project" ON)
option(BUILD_SHARED_LIBS "By default, shared libs are enabled. Turn off for a static build." ON)
set(YANG_MODULE_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/yang/modules/libyang" CACHE STRING "Directory where to copy the YANG modules to")
@@ -321,9 +322,10 @@ if ("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG")
source_format_enable(0.77)
endif()
-# generate files
-configure_file(${PROJECT_SOURCE_DIR}/src/ly_config.h.in ${PROJECT_BINARY_DIR}/src/ly_config.h @ONLY)
-configure_file(${PROJECT_SOURCE_DIR}/src/version.h.in ${PROJECT_BINARY_DIR}/src/version.h @ONLY)
+# generate and copy all public header files
+configure_file(${PROJECT_SOURCE_DIR}/src/ly_config.h.in ${PROJECT_BINARY_DIR}/libyang/ly_config.h @ONLY)
+configure_file(${PROJECT_SOURCE_DIR}/src/version.h.in ${PROJECT_BINARY_DIR}/libyang/version.h @ONLY)
+file(COPY ${headers} DESTINATION ${PROJECT_BINARY_DIR}/libyang)
# DOC-only target with no extra dependencies
if("${BUILD_TYPE_UPPER}" STREQUAL "DOCONLY")
@@ -409,7 +411,7 @@ target_link_libraries(yang ${PCRE2_LIBRARIES})
# generated header list
foreach(h IN LISTS gen_headers)
- list(APPEND g_headers ${PROJECT_BINARY_DIR}/${h})
+ list(APPEND g_headers ${PROJECT_BINARY_DIR}/libyang/${h})
endforeach()
# install the modules
@@ -462,10 +464,12 @@ if(ENABLE_TOOLS)
endif()
# generate doxygen documentation for libyang API
-gen_doc("${doxy_files}" ${LIBYANG_VERSION} ${LIBYANG_DESCRIPTION} ${project_logo})
+if(ENABLE_COMMON_TARGETS)
+ gen_doc("${doxy_files}" ${LIBYANG_VERSION} ${LIBYANG_DESCRIPTION} ${project_logo})
+endif()
# generate API/ABI report
-if ("${BUILD_TYPE_UPPER}" STREQUAL "ABICHECK")
+if("${BUILD_TYPE_UPPER}" STREQUAL "ABICHECK")
lib_abi_check(yang "${headers}" ${LIBYANG_SOVERSION_FULL} dae82c1a652bdca0074544c62469a7f51d92c5e8)
endif()
@@ -474,7 +478,9 @@ endif()
source_format(${format_sources})
# uninstall
-add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_MODULE_PATH}/uninstall.cmake")
+if(ENABLE_COMMON_TARGETS)
+ add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_MODULE_PATH}/uninstall.cmake")
+endif()
# clean cmake cache
add_custom_target(cclean
diff --git a/src/log.c b/src/log.c
index f936abfee..9bb4b1902 100644
--- a/src/log.c
+++ b/src/log.c
@@ -592,7 +592,8 @@ static void
log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR err, LY_VECODE vecode, char *data_path,
char *schema_path, uint64_t line, const char *apptag, const char *format, va_list args)
{
- char *msg = NULL;
+ char *dyn_msg = NULL;
+ const char *msg;
ly_bool free_strs = 1, lolog, lostore;
/* learn effective logger options */
@@ -610,33 +611,26 @@ log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR err, LY_VECODE
}
if (err == LY_EMEM) {
- /* just print it, anything else would most likely fail anyway */
- if (lolog) {
- if (log_clb) {
- log_clb(level, LY_EMEM_MSG, data_path, schema_path, line);
- } else {
- fprintf(stderr, "libyang[%d]: ", level);
- vfprintf(stderr, format, args);
- log_stderr_path_line(data_path, schema_path, line);
- }
+ /* no not use more dynamic memory */
+ vsnprintf(last_msg, LY_LAST_MSG_SIZE, format, args);
+ msg = last_msg;
+ } else {
+ /* print into a single message */
+ if (vasprintf(&dyn_msg, format, args) == -1) {
+ LOGMEM(ctx);
+ goto cleanup;
}
- goto cleanup;
- }
+ msg = dyn_msg;
- /* print into a single message */
- if (vasprintf(&msg, format, args) == -1) {
- LOGMEM(ctx);
- goto cleanup;
+ /* store as the last message */
+ strncpy(last_msg, msg, LY_LAST_MSG_SIZE - 1);
}
- /* store as the last message */
- strncpy(last_msg, msg, LY_LAST_MSG_SIZE - 1);
-
/* store the error/warning in the context (if we need to store errors internally, it does not matter what are
- * the user log options) */
- if ((level < LY_LLVRB) && ctx && lostore) {
+ * the user log options), if the message is not dynamic, it would most likely fail to store (no memory) */
+ if ((level < LY_LLVRB) && ctx && lostore && dyn_msg) {
free_strs = 0;
- if (log_store(ctx, level, err, vecode, msg, data_path, schema_path, line, apptag ? strdup(apptag) : NULL)) {
+ if (log_store(ctx, level, err, vecode, dyn_msg, data_path, schema_path, line, apptag ? strdup(apptag) : NULL)) {
goto cleanup;
}
}
@@ -656,7 +650,7 @@ log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR err, LY_VECODE
if (free_strs) {
free(data_path);
free(schema_path);
- free(msg);
+ free(dyn_msg);
}
}
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
index 8dbc68bbf..bd6105fa2 100644
--- a/src/plugins_types/union.c
+++ b/src/plugins_types/union.c
@@ -41,9 +41,9 @@
*/
/**
- * @brief Size in bytes of the index in the LYB Binary Format.
+ * @brief Size in bytes of the used type index in the LYB Binary Format.
*/
-#define IDX_SIZE 4
+#define TYPE_IDX_SIZE 4
/**
* @brief Assign a value to the union subvalue.
@@ -95,14 +95,14 @@ lyb_union_validate(const void *lyb_data, size_t lyb_data_len, const struct lysc_
uint64_t type_idx = 0;
/* Basic validation. */
- if (lyb_data_len < IDX_SIZE) {
+ if (lyb_data_len < TYPE_IDX_SIZE) {
ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB union value size %zu (expected at least 4).",
lyb_data_len);
return ret;
}
/* Get index in correct byte order. */
- memcpy(&type_idx, lyb_data, IDX_SIZE);
+ memcpy(&type_idx, lyb_data, TYPE_IDX_SIZE);
type_idx = le64toh(type_idx);
if (type_idx >= LY_ARRAY_COUNT(type_u->types)) {
ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
@@ -132,7 +132,7 @@ lyb_parse_union(const void *lyb_data, size_t lyb_data_len, uint32_t *type_idx, c
assert(lyb_data && !(lyb_value && !lyb_value_len));
if (type_idx) {
- memcpy(&num, lyb_data, IDX_SIZE);
+ memcpy(&num, lyb_data, TYPE_IDX_SIZE);
num = le64toh(num);
*type_idx = num;
@@ -140,24 +140,25 @@ lyb_parse_union(const void *lyb_data, size_t lyb_data_len, uint32_t *type_idx, c
if (lyb_value && lyb_value_len && lyb_data_len) {
/* Get lyb_value and its length. */
- if (lyb_data_len == IDX_SIZE) {
+ if (lyb_data_len == TYPE_IDX_SIZE) {
*lyb_value_len = 0;
*lyb_value = "";
} else {
- *lyb_value_len = lyb_data_len - IDX_SIZE;
- *lyb_value = (char *)lyb_data + IDX_SIZE;
+ *lyb_value_len = lyb_data_len - TYPE_IDX_SIZE;
+ *lyb_value = (char *)lyb_data + TYPE_IDX_SIZE;
}
}
}
/**
- * @brief Store subvalue as a specific type.
+ * @brief Store (and validate) subvalue as a specific type.
*
* @param[in] ctx libyang context.
- * @param[in] type Specific union type to use for storing.
- * @param[in] subvalue Union subvalue structure.
+ * @param[in] type_u Union type.
+ * @param[in] type_idx Union type index to use for storing (and validating).
+ * @param[in,out] subvalue Union subvalue structure, its value needs to be filled.
* @param[in] options The store options.
- * @param[in] resolve Whether the value needs to be resolved (validated by a callback).
+ * @param[in] validate Whether the value needs to be validated.
* @param[in] ctx_node Context node for prefix resolution.
* @param[in] tree Data tree for resolving (validation).
* @param[in,out] unres Global unres structure.
@@ -165,50 +166,88 @@ lyb_parse_union(const void *lyb_data, size_t lyb_data_len, uint32_t *type_idx, c
* @return LY_ERR value.
*/
static LY_ERR
-union_store_type(const struct ly_ctx *ctx, struct lysc_type *type, struct lyd_value_union *subvalue, uint32_t options,
- ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lys_glob_unres *unres,
- struct ly_err_item **err)
+union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint32_t type_idx, struct lyd_value_union *subvalue,
+ uint32_t options, ly_bool validate, const struct lyd_node *ctx_node, const struct lyd_node *tree,
+ struct lys_glob_unres *unres, struct ly_err_item **err)
{
- LY_ERR ret;
+ LY_ERR rc = LY_SUCCESS;
+ struct lysc_type *type = type_u->types[type_idx];
const void *value = NULL;
size_t value_len = 0;
- uint32_t opts;
+ ly_bool dynamic = 0;
+ LY_VALUE_FORMAT format;
+ void *prefix_data;
+ uint32_t opts = 0, ti;
*err = NULL;
if (subvalue->format == LY_VALUE_LYB) {
- lyb_parse_union(subvalue->original, subvalue->orig_len, NULL, &value, &value_len);
+ lyb_parse_union(subvalue->original, subvalue->orig_len, &ti, &value, &value_len);
+ if (ti != type_idx) {
+ /* value of another type, first store the value properly and then use its JSON value for parsing */
+ rc = type_u->types[ti]->plugin->store(ctx, type_u->types[ti], value, value_len, LYPLG_TYPE_STORE_ONLY,
+ subvalue->format, subvalue->prefix_data, subvalue->hints, subvalue->ctx_node, &subvalue->value, unres, err);
+ if ((rc != LY_SUCCESS) && (rc != LY_EINCOMPLETE)) {
+ /* clear any leftover/freed garbage */
+ memset(&subvalue->value, 0, sizeof subvalue->value);
+ return rc;
+ }
+
+ assert(subvalue->value.realtype);
+ value = subvalue->value.realtype->plugin->print(ctx, &subvalue->value, LY_VALUE_JSON, NULL, &dynamic, &value_len);
+
+ /* to avoid leaks, free subvalue->value, but we need the value, which may be stored there */
+ if (!dynamic) {
+ value = strndup(value, value_len);
+ dynamic = 1;
+ }
+ type_u->types[ti]->plugin->free(ctx, &subvalue->value);
+
+ format = LY_VALUE_JSON;
+ prefix_data = NULL;
+ } else {
+ format = subvalue->format;
+ prefix_data = subvalue->prefix_data;
+ }
} else {
value = subvalue->original;
value_len = subvalue->orig_len;
+ format = subvalue->format;
+ prefix_data = subvalue->prefix_data;
+ }
+
+ if (options & LYPLG_TYPE_STORE_ONLY) {
+ opts |= LYPLG_TYPE_STORE_ONLY;
+ }
+ if (dynamic) {
+ opts |= LYPLG_TYPE_STORE_DYNAMIC;
}
- opts = (options & LYPLG_TYPE_STORE_ONLY) ? LYPLG_TYPE_STORE_ONLY : 0;
- ret = type->plugin->store(ctx, type, value, value_len, opts, subvalue->format, subvalue->prefix_data, subvalue->hints,
+ rc = type->plugin->store(ctx, type, value, value_len, opts, format, prefix_data, subvalue->hints,
subvalue->ctx_node, &subvalue->value, unres, err);
- if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
+ if ((rc != LY_SUCCESS) && (rc != LY_EINCOMPLETE)) {
/* clear any leftover/freed garbage */
memset(&subvalue->value, 0, sizeof subvalue->value);
- return ret;
+ return rc;
}
- if (resolve && (ret == LY_EINCOMPLETE)) {
- /* we need the value resolved */
- ret = type->plugin->validate(ctx, type, ctx_node, tree, &subvalue->value, err);
- if (ret) {
- /* resolve failed, we need to free the stored value */
+ if (validate && (rc == LY_EINCOMPLETE)) {
+ /* we need the value validated */
+ rc = type->plugin->validate(ctx, type, ctx_node, tree, &subvalue->value, err);
+ if (rc) {
+ /* validate failed, we need to free the stored value */
type->plugin->free(ctx, &subvalue->value);
}
}
- return ret;
+ return rc;
}
/**
* @brief Find the first valid type for a union value.
*
* @param[in] ctx libyang context.
- * @param[in] types Sized array of union types.
+ * @param[in] type_u Union type.
* @param[in] subvalue Union subvalue structure.
* @param[in] options The store options.
* @param[in] resolve Whether the value needs to be resolved (validated by a callback).
@@ -220,7 +259,7 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type *type, struct lyd_va
* @return LY_ERR value.
*/
static LY_ERR
-union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_value_union *subvalue,
+union_find_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct lyd_value_union *subvalue,
uint32_t options, ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree,
uint32_t *type_idx, struct lys_glob_unres *unres, struct ly_err_item **err)
{
@@ -233,20 +272,16 @@ union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_v
*err = NULL;
- if (!types || !LY_ARRAY_COUNT(types)) {
- return LY_EINVAL;
- }
-
/* alloc errors */
- errs = calloc(LY_ARRAY_COUNT(types), sizeof *errs);
+ errs = calloc(LY_ARRAY_COUNT(type_u->types), sizeof *errs);
LY_CHECK_RET(!errs, LY_EMEM);
/* turn logging temporarily off */
prev_lo = ly_temp_log_options(&temp_lo);
/* use the first usable subtype to store the value */
- for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
- ret = union_store_type(ctx, types[u], subvalue, options, resolve, ctx_node, tree, unres, &e);
+ for (u = 0; u < LY_ARRAY_COUNT(type_u->types); ++u) {
+ ret = union_store_type(ctx, type_u, u, subvalue, options, resolve, ctx_node, tree, unres, &e);
if ((ret == LY_SUCCESS) || (ret == LY_EINCOMPLETE)) {
break;
}
@@ -254,22 +289,26 @@ union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_v
errs[u] = e;
}
- if (u == LY_ARRAY_COUNT(types)) {
+ if (u == LY_ARRAY_COUNT(type_u->types)) {
/* create the full error */
- msg_len = asprintf(&msg, "Invalid union value \"%.*s\" - no matching subtype found:\n",
- (int)subvalue->orig_len, (char *)subvalue->original);
+ if (subvalue->format == LY_VALUE_LYB) {
+ msg_len = asprintf(&msg, "Invalid LYB union value - no matching subtype found:\n");
+ } else {
+ msg_len = asprintf(&msg, "Invalid union value \"%.*s\" - no matching subtype found:\n",
+ (int)subvalue->orig_len, (char *)subvalue->original);
+ }
if (msg_len == -1) {
LY_CHECK_ERR_GOTO(!errs, ret = LY_EMEM, cleanup);
}
- for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
+ for (u = 0; u < LY_ARRAY_COUNT(type_u->types); ++u) {
if (!errs[u]) {
/* no error for some reason */
continue;
}
- msg = ly_realloc(msg, msg_len + 4 + strlen(types[u]->plugin->id) + 2 + strlen(errs[u]->msg) + 2);
+ msg = ly_realloc(msg, msg_len + 4 + strlen(type_u->types[u]->plugin->id) + 2 + strlen(errs[u]->msg) + 2);
LY_CHECK_ERR_GOTO(!msg, ret = LY_EMEM, cleanup);
- msg_len += sprintf(msg + msg_len, " %s: %s\n", types[u]->plugin->id, errs[u]->msg);
+ msg_len += sprintf(msg + msg_len, " %s: %s\n", type_u->types[u]->plugin->id, errs[u]->msg);
}
ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "%s", msg);
@@ -278,7 +317,7 @@ union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_v
}
cleanup:
- for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
+ for (u = 0; u < LY_ARRAY_COUNT(type_u->types); ++u) {
ly_err_free(errs[u]);
}
free(errs);
@@ -334,7 +373,7 @@ lyb_fill_subvalue(const struct ly_ctx *ctx, struct lysc_type_union *type_u, cons
}
/* use the specific type to store the value */
- ret = union_store_type(ctx, type_u->types[type_idx], subvalue, *options, 0, NULL, NULL, unres, err);
+ ret = union_store_type(ctx, type_u, type_idx, subvalue, *options, 0, NULL, NULL, unres, err);
return ret;
}
@@ -362,7 +401,7 @@ lyplg_type_store_union(const struct ly_ctx *ctx, const struct lysc_type *type, c
ret = lyb_fill_subvalue(ctx, type_u, value, value_len, prefix_data, subvalue, &options, unres, err);
LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
} else {
- /* Store @p value to subvalue. */
+ /* store value to subvalue */
ret = union_subvalue_assignment(value, value_len, &subvalue->original, &subvalue->orig_len, &options);
LY_CHECK_GOTO(ret, cleanup);
@@ -372,7 +411,7 @@ lyplg_type_store_union(const struct ly_ctx *ctx, const struct lysc_type *type, c
LY_CHECK_GOTO(ret, cleanup);
/* use the first usable subtype to store the value */
- ret = union_find_type(ctx, type_u->types, subvalue, options, 0, NULL, NULL, NULL, unres, err);
+ ret = union_find_type(ctx, type_u, subvalue, options, 0, NULL, NULL, NULL, unres, err);
LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
}
@@ -399,22 +438,31 @@ lyplg_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type
struct lysc_type_union *type_u = (struct lysc_type_union *)type;
struct lyd_value_union *subvalue = storage->subvalue;
uint32_t type_idx;
+ ly_bool validated = 0;
*err = NULL;
/* because of types that do not store their own type as realtype (leafref), we are not able to call their
- * validate callback (there is no way to get the type TODO could be added to struct lyd_value_union), so
- * we have to perform union value storing again from scratch */
+ * validate callback (there is no way to get the type) but even if possible, the value may be invalid
+ * for the type, so we may have to perform union value storing again from scratch */
subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
if (subvalue->format == LY_VALUE_LYB) {
- /* use the specific type to store the value */
+ /* use the specific type to store and validate the value */
lyb_parse_union(subvalue->original, 0, &type_idx, NULL, NULL);
- ret = union_store_type(ctx, type_u->types[type_idx], subvalue, 0, 1, ctx_node, tree, NULL, err);
- LY_CHECK_RET(ret);
- } else {
+ ret = union_store_type(ctx, type_u, type_idx, subvalue, 0, 1, ctx_node, tree, NULL, err);
+ if (ret) {
+ /* validation failed, we need to try storing the value again */
+ ly_err_free(*err);
+ *err = NULL;
+ } else {
+ validated = 1;
+ }
+ }
+
+ if (!validated) {
/* use the first usable subtype to store the value */
- ret = union_find_type(ctx, type_u->types, subvalue, 0, 1, ctx_node, tree, NULL, NULL, err);
+ ret = union_find_type(ctx, type_u, subvalue, 0, 1, ctx_node, tree, NULL, NULL, err);
LY_CHECK_RET(ret);
}
@@ -495,7 +543,7 @@ lyb_union_print(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct
ctx = subvalue->ctx_node->module->ctx;
}
subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
- r = union_find_type(ctx, type_u->types, subvalue, 0, 0, NULL, NULL, &type_idx, NULL, &err);
+ r = union_find_type(ctx, type_u, subvalue, 0, 0, NULL, NULL, &type_idx, NULL, &err);
ly_err_free(err);
LY_CHECK_RET((r != LY_SUCCESS) && (r != LY_EINCOMPLETE), NULL);
@@ -505,14 +553,14 @@ lyb_union_print(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct
LY_CHECK_RET(!pval, NULL);
/* Create LYB data. */
- *value_len = IDX_SIZE + pval_len;
+ *value_len = TYPE_IDX_SIZE + pval_len;
ret = malloc(*value_len);
LY_CHECK_RET(!ret, NULL);
num = type_idx;
num = htole64(num);
- memcpy(ret, &num, IDX_SIZE);
- memcpy((char *)ret + IDX_SIZE, pval, pval_len);
+ memcpy(ret, &num, TYPE_IDX_SIZE);
+ memcpy((char *)ret + TYPE_IDX_SIZE, pval, pval_len);
if (dynamic) {
free(pval);
diff --git a/src/tree_data.h b/src/tree_data.h
index 9f918af22..ff25b8b18 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -56,11 +56,11 @@ struct rb_node;
* it can be cast to several other structures.
*
* In case the ::lyd_node.schema pointer is NULL, the node is actually __opaq__ and can be safely cast to ::lyd_node_opaq.
- * The opaq node represent an unknown node which wasn't mapped to any [(compiled) schema](@ref howtoSchema) node in the
- * context. Such a node can appear in several places in the data tree.
+ * The opaq node represents an **unknown node** which wasn't mapped to any [(compiled) schema](@ref howtoSchema) node in the
+ * context. But it may represent a schema node data instance which is invalid. That may happen if the value (leaf/leaf-list)
+ * is invalid or there are invalid/missing keys of a list instance. Such a node can appear in several places in the data tree.
* - As a part of the tree structure, but only in the case the ::LYD_PARSE_OPAQ option was used when input data were
- * [parsed](@ref howtoDataParsers), because unknown data instances are ignored by default. The same way, the opaq nodes can
- * appear as a node's attributes.
+ * [parsed](@ref howtoDataParsers), because unknown data instances are ignored and invalid data produce errors by default.
* - As a representation of YANG anydata/anyxml content.
* - As envelopes of standard data tree instances (RPCs, actions or Notifications).
*
@@ -1281,7 +1281,8 @@ LIBYANG_API_DECL LY_ERR lyd_new_ext_inner(const struct lysc_ext_instance *ext, c
no error set. */
#define LYD_NEW_PATH_OPAQ 0x40 /**< Enables the creation of opaque nodes with some specific rules. If the __last node__
in the path is not uniquely defined ((leaf-)list without a predicate) or has an
- invalid value (leaf/leaf-list), it is created as opaque. */
+ invalid value (leaf/leaf-list), it is created as opaque. Otherwise a regular node
+ is created. */
#define LYD_NEW_PATH_WITH_OPAQ 0x80 /**< Consider opaque nodes normally when searching for existing nodes. */
#define LYD_NEW_ANY_USE_VALUE 0x100 /**< Whether to use dynamic @p value or make a copy. */
diff --git a/tests/utests/types/binary.c b/tests/utests/types/binary.c
index 4d0cfbcc5..efdc8f112 100644
--- a/tests/utests/types/binary.c
+++ b/tests/utests/types/binary.c
@@ -327,8 +327,8 @@ test_plugin_sort(void **state)
v2 = "YWhv"; /* aho */
assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2),
0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err));
- assert_int_equal(1, type->sort(UTEST_LYCTX, &val1, &val2));
- assert_int_equal(-1, type->sort(UTEST_LYCTX, &val2, &val1));
+ assert_true(0 < type->sort(UTEST_LYCTX, &val1, &val2));
+ assert_true(0 > type->sort(UTEST_LYCTX, &val2, &val1));
type->free(UTEST_LYCTX, &val1);
type->free(UTEST_LYCTX, &val2);
}
diff --git a/tests/utests/types/enumeration.c b/tests/utests/types/enumeration.c
index 0d42c54f6..8ff1c8ca9 100644
--- a/tests/utests/types/enumeration.c
+++ b/tests/utests/types/enumeration.c
@@ -108,9 +108,9 @@ test_plugin_sort(void **state)
v2 = "black";
assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2),
0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err));
- assert_int_equal(1, type->sort(UTEST_LYCTX, &val1, &val2));
+ assert_true(0 < type->sort(UTEST_LYCTX, &val1, &val2));
assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1));
- assert_int_equal(-1, type->sort(UTEST_LYCTX, &val2, &val1));
+ assert_true(0 > type->sort(UTEST_LYCTX, &val2, &val1));
type->free(UTEST_LYCTX, &val1);
type->free(UTEST_LYCTX, &val2);
}
diff --git a/tests/utests/types/leafref.c b/tests/utests/types/leafref.c
index 81e8ded83..002763ca1 100644
--- a/tests/utests/types/leafref.c
+++ b/tests/utests/types/leafref.c
@@ -239,9 +239,9 @@ test_plugin_sort(void **state)
v2 = "str2";
assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2),
0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err));
- assert_int_equal(-1, type->sort(UTEST_LYCTX, &val1, &val2));
+ assert_true(0 > type->sort(UTEST_LYCTX, &val1, &val2));
assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1));
- assert_int_equal(1, type->sort(UTEST_LYCTX, &val2, &val1));
+ assert_true(0 < type->sort(UTEST_LYCTX, &val2, &val1));
type->free(UTEST_LYCTX, &val1);
type->free(UTEST_LYCTX, &val2);
}
diff --git a/tests/utests/types/union.c b/tests/utests/types/union.c
index 0d6ad2145..c2541aaaf 100644
--- a/tests/utests/types/union.c
+++ b/tests/utests/types/union.c
@@ -168,9 +168,9 @@ test_plugin_sort(void **state)
v2 = "-1";
assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2),
0, LY_VALUE_JSON, NULL, LYD_VALHINT_DECNUM, NULL, &val2, NULL, &err));
- assert_int_equal(1, type->sort(UTEST_LYCTX, &val1, &val2));
+ assert_true(0 < type->sort(UTEST_LYCTX, &val1, &val2));
assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1));
- assert_int_equal(-1, type->sort(UTEST_LYCTX, &val2, &val1));
+ assert_true(0 > type->sort(UTEST_LYCTX, &val2, &val1));
type->free(UTEST_LYCTX, &val1);
type->free(UTEST_LYCTX, &val2);
@@ -180,8 +180,8 @@ test_plugin_sort(void **state)
v2 = "-2";
assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2),
0, LY_VALUE_JSON, NULL, LYD_VALHINT_DECNUM, NULL, &val2, NULL, &err));
- assert_int_equal(1, type->sort(UTEST_LYCTX, &val1, &val2));
- assert_int_equal(-1, type->sort(UTEST_LYCTX, &val2, &val1));
+ assert_true(0 < type->sort(UTEST_LYCTX, &val1, &val2));
+ assert_true(0 > type->sort(UTEST_LYCTX, &val2, &val1));
type->free(UTEST_LYCTX, &val1);
type->free(UTEST_LYCTX, &val2);
}
@@ -192,6 +192,7 @@ test_validation(void **state)
const char *schema, *data;
struct lyd_node *tree;
char *out;
+ uint32_t uint_val;
schema = MODULE_CREATE_YANG("val",
"leaf l1 {\n"
@@ -230,6 +231,73 @@ test_validation(void **state)
free(out);
lyd_free_all(tree);
+
+ schema = MODULE_CREATE_YANG("lref",
+ "container test {\n"
+ " list a {\n"
+ " key \"name\";\n"
+ " leaf name {\n"
+ " type enumeration {\n"
+ " enum zero;\n"
+ " enum one;\n"
+ " enum two;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " list b {\n"
+ " key \"name\";\n"
+ " leaf name {\n"
+ " type uint32;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " list community {\n"
+ " key \"name\";\n"
+ " leaf name {\n"
+ " type string;\n"
+ " }\n"
+ " leaf view {\n"
+ " type union {\n"
+ " type leafref {\n"
+ " path \"../../a/name\";\n"
+ " }\n"
+ " type leafref {\n"
+ " path \"../../b/name\";\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* parse from LYB #1 */
+ data = "2test2";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ assert_int_equal(LY_SUCCESS, lyd_print_mem(&out, tree, LYD_LYB, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS));
+ lyd_free_all(tree);
+ CHECK_PARSE_LYD_PARAM(out, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ free(out);
+ lyd_free_all(tree);
+
+ /* parse from LYB #2 */
+ data = "onetestone";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ assert_int_equal(LY_SUCCESS, lyd_print_mem(&out, tree, LYD_LYB, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS));
+ lyd_free_all(tree);
+ CHECK_PARSE_LYD_PARAM(out, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ free(out);
+
+ /* remove the target and create another, which is represented the same way in LYB */
+ lyd_free_tree(lyd_child(tree));
+ uint_val = 1;
+ assert_int_equal(LY_SUCCESS, lyd_new_list(tree, NULL, "b", LYD_NEW_VAL_BIN, NULL, &uint_val, sizeof uint_val));
+ assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT, NULL));
+ CHECK_LOG_CTX("Invalid LYB union value - no matching subtype found:\n"
+ " libyang 2 - leafref, version 1: Invalid leafref value \"one\" - no target instance \"../../a/name\" with the same value.\n"
+ " libyang 2 - leafref, version 1: Invalid type uint32 value \"one\".\n", "/lref:test/community[name='test']/view", 0);
+
+ lyd_free_all(tree);
}
int
diff --git a/tests/utests/types/yang_types.c b/tests/utests/types/yang_types.c
index 42e1f3289..f79238eb5 100644
--- a/tests/utests/types/yang_types.c
+++ b/tests/utests/types/yang_types.c
@@ -301,8 +301,8 @@ test_sort(void **state)
v2 = "2005-05-25T23:15:15.0001Z";
assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2),
0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err));
- assert_int_equal(-1, type->sort(UTEST_LYCTX, &val1, &val2));
- assert_int_equal(1, type->sort(UTEST_LYCTX, &val2, &val1));
+ assert_true(0 > type->sort(UTEST_LYCTX, &val1, &val2));
+ assert_true(0 < type->sort(UTEST_LYCTX, &val2, &val1));
assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1));
type->free(UTEST_LYCTX, &val1);
type->free(UTEST_LYCTX, &val2);