Coverage Report

Created: 2026-03-26 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libyang/src/plugins.c
Line
Count
Source
1
/**
2
 * @file plugins.c
3
 * @author Radek Krejci <rkrejci@cesnet.cz>
4
 * @author Michal Vasko <mvasko@cesnet.cz>
5
 * @brief Manipulate with the type and extension plugins.
6
 *
7
 * Copyright (c) 2021 - 2026 CESNET, z.s.p.o.
8
 *
9
 * This source code is licensed under BSD 3-Clause License (the "License").
10
 * You may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *     https://opensource.org/licenses/BSD-3-Clause
14
 */
15
16
#define _GNU_SOURCE
17
18
#include "plugins.h"
19
#include "plugins_internal.h"
20
21
#include <assert.h>
22
#include <dirent.h>
23
#ifndef STATIC
24
# include <dlfcn.h>
25
#endif
26
#include <errno.h>
27
#include <limits.h>
28
#include <pthread.h>
29
#include <stddef.h>
30
#include <stdint.h>
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
35
#include "ly_common.h"
36
#include "ly_config.h"
37
#include "plugins_exts.h"
38
#include "plugins_types.h"
39
#include "set.h"
40
41
/*
42
 * internal type plugins records
43
 */
44
extern const struct lyplg_type_record plugins_binary[];
45
extern const struct lyplg_type_record plugins_bits[];
46
extern const struct lyplg_type_record plugins_boolean[];
47
extern const struct lyplg_type_record plugins_decimal64[];
48
extern const struct lyplg_type_record plugins_empty[];
49
extern const struct lyplg_type_record plugins_enumeration[];
50
extern const struct lyplg_type_record plugins_identityref[];
51
extern const struct lyplg_type_record plugins_instanceid[];
52
extern const struct lyplg_type_record plugins_integer[];
53
extern const struct lyplg_type_record plugins_leafref[];
54
extern const struct lyplg_type_record plugins_string[];
55
extern const struct lyplg_type_record plugins_union[];
56
57
/*
58
 * yang
59
 */
60
extern const struct lyplg_type_record plugins_instanceid_keys[];
61
62
/*
63
 * ietf-inet-types
64
 */
65
extern const struct lyplg_type_record plugins_ipv4_address[];
66
extern const struct lyplg_type_record plugins_ipv4_address_no_zone[];
67
extern const struct lyplg_type_record plugins_ipv6_address[];
68
extern const struct lyplg_type_record plugins_ipv6_address_no_zone[];
69
extern const struct lyplg_type_record plugins_ipv4_address_prefix[];
70
extern const struct lyplg_type_record plugins_ipv6_address_prefix[];
71
72
/*
73
 * ietf-yang-types
74
 */
75
extern const struct lyplg_type_record plugins_date_and_time[];
76
extern const struct lyplg_type_record plugins_date[];
77
extern const struct lyplg_type_record plugins_time[];
78
extern const struct lyplg_type_record plugins_hex_string[];
79
extern const struct lyplg_type_record plugins_xpath10[];
80
81
/*
82
 * ietf-netconf-acm
83
 */
84
extern const struct lyplg_type_record plugins_node_instanceid[];
85
86
/*
87
 * libnetconf2-netconf-server
88
 */
89
extern const struct lyplg_type_record plugins_time_period[];
90
91
/*
92
 * lyds_tree
93
 */
94
extern const struct lyplg_type_record plugins_lyds_tree[];
95
96
/*
97
 * internal extension plugins records
98
 */
