f7f5561910a22302366f0972d93237aae4841f15
[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     return send_initial_control_ping();
220 }
221
222 JNIEXPORT jobject JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientConnect(
223         JNIEnv *env, jclass obj, jstring clientName) {
224     int rv;
225     const char *client_name;
226     void vl_msg_reply_handler_hookup(void);
227     jvpp_main_t * jm = &jvpp_main;
228     jvpp_registry_main_t * rm = &jvpp_registry_main;
229
230     jclass connectionInfoClass = (*env)->FindClass(env,
231             "io/fd/vpp/jvpp/VppJNIConnection$ConnectionInfo");
232     jmethodID connectionInfoConstructor = (*env)->GetMethodID(env,
233             connectionInfoClass, "<init>", "(JII)V");
234
235     /*
236      * Bail out now if we're not running as root
237      */
238     if (geteuid() != 0) {
239         return (*env)->NewObject(env, connectionInfoClass,
240                 connectionInfoConstructor, 0, 0,
241                 VNET_API_ERROR_NOT_RUNNING_AS_ROOT);
242     }
243
244     if (rm->is_connected) {
245         return (*env)->NewObject(env, connectionInfoClass,
246                 connectionInfoConstructor, 0, 0,
247                 VNET_API_ERROR_ALREADY_CONNECTED);
248     }
249
250     client_name = (*env)->GetStringUTFChars(env, clientName, 0);
251     if (!client_name) {
252         return (*env)->NewObject(env, connectionInfoClass,
253                 connectionInfoConstructor, 0, 0, VNET_API_ERROR_INVALID_VALUE);
254     }
255
256     rv = connect_to_vpe((char *) client_name);
257
258     if (rv < 0)
259         clib_warning("connection failed, rv %d", rv);
260
261     (*env)->ReleaseStringUTFChars(env, clientName, client_name);
262
263     return (*env)->NewObject(env, connectionInfoClass,
264             connectionInfoConstructor, (jlong) jm->vl_input_queue,
265             (jint) jm->my_client_index, (jint) rv);
266 }
267
268 JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_JVppRegistryImpl_controlPing0(
269         JNIEnv *env, jobject regstryObject) {
270     jvpp_main_t * jm = &jvpp_main;
271     vl_api_control_ping_t * mp;
272     u32 my_context_id = vppjni_get_context_id(&jvpp_main);
273     jvpp_registry_main_t * rm = &jvpp_registry_main;
274
275     if (rm->registryObject == 0) {
276         rm->registryObject = (*env)->NewGlobalRef(env, regstryObject);
277     }
278     if (rm->registryClass == 0) {
279         rm->registryClass = (jclass) (*env)->NewGlobalRef(env,
280                 (*env)->GetObjectClass(env, regstryObject));
281     }
282
283     mp = vl_msg_api_alloc(sizeof(*mp));
284     memset(mp, 0, sizeof(*mp));
285     mp->_vl_msg_id = ntohs(VL_API_CONTROL_PING);
286     mp->client_index = jm->my_client_index;
287     mp->context = clib_host_to_net_u32(my_context_id);
288
289     // send message:
290     vl_msg_api_send_shmem(jm->vl_input_queue, (u8 *) &mp);
291     return my_context_id;
292 }
293
294 JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientDisconnect(
295         JNIEnv *env, jclass clazz) {
296     jvpp_registry_main_t * rm = &jvpp_registry_main;
297     rm->is_connected = 0; // TODO make thread safe
298     vl_client_disconnect_from_vlib();
299
300     // cleanup:
301     if (rm->registryObject) {
302         (*env)->DeleteGlobalRef(env, rm->registryObject);
303         rm->registryObject = 0;
304     }
305     if (rm->registryClass) {
306         (*env)->DeleteGlobalRef(env, rm->registryClass);
307         rm->registryClass = 0;
308     }
309 }
310
311 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
312     jvpp_main_t * jm = &jvpp_main;
313     jvpp_registry_main_t * rm = &jvpp_registry_main;
314     JNIEnv* env;
315
316     if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
317         return JNI_EVERSION;
318     }
319
320     rm->controlPingReplyClass = (jclass) (*env)->NewGlobalRef(env,
321             (*env)->FindClass(env, "io/fd/vpp/jvpp/dto/ControlPingReply"));
322     if ((*env)->ExceptionCheck(env)) {
323         (*env)->ExceptionDescribe(env);
324         clib_warning("Failed to cache class references\n");
325         return JNI_ERR;
326     }
327
328     rm->callbackExceptionClass = (jclass) (*env)->NewGlobalRef(env,
329             (*env)->FindClass(env, "io/fd/vpp/jvpp/VppCallbackException"));
330     if ((*env)->ExceptionCheck(env)) {
331         (*env)->ExceptionDescribe(env);
332         return JNI_ERR;
333     }
334
335     jm->jvm = vm;
336     return JNI_VERSION_1_8;
337 }
338
339 void JNI_OnUnload(JavaVM *vm, void *reserved) {
340     jvpp_main_t * jm = &jvpp_main;
341     JNIEnv* env;
342     if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
343         return;
344     }
345
346     jm->jenv = NULL;
347     jm->jvm = NULL;
348 }