misc: api move continued
[vpp.git] / src / vpp-api / vapi / vapi.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2017 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <stdint.h>
21 #include <arpa/inet.h>
22 #include <stddef.h>
23 #include <assert.h>
24
25 #include <vpp-api/vapi/vapi_dbg.h>
26 #include <vpp-api/vapi/vapi.h>
27 #include <vpp-api/vapi/vapi_internal.h>
28 #include <vppinfra/types.h>
29 #include <vppinfra/pool.h>
30 #include <vlib/vlib.h>
31 #include <vlibapi/api_common.h>
32 #include <vlibmemory/memory_client.h>
33
34 #include <vapi/vlib.api.vapi.h>
35 #include <vapi/memclnt.api.vapi.h>
36
37 /* we need to use control pings for some stuff and because we're forced to put
38  * the code in headers, we need a way to be able to grab the ids of these
39  * messages - so declare them here as extern */
40 vapi_msg_id_t vapi_msg_id_control_ping = 0;
41 vapi_msg_id_t vapi_msg_id_control_ping_reply = 0;
42
43 DEFINE_VAPI_MSG_IDS_MEMCLNT_API_JSON;
44 DEFINE_VAPI_MSG_IDS_VLIB_API_JSON;
45
46 struct
47 {
48   size_t count;
49   vapi_message_desc_t **msgs;
50   size_t max_len_name_with_crc;
51 } __vapi_metadata;
52
53 typedef struct
54 {
55   u32 context;
56   vapi_cb_t callback;
57   void *callback_ctx;
58   bool is_dump;
59 } vapi_req_t;
60
61 static const u32 context_counter_mask = (1 << 31);
62
63 typedef struct
64 {
65   vapi_error_e (*cb) (vapi_ctx_t ctx, void *callback_ctx, vapi_msg_id_t id,
66                       void *payload);
67   void *ctx;
68 } vapi_generic_cb_with_ctx;
69
70 typedef struct
71 {
72   vapi_error_e (*cb) (vapi_ctx_t ctx, void *callback_ctx, void *payload);
73   void *ctx;
74 } vapi_event_cb_with_ctx;
75
76 struct vapi_ctx_s
77 {
78   vapi_mode_e mode;
79   int requests_size;            /* size of the requests array (circular queue) */
80   int requests_start;           /* index of first request */
81   int requests_count;           /* number of used slots */
82   vapi_req_t *requests;
83   u32 context_counter;
84   vapi_generic_cb_with_ctx generic_cb;
85   vapi_event_cb_with_ctx *event_cbs;
86   u16 *vapi_msg_id_t_to_vl_msg_id;
87   u16 vl_msg_id_max;
88   vapi_msg_id_t *vl_msg_id_to_vapi_msg_t;
89   bool connected;
90   bool handle_keepalives;
91   pthread_mutex_t requests_mutex;
92 };
93
94 u32
95 vapi_gen_req_context (vapi_ctx_t ctx)
96 {
97   ++ctx->context_counter;
98   ctx->context_counter %= context_counter_mask;
99   return ctx->context_counter | context_counter_mask;
100 }
101
102 size_t
103 vapi_get_request_count (vapi_ctx_t ctx)
104 {
105   return ctx->requests_count;
106 }
107
108 bool
109 vapi_requests_full (vapi_ctx_t ctx)
110 {
111   return (ctx->requests_count == ctx->requests_size);
112 }
113
114 bool
115 vapi_requests_empty (vapi_ctx_t ctx)
116 {
117   return (0 == ctx->requests_count);
118 }
119
120 static int
121 vapi_requests_end (vapi_ctx_t ctx)
122 {
123   return (ctx->requests_start + ctx->requests_count) % ctx->requests_size;
124 }
125
126 void
127 vapi_store_request (vapi_ctx_t ctx, u32 context, bool is_dump,
128                     vapi_cb_t callback, void *callback_ctx)
129 {
130   assert (!vapi_requests_full (ctx));
131   /* if the mutex is not held, bad things will happen */
132   assert (0 != pthread_mutex_trylock (&ctx->requests_mutex));
133   const int requests_end = vapi_requests_end (ctx);
134   vapi_req_t *slot = &ctx->requests[requests_end];
135   slot->is_dump = is_dump;
136   slot->context = context;
137   slot->callback = callback;
138   slot->callback_ctx = callback_ctx;
139   VAPI_DBG ("stored@%d: context:%x (start is @%d)", requests_end, context,
140             ctx->requests_start);
141   ++ctx->requests_count;
142   assert (!vapi_requests_empty (ctx));
143 }
144
145 #if VAPI_DEBUG_ALLOC
146 struct to_be_freed_s;
147 struct to_be_freed_s
148 {
149   void *v;
150   struct to_be_freed_s *next;
151 };
152
153 static struct to_be_freed_s *to_be_freed = NULL;
154
155 void
156 vapi_add_to_be_freed (void *v)
157 {
158   struct to_be_freed_s *prev = NULL;
159   struct to_be_freed_s *tmp;
160   tmp = to_be_freed;
161   while (tmp && tmp->v)
162     {
163       prev = tmp;
164       tmp = tmp->next;
165     }
166   if (!tmp)
167     {
168       if (!prev)
169         {
170           tmp = to_be_freed = calloc (1, sizeof (*to_be_freed));
171         }
172       else
173         {
174           tmp = prev->next = calloc (1, sizeof (*to_be_freed));
175         }
176     }
177   VAPI_DBG ("To be freed %p", v);
178   tmp->v = v;
179 }
180
181 void
182 vapi_trace_free (void *v)
183 {
184   struct to_be_freed_s *tmp = to_be_freed;
185   while (tmp && tmp->v != v)
186     {
187       tmp = tmp->next;
188     }
189   if (tmp && tmp->v == v)
190     {
191       VAPI_DBG ("Freed %p", v);
192       tmp->v = NULL;
193     }
194   else
195     {
196       VAPI_ERR ("Trying to free untracked pointer %p", v);
197       abort ();
198     }
199 }
200
201 void
202 vapi_to_be_freed_validate ()
203 {
204   struct to_be_freed_s *tmp = to_be_freed;
205   while (tmp)
206     {
207       if (tmp->v)
208         {
209           VAPI_ERR ("Unfreed msg %p!", tmp->v);
210         }
211       tmp = tmp->next;
212     }
213 }
214
215 #endif
216
217 void *
218 vapi_msg_alloc (vapi_ctx_t ctx, size_t size)
219 {
220   if (!ctx->connected)
221     {
222       return NULL;
223     }
224   void *rv = vl_msg_api_alloc_or_null (size);
225   if (rv)
226     {
227       clib_memset (rv, 0, size);
228     }
229   return rv;
230 }
231
232 void
233 vapi_msg_free (vapi_ctx_t ctx, void *msg)
234 {
235   if (!ctx->connected)
236     {
237       return;
238     }
239 #if VAPI_DEBUG_ALLOC
240   vapi_trace_free (msg);
241 #endif
242   vl_msg_api_free (msg);
243 }
244
245 vapi_msg_id_t
246 vapi_lookup_vapi_msg_id_t (vapi_ctx_t ctx, u16 vl_msg_id)
247 {
248   if (vl_msg_id <= ctx->vl_msg_id_max)
249     {
250       return ctx->vl_msg_id_to_vapi_msg_t[vl_msg_id];
251     }
252   return VAPI_INVALID_MSG_ID;
253 }
254
255 vapi_error_e
256 vapi_ctx_alloc (vapi_ctx_t * result)
257 {
258   vapi_ctx_t ctx = calloc (1, sizeof (struct vapi_ctx_s));
259   if (!ctx)
260     {
261       return VAPI_ENOMEM;
262     }
263   ctx->context_counter = 0;
264   ctx->vapi_msg_id_t_to_vl_msg_id =
265     malloc (__vapi_metadata.count *
266             sizeof (*ctx->vapi_msg_id_t_to_vl_msg_id));
267   if (!ctx->vapi_msg_id_t_to_vl_msg_id)
268     {
269       goto fail;
270     }
271   clib_memset (ctx->vapi_msg_id_t_to_vl_msg_id, ~0,
272                __vapi_metadata.count *
273                sizeof (*ctx->vapi_msg_id_t_to_vl_msg_id));
274   ctx->event_cbs = calloc (__vapi_metadata.count, sizeof (*ctx->event_cbs));
275   if (!ctx->event_cbs)
276     {
277       goto fail;
278     }
279   pthread_mutex_init (&ctx->requests_mutex, NULL);
280   *result = ctx;
281   return VAPI_OK;
282 fail:
283   vapi_ctx_free (ctx);
284   return VAPI_ENOMEM;
285 }
286
287 void
288 vapi_ctx_free (vapi_ctx_t ctx)
289 {
290   assert (!ctx->connected);
291   free (ctx->requests);
292   free (ctx->vapi_msg_id_t_to_vl_msg_id);
293   free (ctx->event_cbs);
294   free (ctx->vl_msg_id_to_vapi_msg_t);
295   pthread_mutex_destroy (&ctx->requests_mutex);
296   free (ctx);
297 }
298
299 bool
300 vapi_is_msg_available (vapi_ctx_t ctx, vapi_msg_id_t id)
301 {
302   return vapi_lookup_vl_msg_id (ctx, id) != UINT16_MAX;
303 }
304
305 vapi_error_e
306 vapi_connect (vapi_ctx_t ctx, const char *name,
307               const char *chroot_prefix,
308               int max_outstanding_requests,
309               int response_queue_size, vapi_mode_e mode,
310               bool handle_keepalives)
311 {
312   if (response_queue_size <= 0 || max_outstanding_requests <= 0)
313     {
314       return VAPI_EINVAL;
315     }
316   if (!clib_mem_get_per_cpu_heap () && !clib_mem_init (0, 1024 * 1024 * 32))
317     {
318       return VAPI_ENOMEM;
319     }
320   ctx->requests_size = max_outstanding_requests;
321   const size_t size = ctx->requests_size * sizeof (*ctx->requests);
322   void *tmp = realloc (ctx->requests, size);
323   if (!tmp)
324     {
325       return VAPI_ENOMEM;
326     }
327   ctx->requests = tmp;
328   clib_memset (ctx->requests, 0, size);
329   /* coverity[MISSING_LOCK] - 177211 requests_mutex is not needed here */
330   ctx->requests_start = ctx->requests_count = 0;
331   if (chroot_prefix)
332     {
333       VAPI_DBG ("set memory root path `%s'", chroot_prefix);
334       vl_set_memory_root_path ((char *) chroot_prefix);
335     }
336   static char api_map[] = "/vpe-api";
337   VAPI_DBG ("client api map `%s'", api_map);
338   if ((vl_client_api_map (api_map)) < 0)
339     {
340       return VAPI_EMAP_FAIL;
341     }
342   VAPI_DBG ("connect client `%s'", name);
343   if (vl_client_connect ((char *) name, 0, response_queue_size) < 0)
344     {
345       vl_client_api_unmap ();
346       return VAPI_ECON_FAIL;
347     }
348 #if VAPI_DEBUG_CONNECT
349   VAPI_DBG ("start probing messages");
350 #endif
351   int rv;
352   int i;
353   for (i = 0; i < __vapi_metadata.count; ++i)
354     {
355       vapi_message_desc_t *m = __vapi_metadata.msgs[i];
356       u8 scratch[m->name_with_crc_len + 1];
357       memcpy (scratch, m->name_with_crc, m->name_with_crc_len + 1);
358       u32 id = vl_msg_api_get_msg_index (scratch);
359       if (VAPI_INVALID_MSG_ID != id)
360         {
361           if (id > UINT16_MAX)
362             {
363               VAPI_ERR ("Returned vl_msg_id `%u' > UINT16MAX `%u'!", id,
364                         UINT16_MAX);
365               rv = VAPI_EINVAL;
366               goto fail;
367             }
368           if (id > ctx->vl_msg_id_max)
369             {
370               vapi_msg_id_t *tmp = realloc (ctx->vl_msg_id_to_vapi_msg_t,
371                                             sizeof
372                                             (*ctx->vl_msg_id_to_vapi_msg_t) *
373                                             (id + 1));
374               if (!tmp)
375                 {
376                   rv = VAPI_ENOMEM;
377                   goto fail;
378                 }
379               ctx->vl_msg_id_to_vapi_msg_t = tmp;
380               ctx->vl_msg_id_max = id;
381             }
382           ctx->vl_msg_id_to_vapi_msg_t[id] = m->id;
383           ctx->vapi_msg_id_t_to_vl_msg_id[m->id] = id;
384 #if VAPI_DEBUG_CONNECT
385           VAPI_DBG ("Message `%s' has vl_msg_id `%u'", m->name_with_crc,
386                     (unsigned) id);
387 #endif
388         }
389       else
390         {
391           ctx->vapi_msg_id_t_to_vl_msg_id[m->id] = UINT16_MAX;
392           VAPI_DBG ("Message `%s' not available", m->name_with_crc);
393         }
394     }
395 #if VAPI_DEBUG_CONNECT
396   VAPI_DBG ("finished probing messages");
397 #endif
398   if (!vapi_is_msg_available (ctx, vapi_msg_id_control_ping) ||
399       !vapi_is_msg_available (ctx, vapi_msg_id_control_ping_reply))
400     {
401       VAPI_ERR
402         ("control ping or control ping reply not available, cannot connect");
403       rv = VAPI_EINCOMPATIBLE;
404       goto fail;
405     }
406   ctx->mode = mode;
407   ctx->connected = true;
408   if (vapi_is_msg_available (ctx, vapi_msg_id_memclnt_keepalive))
409     {
410       ctx->handle_keepalives = handle_keepalives;
411     }
412   else
413     {
414       ctx->handle_keepalives = false;
415     }
416   return VAPI_OK;
417 fail:
418   vl_client_disconnect ();
419   vl_client_api_unmap ();
420   return rv;
421 }
422
423 vapi_error_e
424 vapi_disconnect (vapi_ctx_t ctx)
425 {
426   if (!ctx->connected)
427     {
428       return VAPI_EINVAL;
429     }
430   vl_client_disconnect ();
431   vl_client_api_unmap ();
432 #if VAPI_DEBUG_ALLOC
433   vapi_to_be_freed_validate ();
434 #endif
435   ctx->connected = false;
436   return VAPI_OK;
437 }
438
439 vapi_error_e
440 vapi_get_fd (vapi_ctx_t ctx, int *fd)
441 {
442   return VAPI_ENOTSUP;
443 }
444
445 vapi_error_e
446 vapi_send (vapi_ctx_t ctx, void *msg)
447 {
448   vapi_error_e rv = VAPI_OK;
449   if (!ctx || !msg || !ctx->connected)
450     {
451       rv = VAPI_EINVAL;
452       goto out;
453     }
454   int tmp;
455   svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue;
456 #if VAPI_DEBUG
457   unsigned msgid = be16toh (*(u16 *) msg);
458   if (msgid <= ctx->vl_msg_id_max)
459     {
460       vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid];
461       if (id < __vapi_metadata.count)
462         {
463           VAPI_DBG ("send msg@%p:%u[%s]", msg, msgid,
464                     __vapi_metadata.msgs[id]->name);
465         }
466       else
467         {
468           VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid);
469         }
470     }
471   else
472     {
473       VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid);
474     }
475 #endif
476   tmp = svm_queue_add (q, (u8 *) & msg,
477                        VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1);
478   if (tmp < 0)
479     {
480       rv = VAPI_EAGAIN;
481     }
482   else
483     VL_MSG_API_POISON (msg);
484 out:
485   VAPI_DBG ("vapi_send() rv = %d", rv);
486   return rv;
487 }
488
489 vapi_error_e
490 vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2)
491 {
492   vapi_error_e rv = VAPI_OK;
493   if (!ctx || !msg1 || !msg2 || !ctx->connected)
494     {
495       rv = VAPI_EINVAL;
496       goto out;
497     }
498   svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue;
499 #if VAPI_DEBUG
500   unsigned msgid1 = be16toh (*(u16 *) msg1);
501   unsigned msgid2 = be16toh (*(u16 *) msg2);
502   const char *name1 = "UNKNOWN";
503   const char *name2 = "UNKNOWN";
504   if (msgid1 <= ctx->vl_msg_id_max)
505     {
506       vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid1];
507       if (id < __vapi_metadata.count)
508         {
509           name1 = __vapi_metadata.msgs[id]->name;
510         }
511     }
512   if (msgid2 <= ctx->vl_msg_id_max)
513     {
514       vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid2];
515       if (id < __vapi_metadata.count)
516         {
517           name2 = __vapi_metadata.msgs[id]->name;
518         }
519     }
520   VAPI_DBG ("send two: %u[%s], %u[%s]", msgid1, name1, msgid2, name2);
521 #endif
522   int tmp = svm_queue_add2 (q, (u8 *) & msg1, (u8 *) & msg2,
523                             VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1);
524   if (tmp < 0)
525     {
526       rv = VAPI_EAGAIN;
527     }
528   else
529     VL_MSG_API_POISON (msg1);
530 out:
531   VAPI_DBG ("vapi_send() rv = %d", rv);
532   return rv;
533 }
534
535 vapi_error_e
536 vapi_recv (vapi_ctx_t ctx, void **msg, size_t * msg_size,
537            svm_q_conditional_wait_t cond, u32 time)
538 {
539   if (!ctx || !ctx->connected || !msg || !msg_size)
540     {
541       return VAPI_EINVAL;
542     }
543   vapi_error_e rv = VAPI_OK;
544   api_main_t *am = vlibapi_get_main ();
545   uword data;
546
547   if (am->our_pid == 0)
548     {
549       return VAPI_EINVAL;
550     }
551
552   svm_queue_t *q = am->vl_input_queue;
553 again:
554   VAPI_DBG ("doing shm queue sub");
555
556   int tmp = svm_queue_sub (q, (u8 *) & data, cond, time);
557
558   if (tmp == 0)
559     {
560       VL_MSG_API_UNPOISON ((void *) data);
561 #if VAPI_DEBUG_ALLOC
562       vapi_add_to_be_freed ((void *) data);
563 #endif
564       msgbuf_t *msgbuf =
565         (msgbuf_t *) ((u8 *) data - offsetof (msgbuf_t, data));
566       if (!msgbuf->data_len)
567         {
568           vapi_msg_free (ctx, (u8 *) data);
569           return VAPI_EAGAIN;
570         }
571       *msg = (u8 *) data;
572       *msg_size = ntohl (msgbuf->data_len);
573 #if VAPI_DEBUG
574       unsigned msgid = be16toh (*(u16 *) * msg);
575       if (msgid <= ctx->vl_msg_id_max)
576         {
577           vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid];
578           if (id < __vapi_metadata.count)
579             {
580               VAPI_DBG ("recv msg@%p:%u[%s]", *msg, msgid,
581                         __vapi_metadata.msgs[id]->name);
582             }
583           else
584             {
585               VAPI_DBG ("recv msg@%p:%u[UNKNOWN]", *msg, msgid);
586             }
587         }
588       else
589         {
590           VAPI_DBG ("recv msg@%p:%u[UNKNOWN]", *msg, msgid);
591         }
592 #endif
593       if (ctx->handle_keepalives)
594         {
595           unsigned msgid = be16toh (*(u16 *) * msg);
596           if (msgid ==
597               vapi_lookup_vl_msg_id (ctx, vapi_msg_id_memclnt_keepalive))
598             {
599               vapi_msg_memclnt_keepalive_reply *reply = NULL;
600               do
601                 {
602                   reply = vapi_msg_alloc (ctx, sizeof (*reply));
603                 }
604               while (!reply);
605               reply->header.context = vapi_get_client_index (ctx);
606               reply->header._vl_msg_id =
607                 vapi_lookup_vl_msg_id (ctx,
608                                        vapi_msg_id_memclnt_keepalive_reply);
609               reply->payload.retval = 0;
610               vapi_msg_memclnt_keepalive_reply_hton (reply);
611               while (VAPI_EAGAIN == vapi_send (ctx, reply));
612               vapi_msg_free (ctx, *msg);
613               VAPI_DBG ("autohandled memclnt_keepalive");
614               goto again;
615             }
616         }
617     }
618   else
619     {
620       rv = VAPI_EAGAIN;
621     }
622   return rv;
623 }
624
625 vapi_error_e
626 vapi_wait (vapi_ctx_t ctx, vapi_wait_mode_e mode)
627 {
628   return VAPI_ENOTSUP;
629 }
630
631 static vapi_error_e
632 vapi_dispatch_response (vapi_ctx_t ctx, vapi_msg_id_t id,
633                         u32 context, void *msg)
634 {
635   int mrv;
636   if (0 != (mrv = pthread_mutex_lock (&ctx->requests_mutex)))
637     {
638       VAPI_DBG ("pthread_mutex_lock() failed, rv=%d:%s", mrv, strerror (mrv));
639       return VAPI_MUTEX_FAILURE;
640     }
641   int tmp = ctx->requests_start;
642   const int requests_end = vapi_requests_end (ctx);
643   while (ctx->requests[tmp].context != context && tmp != requests_end)
644     {
645       ++tmp;
646       if (tmp == ctx->requests_size)
647         {
648           tmp = 0;
649         }
650     }
651   VAPI_DBG ("dispatch, search from %d, %s at %d", ctx->requests_start,
652             ctx->requests[tmp].context == context ? "matched" : "stopped",
653             tmp);
654   vapi_error_e rv = VAPI_OK;
655   if (ctx->requests[tmp].context == context)
656     {
657       while (ctx->requests_start != tmp)
658         {
659           VAPI_ERR ("No response to req with context=%u",
660                     (unsigned) ctx->requests[tmp].context);
661           ctx->requests[ctx->requests_start].callback (ctx, ctx->requests
662                                                        [ctx->
663                                                         requests_start].callback_ctx,
664                                                        VAPI_ENORESP, true,
665                                                        NULL);
666           clib_memset (&ctx->requests[ctx->requests_start], 0,
667                        sizeof (ctx->requests[ctx->requests_start]));
668           ++ctx->requests_start;
669           --ctx->requests_count;
670           if (ctx->requests_start == ctx->requests_size)
671             {
672               ctx->requests_start = 0;
673             }
674         }
675       // now ctx->requests_start == tmp
676       int payload_offset = vapi_get_payload_offset (id);
677       void *payload = ((u8 *) msg) + payload_offset;
678       bool is_last = true;
679       if (ctx->requests[tmp].is_dump)
680         {
681           if (vapi_msg_id_control_ping_reply == id)
682             {
683               payload = NULL;
684             }
685           else
686             {
687               is_last = false;
688             }
689         }
690       if (payload_offset != -1)
691         {
692           rv =
693             ctx->requests[tmp].callback (ctx, ctx->requests[tmp].callback_ctx,
694                                          VAPI_OK, is_last, payload);
695         }
696       else
697         {
698           /* this is a message without payload, so bend the callback a little
699            */
700           rv =
701             ((vapi_error_e (*)(vapi_ctx_t, void *, vapi_error_e, bool))
702              ctx->requests[tmp].callback) (ctx,
703                                            ctx->requests[tmp].callback_ctx,
704                                            VAPI_OK, is_last);
705         }
706       if (is_last)
707         {
708           clib_memset (&ctx->requests[ctx->requests_start], 0,
709                        sizeof (ctx->requests[ctx->requests_start]));
710           ++ctx->requests_start;
711           --ctx->requests_count;
712           if (ctx->requests_start == ctx->requests_size)
713             {
714               ctx->requests_start = 0;
715             }
716         }
717       VAPI_DBG ("after dispatch, req start = %d, end = %d, count = %d",
718                 ctx->requests_start, requests_end, ctx->requests_count);
719     }
720   if (0 != (mrv = pthread_mutex_unlock (&ctx->requests_mutex)))
721     {
722       VAPI_DBG ("pthread_mutex_unlock() failed, rv=%d:%s", mrv,
723                 strerror (mrv));
724       abort ();                 /* this really shouldn't happen */
725     }
726   return rv;
727 }
728
729 static vapi_error_e
730 vapi_dispatch_event (vapi_ctx_t ctx, vapi_msg_id_t id, void *msg)
731 {
732   if (ctx->event_cbs[id].cb)
733     {
734       return ctx->event_cbs[id].cb (ctx, ctx->event_cbs[id].ctx, msg);
735     }
736   else if (ctx->generic_cb.cb)
737     {
738       return ctx->generic_cb.cb (ctx, ctx->generic_cb.ctx, id, msg);
739     }
740   else
741     {
742       VAPI_DBG
743         ("No handler/generic handler for msg id %u[%s], message ignored",
744          (unsigned) id, __vapi_metadata.msgs[id]->name);
745     }
746   return VAPI_OK;
747 }
748
749 bool
750 vapi_msg_is_with_context (vapi_msg_id_t id)
751 {
752   assert (id <= __vapi_metadata.count);
753   return __vapi_metadata.msgs[id]->has_context;
754 }
755
756 vapi_error_e
757 vapi_dispatch_one (vapi_ctx_t ctx)
758 {
759   VAPI_DBG ("vapi_dispatch_one()");
760   void *msg;
761   size_t size;
762   vapi_error_e rv = vapi_recv (ctx, &msg, &size, SVM_Q_WAIT, 0);
763   if (VAPI_OK != rv)
764     {
765       VAPI_DBG ("vapi_recv failed with rv=%d", rv);
766       return rv;
767     }
768   u16 vpp_id = be16toh (*(u16 *) msg);
769   if (vpp_id > ctx->vl_msg_id_max)
770     {
771       VAPI_ERR ("Unknown msg ID received, id `%u', out of range <0,%u>",
772                 (unsigned) vpp_id, (unsigned) ctx->vl_msg_id_max);
773       vapi_msg_free (ctx, msg);
774       return VAPI_EINVAL;
775     }
776   if (VAPI_INVALID_MSG_ID == (unsigned) ctx->vl_msg_id_to_vapi_msg_t[vpp_id])
777     {
778       VAPI_ERR ("Unknown msg ID received, id `%u' marked as not supported",
779                 (unsigned) vpp_id);
780       vapi_msg_free (ctx, msg);
781       return VAPI_EINVAL;
782     }
783   const vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[vpp_id];
784   const size_t expect_size = vapi_get_message_size (id);
785   if (size < expect_size)
786     {
787       VAPI_ERR
788         ("Invalid msg received, unexpected size `%zu' < expected min `%zu'",
789          size, expect_size);
790       vapi_msg_free (ctx, msg);
791       return VAPI_EINVAL;
792     }
793   u32 context;
794   vapi_get_swap_to_host_func (id) (msg);
795   if (vapi_msg_is_with_context (id))
796     {
797       context = *(u32 *) (((u8 *) msg) + vapi_get_context_offset (id));
798       /* is this a message originating from VAPI? */
799       VAPI_DBG ("dispatch, context is %x", context);
800       if (context & context_counter_mask)
801         {
802           rv = vapi_dispatch_response (ctx, id, context, msg);
803           goto done;
804         }
805     }
806   rv = vapi_dispatch_event (ctx, id, msg);
807
808 done:
809   vapi_msg_free (ctx, msg);
810   return rv;
811 }
812
813 vapi_error_e
814 vapi_dispatch (vapi_ctx_t ctx)
815 {
816   vapi_error_e rv = VAPI_OK;
817   while (!vapi_requests_empty (ctx))
818     {
819       rv = vapi_dispatch_one (ctx);
820       if (VAPI_OK != rv)
821         {
822           return rv;
823         }
824     }
825   return rv;
826 }
827
828 void
829 vapi_set_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id,
830                    vapi_event_cb callback, void *callback_ctx)
831 {
832   vapi_event_cb_with_ctx *c = &ctx->event_cbs[id];
833   c->cb = callback;
834   c->ctx = callback_ctx;
835 }
836
837 void
838 vapi_clear_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id)
839 {
840   vapi_set_event_cb (ctx, id, NULL, NULL);
841 }
842
843 void
844 vapi_set_generic_event_cb (vapi_ctx_t ctx, vapi_generic_event_cb callback,
845                            void *callback_ctx)
846 {
847   ctx->generic_cb.cb = callback;
848   ctx->generic_cb.ctx = callback_ctx;
849 }
850
851 void
852 vapi_clear_generic_event_cb (vapi_ctx_t ctx)
853 {
854   ctx->generic_cb.cb = NULL;
855   ctx->generic_cb.ctx = NULL;
856 }
857
858 u16
859 vapi_lookup_vl_msg_id (vapi_ctx_t ctx, vapi_msg_id_t id)
860 {
861   assert (id < __vapi_metadata.count);
862   return ctx->vapi_msg_id_t_to_vl_msg_id[id];
863 }
864
865 int
866 vapi_get_client_index (vapi_ctx_t ctx)
867 {
868   return vlibapi_get_main ()->my_client_index;
869 }
870
871 bool
872 vapi_is_nonblocking (vapi_ctx_t ctx)
873 {
874   return (VAPI_MODE_NONBLOCKING == ctx->mode);
875 }
876
877 size_t
878 vapi_get_max_request_count (vapi_ctx_t ctx)
879 {
880   return ctx->requests_size - 1;
881 }
882
883 int
884 vapi_get_payload_offset (vapi_msg_id_t id)
885 {
886   assert (id < __vapi_metadata.count);
887   return __vapi_metadata.msgs[id]->payload_offset;
888 }
889
890 void (*vapi_get_swap_to_host_func (vapi_msg_id_t id)) (void *msg)
891 {
892   assert (id < __vapi_metadata.count);
893   return __vapi_metadata.msgs[id]->swap_to_host;
894 }
895
896 void (*vapi_get_swap_to_be_func (vapi_msg_id_t id)) (void *msg)
897 {
898   assert (id < __vapi_metadata.count);
899   return __vapi_metadata.msgs[id]->swap_to_be;
900 }
901
902 size_t
903 vapi_get_message_size (vapi_msg_id_t id)
904 {
905   assert (id < __vapi_metadata.count);
906   return __vapi_metadata.msgs[id]->size;
907 }
908
909 size_t
910 vapi_get_context_offset (vapi_msg_id_t id)
911 {
912   assert (id < __vapi_metadata.count);
913   return __vapi_metadata.msgs[id]->context_offset;
914 }
915
916 vapi_msg_id_t
917 vapi_register_msg (vapi_message_desc_t * msg)
918 {
919   int i = 0;
920   for (i = 0; i < __vapi_metadata.count; ++i)
921     {
922       if (!strcmp
923           (msg->name_with_crc, __vapi_metadata.msgs[i]->name_with_crc))
924         {
925           /* this happens if somebody is linking together several objects while
926            * using the static inline headers, just fill in the already
927            * assigned id here so that all the objects are in sync */
928           msg->id = __vapi_metadata.msgs[i]->id;
929           return msg->id;
930         }
931     }
932   vapi_msg_id_t id = __vapi_metadata.count;
933   ++__vapi_metadata.count;
934   __vapi_metadata.msgs =
935     realloc (__vapi_metadata.msgs,
936              sizeof (*__vapi_metadata.msgs) * __vapi_metadata.count);
937   __vapi_metadata.msgs[id] = msg;
938   size_t s = strlen (msg->name_with_crc);
939   if (s > __vapi_metadata.max_len_name_with_crc)
940     {
941       __vapi_metadata.max_len_name_with_crc = s;
942     }
943   msg->id = id;
944   return id;
945 }
946
947 vapi_error_e
948 vapi_producer_lock (vapi_ctx_t ctx)
949 {
950   int mrv;
951   if (0 != (mrv = pthread_mutex_lock (&ctx->requests_mutex)))
952     {
953       VAPI_DBG ("pthread_mutex_lock() failed, rv=%d:%s", mrv, strerror (mrv));
954       (void) mrv;               /* avoid warning if the above debug is not enabled */
955       return VAPI_MUTEX_FAILURE;
956     }
957   return VAPI_OK;
958 }
959
960 vapi_error_e
961 vapi_producer_unlock (vapi_ctx_t ctx)
962 {
963   int mrv;
964   if (0 != (mrv = pthread_mutex_unlock (&ctx->requests_mutex)))
965     {
966       VAPI_DBG ("pthread_mutex_unlock() failed, rv=%d:%s", mrv,
967                 strerror (mrv));
968       (void) mrv;               /* avoid warning if the above debug is not enabled */
969       return VAPI_MUTEX_FAILURE;
970     }
971   return VAPI_OK;
972 }
973
974 size_t
975 vapi_get_message_count ()
976 {
977   return __vapi_metadata.count;
978 }
979
980 const char *
981 vapi_get_msg_name (vapi_msg_id_t id)
982 {
983   return __vapi_metadata.msgs[id]->name;
984 }
985
986 /*
987  * fd.io coding-style-patch-verification: ON
988  *
989  * Local Variables:
990  * eval: (c-set-style "gnu")
991  * End:
992  */