99
extern struct lyplg_ext_record plugins_metadata[];
100
extern struct lyplg_ext_record plugins_nacm[];
101
extern struct lyplg_ext_record plugins_yangdata[];
102
extern struct lyplg_ext_record plugins_schema_mount[];
103
extern struct lyplg_ext_record plugins_structure[];
104
extern struct lyplg_ext_record plugins_openconfig[];
105
106
static pthread_mutex_t plugins_guard = PTHREAD_MUTEX_INITIALIZER;
107
108
/**
109
 * @brief Counter for currently present contexts able to refer to the loaded plugins.
110
 *
111
 * Plugins are shared among all the created contexts. They are loaded with the creation of the very first context and
112
 * unloaded with the destroy of the last context. Therefore, to reload the list of plugins, all the contexts must be
113
 * destroyed and with the creation of a first new context after that, the plugins will be reloaded.
114
 */
115
static uint32_t context_refcount;
116
117
/**
118
 * @brief Record describing an implemented extension.
119
 *
120
 * Matches ::lyplg_ext_record and ::lyplg_type_record
121
 */
122
struct lyplg_record {
123
    const char *module;          /**< name of the module where the extension/type is defined */
124
    const char *revision;        /**< optional module revision - if not specified, the plugin applies to any revision,
125
                                      which is not an optimal approach due to a possible future revisions of the module.
126
                                      Instead, there should be defined multiple items in the plugins list, each with the
127
                                      different revision, but all with the same pointer to the plugin functions. The
128
                                      only valid use case for the NULL revision is the case the module has no revision. */
129
    const char *name;            /**< name of the extension/typedef */
130
    int8_t plugin[];             /**< specific plugin type's data - ::lyplg_ext or ::lyplg_type */
131
};
132
133
#ifndef STATIC
134
static struct ly_set plugins_handlers;
135
#endif
136
137
/* global sets of loaded plugins */
138
struct ly_set ly_plugins_types;
139
struct ly_set ly_plugins_extensions;
140
141
/* global counters for the number of static plugins */
142
uint32_t ly_static_type_plugins_count;
143
uint32_t ly_static_ext_plugins_count;
144
145
LIBYANG_API_DEF struct lyplg_type *
146
lysc_get_type_plugin(uintptr_t plugin_ref)
147
0
{
148
0
    if (!plugin_ref) {
149
0
        return NULL;
150
0
    }
151
152
0
    if (plugin_ref <= ly_static_type_plugins_count) {
153
        /* plugin is static, fetch it from the global list */
154
0
        return &((struct lyplg_type_record *)ly_plugins_types.objs[plugin_ref - 1])->plugin;
155
0
    } else {
156
        /* plugin is dynamic, return the pointer */
157
0
        return (struct lyplg_type *)plugin_ref;
158
0
    }
159
0
}
160
161
LIBYANG_API_DEF struct lyplg_ext *
162
lysc_get_ext_plugin(uintptr_t plugin_ref)
163
0
{
164
0
    if (!plugin_ref) {
165
0
        return NULL;
166
0
    }
167
168
0
    if (plugin_ref <= ly_static_ext_plugins_count) {
169
        /* plugin is static, fetch it from the global list */
170
0
        return &((struct lyplg_ext_record *)ly_plugins_extensions.objs[plugin_ref - 1])->plugin;
171
0
    } else {
172
        /* plugin is dynamic, return the pointer */
173
0
        return (struct lyplg_ext *)plugin_ref;
174
0
    }
175
0
}
176
177
/**
178
 * @brief Iterate over list of loaded plugins of the given @p type.
179
 *
180
 * @param[in] ctx Context for which the plugin is searched for.
181
 * @param[in] type Type of the plugins to iterate.
182
 * @param[in,out] index Iterator - set to 0 for the first call.
183
 * @return The plugin records, NULL if no more record is available.
184
 */
