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