1 /**
2 * @file plugins.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @author Michal Vasko <mvasko@cesnet.cz>
5 * @brief YANG plugin routines implementation
6 *
7 * Copyright (c) 2015 - 2018 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 #define _GNU_SOURCE
16
17 #include <assert.h>
18 #include <errno.h>
19 #include <dirent.h>
20 #include <dlfcn.h>
21 #include <pthread.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26
27 #include "common.h"
28 #include "extensions.h"
29 #include "user_types.h"
30 #include "plugin_config.h"
31 #include "libyang.h"
32 #include "parser.h"
33
34 /* internal structures storing the plugins */
35 static struct lyext_plugin_list *ext_plugins = NULL;
36 static uint16_t ext_plugins_count = 0; /* size of the ext_plugins array */
37
38 static struct lytype_plugin_list *type_plugins = NULL;
39 static uint16_t type_plugins_count = 0;
40
41 static struct ly_set dlhandlers = {0, 0, {NULL}};
42 static pthread_mutex_t plugins_lock = PTHREAD_MUTEX_INITIALIZER;
43
44 static char **loaded_plugins = NULL; /* both ext and type plugin names */
45 static uint16_t loaded_plugins_count = 0;
46
47 /**
48 * @brief reference counter for the plugins, it actually counts number of contexts
49 */
50 static uint32_t plugin_refs;
51
52 API const char * const *
ly_get_loaded_plugins(void)53 ly_get_loaded_plugins(void)
54 {
55 FUN_IN;
56
57 return (const char * const *)loaded_plugins;
58 }
59
60 API int
ly_clean_plugins(void)61 ly_clean_plugins(void)
62 {
63 FUN_IN;
64
65 unsigned int u;
66 int ret = EXIT_SUCCESS;
67
68 #ifdef STATIC
69 /* lock the extension plugins list */
70 pthread_mutex_lock(&plugins_lock);
71
72 if(ext_plugins) {
73 free(ext_plugins);
74 ext_plugins = NULL;
75 ext_plugins_count = 0;
76 }
77
78 if(type_plugins) {
79 free(type_plugins);
80 type_plugins = NULL;
81 type_plugins_count = 0;
82 }
83
84 for (u = 0; u < loaded_plugins_count; ++u) {
85 free(loaded_plugins[u]);
86 }
87 free(loaded_plugins);
88 loaded_plugins = NULL;
89 loaded_plugins_count = 0;
90
91 /* unlock the global structures */
92 pthread_mutex_unlock(&plugins_lock);
93 return ret;
94 #endif /* STATIC */
95
96 /* lock the extension plugins list */
97 pthread_mutex_lock(&plugins_lock);
98
99 if (--plugin_refs) {
100 /* there is a context that may refer to the plugins, so we cannot remove them */
101 ret = EXIT_FAILURE;
102 goto cleanup;
103 }
104
105 if (!ext_plugins_count && !type_plugins_count) {
106 /* no plugin loaded - nothing to do */
107 goto cleanup;
108 }
109
110 /* clean the lists */
111 free(ext_plugins);
112 ext_plugins = NULL;
113 ext_plugins_count = 0;
114
115 free(type_plugins);
116 type_plugins = NULL;
117 type_plugins_count = 0;
118
119 for (u = 0; u < loaded_plugins_count; ++u) {
120 free(loaded_plugins[u]);
121 }
122 free(loaded_plugins);
123 loaded_plugins = NULL;
124 loaded_plugins_count = 0;
125
126 /* close the dl handlers */
127 for (u = 0; u < dlhandlers.number; u++) {
128 dlclose(dlhandlers.set.g[u]);
129 }
130 free(dlhandlers.set.g);
131 dlhandlers.set.g = NULL;
132 dlhandlers.size = 0;
133 dlhandlers.number = 0;
134
135 cleanup:
136 /* unlock the global structures */
137 pthread_mutex_unlock(&plugins_lock);
138
139 return ret;
140 }
141
142 static int
lytype_load_plugin(void * dlhandler,const char * file_name)143 lytype_load_plugin(void *dlhandler, const char *file_name)
144 {
145 struct lytype_plugin_list *plugin;
146 char *str;
147 int *version;
148
149 #ifdef STATIC
150 return 0;
151 #endif /* STATIC */
152
153 /* get the plugin data */
154 plugin = dlsym(dlhandler, file_name);
155 str = dlerror();
156 if (str) {
157 LOGERR(NULL, LY_ESYS, "Processing \"%s\" user type plugin failed, missing plugin list object (%s).", file_name, str);
158 return 1;
159 }
160 version = dlsym(dlhandler, "lytype_api_version");
161 if (dlerror() || *version != LYTYPE_API_VERSION) {
162 LOGWRN(NULL, "Processing \"%s\" user type plugin failed, wrong API version - %d expected, %d found.",
163 file_name, LYTYPE_API_VERSION, version ? *version : 0);
164 return 1;
165 }
166 return ly_register_types(plugin, file_name);
167 }
168
169 API int
ly_register_types(struct lytype_plugin_list * plugin,const char * log_name)170 ly_register_types(struct lytype_plugin_list *plugin, const char *log_name)
171 {
172 FUN_IN;
173
174 struct lytype_plugin_list *p;
175 uint32_t u, v;
176
177 for (u = 0; plugin[u].name; u++) {
178 /* check user type implementations for collisions */
179 for (v = 0; v < type_plugins_count; v++) {
180 if (!strcmp(plugin[u].name, type_plugins[v].name) &&
181 !strcmp(plugin[u].module, type_plugins[v].module) &&
182 (!plugin[u].revision || !type_plugins[v].revision || !strcmp(plugin[u].revision, type_plugins[v].revision))) {
183 LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed,"
184 "implementation collision for extension %s from module %s%s%s.",
185 log_name, plugin[u].name, plugin[u].module, plugin[u].revision ? "@" : "",
186 plugin[u].revision ? plugin[u].revision : "");
187 return 1;
188 }
189 }
190 }
191
192 /* add the new plugins, we have number of new plugins as u */
193 p = realloc(type_plugins, (type_plugins_count + u) * sizeof *type_plugins);
194 if (!p) {
195 LOGMEM(NULL);
196 return -1;
197 }
198 type_plugins = p;
199 for (; u; u--) {
200 memcpy(&type_plugins[type_plugins_count], &plugin[u - 1], sizeof *plugin);
201 type_plugins_count++;
202 }
203
204 return 0;
205 }
206
207 static int
lyext_load_plugin(void * dlhandler,const char * file_name)208 lyext_load_plugin(void *dlhandler, const char *file_name)
209 {
210 struct lyext_plugin_list *plugin;
211 char *str;
212 int *version;
213
214 #ifdef STATIC
215 return 0;
216 #endif /* STATIC */
217
218 /* get the plugin data */
219 plugin = dlsym(dlhandler, file_name);
220 str = dlerror();
221 if (str) {
222 LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed, missing plugin list object (%s).", file_name, str);
223 return 1;
224 }
225 version = dlsym(dlhandler, "lyext_api_version");
226 if (dlerror() || *version != LYEXT_API_VERSION) {
227 LOGWRN(NULL, "Processing \"%s\" extension plugin failed, wrong API version - %d expected, %d found.",
228 file_name, LYEXT_API_VERSION, version ? *version : 0);
229 return 1;
230 }
231 return ly_register_exts(plugin, file_name);
232 }
233
234 API int
ly_register_exts(struct lyext_plugin_list * plugin,const char * log_name)235 ly_register_exts(struct lyext_plugin_list *plugin, const char *log_name)
236 {
237 FUN_IN;
238
239 struct lyext_plugin_list *p;
240 struct lyext_plugin_complex *pluginc;
241 uint32_t u, v;
242
243 for (u = 0; plugin[u].name; u++) {
244 /* check extension implementations for collisions */
245 for (v = 0; v < ext_plugins_count; v++) {
246 if (!strcmp(plugin[u].name, ext_plugins[v].name) &&
247 !strcmp(plugin[u].module, ext_plugins[v].module) &&
248 (!plugin[u].revision || !ext_plugins[v].revision || !strcmp(plugin[u].revision, ext_plugins[v].revision))) {
249 LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed,"
250 "implementation collision for extension %s from module %s%s%s.",
251 log_name, plugin[u].name, plugin[u].module, plugin[u].revision ? "@" : "",
252 plugin[u].revision ? plugin[u].revision : "");
253 return 1;
254 }
255 }
256
257 /* check for valid supported substatements in case of complex extension */
258 if (plugin[u].plugin->type == LYEXT_COMPLEX && ((struct lyext_plugin_complex *)plugin[u].plugin)->substmt) {
259 pluginc = (struct lyext_plugin_complex *)plugin[u].plugin;
260 for (v = 0; pluginc->substmt[v].stmt; v++) {
261 if (pluginc->substmt[v].stmt >= LY_STMT_SUBMODULE ||
262 pluginc->substmt[v].stmt == LY_STMT_VERSION ||
263 pluginc->substmt[v].stmt == LY_STMT_YINELEM) {
264 LOGERR(NULL, LY_EINVAL,
265 "Extension plugin \"%s\" (extension %s) allows not supported extension substatement (%s)",
266 log_name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
267 return 1;
268 }
269 if (pluginc->substmt[v].cardinality > LY_STMT_CARD_MAND &&
270 pluginc->substmt[v].stmt >= LY_STMT_MODIFIER &&
271 pluginc->substmt[v].stmt <= LY_STMT_STATUS) {
272 LOGERR(NULL, LY_EINVAL, "Extension plugin \"%s\" (extension %s) allows multiple instances on \"%s\" "
273 "substatement, which is not supported.",
274 log_name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
275 return 1;
276 }
277 }
278 }
279 }
280
281 /* add the new plugins, we have number of new plugins as u */
282 p = realloc(ext_plugins, (ext_plugins_count + u) * sizeof *ext_plugins);
283 if (!p) {
284 LOGMEM(NULL);
285 return -1;
286 }
287 ext_plugins = p;
288 for (; u; u--) {
289 memcpy(&ext_plugins[ext_plugins_count], &plugin[u - 1], sizeof *plugin);
290 ext_plugins_count++;
291 }
292
293 return 0;
294 }
295
296 /* spends name */
297 static void
ly_add_loaded_plugin(char * name)298 ly_add_loaded_plugin(char *name)
299 {
300 loaded_plugins = ly_realloc(loaded_plugins, (loaded_plugins_count + 2) * sizeof *loaded_plugins);
301 LY_CHECK_ERR_RETURN(!loaded_plugins, free(name); LOGMEM(NULL), );
302 ++loaded_plugins_count;
303
304 loaded_plugins[loaded_plugins_count - 1] = name;
305 loaded_plugins[loaded_plugins_count] = NULL;
306 }
307
308 static void
ly_load_plugins_dir(DIR * dir,const char * dir_path,int ext_or_type)309 ly_load_plugins_dir(DIR *dir, const char *dir_path, int ext_or_type)
310 {
311 struct dirent *file;
312 size_t len;
313 char *str, *name;
314 void *dlhandler;
315 int ret;
316
317 #ifdef STATIC
318 return;
319 #endif /* STATIC */
320
321 while ((file = readdir(dir))) {
322 /* required format of the filename is *LY_PLUGIN_SUFFIX */
323 len = strlen(file->d_name);
324 if (len < LY_PLUGIN_SUFFIX_LEN + 1 ||
325 strcmp(&file->d_name[len - LY_PLUGIN_SUFFIX_LEN], LY_PLUGIN_SUFFIX)) {
326 continue;
327 }
328
329 /* and construct the filepath */
330 if (asprintf(&str, "%s/%s", dir_path, file->d_name) == -1) {
331 LOGMEM(NULL);
332 return;
333 }
334
335 /* load the plugin */
336 dlhandler = dlopen(str, RTLD_NOW);
337 if (!dlhandler) {
338 LOGERR(NULL, LY_ESYS, "Loading \"%s\" as a plugin failed (%s).", str, dlerror());
339 free(str);
340 continue;
341 }
342 if (ly_set_contains(&dlhandlers, dlhandler) != -1) {
343 /* the plugin is already loaded */
344 LOGVRB("Plugin \"%s\" already loaded.", str);
345 free(str);
346
347 /* keep the refcount of the shared object correct */
348 dlclose(dlhandler);
349 continue;
350 }
351 dlerror(); /* Clear any existing error */
352
353 /* store the name without the suffix */
354 name = strndup(file->d_name, len - LY_PLUGIN_SUFFIX_LEN);
355 if (!name) {
356 LOGMEM(NULL);
357 dlclose(dlhandler);
358 free(str);
359 return;
360 }
361
362 if (ext_or_type) {
363 ret = lyext_load_plugin(dlhandler, name);
364 } else {
365 ret = lytype_load_plugin(dlhandler, name);
366 }
367 if (!ret) {
368 LOGVRB("Plugin \"%s\" successfully loaded.", str);
369 /* spends name */
370 ly_add_loaded_plugin(name);
371 /* keep the handler */
372 ly_set_add(&dlhandlers, dlhandler, LY_SET_OPT_USEASLIST);
373 } else {
374 free(name);
375 dlclose(dlhandler);
376 }
377 free(str);
378
379 if (ret == -1) {
380 /* finish on error */
381 break;
382 }
383 }
384 }
385
386 API void
ly_load_plugins(void)387 ly_load_plugins(void)
388 {
389 FUN_IN;
390
391 DIR* dir;
392 const char *pluginsdir;
393
394 #ifdef STATIC
395 /* lock the extension plugins list */
396 pthread_mutex_lock(&plugins_lock);
397
398 ext_plugins = static_load_lyext_plugins(&ext_plugins_count);
399 type_plugins = static_load_lytype_plugins(&type_plugins_count);
400
401 int u;
402 for (u = 0; u < static_loaded_plugins_count; u++) {
403 ly_add_loaded_plugin(strdup(static_loaded_plugins[u]));
404 }
405
406 /* unlock the global structures */
407 pthread_mutex_unlock(&plugins_lock);
408 return;
409 #endif /* STATIC */
410
411 /* lock the extension plugins list */
412 pthread_mutex_lock(&plugins_lock);
413
414 /* increase references */
415 ++plugin_refs;
416
417 /* try to get the plugins directory from environment variable */
418 pluginsdir = getenv("LIBYANG_EXTENSIONS_PLUGINS_DIR");
419 if (!pluginsdir) {
420 pluginsdir = LYEXT_PLUGINS_DIR;
421 }
422
423 dir = opendir(pluginsdir);
424 if (!dir) {
425 /* no directory (or no access to it), no extension plugins */
426 LOGWRN(NULL, "Failed to open libyang extensions plugins directory \"%s\" (%s).", pluginsdir, strerror(errno));
427 } else {
428 ly_load_plugins_dir(dir, pluginsdir, 1);
429 closedir(dir);
430 }
431
432 /* try to get the plugins directory from environment variable */
433 pluginsdir = getenv("LIBYANG_USER_TYPES_PLUGINS_DIR");
434 if (!pluginsdir) {
435 pluginsdir = LY_USER_TYPES_PLUGINS_DIR;
436 }
437
438 dir = opendir(pluginsdir);
439 if (!dir) {
440 /* no directory (or no access to it), no extension plugins */
441 LOGWRN(NULL, "Failed to open libyang user types plugins directory \"%s\" (%s).", pluginsdir, strerror(errno));
442 } else {
443 ly_load_plugins_dir(dir, pluginsdir, 0);
444 closedir(dir);
445 }
446
447 /* unlock the global structures */
448 pthread_mutex_unlock(&plugins_lock);
449 }
450
451 struct lyext_plugin *
ext_get_plugin(const char * name,const char * module,const char * revision)452 ext_get_plugin(const char *name, const char *module, const char *revision)
453 {
454 uint16_t u;
455
456 assert(name);
457 assert(module);
458
459 for (u = 0; u < ext_plugins_count; u++) {
460 if (!strcmp(name, ext_plugins[u].name) && !strcmp(module, ext_plugins[u].module) &&
461 ((!revision && !ext_plugins[u].revision) || (revision && !strcmp(revision, ext_plugins[u].revision)))) {
462 /* we have the match */
463 return ext_plugins[u].plugin;
464 }
465 }
466
467 /* plugin not found */
468 return NULL;
469 }
470
471 API int
lys_ext_instance_presence(struct lys_ext * def,struct lys_ext_instance ** ext,uint8_t ext_size)472 lys_ext_instance_presence(struct lys_ext *def, struct lys_ext_instance **ext, uint8_t ext_size)
473 {
474 FUN_IN;
475
476 uint8_t index;
477
478 if (!def || (ext_size && !ext)) {
479 LOGARG;
480 return -1;
481 }
482
483 /* search for the extension instance */
484 for (index = 0; index < ext_size; index++) {
485 if (ext[index]->module->ctx == def->module->ctx) {
486 /* from the same context */
487 if (ext[index]->def == def) {
488 return index;
489 }
490 } else {
491 /* from different contexts */
492 if (ly_strequal0(ext[index]->def->name, def->name)
493 && ly_strequal0(lys_main_module(ext[index]->def->module)->name, lys_main_module(def->module)->name)) {
494 return index;
495 }
496 }
497 }
498
499 /* not found */
500 return -1;
501 }
502
503 API void *
lys_ext_complex_get_substmt(LY_STMT stmt,struct lys_ext_instance_complex * ext,struct lyext_substmt ** info)504 lys_ext_complex_get_substmt(LY_STMT stmt, struct lys_ext_instance_complex *ext, struct lyext_substmt **info)
505 {
506 FUN_IN;
507
508 int i;
509
510 if (!ext || !ext->def || !ext->def->plugin || ext->def->plugin->type != LYEXT_COMPLEX) {
511 LOGARG;
512 return NULL;
513 }
514
515 if (!ext->substmt) {
516 /* no substatement defined in the plugin */
517 if (info) {
518 *info = NULL;
519 }
520 return NULL;
521 }
522
523 /* search the substatements defined by the plugin */
524 for (i = 0; ext->substmt[i].stmt; i++) {
525 if (stmt == LY_STMT_NODE) {
526 if (ext->substmt[i].stmt >= LY_STMT_ACTION && ext->substmt[i].stmt <= LY_STMT_USES) {
527 if (info) {
528 *info = &ext->substmt[i];
529 }
530 break;
531 }
532 } else if (ext->substmt[i].stmt == stmt) {
533 if (info) {
534 *info = &ext->substmt[i];
535 }
536 break;
537 }
538 }
539
540 if (ext->substmt[i].stmt) {
541 return &ext->content[ext->substmt[i].offset];
542 } else {
543 return NULL;
544 }
545 }
546
547 LY_STMT
lys_snode2stmt(LYS_NODE nodetype)548 lys_snode2stmt(LYS_NODE nodetype)
549 {
550 switch(nodetype) {
551 case LYS_CONTAINER:
552 return LY_STMT_CONTAINER;
553 case LYS_CHOICE:
554 return LY_STMT_CHOICE;
555 case LYS_LEAF:
556 return LY_STMT_LEAF;
557 case LYS_LEAFLIST:
558 return LY_STMT_LEAFLIST;
559 case LYS_LIST:
560 return LY_STMT_LIST;
561 case LYS_ANYXML:
562 case LYS_ANYDATA:
563 return LY_STMT_ANYDATA;
564 case LYS_CASE:
565 return LY_STMT_CASE;
566 case LYS_NOTIF:
567 return LY_STMT_NOTIFICATION;
568 case LYS_RPC:
569 return LY_STMT_RPC;
570 case LYS_INPUT:
571 return LY_STMT_INPUT;
572 case LYS_OUTPUT:
573 return LY_STMT_OUTPUT;
574 case LYS_GROUPING:
575 return LY_STMT_GROUPING;
576 case LYS_USES:
577 return LY_STMT_USES;
578 case LYS_AUGMENT:
579 return LY_STMT_AUGMENT;
580 case LYS_ACTION:
581 return LY_STMT_ACTION;
582 default:
583 return LY_STMT_NODE;
584 }
585 }
586
587 static struct lytype_plugin_list *
lytype_find(const char * module,const char * revision,const char * type_name)588 lytype_find(const char *module, const char *revision, const char *type_name)
589 {
590 uint16_t u;
591
592 for (u = 0; u < type_plugins_count; ++u) {
593 if (ly_strequal(module, type_plugins[u].module, 0) && ((!revision && !type_plugins[u].revision)
594 || (revision && ly_strequal(revision, type_plugins[u].revision, 0)))
595 && ly_strequal(type_name, type_plugins[u].name, 0)) {
596 return &(type_plugins[u]);
597 }
598 }
599
600 return NULL;
601 }
602
603 int
lytype_store(const struct lys_module * mod,const char * type_name,const char ** value_str,lyd_val * value)604 lytype_store(const struct lys_module *mod, const char *type_name, const char **value_str, lyd_val *value)
605 {
606 struct lytype_plugin_list *p;
607 char *err_msg = NULL;
608
609 assert(mod && type_name && value_str && value);
610
611 p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type_name);
612 if (p) {
613 if (p->store_clb(mod->ctx, type_name, value_str, value, &err_msg)) {
614 if (!err_msg) {
615 if (asprintf(&err_msg, "Failed to store value \"%s\" of user type \"%s\".", *value_str, type_name) == -1) {
616 LOGMEM(mod->ctx);
617 return -1;
618 }
619 }
620 LOGERR(mod->ctx, LY_EPLUGIN, err_msg);
621 free(err_msg);
622 return -1;
623 }
624
625 /* value successfully stored */
626 return 0;
627 }
628
629 return 1;
630 }
631
632 void
lytype_free(const struct lys_type * type,lyd_val value,const char * value_str)633 lytype_free(const struct lys_type *type, lyd_val value, const char *value_str)
634 {
635 struct lytype_plugin_list *p;
636 struct lys_node_leaf sleaf;
637 struct lyd_node_leaf_list leaf;
638 struct lys_module *mod;
639
640 memset(&sleaf, 0, sizeof sleaf);
641 memset(&leaf, 0, sizeof leaf);
642
643 while (type->base == LY_TYPE_LEAFREF) {
644 type = &type->info.lref.target->type;
645 }
646 if (type->base == LY_TYPE_UNION) {
647 /* create a fake schema node */
648 sleaf.module = type->parent->module;
649 sleaf.name = "fake-leaf";
650 sleaf.type = *type;
651 sleaf.nodetype = LYS_LEAF;
652
653 /* and a fake data node */
654 leaf.schema = (struct lys_node *)&sleaf;
655 leaf.value = value;
656 leaf.value_str = value_str;
657
658 /* find the original type */
659 type = lyd_leaf_type(&leaf);
660 if (!type) {
661 LOGINT(sleaf.module->ctx);
662 return;
663 }
664 }
665
666 mod = type->der->module;
667 if (!mod) {
668 LOGINT(type->parent->module->ctx);
669 return;
670 }
671
672 p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type->der->name);
673 if (!p) {
674 LOGINT(mod->ctx);
675 return;
676 }
677
678 if (p->free_clb) {
679 p->free_clb(value.ptr);
680 }
681 }
682