papi: use correct size for fixed length strings
[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 #include <vlibmemory/memory_api.h>
34 #include <vlibmemory/api.h>
35
36 #include <vapi/memclnt.api.vapi.h>
37 #include <vapi/vlib.api.vapi.h>
38
39 #include <vlibmemory/vl_memory_msg_enum.h>
40
41 #define vl_typedefs /* define message structures */
42 #include <vlibmemory/vl_memory_api_h.h>
43 #undef vl_typedefs
44
45 /* we need to use control pings for some stuff and because we're forced to put
46  * the code in headers, we need a way to be able to grab the ids of these
47  * messages - so declare them here as extern */
48 vapi_msg_id_t vapi_msg_id_control_ping = 0;
49 vapi_msg_id_t vapi_msg_id_control_ping_reply = 0;
50
51 DEFINE_VAPI_MSG_IDS_MEMCLNT_API_JSON;
52 DEFINE_VAPI_MSG_IDS_VLIB_API_JSON;
53
54 struct
55 {
56   size_t count;
57   vapi_message_desc_t **msgs;
58   size_t max_len_name_with_crc;
59 } __vapi_metadata;
60
61 typedef struct
62 {
63   u32 context;
64   vapi_cb_t callback;
65   void *callback_ctx;
66   vapi_msg_id_t response_id;
67   enum vapi_request_type type;
68 } vapi_req_t;
69
70 static const u32 context_counter_mask = (1 << 31);
71
72 typedef struct
73 {
74   vapi_error_e (*cb) (vapi_ctx_t ctx, void *callback_ctx, vapi_msg_id_t id,
75                       void *payload);
76   void *ctx;
77 } vapi_generic_cb_with_ctx;
78
79 typedef struct
80 {
81   vapi_error_e (*cb) (vapi_ctx_t ctx, void *callback_ctx, void *payload);
82   void *ctx;
83 } vapi_event_cb_with_ctx;
84
85 struct vapi_ctx_s
86 {
87   vapi_mode_e mode;
88   int requests_size;            /* size of the requests array (circular queue) */
89   int requests_start;           /* index of first request */
90   int requests_count;           /* number of used slots */
91   vapi_req_t *requests;
92   u32 context_counter;
93   vapi_generic_cb_with_ctx generic_cb;
94   vapi_event_cb_with_ctx *event_cbs;
95   u16 *vapi_msg_id_t_to_vl_msg_id;
96   u16 vl_msg_id_max;
97   vapi_msg_id_t *vl_msg_id_to_vapi_msg_t;
98   bool connected;
99   bool handle_keepalives;
100   pthread_mutex_t requests_mutex;
101
102   svm_queue_t *vl_input_queue;
103   u32 my_client_index;
104   /** client message index hash table */
105   uword *msg_index_by_name_and_crc;
106 };
107
108 u32
109 vapi_gen_req_context (vapi_ctx_t ctx)
110 {
111   ++ctx->context_counter;
112   ctx->context_counter %= context_counter_mask;
113   return ctx->context_counter | context_counter_mask;
114 }
115
116 size_t
117 vapi_get_request_count (vapi_ctx_t ctx)
118 {
119   return ctx->requests_count;
120 }
121
122 bool
123 vapi_requests_full (vapi_ctx_t ctx)
124 {
125   return (ctx->requests_count == ctx->requests_size);
126 }
127
128 bool
129 vapi_requests_empty (vapi_ctx_t ctx)
130 {
131   return (0 == ctx->requests_count);
132 }
133
134 static int
135 vapi_requests_end (vapi_ctx_t ctx)
136 {
137   return (ctx->requests_start + ctx->requests_count) % ctx->requests_size;
138 }
139
140 void
141 vapi_store_request (vapi_ctx_t ctx, u32 context, vapi_msg_id_t response_id,
142                     enum vapi_request_type request_type, vapi_cb_t callback,
143                     void *callback_ctx)
144 {
145   assert (!vapi_requests_full (ctx));
146   /* if the mutex is not held, bad things will happen */
147   assert (0 != pthread_mutex_trylock (&ctx->requests_mutex));
148   const int requests_end = vapi_requests_end (ctx);
149   vapi_req_t *slot = &ctx->requests[requests_end];
150   slot->type = request_type;
151   slot->response_id = response_id;
152   slot->context = context;
153   slot->callback = callback;
154   slot->callback_ctx = callback_ctx;
155   VAPI_DBG ("stored@%d: context:%x (start is @%d)", requests_end, context,
156             ctx->requests_start);
157   ++ctx->requests_count;
158   assert (!vapi_requests_empty (ctx));
159 }
160
161 #if VAPI_DEBUG_ALLOC
162 struct to_be_freed_s;
163 struct to_be_freed_s
164 {
165   void *v;
166   struct to_be_freed_s *next;
167 };
168
169 static struct to_be_freed_s *to_be_freed = NULL;
170
171 void
172 vapi_add_to_be_freed (void *v)
173 {
174   struct to_be_freed_s *prev = NULL;
175   struct to_be_freed_s *tmp;
176   tmp = to_be_freed;
177   while (tmp && tmp->v)
178     {
179       prev = tmp;
180       tmp = tmp->next;
181     }
182   if (!tmp)
183     {
184       if (!prev)
185         {
186           tmp = to_be_freed = calloc (1, sizeof (*to_be_freed));
187         }
188       else
189         {
190           tmp = prev->next = calloc (1, sizeof (*to_be_freed));
191         }
192     }
193   VAPI_DBG ("To be freed %p", v);
194   tmp->v = v;
195 }
196
197 void
198 vapi_trace_free (void *v)
199 {
200   struct to_be_freed_s *tmp = to_be_freed;
201   while (tmp && tmp->v != v)
202     {
203       tmp = tmp->next;
204     }
205   if (tmp && tmp->v == v)
206     {
207       VAPI_DBG ("Freed %p", v);
208       tmp->v = NULL;
209     }
210   else
211     {
212       VAPI_ERR ("Trying to free untracked pointer %p", v);
213       abort ();
214     }
215 }
216
217 void
218 vapi_to_be_freed_validate ()
219 {
220   struct to_be_freed_s *tmp = to_be_freed;
221   while (tmp)
222     {
223       if (tmp->v)
224         {
225           VAPI_ERR ("Unfreed msg %p!", tmp->v);
226         }
227       tmp = tmp->next;
228     }
229 }
230
231 #endif
232
233 void *
234 vapi_msg_alloc (vapi_ctx_t ctx, size_t size)
235 {
236   if (!ctx->connected)
237     {
238       return NULL;
239     }
240   void *rv = vl_msg_api_alloc_as_if_client_or_null (size);
241   if (rv)
242     {
243       clib_memset (rv, 0, size);
244     }
245   return rv;
246 }
247
248 void
249 vapi_msg_free (vapi_ctx_t ctx, void *msg)
250 {
251   if (!ctx->connected)
252     {
253       return;
254     }
255 #if VAPI_DEBUG_ALLOC
256   vapi_trace_free (msg);
257 #endif
258   vl_msg_api_free (msg);
259 }
260
261 vapi_msg_id_t
262 vapi_lookup_vapi_msg_id_t (vapi_ctx_t ctx, u16 vl_msg_id)
263 {
264   if (vl_msg_id <= ctx->vl_msg_id_max)
265     {
266       return ctx->vl_msg_id_to_vapi_msg_t[vl_msg_id];
267     }
268   return VAPI_INVALID_MSG_ID;
269 }
270
271 vapi_error_e
272 vapi_ctx_alloc (vapi_ctx_t * result)
273 {
274   vapi_ctx_t ctx = calloc (1, sizeof (struct vapi_ctx_s));
275   if (!ctx)
276     {
277       return VAPI_ENOMEM;
278     }
279   ctx->context_counter = 0;
280   ctx->vapi_msg_id_t_to_vl_msg_id =
281     malloc (__vapi_metadata.count *
282             sizeof (*ctx->vapi_msg_id_t_to_vl_msg_id));
283   if (!ctx->vapi_msg_id_t_to_vl_msg_id)
284     {
285       goto fail;
286     }
287   clib_memset (ctx->vapi_msg_id_t_to_vl_msg_id, ~0,
288                __vapi_metadata.count *
289                sizeof (*ctx->vapi_msg_id_t_to_vl_msg_id));
290   ctx->event_cbs = calloc (__vapi_metadata.count, sizeof (*ctx->event_cbs));
291   if (!ctx->event_cbs)
292     {
293       goto fail;
294     }
295   pthread_mutex_init (&ctx->requests_mutex, NULL);
296   *result = ctx;
297   return VAPI_OK;
298 fail:
299   vapi_ctx_free (ctx);
300   return VAPI_ENOMEM;
301 }
302
303 void
304 vapi_ctx_free (vapi_ctx_t ctx)
305 {
306   assert (!ctx->connected);
307   free (ctx->requests);
308   free (ctx->vapi_msg_id_t_to_vl_msg_id);
309   free (ctx->event_cbs);
310   free (ctx->vl_msg_id_to_vapi_msg_t);
311   pthread_mutex_destroy (&ctx->requests_mutex);
312   free (ctx);
313 }
314
315 bool
316 vapi_is_msg_available (vapi_ctx_t ctx, vapi_msg_id_t id)
317 {
318   return vapi_lookup_vl_msg_id (ctx, id) != UINT16_MAX;
319 }
320
321 /* Cut and paste to avoid adding dependency to client library */
322 __clib_nosanitize_addr static void
323 VL_API_VEC_UNPOISON (const void *v)
324 {
325   const vec_header_t *vh = &((vec_header_t *) v)[-1];
326   clib_mem_unpoison (vh, sizeof (*vh) + vec_len (v));
327 }
328
329 static void
330 vapi_api_name_and_crc_free (vapi_ctx_t ctx)
331 {
332   int i;
333   u8 **keys = 0;
334   hash_pair_t *hp;
335
336   if (!ctx->msg_index_by_name_and_crc)
337     return;
338   hash_foreach_pair (hp, ctx->msg_index_by_name_and_crc,
339                      ({ vec_add1 (keys, (u8 *) hp->key); }));
340   for (i = 0; i < vec_len (keys); i++)
341     vec_free (keys[i]);
342   vec_free (keys);
343   hash_free (ctx->msg_index_by_name_and_crc);
344 }
345
346 static void
347 vapi_memclnt_create_v2_reply_t_handler (vapi_ctx_t ctx,
348                                         vl_api_memclnt_create_v2_reply_t *mp)
349 {
350   serialize_main_t _sm, *sm = &_sm;
351   u8 *tblv;
352   u32 nmsgs;
353   int i;
354   u8 *name_and_crc;
355   u32 msg_index;
356
357   ctx->my_client_index = mp->index;
358
359   /* Clean out any previous hash table (unlikely) */
360   vapi_api_name_and_crc_free (ctx);
361
362   ctx->msg_index_by_name_and_crc = hash_create_string (0, sizeof (uword));
363
364   /* Recreate the vnet-side API message handler table */
365   tblv = uword_to_pointer (mp->message_table, u8 *);
366   unserialize_open_data (sm, tblv, vec_len (tblv));
367   unserialize_integer (sm, &nmsgs, sizeof (u32));
368
369   VL_API_VEC_UNPOISON (tblv);
370
371   for (i = 0; i < nmsgs; i++)
372     {
373       msg_index = unserialize_likely_small_unsigned_integer (sm);
374       unserialize_cstring (sm, (char **) &name_and_crc);
375       hash_set_mem (ctx->msg_index_by_name_and_crc, name_and_crc, msg_index);
376     }
377 }
378
379 static void
380 vapi_memclnt_delete_reply_t_handler (vapi_ctx_t ctx,
381                                      vl_api_memclnt_delete_reply_t *mp)
382 {
383   void *oldheap;
384   oldheap = vl_msg_push_heap ();
385   svm_queue_free (ctx->vl_input_queue);
386   vl_msg_pop_heap (oldheap);
387
388   ctx->my_client_index = ~0;
389   ctx->vl_input_queue = 0;
390 }
391
392 static int
393 vapi_client_connect (vapi_ctx_t ctx, const char *name, int ctx_quota,
394                      int input_queue_size, bool keepalive)
395 {
396   vl_api_memclnt_create_v2_t *mp;
397   vl_api_memclnt_create_v2_reply_t *rp;
398   svm_queue_t *vl_input_queue;
399   vl_shmem_hdr_t *shmem_hdr;
400   int rv = 0;
401   void *oldheap;
402   api_main_t *am = vlibapi_get_main ();
403
404   shmem_hdr = am->shmem_hdr;
405
406   if (shmem_hdr == 0 || shmem_hdr->vl_input_queue == 0)
407     {
408       clib_warning ("shmem_hdr / input queue NULL");
409       return -1;
410     }
411
412   clib_mem_unpoison (shmem_hdr, sizeof (*shmem_hdr));
413   VL_MSG_API_SVM_QUEUE_UNPOISON (shmem_hdr->vl_input_queue);
414
415   oldheap = vl_msg_push_heap ();
416   vl_input_queue =
417     svm_queue_alloc_and_init (input_queue_size, sizeof (uword), getpid ());
418   vl_msg_pop_heap (oldheap);
419
420   ctx->my_client_index = ~0;
421   ctx->vl_input_queue = vl_input_queue;
422
423   mp = vl_msg_api_alloc_as_if_client (sizeof (vl_api_memclnt_create_v2_t));
424   clib_memset (mp, 0, sizeof (*mp));
425   mp->_vl_msg_id = ntohs (VL_API_MEMCLNT_CREATE_V2);
426   mp->ctx_quota = ctx_quota;
427   mp->input_queue = (uword) vl_input_queue;
428   strncpy ((char *) mp->name, name, sizeof (mp->name) - 1);
429   mp->keepalive = keepalive;
430
431   vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) &mp);
432
433   while (1)
434     {
435       int qstatus;
436       struct timespec ts, tsrem;
437       int i;
438
439       /* Wait up to 10 seconds */
440       for (i = 0; i < 1000; i++)
441         {
442           qstatus =
443             svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0);
444           if (qstatus == 0)
445             goto read_one_msg;
446           ts.tv_sec = 0;
447           ts.tv_nsec = 10000 * 1000; /* 10 ms */
448           while (nanosleep (&ts, &tsrem) < 0)
449             ts = tsrem;
450         }
451       /* Timeout... */
452       return -1;
453
454     read_one_msg:
455       VL_MSG_API_UNPOISON (rp);
456       if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_CREATE_V2_REPLY)
457         {
458           clib_warning ("unexpected reply: id %d", ntohs (rp->_vl_msg_id));
459           continue;
460         }
461       rv = clib_net_to_host_u32 (rp->response);
462       vapi_memclnt_create_v2_reply_t_handler (ctx, rp);
463       break;
464     }
465   return (rv);
466 }
467
468 static void
469 vapi_client_send_disconnect (vapi_ctx_t ctx, u8 do_cleanup)
470 {
471   vl_api_memclnt_delete_t *mp;
472   vl_shmem_hdr_t *shmem_hdr;
473   api_main_t *am = vlibapi_get_main ();
474
475   ASSERT (am->vlib_rp);
476   shmem_hdr = am->shmem_hdr;
477   ASSERT (shmem_hdr && shmem_hdr->vl_input_queue);
478
479   mp = vl_msg_api_alloc (sizeof (vl_api_memclnt_delete_t));
480   clib_memset (mp, 0, sizeof (*mp));
481   mp->_vl_msg_id = ntohs (VL_API_MEMCLNT_DELETE);
482   mp->index = ctx->my_client_index;
483   mp->do_cleanup = do_cleanup;
484
485   vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) &mp);
486 }
487
488 static int
489 vapi_client_disconnect (vapi_ctx_t ctx)
490 {
491   vl_api_memclnt_delete_reply_t *rp;
492   svm_queue_t *vl_input_queue;
493   time_t begin;
494   msgbuf_t *msgbuf;
495
496   vl_input_queue = ctx->vl_input_queue;
497   vapi_client_send_disconnect (ctx, 0 /* wait for reply */);
498
499   /*
500    * Have to be careful here, in case the client is disconnecting
501    * because e.g. the vlib process died, or is unresponsive.
502    */
503   begin = time (0);
504   while (1)
505     {
506       time_t now;
507
508       now = time (0);
509
510       if (now >= (begin + 2))
511         {
512           clib_warning ("peer unresponsive, give up");
513           ctx->my_client_index = ~0;
514           return -1;
515         }
516       if (svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0) < 0)
517         continue;
518
519       VL_MSG_API_UNPOISON (rp);
520
521       /* drain the queue */
522       if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY)
523         {
524           clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id));
525           msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data));
526           vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len));
527           continue;
528         }
529       msgbuf = (msgbuf_t *) ((u8 *) rp - offsetof (msgbuf_t, data));
530       vl_msg_api_handler ((void *) rp, ntohl (msgbuf->data_len));
531       break;
532     }
533
534   vapi_api_name_and_crc_free (ctx);
535   return 0;
536 }
537
538 u32
539 vapi_api_get_msg_index (vapi_ctx_t ctx, u8 *name_and_crc)
540 {
541   uword *p;
542
543   if (ctx->msg_index_by_name_and_crc)
544     {
545       p = hash_get_mem (ctx->msg_index_by_name_and_crc, name_and_crc);
546       if (p)
547         return p[0];
548     }
549   return ~0;
550 }
551
552 vapi_error_e
553 vapi_connect (vapi_ctx_t ctx, const char *name, const char *chroot_prefix,
554               int max_outstanding_requests, int response_queue_size,
555               vapi_mode_e mode, bool handle_keepalives)
556 {
557   int rv;
558
559   if (response_queue_size <= 0 || max_outstanding_requests <= 0)
560     {
561       return VAPI_EINVAL;
562     }
563   if (!clib_mem_get_per_cpu_heap () && !clib_mem_init (0, 1024 * 1024 * 32))
564     {
565       return VAPI_ENOMEM;
566     }
567
568   ctx->requests_size = max_outstanding_requests;
569   const size_t size = ctx->requests_size * sizeof (*ctx->requests);
570   void *tmp = realloc (ctx->requests, size);
571   if (!tmp)
572     {
573       return VAPI_ENOMEM;
574     }
575   ctx->requests = tmp;
576   clib_memset (ctx->requests, 0, size);
577   /* coverity[MISSING_LOCK] - 177211 requests_mutex is not needed here */
578   ctx->requests_start = ctx->requests_count = 0;
579
580   if (chroot_prefix)
581     {
582       VAPI_DBG ("set memory root path `%s'", chroot_prefix);
583       vl_set_memory_root_path ((char *) chroot_prefix);
584     }
585   static char api_map[] = "/vpe-api";
586   VAPI_DBG ("client api map `%s'", api_map);
587   if ((rv = vl_map_shmem (api_map, 0 /* is_vlib */)) < 0)
588     {
589       return VAPI_EMAP_FAIL;
590     }
591   VAPI_DBG ("connect client `%s'", name);
592   if (vapi_client_connect (ctx, (char *) name, 0, response_queue_size, true) <
593       0)
594     {
595       vl_client_api_unmap ();
596       return VAPI_ECON_FAIL;
597     }
598 #if VAPI_DEBUG_CONNECT
599   VAPI_DBG ("start probing messages");
600 #endif
601
602   int i;
603   for (i = 0; i < __vapi_metadata.count; ++i)
604     {
605       vapi_message_desc_t *m = __vapi_metadata.msgs[i];
606       u8 scratch[m->name_with_crc_len + 1];
607       memcpy (scratch, m->name_with_crc, m->name_with_crc_len + 1);
608       u32 id = vapi_api_get_msg_index (ctx, scratch);
609
610       if (VAPI_INVALID_MSG_ID != id)
611         {
612           if (id > UINT16_MAX)
613             {
614               VAPI_ERR ("Returned vl_msg_id `%u' > UINT16MAX `%u'!", id,
615                         UINT16_MAX);
616               rv = VAPI_EINVAL;
617               goto fail;
618             }
619           if (id > ctx->vl_msg_id_max)
620             {
621               vapi_msg_id_t *tmp =
622                 realloc (ctx->vl_msg_id_to_vapi_msg_t,
623                          sizeof (*ctx->vl_msg_id_to_vapi_msg_t) * (id + 1));
624               if (!tmp)
625                 {
626                   rv = VAPI_ENOMEM;
627                   goto fail;
628                 }
629               ctx->vl_msg_id_to_vapi_msg_t = tmp;
630               ctx->vl_msg_id_max = id;
631             }
632           ctx->vl_msg_id_to_vapi_msg_t[id] = m->id;
633           ctx->vapi_msg_id_t_to_vl_msg_id[m->id] = id;
634 #if VAPI_DEBUG_CONNECT
635           VAPI_DBG ("Message `%s' has vl_msg_id `%u'", m->name_with_crc,
636                     (unsigned) id);
637 #endif
638         }
639       else
640         {
641           ctx->vapi_msg_id_t_to_vl_msg_id[m->id] = UINT16_MAX;
642           VAPI_DBG ("Message `%s' not available", m->name_with_crc);
643         }
644     }
645 #if VAPI_DEBUG_CONNECT
646   VAPI_DBG ("finished probing messages");
647 #endif
648   if (!vapi_is_msg_available (ctx, vapi_msg_id_control_ping) ||
649       !vapi_is_msg_available (ctx, vapi_msg_id_control_ping_reply))
650     {
651       VAPI_ERR (
652         "control ping or control ping reply not available, cannot connect");
653       rv = VAPI_EINCOMPATIBLE;
654       goto fail;
655     }
656   ctx->mode = mode;
657   ctx->connected = true;
658   if (vapi_is_msg_available (ctx, vapi_msg_id_memclnt_keepalive))
659     {
660       ctx->handle_keepalives = handle_keepalives;
661     }
662   else
663     {
664       ctx->handle_keepalives = false;
665     }
666   return VAPI_OK;
667 fail:
668   vapi_client_disconnect (ctx);
669   vl_client_api_unmap ();
670   return rv;
671 }
672
673 /*
674  * API client running in the same process as VPP
675  */
676 vapi_error_e
677 vapi_connect_from_vpp (vapi_ctx_t ctx, const char *name,
678                        int max_outstanding_requests, int response_queue_size,
679                        vapi_mode_e mode, bool handle_keepalives)
680 {
681   int rv;
682
683   if (response_queue_size <= 0 || max_outstanding_requests <= 0)
684     {
685       return VAPI_EINVAL;
686     }
687
688   ctx->requests_size = max_outstanding_requests;
689   const size_t size = ctx->requests_size * sizeof (*ctx->requests);
690   void *tmp = realloc (ctx->requests, size);
691   if (!tmp)
692     {
693       return VAPI_ENOMEM;
694     }
695   ctx->requests = tmp;
696   clib_memset (ctx->requests, 0, size);
697   /* coverity[MISSING_LOCK] - 177211 requests_mutex is not needed here */
698   ctx->requests_start = ctx->requests_count = 0;
699
700   VAPI_DBG ("connect client `%s'", name);
701   if (vapi_client_connect (ctx, (char *) name, 0, response_queue_size,
702                            handle_keepalives) < 0)
703     {
704       return VAPI_ECON_FAIL;
705     }
706
707   int i;
708   for (i = 0; i < __vapi_metadata.count; ++i)
709     {
710       vapi_message_desc_t *m = __vapi_metadata.msgs[i];
711       u8 scratch[m->name_with_crc_len + 1];
712       memcpy (scratch, m->name_with_crc, m->name_with_crc_len + 1);
713       u32 id = vapi_api_get_msg_index (ctx, scratch);
714       if (VAPI_INVALID_MSG_ID != id)
715         {
716           if (id > UINT16_MAX)
717             {
718               VAPI_ERR ("Returned vl_msg_id `%u' > UINT16MAX `%u'!", id,
719                         UINT16_MAX);
720               rv = VAPI_EINVAL;
721               goto fail;
722             }
723           if (id > ctx->vl_msg_id_max)
724             {
725               vapi_msg_id_t *tmp =
726                 realloc (ctx->vl_msg_id_to_vapi_msg_t,
727                          sizeof (*ctx->vl_msg_id_to_vapi_msg_t) * (id + 1));
728               if (!tmp)
729                 {
730                   rv = VAPI_ENOMEM;
731                   goto fail;
732                 }
733               ctx->vl_msg_id_to_vapi_msg_t = tmp;
734               ctx->vl_msg_id_max = id;
735             }
736           ctx->vl_msg_id_to_vapi_msg_t[id] = m->id;
737           ctx->vapi_msg_id_t_to_vl_msg_id[m->id] = id;
738         }
739       else
740         {
741           ctx->vapi_msg_id_t_to_vl_msg_id[m->id] = UINT16_MAX;
742           VAPI_DBG ("Message `%s' not available", m->name_with_crc);
743         }
744     }
745   if (!vapi_is_msg_available (ctx, vapi_msg_id_control_ping) ||
746       !vapi_is_msg_available (ctx, vapi_msg_id_control_ping_reply))
747     {
748       VAPI_ERR (
749         "control ping or control ping reply not available, cannot connect");
750       rv = VAPI_EINCOMPATIBLE;
751       goto fail;
752     }
753   ctx->mode = mode;
754   ctx->connected = true;
755   if (vapi_is_msg_available (ctx, vapi_msg_id_memclnt_keepalive))
756     {
757       ctx->handle_keepalives = handle_keepalives;
758     }
759   else
760     {
761       ctx->handle_keepalives = false;
762     }
763   return VAPI_OK;
764 fail:
765   vapi_client_disconnect (ctx);
766   return rv;
767 }
768
769 vapi_error_e
770 vapi_disconnect_from_vpp (vapi_ctx_t ctx)
771 {
772   if (!ctx->connected)
773     {
774       return VAPI_EINVAL;
775     }
776   vl_api_memclnt_delete_reply_t *rp;
777   svm_queue_t *vl_input_queue;
778   time_t begin;
779   vl_input_queue = ctx->vl_input_queue;
780   vapi_client_send_disconnect (ctx, 0 /* wait for reply */);
781
782   /*
783    * Have to be careful here, in case the client is disconnecting
784    * because e.g. the vlib process died, or is unresponsive.
785    */
786   begin = time (0);
787   vapi_error_e rv = VAPI_OK;
788   while (1)
789     {
790       time_t now;
791
792       now = time (0);
793
794       if (now >= (begin + 2))
795         {
796           clib_warning ("peer unresponsive, give up");
797           ctx->my_client_index = ~0;
798           rv = VAPI_ENORESP;
799           goto fail;
800         }
801       if (svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0) < 0)
802         continue;
803
804       VL_MSG_API_UNPOISON (rp);
805
806       /* drain the queue */
807       if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY)
808         {
809           clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id));
810           vl_msg_api_free (rp);
811           continue;
812         }
813       vapi_memclnt_delete_reply_t_handler (
814         ctx, (void *) rp /*, ntohl (msgbuf->data_len)*/);
815       break;
816     }
817 fail:
818   vapi_api_name_and_crc_free (ctx);
819
820   ctx->connected = false;
821   return rv;
822 }
823
824 vapi_error_e
825 vapi_disconnect (vapi_ctx_t ctx)
826 {
827   if (!ctx->connected)
828     {
829       return VAPI_EINVAL;
830     }
831
832   vl_api_memclnt_delete_reply_t *rp;
833   svm_queue_t *vl_input_queue;
834   time_t begin;
835   vl_input_queue = ctx->vl_input_queue;
836   vapi_client_send_disconnect (ctx, 0 /* wait for reply */);
837
838   /*
839    * Have to be careful here, in case the client is disconnecting
840    * because e.g. the vlib process died, or is unresponsive.
841    */
842   begin = time (0);
843   vapi_error_e rv = VAPI_OK;
844   while (1)
845     {
846       time_t now;
847
848       now = time (0);
849
850       if (now >= (begin + 2))
851         {
852           clib_warning ("peer unresponsive, give up");
853           ctx->my_client_index = ~0;
854           rv = VAPI_ENORESP;
855           goto fail;
856         }
857       if (svm_queue_sub (vl_input_queue, (u8 *) &rp, SVM_Q_NOWAIT, 0) < 0)
858         continue;
859
860       VL_MSG_API_UNPOISON (rp);
861
862       /* drain the queue */
863       if (ntohs (rp->_vl_msg_id) != VL_API_MEMCLNT_DELETE_REPLY)
864         {
865           clib_warning ("queue drain: %d", ntohs (rp->_vl_msg_id));
866           vl_msg_api_free (rp);
867           continue;
868         }
869       vapi_memclnt_delete_reply_t_handler (
870         ctx, (void *) rp /*, ntohl (msgbuf->data_len)*/);
871       break;
872     }
873 fail:
874   vapi_api_name_and_crc_free (ctx);
875
876   vl_client_api_unmap ();
877 #if VAPI_DEBUG_ALLOC
878   vapi_to_be_freed_validate ();
879 #endif
880   ctx->connected = false;
881   return rv;
882 }
883
884 vapi_error_e
885 vapi_get_fd (vapi_ctx_t ctx, int *fd)
886 {
887   return VAPI_ENOTSUP;
888 }
889
890 vapi_error_e
891 vapi_send (vapi_ctx_t ctx, void *msg)
892 {
893   vapi_error_e rv = VAPI_OK;
894   if (!ctx || !msg || !ctx->connected)
895     {
896       rv = VAPI_EINVAL;
897       goto out;
898     }
899   int tmp;
900   svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue;
901 #if VAPI_DEBUG
902   unsigned msgid = be16toh (*(u16 *) msg);
903   if (msgid <= ctx->vl_msg_id_max)
904     {
905       vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid];
906       if (id < __vapi_metadata.count)
907         {
908           VAPI_DBG ("send msg@%p:%u[%s]", msg, msgid,
909                     __vapi_metadata.msgs[id]->name);
910         }
911       else
912         {
913           VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid);
914         }
915     }
916   else
917     {
918       VAPI_DBG ("send msg@%p:%u[UNKNOWN]", msg, msgid);
919     }
920 #endif
921   tmp = svm_queue_add (q, (u8 *) & msg,
922                        VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1);
923   if (tmp < 0)
924     {
925       rv = VAPI_EAGAIN;
926     }
927   else
928     VL_MSG_API_POISON (msg);
929 out:
930   VAPI_DBG ("vapi_send() rv = %d", rv);
931   return rv;
932 }
933
934 vapi_error_e
935 vapi_send2 (vapi_ctx_t ctx, void *msg1, void *msg2)
936 {
937   vapi_error_e rv = VAPI_OK;
938   if (!ctx || !msg1 || !msg2 || !ctx->connected)
939     {
940       rv = VAPI_EINVAL;
941       goto out;
942     }
943   svm_queue_t *q = vlibapi_get_main ()->shmem_hdr->vl_input_queue;
944 #if VAPI_DEBUG
945   unsigned msgid1 = be16toh (*(u16 *) msg1);
946   unsigned msgid2 = be16toh (*(u16 *) msg2);
947   const char *name1 = "UNKNOWN";
948   const char *name2 = "UNKNOWN";
949   if (msgid1 <= ctx->vl_msg_id_max)
950     {
951       vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid1];
952       if (id < __vapi_metadata.count)
953         {
954           name1 = __vapi_metadata.msgs[id]->name;
955         }
956     }
957   if (msgid2 <= ctx->vl_msg_id_max)
958     {
959       vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid2];
960       if (id < __vapi_metadata.count)
961         {
962           name2 = __vapi_metadata.msgs[id]->name;
963         }
964     }
965   VAPI_DBG ("send two: %u[%s], %u[%s]", msgid1, name1, msgid2, name2);
966 #endif
967   int tmp = svm_queue_add2 (q, (u8 *) & msg1, (u8 *) & msg2,
968                             VAPI_MODE_BLOCKING == ctx->mode ? 0 : 1);
969   if (tmp < 0)
970     {
971       rv = VAPI_EAGAIN;
972     }
973   else
974     VL_MSG_API_POISON (msg1);
975 out:
976   VAPI_DBG ("vapi_send() rv = %d", rv);
977   return rv;
978 }
979
980 vapi_error_e
981 vapi_recv (vapi_ctx_t ctx, void **msg, size_t * msg_size,
982            svm_q_conditional_wait_t cond, u32 time)
983 {
984   if (!ctx || !ctx->connected || !msg || !msg_size)
985     {
986       return VAPI_EINVAL;
987     }
988   vapi_error_e rv = VAPI_OK;
989   uword data;
990
991   svm_queue_t *q = ctx->vl_input_queue;
992
993 again:
994   VAPI_DBG ("doing shm queue sub");
995
996   int tmp = svm_queue_sub (q, (u8 *) & data, cond, time);
997
998   if (tmp == 0)
999     {
1000       VL_MSG_API_UNPOISON ((void *) data);
1001 #if VAPI_DEBUG_ALLOC
1002       vapi_add_to_be_freed ((void *) data);
1003 #endif
1004       msgbuf_t *msgbuf =
1005         (msgbuf_t *) ((u8 *) data - offsetof (msgbuf_t, data));
1006       if (!msgbuf->data_len)
1007         {
1008           vapi_msg_free (ctx, (u8 *) data);
1009           return VAPI_EAGAIN;
1010         }
1011       *msg = (u8 *) data;
1012       *msg_size = ntohl (msgbuf->data_len);
1013 #if VAPI_DEBUG
1014       unsigned msgid = be16toh (*(u16 *) * msg);
1015       if (msgid <= ctx->vl_msg_id_max)
1016         {
1017           vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[msgid];
1018           if (id < __vapi_metadata.count)
1019             {
1020               VAPI_DBG ("recv msg@%p:%u[%s]", *msg, msgid,
1021                         __vapi_metadata.msgs[id]->name);
1022             }
1023           else
1024             {
1025               VAPI_DBG ("recv msg@%p:%u[UNKNOWN]", *msg, msgid);
1026             }
1027         }
1028       else
1029         {
1030           VAPI_DBG ("recv msg@%p:%u[UNKNOWN]", *msg, msgid);
1031         }
1032 #endif
1033       if (ctx->handle_keepalives)
1034         {
1035           unsigned msgid = be16toh (*(u16 *) * msg);
1036           if (msgid ==
1037               vapi_lookup_vl_msg_id (ctx, vapi_msg_id_memclnt_keepalive))
1038             {
1039               vapi_msg_memclnt_keepalive_reply *reply = NULL;
1040               do
1041                 {
1042                   reply = vapi_msg_alloc (ctx, sizeof (*reply));
1043                 }
1044               while (!reply);
1045               reply->header.context = vapi_get_client_index (ctx);
1046               reply->header._vl_msg_id =
1047                 vapi_lookup_vl_msg_id (ctx,
1048                                        vapi_msg_id_memclnt_keepalive_reply);
1049               reply->payload.retval = 0;
1050               vapi_msg_memclnt_keepalive_reply_hton (reply);
1051               while (VAPI_EAGAIN == vapi_send (ctx, reply));
1052               vapi_msg_free (ctx, *msg);
1053               goto again;
1054             }
1055         }
1056     }
1057   else
1058     {
1059       rv = VAPI_EAGAIN;
1060     }
1061   return rv;
1062 }
1063
1064 vapi_error_e
1065 vapi_wait (vapi_ctx_t ctx)
1066 {
1067   svm_queue_lock (ctx->vl_input_queue);
1068   svm_queue_wait (ctx->vl_input_queue);
1069   svm_queue_unlock (ctx->vl_input_queue);
1070
1071   return VAPI_OK;
1072 }
1073
1074 static vapi_error_e
1075 vapi_dispatch_response (vapi_ctx_t ctx, vapi_msg_id_t id,
1076                         u32 context, void *msg)
1077 {
1078   int mrv;
1079   if (0 != (mrv = pthread_mutex_lock (&ctx->requests_mutex)))
1080     {
1081       VAPI_DBG ("pthread_mutex_lock() failed, rv=%d:%s", mrv, strerror (mrv));
1082       return VAPI_MUTEX_FAILURE;
1083     }
1084   int tmp = ctx->requests_start;
1085   const int requests_end = vapi_requests_end (ctx);
1086   while (ctx->requests[tmp].context != context && tmp != requests_end)
1087     {
1088       ++tmp;
1089       if (tmp == ctx->requests_size)
1090         {
1091           tmp = 0;
1092         }
1093     }
1094   VAPI_DBG ("dispatch, search from %d, %s at %d", ctx->requests_start,
1095             ctx->requests[tmp].context == context ? "matched" : "stopped",
1096             tmp);
1097   vapi_error_e rv = VAPI_OK;
1098   if (ctx->requests[tmp].context == context)
1099     {
1100       while (ctx->requests_start != tmp)
1101         {
1102           VAPI_ERR ("No response to req with context=%u",
1103                     (unsigned) ctx->requests[tmp].context);
1104           ctx->requests[ctx->requests_start].callback (ctx, ctx->requests
1105                                                        [ctx->
1106                                                         requests_start].callback_ctx,
1107                                                        VAPI_ENORESP, true,
1108                                                        NULL);
1109           clib_memset (&ctx->requests[ctx->requests_start], 0,
1110                        sizeof (ctx->requests[ctx->requests_start]));
1111           ++ctx->requests_start;
1112           --ctx->requests_count;
1113           if (ctx->requests_start == ctx->requests_size)
1114             {
1115               ctx->requests_start = 0;
1116             }
1117         }
1118       // now ctx->requests_start == tmp
1119       int payload_offset = vapi_get_payload_offset (id);
1120       void *payload = ((u8 *) msg) + payload_offset;
1121       bool is_last = true;
1122       switch (ctx->requests[tmp].type)
1123         {
1124         case VAPI_REQUEST_STREAM:
1125           if (ctx->requests[tmp].response_id == id)
1126             {
1127               is_last = false;
1128             }
1129           else
1130             {
1131               VAPI_DBG ("Stream response ID doesn't match current ID, move to "
1132                         "next ID");
1133               clib_memset (&ctx->requests[tmp], 0,
1134                            sizeof (ctx->requests[tmp]));
1135               ++ctx->requests_start;
1136               --ctx->requests_count;
1137               if (ctx->requests_start == ctx->requests_size)
1138                 {
1139                   ctx->requests_start = 0;
1140                 }
1141               tmp = ctx->requests_start;
1142               if (ctx->requests[tmp].context != context)
1143                 {
1144                   VAPI_ERR ("Unexpected context %u, expected context %u!",
1145                             ctx->requests[tmp].context, context);
1146                 }
1147             }
1148           break;
1149         case VAPI_REQUEST_DUMP:
1150           if (vapi_msg_id_control_ping_reply == id)
1151             {
1152               payload = NULL;
1153             }
1154           else
1155             {
1156               is_last = false;
1157             }
1158           break;
1159         case VAPI_REQUEST_REG:
1160           break;
1161         }
1162       if (payload_offset != -1)
1163         {
1164           rv = ctx->requests[tmp].callback (
1165             ctx, ctx->requests[tmp].callback_ctx, VAPI_OK, is_last, payload);
1166         }
1167       else
1168         {
1169           /* this is a message without payload, so bend the callback a little
1170            */
1171           rv =
1172             ((vapi_error_e (*)(vapi_ctx_t, void *, vapi_error_e, bool))
1173              ctx->requests[tmp].callback) (ctx,
1174                                            ctx->requests[tmp].callback_ctx,
1175                                            VAPI_OK, is_last);
1176         }
1177       if (is_last)
1178         {
1179           clib_memset (&ctx->requests[ctx->requests_start], 0,
1180                        sizeof (ctx->requests[ctx->requests_start]));
1181           ++ctx->requests_start;
1182           --ctx->requests_count;
1183           if (ctx->requests_start == ctx->requests_size)
1184             {
1185               ctx->requests_start = 0;
1186             }
1187         }
1188       VAPI_DBG ("after dispatch, req start = %d, end = %d, count = %d",
1189                 ctx->requests_start, requests_end, ctx->requests_count);
1190     }
1191   if (0 != (mrv = pthread_mutex_unlock (&ctx->requests_mutex)))
1192     {
1193       VAPI_DBG ("pthread_mutex_unlock() failed, rv=%d:%s", mrv,
1194                 strerror (mrv));
1195       abort ();                 /* this really shouldn't happen */
1196     }
1197   return rv;
1198 }
1199
1200 static vapi_error_e
1201 vapi_dispatch_event (vapi_ctx_t ctx, vapi_msg_id_t id, void *msg)
1202 {
1203   if (ctx->event_cbs[id].cb)
1204     {
1205       return ctx->event_cbs[id].cb (ctx, ctx->event_cbs[id].ctx, msg);
1206     }
1207   else if (ctx->generic_cb.cb)
1208     {
1209       return ctx->generic_cb.cb (ctx, ctx->generic_cb.ctx, id, msg);
1210     }
1211   else
1212     {
1213       VAPI_DBG
1214         ("No handler/generic handler for msg id %u[%s], message ignored",
1215          (unsigned) id, __vapi_metadata.msgs[id]->name);
1216     }
1217   return VAPI_OK;
1218 }
1219
1220 bool
1221 vapi_msg_is_with_context (vapi_msg_id_t id)
1222 {
1223   assert (id <= __vapi_metadata.count);
1224   return __vapi_metadata.msgs[id]->has_context;
1225 }
1226
1227 static int
1228 vapi_verify_msg_size (vapi_msg_id_t id, void *buf, uword buf_size)
1229 {
1230   assert (id < __vapi_metadata.count);
1231   return __vapi_metadata.msgs[id]->verify_msg_size (buf, buf_size);
1232 }
1233
1234 vapi_error_e
1235 vapi_dispatch_one (vapi_ctx_t ctx)
1236 {
1237   VAPI_DBG ("vapi_dispatch_one()");
1238   void *msg;
1239   uword size;
1240   svm_q_conditional_wait_t cond =
1241     vapi_is_nonblocking (ctx) ? SVM_Q_NOWAIT : SVM_Q_WAIT;
1242   vapi_error_e rv = vapi_recv (ctx, &msg, &size, cond, 0);
1243   if (VAPI_OK != rv)
1244     {
1245       VAPI_DBG ("vapi_recv failed with rv=%d", rv);
1246       return rv;
1247     }
1248   u16 vpp_id = be16toh (*(u16 *) msg);
1249   if (vpp_id > ctx->vl_msg_id_max)
1250     {
1251       VAPI_ERR ("Unknown msg ID received, id `%u', out of range <0,%u>",
1252                 (unsigned) vpp_id, (unsigned) ctx->vl_msg_id_max);
1253       vapi_msg_free (ctx, msg);
1254       return VAPI_EINVAL;
1255     }
1256   if (VAPI_INVALID_MSG_ID == (unsigned) ctx->vl_msg_id_to_vapi_msg_t[vpp_id])
1257     {
1258       VAPI_ERR ("Unknown msg ID received, id `%u' marked as not supported",
1259                 (unsigned) vpp_id);
1260       vapi_msg_free (ctx, msg);
1261       return VAPI_EINVAL;
1262     }
1263   const vapi_msg_id_t id = ctx->vl_msg_id_to_vapi_msg_t[vpp_id];
1264   vapi_get_swap_to_host_func (id) (msg);
1265   if (vapi_verify_msg_size (id, msg, size))
1266     {
1267       vapi_msg_free (ctx, msg);
1268       return VAPI_EINVAL;
1269     }
1270   u32 context;
1271   if (vapi_msg_is_with_context (id))
1272     {
1273       context = *(u32 *) (((u8 *) msg) + vapi_get_context_offset (id));
1274       /* is this a message originating from VAPI? */
1275       VAPI_DBG ("dispatch, context is %x", context);
1276       if (context & context_counter_mask)
1277         {
1278           rv = vapi_dispatch_response (ctx, id, context, msg);
1279           goto done;
1280         }
1281     }
1282   rv = vapi_dispatch_event (ctx, id, msg);
1283
1284 done:
1285   vapi_msg_free (ctx, msg);
1286   return rv;
1287 }
1288
1289 vapi_error_e
1290 vapi_dispatch (vapi_ctx_t ctx)
1291 {
1292   vapi_error_e rv = VAPI_OK;
1293   while (!vapi_requests_empty (ctx))
1294     {
1295       rv = vapi_dispatch_one (ctx);
1296       if (VAPI_OK != rv)
1297         {
1298           return rv;
1299         }
1300     }
1301   return rv;
1302 }
1303
1304 void
1305 vapi_set_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id,
1306                    vapi_event_cb callback, void *callback_ctx)
1307 {
1308   vapi_event_cb_with_ctx *c = &ctx->event_cbs[id];
1309   c->cb = callback;
1310   c->ctx = callback_ctx;
1311 }
1312
1313 void
1314 vapi_clear_event_cb (vapi_ctx_t ctx, vapi_msg_id_t id)
1315 {
1316   vapi_set_event_cb (ctx, id, NULL, NULL);
1317 }
1318
1319 void
1320 vapi_set_generic_event_cb (vapi_ctx_t ctx, vapi_generic_event_cb callback,
1321                            void *callback_ctx)
1322 {
1323   ctx->generic_cb.cb = callback;
1324   ctx->generic_cb.ctx = callback_ctx;
1325 }
1326
1327 void
1328 vapi_clear_generic_event_cb (vapi_ctx_t ctx)
1329 {
1330   ctx->generic_cb.cb = NULL;
1331   ctx->generic_cb.ctx = NULL;
1332 }
1333
1334 u16
1335 vapi_lookup_vl_msg_id (vapi_ctx_t ctx, vapi_msg_id_t id)
1336 {
1337   assert (id < __vapi_metadata.count);
1338   return ctx->vapi_msg_id_t_to_vl_msg_id[id];
1339 }
1340
1341 int
1342 vapi_get_client_index (vapi_ctx_t ctx)
1343 {
1344   return ctx->my_client_index;
1345 }
1346
1347 bool
1348 vapi_is_nonblocking (vapi_ctx_t ctx)
1349 {
1350   return (VAPI_MODE_NONBLOCKING == ctx->mode);
1351 }
1352
1353 size_t
1354 vapi_get_max_request_count (vapi_ctx_t ctx)
1355 {
1356   return ctx->requests_size - 1;
1357 }
1358
1359 int
1360 vapi_get_payload_offset (vapi_msg_id_t id)
1361 {
1362   assert (id < __vapi_metadata.count);
1363   return __vapi_metadata.msgs[id]->payload_offset;
1364 }
1365
1366 void (*vapi_get_swap_to_host_func (vapi_msg_id_t id)) (void *msg)
1367 {
1368   assert (id < __vapi_metadata.count);
1369   return __vapi_metadata.msgs[id]->swap_to_host;
1370 }
1371
1372 void (*vapi_get_swap_to_be_func (vapi_msg_id_t id)) (void *msg)
1373 {
1374   assert (id < __vapi_metadata.count);
1375   return __vapi_metadata.msgs[id]->swap_to_be;
1376 }
1377
1378 size_t
1379 vapi_get_context_offset (vapi_msg_id_t id)
1380 {
1381   assert (id < __vapi_metadata.count);
1382   return __vapi_metadata.msgs[id]->context_offset;
1383 }
1384
1385 vapi_msg_id_t
1386 vapi_register_msg (vapi_message_desc_t * msg)
1387 {
1388   int i = 0;
1389   for (i = 0; i < __vapi_metadata.count; ++i)
1390     {
1391       if (!strcmp
1392           (msg->name_with_crc, __vapi_metadata.msgs[i]->name_with_crc))
1393         {
1394           /* this happens if somebody is linking together several objects while
1395            * using the static inline headers, just fill in the already
1396            * assigned id here so that all the objects are in sync */
1397           msg->id = __vapi_metadata.msgs[i]->id;
1398           return msg->id;
1399         }
1400     }
1401   vapi_msg_id_t id = __vapi_metadata.count;
1402   ++__vapi_metadata.count;
1403   __vapi_metadata.msgs =
1404     realloc (__vapi_metadata.msgs,
1405              sizeof (*__vapi_metadata.msgs) * __vapi_metadata.count);
1406   __vapi_metadata.msgs[id] = msg;
1407   size_t s = strlen (msg->name_with_crc);
1408   if (s > __vapi_metadata.max_len_name_with_crc)
1409     {
1410       __vapi_metadata.max_len_name_with_crc = s;
1411     }
1412   msg->id = id;
1413   return id;
1414 }
1415
1416 vapi_error_e
1417 vapi_producer_lock (vapi_ctx_t ctx)
1418 {
1419   int mrv;
1420   if (0 != (mrv = pthread_mutex_lock (&ctx->requests_mutex)))
1421     {
1422       VAPI_DBG ("pthread_mutex_lock() failed, rv=%d:%s", mrv, strerror (mrv));
1423       (void) mrv;               /* avoid warning if the above debug is not enabled */
1424       return VAPI_MUTEX_FAILURE;
1425     }
1426   return VAPI_OK;
1427 }
1428
1429 vapi_error_e
1430 vapi_producer_unlock (vapi_ctx_t ctx)
1431 {
1432   int mrv;
1433   if (0 != (mrv = pthread_mutex_unlock (&ctx->requests_mutex)))
1434     {
1435       VAPI_DBG ("pthread_mutex_unlock() failed, rv=%d:%s", mrv,
1436                 strerror (mrv));
1437       (void) mrv;               /* avoid warning if the above debug is not enabled */
1438       return VAPI_MUTEX_FAILURE;
1439     }
1440   return VAPI_OK;
1441 }
1442
1443 size_t
1444 vapi_get_message_count ()
1445 {
1446   return __vapi_metadata.count;
1447 }
1448
1449 const char *
1450 vapi_get_msg_name (vapi_msg_id_t id)
1451 {
1452   return __vapi_metadata.msgs[id]->name;
1453 }
1454
1455 void
1456 vapi_stop_rx_thread (vapi_ctx_t ctx)
1457 {
1458   if (!ctx || !ctx->connected || !ctx->vl_input_queue)
1459     {
1460       return;
1461     }
1462
1463   vl_client_stop_rx_thread (ctx->vl_input_queue);
1464 }
1465 /*
1466  * fd.io coding-style-patch-verification: ON
1467  *
1468  * Local Variables:
1469  * eval: (c-set-style "gnu")
1470  * End:
1471  */