blob: 5ff6616e0ad99237a40dc71ccc048e2d9e75264c [file] [log] [blame]
Radek Krejci3e6632f2021-03-22 22:08:21 +01001/**
2 * @file plugins.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief Manipulate with the type and extension plugins.
5 *
6 * Copyright (c) 2021 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
Radek Krejci968d7552021-03-26 20:33:51 +010015#define _GNU_SOURCE
16
Radek Krejci3e6632f2021-03-22 22:08:21 +010017#include "plugins.h"
18#include "plugins_internal.h"
19
20#include <assert.h>
Radek Krejci968d7552021-03-26 20:33:51 +010021#include <dirent.h>
Michal Vasko0d8434d2024-02-20 15:49:10 +010022#ifndef STATIC
23# include <dlfcn.h>
24#endif
Radek Krejci968d7552021-03-26 20:33:51 +010025#include <errno.h>
26#include <limits.h>
Radek Krejci3e6632f2021-03-22 22:08:21 +010027#include <pthread.h>
Radek Krejcibf940f92021-03-24 21:04:13 +010028#include <stddef.h>
Radek Krejci0aa1f702021-04-01 16:16:19 +020029#include <stdint.h>
30#include <stdio.h>
31#include <stdlib.h>
Radek Krejci3e6632f2021-03-22 22:08:21 +010032#include <string.h>
33
Michal Vasko8f702ee2024-02-20 15:44:24 +010034#include "ly_common.h"
35#include "ly_config.h"
Radek Krejci3e6632f2021-03-22 22:08:21 +010036#include "plugins_exts.h"
37#include "plugins_types.h"
Radek Krejci0aa1f702021-04-01 16:16:19 +020038#include "set.h"
Radek Krejci3e6632f2021-03-22 22:08:21 +010039
40/*
41 * internal type plugins records
42 */
43extern const struct lyplg_type_record plugins_binary[];
44extern const struct lyplg_type_record plugins_bits[];
45extern const struct lyplg_type_record plugins_boolean[];
46extern const struct lyplg_type_record plugins_decimal64[];
47extern const struct lyplg_type_record plugins_empty[];
48extern const struct lyplg_type_record plugins_enumeration[];
49extern const struct lyplg_type_record plugins_identityref[];
50extern const struct lyplg_type_record plugins_instanceid[];
51extern const struct lyplg_type_record plugins_integer[];
52extern const struct lyplg_type_record plugins_leafref[];
53extern const struct lyplg_type_record plugins_string[];
54extern const struct lyplg_type_record plugins_union[];
55
Michal Vaskode4a3412021-04-14 15:38:27 +020056/*
Michal Vasko79a7a872022-06-17 09:00:48 +020057 * yang
58 */
59extern const struct lyplg_type_record plugins_instanceid_keys[];
60
61/*
Michal Vaskode4a3412021-04-14 15:38:27 +020062 * ietf-inet-types
63 */
Michal Vasko3159e782021-05-03 15:12:35 +020064extern const struct lyplg_type_record plugins_ipv4_address[];
Michal Vasko82bf15e2021-05-06 16:01:56 +020065extern const struct lyplg_type_record plugins_ipv4_address_no_zone[];
Michal Vasko7caa3e62021-05-03 14:59:25 +020066extern const struct lyplg_type_record plugins_ipv6_address[];
Michal Vasko18a4a732021-05-06 16:21:44 +020067extern const struct lyplg_type_record plugins_ipv6_address_no_zone[];
Michal Vasko15dc9fa2021-05-03 14:33:05 +020068extern const struct lyplg_type_record plugins_ipv4_prefix[];
69extern const struct lyplg_type_record plugins_ipv6_prefix[];
Michal Vasko3e52de52021-04-13 13:45:55 +020070
Radek Krejci3e6632f2021-03-22 22:08:21 +010071/*
Michal Vaskode4a3412021-04-14 15:38:27 +020072 * ietf-yang-types
73 */
74extern const struct lyplg_type_record plugins_date_and_time[];
Michal Vasko30e75a72023-08-08 11:35:54 +020075extern const struct lyplg_type_record plugins_hex_string[];
Michal Vaskode4a3412021-04-14 15:38:27 +020076extern const struct lyplg_type_record plugins_xpath10[];
77
78/*
Michal Vasko7a53c7d2021-08-06 11:28:57 +020079 * ietf-netconf-acm
80 */
81extern const struct lyplg_type_record plugins_node_instanceid[];
82
83/*
aPiecek6cf1d162023-11-08 16:07:00 +010084 * lyds_tree
85 */
86extern const struct lyplg_type_record plugins_lyds_tree[];
87
88/*
Radek Krejci3e6632f2021-03-22 22:08:21 +010089 * internal extension plugins records
90 */
91extern struct lyplg_ext_record plugins_metadata[];
92extern struct lyplg_ext_record plugins_nacm[];
93extern struct lyplg_ext_record plugins_yangdata[];
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010094extern struct lyplg_ext_record plugins_schema_mount[];
Michal Vaskoedb0fa52022-10-04 10:36:00 +020095extern struct lyplg_ext_record plugins_structure[];
Radek Krejci3e6632f2021-03-22 22:08:21 +010096
97static pthread_mutex_t plugins_guard = PTHREAD_MUTEX_INITIALIZER;
98
99/**
100 * @brief Counter for currently present contexts able to refer to the loaded plugins.
101 *
102 * Plugins are shared among all the created contexts. They are loaded with the creation of the very first context and
103 * unloaded with the destroy of the last context. Therefore, to reload the list of plugins, all the contexts must be
104 * destroyed and with the creation of a first new context after that, the plugins will be reloaded.
105 */
106static uint32_t context_refcount = 0;
107
108/**
109 * @brief Record describing an implemented extension.
110 *
111 * Matches ::lyplg_ext_record and ::lyplg_type_record
112 */
113struct lyplg_record {
114 const char *module; /**< name of the module where the extension/type is defined */
115 const char *revision; /**< optional module revision - if not specified, the plugin applies to any revision,
116 which is not an optimal approach due to a possible future revisions of the module.
117 Instead, there should be defined multiple items in the plugins list, each with the
118 different revision, but all with the same pointer to the plugin functions. The
119 only valid use case for the NULL revision is the case the module has no revision. */
120 const char *name; /**< name of the extension/typedef */
121 int8_t plugin[]; /**< specific plugin type's data - ::lyplg_ext or ::lyplg_type */
122};
123
Michal Vasko9e2bc702021-06-09 11:43:36 +0200124#ifndef STATIC
Radek Krejcibf940f92021-03-24 21:04:13 +0100125static struct ly_set plugins_handlers = {0};
Michal Vasko9e2bc702021-06-09 11:43:36 +0200126#endif
Radek Krejci3e6632f2021-03-22 22:08:21 +0100127static struct ly_set plugins_types = {0};
128static struct ly_set plugins_extensions = {0};
129
130/**
131 * @brief Iterate over list of loaded plugins of the given @p type.
132 *
stewegf7aeeae2024-04-02 13:15:52 +0200133 * @param[in] ctx The context for which the plugin is searched for
Radek Krejci3e6632f2021-03-22 22:08:21 +0100134 * @param[in] type Type of the plugins to iterate.
135 * @param[in,out] index The iterator - set to 0 for the first call.
136 * @return The plugin records, NULL if no more record is available.
137 */
138static struct lyplg_record *
stewegf7aeeae2024-04-02 13:15:52 +0200139plugins_iter(const struct ly_ctx *ctx, enum LYPLG type, uint32_t *index)
Radek Krejci3e6632f2021-03-22 22:08:21 +0100140{
stewegf7aeeae2024-04-02 13:15:52 +0200141 const struct ly_set *plugins;
Radek Krejci3e6632f2021-03-22 22:08:21 +0100142
143 assert(index);
144
145 if (type == LYPLG_EXTENSION) {
stewegf7aeeae2024-04-02 13:15:52 +0200146 plugins = ctx ? &ctx->plugins_extensions : &plugins_extensions;
Radek Krejci3e6632f2021-03-22 22:08:21 +0100147 } else {
stewegf7aeeae2024-04-02 13:15:52 +0200148 plugins = ctx ? &ctx->plugins_types : &plugins_types;
Radek Krejci3e6632f2021-03-22 22:08:21 +0100149 }
150
151 if (*index == plugins->count) {
152 return NULL;
153 }
154
155 *index += 1;
156 return plugins->objs[*index - 1];
157}
158
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200159static void *
stewegf7aeeae2024-04-02 13:15:52 +0200160lyplg_record_find(const struct ly_ctx *ctx, enum LYPLG type, const char *module, const char *revision, const char *name)
Radek Krejci3e6632f2021-03-22 22:08:21 +0100161{
162 uint32_t i = 0;
163 struct lyplg_record *item;
164
165 assert(module);
166 assert(name);
167
stewegf7aeeae2024-04-02 13:15:52 +0200168 while ((item = plugins_iter(ctx, type, &i)) != NULL) {
Radek Krejci3e6632f2021-03-22 22:08:21 +0100169 if (!strcmp(item->module, module) && !strcmp(item->name, name)) {
170 if (item->revision && revision && strcmp(item->revision, revision)) {
171 continue;
172 } else if (!revision && item->revision) {
173 continue;
174 }
175
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200176 return item;
Radek Krejci3e6632f2021-03-22 22:08:21 +0100177 }
178 }
179
180 return NULL;
181}
182
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200183struct lyplg_type *
stewegf7aeeae2024-04-02 13:15:52 +0200184lyplg_type_plugin_find(const struct ly_ctx *ctx, const char *module, const char *revision, const char *name)
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200185{
stewegf7aeeae2024-04-02 13:15:52 +0200186 struct lyplg_record *record = NULL;
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200187
stewegf7aeeae2024-04-02 13:15:52 +0200188 if (ctx) {
189 /* try to find context specific plugin */
190 record = lyplg_record_find(ctx, LYPLG_TYPE, module, revision, name);
191 }
192
193 if (!record) {
194 /* try to find shared plugin */
195 record = lyplg_record_find(NULL, LYPLG_TYPE, module, revision, name);
196 }
197
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200198 return record ? &((struct lyplg_type_record *)record)->plugin : NULL;
199}
200
201struct lyplg_ext_record *
stewegf7aeeae2024-04-02 13:15:52 +0200202lyplg_ext_record_find(const struct ly_ctx *ctx, const char *module, const char *revision, const char *name)
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200203{
stewegf7aeeae2024-04-02 13:15:52 +0200204 struct lyplg_ext_record *record = NULL;
205
206 if (ctx) {
207 /* try to find context specific plugin */
208 record = lyplg_record_find(ctx, LYPLG_EXTENSION, module, revision, name);
209 }
210
211 if (!record) {
212 /* try to find shared plugin */
213 record = lyplg_record_find(NULL, LYPLG_EXTENSION, module, revision, name);
214 }
215
216 return record;
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200217}
218
Radek Krejci3e6632f2021-03-22 22:08:21 +0100219/**
220 * @brief Insert the provided extension plugin records into the internal set of extension plugins for use by libyang.
221 *
stewegf7aeeae2024-04-02 13:15:52 +0200222 * @param[in] ctx The context to which the plugin should be associated with. If NULL, the plugin is considered to be shared
223 * between all existing contexts.
224 * @param[in] type The type of plugins records
Radek Krejci3e6632f2021-03-22 22:08:21 +0100225 * @param[in] recs An array of plugin records provided by the plugin implementation. The array must be terminated by a zeroed
226 * record.
227 * @return LY_SUCCESS in case of success
228 * @return LY_EINVAL for invalid information in @p recs.
229 * @return LY_EMEM in case of memory allocation failure.
230 */
231static LY_ERR
stewegf7aeeae2024-04-02 13:15:52 +0200232plugins_insert(struct ly_ctx *ctx, enum LYPLG type, const void *recs)
Radek Krejci3e6632f2021-03-22 22:08:21 +0100233{
stewegf7aeeae2024-04-02 13:15:52 +0200234 struct ly_set *plugins;
235
Radek Krejci3e6632f2021-03-22 22:08:21 +0100236 if (!recs) {
237 return LY_SUCCESS;
238 }
239
240 if (type == LYPLG_EXTENSION) {
241 const struct lyplg_ext_record *rec = (const struct lyplg_ext_record *)recs;
242
stewegf7aeeae2024-04-02 13:15:52 +0200243 plugins = ctx ? &ctx->plugins_extensions : &plugins_extensions;
244
Radek Krejci3e6632f2021-03-22 22:08:21 +0100245 for (uint32_t i = 0; rec[i].name; i++) {
stewegf7aeeae2024-04-02 13:15:52 +0200246 LY_CHECK_RET(ly_set_add(plugins, (void *)&rec[i], 0, NULL));
Radek Krejci3e6632f2021-03-22 22:08:21 +0100247 }
Radek Krejcibf940f92021-03-24 21:04:13 +0100248 } else { /* LYPLG_TYPE */
Radek Krejci3e6632f2021-03-22 22:08:21 +0100249 const struct lyplg_type_record *rec = (const struct lyplg_type_record *)recs;
250
stewegf7aeeae2024-04-02 13:15:52 +0200251 plugins = ctx ? &ctx->plugins_types : &plugins_types;
252
Radek Krejci3e6632f2021-03-22 22:08:21 +0100253 for (uint32_t i = 0; rec[i].name; i++) {
stewegf7aeeae2024-04-02 13:15:52 +0200254 LY_CHECK_RET(ly_set_add(plugins, (void *)&rec[i], 0, NULL));
Radek Krejci3e6632f2021-03-22 22:08:21 +0100255 }
256 }
257
258 return LY_SUCCESS;
259}
260
Michal Vasko9e2bc702021-06-09 11:43:36 +0200261#ifndef STATIC
262
Radek Krejci3e6632f2021-03-22 22:08:21 +0100263static void
Michal Vaskoe43a34e2021-04-13 13:43:04 +0200264lyplg_close_cb(void *handle)
265{
266 dlclose(handle);
267}
268
269static void
Radek Krejci3e6632f2021-03-22 22:08:21 +0100270lyplg_clean_(void)
271{
272 if (--context_refcount) {
273 /* there is still some other context, do not remove the plugins */
274 return;
275 }
276
277 ly_set_erase(&plugins_types, NULL);
278 ly_set_erase(&plugins_extensions, NULL);
Michal Vaskoe43a34e2021-04-13 13:43:04 +0200279 ly_set_erase(&plugins_handlers, lyplg_close_cb);
Radek Krejci3e6632f2021-03-22 22:08:21 +0100280}
281
Michal Vasko9e2bc702021-06-09 11:43:36 +0200282#endif
283
Radek Krejci3e6632f2021-03-22 22:08:21 +0100284void
285lyplg_clean(void)
286{
Michal Vasko9e2bc702021-06-09 11:43:36 +0200287#ifndef STATIC
Radek Krejci3e6632f2021-03-22 22:08:21 +0100288 pthread_mutex_lock(&plugins_guard);
289 lyplg_clean_();
290 pthread_mutex_unlock(&plugins_guard);
Michal Vasko9e2bc702021-06-09 11:43:36 +0200291#endif
Radek Krejci3e6632f2021-03-22 22:08:21 +0100292}
293
Michal Vasko9e2bc702021-06-09 11:43:36 +0200294#ifndef STATIC
295
296/**
297 * @brief Just a variadic data to cover extension and type plugins by a single ::plugins_load() function.
298 *
299 * The values are taken from ::LY_PLUGINS_EXTENSIONS and ::LYPLG_TYPES macros.
300 */
301static const struct {
302 const char *id; /**< string identifier: type/extension */
303 const char *apiver_var; /**< expected variable name holding API version value */
304 const char *plugins_var; /**< expected variable name holding plugin records */
305 const char *envdir; /**< environment variable containing directory with the plugins */
306 const char *dir; /**< default directory with the plugins (has less priority than envdir) */
307 uint32_t apiver; /**< expected API version */
308} plugins_load_info[] = {
309 { /* LYPLG_TYPE */
310 .id = "type",
311 .apiver_var = "plugins_types_apiver__",
312 .plugins_var = "plugins_types__",
313 .envdir = "LIBYANG_TYPES_PLUGINS_DIR",
314 .dir = LYPLG_TYPE_DIR,
315 .apiver = LYPLG_TYPE_API_VERSION
316 }, {/* LYPLG_EXTENSION */
317 .id = "extension",
318 .apiver_var = "plugins_extensions_apiver__",
319 .plugins_var = "plugins_extensions__",
320 .envdir = "LIBYANG_EXTENSIONS_PLUGINS_DIR",
321 .dir = LYPLG_EXT_DIR,
322 .apiver = LYPLG_EXT_API_VERSION
323 }
324};
325
Radek Krejcibf940f92021-03-24 21:04:13 +0100326/**
327 * @brief Get the expected plugin objects from the loaded dynamic object and add the defined plugins into the lists of
328 * available extensions/types plugins.
329 *
330 * @param[in] dlhandler Loaded dynamic library handler.
331 * @param[in] pathname Path of the loaded library for logging.
332 * @param[in] type Type of the plugins to get from the dynamic library. Note that a single library can hold both types
333 * and extensions plugins implementations, so this function should be called twice (once for each plugin type) with
334 * different @p type values
335 * @return LY_ERR values.
336 */
337static LY_ERR
338plugins_load(void *dlhandler, const char *pathname, enum LYPLG type)
339{
340 const void *plugins;
341 uint32_t *version;
342
343 /* type plugin */
344 version = dlsym(dlhandler, plugins_load_info[type].apiver_var);
345 if (version) {
346 /* check version ... */
347 if (*version != plugins_load_info[type].apiver) {
348 LOGERR(NULL, LY_EINVAL, "Processing user %s plugin \"%s\" failed, wrong API version - %d expected, %d found.",
349 plugins_load_info[type].id, pathname, plugins_load_info[type].apiver, *version);
350 return LY_EINVAL;
351 }
352
353 /* ... get types plugins information ... */
354 if (!(plugins = dlsym(dlhandler, plugins_load_info[type].plugins_var))) {
355 char *errstr = dlerror();
Michal Vasko26bbb272022-08-02 14:54:33 +0200356
Radek Krejcibf940f92021-03-24 21:04:13 +0100357 LOGERR(NULL, LY_EINVAL, "Processing user %s plugin \"%s\" failed, missing %s plugins information (%s).",
358 plugins_load_info[type].id, pathname, plugins_load_info[type].id, errstr);
359 return LY_EINVAL;
360 }
361
362 /* ... and load all the types plugins */
stewegf7aeeae2024-04-02 13:15:52 +0200363 LY_CHECK_RET(plugins_insert(NULL, type, plugins));
Radek Krejcibf940f92021-03-24 21:04:13 +0100364 }
365
366 return LY_SUCCESS;
367}
368
369static LY_ERR
370plugins_load_module(const char *pathname)
371{
372 LY_ERR ret = LY_SUCCESS;
373 void *dlhandler;
374 uint32_t types_count = 0, extensions_count = 0;
375
376 dlerror(); /* Clear any existing error */
377
378 dlhandler = dlopen(pathname, RTLD_NOW);
379 if (!dlhandler) {
380 LOGERR(NULL, LY_ESYS, "Loading \"%s\" as a plugin failed (%s).", pathname, dlerror());
381 return LY_ESYS;
382 }
383
384 if (ly_set_contains(&plugins_handlers, dlhandler, NULL)) {
385 /* the plugin is already loaded */
386 LOGVRB("Plugin \"%s\" already loaded.", pathname);
387
388 /* keep the correct refcount */
389 dlclose(dlhandler);
390 return LY_SUCCESS;
391 }
392
393 /* remember the current plugins lists for recovery */
394 types_count = plugins_types.count;
395 extensions_count = plugins_extensions.count;
396
397 /* type plugin */
398 ret = plugins_load(dlhandler, pathname, LYPLG_TYPE);
399 LY_CHECK_GOTO(ret, error);
400
401 /* extension plugin */
402 ret = plugins_load(dlhandler, pathname, LYPLG_EXTENSION);
403 LY_CHECK_GOTO(ret, error);
404
405 /* remember the dynamic plugin */
406 ret = ly_set_add(&plugins_handlers, dlhandler, 1, NULL);
407 LY_CHECK_GOTO(ret, error);
408
409 return LY_SUCCESS;
410
411error:
412 dlclose(dlhandler);
413
414 /* revert changes in the lists */
415 while (plugins_types.count > types_count) {
416 ly_set_rm_index(&plugins_types, plugins_types.count - 1, NULL);
417 }
418 while (plugins_extensions.count > extensions_count) {
419 ly_set_rm_index(&plugins_extensions, plugins_extensions.count - 1, NULL);
420 }
421
422 return ret;
423}
424
Radek Krejci968d7552021-03-26 20:33:51 +0100425static LY_ERR
426plugins_insert_dir(enum LYPLG type)
427{
428 LY_ERR ret = LY_SUCCESS;
429 const char *pluginsdir;
430 DIR *dir;
431 ly_bool default_dir = 0;
432
433 /* try to get the plugins directory from environment variable */
434 pluginsdir = getenv(plugins_load_info[type].envdir);
435 if (!pluginsdir) {
436 /* remember that we are going to a default dir and do not print warning if the directory doesn't exist */
437 default_dir = 1;
438 pluginsdir = plugins_load_info[type].dir;
439 }
440
441 dir = opendir(pluginsdir);
442 if (!dir) {
443 /* no directory (or no access to it), no extension plugins */
444 if (!default_dir || (errno != ENOENT)) {
445 LOGWRN(NULL, "Failed to open libyang %s plugins directory \"%s\" (%s).", plugins_load_info[type].id,
446 pluginsdir, strerror(errno));
447 }
448 } else {
449 struct dirent *file;
450
451 while ((file = readdir(dir))) {
452 size_t len;
453 char pathname[PATH_MAX];
454
455 /* required format of the filename is *LYPLG_SUFFIX */
456 len = strlen(file->d_name);
457 if ((len < LYPLG_SUFFIX_LEN + 1) || strcmp(&file->d_name[len - LYPLG_SUFFIX_LEN], LYPLG_SUFFIX)) {
458 continue;
459 }
460
461 /* and construct the filepath */
462 snprintf(pathname, PATH_MAX, "%s/%s", pluginsdir, file->d_name);
463
464 ret = plugins_load_module(pathname);
465 if (ret) {
466 break;
467 }
468 }
469 closedir(dir);
470 }
471
472 return ret;
473}
474
Michal Vasko9e2bc702021-06-09 11:43:36 +0200475#endif
476
Radek Krejci3e6632f2021-03-22 22:08:21 +0100477LY_ERR
Michal Vasko6a027db2024-02-21 09:55:34 +0100478lyplg_init(ly_bool builtin_type_plugins_only)
Radek Krejci3e6632f2021-03-22 22:08:21 +0100479{
480 LY_ERR ret;
481
482 pthread_mutex_lock(&plugins_guard);
483 /* let only the first context to initiate plugins, but let others wait for finishing the initiation */
484 if (context_refcount++) {
485 /* already initiated */
486 pthread_mutex_unlock(&plugins_guard);
487 return LY_SUCCESS;
488 }
489
490 /* internal types */
stewegf7aeeae2024-04-02 13:15:52 +0200491 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_binary), error);
492 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_bits), error);
493 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_boolean), error);
494 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_decimal64), error);
495 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_empty), error);
496 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_enumeration), error);
497 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_identityref), error);
498 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_instanceid), error);
499 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_integer), error);
500 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_leafref), error);
501 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_string), error);
502 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_union), error);
Radek Krejci3e6632f2021-03-22 22:08:21 +0100503
Michal Vasko6d42a112024-05-21 15:54:55 +0200504 /* metadata and lyds_tree, which requires them */
505 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_metadata), error);
Michal Vasko6b144d22024-05-21 08:17:50 +0200506 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_lyds_tree), error);
507
Michal Vasko6a027db2024-02-21 09:55:34 +0100508 if (!builtin_type_plugins_only) {
stewegd4cde642024-02-21 08:34:16 +0100509 /* yang */
stewegf7aeeae2024-04-02 13:15:52 +0200510 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_instanceid_keys), error);
Michal Vasko79a7a872022-06-17 09:00:48 +0200511
stewegd4cde642024-02-21 08:34:16 +0100512 /* ietf-inet-types */
stewegf7aeeae2024-04-02 13:15:52 +0200513 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv4_address), error);
514 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv4_address_no_zone), error);
515 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv6_address), error);
516 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv6_address_no_zone), error);
517 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv4_prefix), error);
518 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv6_prefix), error);
Michal Vasko3e52de52021-04-13 13:45:55 +0200519
stewegd4cde642024-02-21 08:34:16 +0100520 /* ietf-yang-types */
stewegf7aeeae2024-04-02 13:15:52 +0200521 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_date_and_time), error);
522 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_hex_string), error);
523 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_xpath10), error);
Michal Vaskode4a3412021-04-14 15:38:27 +0200524
stewegd4cde642024-02-21 08:34:16 +0100525 /* ietf-netconf-acm */
stewegf7aeeae2024-04-02 13:15:52 +0200526 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_node_instanceid), error);
Michal Vasko7a53c7d2021-08-06 11:28:57 +0200527
stewegd4cde642024-02-21 08:34:16 +0100528 /* internal extensions */
stewegf7aeeae2024-04-02 13:15:52 +0200529 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_nacm), error);
530 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_yangdata), error);
531 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_schema_mount), error);
532 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_structure), error);
stewegd4cde642024-02-21 08:34:16 +0100533 }
Radek Krejci3e6632f2021-03-22 22:08:21 +0100534
Michal Vasko9e2bc702021-06-09 11:43:36 +0200535#ifndef STATIC
Radek Krejci968d7552021-03-26 20:33:51 +0100536 /* external types */
537 LY_CHECK_GOTO(ret = plugins_insert_dir(LYPLG_TYPE), error);
538
539 /* external extensions */
540 LY_CHECK_GOTO(ret = plugins_insert_dir(LYPLG_EXTENSION), error);
Michal Vasko9e2bc702021-06-09 11:43:36 +0200541#endif
Radek Krejci968d7552021-03-26 20:33:51 +0100542
Radek Krejci3e6632f2021-03-22 22:08:21 +0100543 /* initiation done, wake-up possibly waiting threads creating another contexts */
544 pthread_mutex_unlock(&plugins_guard);
545
546 return LY_SUCCESS;
547
548error:
549 /* initiation was not successful - cleanup (and let others to try) */
Michal Vasko9e2bc702021-06-09 11:43:36 +0200550#ifndef STATIC
Radek Krejci3e6632f2021-03-22 22:08:21 +0100551 lyplg_clean_();
Michal Vasko9e2bc702021-06-09 11:43:36 +0200552#endif
Radek Krejci3e6632f2021-03-22 22:08:21 +0100553 pthread_mutex_unlock(&plugins_guard);
554
555 if (ret == LY_EINVAL) {
556 /* all the plugins here are internal, invalid record actually means an internal libyang error */
557 ret = LY_EINT;
558 }
559 return ret;
560}
Radek Krejcibf940f92021-03-24 21:04:13 +0100561
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100562LIBYANG_API_DEF LY_ERR
Radek Krejcibf940f92021-03-24 21:04:13 +0100563lyplg_add(const char *pathname)
564{
Michal Vasko9e2bc702021-06-09 11:43:36 +0200565#ifdef STATIC
566 (void)pathname;
567
568 LOGERR(NULL, LY_EINVAL, "Plugins are not supported in statically built library.");
569 return LY_EINVAL;
Jan Kundrát323c3122021-12-14 11:44:57 +0100570#elif defined (_WIN32)
571 (void)pathname;
572
573 LOGERR(NULL, LY_EINVAL, "Plugins are not (yet) supported on Windows.");
574 return LY_EINVAL;
Michal Vasko9e2bc702021-06-09 11:43:36 +0200575#else
Radek Krejcibf940f92021-03-24 21:04:13 +0100576 LY_ERR ret = LY_SUCCESS;
577
578 LY_CHECK_ARG_RET(NULL, pathname, LY_EINVAL);
579
580 /* works only in case a context exists */
581 pthread_mutex_lock(&plugins_guard);
582 if (!context_refcount) {
583 /* no context */
584 pthread_mutex_unlock(&plugins_guard);
585 LOGERR(NULL, LY_EDENIED, "To add a plugin, at least one context must exists.");
586 return LY_EDENIED;
587 }
588
589 ret = plugins_load_module(pathname);
590
591 pthread_mutex_unlock(&plugins_guard);
592
593 return ret;
Michal Vasko9e2bc702021-06-09 11:43:36 +0200594#endif
Radek Krejcibf940f92021-03-24 21:04:13 +0100595}
stewegf7aeeae2024-04-02 13:15:52 +0200596
597/**
598 * @brief Manually load an extension plugins from memory
599 *
600 * Note, that a plugin can be loaded only if there is at least one context. The loaded plugins are connected with the
601 * existence of a context. When all the contexts are destroyed, all the plugins are unloaded.
602 *
603 * @param[in] ctx The context to which the plugin should be associated with. If NULL, the plugin is considered to be shared
604 * between all existing contexts.
605 * @param[in] version The version of plugin records.
606 * @param[in] type The type of plugins records.
607 * @param[in] recs An array of plugin records provided by the plugin implementation. The array must be terminated by a zeroed
608 * record.
609 *
610 * @return LY_SUCCESS if the plugins with compatible version were successfully loaded.
611 * @return LY_EDENIED in case there is no context and the plugin cannot be loaded.
612 * @return LY_EINVAL when recs is NULL or the plugin contains invalid content for this libyang version.
613 */
614static LY_ERR
615lyplg_add_plugin(struct ly_ctx *ctx, uint32_t version, enum LYPLG type, const void *recs)
616{
617 LY_ERR ret = LY_SUCCESS;
Michal Vasko52098452024-04-02 14:04:41 +0200618 uint32_t cur_ver = (type == LYPLG_TYPE) ? LYPLG_TYPE_API_VERSION : LYPLG_EXT_API_VERSION;
stewegf7aeeae2024-04-02 13:15:52 +0200619
620 LY_CHECK_ARG_RET(NULL, recs, LY_EINVAL);
621
Michal Vasko52098452024-04-02 14:04:41 +0200622 if (version != cur_ver) {
623 LOGERR(ctx, LY_EINVAL, "Adding user %s plugin failed, wrong API version - %" PRIu32 " expected, %" PRIu32 " found.",
624 (type == LYPLG_TYPE) ? "type" : "extension", cur_ver, version);
stewegf7aeeae2024-04-02 13:15:52 +0200625 return LY_EINVAL;
626 }
627
628 /* works only in case a context exists */
629 pthread_mutex_lock(&plugins_guard);
630 if (!context_refcount) {
631 /* no context */
632 pthread_mutex_unlock(&plugins_guard);
633 LOGERR(NULL, LY_EDENIED, "To add a plugin, at least one context must exists.");
634 return LY_EDENIED;
635 }
636
Michal Vasko964407a2024-04-02 13:23:02 +0200637 ret = plugins_insert(ctx, type, recs);
stewegf7aeeae2024-04-02 13:15:52 +0200638 pthread_mutex_unlock(&plugins_guard);
639
640 return ret;
641}
642
643LIBYANG_API_DEF LY_ERR
644lyplg_add_extension_plugin(struct ly_ctx *ctx, uint32_t version, const struct lyplg_ext_record *recs)
645{
646 return lyplg_add_plugin(ctx, version, LYPLG_EXTENSION, recs);
647}
648
649LIBYANG_API_DEF LY_ERR
650lyplg_add_type_plugin(struct ly_ctx *ctx, uint32_t version, const struct lyplg_type_record *recs)
651{
652 return lyplg_add_plugin(ctx, version, LYPLG_TYPE, recs);
653}