api: API trace improvements
[vpp.git] / src / vat2 / main.c
1 /*
2  * Copyright (c) 2020 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 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdbool.h>
19 #include <ctype.h>
20 #include <getopt.h>
21 #include <string.h>
22 #include <vlib/vlib.h>
23 #include <vlibapi/api_types.h>
24 #include <vppinfra/hash.h>
25 #include <vppinfra/cJSON.h>
26
27 /* VPP API client includes */
28 #include <vpp-api/client/vppapiclient.h>
29
30 #include <limits.h>
31 #include "vat2.h"
32
33 /*
34  * Filter these messages as they are used to manage the API connection to VPP
35  */
36 char *filter_messages_strings[] = { "memclnt_create",
37                                     "memclnt_delete",
38                                     "sockclnt_create",
39                                     "sockclnt_delete",
40                                     "memclnt_rx_thread_suspend",
41                                     "memclnt_read_timeout",
42                                     "rx_thread_exit",
43                                     "trace_plugin_msg_ids",
44                                     0 };
45
46 static bool
47 filter_message (char *msgname)
48 {
49   char **p = filter_messages_strings;
50
51   while (*p)
52     {
53       if (strcmp (*p, msgname) == 0)
54         return true;
55       p++;
56     }
57   return false;
58 }
59
60 uword *function_by_name;
61 bool debug = false;
62
63 char *vat2_plugin_path;
64 static void
65 vat2_find_plugin_path ()
66 {
67   char *p, path[PATH_MAX];
68   int rv;
69   u8 *s;
70
71   /* find executable path */
72   if ((rv = readlink ("/proc/self/exe", path, PATH_MAX - 1)) == -1)
73     return;
74
75   /* readlink doesn't provide null termination */
76   path[rv] = 0;
77
78   /* strip filename */
79   if ((p = strrchr (path, '/')) == 0)
80     return;
81   *p = 0;
82
83   /* strip bin/ */
84   if ((p = strrchr (path, '/')) == 0)
85     return;
86   *p = 0;
87
88   s = format (0, "%s/lib/" CLIB_TARGET_TRIPLET "/vat2_plugins:"
89               "%s/lib/vat2_plugins", path, path);
90   vec_add1 (s, 0);
91   vat2_plugin_path = (char *) s;
92 }
93
94 void
95 vac_callback (unsigned char *data, int len)
96 {
97   u16 result_msg_id = ntohs(*((u16 *)data));
98   DBG("Received something async: %d\n", result_msg_id);
99 }
100
101 int vat2_load_plugins (char *path, char *filter, int *loaded);
102
103 static int
104 register_function (void)
105 {
106   int loaded;
107
108   vat2_find_plugin_path();
109   DBG("Plugin Path %s\n", vat2_plugin_path);
110   int rv = vat2_load_plugins(vat2_plugin_path, 0, &loaded);
111   DBG("Loaded %u plugins\n", loaded);
112   return rv;
113 }
114
115 struct apifuncs_s
116 {
117   cJSON (*f) (cJSON *);
118   cJSON (*tojson) (void *);
119   u32 crc;
120 };
121
122 struct apifuncs_s *apifuncs = 0;
123
124 void
125 vat2_register_function (char *name, cJSON (*f) (cJSON *),
126                         cJSON (*tojson) (void *), u32 crc)
127 {
128   struct apifuncs_s funcs = { .f = f, .tojson = tojson, .crc = crc };
129   vec_add1 (apifuncs, funcs);
130   hash_set_mem (function_by_name, name, vec_len (apifuncs) - 1);
131 }
132
133 static int
134 vat2_exec_command_by_name (char *msgname, cJSON *o)
135 {
136   if (filter_message (msgname))
137     return 0;
138
139   cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
140   if (!crc_obj)
141     {
142       fprintf (stderr, "Missing '_crc' element!\n");
143       return -1;
144     }
145   char *crc_str = cJSON_GetStringValue (crc_obj);
146   u32 crc = (u32) strtol (crc_str, NULL, 16);
147
148   uword *p = hash_get_mem (function_by_name, msgname);
149   if (!p)
150     {
151       fprintf (stderr, "No such command %s\n", msgname);
152       return -1;
153     }
154   if (crc != apifuncs[p[0]].crc)
155     {
156       fprintf (stderr, "API CRC does not match: %s!\n", msgname);
157     }
158
159   cJSON *(*fp) (cJSON *);
160   fp = (void *) apifuncs[p[0]].f;
161   cJSON *r = (*fp) (o);
162
163   if (r)
164     {
165       char *output = cJSON_Print (r);
166       cJSON_Delete (r);
167       printf ("%s\n", output);
168       free (output);
169     }
170   else
171     {
172       fprintf (stderr, "Call failed: %s\n", msgname);
173       return -1;
174     }
175   return 0;
176 }
177
178 static int
179 vat2_exec_command (cJSON *o)
180 {
181
182   cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname");
183   if (!msg_id_obj)
184     {
185       fprintf (stderr, "Missing '_msgname' element!\n");
186       return -1;
187     }
188
189   char *name = cJSON_GetStringValue (msg_id_obj);
190
191   return vat2_exec_command_by_name (name, o);
192 }
193
194 static void
195 print_template (char *msgname)
196 {
197   uword *p = hash_get_mem (function_by_name, msgname);
198   if (!p)
199     goto error;
200
201   cJSON *(*fp) (void *);
202   fp = (void *) apifuncs[p[0]].tojson;
203   if (!fp)
204     goto error;
205
206   void *scratch = malloc (2048);
207   if (!scratch)
208     goto error;
209
210   memset (scratch, 0, 2048);
211   cJSON *t = fp (scratch);
212   if (!t)
213     goto error;
214   free (scratch);
215   char *output = cJSON_Print (t);
216   if (!output)
217     goto error;
218
219   cJSON_Delete (t);
220   printf ("%s\n", output);
221   free (output);
222
223   return;
224
225 error:
226   fprintf (stderr, "error printing template for: %s\n", msgname);
227 }
228
229 static void
230 dump_apis (void)
231 {
232   char *name;
233   u32 *i;
234   hash_foreach_mem (name, i, function_by_name, ({ printf ("%s\n", name); }));
235 }
236
237 static void
238 print_help (void)
239 {
240   char *help_string =
241     "Usage: vat2 [OPTION] <message-name> <JSON object>\n"
242     "Send API message to VPP and print reply\n"
243     "\n"
244     "-d, --debug       Print additional information\n"
245     "-p, --prefix      Specify shared memory prefix to connect to a given VPP "
246     "instance\n"
247     "-f, --file        File containing a JSON object with the arguments for "
248     "the message to send\n"
249     "--dump-apis       List all APIs available in VAT2 (might not reflect "
250     "running VPP)\n"
251     "-t, --template    Print a template JSON object for given API message\n"
252     "\n";
253   printf ("%s", help_string);
254 }
255
256 int main (int argc, char **argv)
257 {
258   /* Create a heap of 64MB */
259   clib_mem_init (0, 64 << 20);
260   char *filename = 0, *prefix = 0;
261   int index;
262   int c;
263   opterr = 0;
264   cJSON *o = 0;
265   int option_index = 0;
266   bool dump_api = false;
267   bool template = false;
268   char *msgname = 0;
269   static int debug_flag;
270   static struct option long_options[] = {
271     { "debug", no_argument, &debug_flag, 1 },
272     { "prefix", optional_argument, 0, 'p' },
273     { "file", required_argument, 0, 'f' },
274     { "dump-apis", no_argument, 0, 0 },
275     { "template", no_argument, 0, 't' },
276     { 0, 0, 0, 0 }
277   };
278
279   while ((c = getopt_long (argc, argv, "hdp:f:", long_options,
280                            &option_index)) != -1)
281     {
282       switch (c)
283         {
284         case 0:
285           if (option_index == 3)
286             dump_api = true;
287           break;
288         case 'd':
289           debug = true;
290           break;
291         case 't':
292           template = true;
293           break;
294         case 'p':
295           prefix = optarg;
296           break;
297         case 'f':
298           filename = optarg;
299           break;
300         case '?':
301           print_help ();
302           return 1;
303         default:
304           abort ();
305         }
306     }
307   debug = debug_flag == 1 ? true : false;
308   DBG ("debug = %d, filename = %s shared memory prefix: %s\n", debug, filename,
309        prefix);
310
311   for (index = optind; index < argc; index++)
312     DBG ("Non-option argument %s\n", argv[index]);
313
314   index = optind;
315
316   if (argc > index + 2)
317     {
318       fprintf (stderr, "%s: Too many arguments\n", argv[0]);
319       exit (-1);
320     }
321
322   /* Load plugins */
323   function_by_name = hash_create_string (0, sizeof (uword));
324   int res = register_function();
325   if (res < 0) {
326     fprintf(stderr, "%s: loading plugins failed\n", argv[0]);
327     exit(-1);
328   }
329
330   if (template)
331     {
332       print_template (argv[index]);
333       exit (0);
334     }
335
336   if (dump_api)
337     {
338       dump_apis ();
339       exit (0);
340     }
341
342   /* Read message arguments from command line */
343   if (argc >= (index + 1))
344     {
345       msgname = argv[index];
346     }
347   if (argc == (index + 2)) {
348     o = cJSON_Parse(argv[index+1]);
349     if (!o) {
350       fprintf(stderr, "%s: Failed parsing JSON input: %s\n", argv[0], cJSON_GetErrorPtr());
351       exit(-1);
352     }
353   }
354
355   if (!msgname && !filename)
356     {
357       print_help ();
358       exit (-1);
359     }
360
361   /* Read message from file */
362   if (filename) {
363       if (argc > index)
364         {
365           fprintf (stderr, "%s: Superfluous arguments when filename given\n",
366                    argv[0]);
367           exit (-1);
368         }
369
370     FILE *f = fopen(filename, "r");
371     size_t chunksize, bufsize;
372     size_t n_read = 0;
373     size_t n;
374
375     if (!f) {
376       fprintf(stderr, "%s: can't open file: %s\n", argv[0], filename);
377       exit(-1);
378     }
379
380     chunksize = bufsize = 1024;
381     char *buf = malloc(bufsize);
382     while ((n = fread (buf + n_read, 1, chunksize, f)))
383       {
384         n_read += n;
385         if (n == chunksize)
386           {
387             bufsize += chunksize;
388             buf = realloc (buf, bufsize);
389           }
390       }
391     fclose(f);
392     if (n_read) {
393       o = cJSON_Parse(buf);
394       if (!o) {
395         fprintf(stderr, "%s: Failed parsing JSON input: %s\n", argv[0], cJSON_GetErrorPtr());
396         exit(-1);
397       }
398     }
399     free (buf);
400   }
401
402   if (!o)
403     {
404       fprintf (stderr, "%s: Failed parsing JSON input\n", argv[0]);
405       exit (-1);
406     }
407
408   if (vac_connect ("vat2", prefix, 0, 1024))
409     {
410       fprintf (stderr, "Failed connecting to VPP\n");
411       exit (-1);
412     }
413
414   if (msgname)
415     {
416       vat2_exec_command_by_name (msgname, o);
417     }
418   else
419     {
420       if (cJSON_IsArray (o))
421         {
422           size_t size = cJSON_GetArraySize (o);
423           for (int i = 0; i < size; i++)
424             vat2_exec_command (cJSON_GetArrayItem (o, i));
425         }
426     }
427   cJSON_Delete (o);
428   vac_disconnect();
429   exit (0);
430
431 }