blob: 6c0c9f633e8c0d4ce99105a19f33398cd7cf11d9 [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 */
Michal Vasko87e29a82018-05-21 13:50:43 +0200232 if (asprintf(&str, "%s/%s", dir_path, file->d_name) == -1) {
233 LOGMEM(NULL);
234 return;
235 }
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100236
Radek Krejcib1473bb2018-04-18 09:23:17 +0200237 /* load the plugin */
238 dlhandler = dlopen(str, RTLD_NOW);
239 if (!dlhandler) {
240 LOGERR(NULL, LY_ESYS, "Loading \"%s\" as a plugin failed (%s).", str, dlerror());
241 free(str);
242 continue;
243 }
244 if (ly_set_contains(&dlhandlers, dlhandler) != -1) {
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100245 /* the plugin is already loaded */
246 LOGVRB("Plugin \"%s\" already loaded.", str);
247 free(str);
248
249 /* keep the refcount of the shared object correct */
250 dlclose(dlhandler);
251 continue;
252 }
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100253 dlerror(); /* Clear any existing error */
254
255 if (ext_or_type) {
256 ret = lyext_load_plugin(dlhandler, name);
257 } else {
258 ret = lytype_load_plugin(dlhandler, name);
259 }
260 if (ret == 1) {
Michal Vaskoe65f39a2018-04-25 11:29:10 +0200261 free(str);
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100262 dlclose(dlhandler);
263 continue;
264 } else if (ret == -1) {
Michal Vaskoe65f39a2018-04-25 11:29:10 +0200265 free(str);
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100266 dlclose(dlhandler);
267 break;
268 }
Radek Krejcib1473bb2018-04-18 09:23:17 +0200269 LOGVRB("Plugin \"%s\" successfully loaded.", str);
Michal Vaskoe65f39a2018-04-25 11:29:10 +0200270 free(str);
Michal Vaskoc6cd3f02018-03-02 14:07:42 +0100271
272 /* keep the handler */
273 ly_set_add(&dlhandlers, dlhandler, LY_SET_OPT_USEASLIST);
274 }
275}
276
277API void
278ly_load_plugins(void)
279{
280 DIR* dir;
281 const char *pluginsdir;
282
283 /* lock the extension plugins list */
284 pthread_mutex_lock(&plugins_lock);
285
286 /* increase references */
287 ++plugin_refs;
288
289 /* try to get the plugins directory from environment variable */
290 pluginsdir = getenv("LIBYANG_EXTENSIONS_PLUGINS_DIR");
291 if (!pluginsdir) {
292 pluginsdir = LYEXT_PLUGINS_DIR;
293 }
294
295 dir = opendir(pluginsdir);
296 if (!dir) {
297 /* no directory (or no access to it), no extension plugins */
298 LOGWRN(NULL, "Failed to open libyang extensions plugins directory \"%s\" (%s).", pluginsdir, strerror(errno));
299 } else {
300 ly_load_plugins_dir(dir, pluginsdir, 1);
301 closedir(dir);
302 }
303
304 /* try to get the plugins directory from environment variable */
305 pluginsdir = getenv("LIBYANG_USER_TYPES_PLUGINS_DIR");
306 if (!pluginsdir) {
307 pluginsdir = LY_USER_TYPES_PLUGINS_DIR;
308 }
309
310 dir = opendir(pluginsdir);
311 if (!dir) {
312 /* no directory (or no access to it), no extension plugins */
313 LOGWRN(NULL, "Failed to open libyang user types plugins directory \"%s\" (%s).", pluginsdir, strerror(errno));
314 } else {
315 ly_load_plugins_dir(dir, pluginsdir, 0);
316 closedir(dir);
317 }
318
319 /* unlock the global structures */
320 pthread_mutex_unlock(&plugins_lock);
321}
322
323struct lyext_plugin *
324ext_get_plugin(const char *name, const char *module, const char *revision)
325{
326 uint16_t u;
327
328 assert(name);
329 assert(module);
330
331 for (u = 0; u < ext_plugins_count; u++) {
332 if (!strcmp(name, ext_plugins[u].name) &&
333 !strcmp(module, ext_plugins[u].module) &&
334 (!ext_plugins[u].revision || !strcmp(revision, ext_plugins[u].revision))) {
335 /* we have the match */
336 return ext_plugins[u].plugin;
337 }
338 }
339
340 /* plugin not found */
341 return NULL;
342}
343
344API int
345lys_ext_instance_presence(struct lys_ext *def, struct lys_ext_instance **ext, uint8_t ext_size)
346{
347 uint8_t index;
348
349 if (!def || (ext_size && !ext)) {
350 LOGARG;
351 return -1;
352 }
353
354 /* search for the extension instance */
355 for (index = 0; index < ext_size; index++) {
356 if (ext[index]->def == def) {
357 return index;
358 }
359 }
360
361 /* not found */
362 return -1;
363}
364
365API void *
366lys_ext_complex_get_substmt(LY_STMT stmt, struct lys_ext_instance_complex *ext, struct lyext_substmt **info)
367{
368 int i;
369
370 if (!ext || !ext->def || !ext->def->plugin || ext->def->plugin->type != LYEXT_COMPLEX) {
371 LOGARG;
372 return NULL;
373 }
374
375 if (!ext->substmt) {
376 /* no substatement defined in the plugin */
377 if (info) {
378 *info = NULL;
379 }
380 return NULL;
381 }
382
383 /* search the substatements defined by the plugin */
384 for (i = 0; ext->substmt[i].stmt; i++) {
385 if (stmt == LY_STMT_NODE) {
386 if (ext->substmt[i].stmt >= LY_STMT_ACTION && ext->substmt[i].stmt <= LY_STMT_USES) {
387 if (info) {
388 *info = &ext->substmt[i];
389 }
390 break;
391 }
392 } else if (ext->substmt[i].stmt == stmt) {
393 if (info) {
394 *info = &ext->substmt[i];
395 }
396 break;
397 }
398 }
399
400 if (ext->substmt[i].stmt) {
401 return &ext->content[ext->substmt[i].offset];
402 } else {
403 return NULL;
404 }
405}
406
407LY_STMT
408lys_snode2stmt(LYS_NODE nodetype)
409{
410 switch(nodetype) {
411 case LYS_CONTAINER:
412 return LY_STMT_CONTAINER;
413 case LYS_CHOICE:
414 return LY_STMT_CHOICE;
415 case LYS_LEAF:
416 return LY_STMT_LEAF;
417 case LYS_LEAFLIST:
418 return LY_STMT_LEAFLIST;
419 case LYS_LIST:
420 return LY_STMT_LIST;
421 case LYS_ANYXML:
422 case LYS_ANYDATA:
423 return LY_STMT_ANYDATA;
424 case LYS_CASE:
425 return LY_STMT_CASE;
426 case LYS_NOTIF:
427 return LY_STMT_NOTIFICATION;
428 case LYS_RPC:
429 return LY_STMT_RPC;
430 case LYS_INPUT:
431 return LY_STMT_INPUT;
432 case LYS_OUTPUT:
433 return LY_STMT_OUTPUT;
434 case LYS_GROUPING:
435 return LY_STMT_GROUPING;
436 case LYS_USES:
437 return LY_STMT_USES;
438 case LYS_AUGMENT:
439 return LY_STMT_AUGMENT;
440 case LYS_ACTION:
441 return LY_STMT_ACTION;
442 default:
443 return LY_STMT_NODE;
444 }
445}
446
447static struct lytype_plugin_list *
448lytype_find(const char *module, const char *revision, const char *type_name)
449{
450 uint16_t u;
451
452 for (u = 0; u < type_plugins_count; ++u) {
453 if (ly_strequal(module, type_plugins[u].module, 0) && ((!revision && !type_plugins[u].revision)
454 || (revision && ly_strequal(revision, type_plugins[u].revision, 0)))
455 && ly_strequal(type_name, type_plugins[u].name, 0)) {
456 return &(type_plugins[u]);
457 }
458 }
459
460 return NULL;
461}
462
463int
464lytype_store(const struct lys_module *mod, const char *type_name, const char *value_str, lyd_val *value)
465{
466 struct lytype_plugin_list *p;
467 char *err_msg = NULL;
468
469 assert(mod && type_name && value_str && value);
470
471 p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type_name);
472 if (p) {
473 if (p->store_clb(type_name, value_str, value, &err_msg)) {
474 if (!err_msg) {
475 if (asprintf(&err_msg, "Failed to store value \"%s\" of user type \"%s\".", value_str, type_name) == -1) {
476 LOGMEM(mod->ctx);
477 return -1;
478 }
479 }
480 LOGERR(mod->ctx, LY_EPLUGIN, err_msg);
481 free(err_msg);
482 return -1;
483 }
484
485 /* value successfully stored */
486 return 0;
487 }
488
489 return 1;
490}
491
492void
493lytype_free(const struct lys_module *mod, const char *type_name, lyd_val value)
494{
495 struct lytype_plugin_list *p;
496
497 p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type_name);
498 if (!p) {
499 LOGINT(mod->ctx);
500 return;
501 }
502
503 if (p->free_clb) {
504 p->free_clb(value.ptr);
505 }
506}