blob: 241a209679872929effd3e83bf9c6e945a833b66 [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
80 /* unlock the global structures */
81 pthread_mutex_unlock(&plugins_lock);
82 return ret;
83#endif /* STATIC */
84
Michal Vaskoc6cd3f02018-03-02 14:07:42 +010085 /* lock the extension plugins list */
86 pthread_mutex_lock(&plugins_lock);
87
88 if (--plugin_refs) {
89 /* there is a context that may refer to the plugins, so we cannot remove them */
90 ret = EXIT_FAILURE;
91 goto cleanup;
92 }
93
94 if (!ext_plugins_count && !type_plugins_count) {
95 /* no plugin loaded - nothing to do */
96 goto cleanup;
97 }
98
99 /* clean the lists */
100 free(ext_plugins);
101 ext_plugins = NULL;
102 ext_plugins_count = 0;
103
104 free(type_plugins);
105 type_plugins = NULL;
106 type_plugins_count = 0;
107
Michal Vasko3b887ec2018-05-31 09:31:46 +0200108 for (u = 0; u < loaded_plugins_count; ++u) {
109 free(loaded_plugins[u]);
110 }
111 free(loaded_plugins);
112 loaded_plugins = NULL;
113 loaded_plugins_count = 0;
114
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100115 /* close the dl handlers */
116 for (u = 0; u < dlhandlers.number; u++) {
117 dlclose(dlhandlers.set.g[u]);
118 }
119 free(dlhandlers.set.g);
120 dlhandlers.set.g = NULL;
121 dlhandlers.size = 0;
122 dlhandlers.number = 0;
123
124cleanup:
125 /* unlock the global structures */
126 pthread_mutex_unlock(&plugins_lock);
127
128 return ret;
129}
130
131static int
132lytype_load_plugin(void *dlhandler, const char *file_name)
133{
134 struct lytype_plugin_list *plugin, *p;
135 uint32_t u, v;
136 char *str;
137
Mislav Novakovic61c79082018-05-21 13:08:24 +0200138#ifdef STATIC
139 return 0;
140#endif /* STATIC */
141
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100142 /* get the plugin data */
143 plugin = dlsym(dlhandler, file_name);
144 str = dlerror();
145 if (str) {
146 LOGERR(NULL, LY_ESYS, "Processing \"%s\" user type plugin failed, missing plugin list object (%s).", file_name, str);
147 return 1;
148 }
149
150 for (u = 0; plugin[u].name; u++) {
151 /* check user type implementations for collisions */
152 for (v = 0; v < type_plugins_count; v++) {
153 if (!strcmp(plugin[u].name, type_plugins[v].name) &&
154 !strcmp(plugin[u].module, type_plugins[v].module) &&
155 (!plugin[u].revision || !type_plugins[v].revision || !strcmp(plugin[u].revision, type_plugins[v].revision))) {
156 LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed,"
157 "implementation collision for extension %s from module %s%s%s.",
158 file_name, plugin[u].name, plugin[u].module, plugin[u].revision ? "@" : "",
159 plugin[u].revision ? plugin[u].revision : "");
160 return 1;
161 }
162 }
163 }
164
165 /* add the new plugins, we have number of new plugins as u */
166 p = realloc(type_plugins, (type_plugins_count + u) * sizeof *type_plugins);
167 if (!p) {
168 LOGMEM(NULL);
169 return -1;
170 }
171 type_plugins = p;
172 for (; u; u--) {
173 memcpy(&type_plugins[type_plugins_count], &plugin[u - 1], sizeof *plugin);
174 type_plugins_count++;
175 }
176
177 return 0;
178}
179
180static int
181lyext_load_plugin(void *dlhandler, const char *file_name)
182{
183 struct lyext_plugin_list *plugin, *p;
184 struct lyext_plugin_complex *pluginc;
185 uint32_t u, v;
186 char *str;
187
Mislav Novakovic7073ef52018-05-24 17:23:02 +0200188#ifdef STATIC
189 return 0;
190#endif /* STATIC */
191
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100192 /* get the plugin data */
193 plugin = dlsym(dlhandler, file_name);
194 str = dlerror();
195 if (str) {
196 LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed, missing plugin list object (%s).", file_name, str);
197 return 1;
198 }
199
200 for (u = 0; plugin[u].name; u++) {
201 /* check extension implementations for collisions */
202 for (v = 0; v < ext_plugins_count; v++) {
203 if (!strcmp(plugin[u].name, ext_plugins[v].name) &&
204 !strcmp(plugin[u].module, ext_plugins[v].module) &&
205 (!plugin[u].revision || !ext_plugins[v].revision || !strcmp(plugin[u].revision, ext_plugins[v].revision))) {
206 LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed,"
207 "implementation collision for extension %s from module %s%s%s.",
208 file_name, plugin[u].name, plugin[u].module, plugin[u].revision ? "@" : "",
209 plugin[u].revision ? plugin[u].revision : "");
210 return 1;
211 }
212 }
213
214 /* check for valid supported substatements in case of complex extension */
215 if (plugin[u].plugin->type == LYEXT_COMPLEX && ((struct lyext_plugin_complex *)plugin[u].plugin)->substmt) {
216 pluginc = (struct lyext_plugin_complex *)plugin[u].plugin;
217 for (v = 0; pluginc->substmt[v].stmt; v++) {
218 if (pluginc->substmt[v].stmt >= LY_STMT_SUBMODULE ||
219 pluginc->substmt[v].stmt == LY_STMT_VERSION ||
220 pluginc->substmt[v].stmt == LY_STMT_YINELEM) {
221 LOGERR(NULL, LY_EINVAL,
222 "Extension plugin \"%s\" (extension %s) allows not supported extension substatement (%s)",
223 file_name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
224 return 1;
225 }
226 if (pluginc->substmt[v].cardinality > LY_STMT_CARD_MAND &&
227 pluginc->substmt[v].stmt >= LY_STMT_MODIFIER &&
228 pluginc->substmt[v].stmt <= LY_STMT_STATUS) {
229 LOGERR(NULL, LY_EINVAL, "Extension plugin \"%s\" (extension %s) allows multiple instances on \"%s\" "
230 "substatement, which is not supported.",
231 file_name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
232 return 1;
233 }
234 }
235 }
236 }
237
238 /* add the new plugins, we have number of new plugins as u */
239 p = realloc(ext_plugins, (ext_plugins_count + u) * sizeof *ext_plugins);
240 if (!p) {
241 LOGMEM(NULL);
242 return -1;
243 }
244 ext_plugins = p;
245 for (; u; u--) {
246 memcpy(&ext_plugins[ext_plugins_count], &plugin[u - 1], sizeof *plugin);
247 ext_plugins_count++;
248 }
249
250 return 0;
251}
252
Michal Vasko3b887ec2018-05-31 09:31:46 +0200253/* spends name */
254static void
255ly_add_loaded_plugin(char *name)
256{
257 loaded_plugins = ly_realloc(loaded_plugins, (loaded_plugins_count + 2) * sizeof *loaded_plugins);
258 LY_CHECK_ERR_RETURN(!loaded_plugins, free(name); LOGMEM(NULL), );
259 ++loaded_plugins_count;
260
261 loaded_plugins[loaded_plugins_count - 1] = name;
262 loaded_plugins[loaded_plugins_count] = NULL;
263}
264
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100265static void
266ly_load_plugins_dir(DIR *dir, const char *dir_path, int ext_or_type)
267{
268 struct dirent *file;
269 size_t len;
Michal Vasko3b887ec2018-05-31 09:31:46 +0200270 char *str, *name;
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100271 void *dlhandler;
272 int ret;
273
Mislav Novakovic61c79082018-05-21 13:08:24 +0200274#ifdef STATIC
275 return;
276#endif /* STATIC */
277
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100278 while ((file = readdir(dir))) {
279 /* required format of the filename is *LY_PLUGIN_SUFFIX */
280 len = strlen(file->d_name);
281 if (len < LY_PLUGIN_SUFFIX_LEN + 1 ||
282 strcmp(&file->d_name[len - LY_PLUGIN_SUFFIX_LEN], LY_PLUGIN_SUFFIX)) {
283 continue;
284 }
285
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100286 /* and construct the filepath */
Michal Vasko87e29a82018-05-21 13:50:43 +0200287 if (asprintf(&str, "%s/%s", dir_path, file->d_name) == -1) {
288 LOGMEM(NULL);
289 return;
290 }
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100291
Radek Krejcib1473bb2018-04-18 09:23:17 +0200292 /* load the plugin */
293 dlhandler = dlopen(str, RTLD_NOW);
294 if (!dlhandler) {
295 LOGERR(NULL, LY_ESYS, "Loading \"%s\" as a plugin failed (%s).", str, dlerror());
296 free(str);
297 continue;
298 }
299 if (ly_set_contains(&dlhandlers, dlhandler) != -1) {
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100300 /* the plugin is already loaded */
301 LOGVRB("Plugin \"%s\" already loaded.", str);
302 free(str);
303
304 /* keep the refcount of the shared object correct */
305 dlclose(dlhandler);
306 continue;
307 }
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100308 dlerror(); /* Clear any existing error */
309
Michal Vasko3b887ec2018-05-31 09:31:46 +0200310 /* store the name without the suffix */
311 name = strndup(file->d_name, len - LY_PLUGIN_SUFFIX_LEN);
312 if (!name) {
313 LOGMEM(NULL);
314 free(str);
315 return;
316 }
317
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100318 if (ext_or_type) {
319 ret = lyext_load_plugin(dlhandler, name);
320 } else {
321 ret = lytype_load_plugin(dlhandler, name);
322 }
Michal Vasko3b887ec2018-05-31 09:31:46 +0200323 if (!ret) {
324 LOGVRB("Plugin \"%s\" successfully loaded.", str);
325 /* spends name */
326 ly_add_loaded_plugin(name);
327 /* keep the handler */
328 ly_set_add(&dlhandlers, dlhandler, LY_SET_OPT_USEASLIST);
329 } else {
330 free(name);
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100331 dlclose(dlhandler);
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100332 }
Michal Vaskoe65f39a2018-04-25 11:29:10 +0200333 free(str);
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100334
Michal Vasko3b887ec2018-05-31 09:31:46 +0200335 if (ret == -1) {
336 /* finish on error */
337 break;
338 }
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100339 }
340}
341
342API void
343ly_load_plugins(void)
344{
345 DIR* dir;
346 const char *pluginsdir;
347
Mislav Novakovic61c79082018-05-21 13:08:24 +0200348#ifdef STATIC
349 /* lock the extension plugins list */
350 pthread_mutex_lock(&plugins_lock);
351
352 ext_plugins = static_load_lyext_plugins(&ext_plugins_count);
353 type_plugins = static_load_lytype_plugins(&type_plugins_count);
354
355 /* unlock the global structures */
356 pthread_mutex_unlock(&plugins_lock);
357 return;
358#endif /* STATIC */
359
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100360 /* lock the extension plugins list */
361 pthread_mutex_lock(&plugins_lock);
362
363 /* increase references */
364 ++plugin_refs;
365
366 /* try to get the plugins directory from environment variable */
367 pluginsdir = getenv("LIBYANG_EXTENSIONS_PLUGINS_DIR");
368 if (!pluginsdir) {
369 pluginsdir = LYEXT_PLUGINS_DIR;
370 }
371
372 dir = opendir(pluginsdir);
373 if (!dir) {
374 /* no directory (or no access to it), no extension plugins */
375 LOGWRN(NULL, "Failed to open libyang extensions plugins directory \"%s\" (%s).", pluginsdir, strerror(errno));
376 } else {
377 ly_load_plugins_dir(dir, pluginsdir, 1);
378 closedir(dir);
379 }
380
381 /* try to get the plugins directory from environment variable */
382 pluginsdir = getenv("LIBYANG_USER_TYPES_PLUGINS_DIR");
383 if (!pluginsdir) {
384 pluginsdir = LY_USER_TYPES_PLUGINS_DIR;
385 }
386
387 dir = opendir(pluginsdir);
388 if (!dir) {
389 /* no directory (or no access to it), no extension plugins */
390 LOGWRN(NULL, "Failed to open libyang user types plugins directory \"%s\" (%s).", pluginsdir, strerror(errno));
391 } else {
392 ly_load_plugins_dir(dir, pluginsdir, 0);
393 closedir(dir);
394 }
395
396 /* unlock the global structures */
397 pthread_mutex_unlock(&plugins_lock);
398}
399
400struct lyext_plugin *
401ext_get_plugin(const char *name, const char *module, const char *revision)
402{
403 uint16_t u;
404
405 assert(name);
406 assert(module);
407
408 for (u = 0; u < ext_plugins_count; u++) {
409 if (!strcmp(name, ext_plugins[u].name) &&
410 !strcmp(module, ext_plugins[u].module) &&
411 (!ext_plugins[u].revision || !strcmp(revision, ext_plugins[u].revision))) {
412 /* we have the match */
413 return ext_plugins[u].plugin;
414 }
415 }
416
417 /* plugin not found */
418 return NULL;
419}
420
421API int
422lys_ext_instance_presence(struct lys_ext *def, struct lys_ext_instance **ext, uint8_t ext_size)
423{
424 uint8_t index;
425
426 if (!def || (ext_size && !ext)) {
427 LOGARG;
428 return -1;
429 }
430
431 /* search for the extension instance */
432 for (index = 0; index < ext_size; index++) {
433 if (ext[index]->def == def) {
434 return index;
435 }
436 }
437
438 /* not found */
439 return -1;
440}
441
442API void *
443lys_ext_complex_get_substmt(LY_STMT stmt, struct lys_ext_instance_complex *ext, struct lyext_substmt **info)
444{
445 int i;
446
447 if (!ext || !ext->def || !ext->def->plugin || ext->def->plugin->type != LYEXT_COMPLEX) {
448 LOGARG;
449 return NULL;
450 }
451
452 if (!ext->substmt) {
453 /* no substatement defined in the plugin */
454 if (info) {
455 *info = NULL;
456 }
457 return NULL;
458 }
459
460 /* search the substatements defined by the plugin */
461 for (i = 0; ext->substmt[i].stmt; i++) {
462 if (stmt == LY_STMT_NODE) {
463 if (ext->substmt[i].stmt >= LY_STMT_ACTION && ext->substmt[i].stmt <= LY_STMT_USES) {
464 if (info) {
465 *info = &ext->substmt[i];
466 }
467 break;
468 }
469 } else if (ext->substmt[i].stmt == stmt) {
470 if (info) {
471 *info = &ext->substmt[i];
472 }
473 break;
474 }
475 }
476
477 if (ext->substmt[i].stmt) {
478 return &ext->content[ext->substmt[i].offset];
479 } else {
480 return NULL;
481 }
482}
483
484LY_STMT
485lys_snode2stmt(LYS_NODE nodetype)
486{
487 switch(nodetype) {
488 case LYS_CONTAINER:
489 return LY_STMT_CONTAINER;
490 case LYS_CHOICE:
491 return LY_STMT_CHOICE;
492 case LYS_LEAF:
493 return LY_STMT_LEAF;
494 case LYS_LEAFLIST:
495 return LY_STMT_LEAFLIST;
496 case LYS_LIST:
497 return LY_STMT_LIST;
498 case LYS_ANYXML:
499 case LYS_ANYDATA:
500 return LY_STMT_ANYDATA;
501 case LYS_CASE:
502 return LY_STMT_CASE;
503 case LYS_NOTIF:
504 return LY_STMT_NOTIFICATION;
505 case LYS_RPC:
506 return LY_STMT_RPC;
507 case LYS_INPUT:
508 return LY_STMT_INPUT;
509 case LYS_OUTPUT:
510 return LY_STMT_OUTPUT;
511 case LYS_GROUPING:
512 return LY_STMT_GROUPING;
513 case LYS_USES:
514 return LY_STMT_USES;
515 case LYS_AUGMENT:
516 return LY_STMT_AUGMENT;
517 case LYS_ACTION:
518 return LY_STMT_ACTION;
519 default:
520 return LY_STMT_NODE;
521 }
522}
523
524static struct lytype_plugin_list *
525lytype_find(const char *module, const char *revision, const char *type_name)
526{
527 uint16_t u;
528
529 for (u = 0; u < type_plugins_count; ++u) {
530 if (ly_strequal(module, type_plugins[u].module, 0) && ((!revision && !type_plugins[u].revision)
531 || (revision && ly_strequal(revision, type_plugins[u].revision, 0)))
532 && ly_strequal(type_name, type_plugins[u].name, 0)) {
533 return &(type_plugins[u]);
534 }
535 }
536
537 return NULL;
538}
539
540int
541lytype_store(const struct lys_module *mod, const char *type_name, const char *value_str, lyd_val *value)
542{
543 struct lytype_plugin_list *p;
544 char *err_msg = NULL;
545
546 assert(mod && type_name && value_str && value);
547
548 p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type_name);
549 if (p) {
550 if (p->store_clb(type_name, value_str, value, &err_msg)) {
551 if (!err_msg) {
552 if (asprintf(&err_msg, "Failed to store value \"%s\" of user type \"%s\".", value_str, type_name) == -1) {
553 LOGMEM(mod->ctx);
554 return -1;
555 }
556 }
557 LOGERR(mod->ctx, LY_EPLUGIN, err_msg);
558 free(err_msg);
559 return -1;
560 }
561
562 /* value successfully stored */
563 return 0;
564 }
565
566 return 1;
567}
568
569void
570lytype_free(const struct lys_module *mod, const char *type_name, lyd_val value)
571{
572 struct lytype_plugin_list *p;
573
574 p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type_name);
575 if (!p) {
576 LOGINT(mod->ctx);
577 return;
578 }
579
580 if (p->free_clb) {
581 p->free_clb(value.ptr);
582 }
583}