/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 | } |