feature: Add packet trace API
[vpp.git] / src / vat / main.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 #include "vat.h"
16 #include "plugin.h"
17 #include <signal.h>
18 #include <limits.h>
19
20 vat_main_t vat_main;
21
22 #include <vlibapi/api_helper_macros.h>
23
24 void
25 vat_suspend (vlib_main_t * vm, f64 interval)
26 {
27   /* do nothing in the standalone version, just return */
28 }
29
30 int
31 connect_to_vpe (char *name)
32 {
33   vat_main_t *vam = &vat_main;
34   api_main_t *am = vlibapi_get_main ();
35
36   if (vl_client_connect_to_vlib ("/vpe-api", name, 32) < 0)
37     return -1;
38
39   vam->vl_input_queue = am->shmem_hdr->vl_input_queue;
40   vam->my_client_index = am->my_client_index;
41
42   return 0;
43 }
44
45 /* *INDENT-OFF* */
46
47
48 vlib_main_t vlib_global_main;
49
50 static struct
51 {
52   vec_header_t h;
53   vlib_main_t *vm;
54 } __attribute__ ((packed)) __bootstrap_vlib_main_vector
55 __attribute__ ((aligned (CLIB_CACHE_LINE_BYTES))) =
56 {
57   .h.len = 1,
58   .vm = &vlib_global_main,
59 };
60 /* *INDENT-ON* */
61
62 vlib_main_t **vlib_mains = &__bootstrap_vlib_main_vector.vm;
63
64 void
65 vlib_cli_output (struct vlib_main_t *vm, char *fmt, ...)
66 {
67   clib_warning ("BUG");
68 }
69
70 static u8 *
71 format_api_error (u8 * s, va_list * args)
72 {
73   vat_main_t *vam = va_arg (*args, vat_main_t *);
74   i32 error = va_arg (*args, u32);
75   uword *p;
76
77   p = hash_get (vam->error_string_by_error_number, -error);
78
79   if (p)
80     s = format (s, "%s", p[0]);
81   else
82     s = format (s, "%d", error);
83   return s;
84 }
85
86 void
87 do_one_file (vat_main_t * vam)
88 {
89   int rv;
90   int (*fp) (vat_main_t * vam);
91   int arg_len;
92   unformat_input_t _input;
93   u8 *cmdp, *argsp;
94   uword *p;
95   u8 *this_cmd = 0;
96
97   vam->input = &_input;
98
99   /* Used by the "quit" command handler */
100   if (setjmp (vam->jump_buf) != 0)
101     return;
102
103   vam->jump_buf_set = 1;
104
105   while (1)
106     {
107       if (vam->ifp == stdin)
108         {
109           if (vam->exec_mode == 0)
110             rv = write (1, "vat# ", 5);
111           else
112             rv = write (1, "exec# ", 6);
113         }
114
115       _vec_len (vam->inbuf) = 4096;
116
117       if (vam->do_exit ||
118           fgets ((char *) vam->inbuf, vec_len (vam->inbuf), vam->ifp) == 0)
119         break;
120
121       vam->input_line_number++;
122
123       vec_free (this_cmd);
124
125       this_cmd =
126         (u8 *) clib_macro_eval (&vam->macro_main, (i8 *) vam->inbuf,
127                                 1 /* complain */ ,
128                                 0 /* level */ ,
129                                 8 /* max_level */ );
130
131       if (vam->exec_mode == 0)
132         {
133           /* Split input into cmd + args */
134           cmdp = this_cmd;
135
136           while (cmdp < (this_cmd + vec_len (this_cmd)))
137             {
138               if (*cmdp == ' ' || *cmdp == '\t' || *cmdp == '\n')
139                 {
140                   cmdp++;
141                 }
142               else
143                 break;
144             }
145           argsp = cmdp;
146           while (argsp < (this_cmd + vec_len (this_cmd)))
147             {
148               if (*argsp != ' ' && *argsp != '\t' && *argsp != '\n')
149                 {
150                   argsp++;
151                 }
152               else
153                 break;
154             }
155           *argsp++ = 0;
156           while (argsp < (this_cmd + vec_len (this_cmd)))
157             {
158               if (*argsp == ' ' || *argsp == '\t' || *argsp == '\n')
159                 {
160                   argsp++;
161                 }
162               else
163                 break;
164             }
165
166
167           /* Blank input line? */
168           if (*cmdp == 0)
169             continue;
170
171           p = hash_get_mem (vam->function_by_name, cmdp);
172           if (p == 0)
173             {
174               errmsg ("'%s': function not found\n", cmdp);
175               continue;
176             }
177
178           arg_len = strlen ((char *) argsp);
179
180           unformat_init_string (vam->input, (char *) argsp, arg_len);
181           fp = (void *) p[0];
182         }
183       else
184         {
185           unformat_init_string (vam->input, (char *) this_cmd,
186                                 strlen ((char *) this_cmd));
187           cmdp = this_cmd;
188           fp = exec;
189         }
190
191       rv = (*fp) (vam);
192       if (rv < 0)
193         errmsg ("%s error: %U\n", cmdp, format_api_error, vam, rv);
194       unformat_free (vam->input);
195
196       if (vam->regenerate_interface_table)
197         {
198           vam->regenerate_interface_table = 0;
199           api_sw_interface_dump (vam);
200         }
201
202       /* Hack to pick up new client index after memfd_segment_create pivot */
203       if (vam->client_index_invalid)
204         {
205           vat_main_t *vam = &vat_main;
206           api_main_t *am = vlibapi_get_main ();
207
208           vam->vl_input_queue = am->shmem_hdr->vl_input_queue;
209           vam->my_client_index = am->my_client_index;
210           vam->client_index_invalid = 0;
211         }
212     }
213 }
214
215 static void
216 init_error_string_table (vat_main_t * vam)
217 {
218
219   vam->error_string_by_error_number = hash_create (0, sizeof (uword));
220
221 #define _(n,v,s) hash_set (vam->error_string_by_error_number, -v, s);
222   foreach_vnet_api_error;
223 #undef _
224
225   hash_set (vam->error_string_by_error_number, 99, "Misc");
226 }
227
228 static i8 *
229 eval_current_file (clib_macro_main_t * mm, i32 complain)
230 {
231   vat_main_t *vam = &vat_main;
232   return ((i8 *) format (0, "%s%c", vam->current_file, 0));
233 }
234
235 static i8 *
236 eval_current_line (clib_macro_main_t * mm, i32 complain)
237 {
238   vat_main_t *vam = &vat_main;
239   return ((i8 *) format (0, "%d%c", vam->input_line_number, 0));
240 }
241
242 static void
243 signal_handler (int signum, siginfo_t * si, ucontext_t * uc)
244 {
245   vat_main_t *vam = &vat_main;
246
247   switch (signum)
248     {
249       /* these (caught) signals cause the application to exit */
250     case SIGINT:
251     case SIGTERM:
252       if (vam->jump_buf_set)
253         {
254           vam->do_exit = 1;
255           return;
256         }
257
258       /* FALLTHROUGH on purpose */
259
260     default:
261       break;
262     }
263
264   _exit (1);
265 }
266
267 static void
268 setup_signal_handlers (void)
269 {
270   uword i;
271   struct sigaction sa;
272
273   for (i = 1; i < 32; i++)
274     {
275       clib_memset (&sa, 0, sizeof (sa));
276       sa.sa_sigaction = (void *) signal_handler;
277       sa.sa_flags = SA_SIGINFO;
278
279       switch (i)
280         {
281           /* these signals take the default action */
282         case SIGABRT:
283         case SIGKILL:
284         case SIGSTOP:
285         case SIGUSR1:
286         case SIGUSR2:
287         case SIGPROF:
288           continue;
289
290           /* ignore SIGPIPE, SIGCHLD */
291         case SIGPIPE:
292         case SIGCHLD:
293         case SIGWINCH:
294           sa.sa_sigaction = (void *) SIG_IGN;
295           break;
296
297           /* catch and handle all other signals */
298         default:
299           break;
300         }
301
302       if (sigaction (i, &sa, 0) < 0)
303         clib_unix_warning ("sigaction %U", format_signal, i);
304     }
305 }
306
307 static void
308 vat_find_plugin_path ()
309 {
310   char *p, path[PATH_MAX];
311   int rv;
312   u8 *s;
313
314   /* find executable path */
315   if ((rv = readlink ("/proc/self/exe", path, PATH_MAX - 1)) == -1)
316     return;
317
318   /* readlink doesn't provide null termination */
319   path[rv] = 0;
320
321   /* strip filename */
322   if ((p = strrchr (path, '/')) == 0)
323     return;
324   *p = 0;
325
326   /* strip bin/ */
327   if ((p = strrchr (path, '/')) == 0)
328     return;
329   *p = 0;
330
331   s = format (0, "%s/lib/" CLIB_TARGET_TRIPLET "/vpp_api_test_plugins:"
332               "%s/lib/vpp_api_test_plugins", path, path);
333   vec_add1 (s, 0);
334   vat_plugin_path = (char *) s;
335 }
336
337 static void
338 load_features (void)
339 {
340   vat_registered_features_t *f;
341   vat_main_t *vam = &vat_main;
342   clib_error_t *error;
343
344   f = vam->feature_function_registrations;
345
346   while (f)
347     {
348       error = f->function (vam);
349       if (error)
350         {
351           clib_warning ("INIT FAILED");
352         }
353       f = f->next;
354     }
355 }
356
357 static inline clib_error_t *
358 call_init_exit_functions_internal (vlib_main_t * vm,
359                                    _vlib_init_function_list_elt_t ** headp,
360                                    int call_once, int do_sort)
361 {
362   clib_error_t *error = 0;
363   _vlib_init_function_list_elt_t *i;
364
365 #if 0
366   /* Not worth copying the topological sort code */
367   if (do_sort && (error = vlib_sort_init_exit_functions (headp)))
368     return (error);
369 #endif
370
371   i = *headp;
372   while (i)
373     {
374       if (call_once && !hash_get (vm->init_functions_called, i->f))
375         {
376           if (call_once)
377             hash_set1 (vm->init_functions_called, i->f);
378           error = i->f (vm);
379           if (error)
380             return error;
381         }
382       i = i->next_init_function;
383     }
384   return error;
385 }
386
387 clib_error_t *
388 vlib_call_init_exit_functions (vlib_main_t * vm,
389                                _vlib_init_function_list_elt_t ** headp,
390                                int call_once)
391 {
392   return call_init_exit_functions_internal (vm, headp, call_once,
393                                             1 /* do_sort */ );
394 }
395
396 int
397 main (int argc, char **argv)
398 {
399   vat_main_t *vam = &vat_main;
400   unformat_input_t _argv, *a = &_argv;
401   u8 **input_files = 0;
402   u8 *output_file = 0;
403   u8 *chroot_prefix;
404   u8 *this_input_file;
405   u8 interactive = 1;
406   u8 json_output = 0;
407   int i;
408   f64 timeout;
409   clib_error_t *error;
410   vlib_main_t *vm = &vlib_global_main;
411
412   clib_mem_init_thread_safe (0, 128 << 20);
413
414   clib_macro_init (&vam->macro_main);
415   clib_macro_add_builtin (&vam->macro_main, "current_file",
416                           eval_current_file);
417   clib_macro_add_builtin (&vam->macro_main, "current_line",
418                           eval_current_line);
419
420   init_error_string_table (vam);
421   vec_validate (vam->cmd_reply, 0);
422   vec_reset_length (vam->cmd_reply);
423
424   vat_find_plugin_path ();
425
426   unformat_init_command_line (a, argv);
427
428   while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
429     {
430       if (unformat (a, "in %s", &this_input_file))
431         vec_add1 (input_files, this_input_file);
432       else if (unformat (a, "out %s", &output_file))
433         ;
434       else if (unformat (a, "script"))
435         interactive = 0;
436       else if (unformat (a, "json"))
437         json_output = 1;
438       else if (unformat (a, "socket-name %s", &vam->socket_name))
439         ;
440       else if (unformat (a, "default-socket"))
441         {
442           vam->socket_name = format (0, "%s%c", API_SOCKET_FILE, 0);
443         }
444       else if (unformat (a, "plugin_path %s", (u8 *) & vat_plugin_path))
445         vec_add1 (vat_plugin_path, 0);
446       else if (unformat (a, "plugin_name_filter %s",
447                          (u8 *) & vat_plugin_name_filter))
448         vec_add1 (vat_plugin_name_filter, 0);
449       else if (unformat (a, "chroot prefix %s", &chroot_prefix))
450         {
451           vl_set_memory_root_path ((char *) chroot_prefix);
452         }
453       else
454         {
455           fformat
456             (stderr,
457              "%s: usage [in <f1> ... in <fn>] [out <fn>] [script] [json]\n"
458              "[plugin_path <path>][default-socket][socket-name <name>]\n"
459              "[plugin_name_filter <filter>][chroot prefix <path>]\n",
460              argv[0]);
461           exit (1);
462         }
463     }
464
465   if (output_file)
466     vam->ofp = fopen ((char *) output_file, "w");
467   else
468     vam->ofp = stdout;
469
470   if (vam->ofp == NULL)
471     {
472       fformat (stderr, "Couldn't open output file %s\n",
473                output_file ? (char *) output_file : "stdout");
474       exit (1);
475     }
476
477   clib_time_init (&vam->clib_time);
478
479   vat_api_hookup (vam);
480   vat_plugin_api_reference ();
481
482   setup_signal_handlers ();
483
484   if (vam->socket_name && vat_socket_connect (vam))
485     fformat (stderr, "WARNING: socket connection failed");
486
487   if ((!vam->socket_client_main || vam->socket_client_main->socket_fd == 0)
488       && connect_to_vpe ("vpp_api_test") < 0)
489     {
490       svm_region_exit ();
491       fformat (stderr, "Couldn't connect to vpe, exiting...\n");
492       exit (1);
493     }
494
495   vam->json_output = json_output;
496
497   if (!json_output)
498     api_sw_interface_dump (vam);
499
500   vec_validate (vam->inbuf, 4096);
501
502   load_features ();
503
504   vam->current_file = (u8 *) "plugin-init";
505   vat_plugin_init (vam);
506
507   /* Set up the init function hash table */
508   vm->init_functions_called = hash_create (0, 0);
509
510   /* Execute plugin init and api_init functions */
511   error = vlib_call_init_exit_functions
512     (vm, &vm->init_function_registrations, 1 /* call once */ );
513
514   if (error)
515     clib_error_report (error);
516
517   error = vlib_call_init_exit_functions
518     (vm, &vm->api_init_function_registrations, 1 /* call_once */ );
519
520   if (error)
521     clib_error_report (error);
522
523   for (i = 0; i < vec_len (input_files); i++)
524     {
525       vam->ifp = fopen ((char *) input_files[i], "r");
526       if (vam->ifp == NULL)
527         {
528           fformat (stderr, "Couldn't open input file %s\n", input_files[i]);
529           continue;
530         }
531       vam->current_file = input_files[i];
532       vam->input_line_number = 0;
533       do_one_file (vam);
534       fclose (vam->ifp);
535     }
536
537   if (output_file)
538     fclose (vam->ofp);
539
540   if (interactive)
541     {
542       vam->ifp = stdin;
543       vam->ofp = stdout;
544       vam->current_file = (u8 *) "interactive";
545       do_one_file (vam);
546       fclose (vam->ifp);
547     }
548
549   /*
550    * Particularly when running a script, don't be in a hurry to leave.
551    * A reply message queued to this process will end up constipating
552    * the allocation rings.
553    */
554   timeout = vat_time_now (vam) + 2.0;
555   while (vam->result_ready == 0 && vat_time_now (vam) < timeout)
556     ;
557
558   if (vat_time_now (vam) > timeout)
559     clib_warning ("BUG: message reply spin-wait timeout");
560
561   vl_client_disconnect_from_vlib ();
562   exit (0);
563 }
564
565 /*
566  * fd.io coding-style-patch-verification: ON
567  *
568  * Local Variables:
569  * eval: (c-set-style "gnu")
570  * End:
571  */