185
static struct lyplg_record *
186
plugins_iter(const struct ly_ctx *ctx, enum LYPLG type, uint32_t *index)
187
12.4M
{
188
12.4M
    const struct ly_set *plugins;
189
190
12.4M
    assert(index);
191
192
12.4M
    if (type == LYPLG_EXTENSION) {
193
231k
        plugins = ctx ? &ctx->plugins_extensions : &ly_plugins_extensions;
194
12.1M
    } else {
195
12.1M
        plugins = ctx ? &ctx->plugins_types : &ly_plugins_types;
196
12.1M
    }
197
198
12.4M
    if (*index == plugins->count) {
199
815k
        return NULL;
200
815k
    }
201
202
11.5M
    *index += 1;
203
11.5M
    return plugins->objs[*index - 1];
204
12.4M
}
205
206
/**
207
 * @brief Find the given @p type plugin record.
208
 *
209
 * @param[in] ctx Context for which the plugin record is searched for, NULL for static plugins.
210
 * @param[in] type Type of the plugin record to find.
211
 * @param[in] module Module name of the plugin record.
212
 * @param[in] revision Revision of the @p module.
213
 * @param[in] name Name of the plugin record.
214
 * @param[out] record_idx Optional output parameter to get the index of the found plugin record.
215
 * @return Found plugin record or NULL if not found.
216
 */
217
static void *
218
lyplg_record_find(const struct ly_ctx *ctx, enum LYPLG type, const char *module, const char *revision, const char *name,
219
        uint32_t *record_idx)
220
1.55M
{
221
1.55M
    uint32_t i = 0;
222
1.55M
    struct lyplg_record *item;
223
224
1.55M
    assert(module);
225
1.55M
    assert(name);
226
227
1.55M
    if (record_idx) {
228
1.55M
        *record_idx = 0;
229
1.55M
    }
230
231
12.4M
    while ((item = plugins_iter(ctx, type, &i)) != NULL) {
232
11.5M
        if (!strcmp(item->module, module) && !strcmp(item->name, name)) {
233
743k
            if (item->revision && revision && strcmp(item->revision, revision)) {
234
7.99k
                continue;
235
735k
            } else if (!revision && item->revision) {
236
0
                continue;
237
0
            }
238
239
735k
            if (record_idx) {
240
                /* return the record idx, -1 because the iterator is already increased */
241
735k
                *record_idx = i - 1;
242
735k
            }
243
244
735k
            return item;
245
743k
        }
246
11.5M
    }
247
248
815k
    return NULL;
249
1.55M
}
250
251
/**
252
 * @brief Find the plugin of the given @p type.
253
 *
254
 * @param[in] ctx Context for which the plugin record is searched for, NULL for static plugins.
255
 * @param[in] type Type of the plugin record to find.
256
 * @param[in] module Module name of the plugin.
257
 * @param[in] revision Revision of the @p module.
258
 * @param[in] name Name of the plugin.
259
 * @return Reference to the callbacks plugin structure. Use ::LYSC_GET_TYPE_PLG
260
 * or ::LYSC_GET_EXT_PLG on the returned value to get the actual plugin. 0 if not found.
261
 */
