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