Add client-side msg_name_and_crc -> msg_index table
[vpp.git] / vpp-api / python / pneum / pneum.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stddef.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/mman.h>
21 #include <sys/stat.h>
22 #include <netinet/in.h>
23 #include <netdb.h>
24 #include <signal.h>
25 #include <setjmp.h>
26 #include <stdbool.h>
27
28 #include <vnet/vnet.h>
29 #include <vlib/vlib.h>
30 #include <vlib/unix/unix.h>
31 #include <vlibapi/api.h>
32 #include <vlibmemory/api.h>
33
34 #include <vpp-api/vpe_msg_enum.h>
35
36 #include "pneum.h"
37
38 #define vl_typedefs             /* define message structures */
39 #include <vpp-api/vpe_all_api_h.h>
40 #undef vl_typedefs
41
42 #define vl_endianfun             /* define message structures */
43 #include <vpp-api/vpe_all_api_h.h>
44 #undef vl_endianfun
45
46 typedef struct {
47   u8 rx_thread_jmpbuf_valid;
48   u8 connected_to_vlib;
49   jmp_buf rx_thread_jmpbuf;
50   pthread_t rx_thread_handle;
51 } pneum_main_t;
52
53 pneum_main_t pneum_main;
54
55 pneum_callback_t pneum_callback;
56
57 /*
58  * Satisfy external references when -lvlib is not available.
59  */
60 void vlib_cli_output (struct vlib_main_t * vm, char * fmt, ...)
61 {
62   clib_warning ("vlib_cli_output called...");
63 }
64
65 void
66 pneum_free (void * msg)
67 {
68   vl_msg_api_free (msg);
69 }
70
71 static void
72 pneum_api_handler (void *msg)
73 {
74   u16 id = ntohs(*((u16 *)msg));
75   if (id == VL_API_RX_THREAD_EXIT) {
76     pneum_main_t *pm = &pneum_main;
77     vl_msg_api_free(msg);
78     longjmp(pm->rx_thread_jmpbuf, 1);
79   }
80   msgbuf_t *msgbuf = (msgbuf_t *)(((u8 *)msg) - offsetof(msgbuf_t, data));
81   int l = ntohl(msgbuf->data_len);
82   if (l == 0)
83     clib_warning("Message ID %d has wrong length: %d\n", id, l);
84
85   /* Call Python callback */
86   ASSERT(pneum_callback);
87   (pneum_callback)(msg, l);
88   pneum_free(msg);
89 }
90
91 static void *
92 pneum_rx_thread_fn (void *arg)
93 {
94   unix_shared_memory_queue_t *q;
95   pneum_main_t *pm = &pneum_main;
96   api_main_t *am = &api_main;
97   uword msg;
98
99   q = am->vl_input_queue;
100
101   /* So we can make the rx thread terminate cleanly */
102   if (setjmp(pm->rx_thread_jmpbuf) == 0) {
103     pm->rx_thread_jmpbuf_valid = 1;
104     while (1)
105       while (!unix_shared_memory_queue_sub(q, (u8 *)&msg, 0))
106         pneum_api_handler((void *)msg);
107   }
108   pthread_exit(0);
109 }
110
111 uword *
112 pneum_msg_table_get_hash (void)
113 {
114   api_main_t *am = &api_main;
115   return (am->msg_index_by_name_and_crc);
116 }
117
118 int
119 pneum_msg_table_size(void)
120 {
121   api_main_t *am = &api_main;
122   return hash_elts(am->msg_index_by_name_and_crc);
123 }
124
125 int
126 pneum_connect (char * name, char * chroot_prefix, pneum_callback_t cb)
127 {
128   int rv = 0;
129   pneum_main_t *pm = &pneum_main;
130
131   if (chroot_prefix != NULL)
132     vl_set_memory_root_path (chroot_prefix);
133
134   if ((rv = vl_client_api_map("/vpe-api"))) {
135     clib_warning ("vl_client_api map rv %d", rv);
136     return rv;
137   }
138
139   if (vl_client_connect(name, 0, 32) < 0) {
140     vl_client_api_unmap();
141     return (-1);
142   }
143
144   if (cb) {
145     /* Start the rx queue thread */
146     rv = pthread_create(&pm->rx_thread_handle, NULL, pneum_rx_thread_fn, 0);
147     if (rv) {
148       clib_warning("pthread_create returned %d", rv);
149       vl_client_api_unmap();
150       return (-1);
151     }
152     pneum_callback = cb;
153   }
154
155   pm->connected_to_vlib = 1;
156
157   return (0);
158 }
159
160 int
161 pneum_disconnect (void)
162 {
163   api_main_t *am = &api_main;
164   pneum_main_t *pm = &pneum_main;
165
166   if (pm->rx_thread_jmpbuf_valid) {
167     vl_api_rx_thread_exit_t *ep;
168     uword junk;
169     ep = vl_msg_api_alloc (sizeof (*ep));
170     ep->_vl_msg_id = ntohs(VL_API_RX_THREAD_EXIT);
171     vl_msg_api_send_shmem(am->vl_input_queue, (u8 *)&ep);
172     pthread_join(pm->rx_thread_handle, (void **) &junk);
173   }
174   if (pm->connected_to_vlib) {
175     vl_client_disconnect();
176     vl_client_api_unmap();
177     pneum_callback = 0;
178   }
179   memset (pm, 0, sizeof (*pm));
180
181   return (0);
182 }
183
184 int
185 pneum_read (char **p, int *l)
186 {
187   unix_shared_memory_queue_t *q;
188   api_main_t *am = &api_main;
189   pneum_main_t *pm = &pneum_main;
190   uword msg;
191
192   if (!pm->connected_to_vlib) return -1;
193
194   *l = 0;
195
196   if (am->our_pid == 0) return (-1);
197
198   q = am->vl_input_queue;
199   int rv = unix_shared_memory_queue_sub(q, (u8 *)&msg, 0);
200   if (rv == 0) {
201     u16 msg_id = ntohs(*((u16 *)msg));
202     msgbuf_t *msgbuf = (msgbuf_t *)(((u8 *)msg) - offsetof(msgbuf_t, data));
203     *l = ntohl(msgbuf->data_len);
204     if (*l == 0) {
205       printf("Unregistered API message: %d\n", msg_id);
206       return (-1);
207     }
208     *p = (char *)msg;
209   } else {
210     printf("Read failed with %d\n", rv);
211   }
212   return (rv);
213 }
214
215 /*
216  * XXX: Makes the assumption that client_index is the first member
217  */
218 typedef VL_API_PACKED(struct _vl_api_header {
219   u16 _vl_msg_id;
220   u32 client_index;
221 }) vl_api_header_t;
222
223 static unsigned int
224 pneum_client_index (void)
225 {
226   return (api_main.my_client_index);
227 }
228
229 int
230 pneum_write (char *p, int l)
231 {
232   int rv = -1;
233   api_main_t *am = &api_main;
234   vl_api_header_t *mp = vl_msg_api_alloc(l);
235   unix_shared_memory_queue_t *q;
236   pneum_main_t *pm = &pneum_main;
237
238   if (!pm->connected_to_vlib) return -1;
239   if (!mp) return (-1);
240   memcpy(mp, p, l);
241   mp->client_index = pneum_client_index();
242   q = am->shmem_hdr->vl_input_queue;
243   rv = unix_shared_memory_queue_add(q, (u8 *)&mp, 0);
244   if (rv != 0) {
245     printf("vpe_api_write fails: %d\n", rv);
246     /* Clear message */
247     pneum_free(mp);
248   }
249   return (rv);
250 }