262
static uintptr_t
263
lyplg_plugin_find(const struct ly_ctx *ctx, enum LYPLG type, const char *module, const char *revision, const char *name)
264
775k
{
265
775k
    struct lyplg_type_record *record = NULL;
266
775k
    uint32_t record_idx = 0, static_plugin_count;
267
268
775k
    if (ctx) {
269
        /* try to find context specific plugin */
270
775k
        record = lyplg_record_find(ctx, type, module, revision, name, &record_idx);
271
775k
        if (record) {
272
            /* plugin found in the context, hence it is dynamic so return the ptr to it */
273
0
            return (uintptr_t)&record->plugin;
274
0
        }
275
775k
    }
276
277
    /* try to find shared plugin */
278
775k
    record = lyplg_record_find(NULL, type, module, revision, name, &record_idx);
279
775k
    if (!record) {
280
        /* not found */
281
39.9k
        return 0;
282
39.9k
    }
283
284
735k
    static_plugin_count = (type == LYPLG_TYPE) ? ly_static_type_plugins_count : ly_static_ext_plugins_count;
285
735k
    if (record_idx < static_plugin_count) {
286
        /* static plugin, return an index with an offset of +1 in order to keep 0 as an invalid index (a NULL ptr) */
287
735k
        return record_idx + 1;
288
735k
    } else {
289
        /* dynamic plugin, return the pointer to it */
290
0
        return (uintptr_t)&record->plugin;
291
0
    }
292
735k
}
293
294
uintptr_t
295
lyplg_type_plugin_find(const struct ly_ctx *ctx, const char *module, const char *revision, const char *name)
296
743k
{
297
743k
    return lyplg_plugin_find(ctx, LYPLG_TYPE, module, revision, name);
298
743k
}
299
300
uintptr_t
301
lyplg_ext_plugin_find(const struct ly_ctx *ctx, const char *module, const char *revision, const char *name)
302
31.9k
{
303
31.9k
    return lyplg_plugin_find(ctx, LYPLG_EXTENSION, module, revision, name);
304
31.9k
}
305
306
/**
307
 * @brief Insert the provided extension plugin records into the internal set of extension plugins for use by libyang.
308
 *
309
 * @param[in] ctx The context to which the plugin should be associated with. If NULL, the plugin is considered to be shared
310
 * between all existing contexts.
311
 * @param[in] type The type of plugins records
312
 * @param[in] recs An array of plugin records provided by the plugin implementation. The array must be terminated by a zeroed
313
 * record.
314
 * @return LY_SUCCESS in case of success
315
 * @return LY_EINVAL for invalid information in @p recs.
316
 * @return LY_EMEM in case of memory allocation failure.
317
 */
318
static LY_ERR
319
plugins_insert(struct ly_ctx *ctx, enum LYPLG type, const void *recs)
320
33
{
321
33
    struct ly_set *plugins;
322
33
    const struct lyplg_ext_record *rec_ext;
323
33
    const struct lyplg_type_record *rec_type;
324
33
    uint32_t i;
325
326
33
    if (!recs) {
327
0
        return LY_SUCCESS;
328
0
    }
329
330
33
    if (type == LYPLG_EXTENSION) {
331
6
        rec_ext = (const struct lyplg_ext_record *)recs;
332
6
        plugins = ctx ? &ctx->plugins_extensions : &ly_plugins_extensions;
333
334
17
        for (i = 0; rec_ext[i].name; i++) {
335
11
            if (rec_ext[i].plugin.snode && !rec_ext[i].plugin.validate) {
336
0
                LOGERR(ctx, LY_EINT, "Ext plugin \"%s\" has no validate() callback defined.", rec_ext[i].name);
337
0
                return LY_EINT;
338
0
            }
339
340
11
            LY_CHECK_RET(ly_set_add(plugins, (void *)&rec_ext[i], 0, NULL));
341
11
        }
342
27
    } else { /* LYPLG_TYPE */
343
27
        rec_type = (const struct lyplg_type_record *)recs;
344
27
        plugins = ctx ? &ctx->plugins_types : &ly_plugins_types;
345
346
71
        for (i = 0; rec_type[i].name; i++) {
347
44
            LY_CHECK_RET(ly_set_add(plugins, (void *)&rec_type[i], 0, NULL));
348
44
        }
349
27
    }
350
351
33
    return LY_SUCCESS;
352
33
}
353
354
#ifndef STATIC
355
356
static void
357
lyplg_close_cb(void *handle)
358
{
359
    dlclose(handle);
360
}
361
362
static void
363
lyplg_clean_(void)
364
{
365
    if (--context_refcount) {
366
        /* there is still some other context, do not remove the plugins */
367
        return;
368
    }
369
370
    ly_set_erase(&ly_plugins_types, NULL);
371
    ly_set_erase(&ly_plugins_extensions, NULL);
372
    ly_set_erase(&plugins_handlers, lyplg_close_cb);
373
    ly_static_type_plugins_count = ly_static_ext_plugins_count = 0;
374
}
375
376
#endif
377
378
void
379
lyplg_clean(void)
380
7.99k
{
381
#ifndef STATIC
382
    pthread_mutex_lock(&plugins_guard);
383
    lyplg_clean_();
384
    pthread_mutex_unlock(&plugins_guard);
385
#endif
386
7.99k
}
387
388
#ifndef STATIC
389
390
/**
391
 * @brief Just a variadic data to cover extension and type plugins by a single ::plugins_load() function.
392
 *
393
 * The values are taken from ::LY_PLUGINS_EXTENSIONS and ::LYPLG_TYPES macros.
394
 */
