crypto crypto-openssl: support hashing operations
[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, RTLD_LAZY);
308
309   if (handle == 0)
310     {
311       PLUGIN_LOG_ERR ("%s", dlerror ());
312       PLUGIN_LOG_ERR ("Failed to load plugin '%s'", pi->name);
313       goto error;
314     }
315
316   pi->handle = handle;
317
318   if (reread_reg)
319     reg = dlsym (pi->handle, "vlib_plugin_registration");
320
321   pi->reg = reg;
322   pi->version = str_array_to_vec ((char *) &reg->version,
323                                   sizeof (reg->version));
324
325   if (reg->early_init)
326     {
327       clib_error_t *(*ei) (vlib_main_t *);
328       void *h;
329
330       h = dlsym (pi->handle, reg->early_init);
331       if (h)
332         {
333           ei = h;
334           error = (*ei) (pm->vlib_main);
335           if (error)
336             {
337               u8 *err = format (0, "%s: %U%c", pi->name,
338                                 format_clib_error, error, 0);
339               PLUGIN_LOG_ERR ((char *) err);
340               clib_error_free (error);
341               dlclose (pi->handle);
342               pi->handle = 0;
343               goto error;
344             }
345         }
346       else
347         PLUGIN_LOG_ERR ("Plugin %s: early init function %s set but not found",
348                         (char *) pi->name, reg->early_init);
349     }
350
351   if (reg->description)
352     PLUGIN_LOG_NOTICE ("Loaded plugin: %s (%s)", pi->name, reg->description);
353   else
354     PLUGIN_LOG_NOTICE ("Loaded plugin: %s", pi->name);
355
356   vec_free (data);
357   elf_main_free (&em);
358   return 0;
359
360 error:
361   vec_free (data);
362   elf_main_free (&em);
363   return -1;
364 }
365
366 static u8 **
367 split_plugin_path (plugin_main_t * pm)
368 {
369   int i;
370   u8 **rv = 0;
371   u8 *path = pm->plugin_path;
372   u8 *this = 0;
373
374   for (i = 0; i < vec_len (pm->plugin_path); i++)
375     {
376       if (path[i] != ':')
377         {
378           vec_add1 (this, path[i]);
379           continue;
380         }
381       vec_add1 (this, 0);
382       vec_add1 (rv, this);
383       this = 0;
384     }
385   if (this)
386     {
387       vec_add1 (this, 0);
388       vec_add1 (rv, this);
389     }
390   return rv;
391 }
392
393 static int
394 plugin_name_sort_cmp (void *a1, void *a2)
395 {
396   plugin_info_t *p1 = a1;
397   plugin_info_t *p2 = a2;
398
399   return strcmp ((char *) p1->name, (char *) p2->name);
400 }
401
402 static int
403 index_cmp (void *a1, void *a2)
404 {
405   uword *i1 = (uword *) a1, *i2 = (uword *) a2;
406
407   if (*i1 < *i2)
408     return -1;
409   else if (*i1 > *i2)
410     return 1;
411   else
412     return 0;
413 }
414
415 int
416 vlib_load_new_plugins (plugin_main_t * pm, int from_early_init)
417 {
418   DIR *dp;
419   struct dirent *entry;
420   struct stat statb;
421   uword *p;
422   plugin_info_t *pi;
423   u8 **plugin_path;
424   uword *not_loaded_indices = 0;
425   int i;
426
427   plugin_path = split_plugin_path (pm);
428
429   for (i = 0; i < vec_len (plugin_path); i++)
430     {
431       dp = opendir ((char *) plugin_path[i]);
432
433       if (dp == 0)
434         continue;
435
436       while ((entry = readdir (dp)))
437         {
438           u8 *plugin_name;
439           u8 *filename;
440
441           if (pm->plugin_name_filter)
442             {
443               int j;
444               for (j = 0; j < vec_len (pm->plugin_name_filter); j++)
445                 if (entry->d_name[j] != pm->plugin_name_filter[j])
446                   goto next;
447             }
448
449           filename = format (0, "%s/%s%c", plugin_path[i], entry->d_name, 0);
450
451           /* Only accept .so */
452           char *ext = strrchr ((const char *) filename, '.');
453           /* unreadable */
454           if (!ext || (strcmp (ext, ".so") != 0) ||
455               stat ((char *) filename, &statb) < 0)
456             {
457             ignore:
458               vec_free (filename);
459               continue;
460             }
461
462           /* a dir or other things which aren't plugins */
463           if (!S_ISREG (statb.st_mode))
464             goto ignore;
465
466           plugin_name = format (0, "%s%c", entry->d_name, 0);
467           /* Have we seen this plugin already? */
468           p = hash_get_mem (pm->plugin_by_name_hash, plugin_name);
469           if (p == 0)
470             {
471               /* No, add it to the plugin vector */
472               vec_add2 (pm->plugin_info, pi, 1);
473               pi->name = plugin_name;
474               pi->filename = filename;
475               pi->file_info = statb;
476               pi->handle = 0;
477               hash_set_mem (pm->plugin_by_name_hash, plugin_name,
478                             pi - pm->plugin_info);
479             }
480         next:
481           ;
482         }
483       closedir (dp);
484       vec_free (plugin_path[i]);
485     }
486   vec_free (plugin_path);
487
488
489   /*
490    * Sort the plugins by name. This is important.
491    * API traces contain absolute message numbers.
492    * Loading plugins in directory (vs. alphabetical) order
493    * makes trace replay incredibly fragile.
494    */
495   vec_sort_with_function (pm->plugin_info, plugin_name_sort_cmp);
496
497   /*
498    * Attempt to load the plugins
499    */
500   for (i = 0; i < vec_len (pm->plugin_info); i++)
501     {
502       pi = vec_elt_at_index (pm->plugin_info, i);
503
504       if (load_one_plugin (pm, pi, from_early_init))
505         {
506           /* Make a note of any which fail to load */
507           vec_add1 (not_loaded_indices, i);
508         }
509     }
510
511   /*
512    * Honor override list
513    */
514   for (i = 0; i < vec_len (pm->plugin_info); i++)
515     {
516       uword *p;
517
518       pi = vec_elt_at_index (pm->plugin_info, i);
519
520       p = hash_get_mem (pm->plugin_overrides_by_name_hash, pi->name);
521
522       /* Plugin overridden? */
523       if (p)
524         {
525           PLUGIN_LOG_NOTICE ("Plugin '%s' overridden by '%s'", pi->name,
526                              p[0]);
527           vec_add1 (not_loaded_indices, i);
528         }
529     }
530
531   /*
532    * Sort the vector of indices to delete to avoid screwing up
533    * the indices as we delete them.
534    */
535   vec_sort_with_function (not_loaded_indices, index_cmp);
536
537   /*
538    * Remove duplicates, which can happen if a plugin is
539    * disabled from the command line and disabled by
540    * a plugin which is loaded.
541    */
542   for (i = 0; i < vec_len (not_loaded_indices); i++)
543     {
544       if (i < vec_len (not_loaded_indices) - 1)
545         {
546           if (not_loaded_indices[i + 1] == not_loaded_indices[i])
547             {
548               vec_delete (not_loaded_indices, 1, i);
549               i--;
550             }
551         }
552     }
553
554   /* Remove plugin info vector elements corresponding to load failures */
555   if (vec_len (not_loaded_indices) > 0)
556     {
557       for (i = vec_len (not_loaded_indices) - 1; i >= 0; i--)
558         {
559           pi = vec_elt_at_index (pm->plugin_info, not_loaded_indices[i]);
560           hash_unset_mem (pm->plugin_by_name_hash, pi->name);
561           if (pi->handle)
562             {
563               dlclose (pi->handle);
564               PLUGIN_LOG_NOTICE ("Unloaded plugin: %s", pi->name);
565             }
566           vec_free (pi->name);
567           vec_free (pi->filename);
568           vec_delete (pm->plugin_info, 1, not_loaded_indices[i]);
569         }
570       vec_free (not_loaded_indices);
571     }
572
573   /* Recreate the plugin name hash */
574   hash_free (pm->plugin_by_name_hash);
575   pm->plugin_by_name_hash = hash_create_string (0, sizeof (uword));
576
577   for (i = 0; i < vec_len (pm->plugin_info); i++)
578     {
579       pi = vec_elt_at_index (pm->plugin_info, i);
580       hash_set_mem (pm->plugin_by_name_hash, pi->name, pi - pm->plugin_info);
581     }
582
583   return 0;
584 }
585
586 int
587 vlib_plugin_early_init (vlib_main_t * vm)
588 {
589   plugin_main_t *pm = &vlib_plugin_main;
590
591   pm->logger =
592     vlib_log_register_class_rate_limit ("plugin", "load",
593                                         0x7FFFFFFF /* aka no rate limit */ );
594
595   if (pm->plugin_path == 0)
596     pm->plugin_path = format (0, "%s%c", vlib_plugin_path, 0);
597
598   PLUGIN_LOG_DBG ("plugin path %s", pm->plugin_path);
599
600   pm->plugin_by_name_hash = hash_create_string (0, sizeof (uword));
601   pm->plugin_overrides_by_name_hash = hash_create_string (0, sizeof (uword));
602   pm->vlib_main = vm;
603
604   return vlib_load_new_plugins (pm, 1 /* from_early_init */ );
605 }
606
607 u8 *
608 vlib_get_vat_plugin_path (void)
609 {
610   plugin_main_t *pm = &vlib_plugin_main;
611   return (pm->vat_plugin_path);
612 }
613
614 u8 *
615 vlib_get_vat_plugin_name_filter (void)
616 {
617   plugin_main_t *pm = &vlib_plugin_main;
618   return (pm->vat_plugin_name_filter);
619 }
620
621 static clib_error_t *
622 vlib_plugins_show_cmd_fn (vlib_main_t * vm,
623                           unformat_input_t * input, vlib_cli_command_t * cmd)
624 {
625   plugin_main_t *pm = &vlib_plugin_main;
626   u8 *s = 0;
627   u8 *key = 0;
628   uword value = 0;
629   int index = 1;
630   plugin_info_t *pi;
631
632   s = format (s, " Plugin path is: %s\n\n", pm->plugin_path);
633   s = format (s, "     %-41s%-33s%s\n", "Plugin", "Version", "Description");
634
635   /* *INDENT-OFF* */
636   hash_foreach_mem (key, value, pm->plugin_by_name_hash,
637     {
638       if (key != 0)
639         {
640           pi = vec_elt_at_index (pm->plugin_info, value);
641           s = format (s, "%3d. %-40s %-32s %s\n", index, key, pi->version,
642                       (pi->reg && pi->reg->description) ?
643                       pi->reg->description : "");
644           index++;
645         }
646     });
647   /* *INDENT-ON* */
648
649   vlib_cli_output (vm, "%v", s);
650   vec_free (s);
651   return 0;
652 }
653
654 /* *INDENT-OFF* */
655 VLIB_CLI_COMMAND (plugins_show_cmd, static) =
656 {
657   .path = "show plugins",
658   .short_help = "show loaded plugins",
659   .function = vlib_plugins_show_cmd_fn,
660 };
661 /* *INDENT-ON* */
662
663 static clib_error_t *
664 config_one_plugin (vlib_main_t * vm, char *name, unformat_input_t * input)
665 {
666   plugin_main_t *pm = &vlib_plugin_main;
667   plugin_config_t *pc;
668   clib_error_t *error = 0;
669   uword *p;
670   int is_enable = 0;
671   int is_disable = 0;
672   int skip_version_check = 0;
673
674   if (pm->config_index_by_name == 0)
675     pm->config_index_by_name = hash_create_string (0, sizeof (uword));
676
677   p = hash_get_mem (pm->config_index_by_name, name);
678
679   if (p)
680     {
681       error = clib_error_return (0, "plugin '%s' already configured", name);
682       goto done;
683     }
684
685   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
686     {
687       if (unformat (input, "enable"))
688         is_enable = 1;
689       else if (unformat (input, "disable"))
690         is_disable = 1;
691       else if (unformat (input, "skip-version-check"))
692         skip_version_check = 1;
693       else
694         {
695           error = clib_error_return (0, "unknown input '%U'",
696                                      format_unformat_error, input);
697           goto done;
698         }
699     }
700
701   if (is_enable && is_disable)
702     {
703       error = clib_error_return (0, "please specify either enable or disable"
704                                  " for plugin '%s'", name);
705       goto done;
706     }
707
708   vec_add2 (pm->configs, pc, 1);
709   hash_set_mem (pm->config_index_by_name, name, pc - pm->configs);
710   pc->is_enabled = is_enable;
711   pc->is_disabled = is_disable;
712   pc->skip_version_check = skip_version_check;
713   pc->name = name;
714
715 done:
716   return error;
717 }
718
719 clib_error_t *
720 vlib_plugin_config (vlib_main_t * vm, unformat_input_t * input)
721 {
722   plugin_main_t *pm = &vlib_plugin_main;
723   clib_error_t *error = 0;
724   unformat_input_t in;
725
726   unformat_init (&in, 0, 0);
727
728   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
729     {
730       u8 *s, *v;
731       if (unformat (input, "%s %v", &s, &v))
732         {
733           if (strncmp ((const char *) s, "plugins", 8) == 0)
734             {
735               if (vec_len (in.buffer) > 0)
736                 vec_add1 (in.buffer, ' ');
737               vec_add (in.buffer, v, vec_len (v));
738             }
739         }
740       else
741         {
742           error = clib_error_return (0, "unknown input '%U'",
743                                      format_unformat_error, input);
744           goto done;
745         }
746
747       vec_free (v);
748       vec_free (s);
749     }
750 done:
751   input = &in;
752   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
753     {
754       unformat_input_t sub_input;
755       u8 *s = 0;
756       if (unformat (input, "path %s", &s))
757         pm->plugin_path = s;
758       else if (unformat (input, "name-filter %s", &s))
759         pm->plugin_name_filter = s;
760       else if (unformat (input, "vat-path %s", &s))
761         pm->vat_plugin_path = s;
762       else if (unformat (input, "vat-name-filter %s", &s))
763         pm->vat_plugin_name_filter = s;
764       else if (unformat (input, "plugin default %U",
765                          unformat_vlib_cli_sub_input, &sub_input))
766         {
767           pm->plugins_default_disable =
768             unformat (&sub_input, "disable") ? 1 : 0;
769           unformat_free (&sub_input);
770         }
771       else if (unformat (input, "plugin %s %U", &s,
772                          unformat_vlib_cli_sub_input, &sub_input))
773         {
774           error = config_one_plugin (vm, (char *) s, &sub_input);
775           unformat_free (&sub_input);
776           if (error)
777             goto done2;
778         }
779       else
780         {
781           error = clib_error_return (0, "unknown input '%U'",
782                                      format_unformat_error, input);
783           {
784             vec_free (s);
785             goto done2;
786           }
787         }
788     }
789
790 done2:
791   unformat_free (&in);
792   return error;
793 }
794
795 /* discard whole 'plugins' section, as it is already consumed prior to
796    plugin load */
797 static clib_error_t *
798 plugins_config (vlib_main_t * vm, unformat_input_t * input)
799 {
800   u8 *junk;
801
802   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
803     {
804       if (unformat (input, "%s", &junk))
805         {
806           vec_free (junk);
807           return 0;
808         }
809       else
810         return clib_error_return (0, "unknown input '%U'",
811                                   format_unformat_error, input);
812     }
813   return 0;
814 }
815
816 VLIB_CONFIG_FUNCTION (plugins_config, "plugins");
817
818 /*
819  * fd.io coding-style-patch-verification: ON
820  *
821  * Local Variables:
822  * eval: (c-set-style "gnu")
823  * End:
824  */