vlib: add plugin override support
[vpp.git] / src / vpp / api / plugin.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * plugin.c: plugin handling
17  */
18
19 #include <vat/vat.h>
20 #include <vat/plugin.h>
21 #include <dlfcn.h>
22 #include <dirent.h>
23
24 plugin_main_t vat_plugin_main;
25
26 static vlib_log_class_t vat_builtin_logger;
27
28 #define PLUGIN_LOG_DBG(...) \
29   do {vlib_log_debug (vat_builtin_logger, __VA_ARGS__);} while(0)
30 #define PLUGIN_LOG_ERR(...) \
31   do {vlib_log_err (vat_builtin_logger, __VA_ARGS__);} while(0)
32 #define PLUGIN_LOG_NOTICE(...) \
33   do {vlib_log_notice (vat_builtin_logger, __VA_ARGS__);} while(0)
34
35 static int
36 load_one_vat_plugin (plugin_main_t * pm, plugin_info_t * pi)
37 {
38   void *handle, *register_handle;
39   clib_error_t *(*fp) (vat_main_t *);
40   clib_error_t *error;
41
42   handle = dlopen ((char *) pi->filename, RTLD_LAZY);
43
44   /*
45    * Note: this can happen if the plugin has an undefined symbol reference,
46    * so print a warning. Otherwise, the poor slob won't know what happened.
47    * Ask me how I know that...
48    */
49   if (handle == 0)
50     {
51       PLUGIN_LOG_ERR ("%s", dlerror ());
52       return 0;
53     }
54
55   pi->handle = handle;
56
57   register_handle = dlsym (pi->handle, "vat_plugin_register");
58   if (register_handle == 0)
59     {
60       PLUGIN_LOG_ERR ("%s: symbol vat_plugin_register not found", pi->name);
61       dlclose (handle);
62       return 0;
63     }
64
65   fp = register_handle;
66
67   error = (*fp) (pm->vat_main);
68
69   if (error)
70     {
71       u8 *err = format (0, "%U%c", format_clib_error, error, 0);
72       PLUGIN_LOG_ERR ((char *) err);
73       clib_error_free (error);
74       dlclose (handle);
75       pi->handle = 0;
76       return 1;
77     }
78
79   PLUGIN_LOG_NOTICE ("Loaded plugin: %s", pi->name);
80
81   return 0;
82 }
83
84 static u8 **
85 split_plugin_path (plugin_main_t * pm)
86 {
87   int i;
88   u8 **rv = 0;
89   u8 *path = pm->plugin_path;
90   u8 *this = 0;
91
92   for (i = 0; i < vec_len (pm->plugin_path); i++)
93     {
94       if (path[i] != ':')
95         {
96           vec_add1 (this, path[i]);
97           continue;
98         }
99       vec_add1 (this, 0);
100       vec_add1 (rv, this);
101       this = 0;
102     }
103   if (this)
104     {
105       vec_add1 (this, 0);
106       vec_add1 (rv, this);
107     }
108   return rv;
109 }
110
111 int
112 vat_load_new_plugins (plugin_main_t * pm)
113 {
114   DIR *dp;
115   struct dirent *entry;
116   struct stat statb;
117   uword *p;
118   plugin_info_t *pi;
119   u8 **plugin_path;
120   int i;
121
122   plugin_path = split_plugin_path (pm);
123
124   for (i = 0; i < vec_len (plugin_path); i++)
125     {
126       dp = opendir ((char *) plugin_path[i]);
127
128       if (dp == 0)
129         continue;
130
131       while ((entry = readdir (dp)))
132         {
133           u8 *plugin_name;
134           u8 *file_name;
135
136           if (pm->plugin_name_filter)
137             {
138               int j;
139               for (j = 0; j < vec_len (pm->plugin_name_filter); j++)
140                 if (entry->d_name[j] != pm->plugin_name_filter[j])
141                   goto next;
142             }
143
144           file_name = format (0, "%s/%s%c", plugin_path[i], entry->d_name, 0);
145           plugin_name = format (0, "%s%c", entry->d_name, 0);
146
147           /* unreadable */
148           if (stat ((char *) file_name, &statb) < 0)
149             {
150             ignore:
151               vec_free (file_name);
152               vec_free (plugin_name);
153               continue;
154             }
155
156           /* a dir or other things which aren't plugins */
157           if (!S_ISREG (statb.st_mode))
158             goto ignore;
159
160           p = hash_get_mem (pm->plugin_by_name_hash, plugin_name);
161           if (p == 0)
162             {
163               vec_add2 (pm->plugin_info, pi, 1);
164               pi->name = plugin_name;
165               pi->filename = file_name;
166               pi->file_info = statb;
167
168               if (load_one_vat_plugin (pm, pi))
169                 {
170                   vec_free (file_name);
171                   vec_free (plugin_name);
172                   _vec_len (pm->plugin_info) = vec_len (pm->plugin_info) - 1;
173                   continue;
174                 }
175               clib_memset (pi, 0, sizeof (*pi));
176               hash_set_mem (pm->plugin_by_name_hash, plugin_name,
177                             pi - pm->plugin_info);
178             }
179         next:
180           ;
181         }
182       closedir (dp);
183       vec_free (plugin_path[i]);
184     }
185   vec_free (plugin_path);
186   return 0;
187 }
188
189 #define QUOTE_(x) #x
190 #define QUOTE(x) QUOTE_(x)
191
192 extern char *vat_plugin_path;
193
194 char *vat_plugin_name_filter = 0;
195
196 int
197 vat_plugin_init (vat_main_t * vam)
198 {
199   plugin_main_t *pm = &vat_plugin_main;
200   u8 *vlib_get_vat_plugin_path (void);
201   u8 *vlib_get_vat_plugin_name_filter (void);
202   u8 *plugin_path;
203   u8 *plugin_name_filter;
204
205   vat_builtin_logger =
206     vlib_log_register_class_rate_limit ("vat-plug", "load",
207                                         0x7FFFFFFF /* aka no rate limit */ );
208
209   plugin_path = vlib_get_vat_plugin_path ();
210   plugin_name_filter = vlib_get_vat_plugin_name_filter ();
211
212   if (plugin_path)
213     vat_plugin_path = (char *) plugin_path;
214
215   if (plugin_name_filter)
216     vat_plugin_name_filter = (char *) plugin_name_filter;
217
218   pm->plugin_path = format (0, "%s%c", vat_plugin_path, 0);
219
220   if (vat_plugin_name_filter)
221     pm->plugin_name_filter = format (0, "%s%c", vat_plugin_name_filter, 0);
222
223   pm->plugin_by_name_hash = hash_create_string (0, sizeof (uword));
224   pm->vat_main = vam;
225
226   return vat_load_new_plugins (pm);
227 }
228
229 /*
230  * fd.io coding-style-patch-verification: ON
231  *
232  * Local Variables:
233  * eval: (c-set-style "gnu")
234  * End:
235  */