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