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