http_static: sanitize path before file read
[vpp.git] / src / vlib / init.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  * init.c: mechanism for functions to be called at init/exit.
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #include <vlib/vlib.h>
41 #include <vppinfra/ptclosure.h>
42
43 /**
44  * @file
45  * @brief Init function ordering and execution implementation
46  * Topological sort for all classes of init functions, and
47  * a relatively simple API routine to invoke them.
48  */
49
50 /*? %%clicmd:group_label Init functions %% ?*/
51
52 static int
53 comma_split (u8 * s, u8 ** a, u8 ** b)
54 {
55   *a = s;
56
57   while (*s && *s != ',')
58     s++;
59
60   if (*s == ',')
61     *s = 0;
62   else
63     return 1;
64
65   *b = (u8 *) (s + 1);
66   return 0;
67 }
68
69 /**
70  * @brief Topological sorter for init function chains.
71  * @param head [in/out] address of the listhead to be sorted
72  * @returns 0 on success, otherwise a clib_error_t *.
73  */
74
75 clib_error_t *vlib_sort_init_exit_functions
76   (_vlib_init_function_list_elt_t ** head)
77 {
78   uword *index_by_name;
79   uword *reg_by_index;
80   u8 **init_f_names = 0;
81   u8 *init_f_name;
82   char **these_constraints;
83   char *this_constraint_c;
84   u8 **constraints = 0;
85   u8 *constraint_tuple;
86   u8 *this_constraint;
87   char *prev_name;
88   u8 **orig, **closure;
89   uword *p;
90   int i, j, k;
91   u8 *a_name, *b_name;
92   int a_index, b_index;
93   int n_init_fns;
94   u32 *result = 0;
95   _vlib_init_function_list_elt_t *this_reg = 0;
96   hash_pair_t *hp;
97   u8 **keys_to_delete = 0;
98
99   /*
100    * two hash tables: name to index in init_f_names, and
101    * init function registration pointer by index
102    */
103   index_by_name = hash_create_string (0, sizeof (uword));
104   reg_by_index = hash_create (0, sizeof (uword));
105
106   this_reg = *head;
107
108   /* pass 1, collect init fcn names, construct a before b pairs */
109   while (this_reg)
110     {
111       init_f_name = format (0, "%s%c", this_reg->name, 0);
112       hash_set (reg_by_index, vec_len (init_f_names), (uword) this_reg);
113
114       hash_set_mem (index_by_name, init_f_name, vec_len (init_f_names));
115
116       vec_add1 (init_f_names, init_f_name);
117
118       these_constraints = this_reg->runs_before;
119       while (these_constraints && these_constraints[0])
120         {
121           this_constraint_c = these_constraints[0];
122
123           constraint_tuple = format (0, "%s,%s%c", init_f_name,
124                                      this_constraint_c, 0);
125           vec_add1 (constraints, constraint_tuple);
126           these_constraints++;
127         }
128
129       these_constraints = this_reg->runs_after;
130       while (these_constraints && these_constraints[0])
131         {
132           this_constraint_c = these_constraints[0];
133
134           constraint_tuple = format (0, "%s,%s%c",
135                                      this_constraint_c, init_f_name, 0);
136           vec_add1 (constraints, constraint_tuple);
137           these_constraints++;
138         }
139
140       this_reg = this_reg->next_init_function;
141     }
142
143   /*
144    * pass 2: collect "a then b then c then d" constraints.
145    * all init fcns must be known at this point.
146    */
147   this_reg = *head;
148   while (this_reg)
149     {
150       these_constraints = this_reg->init_order;
151
152       prev_name = 0;
153       /* Across the list of constraints */
154       while (these_constraints && these_constraints[0])
155         {
156           this_constraint_c = these_constraints[0];
157           p = hash_get_mem (index_by_name, this_constraint_c);
158           if (p == 0)
159             {
160               clib_warning
161                 ("order constraint fcn '%s' not found", this_constraint_c);
162               these_constraints++;
163               continue;
164             }
165
166           if (prev_name == 0)
167             {
168               prev_name = this_constraint_c;
169               these_constraints++;
170               continue;
171             }
172
173           constraint_tuple = format (0, "%s,%s%c", prev_name,
174                                      this_constraint_c, 0);
175           vec_add1 (constraints, constraint_tuple);
176           prev_name = this_constraint_c;
177           these_constraints++;
178         }
179       this_reg = this_reg->next_init_function;
180     }
181
182   n_init_fns = vec_len (init_f_names);
183   orig = clib_ptclosure_alloc (n_init_fns);
184
185   for (i = 0; i < vec_len (constraints); i++)
186     {
187       this_constraint = constraints[i];
188
189       if (comma_split (this_constraint, &a_name, &b_name))
190         return clib_error_return (0, "comma_split failed!");
191
192       p = hash_get_mem (index_by_name, a_name);
193       /*
194        * Note: the next two errors mean that something is
195        * b0rked. As in: if you code "A runs before on B," and you type
196        * B incorrectly, you lose. Nonexistent init functions are tolerated.
197        */
198       if (p == 0)
199         {
200           clib_warning ("init function '%s' not found (before '%s')",
201                         a_name, b_name);
202           continue;
203         }
204       a_index = p[0];
205
206       p = hash_get_mem (index_by_name, b_name);
207       if (p == 0)
208         {
209           clib_warning ("init function '%s' not found (after '%s')",
210                         b_name, a_name);
211           continue;
212         }
213       b_index = p[0];
214
215       /* add a before b to the original set of constraints */
216       orig[a_index][b_index] = 1;
217       vec_free (this_constraint);
218     }
219
220   /* Compute the positive transitive closure of the original constraints */
221   closure = clib_ptclosure (orig);
222
223   /* Compute a partial order across feature nodes, if one exists. */
224 again:
225   for (i = 0; i < n_init_fns; i++)
226     {
227       for (j = 0; j < n_init_fns; j++)
228         {
229           if (closure[i][j])
230             goto item_constrained;
231         }
232       /* Item i can be output */
233       vec_add1 (result, i);
234       {
235         for (k = 0; k < n_init_fns; k++)
236           closure[k][i] = 0;
237         /*
238          * Add a "Magic" a before a constraint.
239          * This means we'll never output it again
240          */
241         closure[i][i] = 1;
242         goto again;
243       }
244     item_constrained:
245       ;
246     }
247
248   /* see if we got a partial order... */
249   if (vec_len (result) != n_init_fns)
250     return clib_error_return
251       (0, "Failed to find a suitable init function order!");
252
253   /*
254    * We win.
255    * Bind the index variables, and output the feature node name vector
256    * using the partial order we just computed. Result is in stack
257    * order, because the entry with the fewest constraints (e.g. none)
258    * is output first, etc.
259    * Reset the listhead, and add items in result (aka reverse) order.
260    */
261   *head = 0;
262   for (i = 0; i < n_init_fns; i++)
263     {
264       p = hash_get (reg_by_index, result[i]);
265       ASSERT (p != 0);
266       this_reg = (_vlib_init_function_list_elt_t *) p[0];
267
268       this_reg->next_init_function = *head;
269       *head = this_reg;
270     }
271
272   /* Finally, clean up all the fine data we allocated */
273   hash_foreach_pair (hp, index_by_name,
274   ({
275     vec_add1 (keys_to_delete, (u8 *)hp->key);
276   }));
277   hash_free (index_by_name);
278   for (i = 0; i < vec_len (keys_to_delete); i++)
279     vec_free (keys_to_delete[i]);
280   vec_free (keys_to_delete);
281   hash_free (reg_by_index);
282   vec_free (result);
283   clib_ptclosure_free (orig);
284   clib_ptclosure_free (closure);
285   return 0;
286 }
287
288 /**
289  * @brief call a set of init / exit / main-loop enter functions
290  * @param vm vlib_main_t
291  * @param head address of the listhead to sort and then invoke
292  * @returns 0 on success, clib_error_t * on error
293  *
294  * The "init_functions_called" hash supports a subtle mix of procedural
295  * and formally-specified ordering constraints. The following schemes
296  * are *roughly* equivalent:
297  *
298  * static clib_error_t *init_runs_first (vlib_main_t *vm)
299  * {
300  *    clib_error_t *error;
301  *
302  *    ... do some stuff...
303  *
304  *    if ((error = vlib_call_init_function (init_runs_next)))
305  *      return error;
306  *    ...
307  * }
308  * VLIB_INIT_FUNCTION (init_runs_first);
309  *
310  * and
311  *
312  * static clib_error_t *init_runs_first (vlib_main_t *vm)
313  * {
314  *    ... do some stuff...
315  * }
316  * VLIB_INIT_FUNCTION (init_runs_first) =
317  * {
318  *     .runs_before = VLIB_INITS("init_runs_next"),
319  * };
320  *
321  * The first form will [most likely] call "init_runs_next" on the
322  * spot. The second form means that "init_runs_first" runs before
323  * "init_runs_next," possibly much earlier in the sequence.
324  *
325  * Please DO NOT construct sets of init functions where A before B
326  * actually means A *right before* B. It's not necessary - simply combine
327  * A and B - and it leads to hugely annoying debugging exercises.
328  */
329
330 static inline clib_error_t *
331 call_init_exit_functions_internal (vlib_main_t *vm,
332                                    _vlib_init_function_list_elt_t **headp,
333                                    int call_once, int do_sort, int is_global)
334 {
335   vlib_global_main_t *vgm = vlib_get_global_main ();
336   clib_error_t *error = 0;
337   _vlib_init_function_list_elt_t *i;
338
339   if (do_sort && (error = vlib_sort_init_exit_functions (headp)))
340     return (error);
341
342   i = *headp;
343   while (i)
344     {
345       uword *h;
346
347       if (is_global)
348         h = hash_get (vgm->init_functions_called, i->f);
349       else
350         h = hash_get (vm->worker_init_functions_called, i->f);
351
352       if (call_once && !h)
353         {
354           if (call_once)
355             {
356               if (is_global)
357                 hash_set1 (vgm->init_functions_called, i->f);
358               else
359                 hash_set1 (vm->worker_init_functions_called, i->f);
360             }
361           error = i->f (vm);
362           if (error)
363             return error;
364         }
365       i = i->next_init_function;
366     }
367   return error;
368 }
369
370 clib_error_t *
371 vlib_call_init_exit_functions (vlib_main_t *vm,
372                                _vlib_init_function_list_elt_t **headp,
373                                int call_once, int is_global)
374 {
375   return call_init_exit_functions_internal (vm, headp, call_once,
376                                             1 /* do_sort */, is_global);
377 }
378
379 clib_error_t *
380 vlib_call_init_exit_functions_no_sort (vlib_main_t *vm,
381                                        _vlib_init_function_list_elt_t **headp,
382                                        int call_once, int is_global)
383 {
384   return call_init_exit_functions_internal (vm, headp, call_once,
385                                             0 /* do_sort */, is_global);
386 }
387
388 clib_error_t *
389 vlib_call_all_init_functions (vlib_main_t * vm)
390 {
391   vlib_global_main_t *vgm = vlib_get_global_main ();
392   /* Call placeholder functions to make sure purely static modules are
393      linked in. */
394 #define _(f) vlib_##f##_reference ();
395   foreach_vlib_module_reference;
396 #undef _
397
398   return vlib_call_init_exit_functions (vm, &vgm->init_function_registrations,
399                                         1 /* call_once */, 1 /* is_global */);
400 }
401
402 clib_error_t *
403 vlib_call_all_main_loop_enter_functions (vlib_main_t * vm)
404 {
405   vlib_global_main_t *vgm = vlib_get_global_main ();
406   return vlib_call_init_exit_functions (
407     vm, &vgm->main_loop_enter_function_registrations, 1 /* call_once */,
408     1 /* is_global */);
409 }
410
411 clib_error_t *
412 vlib_call_all_main_loop_exit_functions (vlib_main_t * vm)
413 {
414   vlib_global_main_t *vgm = vlib_get_global_main ();
415   return vlib_call_init_exit_functions (
416     vm, &vgm->main_loop_exit_function_registrations, 1 /* call_once */,
417     1 /* is_global */);
418 }
419
420 clib_error_t *
421 vlib_call_all_config_functions (vlib_main_t * vm,
422                                 unformat_input_t * input, int is_early)
423 {
424   vlib_global_main_t *vgm = vlib_get_global_main ();
425   clib_error_t *error = 0;
426   vlib_config_function_runtime_t *c, **all;
427   uword *hash = 0, *p;
428   uword i;
429
430   hash = hash_create_string (0, sizeof (uword));
431   all = 0;
432
433   c = vgm->config_function_registrations;
434
435   while (c)
436     {
437       hash_set_mem (hash, c->name, vec_len (all));
438       vec_add1 (all, c);
439       unformat_init (&c->input, 0, 0);
440       c = c->next_registration;
441     }
442
443   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
444     {
445       u8 *s, *v;
446
447       if (!unformat (input, "%s %v", &s, &v) || !(p = hash_get_mem (hash, s)))
448         {
449           error = clib_error_create ("unknown input `%s %v'", s, v);
450           goto done;
451         }
452
453       c = all[p[0]];
454       if (vec_len (c->input.buffer) > 0)
455         vec_add1 (c->input.buffer, ' ');
456       vec_add (c->input.buffer, v, vec_len (v));
457       vec_free (v);
458       vec_free (s);
459     }
460
461   for (i = 0; i < vec_len (all); i++)
462     {
463       c = all[i];
464
465       /* Is this an early config? Are we doing early configs? */
466       if (is_early ^ c->is_early)
467         continue;
468
469       /* Already called? */
470       if (hash_get (vgm->init_functions_called, c->function))
471         continue;
472       hash_set1 (vgm->init_functions_called, c->function);
473
474       error = c->function (vm, &c->input);
475       if (error)
476         goto done;
477     }
478
479 done:
480   for (i = 0; i < vec_len (all); i++)
481     {
482       c = all[i];
483       unformat_free (&c->input);
484     }
485   vec_free (all);
486   hash_free (hash);
487   return error;
488 }
489
490 void
491 vlib_init_dump (void)
492 {
493   vlib_global_main_t *vgm = vlib_get_global_main ();
494   int i = 0;
495
496   _vlib_init_function_list_elt_t *head, *this;
497   head = vgm->init_function_registrations;
498
499   this = head;
500   while (this)
501     {
502       fformat (stdout, "[%d]: %s\n", i++, this->name);
503       this = this->next_init_function;
504     }
505 }
506
507 static clib_error_t *
508 show_init_function_command_fn (vlib_main_t * vm,
509                                unformat_input_t * input,
510                                vlib_cli_command_t * cmd)
511 {
512   vlib_global_main_t *vgm = vlib_get_global_main ();
513   int which = 1;
514   int verbose = 0;
515   int i, n_init_fns;
516   _vlib_init_function_list_elt_t *head, *this;
517   uword *index_by_name;
518   uword *reg_by_index;
519   u8 **init_f_names = 0;
520   u8 *init_f_name;
521   uword *p;
522   _vlib_init_function_list_elt_t *this_reg = 0;
523   hash_pair_t *hp;
524   u8 **keys_to_delete = 0;
525
526   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
527     {
528       if (unformat (input, "init"))
529         which = 1;
530       else if (unformat (input, "enter"))
531         which = 2;
532       else if (unformat (input, "exit"))
533         which = 3;
534       else if (unformat (input, "verbose %d", &verbose))
535         ;
536       else if (unformat (input, "verbose"))
537         verbose = 1;
538       else
539         break;
540     }
541
542   switch (which)
543     {
544     case 1:
545       head = vgm->init_function_registrations;
546       break;
547     case 2:
548       head = vgm->main_loop_enter_function_registrations;
549       break;
550     case 3:
551       head = vgm->main_loop_exit_function_registrations;
552       break;
553     default:
554       return clib_error_return (0, "BUG");
555     }
556
557   if (verbose == 0)
558     {
559       this = head;
560       i = 0;
561       while (this)
562         {
563           vlib_cli_output (vm, "[%d]: %s", i++, this->name);
564           this = this->next_init_function;
565         }
566       return 0;
567     }
568
569   index_by_name = hash_create_string (0, sizeof (uword));
570   reg_by_index = hash_create (0, sizeof (uword));
571
572   this_reg = head;
573   n_init_fns = 0;
574   /* collect init fcn names */
575   while (this_reg)
576     {
577       init_f_name = format (0, "%s%c", this_reg->name, 0);
578       hash_set (reg_by_index, vec_len (init_f_names), (uword) this_reg);
579
580       hash_set_mem (index_by_name, init_f_name, vec_len (init_f_names));
581       vec_add1 (init_f_names, init_f_name);
582       n_init_fns++;
583       this_reg = this_reg->next_init_function;
584     }
585
586   for (i = 0; i < n_init_fns; i++)
587     {
588       p = hash_get (reg_by_index, i);
589       ASSERT (p != 0);
590       this_reg = (_vlib_init_function_list_elt_t *) p[0];
591       vlib_cli_output (vm, "[%d] %s", i, this_reg->name);
592       {
593         char **runs_before, **runs_after, **init_order;
594         runs_before = this_reg->runs_before;
595         while (runs_before && runs_before[0])
596           {
597             _vlib_init_function_list_elt_t *successor;
598             uword successor_index;
599             p = hash_get_mem (index_by_name, runs_before[0]);
600             if (p == 0)
601               {
602                 clib_warning ("couldn't find successor '%s'", runs_before[0]);
603                 runs_before++;
604                 continue;
605               }
606             successor_index = p[0];
607             p = hash_get (reg_by_index, p[0]);
608             ASSERT (p != 0);
609             successor = (_vlib_init_function_list_elt_t *) p[0];
610             vlib_cli_output (vm, "  before '%s' [%lld]",
611                              successor->name, successor_index);
612             runs_before++;
613           }
614         runs_after = this_reg->runs_after;
615         while (runs_after && runs_after[0])
616           {
617             _vlib_init_function_list_elt_t *predecessor;
618             uword predecessor_index;
619             p = hash_get_mem (index_by_name, runs_after[0]);
620             if (p == 0)
621               {
622                 clib_warning ("couldn't find predecessor '%s'",
623                               runs_after[0]);
624                 runs_after++;
625                 continue;
626               }
627             predecessor_index = p[0];
628             p = hash_get (reg_by_index, p[0]);
629             ASSERT (p != 0);
630             predecessor = (_vlib_init_function_list_elt_t *) p[0];
631             vlib_cli_output (vm, "  after '%s' [%lld]",
632                              predecessor->name, predecessor_index);
633             runs_after++;
634           }
635         init_order = this_reg->init_order;
636         while (init_order && init_order[0])
637           {
638             _vlib_init_function_list_elt_t *inorder;
639             uword inorder_index;
640             p = hash_get_mem (index_by_name, init_order[0]);
641             if (p == 0)
642               {
643                 clib_warning ("couldn't find order element'%s'",
644                               init_order[0]);
645                 init_order++;
646                 continue;
647               }
648             inorder_index = p[0];
649             p = hash_get (reg_by_index, p[0]);
650             ASSERT (p != 0);
651             inorder = (_vlib_init_function_list_elt_t *) p[0];
652             vlib_cli_output (vm, "  in order '%s' [%lld]",
653                              inorder->name, inorder_index);
654             init_order++;
655           }
656       }
657     }
658   hash_foreach_pair (hp, index_by_name,
659   ({
660     vec_add1 (keys_to_delete, (u8 *)hp->key);
661   }));
662   hash_free (index_by_name);
663   for (i = 0; i < vec_len (keys_to_delete); i++)
664     vec_free (keys_to_delete[i]);
665   vec_free (keys_to_delete);
666   hash_free (reg_by_index);
667
668   return 0;
669 }
670
671 /*?
672  * Show init function order
673  *
674  * @cliexpar
675  * @cliexstart{show init-function [init | enter | exit] [verbose [nn]]}
676  * @cliexend
677  ?*/
678 VLIB_CLI_COMMAND (show_init_function, static) = {
679   .path = "show init-function",
680   .short_help = "show init-function [init | enter | exit][verbose [nn]]",
681   .function = show_init_function_command_fn,
682 };
683
684
685 /*
686  * fd.io coding-style-patch-verification: ON
687  *
688  * Local Variables:
689  * eval: (c-set-style "gnu")
690  * End:
691  */