4039909098364aba1dea993ea6c675dedf31db29
[vpp.git] / src / vlib / unix / plugin.c
1 /*
2  * plugin.c: plugin handling
3  *
4  * Copyright (c) 2011 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vlib/unix/plugin.h>
19 #include <dlfcn.h>
20 #include <dirent.h>
21
22 plugin_main_t vlib_plugin_main;
23
24 void
25 vlib_set_get_handoff_structure_cb (void *cb)
26 {
27   plugin_main_t *pm = &vlib_plugin_main;
28   pm->handoff_structure_get_cb = cb;
29 }
30
31 static void *
32 vnet_get_handoff_structure (void)
33 {
34   void *(*fp) (void);
35
36   fp = vlib_plugin_main.handoff_structure_get_cb;
37   if (fp == 0)
38     return 0;
39   else
40     return (*fp) ();
41 }
42
43 void *
44 vlib_get_plugin_symbol (char *plugin_name, char *symbol_name)
45 {
46   plugin_main_t *pm = &vlib_plugin_main;
47   uword *p;
48   plugin_info_t *pi;
49
50   if ((p = hash_get_mem (pm->plugin_by_name_hash, plugin_name)) == 0)
51     return 0;
52
53   pi = vec_elt_at_index (pm->plugin_info, p[0]);
54   return dlsym (pi->handle, symbol_name);
55 }
56
57 static int
58 load_one_plugin (plugin_main_t * pm, plugin_info_t * pi, int from_early_init)
59 {
60   void *handle, *register_handle;
61   clib_error_t *(*fp) (vlib_main_t *, void *, int);
62   clib_error_t *error;
63   void *handoff_structure;
64
65   handle = dlopen ((char *) pi->filename, RTLD_LAZY);
66
67   /*
68    * Note: this can happen if the plugin has an undefined symbol reference,
69    * so print a warning. Otherwise, the poor slob won't know what happened.
70    * Ask me how I know that...
71    */
72   if (handle == 0)
73     {
74       clib_warning ("%s", dlerror ());
75       return -1;
76     }
77
78   pi->handle = handle;
79
80
81   register_handle = dlsym (pi->handle, "vlib_plugin_register");
82   if (register_handle == 0)
83     {
84       dlclose (handle);
85       clib_warning ("Plugin missing vlib_plugin_register: %s\n",
86                     (char *) pi->name);
87       return 1;
88     }
89
90   fp = register_handle;
91
92   handoff_structure = vnet_get_handoff_structure ();
93
94   if (handoff_structure == 0)
95     error = clib_error_return (0, "handoff structure callback returned 0");
96   else
97     error = (*fp) (pm->vlib_main, handoff_structure, from_early_init);
98
99   if (error)
100     {
101       clib_error_report (error);
102       dlclose (handle);
103       return 1;
104     }
105
106   clib_warning ("Loaded plugin: %s", pi->name);
107
108   return 0;
109 }
110
111 static u8 **
112 split_plugin_path (plugin_main_t * pm)
113 {
114   int i;
115   u8 **rv = 0;
116   u8 *path = pm->plugin_path;
117   u8 *this = 0;
118
119   for (i = 0; i < vec_len (pm->plugin_path); i++)
120     {
121       if (path[i] != ':')
122         {
123           vec_add1 (this, path[i]);
124           continue;
125         }
126       vec_add1 (this, 0);
127       vec_add1 (rv, this);
128       this = 0;
129     }
130   if (this)
131     {
132       vec_add1 (this, 0);
133       vec_add1 (rv, this);
134     }
135   return rv;
136 }
137
138 int
139 vlib_load_new_plugins (plugin_main_t * pm, int from_early_init)
140 {
141   DIR *dp;
142   struct dirent *entry;
143   struct stat statb;
144   uword *p;
145   plugin_info_t *pi;
146   u8 **plugin_path;
147   int i;
148
149   plugin_path = split_plugin_path (pm);
150
151   for (i = 0; i < vec_len (plugin_path); i++)
152     {
153       dp = opendir ((char *) plugin_path[i]);
154
155       if (dp == 0)
156         continue;
157
158       while ((entry = readdir (dp)))
159         {
160           u8 *plugin_name;
161           u8 *filename;
162
163           if (pm->plugin_name_filter)
164             {
165               int j;
166               for (j = 0; j < vec_len (pm->plugin_name_filter); j++)
167                 if (entry->d_name[j] != pm->plugin_name_filter[j])
168                   goto next;
169             }
170
171           filename = format (0, "%s/%s%c", plugin_path[i], entry->d_name, 0);
172
173           /* Only accept .so */
174           char *ext = strrchr ((const char *) filename, '.');
175           /* unreadable */
176           if (!ext || (strcmp (ext, ".so") != 0) ||
177               stat ((char *) filename, &statb) < 0)
178             {
179             ignore:
180               vec_free (filename);
181               continue;
182             }
183
184           /* a dir or other things which aren't plugins */
185           if (!S_ISREG (statb.st_mode))
186             goto ignore;
187
188           plugin_name = format (0, "%s%c", entry->d_name, 0);
189           p = hash_get_mem (pm->plugin_by_name_hash, plugin_name);
190           if (p == 0)
191             {
192               vec_add2 (pm->plugin_info, pi, 1);
193               pi->name = plugin_name;
194               pi->filename = filename;
195               pi->file_info = statb;
196
197               if (load_one_plugin (pm, pi, from_early_init))
198                 {
199                   vec_free (plugin_name);
200                   vec_free (filename);
201                   _vec_len (pm->plugin_info) = vec_len (pm->plugin_info) - 1;
202                   memset (pi, 0, sizeof (*pi));
203                   continue;
204                 }
205               hash_set_mem (pm->plugin_by_name_hash, plugin_name,
206                             pi - pm->plugin_info);
207             }
208         next:
209           ;
210         }
211       closedir (dp);
212       vec_free (plugin_path[i]);
213     }
214   vec_free (plugin_path);
215   return 0;
216 }
217
218 char *vlib_plugin_path __attribute__ ((weak));
219 char *vlib_plugin_path = "";
220 char *vlib_plugin_name_filter __attribute__ ((weak));
221 char *vlib_plugin_name_filter = 0;
222
223 int
224 vlib_plugin_early_init (vlib_main_t * vm)
225 {
226   plugin_main_t *pm = &vlib_plugin_main;
227
228   pm->plugin_path = format (0, "%s%c", vlib_plugin_path, 0);
229
230   clib_warning ("plugin path %s", pm->plugin_path);
231
232   if (vlib_plugin_name_filter)
233     pm->plugin_name_filter = format (0, "%s%c", vlib_plugin_name_filter, 0);
234
235   pm->plugin_by_name_hash = hash_create_string (0, sizeof (uword));
236   pm->vlib_main = vm;
237
238   return vlib_load_new_plugins (pm, 1 /* from_early_init */ );
239 }
240
241 static clib_error_t *
242 vlib_plugins_show_cmd_fn (vlib_main_t * vm,
243                           unformat_input_t * input, vlib_cli_command_t * cmd)
244 {
245   plugin_main_t *pm = &vlib_plugin_main;
246   u8 *s = 0;
247   u8 *key = 0;
248   uword *value = 0;
249   int index = 1;
250
251   s = format (s, " Plugin path is: %s\n", pm->plugin_path);
252   if (vlib_plugin_name_filter)
253     s = format (s, " Plugin filter: %s\n", vlib_plugin_name_filter);
254
255   s = format (s, " Plugins loaded: \n");
256   hash_foreach_mem (key, value, pm->plugin_by_name_hash,
257                     {
258                     if (key != 0)
259                     s = format (s, "  %d.%s\n", index, key); index++;}
260   );
261
262   vlib_cli_output (vm, "%v", s);
263   vec_free (s);
264   return 0;
265 }
266
267 /* *INDENT-OFF* */
268 VLIB_CLI_COMMAND (plugins_show_cmd, static) =
269 {
270   .path = "show plugins",
271   .short_help = "show loaded plugins",
272   .function = vlib_plugins_show_cmd_fn,
273 };
274 /* *INDENT-ON* */
275
276 /*
277  * fd.io coding-style-patch-verification: ON
278  *
279  * Local Variables:
280  * eval: (c-set-style "gnu")
281  * End:
282  */