395
static const struct {
396
    const char *id;          /**< string identifier: type/extension */
397
    const char *apiver_var;  /**< expected variable name holding API version value */
398
    const char *plugins_var; /**< expected variable name holding plugin records */
399
    const char *envdir;      /**< environment variable containing directory with the plugins */
400
    const char *dir;         /**< default directory with the plugins (has less priority than envdir) */
401
    uint32_t apiver;         /**< expected API version */
402
} plugins_load_info[] = {
403
    {   /* LYPLG_TYPE */
404
        .id = "type",
405
        .apiver_var = "plugins_types_apiver__",
406
        .plugins_var = "plugins_types__",
407
        .envdir = "LIBYANG_TYPES_PLUGINS_DIR",
408
        .dir = LYPLG_TYPE_DIR,
409
        .apiver = LYPLG_TYPE_API_VERSION
410
    }, {/* LYPLG_EXTENSION */
411
        .id = "extension",
412
        .apiver_var = "plugins_extensions_apiver__",
413
        .plugins_var = "plugins_extensions__",
414
        .envdir = "LIBYANG_EXTENSIONS_PLUGINS_DIR",
415
        .dir = LYPLG_EXT_DIR,
416
        .apiver = LYPLG_EXT_API_VERSION
417
    }
418
};
419
420
/**
421
 * @brief Get the expected plugin objects from the loaded dynamic object and add the defined plugins into the lists of
422
 * available extensions/types plugins.
423
 *
424
 * @param[in] dlhandler Loaded dynamic library handler.
425
 * @param[in] pathname Path of the loaded library for logging.
426
 * @param[in] type Type of the plugins to get from the dynamic library. Note that a single library can hold both types
427
 * and extensions plugins implementations, so this function should be called twice (once for each plugin type) with
428
 * different @p type values
429
 * @return LY_ERR values.
430
 */
