blob: 67c072e78c50b37bf89180f0c0c9bbdbd192d34e [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>
Radek Krejci3e6632f2021-03-22 22:08:21 +010022#include <dlfcn.h>
Radek Krejci968d7552021-03-26 20:33:51 +010023#include <errno.h>
24#include <limits.h>
Radek Krejci3e6632f2021-03-22 22:08:21 +010025#include <pthread.h>
Radek Krejcibf940f92021-03-24 21:04:13 +010026#include <stddef.h>
Radek Krejci0aa1f702021-04-01 16:16:19 +020027#include <stdint.h>
28#include <stdio.h>
29#include <stdlib.h>
Radek Krejci3e6632f2021-03-22 22:08:21 +010030#include <string.h>
31
32#include "common.h"
Radek Krejci12a28c72021-04-06 17:23:37 +020033#include "config.h"
Radek Krejci3e6632f2021-03-22 22:08:21 +010034#include "plugins_exts.h"
35#include "plugins_types.h"
Radek Krejci0aa1f702021-04-01 16:16:19 +020036#include "set.h"
Radek Krejci3e6632f2021-03-22 22:08:21 +010037
38/*
39 * internal type plugins records
40 */
41extern const struct lyplg_type_record plugins_binary[];
42extern const struct lyplg_type_record plugins_bits[];
43extern const struct lyplg_type_record plugins_boolean[];
44extern const struct lyplg_type_record plugins_decimal64[];
45extern const struct lyplg_type_record plugins_empty[];
46extern const struct lyplg_type_record plugins_enumeration[];
47extern const struct lyplg_type_record plugins_identityref[];
48extern const struct lyplg_type_record plugins_instanceid[];
49extern const struct lyplg_type_record plugins_integer[];
50extern const struct lyplg_type_record plugins_leafref[];
51extern const struct lyplg_type_record plugins_string[];
52extern const struct lyplg_type_record plugins_union[];
53
Michal Vaskode4a3412021-04-14 15:38:27 +020054/*
55 * ietf-inet-types
56 */
Michal Vasko3159e782021-05-03 15:12:35 +020057extern const struct lyplg_type_record plugins_ipv4_address[];
Michal Vasko82bf15e2021-05-06 16:01:56 +020058extern const struct lyplg_type_record plugins_ipv4_address_no_zone[];
Michal Vasko7caa3e62021-05-03 14:59:25 +020059extern const struct lyplg_type_record plugins_ipv6_address[];
Michal Vasko18a4a732021-05-06 16:21:44 +020060extern const struct lyplg_type_record plugins_ipv6_address_no_zone[];
Michal Vasko15dc9fa2021-05-03 14:33:05 +020061extern const struct lyplg_type_record plugins_ipv4_prefix[];
62extern const struct lyplg_type_record plugins_ipv6_prefix[];
Michal Vasko3e52de52021-04-13 13:45:55 +020063
Radek Krejci3e6632f2021-03-22 22:08:21 +010064/*
Michal Vaskode4a3412021-04-14 15:38:27 +020065 * ietf-yang-types
66 */
67extern const struct lyplg_type_record plugins_date_and_time[];
Michal Vaskode4a3412021-04-14 15:38:27 +020068extern const struct lyplg_type_record plugins_xpath10[];
69
70/*
Michal Vasko7a53c7d2021-08-06 11:28:57 +020071 * ietf-netconf-acm
72 */
73extern const struct lyplg_type_record plugins_node_instanceid[];
74
75/*
Radek Krejci3e6632f2021-03-22 22:08:21 +010076 * internal extension plugins records
77 */
78extern struct lyplg_ext_record plugins_metadata[];
79extern struct lyplg_ext_record plugins_nacm[];
80extern struct lyplg_ext_record plugins_yangdata[];
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010081extern struct lyplg_ext_record plugins_schema_mount[];
Radek Krejci3e6632f2021-03-22 22:08:21 +010082
83static pthread_mutex_t plugins_guard = PTHREAD_MUTEX_INITIALIZER;
84
85/**
86 * @brief Counter for currently present contexts able to refer to the loaded plugins.
87 *
88 * Plugins are shared among all the created contexts. They are loaded with the creation of the very first context and
89 * unloaded with the destroy of the last context. Therefore, to reload the list of plugins, all the contexts must be
90 * destroyed and with the creation of a first new context after that, the plugins will be reloaded.
91 */
92static uint32_t context_refcount = 0;
93
94/**
95 * @brief Record describing an implemented extension.
96 *
97 * Matches ::lyplg_ext_record and ::lyplg_type_record
98 */
99struct lyplg_record {
100 const char *module; /**< name of the module where the extension/type is defined */
101 const char *revision; /**< optional module revision - if not specified, the plugin applies to any revision,
102 which is not an optimal approach due to a possible future revisions of the module.
103 Instead, there should be defined multiple items in the plugins list, each with the
104 different revision, but all with the same pointer to the plugin functions. The
105 only valid use case for the NULL revision is the case the module has no revision. */
106 const char *name; /**< name of the extension/typedef */
107 int8_t plugin[]; /**< specific plugin type's data - ::lyplg_ext or ::lyplg_type */
108};
109
Michal Vasko9e2bc702021-06-09 11:43:36 +0200110#ifndef STATIC
Radek Krejcibf940f92021-03-24 21:04:13 +0100111static struct ly_set plugins_handlers = {0};
Michal Vasko9e2bc702021-06-09 11:43:36 +0200112#endif
Radek Krejci3e6632f2021-03-22 22:08:21 +0100113static struct ly_set plugins_types = {0};
114static struct ly_set plugins_extensions = {0};
115
116/**
117 * @brief Iterate over list of loaded plugins of the given @p type.
118 *
119 * @param[in] type Type of the plugins to iterate.
120 * @param[in,out] index The iterator - set to 0 for the first call.
121 * @return The plugin records, NULL if no more record is available.
122 */
123static struct lyplg_record *
124plugins_iter(enum LYPLG type, uint32_t *index)
125{
126 struct ly_set *plugins;
127
128 assert(index);
129
130 if (type == LYPLG_EXTENSION) {
131 plugins = &plugins_extensions;
132 } else {
133 plugins = &plugins_types;
134 }
135
136 if (*index == plugins->count) {
137 return NULL;
138 }
139
140 *index += 1;
141 return plugins->objs[*index - 1];
142}
143
144void *
145lyplg_find(enum LYPLG type, const char *module, const char *revision, const char *name)
146{
147 uint32_t i = 0;
148 struct lyplg_record *item;
149
150 assert(module);
151 assert(name);
152
153 while ((item = plugins_iter(type, &i)) != NULL) {
154 if (!strcmp(item->module, module) && !strcmp(item->name, name)) {
155 if (item->revision && revision && strcmp(item->revision, revision)) {
156 continue;
157 } else if (!revision && item->revision) {
158 continue;
159 }
160
161 return &item->plugin;
162 }
163 }
164
165 return NULL;
166}
167
168/**
169 * @brief Insert the provided extension plugin records into the internal set of extension plugins for use by libyang.
170 *
171 * @param[in] recs An array of plugin records provided by the plugin implementation. The array must be terminated by a zeroed
172 * record.
173 * @return LY_SUCCESS in case of success
174 * @return LY_EINVAL for invalid information in @p recs.
175 * @return LY_EMEM in case of memory allocation failure.
176 */
177static LY_ERR
178plugins_insert(enum LYPLG type, const void *recs)
179{
180 if (!recs) {
181 return LY_SUCCESS;
182 }
183
184 if (type == LYPLG_EXTENSION) {
185 const struct lyplg_ext_record *rec = (const struct lyplg_ext_record *)recs;
186
187 for (uint32_t i = 0; rec[i].name; i++) {
188 LY_CHECK_RET(ly_set_add(&plugins_extensions, (void *)&rec[i], 0, NULL));
189 }
Radek Krejcibf940f92021-03-24 21:04:13 +0100190 } else { /* LYPLG_TYPE */
Radek Krejci3e6632f2021-03-22 22:08:21 +0100191 const struct lyplg_type_record *rec = (const struct lyplg_type_record *)recs;
192
193 for (uint32_t i = 0; rec[i].name; i++) {
194 LY_CHECK_RET(ly_set_add(&plugins_types, (void *)&rec[i], 0, NULL));
195 }
196 }
197
198 return LY_SUCCESS;
199}
200
Michal Vasko9e2bc702021-06-09 11:43:36 +0200201#ifndef STATIC
202
Radek Krejci3e6632f2021-03-22 22:08:21 +0100203static void
Michal Vaskoe43a34e2021-04-13 13:43:04 +0200204lyplg_close_cb(void *handle)
205{
206 dlclose(handle);
207}
208
209static void
Radek Krejci3e6632f2021-03-22 22:08:21 +0100210lyplg_clean_(void)
211{
212 if (--context_refcount) {
213 /* there is still some other context, do not remove the plugins */
214 return;
215 }
216
217 ly_set_erase(&plugins_types, NULL);
218 ly_set_erase(&plugins_extensions, NULL);
Michal Vaskoe43a34e2021-04-13 13:43:04 +0200219 ly_set_erase(&plugins_handlers, lyplg_close_cb);
Radek Krejci3e6632f2021-03-22 22:08:21 +0100220}
221
Michal Vasko9e2bc702021-06-09 11:43:36 +0200222#endif
223
Radek Krejci3e6632f2021-03-22 22:08:21 +0100224void
225lyplg_clean(void)
226{
Michal Vasko9e2bc702021-06-09 11:43:36 +0200227#ifndef STATIC
Radek Krejci3e6632f2021-03-22 22:08:21 +0100228 pthread_mutex_lock(&plugins_guard);
229 lyplg_clean_();
230 pthread_mutex_unlock(&plugins_guard);
Michal Vasko9e2bc702021-06-09 11:43:36 +0200231#endif
Radek Krejci3e6632f2021-03-22 22:08:21 +0100232}
233
Michal Vasko9e2bc702021-06-09 11:43:36 +0200234#ifndef STATIC
235
236/**
237 * @brief Just a variadic data to cover extension and type plugins by a single ::plugins_load() function.
238 *
239 * The values are taken from ::LY_PLUGINS_EXTENSIONS and ::LYPLG_TYPES macros.
240 */
241static const struct {
242 const char *id; /**< string identifier: type/extension */
243 const char *apiver_var; /**< expected variable name holding API version value */
244 const char *plugins_var; /**< expected variable name holding plugin records */
245 const char *envdir; /**< environment variable containing directory with the plugins */
246 const char *dir; /**< default directory with the plugins (has less priority than envdir) */
247 uint32_t apiver; /**< expected API version */
248} plugins_load_info[] = {
249 { /* LYPLG_TYPE */
250 .id = "type",
251 .apiver_var = "plugins_types_apiver__",
252 .plugins_var = "plugins_types__",
253 .envdir = "LIBYANG_TYPES_PLUGINS_DIR",
254 .dir = LYPLG_TYPE_DIR,
255 .apiver = LYPLG_TYPE_API_VERSION
256 }, {/* LYPLG_EXTENSION */
257 .id = "extension",
258 .apiver_var = "plugins_extensions_apiver__",
259 .plugins_var = "plugins_extensions__",
260 .envdir = "LIBYANG_EXTENSIONS_PLUGINS_DIR",
261 .dir = LYPLG_EXT_DIR,
262 .apiver = LYPLG_EXT_API_VERSION
263 }
264};
265
Radek Krejcibf940f92021-03-24 21:04:13 +0100266/**
267 * @brief Get the expected plugin objects from the loaded dynamic object and add the defined plugins into the lists of
268 * available extensions/types plugins.
269 *
270 * @param[in] dlhandler Loaded dynamic library handler.
271 * @param[in] pathname Path of the loaded library for logging.
272 * @param[in] type Type of the plugins to get from the dynamic library. Note that a single library can hold both types
273 * and extensions plugins implementations, so this function should be called twice (once for each plugin type) with
274 * different @p type values
275 * @return LY_ERR values.
276 */
277static LY_ERR
278plugins_load(void *dlhandler, const char *pathname, enum LYPLG type)
279{
280 const void *plugins;
281 uint32_t *version;
282
283 /* type plugin */
284 version = dlsym(dlhandler, plugins_load_info[type].apiver_var);
285 if (version) {
286 /* check version ... */
287 if (*version != plugins_load_info[type].apiver) {
288 LOGERR(NULL, LY_EINVAL, "Processing user %s plugin \"%s\" failed, wrong API version - %d expected, %d found.",
289 plugins_load_info[type].id, pathname, plugins_load_info[type].apiver, *version);
290 return LY_EINVAL;
291 }
292
293 /* ... get types plugins information ... */
294 if (!(plugins = dlsym(dlhandler, plugins_load_info[type].plugins_var))) {
295 char *errstr = dlerror();
296 LOGERR(NULL, LY_EINVAL, "Processing user %s plugin \"%s\" failed, missing %s plugins information (%s).",
297 plugins_load_info[type].id, pathname, plugins_load_info[type].id, errstr);
298 return LY_EINVAL;
299 }
300
301 /* ... and load all the types plugins */
302 LY_CHECK_RET(plugins_insert(type, plugins));
303 }
304
305 return LY_SUCCESS;
306}
307
308static LY_ERR
309plugins_load_module(const char *pathname)
310{
311 LY_ERR ret = LY_SUCCESS;
312 void *dlhandler;
313 uint32_t types_count = 0, extensions_count = 0;
314
315 dlerror(); /* Clear any existing error */
316
317 dlhandler = dlopen(pathname, RTLD_NOW);
318 if (!dlhandler) {
319 LOGERR(NULL, LY_ESYS, "Loading \"%s\" as a plugin failed (%s).", pathname, dlerror());
320 return LY_ESYS;
321 }
322
323 if (ly_set_contains(&plugins_handlers, dlhandler, NULL)) {
324 /* the plugin is already loaded */
325 LOGVRB("Plugin \"%s\" already loaded.", pathname);
326
327 /* keep the correct refcount */
328 dlclose(dlhandler);
329 return LY_SUCCESS;
330 }
331
332 /* remember the current plugins lists for recovery */
333 types_count = plugins_types.count;
334 extensions_count = plugins_extensions.count;
335
336 /* type plugin */
337 ret = plugins_load(dlhandler, pathname, LYPLG_TYPE);
338 LY_CHECK_GOTO(ret, error);
339
340 /* extension plugin */
341 ret = plugins_load(dlhandler, pathname, LYPLG_EXTENSION);
342 LY_CHECK_GOTO(ret, error);
343
344 /* remember the dynamic plugin */
345 ret = ly_set_add(&plugins_handlers, dlhandler, 1, NULL);
346 LY_CHECK_GOTO(ret, error);
347
348 return LY_SUCCESS;
349
350error:
351 dlclose(dlhandler);
352
353 /* revert changes in the lists */
354 while (plugins_types.count > types_count) {
355 ly_set_rm_index(&plugins_types, plugins_types.count - 1, NULL);
356 }
357 while (plugins_extensions.count > extensions_count) {
358 ly_set_rm_index(&plugins_extensions, plugins_extensions.count - 1, NULL);
359 }
360
361 return ret;
362}
363
Radek Krejci968d7552021-03-26 20:33:51 +0100364static LY_ERR
365plugins_insert_dir(enum LYPLG type)
366{
367 LY_ERR ret = LY_SUCCESS;
368 const char *pluginsdir;
369 DIR *dir;
370 ly_bool default_dir = 0;
371
372 /* try to get the plugins directory from environment variable */
373 pluginsdir = getenv(plugins_load_info[type].envdir);
374 if (!pluginsdir) {
375 /* remember that we are going to a default dir and do not print warning if the directory doesn't exist */
376 default_dir = 1;
377 pluginsdir = plugins_load_info[type].dir;
378 }
379
380 dir = opendir(pluginsdir);
381 if (!dir) {
382 /* no directory (or no access to it), no extension plugins */
383 if (!default_dir || (errno != ENOENT)) {
384 LOGWRN(NULL, "Failed to open libyang %s plugins directory \"%s\" (%s).", plugins_load_info[type].id,
385 pluginsdir, strerror(errno));
386 }
387 } else {
388 struct dirent *file;
389
390 while ((file = readdir(dir))) {
391 size_t len;
392 char pathname[PATH_MAX];
393
394 /* required format of the filename is *LYPLG_SUFFIX */
395 len = strlen(file->d_name);
396 if ((len < LYPLG_SUFFIX_LEN + 1) || strcmp(&file->d_name[len - LYPLG_SUFFIX_LEN], LYPLG_SUFFIX)) {
397 continue;
398 }
399
400 /* and construct the filepath */
401 snprintf(pathname, PATH_MAX, "%s/%s", pluginsdir, file->d_name);
402
403 ret = plugins_load_module(pathname);
404 if (ret) {
405 break;
406 }
407 }
408 closedir(dir);
409 }
410
411 return ret;
412}
413
Michal Vasko9e2bc702021-06-09 11:43:36 +0200414#endif
415
Radek Krejci3e6632f2021-03-22 22:08:21 +0100416LY_ERR
417lyplg_init(void)
418{
419 LY_ERR ret;
420
421 pthread_mutex_lock(&plugins_guard);
422 /* let only the first context to initiate plugins, but let others wait for finishing the initiation */
423 if (context_refcount++) {
424 /* already initiated */
425 pthread_mutex_unlock(&plugins_guard);
426 return LY_SUCCESS;
427 }
428
429 /* internal types */
430 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_binary), error);
431 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_bits), error);
432 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_boolean), error);
433 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_decimal64), error);
434 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_empty), error);
435 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_enumeration), error);
436 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_identityref), error);
437 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_instanceid), error);
438 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_integer), error);
439 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_leafref), error);
440 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_string), error);
441 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_union), error);
442
Michal Vaskode4a3412021-04-14 15:38:27 +0200443 /* ietf-inet-types */
Michal Vasko3159e782021-05-03 15:12:35 +0200444 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv4_address), error);
Michal Vasko82bf15e2021-05-06 16:01:56 +0200445 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv4_address_no_zone), error);
Michal Vasko7caa3e62021-05-03 14:59:25 +0200446 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv6_address), error);
Michal Vasko18a4a732021-05-06 16:21:44 +0200447 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv6_address_no_zone), error);
Michal Vasko15dc9fa2021-05-03 14:33:05 +0200448 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv4_prefix), error);
449 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv6_prefix), error);
Michal Vasko3e52de52021-04-13 13:45:55 +0200450
Michal Vaskode4a3412021-04-14 15:38:27 +0200451 /* ietf-yang-types */
452 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_date_and_time), error);
Michal Vaskode4a3412021-04-14 15:38:27 +0200453 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_xpath10), error);
454
Michal Vasko7a53c7d2021-08-06 11:28:57 +0200455 /* ietf-netconf-acm */
456 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_node_instanceid), error);
457
Radek Krejci3e6632f2021-03-22 22:08:21 +0100458 /* internal extensions */
459 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_EXTENSION, plugins_metadata), error);
460 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_EXTENSION, plugins_nacm), error);
461 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_EXTENSION, plugins_yangdata), error);
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +0100462 LY_CHECK_GOTO(ret = plugins_insert(LYPLG_EXTENSION, plugins_schema_mount), error);
Radek Krejci3e6632f2021-03-22 22:08:21 +0100463
Michal Vasko9e2bc702021-06-09 11:43:36 +0200464#ifndef STATIC
Radek Krejci968d7552021-03-26 20:33:51 +0100465 /* external types */
466 LY_CHECK_GOTO(ret = plugins_insert_dir(LYPLG_TYPE), error);
467
468 /* external extensions */
469 LY_CHECK_GOTO(ret = plugins_insert_dir(LYPLG_EXTENSION), error);
Michal Vasko9e2bc702021-06-09 11:43:36 +0200470#endif
Radek Krejci968d7552021-03-26 20:33:51 +0100471
Radek Krejci3e6632f2021-03-22 22:08:21 +0100472 /* initiation done, wake-up possibly waiting threads creating another contexts */
473 pthread_mutex_unlock(&plugins_guard);
474
475 return LY_SUCCESS;
476
477error:
478 /* initiation was not successful - cleanup (and let others to try) */
Michal Vasko9e2bc702021-06-09 11:43:36 +0200479#ifndef STATIC
Radek Krejci3e6632f2021-03-22 22:08:21 +0100480 lyplg_clean_();
Michal Vasko9e2bc702021-06-09 11:43:36 +0200481#endif
Radek Krejci3e6632f2021-03-22 22:08:21 +0100482 pthread_mutex_unlock(&plugins_guard);
483
484 if (ret == LY_EINVAL) {
485 /* all the plugins here are internal, invalid record actually means an internal libyang error */
486 ret = LY_EINT;
487 }
488 return ret;
489}
Radek Krejcibf940f92021-03-24 21:04:13 +0100490
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100491LIBYANG_API_DEF LY_ERR
Radek Krejcibf940f92021-03-24 21:04:13 +0100492lyplg_add(const char *pathname)
493{
Michal Vasko9e2bc702021-06-09 11:43:36 +0200494#ifdef STATIC
495 (void)pathname;
496
497 LOGERR(NULL, LY_EINVAL, "Plugins are not supported in statically built library.");
498 return LY_EINVAL;
Jan Kundrát323c3122021-12-14 11:44:57 +0100499#elif defined (_WIN32)
500 (void)pathname;
501
502 LOGERR(NULL, LY_EINVAL, "Plugins are not (yet) supported on Windows.");
503 return LY_EINVAL;
Michal Vasko9e2bc702021-06-09 11:43:36 +0200504#else
Radek Krejcibf940f92021-03-24 21:04:13 +0100505 LY_ERR ret = LY_SUCCESS;
506
507 LY_CHECK_ARG_RET(NULL, pathname, LY_EINVAL);
508
509 /* works only in case a context exists */
510 pthread_mutex_lock(&plugins_guard);
511 if (!context_refcount) {
512 /* no context */
513 pthread_mutex_unlock(&plugins_guard);
514 LOGERR(NULL, LY_EDENIED, "To add a plugin, at least one context must exists.");
515 return LY_EDENIED;
516 }
517
518 ret = plugins_load_module(pathname);
519
520 pthread_mutex_unlock(&plugins_guard);
521
522 return ret;
Michal Vasko9e2bc702021-06-09 11:43:36 +0200523#endif
Radek Krejcibf940f92021-03-24 21:04:13 +0100524}