blob: 4045fd59ab200a267afe7b64d397eb050809369e [file] [log] [blame]
Michal Vaskoc6cd3f02018-03-02 14:07:42 +01001/**
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>
Michal Vaskoc6cd3f02018-03-02 14:07:42 +010026
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 */
35static struct lyext_plugin_list *ext_plugins = NULL;
36static uint16_t ext_plugins_count = 0; /* size of the ext_plugins array */
37
38static struct lytype_plugin_list *type_plugins = NULL;
39static uint16_t type_plugins_count = 0;
40
41static struct ly_set dlhandlers = {0, 0, {NULL}};
42static pthread_mutex_t plugins_lock = PTHREAD_MUTEX_INITIALIZER;
43
Michal Vasko3b887ec2018-05-31 09:31:46 +020044static char **loaded_plugins = NULL; /* both ext and type plugin names */
45static uint16_t loaded_plugins_count = 0;
46
Michal Vaskoc6cd3f02018-03-02 14:07:42 +010047/**
48 * @brief reference counter for the plugins, it actually counts number of contexts
49 */
50static uint32_t plugin_refs;
51
Michal Vasko3b887ec2018-05-31 09:31:46 +020052API const char * const *
53ly_get_loaded_plugins(void)
54{
55 return (const char * const *)loaded_plugins;
56}
57
Michal Vaskoc6cd3f02018-03-02 14:07:42 +010058API int
59ly_clean_plugins(void)
60{
61 unsigned int u;
62 int ret = EXIT_SUCCESS;
63
Mislav Novakovic61c79082018-05-21 13:08:24 +020064#ifdef STATIC
65 /* lock the extension plugins list */
66 pthread_mutex_lock(&plugins_lock);
67
68 if(ext_plugins) {
69 free(ext_plugins);
70 ext_plugins = NULL;
71 ext_plugins_count = 0;
72 }
73
74 if(type_plugins) {
75 free(type_plugins);
76 type_plugins = NULL;
77 type_plugins_count = 0;
78 }
79
Mislav Novakovic74726932018-06-04 13:14:03 +020080 for (u = 0; u < loaded_plugins_count; ++u) {
81 free(loaded_plugins[u]);
82 }
83 free(loaded_plugins);
84 loaded_plugins = NULL;
85 loaded_plugins_count = 0;
86
Mislav Novakovic61c79082018-05-21 13:08:24 +020087 /* unlock the global structures */
88 pthread_mutex_unlock(&plugins_lock);
89 return ret;
90#endif /* STATIC */
91
Michal Vaskoc6cd3f02018-03-02 14:07:42 +010092 /* lock the extension plugins list */
93 pthread_mutex_lock(&plugins_lock);
94
95 if (--plugin_refs) {
96 /* there is a context that may refer to the plugins, so we cannot remove them */
97 ret = EXIT_FAILURE;
98 goto cleanup;
99 }
100
101 if (!ext_plugins_count && !type_plugins_count) {
102 /* no plugin loaded - nothing to do */
103 goto cleanup;
104 }
105
106 /* clean the lists */
107 free(ext_plugins);
108 ext_plugins = NULL;
109 ext_plugins_count = 0;
110
111 free(type_plugins);
112 type_plugins = NULL;
113 type_plugins_count = 0;
114
Michal Vasko3b887ec2018-05-31 09:31:46 +0200115 for (u = 0; u < loaded_plugins_count; ++u) {
116 free(loaded_plugins[u]);
117 }
118 free(loaded_plugins);
119 loaded_plugins = NULL;
120 loaded_plugins_count = 0;
121
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100122 /* close the dl handlers */
123 for (u = 0; u < dlhandlers.number; u++) {
124 dlclose(dlhandlers.set.g[u]);
125 }
126 free(dlhandlers.set.g);
127 dlhandlers.set.g = NULL;
128 dlhandlers.size = 0;
129 dlhandlers.number = 0;
130
131cleanup:
132 /* unlock the global structures */
133 pthread_mutex_unlock(&plugins_lock);
134
135 return ret;
136}
137
138static int
139lytype_load_plugin(void *dlhandler, const char *file_name)
140{
141 struct lytype_plugin_list *plugin, *p;
142 uint32_t u, v;
143 char *str;
144
Mislav Novakovic61c79082018-05-21 13:08:24 +0200145#ifdef STATIC
146 return 0;
147#endif /* STATIC */
148
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100149 /* get the plugin data */
150 plugin = dlsym(dlhandler, file_name);
151 str = dlerror();
152 if (str) {
153 LOGERR(NULL, LY_ESYS, "Processing \"%s\" user type plugin failed, missing plugin list object (%s).", file_name, str);
154 return 1;
155 }
156
157 for (u = 0; plugin[u].name; u++) {
158 /* check user type implementations for collisions */
159 for (v = 0; v < type_plugins_count; v++) {
160 if (!strcmp(plugin[u].name, type_plugins[v].name) &&
161 !strcmp(plugin[u].module, type_plugins[v].module) &&
162 (!plugin[u].revision || !type_plugins[v].revision || !strcmp(plugin[u].revision, type_plugins[v].revision))) {
163 LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed,"
164 "implementation collision for extension %s from module %s%s%s.",
165 file_name, plugin[u].name, plugin[u].module, plugin[u].revision ? "@" : "",
166 plugin[u].revision ? plugin[u].revision : "");
167 return 1;
168 }
169 }
170 }
171
172 /* add the new plugins, we have number of new plugins as u */
173 p = realloc(type_plugins, (type_plugins_count + u) * sizeof *type_plugins);
174 if (!p) {
175 LOGMEM(NULL);
176 return -1;
177 }
178 type_plugins = p;
179 for (; u; u--) {
180 memcpy(&type_plugins[type_plugins_count], &plugin[u - 1], sizeof *plugin);
181 type_plugins_count++;
182 }
183
184 return 0;
185}
186
187static int
188lyext_load_plugin(void *dlhandler, const char *file_name)
189{
190 struct lyext_plugin_list *plugin, *p;
191 struct lyext_plugin_complex *pluginc;
192 uint32_t u, v;
193 char *str;
194
Mislav Novakovic7073ef52018-05-24 17:23:02 +0200195#ifdef STATIC
196 return 0;
197#endif /* STATIC */
198
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100199 /* get the plugin data */
200 plugin = dlsym(dlhandler, file_name);
201 str = dlerror();
202 if (str) {
203 LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed, missing plugin list object (%s).", file_name, str);
204 return 1;
205 }
206
207 for (u = 0; plugin[u].name; u++) {
208 /* check extension implementations for collisions */
209 for (v = 0; v < ext_plugins_count; v++) {
210 if (!strcmp(plugin[u].name, ext_plugins[v].name) &&
211 !strcmp(plugin[u].module, ext_plugins[v].module) &&
212 (!plugin[u].revision || !ext_plugins[v].revision || !strcmp(plugin[u].revision, ext_plugins[v].revision))) {
213 LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed,"
214 "implementation collision for extension %s from module %s%s%s.",
215 file_name, plugin[u].name, plugin[u].module, plugin[u].revision ? "@" : "",
216 plugin[u].revision ? plugin[u].revision : "");
217 return 1;
218 }
219 }
220
221 /* check for valid supported substatements in case of complex extension */
222 if (plugin[u].plugin->type == LYEXT_COMPLEX && ((struct lyext_plugin_complex *)plugin[u].plugin)->substmt) {
223 pluginc = (struct lyext_plugin_complex *)plugin[u].plugin;
224 for (v = 0; pluginc->substmt[v].stmt; v++) {
225 if (pluginc->substmt[v].stmt >= LY_STMT_SUBMODULE ||
226 pluginc->substmt[v].stmt == LY_STMT_VERSION ||
227 pluginc->substmt[v].stmt == LY_STMT_YINELEM) {
228 LOGERR(NULL, LY_EINVAL,
229 "Extension plugin \"%s\" (extension %s) allows not supported extension substatement (%s)",
230 file_name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
231 return 1;
232 }
233 if (pluginc->substmt[v].cardinality > LY_STMT_CARD_MAND &&
234 pluginc->substmt[v].stmt >= LY_STMT_MODIFIER &&
235 pluginc->substmt[v].stmt <= LY_STMT_STATUS) {
236 LOGERR(NULL, LY_EINVAL, "Extension plugin \"%s\" (extension %s) allows multiple instances on \"%s\" "
237 "substatement, which is not supported.",
238 file_name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
239 return 1;
240 }
241 }
242 }
243 }
244
245 /* add the new plugins, we have number of new plugins as u */
246 p = realloc(ext_plugins, (ext_plugins_count + u) * sizeof *ext_plugins);
247 if (!p) {
248 LOGMEM(NULL);
249 return -1;
250 }
251 ext_plugins = p;
252 for (; u; u--) {
253 memcpy(&ext_plugins[ext_plugins_count], &plugin[u - 1], sizeof *plugin);
254 ext_plugins_count++;
255 }
256
257 return 0;
258}
259
Michal Vasko3b887ec2018-05-31 09:31:46 +0200260/* spends name */
261static void
262ly_add_loaded_plugin(char *name)
263{
264 loaded_plugins = ly_realloc(loaded_plugins, (loaded_plugins_count + 2) * sizeof *loaded_plugins);
265 LY_CHECK_ERR_RETURN(!loaded_plugins, free(name); LOGMEM(NULL), );
266 ++loaded_plugins_count;
267
268 loaded_plugins[loaded_plugins_count - 1] = name;
269 loaded_plugins[loaded_plugins_count] = NULL;
270}
271
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100272static void
273ly_load_plugins_dir(DIR *dir, const char *dir_path, int ext_or_type)
274{
275 struct dirent *file;
276 size_t len;
Michal Vasko3b887ec2018-05-31 09:31:46 +0200277 char *str, *name;
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100278 void *dlhandler;
279 int ret;
280
Mislav Novakovic61c79082018-05-21 13:08:24 +0200281#ifdef STATIC
282 return;
283#endif /* STATIC */
284
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100285 while ((file = readdir(dir))) {
286 /* required format of the filename is *LY_PLUGIN_SUFFIX */
287 len = strlen(file->d_name);
288 if (len < LY_PLUGIN_SUFFIX_LEN + 1 ||
289 strcmp(&file->d_name[len - LY_PLUGIN_SUFFIX_LEN], LY_PLUGIN_SUFFIX)) {
290 continue;
291 }
292
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100293 /* and construct the filepath */
Michal Vasko87e29a82018-05-21 13:50:43 +0200294 if (asprintf(&str, "%s/%s", dir_path, file->d_name) == -1) {
295 LOGMEM(NULL);
296 return;
297 }
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100298
Radek Krejcib1473bb2018-04-18 09:23:17 +0200299 /* load the plugin */
300 dlhandler = dlopen(str, RTLD_NOW);
301 if (!dlhandler) {
302 LOGERR(NULL, LY_ESYS, "Loading \"%s\" as a plugin failed (%s).", str, dlerror());
303 free(str);
304 continue;
305 }
306 if (ly_set_contains(&dlhandlers, dlhandler) != -1) {
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100307 /* the plugin is already loaded */
308 LOGVRB("Plugin \"%s\" already loaded.", str);
309 free(str);
310
311 /* keep the refcount of the shared object correct */
312 dlclose(dlhandler);
313 continue;
314 }
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100315 dlerror(); /* Clear any existing error */
316
Michal Vasko3b887ec2018-05-31 09:31:46 +0200317 /* store the name without the suffix */
318 name = strndup(file->d_name, len - LY_PLUGIN_SUFFIX_LEN);
319 if (!name) {
320 LOGMEM(NULL);
321 free(str);
322 return;
323 }
324
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100325 if (ext_or_type) {
326 ret = lyext_load_plugin(dlhandler, name);
327 } else {
328 ret = lytype_load_plugin(dlhandler, name);
329 }
Michal Vasko3b887ec2018-05-31 09:31:46 +0200330 if (!ret) {
331 LOGVRB("Plugin \"%s\" successfully loaded.", str);
332 /* spends name */
333 ly_add_loaded_plugin(name);
334 /* keep the handler */
335 ly_set_add(&dlhandlers, dlhandler, LY_SET_OPT_USEASLIST);
336 } else {
337 free(name);
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100338 dlclose(dlhandler);
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100339 }
Michal Vaskoe65f39a2018-04-25 11:29:10 +0200340 free(str);
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100341
Michal Vasko3b887ec2018-05-31 09:31:46 +0200342 if (ret == -1) {
343 /* finish on error */
344 break;
345 }
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100346 }
347}
348
349API void
350ly_load_plugins(void)
351{
352 DIR* dir;
353 const char *pluginsdir;
354
Mislav Novakovic61c79082018-05-21 13:08:24 +0200355#ifdef STATIC
356 /* lock the extension plugins list */
357 pthread_mutex_lock(&plugins_lock);
358
359 ext_plugins = static_load_lyext_plugins(&ext_plugins_count);
360 type_plugins = static_load_lytype_plugins(&type_plugins_count);
361
Mislav Novakovic74726932018-06-04 13:14:03 +0200362 int u;
363 for (u = 0; u < static_loaded_plugins_count; u++) {
364 ly_add_loaded_plugin(strdup(static_loaded_plugins[u]));
365 }
366
Mislav Novakovic61c79082018-05-21 13:08:24 +0200367 /* unlock the global structures */
368 pthread_mutex_unlock(&plugins_lock);
369 return;
370#endif /* STATIC */
371
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100372 /* lock the extension plugins list */
373 pthread_mutex_lock(&plugins_lock);
374
375 /* increase references */
376 ++plugin_refs;
377
378 /* try to get the plugins directory from environment variable */
379 pluginsdir = getenv("LIBYANG_EXTENSIONS_PLUGINS_DIR");
380 if (!pluginsdir) {
381 pluginsdir = LYEXT_PLUGINS_DIR;
382 }
383
384 dir = opendir(pluginsdir);
385 if (!dir) {
386 /* no directory (or no access to it), no extension plugins */
387 LOGWRN(NULL, "Failed to open libyang extensions plugins directory \"%s\" (%s).", pluginsdir, strerror(errno));
388 } else {
389 ly_load_plugins_dir(dir, pluginsdir, 1);
390 closedir(dir);
391 }
392
393 /* try to get the plugins directory from environment variable */
394 pluginsdir = getenv("LIBYANG_USER_TYPES_PLUGINS_DIR");
395 if (!pluginsdir) {
396 pluginsdir = LY_USER_TYPES_PLUGINS_DIR;
397 }
398
399 dir = opendir(pluginsdir);
400 if (!dir) {
401 /* no directory (or no access to it), no extension plugins */
402 LOGWRN(NULL, "Failed to open libyang user types plugins directory \"%s\" (%s).", pluginsdir, strerror(errno));
403 } else {
404 ly_load_plugins_dir(dir, pluginsdir, 0);
405 closedir(dir);
406 }
407
408 /* unlock the global structures */
409 pthread_mutex_unlock(&plugins_lock);
410}
411
412struct lyext_plugin *
413ext_get_plugin(const char *name, const char *module, const char *revision)
414{
415 uint16_t u;
416
417 assert(name);
418 assert(module);
419
420 for (u = 0; u < ext_plugins_count; u++) {
421 if (!strcmp(name, ext_plugins[u].name) &&
422 !strcmp(module, ext_plugins[u].module) &&
423 (!ext_plugins[u].revision || !strcmp(revision, ext_plugins[u].revision))) {
424 /* we have the match */
425 return ext_plugins[u].plugin;
426 }
427 }
428
429 /* plugin not found */
430 return NULL;
431}
432
433API int
434lys_ext_instance_presence(struct lys_ext *def, struct lys_ext_instance **ext, uint8_t ext_size)
435{
436 uint8_t index;
437
438 if (!def || (ext_size && !ext)) {
439 LOGARG;
440 return -1;
441 }
442
443 /* search for the extension instance */
444 for (index = 0; index < ext_size; index++) {
Michal Vaskoe5805292018-07-11 08:56:14 +0200445 if (ext[index]->module->ctx == def->module->ctx) {
446 /* from the same context */
447 if (ext[index]->def == def) {
448 return index;
449 }
450 } else {
451 /* from different contexts */
452 if (ly_strequal0(ext[index]->def->name, def->name)
453 && ly_strequal0(lys_main_module(ext[index]->def->module)->name, lys_main_module(def->module)->name)) {
454 return index;
455 }
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100456 }
457 }
458
459 /* not found */
460 return -1;
461}
462
463API void *
464lys_ext_complex_get_substmt(LY_STMT stmt, struct lys_ext_instance_complex *ext, struct lyext_substmt **info)
465{
466 int i;
467
468 if (!ext || !ext->def || !ext->def->plugin || ext->def->plugin->type != LYEXT_COMPLEX) {
469 LOGARG;
470 return NULL;
471 }
472
473 if (!ext->substmt) {
474 /* no substatement defined in the plugin */
475 if (info) {
476 *info = NULL;
477 }
478 return NULL;
479 }
480
481 /* search the substatements defined by the plugin */
482 for (i = 0; ext->substmt[i].stmt; i++) {
483 if (stmt == LY_STMT_NODE) {
484 if (ext->substmt[i].stmt >= LY_STMT_ACTION && ext->substmt[i].stmt <= LY_STMT_USES) {
485 if (info) {
486 *info = &ext->substmt[i];
487 }
488 break;
489 }
490 } else if (ext->substmt[i].stmt == stmt) {
491 if (info) {
492 *info = &ext->substmt[i];
493 }
494 break;
495 }
496 }
497
498 if (ext->substmt[i].stmt) {
499 return &ext->content[ext->substmt[i].offset];
500 } else {
501 return NULL;
502 }
503}
504
505LY_STMT
506lys_snode2stmt(LYS_NODE nodetype)
507{
508 switch(nodetype) {
509 case LYS_CONTAINER:
510 return LY_STMT_CONTAINER;
511 case LYS_CHOICE:
512 return LY_STMT_CHOICE;
513 case LYS_LEAF:
514 return LY_STMT_LEAF;
515 case LYS_LEAFLIST:
516 return LY_STMT_LEAFLIST;
517 case LYS_LIST:
518 return LY_STMT_LIST;
519 case LYS_ANYXML:
520 case LYS_ANYDATA:
521 return LY_STMT_ANYDATA;
522 case LYS_CASE:
523 return LY_STMT_CASE;
524 case LYS_NOTIF:
525 return LY_STMT_NOTIFICATION;
526 case LYS_RPC:
527 return LY_STMT_RPC;
528 case LYS_INPUT:
529 return LY_STMT_INPUT;
530 case LYS_OUTPUT:
531 return LY_STMT_OUTPUT;
532 case LYS_GROUPING:
533 return LY_STMT_GROUPING;
534 case LYS_USES:
535 return LY_STMT_USES;
536 case LYS_AUGMENT:
537 return LY_STMT_AUGMENT;
538 case LYS_ACTION:
539 return LY_STMT_ACTION;
540 default:
541 return LY_STMT_NODE;
542 }
543}
544
545static struct lytype_plugin_list *
546lytype_find(const char *module, const char *revision, const char *type_name)
547{
548 uint16_t u;
549
550 for (u = 0; u < type_plugins_count; ++u) {
551 if (ly_strequal(module, type_plugins[u].module, 0) && ((!revision && !type_plugins[u].revision)
552 || (revision && ly_strequal(revision, type_plugins[u].revision, 0)))
553 && ly_strequal(type_name, type_plugins[u].name, 0)) {
554 return &(type_plugins[u]);
555 }
556 }
557
558 return NULL;
559}
560
561int
562lytype_store(const struct lys_module *mod, const char *type_name, const char *value_str, lyd_val *value)
563{
564 struct lytype_plugin_list *p;
565 char *err_msg = NULL;
566
567 assert(mod && type_name && value_str && value);
568
569 p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type_name);
570 if (p) {
571 if (p->store_clb(type_name, value_str, value, &err_msg)) {
572 if (!err_msg) {
573 if (asprintf(&err_msg, "Failed to store value \"%s\" of user type \"%s\".", value_str, type_name) == -1) {
574 LOGMEM(mod->ctx);
575 return -1;
576 }
577 }
578 LOGERR(mod->ctx, LY_EPLUGIN, err_msg);
579 free(err_msg);
580 return -1;
581 }
582
583 /* value successfully stored */
584 return 0;
585 }
586
587 return 1;
588}
589
590void
591lytype_free(const struct lys_module *mod, const char *type_name, lyd_val value)
592{
593 struct lytype_plugin_list *p;
594
595 p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type_name);
596 if (!p) {
597 LOGINT(mod->ctx);
598 return;
599 }
600
601 if (p->free_clb) {
602 p->free_clb(value.ptr);
603 }
604}