VPP-205: jvpp plugin support.
[vpp.git] / vpp-api / java / jvpp-registry / jvpp_registry.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 #define _GNU_SOURCE /* for strcasestr(3) */
16 #include <vnet/vnet.h>
17
18 #define vl_api_version(n,v) static u32 vpe_api_version = (v);
19 #include <vpp-api/vpe.api.h>
20 #undef vl_api_version
21
22 #include <jni.h>
23 #include <jvpp-common/jvpp_common.h>
24 #include "org_openvpp_jvpp_VppJNIConnection.h"
25 #include "org_openvpp_jvpp_JVppRegistryImpl.h"
26
27 #include <vpp-api/vpe_msg_enum.h>
28 #define vl_typedefs             /* define message structures */
29 #include <vpp-api/vpe_all_api_h.h>
30 #undef vl_typedefs
31
32 #define vl_endianfun
33 #include <vpp-api/vpe_all_api_h.h>
34 #undef vl_endianfun
35
36 /* instantiate all the print functions we know about */
37 #define vl_print(handle, ...)
38 #define vl_printfun
39 #include <vpp-api/vpe_all_api_h.h>
40 #undef vl_printfun
41
42 /*
43  * The Java runtime isn't compile w/ -fstack-protector,
44  * so we have to supply missing external references for the
45  * regular vpp libraries.
46  */
47 void __stack_chk_guard(void) __attribute__((weak));
48 void __stack_chk_guard(void) {
49 }
50
51 typedef struct {
52     /* UThread attachment */
53     volatile u32 control_ping_result_ready;
54     volatile i32 control_ping_retval;
55
56     /* Control poing callback */
57     jobject registryObject;
58     jclass registryClass;
59     jclass controlPingReplyClass;
60     jclass callbackExceptionClass;
61
62     /* Thread cleanup */
63     pthread_key_t cleanup_rx_thread_key;
64
65     /* Connected indication */
66     volatile u8 is_connected;
67 } jvpp_registry_main_t;
68
69 jvpp_registry_main_t jvpp_registry_main __attribute__((aligned (64)));
70
71 void vl_client_add_api_signatures(vl_api_memclnt_create_t *mp) {
72     /*
73      * Send the main API signature in slot 0. This bit of code must
74      * match the checks in ../vpe/api/api.c: vl_msg_api_version_check().
75      */
76     mp->api_versions[0] = clib_host_to_net_u32(vpe_api_version);
77 }
78
79 /* cleanup handler for RX thread */
80 static_always_inline void cleanup_rx_thread(void *arg) {
81     jvpp_main_t * jm = &jvpp_main;
82     jvpp_registry_main_t * rm = &jvpp_registry_main;
83
84     vppjni_lock(jm, 99);
85
86     int getEnvStat = (*jm->jvm)->GetEnv(jm->jvm, (void **) &(jm->jenv),
87     JNI_VERSION_1_8);
88     if (getEnvStat == JNI_EVERSION) {
89         clib_warning("Unsupported JNI version\n");
90         rm->control_ping_retval = VNET_API_ERROR_UNSUPPORTED_JNI_VERSION;
91         goto out;
92     } else if (getEnvStat != JNI_EDETACHED) {
93         (*jm->jvm)->DetachCurrentThread(jm->jvm);
94     }
95     out: vppjni_unlock(jm);
96 }
97
98 static void vl_api_control_ping_reply_t_handler(
99         vl_api_control_ping_reply_t * mp) {
100     jvpp_main_t * jm = &jvpp_main;
101     jvpp_registry_main_t * rm = &jvpp_registry_main;
102     char was_thread_connected = 0;
103
104     // attach to java thread if not attached
105     int getEnvStat = (*jm->jvm)->GetEnv(jm->jvm, (void **) &(jm->jenv),
106             JNI_VERSION_1_8);
107     if (getEnvStat == JNI_EDETACHED) {
108         if ((*jm->jvm)->AttachCurrentThread(jm->jvm, (void **) &(jm->jenv),
109                 NULL) != 0) {
110             clib_warning("Failed to attach thread\n");
111             rm->control_ping_retval =
112                     VNET_API_ERROR_FAILED_TO_ATTACH_TO_JAVA_THREAD;
113             goto out;
114         }
115
116         // workaround as we can't use pthread_cleanup_push
117         pthread_key_create(&rm->cleanup_rx_thread_key, cleanup_rx_thread);
118         // destructor is only called if the value of key is non null
119         pthread_setspecific(rm->cleanup_rx_thread_key, (void *) 1);
120         was_thread_connected = 1;
121     } else if (getEnvStat == JNI_EVERSION) {
122         clib_warning("Unsupported JNI version\n");
123         rm->control_ping_retval = VNET_API_ERROR_UNSUPPORTED_JNI_VERSION;
124         goto out;
125     }
126
127     if (was_thread_connected == 0) {
128         JNIEnv *env = jm->jenv;
129         if (mp->retval < 0) {
130             call_on_error("controlPing", mp->context, mp->retval,
131                     rm->registryClass, rm->registryObject,
132                     rm->callbackExceptionClass);
133         } else {
134             jmethodID constructor = (*env)->GetMethodID(env,
135                     rm->controlPingReplyClass, "<init>", "()V");
136             jmethodID callbackMethod = (*env)->GetMethodID(env,
137                     rm->registryClass, "onControlPingReply",
138                     "(Lorg/openvpp/jvpp/dto/ControlPingReply;)V");
139
140             jobject dto = (*env)->NewObject(env, rm->controlPingReplyClass,
141                     constructor);
142
143             jfieldID contextFieldId = (*env)->GetFieldID(env,
144                     rm->controlPingReplyClass, "context", "I");
145             (*env)->SetIntField(env, dto, contextFieldId,
146                     clib_net_to_host_u32(mp->context));
147
148             jfieldID clientIndexFieldId = (*env)->GetFieldID(env,
149                     rm->controlPingReplyClass, "clientIndex", "I");
150             (*env)->SetIntField(env, dto, clientIndexFieldId,
151                     clib_net_to_host_u32(mp->client_index));
152
153             jfieldID vpePidFieldId = (*env)->GetFieldID(env,
154                     rm->controlPingReplyClass, "vpePid", "I");
155             (*env)->SetIntField(env, dto, vpePidFieldId,
156                     clib_net_to_host_u32(mp->vpe_pid));
157
158             (*env)->CallVoidMethod(env, rm->registryObject, callbackMethod,
159                     dto);
160         }
161     }
162
163     out: rm->control_ping_result_ready = 1;
164 }
165
166 static int send_initial_control_ping() {
167     f64 timeout;
168     clib_time_t clib_time;
169     vl_api_control_ping_t * mp;
170     jvpp_main_t * jm = &jvpp_main;
171     jvpp_registry_main_t * rm = &jvpp_registry_main;
172
173     clib_time_init(&clib_time);
174
175     rm->control_ping_result_ready = 0;
176     mp = vl_msg_api_alloc(sizeof(*mp));
177     memset(mp, 0, sizeof(*mp));
178     mp->_vl_msg_id = ntohs(VL_API_CONTROL_PING);
179     mp->client_index = jm->my_client_index;
180
181     // send message:
182     vl_msg_api_send_shmem(jm->vl_input_queue, (u8 *) &mp);
183
184     // wait for results: Current time + 10 seconds is the timeout
185     timeout = clib_time_now(&clib_time) + 10.0;
186     int rv = VNET_API_ERROR_RESPONSE_NOT_READY;
187     while (clib_time_now(&clib_time) < timeout) {
188         if (rm->control_ping_result_ready == 1) {
189             rv = rm->control_ping_retval;
190             break;
191         }
192     }
193
194     if (rv != 0) {
195         clib_warning("common: first control ping failed: %d", rv);
196     }
197
198     return rv;
199 }
200
201 static int connect_to_vpe(char *name) {
202     jvpp_main_t * jm = &jvpp_main;
203     api_main_t * am = &api_main;
204
205     if (vl_client_connect_to_vlib("/vpe-api", name, 32) < 0)
206         return -1;
207
208     jm->my_client_index = am->my_client_index;
209
210     jm->vl_input_queue = am->shmem_hdr->vl_input_queue;
211
212     vl_msg_api_set_handlers(VL_API_CONTROL_PING_REPLY, "control_ping_reply",
213             vl_api_control_ping_reply_t_handler, vl_noop_handler,
214             vl_api_control_ping_reply_t_endian,
215             vl_api_control_ping_reply_t_print,
216             sizeof(vl_api_control_ping_reply_t), 1);
217
218     send_initial_control_ping();
219
220     return 0;
221 }
222
223 JNIEXPORT jobject JNICALL Java_org_openvpp_jvpp_VppJNIConnection_clientConnect(
224         JNIEnv *env, jclass obj, jstring clientName) {
225     int rv;
226     const char *client_name;
227     void vl_msg_reply_handler_hookup(void);
228     jvpp_main_t * jm = &jvpp_main;
229     jvpp_registry_main_t * rm = &jvpp_registry_main;
230
231     jclass connectionInfoClass = (*env)->FindClass(env,
232             "org/openvpp/jvpp/VppJNIConnection$ConnectionInfo");
233     jmethodID connectionInfoConstructor = (*env)->GetMethodID(env,
234             connectionInfoClass, "<init>", "(JII)V");
235
236     /*
237      * Bail out now if we're not running as root
238      */
239     if (geteuid() != 0) {
240         return (*env)->NewObject(env, connectionInfoClass,
241                 connectionInfoConstructor, 0, 0,
242                 VNET_API_ERROR_NOT_RUNNING_AS_ROOT);
243     }
244
245     if (rm->is_connected) {
246         return (*env)->NewObject(env, connectionInfoClass,
247                 connectionInfoConstructor, 0, 0,
248                 VNET_API_ERROR_ALREADY_CONNECTED);
249     }
250
251     client_name = (*env)->GetStringUTFChars(env, clientName, 0);
252     if (!client_name) {
253         return (*env)->NewObject(env, connectionInfoClass,
254                 connectionInfoConstructor, 0, 0, VNET_API_ERROR_INVALID_VALUE);
255     }
256
257     rv = connect_to_vpe((char *) client_name);
258
259     if (rv < 0)
260         clib_warning("connection failed, rv %d", rv);
261
262     (*env)->ReleaseStringUTFChars(env, clientName, client_name);
263
264     return (*env)->NewObject(env, connectionInfoClass,
265             connectionInfoConstructor, (jlong) jm->vl_input_queue,
266             (jint) jm->my_client_index, (jint) rv);
267 }
268
269 JNIEXPORT jint JNICALL Java_org_openvpp_jvpp_JVppRegistryImpl_controlPing0(
270         JNIEnv *env, jobject regstryObject) {
271     jvpp_main_t * jm = &jvpp_main;
272     vl_api_control_ping_t * mp;
273     u32 my_context_id = vppjni_get_context_id(&jvpp_main);
274     jvpp_registry_main_t * rm = &jvpp_registry_main;
275
276     if (rm->registryObject == 0) {
277         rm->registryObject = (*env)->NewGlobalRef(env, regstryObject);
278     }
279     if (rm->registryClass == 0) {
280         rm->registryClass = (jclass) (*env)->NewGlobalRef(env,
281                 (*env)->GetObjectClass(env, regstryObject));
282     }
283
284     mp = vl_msg_api_alloc(sizeof(*mp));
285     memset(mp, 0, sizeof(*mp));
286     mp->_vl_msg_id = ntohs(VL_API_CONTROL_PING);
287     mp->client_index = jm->my_client_index;
288     mp->context = clib_host_to_net_u32(my_context_id);
289
290     // send message:
291     vl_msg_api_send_shmem(jm->vl_input_queue, (u8 *) &mp);
292     return my_context_id;
293 }
294
295 JNIEXPORT void JNICALL Java_org_openvpp_jvpp_VppJNIConnection_clientDisconnect(
296         JNIEnv *env, jclass clazz) {
297     jvpp_registry_main_t * rm = &jvpp_registry_main;
298     rm->is_connected = 0; // TODO make thread safe
299     vl_client_disconnect_from_vlib();
300
301     // cleanup:
302     if (rm->registryObject) {
303         (*env)->DeleteGlobalRef(env, rm->registryObject);
304         rm->registryObject = 0;
305     }
306     if (rm->registryClass) {
307         (*env)->DeleteGlobalRef(env, rm->registryClass);
308         rm->registryClass = 0;
309     }
310 }
311
312 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
313     jvpp_main_t * jm = &jvpp_main;
314     jvpp_registry_main_t * rm = &jvpp_registry_main;
315     JNIEnv* env;
316
317     if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
318         return JNI_EVERSION;
319     }
320
321     rm->controlPingReplyClass = (jclass) (*env)->NewGlobalRef(env,
322             (*env)->FindClass(env, "org/openvpp/jvpp/dto/ControlPingReply"));
323     if ((*env)->ExceptionCheck(env)) {
324         (*env)->ExceptionDescribe(env);
325         clib_warning("Failed to cache class references\n");
326         return JNI_ERR;
327     }
328
329     rm->callbackExceptionClass = (jclass) (*env)->NewGlobalRef(env,
330             (*env)->FindClass(env, "org/openvpp/jvpp/VppCallbackException"));
331     if ((*env)->ExceptionCheck(env)) {
332         (*env)->ExceptionDescribe(env);
333         return JNI_ERR;
334     }
335
336     jm->jvm = vm;
337     return JNI_VERSION_1_8;
338 }
339
340 void JNI_OnUnload(JavaVM *vm, void *reserved) {
341     jvpp_main_t * jm = &jvpp_main;
342     JNIEnv* env;
343     if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
344         return;
345     }
346
347     jm->jenv = NULL;
348     jm->jvm = NULL;
349 }