431
static LY_ERR
432
plugins_load(void *dlhandler, const char *pathname, enum LYPLG type)
433
{
434
    const void *plugins;
435
    uint32_t *version;
436
437
    /* type plugin */
438
    version = dlsym(dlhandler, plugins_load_info[type].apiver_var);
439
    if (version) {
440
        /* check version ... */
441
        if (*version != plugins_load_info[type].apiver) {
442
            LOGERR(NULL, LY_EINVAL, "Processing user %s plugin \"%s\" failed, wrong API version - %d expected, %d found.",
443
                    plugins_load_info[type].id, pathname, plugins_load_info[type].apiver, *version);
444
            return LY_EINVAL;
445
        }
446
447
        /* ... get types plugins information ... */
448
        if (!(plugins = dlsym(dlhandler, plugins_load_info[type].plugins_var))) {
449
            char *errstr = dlerror();
450
451
            LOGERR(NULL, LY_EINVAL, "Processing user %s plugin \"%s\" failed, missing %s plugins information (%s).",
452
                    plugins_load_info[type].id, pathname, plugins_load_info[type].id, errstr);
453
            return LY_EINVAL;
454
        }
455
456
        /* ... and load all the types plugins */
457
        LY_CHECK_RET(plugins_insert(NULL, type, plugins));
458
    }
459
460
    return LY_SUCCESS;
461
}
462
463
static LY_ERR
464
plugins_load_module(const char *pathname)
465
{
466
    LY_ERR ret = LY_SUCCESS;
467
    void *dlhandler;
468
    uint32_t types_count = 0, extensions_count = 0;
469
470
    dlerror();    /* Clear any existing error */
471
472
    dlhandler = dlopen(pathname, RTLD_NOW);
473
    if (!dlhandler) {
474
        LOGERR(NULL, LY_ESYS, "Loading \"%s\" as a plugin failed (%s).", pathname, dlerror());
475
        return LY_ESYS;
476
    }
477
478
    if (ly_set_contains(&plugins_handlers, dlhandler, NULL)) {
479
        /* the plugin is already loaded */
480
        LOGVRB("Plugin \"%s\" already loaded.", pathname);
481
482
        /* keep the correct refcount */
483
        dlclose(dlhandler);
484
        return LY_SUCCESS;
485
    }
486
487
    /* remember the current plugins lists for recovery */
488
    types_count = ly_plugins_types.count;
489
    extensions_count = ly_plugins_extensions.count;
490
491
    /* type plugin */
492
    ret = plugins_load(dlhandler, pathname, LYPLG_TYPE);
493
    LY_CHECK_GOTO(ret, error);
494
495
    /* extension plugin */
496
    ret = plugins_load(dlhandler, pathname, LYPLG_EXTENSION);
497
    LY_CHECK_GOTO(ret, error);
498
499
    /* remember the dynamic plugin */
500
    ret = ly_set_add(&plugins_handlers, dlhandler, 1, NULL);
501
    LY_CHECK_GOTO(ret, error);
502
503
    return LY_SUCCESS;
504
505
error:
506
    dlclose(dlhandler);
507
508
    /* revert changes in the lists */
509
    while (ly_plugins_types.count > types_count) {
510
        ly_set_rm_index(&ly_plugins_types, ly_plugins_types.count - 1, NULL);
511
    }
512
    while (ly_plugins_extensions.count > extensions_count) {
513
        ly_set_rm_index(&ly_plugins_extensions, ly_plugins_extensions.count - 1, NULL);
514
    }
515
516
    return ret;
517
}
518
519
static LY_ERR
520
plugins_insert_dir(enum LYPLG type)
521
{
522
    LY_ERR ret = LY_SUCCESS;
523
    const char *pluginsdir;
524
    DIR *dir;
525
    ly_bool default_dir = 0;
526
527
    /* try to get the plugins directory from environment variable */
528
    pluginsdir = getenv(plugins_load_info[type].envdir);
529
    if (!pluginsdir) {
530
        /* remember that we are going to a default dir and do not print warning if the directory doesn't exist */
531
        default_dir = 1;
532
        pluginsdir = plugins_load_info[type].dir;
533
    }
534
535
    dir = opendir(pluginsdir);
536
    if (!dir) {
537
        /* no directory (or no access to it), no extension plugins */
538
        if (!default_dir || (errno != ENOENT)) {
539
            LOGWRN(NULL, "Failed to open libyang %s plugins directory \"%s\" (%s).", plugins_load_info[type].id,
540
                    pluginsdir, strerror(errno));
541
        }
542
    } else {
543
        struct dirent *file;
544
545
        while ((file = readdir(dir))) {
546
            size_t len;
547
            char pathname[PATH_MAX];
548
549
            /* required format of the filename is *LYPLG_SUFFIX */
550
            len = strlen(file->d_name);
551
            if ((len < LYPLG_SUFFIX_LEN + 1) || strcmp(&file->d_name[len - LYPLG_SUFFIX_LEN], LYPLG_SUFFIX)) {
552
                continue;
553
            }
554
555
            /* and construct the filepath */
556
            snprintf(pathname, PATH_MAX, "%s/%s", pluginsdir, file->d_name);
557
558
            ret = plugins_load_module(pathname);
559
            if (ret) {
560
                break;
561
            }
562
        }
563
        closedir(dir);
564
    }
565
566
    return ret;
567
}
568
569
#endif
570
571
LY_ERR
572
lyplg_init(ly_bool builtin_type_plugins_only, ly_bool static_plugins_only)
573
7.99k
{
574
7.99k
    LY_ERR ret;
575
576
    /* let only the first context to initiate plugins, but let others wait for finishing the initiation */
577
7.99k
    pthread_mutex_lock(&plugins_guard);
578
579
7.99k
    if (context_refcount++) {
580
        /* already initiated */
581
7.99k
        pthread_mutex_unlock(&plugins_guard);
582
7.99k
        return LY_SUCCESS;
583
7.99k
    }
584
585
    /* internal types */
586
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_binary), error);
587
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_bits), error);
588
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_boolean), error);
589
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_decimal64), error);
590
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_empty), error);
591
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_enumeration), error);
592
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_identityref), error);
593
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_instanceid), error);
594
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_integer), error);
595
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_leafref), error);
596
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_string), error);
597
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_union), error);
598
599
    /* metadata and lyds_tree, which requires them */
