blob: 8c759680f24154b6f9da33cd3fae1df69a91d809 [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>
26#include <limits.h>
27
28#include "common.h"
29#include "extensions.h"
30#include "user_types.h"
31#include "plugin_config.h"
32#include "libyang.h"
33#include "parser.h"
34
35/* internal structures storing the plugins */
36static struct lyext_plugin_list *ext_plugins = NULL;
37static uint16_t ext_plugins_count = 0; /* size of the ext_plugins array */
38
39static struct lytype_plugin_list *type_plugins = NULL;
40static uint16_t type_plugins_count = 0;
41
42static struct ly_set dlhandlers = {0, 0, {NULL}};
43static pthread_mutex_t plugins_lock = PTHREAD_MUTEX_INITIALIZER;
44
45/**
46 * @brief reference counter for the plugins, it actually counts number of contexts
47 */
48static uint32_t plugin_refs;
49
50API int
51ly_clean_plugins(void)
52{
53 unsigned int u;
54 int ret = EXIT_SUCCESS;
55
56 /* lock the extension plugins list */
57 pthread_mutex_lock(&plugins_lock);
58
59 if (--plugin_refs) {
60 /* there is a context that may refer to the plugins, so we cannot remove them */
61 ret = EXIT_FAILURE;
62 goto cleanup;
63 }
64
65 if (!ext_plugins_count && !type_plugins_count) {
66 /* no plugin loaded - nothing to do */
67 goto cleanup;
68 }
69
70 /* clean the lists */
71 free(ext_plugins);
72 ext_plugins = NULL;
73 ext_plugins_count = 0;
74
75 free(type_plugins);
76 type_plugins = NULL;
77 type_plugins_count = 0;
78
79 /* close the dl handlers */
80 for (u = 0; u < dlhandlers.number; u++) {
81 dlclose(dlhandlers.set.g[u]);
82 }
83 free(dlhandlers.set.g);
84 dlhandlers.set.g = NULL;
85 dlhandlers.size = 0;
86 dlhandlers.number = 0;
87
88cleanup:
89 /* unlock the global structures */
90 pthread_mutex_unlock(&plugins_lock);
91
92 return ret;
93}
94
95static int
96lytype_load_plugin(void *dlhandler, const char *file_name)
97{
98 struct lytype_plugin_list *plugin, *p;
99 uint32_t u, v;
100 char *str;
101
102 /* get the plugin data */
103 plugin = dlsym(dlhandler, file_name);
104 str = dlerror();
105 if (str) {
106 LOGERR(NULL, LY_ESYS, "Processing \"%s\" user type plugin failed, missing plugin list object (%s).", file_name, str);
107 return 1;
108 }
109
110 for (u = 0; plugin[u].name; u++) {
111 /* check user type implementations for collisions */
112 for (v = 0; v < type_plugins_count; v++) {
113 if (!strcmp(plugin[u].name, type_plugins[v].name) &&
114 !strcmp(plugin[u].module, type_plugins[v].module) &&
115 (!plugin[u].revision || !type_plugins[v].revision || !strcmp(plugin[u].revision, type_plugins[v].revision))) {
116 LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed,"
117 "implementation collision for extension %s from module %s%s%s.",
118 file_name, plugin[u].name, plugin[u].module, plugin[u].revision ? "@" : "",
119 plugin[u].revision ? plugin[u].revision : "");
120 return 1;
121 }
122 }
123 }
124
125 /* add the new plugins, we have number of new plugins as u */
126 p = realloc(type_plugins, (type_plugins_count + u) * sizeof *type_plugins);
127 if (!p) {
128 LOGMEM(NULL);
129 return -1;
130 }
131 type_plugins = p;
132 for (; u; u--) {
133 memcpy(&type_plugins[type_plugins_count], &plugin[u - 1], sizeof *plugin);
134 type_plugins_count++;
135 }
136
137 return 0;
138}
139
140static int
141lyext_load_plugin(void *dlhandler, const char *file_name)
142{
143 struct lyext_plugin_list *plugin, *p;
144 struct lyext_plugin_complex *pluginc;
145 uint32_t u, v;
146 char *str;
147
148 /* get the plugin data */
149 plugin = dlsym(dlhandler, file_name);
150 str = dlerror();
151 if (str) {
152 LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed, missing plugin list object (%s).", file_name, str);
153 return 1;
154 }
155
156 for (u = 0; plugin[u].name; u++) {
157 /* check extension implementations for collisions */
158 for (v = 0; v < ext_plugins_count; v++) {
159 if (!strcmp(plugin[u].name, ext_plugins[v].name) &&
160 !strcmp(plugin[u].module, ext_plugins[v].module) &&
161 (!plugin[u].revision || !ext_plugins[v].revision || !strcmp(plugin[u].revision, ext_plugins[v].revision))) {
162 LOGERR(NULL, LY_ESYS, "Processing \"%s\" extension plugin failed,"
163 "implementation collision for extension %s from module %s%s%s.",
164 file_name, plugin[u].name, plugin[u].module, plugin[u].revision ? "@" : "",
165 plugin[u].revision ? plugin[u].revision : "");
166 return 1;
167 }
168 }
169
170 /* check for valid supported substatements in case of complex extension */
171 if (plugin[u].plugin->type == LYEXT_COMPLEX && ((struct lyext_plugin_complex *)plugin[u].plugin)->substmt) {
172 pluginc = (struct lyext_plugin_complex *)plugin[u].plugin;
173 for (v = 0; pluginc->substmt[v].stmt; v++) {
174 if (pluginc->substmt[v].stmt >= LY_STMT_SUBMODULE ||
175 pluginc->substmt[v].stmt == LY_STMT_VERSION ||
176 pluginc->substmt[v].stmt == LY_STMT_YINELEM) {
177 LOGERR(NULL, LY_EINVAL,
178 "Extension plugin \"%s\" (extension %s) allows not supported extension substatement (%s)",
179 file_name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
180 return 1;
181 }
182 if (pluginc->substmt[v].cardinality > LY_STMT_CARD_MAND &&
183 pluginc->substmt[v].stmt >= LY_STMT_MODIFIER &&
184 pluginc->substmt[v].stmt <= LY_STMT_STATUS) {
185 LOGERR(NULL, LY_EINVAL, "Extension plugin \"%s\" (extension %s) allows multiple instances on \"%s\" "
186 "substatement, which is not supported.",
187 file_name, plugin[u].name, ly_stmt_str[pluginc->substmt[v].stmt]);
188 return 1;
189 }
190 }
191 }
192 }
193
194 /* add the new plugins, we have number of new plugins as u */
195 p = realloc(ext_plugins, (ext_plugins_count + u) * sizeof *ext_plugins);
196 if (!p) {
197 LOGMEM(NULL);
198 return -1;
199 }
200 ext_plugins = p;
201 for (; u; u--) {
202 memcpy(&ext_plugins[ext_plugins_count], &plugin[u - 1], sizeof *plugin);
203 ext_plugins_count++;
204 }
205
206 return 0;
207}
208
209static void
210ly_load_plugins_dir(DIR *dir, const char *dir_path, int ext_or_type)
211{
212 struct dirent *file;
213 size_t len;
214 char *str;
215 char name[NAME_MAX];
216 void *dlhandler;
217 int ret;
218
219 while ((file = readdir(dir))) {
220 /* required format of the filename is *LY_PLUGIN_SUFFIX */
221 len = strlen(file->d_name);
222 if (len < LY_PLUGIN_SUFFIX_LEN + 1 ||
223 strcmp(&file->d_name[len - LY_PLUGIN_SUFFIX_LEN], LY_PLUGIN_SUFFIX)) {
224 continue;
225 }
226
227 /* store the name without the suffix */
228 memcpy(name, file->d_name, len - LY_PLUGIN_SUFFIX_LEN);
229 name[len - LY_PLUGIN_SUFFIX_LEN] = '\0';
230
231 /* and construct the filepath */
232 asprintf(&str, "%s/%s", dir_path, file->d_name);
233
Radek Krejcib1473bb2018-04-18 09:23:17 +0200234 /* load the plugin */
235 dlhandler = dlopen(str, RTLD_NOW);
236 if (!dlhandler) {
237 LOGERR(NULL, LY_ESYS, "Loading \"%s\" as a plugin failed (%s).", str, dlerror());
238 free(str);
239 continue;
240 }
241 if (ly_set_contains(&dlhandlers, dlhandler) != -1) {
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100242 /* the plugin is already loaded */
243 LOGVRB("Plugin \"%s\" already loaded.", str);
244 free(str);
245
246 /* keep the refcount of the shared object correct */
247 dlclose(dlhandler);
248 continue;
249 }
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100250 free(str);
251 dlerror(); /* Clear any existing error */
252
253 if (ext_or_type) {
254 ret = lyext_load_plugin(dlhandler, name);
255 } else {
256 ret = lytype_load_plugin(dlhandler, name);
257 }
258 if (ret == 1) {
259 dlclose(dlhandler);
260 continue;
261 } else if (ret == -1) {
262 dlclose(dlhandler);
263 break;
264 }
Radek Krejcib1473bb2018-04-18 09:23:17 +0200265 LOGVRB("Plugin \"%s\" successfully loaded.", str);
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100266
267 /* keep the handler */
268 ly_set_add(&dlhandlers, dlhandler, LY_SET_OPT_USEASLIST);
269 }
270}
271
272API void
273ly_load_plugins(void)
274{
275 DIR* dir;
276 const char *pluginsdir;
277
278 /* lock the extension plugins list */
279 pthread_mutex_lock(&plugins_lock);
280
281 /* increase references */
282 ++plugin_refs;
283
284 /* try to get the plugins directory from environment variable */
285 pluginsdir = getenv("LIBYANG_EXTENSIONS_PLUGINS_DIR");
286 if (!pluginsdir) {
287 pluginsdir = LYEXT_PLUGINS_DIR;
288 }
289
290 dir = opendir(pluginsdir);
291 if (!dir) {
292 /* no directory (or no access to it), no extension plugins */
293 LOGWRN(NULL, "Failed to open libyang extensions plugins directory \"%s\" (%s).", pluginsdir, strerror(errno));
294 } else {
295 ly_load_plugins_dir(dir, pluginsdir, 1);
296 closedir(dir);
297 }
298
299 /* try to get the plugins directory from environment variable */
300 pluginsdir = getenv("LIBYANG_USER_TYPES_PLUGINS_DIR");
301 if (!pluginsdir) {
302 pluginsdir = LY_USER_TYPES_PLUGINS_DIR;
303 }
304
305 dir = opendir(pluginsdir);
306 if (!dir) {
307 /* no directory (or no access to it), no extension plugins */
308 LOGWRN(NULL, "Failed to open libyang user types plugins directory \"%s\" (%s).", pluginsdir, strerror(errno));
309 } else {
310 ly_load_plugins_dir(dir, pluginsdir, 0);
311 closedir(dir);
312 }
313
314 /* unlock the global structures */
315 pthread_mutex_unlock(&plugins_lock);
316}
317
318struct lyext_plugin *
319ext_get_plugin(const char *name, const char *module, const char *revision)
320{
321 uint16_t u;
322
323 assert(name);
324 assert(module);
325
326 for (u = 0; u < ext_plugins_count; u++) {
327 if (!strcmp(name, ext_plugins[u].name) &&
328 !strcmp(module, ext_plugins[u].module) &&
329 (!ext_plugins[u].revision || !strcmp(revision, ext_plugins[u].revision))) {
330 /* we have the match */
331 return ext_plugins[u].plugin;
332 }
333 }
334
335 /* plugin not found */
336 return NULL;
337}
338
339API int
340lys_ext_instance_presence(struct lys_ext *def, struct lys_ext_instance **ext, uint8_t ext_size)
341{
342 uint8_t index;
343
344 if (!def || (ext_size && !ext)) {
345 LOGARG;
346 return -1;
347 }
348
349 /* search for the extension instance */
350 for (index = 0; index < ext_size; index++) {
351 if (ext[index]->def == def) {
352 return index;
353 }
354 }
355
356 /* not found */
357 return -1;
358}
359
360API void *
361lys_ext_complex_get_substmt(LY_STMT stmt, struct lys_ext_instance_complex *ext, struct lyext_substmt **info)
362{
363 int i;
364
365 if (!ext || !ext->def || !ext->def->plugin || ext->def->plugin->type != LYEXT_COMPLEX) {
366 LOGARG;
367 return NULL;
368 }
369
370 if (!ext->substmt) {
371 /* no substatement defined in the plugin */
372 if (info) {
373 *info = NULL;
374 }
375 return NULL;
376 }
377
378 /* search the substatements defined by the plugin */
379 for (i = 0; ext->substmt[i].stmt; i++) {
380 if (stmt == LY_STMT_NODE) {
381 if (ext->substmt[i].stmt >= LY_STMT_ACTION && ext->substmt[i].stmt <= LY_STMT_USES) {
382 if (info) {
383 *info = &ext->substmt[i];
384 }
385 break;
386 }
387 } else if (ext->substmt[i].stmt == stmt) {
388 if (info) {
389 *info = &ext->substmt[i];
390 }
391 break;
392 }
393 }
394
395 if (ext->substmt[i].stmt) {
396 return &ext->content[ext->substmt[i].offset];
397 } else {
398 return NULL;
399 }
400}
401
402LY_STMT
403lys_snode2stmt(LYS_NODE nodetype)
404{
405 switch(nodetype) {
406 case LYS_CONTAINER:
407 return LY_STMT_CONTAINER;
408 case LYS_CHOICE:
409 return LY_STMT_CHOICE;
410 case LYS_LEAF:
411 return LY_STMT_LEAF;
412 case LYS_LEAFLIST:
413 return LY_STMT_LEAFLIST;
414 case LYS_LIST:
415 return LY_STMT_LIST;
416 case LYS_ANYXML:
417 case LYS_ANYDATA:
418 return LY_STMT_ANYDATA;
419 case LYS_CASE:
420 return LY_STMT_CASE;
421 case LYS_NOTIF:
422 return LY_STMT_NOTIFICATION;
423 case LYS_RPC:
424 return LY_STMT_RPC;
425 case LYS_INPUT:
426 return LY_STMT_INPUT;
427 case LYS_OUTPUT:
428 return LY_STMT_OUTPUT;
429 case LYS_GROUPING:
430 return LY_STMT_GROUPING;
431 case LYS_USES:
432 return LY_STMT_USES;
433 case LYS_AUGMENT:
434 return LY_STMT_AUGMENT;
435 case LYS_ACTION:
436 return LY_STMT_ACTION;
437 default:
438 return LY_STMT_NODE;
439 }
440}
441
442static struct lytype_plugin_list *
443lytype_find(const char *module, const char *revision, const char *type_name)
444{
445 uint16_t u;
446
447 for (u = 0; u < type_plugins_count; ++u) {
448 if (ly_strequal(module, type_plugins[u].module, 0) && ((!revision && !type_plugins[u].revision)
449 || (revision && ly_strequal(revision, type_plugins[u].revision, 0)))
450 && ly_strequal(type_name, type_plugins[u].name, 0)) {
451 return &(type_plugins[u]);
452 }
453 }
454
455 return NULL;
456}
457
458int
459lytype_store(const struct lys_module *mod, const char *type_name, const char *value_str, lyd_val *value)
460{
461 struct lytype_plugin_list *p;
462 char *err_msg = NULL;
463
464 assert(mod && type_name && value_str && value);
465
466 p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type_name);
467 if (p) {
468 if (p->store_clb(type_name, value_str, value, &err_msg)) {
469 if (!err_msg) {
470 if (asprintf(&err_msg, "Failed to store value \"%s\" of user type \"%s\".", value_str, type_name) == -1) {
471 LOGMEM(mod->ctx);
472 return -1;
473 }
474 }
475 LOGERR(mod->ctx, LY_EPLUGIN, err_msg);
476 free(err_msg);
477 return -1;
478 }
479
480 /* value successfully stored */
481 return 0;
482 }
483
484 return 1;
485}
486
487void
488lytype_free(const struct lys_module *mod, const char *type_name, lyd_val value)
489{
490 struct lytype_plugin_list *p;
491
492 p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type_name);
493 if (!p) {
494 LOGINT(mod->ctx);
495 return;
496 }
497
498 if (p->free_clb) {
499 p->free_clb(value.ptr);
500 }
501}