libyang NEW support for user type plugins
Refs #448
diff --git a/src/context.c b/src/context.c
index e9f8d87..8c0925e 100644
--- a/src/context.c
+++ b/src/context.c
@@ -95,7 +95,7 @@
lydict_init(&ctx->dict);
/* plugins */
- lyext_load_plugins();
+ ly_load_plugins();
/* initialize thread-specific key */
while ((i = pthread_key_create(&ctx->errlist_key, ly_err_free)) == EAGAIN);
@@ -103,7 +103,6 @@
/* models list */
ctx->models.list = calloc(16, sizeof *ctx->models.list);
LY_CHECK_ERR_RETURN(!ctx->models.list, LOGMEM(NULL); free(ctx), NULL);
- ext_plugins_ref++;
ctx->models.flags = options;
ctx->models.used = 0;
ctx->models.size = 16;
@@ -477,8 +476,7 @@
lydict_clean(&ctx->dict);
/* plugins - will be removed only if this is the last context */
- ext_plugins_ref--;
- lyext_clean_plugins();
+ ly_clean_plugins();
free(ctx);
}
diff --git a/src/extensions.c b/src/extensions.c
deleted file mode 100644
index e38f40b..0000000
--- a/src/extensions.c
+++ /dev/null
@@ -1,354 +0,0 @@
-/**
- * @file extensions.c
- * @author Radek Krejci <rkrejci@cesnet.cz>
- * @brief YANG extensions routines implementation
- *
- * Copyright (c) 2015 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 <assert.h>
-#include <errno.h>
-#include <dirent.h>
-#include <dlfcn.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <limits.h>
-
-#include "common.h"
-#include "extensions.h"
-#include "extensions_config.h"
-#include "libyang.h"
-#include "parser.h"
-
-/* internal structures storing the extension plugins */
-struct lyext_plugin_list *ext_plugins = NULL;
-unsigned int ext_plugins_count = 0; /* size of the ext_plugins array */
-unsigned int ext_plugins_ref = 0; /* number of contexts that may reference the ext_plugins */
-struct ly_set dlhandlers = {0, 0, {NULL}};
-pthread_mutex_t ext_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/**
- * @brief reference counter for the ext_plugins, it actually counts number of contexts
- */
-unsigned int ext_plugins_references = 0;
-
-API int
-lyext_clean_plugins(void)
-{
- unsigned int u;
-
- if (ext_plugins_ref) {
- /* there is a context that may refer to the plugins, so we cannot remove them */
- return EXIT_FAILURE;
- }
-
- if (!ext_plugins_count) {
- /* no plugin loaded - nothing to do */
- return EXIT_SUCCESS;
- }
-
- /* lock the extension plugins list */
- pthread_mutex_lock(&ext_lock);
-
- /* clean the list */
- free(ext_plugins);
- ext_plugins = NULL;
- ext_plugins_count = 0;
-
- /* close the dl handlers */
- for (u = 0; u < dlhandlers.number; u++) {
- dlclose(dlhandlers.set.g[u]);
- }
- free(dlhandlers.set.g);
- dlhandlers.set.g = NULL;
- dlhandlers.size = 0;
- dlhandlers.number = 0;
-
- /* unlock the global structures */
- pthread_mutex_unlock(&ext_lock);
-
- return EXIT_SUCCESS;
-}
-
-API void
-lyext_load_plugins(void)
-{
- DIR* dir;
- struct dirent *file;
- size_t len;
- char *str;
- char name[NAME_MAX];
- void *dlhandler;
- struct lyext_plugin_list *plugin, *p;
- struct lyext_plugin_complex *pluginc;
- unsigned int u, v;
- const char *pluginsdir;
-
- /* try to get the plugins directory from environment variable */
- pluginsdir = getenv("LIBYANG_EXTENSIONS_PLUGINS_DIR");
- if (!pluginsdir) {
- pluginsdir = LYEXT_PLUGINS_DIR;
- }
-
- dir = opendir(pluginsdir);
- if (!dir) {
- /* no directory (or no access to it), no plugins */
- LOGWRN(NULL, "libyang extensions plugins directory \"%s\" does not exist.", pluginsdir);
- return;
- }
-
- /* lock the extension plugins list */
- pthread_mutex_lock(&ext_lock);
-
- while ((file = readdir(dir))) {
- /* required format of the filename is *LYEXT_PLUGIN_SUFFIX */
- len = strlen(file->d_name);
- if (len < LYEXT_PLUGIN_SUFFIX_LEN + 1 ||
- strcmp(&file->d_name[len - LYEXT_PLUGIN_SUFFIX_LEN], LYEXT_PLUGIN_SUFFIX)) {
- continue;
- }
-
- /* store the name without the suffix */
- memcpy(name, file->d_name, len - LYEXT_PLUGIN_SUFFIX_LEN);
- name[len - LYEXT_PLUGIN_SUFFIX_LEN] = '\0';
-
- /* and construct the filepath */
- asprintf(&str, "%s/%s", pluginsdir, file->d_name);
-
- /* load the plugin - first, try if it is already loaded... */
- dlhandler = dlopen(str, RTLD_NOW | RTLD_NOLOAD);
- dlerror(); /* Clear any existing error */
- if (dlhandler) {
- /* the plugin is already loaded */
- LOGVRB("Extension plugin \"%s\" already loaded.", str);
- free(str);
-
- /* keep the refcount of the shared object correct */
- dlclose(dlhandler);
- continue;
- }
-
- /* ... and if not, load it */
- dlhandler = dlopen(str, RTLD_NOW);
- if (!dlhandler) {
- LOGERR(NULL, LY_ESYS, "Loading \"%s\" as an extension plugin failed (%s).", str, dlerror());
- free(str);
- continue;
- }
- LOGVRB("Extension plugin \"%s\" successfully loaded.", str);
- free(str);
- dlerror(); /* Clear any existing error */
-
- /* get the plugin data */
- plugin = dlsym(dlhandler, name);
- str = dlerror();
- if (str) {
- LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed, missing plugin list object (%s).", name, str);
- dlclose(dlhandler);
- continue;
- }
-
- for(u = 0; plugin[u].name; u++) {
- /* check extension implementations for collisions */
- for (v = 0; v < ext_plugins_count; v++) {
- if (!strcmp(plugin[u].name, ext_plugins[v].name) &&
- !strcmp(plugin[u].module, ext_plugins[v].module) &&
- (!plugin[u].revision || !ext_plugins[v].revision || !strcmp(plugin[u].revision, ext_plugins[v].revision))) {
- LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed,"
- "implementation collision for extension %s from module %s%s%s.",
- name, plugin[u].name, plugin[u].module, plugin[u].revision ? "@" : "",
- plugin[u].revision ? plugin[u].revision : "");
- dlclose(dlhandler);
- goto nextplugin;
- }
- }
-
- /* check for valid supported substatements in case of complex extension */
- if (plugin[u].plugin->type == LYEXT_COMPLEX && ((struct lyext_plugin_complex *)plugin[u].plugin)->substmt) {
- pluginc = (struct lyext_plugin_complex *)plugin[u].plugin;
- for (v = 0; pluginc->substmt[v].stmt; v++) {
- if (pluginc->substmt[v].stmt >= LY_STMT_SUBMODULE ||
- pluginc->substmt[v].stmt == LY_STMT_VERSION ||
- pluginc->substmt[v].stmt == LY_STMT_YINELEM) {
- LOGERR(NULL, LY_EINVAL,
- "Extension plugin \"%s\" (extension %s) allows not supported extension substatement (%s)",
- name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
- dlclose(dlhandler);
- goto nextplugin;
- }
- if (pluginc->substmt[v].cardinality > LY_STMT_CARD_MAND &&
- pluginc->substmt[v].stmt >= LY_STMT_MODIFIER &&
- pluginc->substmt[v].stmt <= LY_STMT_STATUS) {
- LOGERR(NULL, LY_EINVAL, "Extension plugin \"%s\" (extension %s) allows multiple instances on \"%s\" "
- "substatement, which is not supported.",
- name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
- dlclose(dlhandler);
- goto nextplugin;
- }
- }
- }
- }
-
-
- /* add the new plugins, we have number of new plugins as u */
- p = realloc(ext_plugins, (ext_plugins_count + u) * sizeof *ext_plugins);
- if (!p) {
- LOGMEM(NULL);
- dlclose(dlhandler);
- closedir(dir);
-
- /* unlock the global structures */
- pthread_mutex_unlock(&ext_lock);
-
- return;
- }
- ext_plugins = p;
- for( ; u; u--) {
- memcpy(&ext_plugins[ext_plugins_count], &plugin[u - 1], sizeof *plugin);
- ext_plugins_count++;
- }
-
- /* keep the handler */
- ly_set_add(&dlhandlers, dlhandler, LY_SET_OPT_USEASLIST);
-
-nextplugin:;
- }
-
- closedir(dir);
-
- /* unlock the global structures */
- pthread_mutex_unlock(&ext_lock);
-}
-
-struct lyext_plugin *
-ext_get_plugin(const char *name, const char *module, const char *revision)
-{
- unsigned int u;
-
- assert(name);
- assert(module);
-
- for (u = 0; u < ext_plugins_count; u++) {
- if (!strcmp(name, ext_plugins[u].name) &&
- !strcmp(module, ext_plugins[u].module) &&
- (!ext_plugins[u].revision || !strcmp(revision, ext_plugins[u].revision))) {
- /* we have the match */
- return ext_plugins[u].plugin;
- }
- }
-
- /* plugin not found */
- return NULL;
-}
-
-API int
-lys_ext_instance_presence(struct lys_ext *def, struct lys_ext_instance **ext, uint8_t ext_size)
-{
- uint8_t index;
-
- if (!def || (ext_size && !ext)) {
- LOGARG;
- return -1;
- }
-
- /* search for the extension instance */
- for (index = 0; index < ext_size; index++) {
- if (ext[index]->def == def) {
- return index;
- }
- }
-
- /* not found */
- return -1;
-}
-
-API void *
-lys_ext_complex_get_substmt(LY_STMT stmt, struct lys_ext_instance_complex *ext, struct lyext_substmt **info)
-{
- int i;
-
- if (!ext || !ext->def || !ext->def->plugin || ext->def->plugin->type != LYEXT_COMPLEX) {
- LOGARG;
- return NULL;
- }
-
- if (!ext->substmt) {
- /* no substatement defined in the plugin */
- if (info) {
- *info = NULL;
- }
- return NULL;
- }
-
- /* search the substatements defined by the plugin */
- for (i = 0; ext->substmt[i].stmt; i++) {
- if (stmt == LY_STMT_NODE) {
- if (ext->substmt[i].stmt >= LY_STMT_ACTION && ext->substmt[i].stmt <= LY_STMT_USES) {
- if (info) {
- *info = &ext->substmt[i];
- }
- break;
- }
- } else if (ext->substmt[i].stmt == stmt) {
- if (info) {
- *info = &ext->substmt[i];
- }
- break;
- }
- }
-
- if (ext->substmt[i].stmt) {
- return &ext->content[ext->substmt[i].offset];
- } else {
- return NULL;
- }
-}
-
-LY_STMT
-lys_snode2stmt(LYS_NODE nodetype)
-{
- switch(nodetype) {
- case LYS_CONTAINER:
- return LY_STMT_CONTAINER;
- case LYS_CHOICE:
- return LY_STMT_CHOICE;
- case LYS_LEAF:
- return LY_STMT_LEAF;
- case LYS_LEAFLIST:
- return LY_STMT_LEAFLIST;
- case LYS_LIST:
- return LY_STMT_LIST;
- case LYS_ANYXML:
- case LYS_ANYDATA:
- return LY_STMT_ANYDATA;
- case LYS_CASE:
- return LY_STMT_CASE;
- case LYS_NOTIF:
- return LY_STMT_NOTIFICATION;
- case LYS_RPC:
- return LY_STMT_RPC;
- case LYS_INPUT:
- return LY_STMT_INPUT;
- case LYS_OUTPUT:
- return LY_STMT_OUTPUT;
- case LYS_GROUPING:
- return LY_STMT_GROUPING;
- case LYS_USES:
- return LY_STMT_USES;
- case LYS_AUGMENT:
- return LY_STMT_AUGMENT;
- case LYS_ACTION:
- return LY_STMT_ACTION;
- default:
- return LY_STMT_NODE;
- }
-}
diff --git a/src/extensions/CMakeLists.txt b/src/extensions/CMakeLists.txt
index 7a26de3..46ed5cf 100644
--- a/src/extensions/CMakeLists.txt
+++ b/src/extensions/CMakeLists.txt
@@ -1,8 +1,8 @@
macro(EXTENSION_PLUGIN PLUGIN_NAME SRCS)
- add_library(${PLUGIN_NAME} SHARED ${SRCS})
- set_target_properties(${PLUGIN_NAME} PROPERTIES PREFIX "")
- target_link_libraries(${PLUGIN_NAME} yang)
- install(TARGETS ${PLUGIN_NAME} DESTINATION ${LIBYANG_EXT_PLUGINS_DIR})
+ add_library(${PLUGIN_NAME} SHARED ${SRCS})
+ set_target_properties(${PLUGIN_NAME} PROPERTIES PREFIX "")
+ target_link_libraries(${PLUGIN_NAME} yang)
+ install(TARGETS ${PLUGIN_NAME} DESTINATION ${EXTENSIONS_PLUGINS_DIR_MACRO})
endmacro(EXTENSION_PLUGIN)
EXTENSION_PLUGIN(nacm "nacm.c")
diff --git a/src/extensions_config.h.in b/src/extensions_config.h.in
deleted file mode 100644
index ac91590..0000000
--- a/src/extensions_config.h.in
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * @file extensions_config.h
- * @author Radek Krejci <rkrejci@cesnet.cz>
- * @brief libyang support for YANG extension implementations - internal configuration values.
- *
- * Copyright (c) 2017 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 LY_EXTENSIONS_CONFIG_H_
-#define LY_EXTENSIONS_CONFIG_H_
-
-#define LYEXT_PLUGINS_DIR "@LIBYANG_EXT_PLUGINS_DIR@" /**< directory with YANG extension plugins */
-
-#if defined __linux__ || defined __unix__
-# define LYEXT_PLUGIN_SUFFIX ".so"
-# define LYEXT_PLUGIN_SUFFIX_LEN 3
-#elif defined __APPLE__
-# define LYEXT_PLUGIN_SUFFIX ".dylib"
-# define LYEXT_PLUGIN_SUFFIX_LEN 6
-#endif
-
-#endif /* LY_EXTENSIONS_CONFIG_H_ */
diff --git a/src/libyang.h.in b/src/libyang.h.in
index 4334d7b..88cce31 100644
--- a/src/libyang.h.in
+++ b/src/libyang.h.in
@@ -49,7 +49,7 @@
* ([RFC 7951](https://tools.ietf.org/html/rfc7951)).
* - [Manipulation with the instance data](@ref howtodatamanipulators).
* - Support for [default values in the instance data](@ref howtodatawd) ([RFC 6243](https://tools.ietf.org/html/rfc6243)).
- * - Support for [YANG extensions](@ref howtoschemaextensions).
+ * - Support for [YANG extensions and user types](@ref howtoschemaplugins).
* - Support for [YANG Metadata](@ref howtoschemametadata) ([RFC 7952](https://tools.ietf.org/html/rfc6243)).
*
* The current implementation covers YANG 1.0 ([RFC 6020](https://tools.ietf.org/html/rfc6020)) as well as
@@ -234,7 +234,7 @@
*
* - @subpage howtoschemasparsers
* - @subpage howtoschemasfeatures
- * - @subpage howtoschemaextensions
+ * - @subpage howtoschemaplugins
* - @subpage howtoschemasprinters
*
* \note There are many functions to access information from the schema trees. Details are available in
@@ -327,7 +327,20 @@
*/
/**
- * @page howtoschemaextensions YANG Extensions Support
+ * @page howtoschemaplugins YANG Extension and User Type Support
+ *
+ * Extensions and user types are supported in the form of **plugins**. These are loaded from the plugin directory
+ * (`LIBDIR/libyang/`) whenever a context is created. However, the list of plugins can be refreshed manually by ly_load_plugins().
+ * The extension plugin directory path (default `LIBDIR/libyang/extensions/`) can be change via the
+ * `LIBYANG_EXTENSIONS_PLUGINS_DIR` environment variable and similarly the user type directory (default `LIBDIR/libyang/user_types/`)
+ * via `LIBYANG_USER_TYPES_PLUGINS_DIR`. Note, that unavailable plugins are not removed, only
+ * any new plugins are loaded. Also note that the availability of new plugins does not affect the current schemas in the
+ * contexts, they are applied only to the newly parsed schemas.
+ *
+ * The plugins list can be cleaned by ly_clean_plugins(). However, since various contexts (respectively their
+ * schemas) can link to the plugins, the cleanup is successful only when there is no remaining context.
+ *
+ * @section extensions Extensions
*
* YANG provides extensions as a mechanism how to add new statements into the language. Since they are very generic -
* extension instance can appear anywhere, they can contain any other YANG statement including extension instances and
@@ -336,7 +349,7 @@
* use cases should be covered and supported.
*
* Since libyang does not understand human text, it is not possible to get the complete defintion of the extension from
- * its description statement. Therefore, libyang allows the schema authors to provide @link lyext_plugin extension
+ * its description statement. Therefore, libyang allows the schema authors to provide @link extplugins extension
* plugin@endlink that provides information from the extension description to libyang.
*
* Here are some notes about the implementation of the particular YANG extensions features
@@ -380,8 +393,8 @@
* ::lys_ext_instance#ext_type is set to a different value than #LYEXT_FLAG, the structure can be cast to the particular
* extension instance structure to access the type-specific members.
*
- * Extension Plugins
- * -----------------
+ * @subsection extplugins Extension Plugins
+ *
* Extension plugins provide more detailed information about the extension in a understandable form for libyang. These
* information is usually provided in a text form in the extension's description statement. libyang provides several
* plugins for the common IETF extensions (NACM, Metadata, ...) that can be used as a code examples for other
@@ -407,18 +420,7 @@
* stored (as offset to the ::lys_ext_instance_complex#content member). The way how the data are stored is
* specified descriptions of #LY_STMT values.
*
- * The plugins are loaded from the plugin directory (LIBDIR/libyang/) whenever a context is created. However, the list
- * of plugins can be refreshed manually by lyext_load_plugins(). The plugin directory path can be change via the
- * `LIBYANG_EXTENSIONS_PLUGINS_DIR` environment variable. Note, that no more available plugins are not removed, only
- * the new plugins are loaded. Also note that availability of new plugins does not affect the current schemas in the
- * contexts, they are applied only to the newly parsed schemas.
- *
- * The plugins list can be cleaned by lyext_clean_plugins(). However, since various contexts (respectively their
- * schemas) can link to the plugins, the cleanup is successful only when there is no remaining context.
- *
- * Metadata Support
- * ----------------
- * @anchor howtoschemametadata
+ * @subsection howtoschemametadata Metadata Support
*
* YANG Metadata annotations are defined in [RFC 7952](https://tools.ietf.org/html/rfc6243) as YANG extension. In
* practice, it allows to have XML attributes (there is also a special encoding for JSON) in YANG modeled data.
@@ -467,12 +469,32 @@
* - the `select`'s content is XPath and it is internally transformed by libyang into the format where the
* XML namespace prefixes are replaced by the YANG module names.
*
+ * @section usertypes User Types
+ *
+ * Using this plugin mechanism, it is also possible to define what can be called **user types**. Values are
+ * always stored as a string in addition to being in a #lyd_val union. It is possible to customize how
+ * the value is stored in the union using a #lytype_store_clb callback.
+ *
+ * Generally, it is meant for storing certain types more effectively. For instance, when working with **ipv4-address**
+ * from the *ietf-inet-types* model, an application will most likely use the address in a binary form, not as a string.
+ * So, in the callback the value is simply transformed into the desired format and saved into #lyd_val value. However,
+ * the callback is allowed to store anything in the union. Another example, if there are many strings being created and
+ * handled, is to store the string length instead having 2 pointers to the same string.
+ *
+ * @subsection typeplugins User Type Plugins
+ *
+ * There is a simple example user type plugin in `src/user_types` that is not compiled nor installed.
+ *
+ * - ::lytype_plugin_list - plugin is supposed to provide callbacks for:
+ * + @link lytype_store_clb storing the value itself @endlink
+ * + freeing the stored value (optionally, if the store callback allocates memory)
+ *
* Functions List
* --------------
* - lys_ext_instance_presence()
* - lys_ext_instance_substmt()
- * - lyext_load_plugins()
- * - lyext_clean_plugins()
+ * - ly_load_plugins()
+ * - ly_clean_plugins()
*/
/**
@@ -1804,7 +1826,7 @@
LY_EINVAL, /**< Invalid value */
LY_EINT, /**< Internal error */
LY_EVALID, /**< Validation failure */
- LY_EEXT /**< Extension error reported by an extension plugin */
+ LY_EPLUGIN /**< Error reported by a plugin */
} LY_ERR;
/**
diff --git a/src/log.c b/src/log.c
index b66e63a..36e5bb1 100644
--- a/src/log.c
+++ b/src/log.c
@@ -290,13 +290,13 @@
return;
}
- if (asprintf(&plugin_msg, "%s (reported by extension plugin %s, %s())", format, plugin, function) == -1) {
+ if (asprintf(&plugin_msg, "%s (reported by plugin %s, %s())", format, plugin, function) == -1) {
LOGMEM(ctx);
return;
}
va_start(ap, format);
- log_vprintf(ctx, level, (level == LY_LLERR ? LY_EEXT : 0), 0, NULL, plugin_msg, ap);
+ log_vprintf(ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0), 0, NULL, plugin_msg, ap);
va_end(ap);
free(plugin_msg);
diff --git a/src/parser.c b/src/parser.c
index d8d330c..883de3f 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -1412,9 +1412,8 @@
itemname = attr->name;
}
- if (store && ((*val_type & LY_DATA_TYPE_MASK) == LY_TYPE_BITS)) {
- free(val->bit);
- val->bit = NULL;
+ if (store) {
+ lyd_free_value(*val, *val_type, type);
}
switch (type->base) {
@@ -1427,7 +1426,7 @@
for (uind = 0; isspace(value[uind]); ++uind);
ptr = &value[uind];
u = strlen(ptr);
- while(u && isspace(ptr[u - 1])) {
+ while (u && isspace(ptr[u - 1])) {
--u;
}
unum = u;
@@ -2067,9 +2066,7 @@
if (store) {
/* erase possible present and invalid value data */
- if (t->base == LY_TYPE_BITS) {
- free(val->bit);
- }
+ lyd_free_value(*val, *val_type, t);
memset(val, 0, sizeof(lyd_val));
}
}
@@ -2096,6 +2093,16 @@
return NULL;
}
+ /* search user types in case this value is supposed to be stored in a custom way */
+ if (store && type->der && type->der->module) {
+ c = lytype_store(type->der->module, type->der->name, *value_, val);
+ if (c == -1) {
+ return NULL;
+ } else if (!c) {
+ *val_type |= LY_TYPE_USER;
+ }
+ }
+
ret = type;
cleanup:
diff --git a/src/parser.h b/src/parser.h
index eda50ce..def99dc 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -201,20 +201,6 @@
unsigned int pututf8(struct ly_ctx *ctx, char *dst, int32_t value);
unsigned int copyutf8(struct ly_ctx *ctx, char *dst, const char *src);
-/*
- * Internal functions implementing YANG extensions support
- * - implemented in extensions.c
- */
-
-/**
- * @brief If available, get the extension plugin for the specified extension
- * @param[in] name Name of the extension
- * @param[in] module Name of the extension's module
- * @param[in] revision Revision of the extension's module
- * @return pointer to the extension plugin structure, NULL if no plugin available
- */
-struct lyext_plugin *ext_get_plugin(const char *name, const char *module, const char *revision);
-
/**
* @brief Find a module. First, imports from \p module with matching \p prefix, \p name, or both are checked,
* \p module itself is also compared, and lastly a callback is used if allowed.
@@ -240,4 +226,39 @@
*/
const struct lys_module *lyp_get_import_module_ns(const struct lys_module *module, const char *ns);
+/*
+ * Internal functions implementing YANG (extension and user type) plugin support
+ * - implemented in plugins.c
+ */
+
+/**
+ * @brief If available, get the extension plugin for the specified extension
+ *
+ * @param[in] name Name of the extension
+ * @param[in] module Name of the extension's module
+ * @param[in] revision Revision of the extension's module
+ * @return pointer to the extension plugin structure, NULL if no plugin available
+ */
+struct lyext_plugin *ext_get_plugin(const char *name, const char *module, const char *revision);
+
+/**
+ * @brief Try to store a value as a user type defined by a plugin.
+ *
+ * @param[in] mod Module of the type.
+ * @param[in] type_name Type (typedef) name.
+ * @param[in] value_str Value to store as a string.
+ * @param[in,out] value Filled value to be overwritten by the user store callback.
+ * @return 0 on successful storing, 1 if the type is not a user type, -1 on error.
+ */
+int lytype_store(const struct lys_module *mod, const char *type_name, const char *value_str, lyd_val *value);
+
+/**
+ * @brief Free a user type stored value.
+ *
+ * @param[in] mod Module of the type.
+ * @param[in] type_name Type (typedef) name.
+ * @param[in] value Value union to free.
+ */
+void lytype_free(const struct lys_module *mod, const char *type_name, lyd_val value);
+
#endif /* LY_PARSER_H_ */
diff --git a/src/plugin_config.h.in b/src/plugin_config.h.in
new file mode 100644
index 0000000..4578492
--- /dev/null
+++ b/src/plugin_config.h.in
@@ -0,0 +1,29 @@
+/**
+ * @file plugin_config.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang plugin config file
+ *
+ * Copyright (c) 2018 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 LY_PLUGIN_CONFIG_H_
+#define LY_PLUGIN_CONFIG_H_
+
+#define LYEXT_PLUGINS_DIR "@EXTENSIONS_PLUGINS_DIR_MACRO@" /**< directory with YANG extension plugins */
+#define LY_USER_TYPES_PLUGINS_DIR "@USER_TYPES_PLUGINS_DIR_MACRO@" /**< directory with user YANG types plugins */
+
+#if defined __linux__ || defined __unix__
+# define LY_PLUGIN_SUFFIX ".so"
+# define LY_PLUGIN_SUFFIX_LEN 3
+#elif defined __APPLE__
+# define LY_PLUGIN_SUFFIX ".dylib"
+# define LY_PLUGIN_SUFFIX_LEN 6
+#endif
+
+#endif /* LY_PLUGIN_CONFIG_H_ */
diff --git a/src/plugins.c b/src/plugins.c
new file mode 100644
index 0000000..c80e963
--- /dev/null
+++ b/src/plugins.c
@@ -0,0 +1,505 @@
+/**
+ * @file plugins.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief YANG plugin routines implementation
+ *
+ * Copyright (c) 2015 - 2018 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 <assert.h>
+#include <errno.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <limits.h>
+
+#include "common.h"
+#include "extensions.h"
+#include "user_types.h"
+#include "plugin_config.h"
+#include "libyang.h"
+#include "parser.h"
+
+/* internal structures storing the plugins */
+static struct lyext_plugin_list *ext_plugins = NULL;
+static uint16_t ext_plugins_count = 0; /* size of the ext_plugins array */
+
+static struct lytype_plugin_list *type_plugins = NULL;
+static uint16_t type_plugins_count = 0;
+
+static struct ly_set dlhandlers = {0, 0, {NULL}};
+static pthread_mutex_t plugins_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * @brief reference counter for the plugins, it actually counts number of contexts
+ */
+static uint32_t plugin_refs;
+
+API int
+ly_clean_plugins(void)
+{
+ unsigned int u;
+ int ret = EXIT_SUCCESS;
+
+ /* lock the extension plugins list */
+ pthread_mutex_lock(&plugins_lock);
+
+ if (--plugin_refs) {
+ /* there is a context that may refer to the plugins, so we cannot remove them */
+ ret = EXIT_FAILURE;
+ goto cleanup;
+ }
+
+ if (!ext_plugins_count && !type_plugins_count) {
+ /* no plugin loaded - nothing to do */
+ goto cleanup;
+ }
+
+ /* clean the lists */
+ free(ext_plugins);
+ ext_plugins = NULL;
+ ext_plugins_count = 0;
+
+ free(type_plugins);
+ type_plugins = NULL;
+ type_plugins_count = 0;
+
+ /* close the dl handlers */
+ for (u = 0; u < dlhandlers.number; u++) {
+ dlclose(dlhandlers.set.g[u]);
+ }
+ free(dlhandlers.set.g);
+ dlhandlers.set.g = NULL;
+ dlhandlers.size = 0;
+ dlhandlers.number = 0;
+
+cleanup:
+ /* unlock the global structures */
+ pthread_mutex_unlock(&plugins_lock);
+
+ return ret;
+}
+
+static int
+lytype_load_plugin(void *dlhandler, const char *file_name)
+{
+ struct lytype_plugin_list *plugin, *p;
+ uint32_t u, v;
+ char *str;
+
+ /* get the plugin data */
+ plugin = dlsym(dlhandler, file_name);
+ str = dlerror();
+ if (str) {
+ LOGERR(NULL, LY_ESYS, "Processing \"%s\" user type plugin failed, missing plugin list object (%s).", file_name, str);
+ return 1;
+ }
+
+ for (u = 0; plugin[u].name; u++) {
+ /* check user type implementations for collisions */
+ for (v = 0; v < type_plugins_count; v++) {
+ if (!strcmp(plugin[u].name, type_plugins[v].name) &&
+ !strcmp(plugin[u].module, type_plugins[v].module) &&
+ (!plugin[u].revision || !type_plugins[v].revision || !strcmp(plugin[u].revision, type_plugins[v].revision))) {
+ LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed,"
+ "implementation collision for extension %s from module %s%s%s.",
+ file_name, plugin[u].name, plugin[u].module, plugin[u].revision ? "@" : "",
+ plugin[u].revision ? plugin[u].revision : "");
+ return 1;
+ }
+ }
+ }
+
+ /* add the new plugins, we have number of new plugins as u */
+ p = realloc(type_plugins, (type_plugins_count + u) * sizeof *type_plugins);
+ if (!p) {
+ LOGMEM(NULL);
+ return -1;
+ }
+ type_plugins = p;
+ for (; u; u--) {
+ memcpy(&type_plugins[type_plugins_count], &plugin[u - 1], sizeof *plugin);
+ type_plugins_count++;
+ }
+
+ return 0;
+}
+
+static int
+lyext_load_plugin(void *dlhandler, const char *file_name)
+{
+ struct lyext_plugin_list *plugin, *p;
+ struct lyext_plugin_complex *pluginc;
+ uint32_t u, v;
+ char *str;
+
+ /* get the plugin data */
+ plugin = dlsym(dlhandler, file_name);
+ str = dlerror();
+ if (str) {
+ LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed, missing plugin list object (%s).", file_name, str);
+ return 1;
+ }
+
+ for (u = 0; plugin[u].name; u++) {
+ /* check extension implementations for collisions */
+ for (v = 0; v < ext_plugins_count; v++) {
+ if (!strcmp(plugin[u].name, ext_plugins[v].name) &&
+ !strcmp(plugin[u].module, ext_plugins[v].module) &&
+ (!plugin[u].revision || !ext_plugins[v].revision || !strcmp(plugin[u].revision, ext_plugins[v].revision))) {
+ LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed,"
+ "implementation collision for extension %s from module %s%s%s.",
+ file_name, plugin[u].name, plugin[u].module, plugin[u].revision ? "@" : "",
+ plugin[u].revision ? plugin[u].revision : "");
+ return 1;
+ }
+ }
+
+ /* check for valid supported substatements in case of complex extension */
+ if (plugin[u].plugin->type == LYEXT_COMPLEX && ((struct lyext_plugin_complex *)plugin[u].plugin)->substmt) {
+ pluginc = (struct lyext_plugin_complex *)plugin[u].plugin;
+ for (v = 0; pluginc->substmt[v].stmt; v++) {
+ if (pluginc->substmt[v].stmt >= LY_STMT_SUBMODULE ||
+ pluginc->substmt[v].stmt == LY_STMT_VERSION ||
+ pluginc->substmt[v].stmt == LY_STMT_YINELEM) {
+ LOGERR(NULL, LY_EINVAL,
+ "Extension plugin \"%s\" (extension %s) allows not supported extension substatement (%s)",
+ file_name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
+ return 1;
+ }
+ if (pluginc->substmt[v].cardinality > LY_STMT_CARD_MAND &&
+ pluginc->substmt[v].stmt >= LY_STMT_MODIFIER &&
+ pluginc->substmt[v].stmt <= LY_STMT_STATUS) {
+ LOGERR(NULL, LY_EINVAL, "Extension plugin \"%s\" (extension %s) allows multiple instances on \"%s\" "
+ "substatement, which is not supported.",
+ file_name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
+ return 1;
+ }
+ }
+ }
+ }
+
+ /* add the new plugins, we have number of new plugins as u */
+ p = realloc(ext_plugins, (ext_plugins_count + u) * sizeof *ext_plugins);
+ if (!p) {
+ LOGMEM(NULL);
+ return -1;
+ }
+ ext_plugins = p;
+ for (; u; u--) {
+ memcpy(&ext_plugins[ext_plugins_count], &plugin[u - 1], sizeof *plugin);
+ ext_plugins_count++;
+ }
+
+ return 0;
+}
+
+static void
+ly_load_plugins_dir(DIR *dir, const char *dir_path, int ext_or_type)
+{
+ struct dirent *file;
+ size_t len;
+ char *str;
+ char name[NAME_MAX];
+ void *dlhandler;
+ int ret;
+
+ while ((file = readdir(dir))) {
+ /* required format of the filename is *LY_PLUGIN_SUFFIX */
+ len = strlen(file->d_name);
+ if (len < LY_PLUGIN_SUFFIX_LEN + 1 ||
+ strcmp(&file->d_name[len - LY_PLUGIN_SUFFIX_LEN], LY_PLUGIN_SUFFIX)) {
+ continue;
+ }
+
+ /* store the name without the suffix */
+ memcpy(name, file->d_name, len - LY_PLUGIN_SUFFIX_LEN);
+ name[len - LY_PLUGIN_SUFFIX_LEN] = '\0';
+
+ /* and construct the filepath */
+ asprintf(&str, "%s/%s", dir_path, file->d_name);
+
+ /* load the plugin - first, try if it is already loaded... */
+ dlhandler = dlopen(str, RTLD_NOW | RTLD_NOLOAD);
+ dlerror(); /* Clear any existing error */
+ if (dlhandler) {
+ /* the plugin is already loaded */
+ LOGVRB("Plugin \"%s\" already loaded.", str);
+ free(str);
+
+ /* keep the refcount of the shared object correct */
+ dlclose(dlhandler);
+ continue;
+ }
+
+ /* ... and if not, load it */
+ dlhandler = dlopen(str, RTLD_NOW);
+ if (!dlhandler) {
+ LOGERR(NULL, LY_ESYS, "Loading \"%s\" as a plugin failed (%s).", str, dlerror());
+ free(str);
+ continue;
+ }
+ LOGVRB("Plugin \"%s\" successfully loaded.", str);
+ free(str);
+ dlerror(); /* Clear any existing error */
+
+ if (ext_or_type) {
+ ret = lyext_load_plugin(dlhandler, name);
+ } else {
+ ret = lytype_load_plugin(dlhandler, name);
+ }
+ if (ret == 1) {
+ dlclose(dlhandler);
+ continue;
+ } else if (ret == -1) {
+ dlclose(dlhandler);
+ break;
+ }
+
+ /* keep the handler */
+ ly_set_add(&dlhandlers, dlhandler, LY_SET_OPT_USEASLIST);
+ }
+}
+
+API void
+ly_load_plugins(void)
+{
+ DIR* dir;
+ const char *pluginsdir;
+
+ /* lock the extension plugins list */
+ pthread_mutex_lock(&plugins_lock);
+
+ /* increase references */
+ ++plugin_refs;
+
+ /* try to get the plugins directory from environment variable */
+ pluginsdir = getenv("LIBYANG_EXTENSIONS_PLUGINS_DIR");
+ if (!pluginsdir) {
+ pluginsdir = LYEXT_PLUGINS_DIR;
+ }
+
+ dir = opendir(pluginsdir);
+ if (!dir) {
+ /* no directory (or no access to it), no extension plugins */
+ LOGWRN(NULL, "Failed to open libyang extensions plugins directory \"%s\" (%s).", pluginsdir, strerror(errno));
+ } else {
+ ly_load_plugins_dir(dir, pluginsdir, 1);
+ closedir(dir);
+ }
+
+ /* try to get the plugins directory from environment variable */
+ pluginsdir = getenv("LIBYANG_USER_TYPES_PLUGINS_DIR");
+ if (!pluginsdir) {
+ pluginsdir = LY_USER_TYPES_PLUGINS_DIR;
+ }
+
+ dir = opendir(pluginsdir);
+ if (!dir) {
+ /* no directory (or no access to it), no extension plugins */
+ LOGWRN(NULL, "Failed to open libyang user types plugins directory \"%s\" (%s).", pluginsdir, strerror(errno));
+ } else {
+ ly_load_plugins_dir(dir, pluginsdir, 0);
+ closedir(dir);
+ }
+
+ /* unlock the global structures */
+ pthread_mutex_unlock(&plugins_lock);
+}
+
+struct lyext_plugin *
+ext_get_plugin(const char *name, const char *module, const char *revision)
+{
+ uint16_t u;
+
+ assert(name);
+ assert(module);
+
+ for (u = 0; u < ext_plugins_count; u++) {
+ if (!strcmp(name, ext_plugins[u].name) &&
+ !strcmp(module, ext_plugins[u].module) &&
+ (!ext_plugins[u].revision || !strcmp(revision, ext_plugins[u].revision))) {
+ /* we have the match */
+ return ext_plugins[u].plugin;
+ }
+ }
+
+ /* plugin not found */
+ return NULL;
+}
+
+API int
+lys_ext_instance_presence(struct lys_ext *def, struct lys_ext_instance **ext, uint8_t ext_size)
+{
+ uint8_t index;
+
+ if (!def || (ext_size && !ext)) {
+ LOGARG;
+ return -1;
+ }
+
+ /* search for the extension instance */
+ for (index = 0; index < ext_size; index++) {
+ if (ext[index]->def == def) {
+ return index;
+ }
+ }
+
+ /* not found */
+ return -1;
+}
+
+API void *
+lys_ext_complex_get_substmt(LY_STMT stmt, struct lys_ext_instance_complex *ext, struct lyext_substmt **info)
+{
+ int i;
+
+ if (!ext || !ext->def || !ext->def->plugin || ext->def->plugin->type != LYEXT_COMPLEX) {
+ LOGARG;
+ return NULL;
+ }
+
+ if (!ext->substmt) {
+ /* no substatement defined in the plugin */
+ if (info) {
+ *info = NULL;
+ }
+ return NULL;
+ }
+
+ /* search the substatements defined by the plugin */
+ for (i = 0; ext->substmt[i].stmt; i++) {
+ if (stmt == LY_STMT_NODE) {
+ if (ext->substmt[i].stmt >= LY_STMT_ACTION && ext->substmt[i].stmt <= LY_STMT_USES) {
+ if (info) {
+ *info = &ext->substmt[i];
+ }
+ break;
+ }
+ } else if (ext->substmt[i].stmt == stmt) {
+ if (info) {
+ *info = &ext->substmt[i];
+ }
+ break;
+ }
+ }
+
+ if (ext->substmt[i].stmt) {
+ return &ext->content[ext->substmt[i].offset];
+ } else {
+ return NULL;
+ }
+}
+
+LY_STMT
+lys_snode2stmt(LYS_NODE nodetype)
+{
+ switch(nodetype) {
+ case LYS_CONTAINER:
+ return LY_STMT_CONTAINER;
+ case LYS_CHOICE:
+ return LY_STMT_CHOICE;
+ case LYS_LEAF:
+ return LY_STMT_LEAF;
+ case LYS_LEAFLIST:
+ return LY_STMT_LEAFLIST;
+ case LYS_LIST:
+ return LY_STMT_LIST;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ return LY_STMT_ANYDATA;
+ case LYS_CASE:
+ return LY_STMT_CASE;
+ case LYS_NOTIF:
+ return LY_STMT_NOTIFICATION;
+ case LYS_RPC:
+ return LY_STMT_RPC;
+ case LYS_INPUT:
+ return LY_STMT_INPUT;
+ case LYS_OUTPUT:
+ return LY_STMT_OUTPUT;
+ case LYS_GROUPING:
+ return LY_STMT_GROUPING;
+ case LYS_USES:
+ return LY_STMT_USES;
+ case LYS_AUGMENT:
+ return LY_STMT_AUGMENT;
+ case LYS_ACTION:
+ return LY_STMT_ACTION;
+ default:
+ return LY_STMT_NODE;
+ }
+}
+
+static struct lytype_plugin_list *
+lytype_find(const char *module, const char *revision, const char *type_name)
+{
+ uint16_t u;
+
+ for (u = 0; u < type_plugins_count; ++u) {
+ if (ly_strequal(module, type_plugins[u].module, 0) && ((!revision && !type_plugins[u].revision)
+ || (revision && ly_strequal(revision, type_plugins[u].revision, 0)))
+ && ly_strequal(type_name, type_plugins[u].name, 0)) {
+ return &(type_plugins[u]);
+ }
+ }
+
+ return NULL;
+}
+
+int
+lytype_store(const struct lys_module *mod, const char *type_name, const char *value_str, lyd_val *value)
+{
+ struct lytype_plugin_list *p;
+ char *err_msg = NULL;
+
+ assert(mod && type_name && value_str && value);
+
+ p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type_name);
+ if (p) {
+ if (p->store_clb(type_name, value_str, value, &err_msg)) {
+ if (!err_msg) {
+ if (asprintf(&err_msg, "Failed to store value \"%s\" of user type \"%s\".", value_str, type_name) == -1) {
+ LOGMEM(mod->ctx);
+ return -1;
+ }
+ }
+ LOGERR(mod->ctx, LY_EPLUGIN, err_msg);
+ free(err_msg);
+ return -1;
+ }
+
+ /* value successfully stored */
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+lytype_free(const struct lys_module *mod, const char *type_name, lyd_val value)
+{
+ struct lytype_plugin_list *p;
+
+ p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type_name);
+ if (!p) {
+ LOGINT(mod->ctx);
+ return;
+ }
+
+ if (p->free_clb) {
+ p->free_clb(value.ptr);
+ }
+}
diff --git a/src/resolve.c b/src/resolve.c
index 176fb97..920cef9 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -3448,9 +3448,7 @@
}
cleanup:
- if (node.value_type == LY_TYPE_BITS) {
- free(node.value.bit);
- }
+ lyd_free_value(node.value, node.value_type, type);
lydict_remove(ctx, node.value_str);
if (tpdf && node.schema) {
free((char *)node.schema->name);
@@ -6891,7 +6889,7 @@
/* final check */
if (eplugin->check_result) {
if ((*eplugin->check_result)(ext)) {
- LOGERR(ctx, LY_EEXT, "Resolving extension failed.");
+ LOGERR(ctx, LY_EPLUGIN, "Resolving extension failed.");
return -1;
}
}
@@ -7683,13 +7681,11 @@
if ((leaf->value_type == LY_TYPE_UNION) || (leaf->value_type == (LY_TYPE_INST | LY_TYPE_INST_UNRES))) {
/* either NULL or instid previously converted to JSON */
- json_val = leaf->value.string;
+ json_val = lydict_insert(ctx, leaf->value.string, 0);
}
if (store) {
- if ((leaf->value_type & LY_DATA_TYPE_MASK) == LY_TYPE_BITS) {
- free(leaf->value.bit);
- }
+ lyd_free_value(leaf->value, leaf->value_type, &((struct lys_node_leaf *)leaf->schema)->type);
memset(&leaf->value, 0, sizeof leaf->value);
}
@@ -7777,9 +7773,7 @@
/* erase possible present and invalid value data */
if (store) {
- if (t->base == LY_TYPE_BITS) {
- free(leaf->value.bit);
- }
+ lyd_free_value(leaf->value, leaf->value_type, t);
memset(&leaf->value, 0, sizeof leaf->value);
}
}
diff --git a/src/tree_data.c b/src/tree_data.c
index d888c55..7211232 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -1848,9 +1848,7 @@
trg_leaf->validity |= LYD_VAL_LEAFREF;
trg_leaf->value.leafref = NULL;
} else {
- if ((trg_leaf->value_type & LY_DATA_TYPE_MASK) == LY_TYPE_BITS) {
- free(trg_leaf->value.bit);
- }
+ lyd_free_value(trg_leaf->value, trg_leaf->value_type, &((struct lys_node_leaf *)trg_leaf->schema)->type);
trg_leaf->value = src_leaf->value;
}
src_leaf->value = (lyd_val)0;
@@ -1895,9 +1893,7 @@
lydict_remove(ctx, trg_leaf->value_str);
trg_leaf->value_str = lydict_insert(ctx, src_leaf->value_str, 0);
- if ((trg_leaf->value_type & LY_DATA_TYPE_MASK) == LY_TYPE_BITS) {
- free(trg_leaf->value.bit);
- }
+ lyd_free_value(trg_leaf->value, trg_leaf->value_type, &((struct lys_node_leaf *)trg_leaf->schema)->type);
trg_leaf->value_type = src_leaf->value_type;
trg_leaf->dflt = src_leaf->dflt;
@@ -4844,6 +4840,7 @@
lyd_free_attr(struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_attr *attr, int recursive)
{
struct lyd_attr *iter;
+ struct lys_type **type;
if (!ctx || !attr) {
return;
@@ -4877,19 +4874,9 @@
iter = iter->next;
lydict_remove(ctx, attr->name);
- switch (attr->value_type & LY_DATA_TYPE_MASK) {
- case LY_TYPE_BITS:
- if (attr->value.bit) {
- free(attr->value.bit);
- }
- break;
- case LY_TYPE_UNION:
- /* unresolved union leaf */
- lydict_remove(ctx, attr->value.string);
- break;
- default:
- break;
- }
+ type = lys_ext_complex_get_substmt(LY_STMT_TYPE, attr->annotation, NULL);
+ assert(type);
+ lyd_free_value(attr->value, attr->value_type, *type);
lydict_remove(ctx, attr->value_str);
free(attr);
}
@@ -4997,10 +4984,39 @@
return a;
}
+void
+lyd_free_value(lyd_val value, uint16_t value_type, struct lys_type *type)
+{
+ if (value_type & LY_TYPE_USER) {
+ assert(type->der && type->der->module);
+ lytype_free(type->der->module, type->der->name, value);
+ } else {
+ switch (value_type & LY_DATA_TYPE_MASK) {
+ case LY_TYPE_BITS:
+ if (value.bit) {
+ free(value.bit);
+ }
+ break;
+ case LY_TYPE_INST:
+ if (!(value_type & LY_TYPE_INST_UNRES)) {
+ break;
+ }
+ /* fallthrough */
+ case LY_TYPE_UNION:
+ /* unresolved union leaf */
+ lydict_remove(type->parent->module->ctx, value.string);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
API void
lyd_free(struct lyd_node *node)
{
struct lyd_node *next, *iter;
+ struct lyd_node_leaf_list *leaf;
if (!node) {
return;
@@ -5032,22 +5048,9 @@
break;
}
} else { /* LYS_LEAF | LYS_LEAFLIST */
- /* free value */
- switch (((struct lyd_node_leaf_list *)node)->value_type & LY_DATA_TYPE_MASK) {
- case LY_TYPE_BITS:
- if (((struct lyd_node_leaf_list *)node)->value.bit) {
- free(((struct lyd_node_leaf_list *)node)->value.bit);
- }
- break;
- case LY_TYPE_UNION:
- /* unresolved union leaf */
- lydict_remove(node->schema->module->ctx, ((struct lyd_node_leaf_list *)node)->value.string);
- break;
- default:
- break;
- }
-
- lydict_remove(node->schema->module->ctx, ((struct lyd_node_leaf_list *)node)->value_str);
+ leaf = (struct lyd_node_leaf_list *)node;
+ lyd_free_value(leaf->value, leaf->value_type, &((struct lys_node_leaf *)leaf->schema)->type);
+ lydict_remove(leaf->schema->module->ctx, leaf->value_str);
}
lyd_unlink(node);
diff --git a/src/tree_data.h b/src/tree_data.h
index cf05d81..5480c86 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -104,6 +104,7 @@
uint16_t uint16; /**< 16-bit signed integer */
uint32_t uint32; /**< 32-bit signed integer */
uint64_t uint64; /**< 64-bit signed integer */
+ void *ptr; /**< arbitrary data stored using a type plugin */
} lyd_val;
/**
diff --git a/src/tree_internal.h b/src/tree_internal.h
index c5d3bfd..d3c9ec0 100644
--- a/src/tree_internal.h
+++ b/src/tree_internal.h
@@ -439,6 +439,8 @@
int lyd_get_unique_default(const char* unique_expr, struct lyd_node *list, const char **dflt);
+void lyd_free_value(lyd_val value, uint16_t value_type, struct lys_type *type);
+
/**
* @brief Check for (validate) mandatory nodes of a data tree. Checks recursively whole data tree. Requires all when
* statement to be solved.
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 2cbe1bf..3a09b73 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -582,16 +582,16 @@
void *lys_ext_complex_get_substmt(LY_STMT stmt, struct lys_ext_instance_complex *ext, struct lyext_substmt **info);
/**
- * @brief Load the available YANG extensions plugins from the plugin directory (LIBDIR/libyang/).
+ * @brief Load the available YANG extension and type plugins from the plugin directory (LIBDIR/libyang/).
*
* This function is automatically called whenever a new context is created. Note that the removed plugins are kept
* in use until all the created contexts are destroyed via ly_ctx_destroy(), so only the newly added plugins are
* usually loaded by this function.
*/
-void lyext_load_plugins(void);
+void ly_load_plugins(void);
/**
- * @brief Unload all the YANG extensions plugins.
+ * @brief Unload all the YANG extension and type plugins.
*
* This function is automatically called whenever the context is destroyed. Note, that in case there is still a
* libyang context in use, the function does nothing since unloading the plugins would break the context's modules
@@ -599,7 +599,7 @@
*
* Since the function is called with ly_ctx_destroy(), there is usually no need to call this function manually.
*/
-int lyext_clean_plugins(void);
+int ly_clean_plugins(void);
/**
* @}
@@ -764,6 +764,7 @@
the value union is filled as if being the target node's type */
#define LY_TYPE_INST_UNRES 0x80 /**< flag for unresolved instance-identifier, always used in conjunction with LY_TYPE_INST
and the value union should not be accessed */
+#define LY_TYPE_USER 0x100 /**< flag for a user type stored value */
/**
*
diff --git a/src/user_types.h b/src/user_types.h
new file mode 100644
index 0000000..e6b0572
--- /dev/null
+++ b/src/user_types.h
@@ -0,0 +1,64 @@
+/**
+ * @file user_types.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang support for user YANG type implementations.
+ *
+ * Copyright (c) 2018 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 LY_USER_TYPES_H_
+#define LY_USER_TYPES_H_
+
+#include "libyang.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup user_types User Types
+ * @ingroup schematree
+ * @{
+ */
+
+/**
+ * @brief Callback for storing user type values.
+ *
+ * This callback should overwrite the value stored in \p value using some custom encoding. Be careful,
+ * if the type is #LY_TYPE_BITS, the bits must be freed before overwritting the union value.
+ *
+ * @param[in] type_name Name of the type being stored.
+ * @param[in] value_str String value to be stored.
+ * @param[in,out] value Value union for the value to be stored in (already is but in the standard way).
+ * @param[out] err_msg Can be filled on error. If not, a generic error message will be printed.
+ * @return 0 on success, non-zero if an error occured and the value could not be stored for any reason.
+ */
+typedef int (*lytype_store_clb)(const char *type_name, const char *value_str, lyd_val *value, char **err_msg);
+
+struct lytype_plugin_list {
+ const char *module; /**< Name of the module where the type is defined. */
+ const char *revision; /**< Optional module revision - if not specified, the plugin applies to any revision,
+ which is not the best approach due to a possible future revisions of the module.
+ Instead, there should be defined multiple items in the plugins list, each with the
+ different revision, but all with the same store callback. The only valid use case
+ for the NULL revision is the case when the module has no revision. */
+ const char *name; /**< Name of the type to be stored in a custom way. */
+ lytype_store_clb store_clb; /**< Callback used for storing values of this type. */
+ void (*free_clb)(void *ptr); /**< Callback used for freeing values of this type. */
+};
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_USER_TYPES_H_ */
diff --git a/src/user_types/user_ipv4.c b/src/user_types/user_ipv4.c
new file mode 100644
index 0000000..b3d0be8
--- /dev/null
+++ b/src/user_types/user_ipv4.c
@@ -0,0 +1,37 @@
+/**
+ * @file user_ipv4.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief example implementation of an ipv4-address as a user type
+ *
+ * Copyright (c) 2018 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 <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include <libyang/user_types.h>
+
+static int
+ipv4_store_clb(const char *type_name, const char *value_str, lyd_val *value, char **err_msg)
+{
+ return 1;
+ value->ptr = malloc(INET_ADDRSTRLEN);
+ if (inet_pton(AF_INET, value_str, value->ptr) != 1) {
+ free(value->ptr);
+ return 1;
+ }
+ return 0;
+}
+
+struct lytype_plugin_list user_ipv4[] = {
+ {"ietf-inet-types", "2013-07-15", "ipv4-address", ipv4_store_clb, free},
+ {"ietf-inet-types", "2013-07-15", "ipv4-address-no-zone", ipv4_store_clb, free},
+ {NULL, NULL, NULL, NULL, NULL} /* terminating item */
+};
diff --git a/src/user_types/user_ipv4.so b/src/user_types/user_ipv4.so
new file mode 100755
index 0000000..513dfbf
--- /dev/null
+++ b/src/user_types/user_ipv4.so
Binary files differ