600
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_metadata), error);
601
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_lyds_tree), error);
602
603
1
    if (!builtin_type_plugins_only) {
604
        /* yang */
605
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_instanceid_keys), error);
606
607
        /* ietf-inet-types */
608
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv4_address), error);
609
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv4_address_no_zone), error);
610
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv6_address), error);
611
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv6_address_no_zone), error);
612
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv4_address_prefix), error);
613
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_ipv6_address_prefix), error);
614
615
        /* ietf-yang-types */
616
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_date_and_time), error);
617
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_date), error);
618
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_time), error);
619
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_hex_string), error);
620
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_xpath10), error);
621
622
        /* libnetconf2-netconf-server */
623
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_time_period), error);
624
625
        /* ietf-netconf-acm */
626
1
        LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_TYPE, plugins_node_instanceid), error);
627
1
    }
628
629
    /* internal extensions */
630
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_nacm), error);
631
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_yangdata), error);
632
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_schema_mount), error);
633
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_structure), error);
634
1
    LY_CHECK_GOTO(ret = plugins_insert(NULL, LYPLG_EXTENSION, plugins_openconfig), error);
635
636
    /* the global plugin sets contain only static plugins at this point, so assign to the counters here.
637
     * the counters are used to determine whether a plugin is static or not */
638
1
    ly_static_type_plugins_count = ly_plugins_types.count;
639
1
    ly_static_ext_plugins_count = ly_plugins_extensions.count;
640
641
#ifndef STATIC
642
    if (!static_plugins_only) {
643
        /* external types */
644
        LY_CHECK_GOTO(ret = plugins_insert_dir(LYPLG_TYPE), error);
645
646
        /* external extensions */
647
        LY_CHECK_GOTO(ret = plugins_insert_dir(LYPLG_EXTENSION), error);
648
    }
649
#endif
650
651
    /* initiation done, wake-up possibly waiting threads creating another contexts */
652
1
    pthread_mutex_unlock(&plugins_guard);
653
654
1
    return LY_SUCCESS;
655
656
0
error:
657
    /* initiation was not successful - cleanup (and let others to try) */
658
#ifndef STATIC
659
    lyplg_clean_();
660
#endif
661
0
    pthread_mutex_unlock(&plugins_guard);
662
663
0
    if (ret == LY_EINVAL) {
664
        /* all the plugins here are internal, invalid record actually means an internal libyang error */
665
0
        ret = LY_EINT;
666
0
    }
667
0
    return ret;
