VPP-534 Remove references to DTOs created in JVpp
[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 "io_fd_vpp_jvpp_VppJNIConnection.h"
25 #include "io_fd_vpp_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                     "(Lio/fd/vpp/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             (*env)->DeleteLocalRef(env, dto);
161         }
162     }
163
164     out: rm->control_ping_result_ready = 1;
165 }
166
167 static int send_initial_control_ping() {
168     f64 timeout;
169     clib_time_t clib_time;
170     vl_api_control_ping_t * mp;
171     jvpp_main_t * jm = &jvpp_main;
172     jvpp_registry_main_t * rm = &jvpp_registry_main;
173
174     clib_time_init(&clib_time);
175
176     rm->control_ping_result_ready = 0;
177     mp = vl_msg_api_alloc(sizeof(*mp));
178     memset(mp, 0, sizeof(*mp));
179     mp->_vl_msg_id = ntohs(VL_API_CONTROL_PING);
180     mp->client_index = jm->my_client_index;
181
182     // send message:
183     vl_msg_api_send_shmem(jm->vl_input_queue, (u8 *) &mp);
184
185     // wait for results: Current time + 10 seconds is the timeout
186     timeout = clib_time_now(&clib_time) + 10.0;
187     int rv = VNET_API_ERROR_RESPONSE_NOT_READY;
188     while (clib_time_now(&clib_time) < timeout) {
189         if (rm->control_ping_result_ready == 1) {
190             rv = rm->control_ping_retval;
191             break;
192         }
193     }
194
195     if (rv != 0) {
196         clib_warning("common: first control ping failed: %d", rv);
197     }
198
199     return rv;
200 }
201
202 static int connect_to_vpe(char *name) {
203     jvpp_main_t * jm = &jvpp_main;
204     api_main_t * am = &api_main;
205
206     if (vl_client_connect_to_vlib("/vpe-api", name, 32) < 0)
207         return -1;
208
209     jm->my_client_index = am->my_client_index;
210
211     jm->vl_input_queue = am->shmem_hdr->vl_input_queue;
212
213     vl_msg_api_set_handlers(VL_API_CONTROL_PING_REPLY, "control_ping_reply",
214             vl_api_control_ping_reply_t_handler, vl_noop_handler,
215             vl_api_control_ping_reply_t_endian,
216             vl_api_control_ping_reply_t_print,
217             sizeof(vl_api_control_ping_reply_t), 1);
218
219     send_initial_control_ping();
220
221     return 0;
222 }
223
224 JNIEXPORT jobject JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientConnect(
225         JNIEnv *env, jclass obj, jstring clientName) {
226     int rv;
227     const char *client_name;
228     void vl_msg_reply_handler_hookup(void);
229     jvpp_main_t * jm = &jvpp_main;
230     jvpp_registry_main_t * rm = &jvpp_registry_main;
231
232     jclass connectionInfoClass = (*env)->FindClass(env,
233             "io/fd/vpp/jvpp/VppJNIConnection$ConnectionInfo");
234     jmethodID connectionInfoConstructor = (*env)->GetMethodID(env,
235             connectionInfoClass, "<init>", "(JII)V");
236
237     /*
238      * Bail out now if we're not running as root
239      */
240     if (geteuid() != 0) {
241         return (*env)->NewObject(env, connectionInfoClass,
242                 connectionInfoConstructor, 0, 0,
243                 VNET_API_ERROR_NOT_RUNNING_AS_ROOT);
244     }
245
246     if (rm->is_connected) {
247         return (*env)->NewObject(env, connectionInfoClass,
248                 connectionInfoConstructor, 0, 0,
249                 VNET_API_ERROR_ALREADY_CONNECTED);
250     }
251
252     client_name = (*env)->GetStringUTFChars(env, clientName, 0);
253     if (!client_name) {
254         return (*env)->NewObject(env, connectionInfoClass,
255                 connectionInfoConstructor, 0, 0, VNET_API_ERROR_INVALID_VALUE);
256     }
257
258     rv = connect_to_vpe((char *) client_name);
259
260     if (rv < 0)
261         clib_warning("connection failed, rv %d", rv);
262
263     (*env)->ReleaseStringUTFChars(env, clientName, client_name);
264
265     return (*env)->NewObject(env, connectionInfoClass,
266             connectionInfoConstructor, (jlong) jm->vl_input_queue,
267             (jint) jm->my_client_index, (jint) rv);
268 }
269
270 JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_JVppRegistryImpl_controlPing0(
271         JNIEnv *env, jobject regstryObject) {
272     jvpp_main_t * jm = &jvpp_main;
273     vl_api_control_ping_t * mp;
274     u32 my_context_id = vppjni_get_context_id(&jvpp_main);
275     jvpp_registry_main_t * rm = &jvpp_registry_main;
276
277     if (rm->registryObject == 0) {
278         rm->registryObject = (*env)->NewGlobalRef(env, regstryObject);
279     }
280     if (rm->registryClass == 0) {
281         rm->registryClass = (jclass) (*env)->NewGlobalRef(env,
282                 (*env)->GetObjectClass(env, regstryObject));
283     }
284
285     mp = vl_msg_api_alloc(sizeof(*mp));
286     memset(mp, 0, sizeof(*mp));
287     mp->_vl_msg_id = ntohs(VL_API_CONTROL_PING);
288     mp->client_index = jm->my_client_index;
289     mp->context = clib_host_to_net_u32(my_context_id);
290
291     // send message:
292     vl_msg_api_send_shmem(jm->vl_input_queue, (u8 *) &mp);
293     return my_context_id;
294 }
295
296 JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientDisconnect(
297         JNIEnv *env, jclass clazz) {
298     jvpp_registry_main_t * rm = &jvpp_registry_main;
299     rm->is_connected = 0; // TODO make thread safe
300     vl_client_disconnect_from_vlib();
301
302     // cleanup:
303     if (rm->registryObject) {
304         (*env)->DeleteGlobalRef(env, rm->registryObject);
305         rm->registryObject = 0;
306     }
307     if (rm->registryClass) {
308         (*env)->DeleteGlobalRef(env, rm->registryClass);
309         rm->registryClass = 0;
310     }
311 }
312
313 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
314     jvpp_main_t * jm = &jvpp_main;
315     jvpp_registry_main_t * rm = &jvpp_registry_main;
316     JNIEnv* env;
317
318     if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
319         return JNI_EVERSION;
320     }
321
322     rm->controlPingReplyClass = (jclass) (*env)->NewGlobalRef(env,
323             (*env)->FindClass(env, "io/fd/vpp/jvpp/dto/ControlPingReply"));
324     if ((*env)->ExceptionCheck(env)) {
325         (*env)->ExceptionDescribe(env);
326         clib_warning("Failed to cache class references\n");
327         return JNI_ERR;
328     }
329
330     rm->callbackExceptionClass = (jclass) (*env)->NewGlobalRef(env,
331             (*env)->FindClass(env, "io/fd/vpp/jvpp/VppCallbackException"));
332     if ((*env)->ExceptionCheck(env)) {
333         (*env)->ExceptionDescribe(env);
334         return JNI_ERR;
335     }
336
337     jm->jvm = vm;
338     return JNI_VERSION_1_8;
339 }
340
341 void JNI_OnUnload(JavaVM *vm, void *reserved) {
342     jvpp_main_t * jm = &jvpp_main;
343     JNIEnv* env;
344     if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
345         return;
346     }
347
348     jm->jenv = NULL;
349     jm->jvm = NULL;
350 }