blob: 22c49476b592de952a07fbe4cfadc7217260895f [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/*
roman34a042c2024-08-12 13:44:20 +020084 * libnetconf2-netconf-server
85 */
86extern const struct lyplg_type_record plugins_time_period[];
87
88/*
aPiecek6cf1d162023-11-08 16:07:00 +010089 * lyds_tree
90 */
91extern const struct lyplg_type_record plugins_lyds_tree[];
92
93/*
Radek Krejci3e6632f2021-03-22 22:08:21 +010094 * internal extension plugins records
95 */
96extern struct lyplg_ext_record plugins_metadata[];
97extern struct lyplg_ext_record plugins_nacm[];
98extern struct lyplg_ext_record plugins_yangdata[];
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010099extern struct lyplg_ext_record plugins_schema_mount[];
Michal Vaskoedb0fa52022-10-04 10:36:00 +0200100extern struct lyplg_ext_record plugins_structure[];
Radek Krejci3e6632f2021-03-22 22:08:21 +0100101
102static pthread_mutex_t plugins_guard = PTHREAD_MUTEX_INITIALIZER;
103
104/**
105 * @brief Counter for currently present contexts able to refer to the loaded plugins.
106 *
107 * Plugins are shared among all the created contexts. They are loaded with the creation of the very first context and
108 * unloaded with the destroy of the last context. Therefore, to reload the list of plugins, all the contexts must be
109 * destroyed and with the creation of a first new context after that, the plugins will be reloaded.
110 */
111static uint32_t context_refcount = 0;
112
113/**
114 * @brief Record describing an implemented extension.
115 *
116 * Matches ::lyplg_ext_record and ::lyplg_type_record
117 */
118struct lyplg_record {
119 const char *module; /**< name of the module where the extension/type is defined */
120 const char *revision; /**< optional module revision - if not specified, the plugin applies to any revision,
121 which is not an optimal approach due to a possible future revisions of the module.
122 Instead, there should be defined multiple items in the plugins list, each with the
123 different revision, but all with the same pointer to the plugin functions. The
124 only valid use case for the NULL revision is the case the module has no revision. */
125 const char *name; /**< name of the extension/typedef */
126 int8_t plugin[]; /**< specific plugin type's data - ::lyplg_ext or ::lyplg_type */
127};
128
Michal Vasko9e2bc702021-06-09 11:43:36 +0200129#ifndef STATIC
Radek Krejcibf940f92021-03-24 21:04:13 +0100130static struct ly_set plugins_handlers = {0};
Michal Vasko9e2bc702021-06-09 11:43:36 +0200131#endif
Radek Krejci3e6632f2021-03-22 22:08:21 +0100132static struct ly_set plugins_types = {0};
133static struct ly_set plugins_extensions = {0};
134
135/**
136 * @brief Iterate over list of loaded plugins of the given @p type.
137 *
stewegf7aeeae2024-04-02 13:15:52 +0200138 * @param[in] ctx The context for which the plugin is searched for
Radek Krejci3e6632f2021-03-22 22:08:21 +0100139 * @param[in] type Type of the plugins to iterate.
140 * @param[in,out] index The iterator - set to 0 for the first call.
141 * @return The plugin records, NULL if no more record is available.
142 */
143static struct lyplg_record *
stewegf7aeeae2024-04-02 13:15:52 +0200144plugins_iter(const struct ly_ctx *ctx, enum LYPLG type, uint32_t *index)
Radek Krejci3e6632f2021-03-22 22:08:21 +0100145{
stewegf7aeeae2024-04-02 13:15:52 +0200146 const struct ly_set *plugins;
Radek Krejci3e6632f2021-03-22 22:08:21 +0100147
148 assert(index);
149
150 if (type == LYPLG_EXTENSION) {
stewegf7aeeae2024-04-02 13:15:52 +0200151 plugins = ctx ? &ctx->plugins_extensions : &plugins_extensions;
Radek Krejci3e6632f2021-03-22 22:08:21 +0100152 } else {
stewegf7aeeae2024-04-02 13:15:52 +0200153 plugins = ctx ? &ctx->plugins_types : &plugins_types;
Radek Krejci3e6632f2021-03-22 22:08:21 +0100154 }
155
156 if (*index == plugins->count) {
157 return NULL;
158 }
159
160 *index += 1;
161 return plugins->objs[*index - 1];
162}
163
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200164static void *
stewegf7aeeae2024-04-02 13:15:52 +0200165lyplg_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 +0100166{
167 uint32_t i = 0;
168 struct lyplg_record *item;
169
170 assert(module);
171 assert(name);
172
stewegf7aeeae2024-04-02 13:15:52 +0200173 while ((item = plugins_iter(ctx, type, &i)) != NULL) {
Radek Krejci3e6632f2021-03-22 22:08:21 +0100174 if (!strcmp(item->module, module) && !strcmp(item->name, name)) {
175 if (item->revision && revision && strcmp(item->revision, revision)) {
176 continue;
177 } else if (!revision && item->revision) {
178 continue;
179 }
180
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200181 return item;
Radek Krejci3e6632f2021-03-22 22:08:21 +0100182 }
183 }
184
185 return NULL;
186}
187
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200188struct lyplg_type *
stewegf7aeeae2024-04-02 13:15:52 +0200189lyplg_type_plugin_find(const struct ly_ctx *ctx, const char *module, const char *revision, const char *name)
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200190{
stewegf7aeeae2024-04-02 13:15:52 +0200191 struct lyplg_record *record = NULL;
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200192
stewegf7aeeae2024-04-02 13:15:52 +0200193 if (ctx) {
194 /* try to find context specific plugin */
195 record = lyplg_record_find(ctx, LYPLG_TYPE, module, revision, name);
196 }
197
198 if (!record) {
199 /* try to find shared plugin */
200 record = lyplg_record_find(NULL, LYPLG_TYPE, module, revision, name);
201 }
202
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200203 return record ? &((struct lyplg_type_record *)record)->plugin : NULL;
204}
205
206struct lyplg_ext_record *
stewegf7aeeae2024-04-02 13:15:52 +0200207lyplg_ext_record_find(const struct ly_ctx *ctx, const char *module, const char *revision, const char *name)
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200208{
stewegf7aeeae2024-04-02 13:15:52 +0200209 struct lyplg_ext_record *record = NULL;
210
211 if (ctx) {
212 /* try to find context specific plugin */
213 record = lyplg_record_find(ctx, LYPLG_EXTENSION, module, revision, name);
214 }
215
216 if (!record) {
217 /* try to find shared plugin */
218 record = lyplg_record_find(NULL, LYPLG_EXTENSION, module, revision, name);
219 }
220
221 return record;
Michal Vaskoc0c64ae2022-10-06 10:15:23 +0200222}
223
Radek Krejci3e6632f2021-03-22 22:08:21 +0100224/**
225 * @brief Insert the provided extension plugin records into the internal set of extension plugins for use by libyang.
226 *
stewegf7aeeae2024-04-02 13:15:52 +0200227 * @param[in] ctx The context to which the plugin should be associated with. If NULL, the plugin is considered to be shared
228 * between all existing contexts.
229 * @param[in] type The type of plugins records
Radek Krejci3e6632f2021-03-22 22:08:21 +0100230 * @param[in] recs An array of plugin records provided by the plugin implementation. The array must be terminated by a zeroed
231 * record.
232 * @return LY_SUCCESS in case of success
233 * @return LY_EINVAL for invalid information in @p recs.
234 * @return LY_EMEM in case of memory allocation failure.
235 */
236static LY_ERR
stewegf7aeeae2024-04-02 13:15:52 +0200237plugins_insert(struct ly_ctx *ctx, enum LYPLG type, const void *recs)
Radek Krejci3e6632f2021-03-22 22:08:21 +0100238{
stewegf7aeeae2024-04-02 13:15:52 +0200239 struct ly_set *plugins;
240
Radek Krejci3e6632f2021-03-22 22:08:21 +0100241 if (!recs) {
242 return LY_SUCCESS;
243 }
244
245 if (type == LYPLG_EXTENSION) {
246 const struct lyplg_ext_record *rec = (const struct lyplg_ext_record *)recs;
247
stewegf7aeeae2024-04-02 13:15:52 +0200248 plugins = ctx ? &ctx->plugins_extensions : &plugins_extensions;
249
Radek Krejci3e6632f2021-03-22 22:08:21 +0100250 for (uint32_t i = 0; rec[i].name; i++) {
stewegf7aeeae2024-04-02 13:15:52 +0200251 LY_CHECK_RET(ly_set_add(plugins, (void *)&rec[i], 0, NULL));
Radek Krejci3e6632f2021-03-22 22:08:21 +0100252 }
Radek Krejcibf940f92021-03-24 21:04:13 +0100253 } else { /* LYPLG_TYPE */
Radek Krejci3e6632f2021-03-22 22:08:21 +0100254 const struct lyplg_type_record *rec = (const struct lyplg_type_record *)recs;
255
stewegf7aeeae2024-04-02 13:15:52 +0200256 plugins = ctx ? &ctx->plugins_types : &plugins_types;
257
Radek Krejci3e6632f2021-03-22 22:08:21 +0100258 for (uint32_t i = 0; rec[i].name; i++) {
stewegf7aeeae2024-04-02 13:15:52 +0200259 LY_CHECK_RET(ly_set_add(plugins, (void *)&rec[i], 0, NULL));
Radek Krejci3e6632f2021-03-22 22:08:21 +0100260 }
261 }
262
263 return LY_SUCCESS;
264}
265
Michal Vasko9e2bc702021-06-09 11:43:36 +0200266#ifndef STATIC
267
Radek Krejci3e6632f2021-03-22 22:08:21 +0100268static void
Michal Vaskoe43a34e2021-04-13 13:43:04 +0200269lyplg_close_cb(void *handle)
270{
271 dlclose(handle);
272}
273
274static void
Radek Krejci3e6632f2021-03-22 22:08:21 +0100275lyplg_clean_(void)
276{
277 if (--context_refcount) {
278 /* there is still some other context, do not remove the plugins */
279 return;
280 }
281
282 ly_set_erase(&plugins_types, NULL);
283 ly_set_erase(&plugins_extensions, NULL);
Michal Vaskoe43a34e2021-04-13 13:43:04 +0200284 ly_set_erase(&plugins_handlers, lyplg_close_cb);
Radek Krejci3e6632f2021-03-22 22:08:21 +0100285}
286
Michal Vasko9e2bc702021-06-09 11:43:36 +0200287#endif
288
Radek Krejci3e6632f2021-03-22 22:08:21 +0100289void
290lyplg_clean(void)
291{
Michal Vasko9e2bc702021-06-09 11:43:36 +0200292#ifndef STATIC
Radek Krejci3e6632f2021-03-22 22:08:21 +0100293 pthread_mutex_lock(&plugins_guard);
294 lyplg_clean_();
295 pthread_mutex_unlock(&plugins_guard);
Michal Vasko9e2bc702021-06-09 11:43:36 +0200296#endif
Radek Krejci3e6632f2021-03-22 22:08:21 +0100297}
298
Michal Vasko9e2bc702021-06-09 11:43:36 +0200299#ifndef STATIC
300
301/**
302 * @brief Just a variadic data to cover extension and type plugins by a single ::plugins_load() function.
303 *
304 * The values are taken from ::LY_PLUGINS_EXTENSIONS and ::LYPLG_TYPES macros.
305 */
306static const struct {
307 const char *id; /**< string identifier: type/extension */
308 const char *apiver_var; /**< expected variable name holding API version value */
309 const char *plugins_var; /**< expected variable name holding plugin records */
310 const char *envdir; /**< environment variable containing directory with the plugins */
311 const char *dir; /**< default directory with the plugins (has less priority than envdir) */
312 uint32_t apiver; /**< expected API version */
313} plugins_load_info[] = {
314 { /* LYPLG_TYPE */
315 .id = "type",
316 .apiver_var = "plugins_types_apiver__",
317 .plugins_var = "plugins_types__",
318 .envdir = "LIBYANG_TYPES_PLUGINS_DIR",
319 .dir = LYPLG_TYPE_DIR,
320 .apiver = LYPLG_TYPE_API_VERSION
321 }, {/* LYPLG_EXTENSION */
322 .id = "extension",
323 .apiver_var = "plugins_extensions_apiver__",
324 .plugins_var = "plugins_extensions__",
325 .envdir = "LIBYANG_EXTENSIONS_PLUGINS_DIR",
326 .dir = LYPLG_EXT_DIR,
327 .apiver = LYPLG_EXT_API_VERSION
328 }
329};
330
Radek Krejcibf940f92021-03-24 21:04:13 +0100331/**
332 * @brief Get the expected plugin objects from the loaded dynamic object and add the defined plugins into the lists of
333 * available extensions/types plugins.
334 *
335 * @param[in] dlhandler Loaded dynamic library handler.
336 * @param[in] pathname Path of the loaded library for logging.
337 * @param[in] type Type of the plugins to get from the dynamic library. Note that a single library can hold both types
338 * and extensions plugins implementations, so this function should be called twice (once for each plugin type) with
339 * different @p type values
340 * @return LY_ERR values.
341 */
342static LY_ERR
343plugins_load(void *dlhandler, const char *pathname, enum LYPLG type)
344{
345 const void *plugins;
346 uint32_t *version;
347
348 /* type plugin */
349 version = dlsym(dlhandler, plugins_load_info[type].apiver_var);
350 if (version) {
351 /* check version ... */
352 if (*version != plugins_load_info[type].apiver) {
353 LOGERR(NULL, LY_EINVAL, "Processing user %s plugin \"%s\" failed, wrong API version - %d expected, %d found.",
354 plugins_load_info[type].id, pathname, plugins_load_info[type].apiver, *version);
355 return LY_EINVAL;
356 }
357
358 /* ... get types plugins information ... */
359 if (!(plugins = dlsym(dlhandler, plugins_load_info[type].plugins_var))) {
360 char *errstr = dlerror();
Michal Vasko26bbb272022-08-02 14:54:33 +0200361
Radek Krejcibf940f92021-03-24 21:04:13 +0100362 LOGERR(NULL, LY_EINVAL, "Processing user %s plugin \"%s\" failed, missing %s plugins information (%s).",
363 plugins_load_info[type].id, pathname, plugins_load_info[type].id, errstr);
364 return LY_EINVAL;
365 }
366
367 /* ... and load all the types plugins */
stewegf7aeeae2024-04-02 13:15:52 +0200368 LY_CHECK_RET(plugins_insert(NULL, type, plugins));
Radek Krejcibf940f92021-03-24 21:04:13 +0100369 }
370
371 return LY_SUCCESS;
372}
373
374static LY_ERR
375plugins_load_module(const char *pathname)
376{
377 LY_ERR ret = LY_SUCCESS;
378 void *dlhandler;
379 uint32_t types_count = 0, extensions_count = 0;
380
381 dlerror(); /* Clear any existing error */
382
383 dlhandler = dlopen(pathname, RTLD_NOW);
384 if (!dlhandler) {
385 LOGERR(NULL, LY_ESYS, "Loading \"%s\" as a plugin failed (%s).", pathname, dlerror());
386 return LY_ESYS;
387 }
388
389 if (ly_set_contains(&plugins_handlers, dlhandler, NULL)) {
390 /* the plugin is already loaded */
391 LOGVRB("Plugin \"%s\" already loaded.", pathname);
392
393 /* keep the correct refcount */
394 dlclose(dlhandler);
395 return LY_SUCCESS;
396 }
397
398 /* remember the current plugins lists for recovery */
399 types_count = plugins_types.count;
400 extensions_count = plugins_extensions.count;
401
402 /* type plugin */
403 ret = plugins_load(dlhandler, pathname, LYPLG_TYPE);
404 LY_CHECK_GOTO(ret, error);
405
406 /* extension plugin */
407 ret = plugins_load(dlhandler, pathname, LYPLG_EXTENSION);
408 LY_CHECK_GOTO(ret, error);
409
410 /* remember the dynamic plugin */
411 ret = ly_set_add(&plugins_handlers, dlhandler, 1, NULL);
412 LY_CHECK_GOTO(ret, error);
413
414 return LY_SUCCESS;
415
416error:
417 dlclose(dlhandler);
418
419 /* revert changes in the lists */
420 while (plugins_types.count > types_count) {
421 ly_set_rm_index(&plugins_types, plugins_types.count - 1, NULL);
422 }
423 while (plugins_extensions.count > extensions_count) {
424 ly_set_rm_index(&plugins_extensions, plugins_extensions.count - 1, NULL);
425 }
426
427 return ret;
428}
429
Radek Krejci968d7552021-03-26 20:33:51 +0100430static LY_ERR
431plugins_insert_dir(enum LYPLG type)
432{
433 LY_ERR ret = LY_SUCCESS;
434 const char *pluginsdir;
435 DIR *dir;
436 ly_bool default_dir = 0;
437
438 /* try to get the plugins directory from environment variable */
439 pluginsdir = getenv(plugins_load_info[type].envdir);
440 if (!pluginsdir) {
441 /* remember that we are going to a default dir and do not print warning if the directory doesn't exist */
442 default_dir = 1;
443 pluginsdir = plugins_load_info[type].dir;
444 }
445
446 dir = opendir(pluginsdir);
447 if (!dir) {
448 /* no directory (or no access to it), no extension plugins */
449 if (!default_dir || (errno != ENOENT)) {
450 LOGWRN(NULL, "Failed to open libyang %s plugins directory \"%s\" (%s).", plugins_load_info[type].id,
451 pluginsdir, strerror(errno));
452 }
453 } else {
454 struct dirent *file;
455
456 while ((file = readdir(dir))) {
457 size_t len;
458 char pathname[PATH_MAX];
459
460 /* required format of the filename is *LYPLG_SUFFIX */
461 len = strlen(file->d_name);
462 if ((len < LYPLG_SUFFIX_LEN + 1) || strcmp(&file->d_name[len - LYPLG_SUFFIX_LEN], LYPLG_SUFFIX)) {
463 continue;
464 }
465
466 /* and construct the filepath */
467 snprintf(pathname, PATH_MAX, "%s/%s", pluginsdir, file->d_name);
468
469 ret = plugins_load_module(pathname);
470 if (ret) {
471 break;
472 }
473 }
474 closedir(dir);
475 }
476
477 return ret;
478}
479
Michal Vasko9e2bc702021-06-09 11:43:36 +0200480#endif
481
Radek Krejci3e6632f2021-03-22 22:08:21 +0100482LY_ERR
Michal Vasko6a027db2024-02-21 09:55:34 +0100483lyplg_init(ly_bool builtin_type_plugins_only)
Radek Krejci3e6632f2021-03-22 22:08:21 +0100484{
485 LY_ERR ret;
486
487 pthread_mutex_lock(&plugins_guard);
488 /* let only the first context to initiate plugins, but let others wait for finishing the initiation */
489 if (context_refcount++) {
490 /* already initiated */
491 pthread_mutex_unlock(&plugins_guard);
492 return LY_SUCCESS;
493 }
494
495 /* internal types */
stewegf7aeeae2024-04-02 13:15:52 +0200496 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_binary), error);
497 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_bits), error);
498 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_boolean), error);
499 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_decimal64), error);
500 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_empty), error);
501 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_enumeration), error);
502 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_identityref), error);
503 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_instanceid), error);
504 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_integer), error);
505 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_leafref), error);
506 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_string), error);
507 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_union), error);
Radek Krejci3e6632f2021-03-22 22:08:21 +0100508
Michal Vasko6d42a112024-05-21 15:54:55 +0200509 /* metadata and lyds_tree, which requires them */
510 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_metadata), error);
Michal Vasko6b144d22024-05-21 08:17:50 +0200511 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_lyds_tree), error);
512
Michal Vasko6a027db2024-02-21 09:55:34 +0100513 if (!builtin_type_plugins_only) {
stewegd4cde642024-02-21 08:34:16 +0100514 /* yang */
stewegf7aeeae2024-04-02 13:15:52 +0200515 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_instanceid_keys), error);
Michal Vasko79a7a872022-06-17 09:00:48 +0200516
stewegd4cde642024-02-21 08:34:16 +0100517 /* ietf-inet-types */
stewegf7aeeae2024-04-02 13:15:52 +0200518 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv4_address), error);
519 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv4_address_no_zone), error);
520 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv6_address), error);
521 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv6_address_no_zone), error);
522 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv4_prefix), error);
523 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv6_prefix), error);
Michal Vasko3e52de52021-04-13 13:45:55 +0200524
stewegd4cde642024-02-21 08:34:16 +0100525 /* ietf-yang-types */
stewegf7aeeae2024-04-02 13:15:52 +0200526 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_date_and_time), error);
527 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_hex_string), error);
528 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_xpath10), error);
Michal Vaskode4a3412021-04-14 15:38:27 +0200529
roman34a042c2024-08-12 13:44:20 +0200530 /* libnetconf2-netconf-server */
531 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_time_period), error);
532
stewegd4cde642024-02-21 08:34:16 +0100533 /* ietf-netconf-acm */
stewegf7aeeae2024-04-02 13:15:52 +0200534 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_node_instanceid), error);
Michal Vasko7a53c7d2021-08-06 11:28:57 +0200535
stewegd4cde642024-02-21 08:34:16 +0100536 /* internal extensions */
stewegf7aeeae2024-04-02 13:15:52 +0200537 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_nacm), error);
538 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_yangdata), error);
539 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_schema_mount), error);
540 LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_structure), error);
stewegd4cde642024-02-21 08:34:16 +0100541 }
Radek Krejci3e6632f2021-03-22 22:08:21 +0100542
Michal Vasko9e2bc702021-06-09 11:43:36 +0200543#ifndef STATIC
Radek Krejci968d7552021-03-26 20:33:51 +0100544 /* external types */
545 LY_CHECK_GOTO(ret = plugins_insert_dir(LYPLG_TYPE), error);
546
547 /* external extensions */
548 LY_CHECK_GOTO(ret = plugins_insert_dir(LYPLG_EXTENSION), error);
Michal Vasko9e2bc702021-06-09 11:43:36 +0200549#endif
Radek Krejci968d7552021-03-26 20:33:51 +0100550
Radek Krejci3e6632f2021-03-22 22:08:21 +0100551 /* initiation done, wake-up possibly waiting threads creating another contexts */
552 pthread_mutex_unlock(&plugins_guard);
553
554 return LY_SUCCESS;
555
556error:
557 /* initiation was not successful - cleanup (and let others to try) */
Michal Vasko9e2bc702021-06-09 11:43:36 +0200558#ifndef STATIC
Radek Krejci3e6632f2021-03-22 22:08:21 +0100559 lyplg_clean_();
Michal Vasko9e2bc702021-06-09 11:43:36 +0200560#endif
Radek Krejci3e6632f2021-03-22 22:08:21 +0100561 pthread_mutex_unlock(&plugins_guard);
562
563 if (ret == LY_EINVAL) {
564 /* all the plugins here are internal, invalid record actually means an internal libyang error */
565 ret = LY_EINT;
566 }
567 return ret;
568}
Radek Krejcibf940f92021-03-24 21:04:13 +0100569
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100570LIBYANG_API_DEF LY_ERR
Radek Krejcibf940f92021-03-24 21:04:13 +0100571lyplg_add(const char *pathname)
572{
Michal Vasko9e2bc702021-06-09 11:43:36 +0200573#ifdef STATIC
574 (void)pathname;
575
576 LOGERR(NULL, LY_EINVAL, "Plugins are not supported in statically built library.");
577 return LY_EINVAL;
Jan Kundrát323c3122021-12-14 11:44:57 +0100578#elif defined (_WIN32)
579 (void)pathname;
580
581 LOGERR(NULL, LY_EINVAL, "Plugins are not (yet) supported on Windows.");
582 return LY_EINVAL;
Michal Vasko9e2bc702021-06-09 11:43:36 +0200583#else
Radek Krejcibf940f92021-03-24 21:04:13 +0100584 LY_ERR ret = LY_SUCCESS;
585
586 LY_CHECK_ARG_RET(NULL, pathname, LY_EINVAL);
587
588 /* works only in case a context exists */
589 pthread_mutex_lock(&plugins_guard);
590 if (!context_refcount) {
591 /* no context */
592 pthread_mutex_unlock(&plugins_guard);
593 LOGERR(NULL, LY_EDENIED, "To add a plugin, at least one context must exists.");
594 return LY_EDENIED;
595 }
596
597 ret = plugins_load_module(pathname);
598
599 pthread_mutex_unlock(&plugins_guard);
600
601 return ret;
Michal Vasko9e2bc702021-06-09 11:43:36 +0200602#endif
Radek Krejcibf940f92021-03-24 21:04:13 +0100603}
stewegf7aeeae2024-04-02 13:15:52 +0200604
605/**
606 * @brief Manually load an extension plugins from memory
607 *
608 * Note, that a plugin can be loaded only if there is at least one context. The loaded plugins are connected with the
609 * existence of a context. When all the contexts are destroyed, all the plugins are unloaded.
610 *
611 * @param[in] ctx The context to which the plugin should be associated with. If NULL, the plugin is considered to be shared
612 * between all existing contexts.
613 * @param[in] version The version of plugin records.
614 * @param[in] type The type of plugins records.
615 * @param[in] recs An array of plugin records provided by the plugin implementation. The array must be terminated by a zeroed
616 * record.
617 *
618 * @return LY_SUCCESS if the plugins with compatible version were successfully loaded.
619 * @return LY_EDENIED in case there is no context and the plugin cannot be loaded.
620 * @return LY_EINVAL when recs is NULL or the plugin contains invalid content for this libyang version.
621 */
622static LY_ERR
623lyplg_add_plugin(struct ly_ctx *ctx, uint32_t version, enum LYPLG type, const void *recs)
624{
625 LY_ERR ret = LY_SUCCESS;
Michal Vasko52098452024-04-02 14:04:41 +0200626 uint32_t cur_ver = (type == LYPLG_TYPE) ? LYPLG_TYPE_API_VERSION : LYPLG_EXT_API_VERSION;
stewegf7aeeae2024-04-02 13:15:52 +0200627
628 LY_CHECK_ARG_RET(NULL, recs, LY_EINVAL);
629
Michal Vasko52098452024-04-02 14:04:41 +0200630 if (version != cur_ver) {
631 LOGERR(ctx, LY_EINVAL, "Adding user %s plugin failed, wrong API version - %" PRIu32 " expected, %" PRIu32 " found.",
632 (type == LYPLG_TYPE) ? "type" : "extension", cur_ver, version);
stewegf7aeeae2024-04-02 13:15:52 +0200633 return LY_EINVAL;
634 }
635
636 /* works only in case a context exists */
637 pthread_mutex_lock(&plugins_guard);
638 if (!context_refcount) {
639 /* no context */
640 pthread_mutex_unlock(&plugins_guard);
641 LOGERR(NULL, LY_EDENIED, "To add a plugin, at least one context must exists.");
642 return LY_EDENIED;
643 }
644
Michal Vasko964407a2024-04-02 13:23:02 +0200645 ret = plugins_insert(ctx, type, recs);
stewegf7aeeae2024-04-02 13:15:52 +0200646 pthread_mutex_unlock(&plugins_guard);
647
648 return ret;
649}
650
651LIBYANG_API_DEF LY_ERR
652lyplg_add_extension_plugin(struct ly_ctx *ctx, uint32_t version, const struct lyplg_ext_record *recs)
653{
654 return lyplg_add_plugin(ctx, version, LYPLG_EXTENSION, recs);
655}
656
657LIBYANG_API_DEF LY_ERR
658lyplg_add_type_plugin(struct ly_ctx *ctx, uint32_t version, const struct lyplg_type_record *recs)
659{
660 return lyplg_add_plugin(ctx, version, LYPLG_TYPE, recs);
661}