668
1
}
669
670
LIBYANG_API_DEF LY_ERR
671
lyplg_add(const char *pathname)
672
0
{
673
0
#ifdef STATIC
674
0
    (void)pathname;
675
676
0
    LOGERR(NULL, LY_EINVAL, "Plugins are not supported in statically built library.");
677
0
    return LY_EINVAL;
678
#elif defined (_WIN32)
679
    (void)pathname;
680
681
    LOGERR(NULL, LY_EINVAL, "Plugins are not (yet) supported on Windows.");
682
    return LY_EINVAL;
683
#else
684
    LY_ERR ret = LY_SUCCESS;
685
686
    LY_CHECK_ARG_RET(NULL, pathname, LY_EINVAL);
687
688
    /* works only in case a context exists */
689
    pthread_mutex_lock(&plugins_guard);
690
    if (!context_refcount) {
691
        /* no context */
692
        pthread_mutex_unlock(&plugins_guard);
693
        LOGERR(NULL, LY_EDENIED, "To add a plugin, at least one context must exists.");
694
        return LY_EDENIED;
695
    }
696
697
    ret = plugins_load_module(pathname);
698
699
    pthread_mutex_unlock(&plugins_guard);
700
701
    return ret;
702
#endif
703
0
}
704
705
/**
706
 * @brief Manually load an extension plugins from memory
707
 *
708
 * Note, that a plugin can be loaded only if there is at least one context. The loaded plugins are connected with the
709
 * existence of a context. When all the contexts are destroyed, all the plugins are unloaded.
710
 *
711
 * @param[in] ctx The context to which the plugin should be associated with. If NULL, the plugin is considered to be shared
712
 * between all existing contexts.
713
 * @param[in] version The version of plugin records.
714
 * @param[in] type The type of plugins records.
715
 * @param[in] recs An array of plugin records provided by the plugin implementation. The array must be terminated by a zeroed
716
 * record.
717
 *
718
 * @return LY_SUCCESS if the plugins with compatible version were successfully loaded.
719
 * @return LY_EDENIED in case there is no context and the plugin cannot be loaded.
720
 * @return LY_EINVAL when recs is NULL or the plugin contains invalid content for this libyang version.
721
 */
722
static LY_ERR
723
lyplg_add_plugin(struct ly_ctx *ctx, uint32_t version, enum LYPLG type, const void *recs)
724
0
{
725
0
    LY_ERR ret = LY_SUCCESS;
726
0
    uint32_t cur_ver = (type == LYPLG_TYPE) ? LYPLG_TYPE_API_VERSION : LYPLG_EXT_API_VERSION;
727
728
0
    LY_CHECK_ARG_RET(NULL, recs, LY_EINVAL);
729
730
0
    if (version != cur_ver) {
731
0
        LOGERR(ctx, LY_EINVAL, "Adding user %s plugin failed, wrong API version - %" PRIu32 " expected, %" PRIu32 " found.",
732
0
                (type == LYPLG_TYPE) ? "type" : "extension", cur_ver, version);
733
0
        return LY_EINVAL;
734
0
    }
735
736
    /* works only in case a context exists */
737
0
    pthread_mutex_lock(&plugins_guard);
738
0
    if (!context_refcount) {
739
        /* no context */
740
0
        pthread_mutex_unlock(&plugins_guard);
741
0
        LOGERR(NULL, LY_EDENIED, "To add a plugin, at least one context must exists.");
742
0
        return LY_EDENIED;
743
0
    }
744
745
0
    ret = plugins_insert(ctx, type, recs);
746
0
    pthread_mutex_unlock(&plugins_guard);
747
748
0
    return ret;
749
0
}
750
751
LIBYANG_API_DEF LY_ERR
752
lyplg_add_extension_plugin(struct ly_ctx *ctx, uint32_t version, const struct lyplg_ext_record *recs)
753
0
{
754
0
    return lyplg_add_plugin(ctx, version, LYPLG_EXTENSION, recs);
755
0
}
756
757
LIBYANG_API_DEF LY_ERR
758
lyplg_add_type_plugin(struct ly_ctx *ctx, uint32_t version, const struct lyplg_type_record *recs)
759
0
{
760
0
    return lyplg_add_plugin(ctx, version, LYPLG_TYPE, recs);
761
0
}