tests: fix coverity warnings
[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 <vppinfra/elf.h>
20 #include <dlfcn.h>
21 #include <dirent.h>
22
23 plugin_main_t vlib_plugin_main;
24
25 #define PLUGIN_LOG_DBG(...) \
26   do {vlib_log_debug (vlib_plugin_main.logger, __VA_ARGS__);} while(0)
27 #define PLUGIN_LOG_ERR(...) \
28   do {vlib_log_err (vlib_plugin_main.logger, __VA_ARGS__);} while(0)
29 #define PLUGIN_LOG_NOTICE(...) \
30   do {vlib_log_notice (vlib_plugin_main.logger, __VA_ARGS__);} while(0)
31
32 char *vlib_plugin_path __attribute__ ((weak));
33 char *vlib_plugin_path = "";
34 char *vlib_plugin_app_version __attribute__ ((weak));
35 char *vlib_plugin_app_version = "";
36
37 void *
38 vlib_get_plugin_symbol (char *plugin_name, char *symbol_name)
39 {
40   plugin_main_t *pm = &vlib_plugin_main;
41   uword *p;
42   plugin_info_t *pi;
43
44   if ((p = hash_get_mem (pm->plugin_by_name_hash, plugin_name)) == 0)
45     return 0;
46
47   pi = vec_elt_at_index (pm->plugin_info, p[0]);
48   return dlsym (pi->handle, symbol_name);
49 }
50
51 static char *
52 str_array_to_vec (char *array, int len)
53 {
54   char c, *r = 0;
55   int n = 0;
56
57   do
58     {
59       c = array[n];
60       vec_add1 (r, c);
61     }
62   while (c && ++n < len);
63
64   if (c)
65     vec_add1 (r, 0);
66
67   return r;
68 }
69
70 static u8 *
71 extract (u8 * sp, u8 * ep)
72 {
73   u8 *rv = 0;
74
75   while (sp <= ep)
76     {
77       vec_add1 (rv, *sp);
78       sp++;
79     }
80   vec_add1 (rv, 0);
81   return rv;
82 }
83
84 /*
85  * If a plugin .so contains a ".vlib_plugin_r2" section,
86  * this function converts the vlib_plugin_r2_t to
87  * a vlib_plugin_registration_t.
88  */
89
90 static clib_error_t *
91 r2_to_reg (elf_main_t * em, vlib_plugin_r2_t * r2,
92            vlib_plugin_registration_t * reg)
93 {
94   clib_error_t *error;
95   elf_section_t *section;
96   uword data_segment_offset;
97   u8 *data;
98
99   /* It turns out that the strings land in the ".data" section */
100   error = elf_get_section_by_name (em, ".data", &section);
101   if (error)
102     return error;
103   data = elf_get_section_contents (em, section->index, 1);
104
105   /*
106    * Offsets in the ".vlib_plugin_r2" section
107    * need to have the data section base subtracted from them.
108    * The offset is in the first 8 bytes of the ".data" section
109    */
110
111   data_segment_offset = *((uword *) data);
112
113   /* Relocate pointers, subtract data_segment_offset */
114 #define _(a) r2->a.data_segment_offset -= data_segment_offset;
115   foreach_r2_string_field;
116 #undef _
117
118   if (r2->version.length >= ARRAY_LEN (reg->version) - 1)
119     return clib_error_return (0, "Version string too long");
120
121   if (r2->version_required.length >= ARRAY_LEN (reg->version_required) - 1)
122     return clib_error_return (0, "Version-required string too long");
123
124   if (r2->overrides.length >= ARRAY_LEN (reg->overrides) - 1)
125     return clib_error_return (0, "Override string too long");
126
127   /* Compatibility with C-initializer */
128   memcpy ((void *) reg->version, data + r2->version.data_segment_offset,
129           r2->version.length);
130   memcpy ((void *) reg->version_required,
131           data + r2->version_required.data_segment_offset,
132           r2->version_required.length);
133   memcpy ((void *) reg->overrides, data + r2->overrides.data_segment_offset,
134           r2->overrides.length);
135
136   if (r2->early_init.length > 0)
137     {
138       u8 *ei = 0;
139       vec_validate (ei, r2->early_init.length + 1);
140       memcpy (ei, data + r2->early_init.data_segment_offset,
141               r2->early_init.length);
142       reg->early_init = (void *) ei;
143     }
144
145   if (r2->description.length > 0)
146     {
147       u8 *desc = 0;
148       vec_validate (desc, r2->description.length + 1);
149       memcpy (desc, data + r2->description.data_segment_offset,
150               r2->description.length);
151       reg->description = (void *) desc;
152     }
153   vec_free (data);
154   return 0;
155 }
156
157
158 static int
159 load_one_plugin (plugin_main_t * pm, plugin_info_t * pi, int from_early_init)
160 {
161   void *handle;
162   int reread_reg = 1;
163   clib_error_t *error;
164   elf_main_t em = { 0 };
165   elf_section_t *section;
166   u8 *data;
167   char *version_required;
168   vlib_plugin_registration_t *reg;
169   vlib_plugin_r2_t *r2;
170   plugin_config_t *pc = 0;
171   uword *p;
172
173   if (elf_read_file (&em, (char *) pi->filename))
174     return -1;
175
176   /* New / improved (well, not really) registration structure? */
177   error = elf_get_section_by_name (&em, ".vlib_plugin_r2", &section);
178   if (error == 0)
179     {
180       data = elf_get_section_contents (&em, section->index, 1);
181       r2 = (vlib_plugin_r2_t *) data;
182       reg = clib_mem_alloc (sizeof (*reg));
183       memset (reg, 0, sizeof (*reg));
184
185       reg->default_disabled = r2->default_disabled != 0;
186       error = r2_to_reg (&em, r2, reg);
187       if (error)
188         {
189           PLUGIN_LOG_ERR ("Bad r2 registration: %s\n", (char *) pi->name);
190           return -1;
191         }
192       if (pm->plugins_default_disable)
193         reg->default_disabled = 1;
194       reread_reg = 0;
195       goto process_reg;
196     }
197
198   error = elf_get_section_by_name (&em, ".vlib_plugin_registration",
199                                    &section);
200   if (error)
201     {
202       PLUGIN_LOG_ERR ("Not a plugin: %s\n", (char *) pi->name);
203       return -1;
204     }
205
206   data = elf_get_section_contents (&em, section->index, 1);
207   reg = (vlib_plugin_registration_t *) data;
208
209   if (vec_len (data) != sizeof (*reg))
210     {
211       PLUGIN_LOG_ERR ("vlib_plugin_registration size mismatch in plugin %s\n",
212                       (char *) pi->name);
213       goto error;
214     }
215
216   if (pm->plugins_default_disable)
217     reg->default_disabled = 1;
218
219 process_reg:
220   p = hash_get_mem (pm->config_index_by_name, pi->name);
221   if (p)
222     {
223       pc = vec_elt_at_index (pm->configs, p[0]);
224       if (pc->is_disabled)
225         {
226           PLUGIN_LOG_NOTICE ("Plugin disabled: %s", pi->name);
227           goto error;
228         }
229       if (reg->default_disabled && pc->is_enabled == 0)
230         {
231           PLUGIN_LOG_NOTICE ("Plugin disabled (default): %s", pi->name);
232           goto error;
233         }
234     }
235   else if (reg->default_disabled)
236     {
237       PLUGIN_LOG_NOTICE ("Plugin disabled (default): %s", pi->name);
238       goto error;
239     }
240
241   version_required = str_array_to_vec ((char *) &reg->version_required,
242                                        sizeof (reg->version_required));
243
244   if ((strlen (version_required) > 0) &&
245       (strncmp (vlib_plugin_app_version, version_required,
246                 strlen (version_required))))
247     {
248       PLUGIN_LOG_ERR ("Plugin %s version mismatch: %s != %s",
249                       pi->name, vlib_plugin_app_version,
250                       reg->version_required);
251       if (!(pc && pc->skip_version_check == 1))
252         {
253           vec_free (version_required);
254           goto error;
255         }
256     }
257
258   /*
259    * Collect names of plugins overridden (disabled) by the
260    * current plugin.
261    */
262   if (reg->overrides[0])
263     {
264       const char *overrides = reg->overrides;
265       u8 *override_name_copy, *overridden_by_name_copy;
266       u8 *sp, *ep;
267       uword *p;
268
269       sp = ep = (u8 *) overrides;
270
271       while (1)
272         {
273           if (*sp == 0
274               || (sp >= (u8 *) overrides + ARRAY_LEN (reg->overrides)))
275             break;
276           if (*sp == ' ' || *sp == ',')
277             {
278               sp++;
279               continue;
280             }
281           ep = sp;
282           while (*ep && *ep != ' ' && *ep != ',' &&
283                  ep < (u8 *) overrides + ARRAY_LEN (reg->overrides))
284             ep++;
285           if (*ep == ' ' || *ep == ',')
286             ep--;
287
288           override_name_copy = extract (sp, ep);
289
290
291           p = hash_get_mem (pm->plugin_overrides_by_name_hash,
292                             override_name_copy);
293           /* Already overridden... */
294           if (p)
295             vec_free (override_name_copy);
296           else
297             {
298               overridden_by_name_copy = format (0, "%s%c", pi->name, 0);
299               hash_set_mem (pm->plugin_overrides_by_name_hash,
300                             override_name_copy, overridden_by_name_copy);
301             }
302           sp = *ep ? ep + 1 : ep;
303         }
304     }
305   vec_free (version_required);
306
307   handle = dlopen ((char *) pi->filename,
308                    RTLD_LAZY | (reg->deep_bind ? RTLD_DEEPBIND : 0));
309
310   if (handle == 0)
311     {
312       PLUGIN_LOG_ERR ("%s", dlerror ());
313       PLUGIN_LOG_ERR ("Failed to load plugin '%s'", pi->name);
314       goto error;
315     }
316
317   pi->handle = handle;
318
319   if (reread_reg)
320     reg = dlsym (pi->handle, "vlib_plugin_registration");
321
322   pi->reg = reg;
323   pi->version = str_array_to_vec ((char *) &reg->version,
324                                   sizeof (reg->version));
325
326   if (reg->early_init)
327     {
328       clib_error_t *(*ei) (vlib_main_t *);
329       void *h;
330
331       h = dlsym (pi->handle, reg->early_init);
332       if (h)
333         {
334           ei = h;
335           error = (*ei) (pm->vlib_main);
336           if (error)
337             {
338               u8 *err = format (0, "%s: %U%c", pi->name,
339                                 format_clib_error, error, 0);
340               PLUGIN_LOG_ERR ((char *) err);
341               clib_error_free (error);
342               dlclose (pi->handle);
343               pi->handle = 0;
344               goto error;
345             }
346         }
347       else
348         PLUGIN_LOG_ERR ("Plugin %s: early init function %s set but not found",
349                         (char *) pi->name, reg->early_init);
350     }
351
352   if (reg->description)
353     PLUGIN_LOG_NOTICE ("Loaded plugin: %s (%s)", pi->name, reg->description);
354   else
355     PLUGIN_LOG_NOTICE ("Loaded plugin: %s", pi->name);
356
357   vec_free (data);
358   elf_main_free (&em);
359   return 0;
360
361 error:
362   vec_free (data);
363   elf_main_free (&em);
364   return -1;
365 }
366
367 static u8 **
368 split_plugin_path (plugin_main_t * pm)
369 {
370   int i;
371   u8 **rv = 0;
372   u8 *path = pm->plugin_path;
373   u8 *this = 0;
374
375   for (i = 0; i < vec_len (pm->plugin_path); i++)
376     {
377       if (path[i] != ':')
378         {
379           vec_add1 (this, path[i]);
380           continue;
381         }
382       vec_add1 (this, 0);
383       vec_add1 (rv, this);
384       this = 0;
385     }
386   if (this)
387     {
388       vec_add1 (this, 0);
389       vec_add1 (rv, this);
390     }
391   return rv;
392 }
393
394 static int
395 plugin_name_sort_cmp (void *a1, void *a2)
396 {
397   plugin_info_t *p1 = a1;
398   plugin_info_t *p2 = a2;
399
400   return strcmp ((char *) p1->name, (char *) p2->name);
401 }
402
403 static int
404 index_cmp (void *a1, void *a2)
405 {
406   uword *i1 = (uword *) a1, *i2 = (uword *) a2;
407
408   if (*i1 < *i2)
409     return -1;
410   else if (*i1 > *i2)
411     return 1;
412   else
413     return 0;
414 }
415
416 int
417 vlib_load_new_plugins (plugin_main_t * pm, int from_early_init)
418 {
419   DIR *dp;
420   struct dirent *entry;
421   struct stat statb;
422   uword *p;
423   plugin_info_t *pi;
424   u8 **plugin_path;
425   uword *not_loaded_indices = 0;
426   int i;
427
428   plugin_path = split_plugin_path (pm);
429
430   for (i = 0; i < vec_len (plugin_path); i++)
431     {
432       dp = opendir ((char *) plugin_path[i]);
433
434       if (dp == 0)
435         continue;
436
437       while ((entry = readdir (dp)))
438         {
439           u8 *plugin_name;
440           u8 *filename;
441
442           if (pm->plugin_name_filter)
443             {
444               int j;
445               for (j = 0; j < vec_len (pm->plugin_name_filter); j++)
446                 if (entry->d_name[j] != pm->plugin_name_filter[j])
447                   goto next;
448             }
449
450           filename = format (0, "%s/%s%c", plugin_path[i], entry->d_name, 0);
451
452           /* Only accept .so */
453           char *ext = strrchr ((const char *) filename, '.');
454           /* unreadable */
455           if (!ext || (strcmp (ext, ".so") != 0) ||
456               stat ((char *) filename, &statb) < 0)
457             {
458             ignore:
459               vec_free (filename);
460               continue;
461             }
462
463           /* a dir or other things which aren't plugins */
464           if (!S_ISREG (statb.st_mode))
465             goto ignore;
466
467           plugin_name = format (0, "%s%c", entry->d_name, 0);
468           /* Have we seen this plugin already? */
469           p = hash_get_mem (pm->plugin_by_name_hash, plugin_name);
470           if (p == 0)
471             {
472               /* No, add it to the plugin vector */
473               vec_add2 (pm->plugin_info, pi, 1);
474               pi->name = plugin_name;
475               pi->filename = filename;
476               pi->file_info = statb;
477               pi->handle = 0;
478               hash_set_mem (pm->plugin_by_name_hash, plugin_name,
479                             pi - pm->plugin_info);
480             }
481         next:
482           ;
483         }
484       closedir (dp);
485       vec_free (plugin_path[i]);
486     }
487   vec_free (plugin_path);
488
489
490   /*
491    * Sort the plugins by name. This is important.
492    * API traces contain absolute message numbers.
493    * Loading plugins in directory (vs. alphabetical) order
494    * makes trace replay incredibly fragile.
495    */
496   vec_sort_with_function (pm->plugin_info, plugin_name_sort_cmp);
497
498   /*
499    * Attempt to load the plugins
500    */
501   for (i = 0; i < vec_len (pm->plugin_info); i++)
502     {
503       pi = vec_elt_at_index (pm->plugin_info, i);
504
505       if (load_one_plugin (pm, pi, from_early_init))
506         {
507           /* Make a note of any which fail to load */
508           vec_add1 (not_loaded_indices, i);
509         }
510     }
511
512   /*
513    * Honor override list
514    */
515   for (i = 0; i < vec_len (pm->plugin_info); i++)
516     {
517       uword *p;
518
519       pi = vec_elt_at_index (pm->plugin_info, i);
520
521       p = hash_get_mem (pm->plugin_overrides_by_name_hash, pi->name);
522
523       /* Plugin overridden? */
524       if (p)
525         {
526           PLUGIN_LOG_NOTICE ("Plugin '%s' overridden by '%s'", pi->name,
527                              p[0]);
528           vec_add1 (not_loaded_indices, i);
529         }
530     }
531
532   /*
533    * Sort the vector of indices to delete to avoid screwing up
534    * the indices as we delete them.
535    */
536   vec_sort_with_function (not_loaded_indices, index_cmp);
537
538   /*
539    * Remove duplicates, which can happen if a plugin is
540    * disabled from the command line and disabled by
541    * a plugin which is loaded.
542    */
543   for (i = 0; i < vec_len (not_loaded_indices); i++)
544     {
545       if (i < vec_len (not_loaded_indices) - 1)
546         {
547           if (not_loaded_indices[i + 1] == not_loaded_indices[i])
548             {
549               vec_delete (not_loaded_indices, 1, i);
550               i--;
551             }
552         }
553     }
554
555   /* Remove plugin info vector elements corresponding to load failures */
556   if (vec_len (not_loaded_indices) > 0)
557     {
558       for (i = vec_len (not_loaded_indices) - 1; i >= 0; i--)
559         {
560           pi = vec_elt_at_index (pm->plugin_info, not_loaded_indices[i]);
561           hash_unset_mem (pm->plugin_by_name_hash, pi->name);
562           if (pi->handle)
563             {
564               dlclose (pi->handle);
565               PLUGIN_LOG_NOTICE ("Unloaded plugin: %s", pi->name);
566             }
567           vec_free (pi->name);
568           vec_free (pi->filename);
569           vec_delete (pm->plugin_info, 1, not_loaded_indices[i]);
570         }
571       vec_free (not_loaded_indices);
572     }
573
574   /* Recreate the plugin name hash */
575   hash_free (pm->plugin_by_name_hash);
576   pm->plugin_by_name_hash = hash_create_string (0, sizeof (uword));
577
578   for (i = 0; i < vec_len (pm->plugin_info); i++)
579     {
580       pi = vec_elt_at_index (pm->plugin_info, i);
581       hash_set_mem (pm->plugin_by_name_hash, pi->name, pi - pm->plugin_info);
582     }
583
584   return 0;
585 }
586
587 int
588 vlib_plugin_early_init (vlib_main_t * vm)
589 {
590   plugin_main_t *pm = &vlib_plugin_main;
591
592   pm->logger =
593     vlib_log_register_class_rate_limit ("plugin", "load",
594                                         0x7FFFFFFF /* aka no rate limit */ );
595
596   if (pm->plugin_path == 0)
597     pm->plugin_path = format (0, "%s", vlib_plugin_path);
598
599   if (pm->plugin_path_add)
600     pm->plugin_path = format (pm->plugin_path, ":%s", pm->plugin_path_add);
601
602   pm->plugin_path = format (pm->plugin_path, "%c", 0);
603
604   PLUGIN_LOG_DBG ("plugin path %s", pm->plugin_path);
605
606   pm->plugin_by_name_hash = hash_create_string (0, sizeof (uword));
607   pm->plugin_overrides_by_name_hash = hash_create_string (0, sizeof (uword));
608   pm->vlib_main = vm;
609
610   return vlib_load_new_plugins (pm, 1 /* from_early_init */ );
611 }
612
613 u8 *
614 vlib_get_vat_plugin_path (void)
615 {
616   plugin_main_t *pm = &vlib_plugin_main;
617   return (pm->vat_plugin_path);
618 }
619
620 u8 *
621 vlib_get_vat_plugin_name_filter (void)
622 {
623   plugin_main_t *pm = &vlib_plugin_main;
624   return (pm->vat_plugin_name_filter);
625 }
626
627 static clib_error_t *
628 vlib_plugins_show_cmd_fn (vlib_main_t * vm,
629                           unformat_input_t * input, vlib_cli_command_t * cmd)
630 {
631   plugin_main_t *pm = &vlib_plugin_main;
632   u8 *s = 0;
633   u8 *key = 0;
634   uword value = 0;
635   int index = 1;
636   plugin_info_t *pi;
637
638   s = format (s, " Plugin path is: %s\n\n", pm->plugin_path);
639   s = format (s, "     %-41s%-33s%s\n", "Plugin", "Version", "Description");
640
641   /* *INDENT-OFF* */
642   hash_foreach_mem (key, value, pm->plugin_by_name_hash,
643     {
644       if (key != 0)
645         {
646           pi = vec_elt_at_index (pm->plugin_info, value);
647           s = format (s, "%3d. %-40s %-32s %s\n", index, key, pi->version,
648                       (pi->reg && pi->reg->description) ?
649                       pi->reg->description : "");
650           index++;
651         }
652     });
653   /* *INDENT-ON* */
654
655   vlib_cli_output (vm, "%v", s);
656   vec_free (s);
657   return 0;
658 }
659
660 /* *INDENT-OFF* */
661 VLIB_CLI_COMMAND (plugins_show_cmd, static) =
662 {
663   .path = "show plugins",
664   .short_help = "show loaded plugins",
665   .function = vlib_plugins_show_cmd_fn,
666 };
667 /* *INDENT-ON* */
668
669 static clib_error_t *
670 config_one_plugin (vlib_main_t * vm, char *name, unformat_input_t * input)
671 {
672   plugin_main_t *pm = &vlib_plugin_main;
673   plugin_config_t *pc;
674   clib_error_t *error = 0;
675   uword *p;
676   int is_enable = 0;
677   int is_disable = 0;
678   int skip_version_check = 0;
679
680   if (pm->config_index_by_name == 0)
681     pm->config_index_by_name = hash_create_string (0, sizeof (uword));
682
683   p = hash_get_mem (pm->config_index_by_name, name);
684
685   if (p)
686     {
687       error = clib_error_return (0, "plugin '%s' already configured", name);
688       goto done;
689     }
690
691   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
692     {
693       if (unformat (input, "enable"))
694         is_enable = 1;
695       else if (unformat (input, "disable"))
696         is_disable = 1;
697       else if (unformat (input, "skip-version-check"))
698         skip_version_check = 1;
699       else
700         {
701           error = clib_error_return (0, "unknown input '%U'",
702                                      format_unformat_error, input);
703           goto done;
704         }
705     }
706
707   if (is_enable && is_disable)
708     {
709       error = clib_error_return (0, "please specify either enable or disable"
710                                  " for plugin '%s'", name);
711       goto done;
712     }
713
714   vec_add2 (pm->configs, pc, 1);
715   hash_set_mem (pm->config_index_by_name, name, pc - pm->configs);
716   pc->is_enabled = is_enable;
717   pc->is_disabled = is_disable;
718   pc->skip_version_check = skip_version_check;
719   pc->name = name;
720
721 done:
722   return error;
723 }
724
725 clib_error_t *
726 vlib_plugin_config (vlib_main_t * vm, unformat_input_t * input)
727 {
728   plugin_main_t *pm = &vlib_plugin_main;
729   clib_error_t *error = 0;
730   unformat_input_t in;
731
732   unformat_init (&in, 0, 0);
733
734   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
735     {
736       u8 *s, *v;
737       if (unformat (input, "%s %v", &s, &v))
738         {
739           if (strncmp ((const char *) s, "plugins", 8) == 0)
740             {
741               if (vec_len (in.buffer) > 0)
742                 vec_add1 (in.buffer, ' ');
743               vec_add (in.buffer, v, vec_len (v));
744             }
745         }
746       else
747         {
748           error = clib_error_return (0, "unknown input '%U'",
749                                      format_unformat_error, input);
750           goto done;
751         }
752
753       vec_free (v);
754       vec_free (s);
755     }
756 done:
757   input = &in;
758   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
759     {
760       unformat_input_t sub_input;
761       u8 *s = 0;
762       if (unformat (input, "path %s", &s))
763         pm->plugin_path = s;
764       else if (unformat (input, "add-path %s", &s))
765         pm->plugin_path_add = s;
766       else if (unformat (input, "name-filter %s", &s))
767         pm->plugin_name_filter = s;
768       else if (unformat (input, "vat-path %s", &s))
769         pm->vat_plugin_path = s;
770       else if (unformat (input, "vat-name-filter %s", &s))
771         pm->vat_plugin_name_filter = s;
772       else if (unformat (input, "plugin default %U",
773                          unformat_vlib_cli_sub_input, &sub_input))
774         {
775           pm->plugins_default_disable =
776             unformat (&sub_input, "disable") ? 1 : 0;
777           unformat_free (&sub_input);
778         }
779       else if (unformat (input, "plugin %s %U", &s,
780                          unformat_vlib_cli_sub_input, &sub_input))
781         {
782           error = config_one_plugin (vm, (char *) s, &sub_input);
783           unformat_free (&sub_input);
784           if (error)
785             goto done2;
786         }
787       else
788         {
789           error = clib_error_return (0, "unknown input '%U'",
790                                      format_unformat_error, input);
791           {
792             vec_free (s);
793             goto done2;
794           }
795         }
796     }
797
798 done2:
799   unformat_free (&in);
800   return error;
801 }
802
803 /* discard whole 'plugins' section, as it is already consumed prior to
804    plugin load */
805 static clib_error_t *
806 plugins_config (vlib_main_t * vm, unformat_input_t * input)
807 {
808   u8 *junk;
809
810   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
811     {
812       if (unformat (input, "%s", &junk))
813         {
814           vec_free (junk);
815           return 0;
816         }
817       else
818         return clib_error_return (0, "unknown input '%U'",
819                                   format_unformat_error, input);
820     }
821   return 0;
822 }
823
824 VLIB_CONFIG_FUNCTION (plugins_config, "plugins");
825
826 /*
827  * fd.io coding-style-patch-verification: ON
828  *
829  * Local Variables:
830  * eval: (c-set-style "gnu")
831  * End:
832  */