1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "tcn.h"
18 #include "apr_version.h"
19 #include "apr_file_io.h"
20 #include "apr_mmap.h"
21 #include "apr_atomic.h"
22 #include "apr_poll.h"
23
24 #include "tcn_version.h"
25
26 #ifdef WIN32
27 #include <Windows.h>
28 #endif
29
30 #ifdef DARWIN
31 #include <pthread.h>
32 #endif
33
34 #ifdef __FreeBSD__
35 #include <pthread_np.h>
36 #endif
37
38 #ifdef __linux__
39 #include <sys/syscall.h>
40 #endif
41
42 #ifdef TCN_DO_STATISTICS
43 extern void sp_poll_dump_statistics();
44 extern void sp_network_dump_statistics();
45 extern void ssl_network_dump_statistics();
46 #endif
47
48 apr_pool_t *tcn_global_pool = NULL;
49 static JavaVM *tcn_global_vm = NULL;
50
51 static jclass jString_class;
52 static jclass jFinfo_class;
53 static jclass jAinfo_class;
54 static jmethodID jString_init;
55 static jmethodID jString_getBytes;
56
57 int tcn_parent_pid = 0;
58
59 /* Called by the JVM when APR_JAVA is loaded */
JNI_OnLoad(JavaVM * vm,void * reserved)60 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
61 {
62 JNIEnv *env;
63 void *ppe;
64 apr_version_t apv;
65 int apvn;
66
67 UNREFERENCED(reserved);
68 if ((*vm)->GetEnv(vm, &ppe, JNI_VERSION_1_4)) {
69 return JNI_ERR;
70 }
71 tcn_global_vm = vm;
72 env = (JNIEnv *)ppe;
73 /* Before doing anything else check if we have a valid
74 * APR version. We need version 1.4.3 as minimum.
75 */
76 apr_version(&apv);
77 apvn = apv.major * 1000 + apv.minor * 100 + apv.patch;
78 if (apvn < 1403) {
79 tcn_Throw(env, "Unsupported APR version %s: this tcnative requires at least 1.4.3",
80 apr_version_string());
81 return JNI_ERR;
82 }
83
84 /* Initialize global java.lang.String class */
85 TCN_LOAD_CLASS(env, jString_class, "java/lang/String", JNI_ERR);
86 TCN_LOAD_CLASS(env, jFinfo_class, TCN_FINFO_CLASS, JNI_ERR);
87 TCN_LOAD_CLASS(env, jAinfo_class, TCN_AINFO_CLASS, JNI_ERR);
88
89 TCN_GET_METHOD(env, jString_class, jString_init,
90 "<init>", "([B)V", JNI_ERR);
91 TCN_GET_METHOD(env, jString_class, jString_getBytes,
92 "getBytes", "()[B", JNI_ERR);
93
94 if(tcn_load_finfo_class(env, jFinfo_class) != APR_SUCCESS)
95 return JNI_ERR;
96 if(tcn_load_ainfo_class(env, jAinfo_class) != APR_SUCCESS)
97 return JNI_ERR;
98 #ifdef WIN32
99 {
100 char *ppid = getenv(TCN_PARENT_IDE);
101 if (ppid)
102 tcn_parent_pid = atoi(ppid);
103 }
104 #else
105 tcn_parent_pid = getppid();
106 #endif
107
108 return JNI_VERSION_1_4;
109 }
110
111
112 /* Called by the JVM before the APR_JAVA is unloaded */
JNI_OnUnload(JavaVM * vm,void * reserved)113 JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved)
114 {
115 JNIEnv *env;
116 void *ppe;
117
118 UNREFERENCED(reserved);
119
120 if ((*vm)->GetEnv(vm, &ppe, JNI_VERSION_1_2)) {
121 return;
122 }
123 if (tcn_global_pool) {
124 env = (JNIEnv *)ppe;
125 TCN_UNLOAD_CLASS(env, jString_class);
126 TCN_UNLOAD_CLASS(env, jFinfo_class);
127 TCN_UNLOAD_CLASS(env, jAinfo_class);
128 apr_terminate();
129 }
130 }
131
tcn_new_stringn(JNIEnv * env,const char * str,size_t l)132 jstring tcn_new_stringn(JNIEnv *env, const char *str, size_t l)
133 {
134 jstring result;
135 jbyteArray bytes = 0;
136
137 if (!str)
138 return NULL;
139 if ((*env)->EnsureLocalCapacity(env, 2) < 0) {
140 return NULL; /* out of memory error */
141 }
142 bytes = (*env)->NewByteArray(env, l);
143 if (bytes != NULL) {
144 (*env)->SetByteArrayRegion(env, bytes, 0, l, (jbyte *)str);
145 result = (*env)->NewObject(env, jString_class, jString_init, bytes);
146 (*env)->DeleteLocalRef(env, bytes);
147 return result;
148 } /* else fall through */
149 return NULL;
150 }
151
tcn_new_arrayb(JNIEnv * env,const unsigned char * data,size_t len)152 jbyteArray tcn_new_arrayb(JNIEnv *env, const unsigned char *data, size_t len)
153 {
154 jbyteArray bytes = (*env)->NewByteArray(env, (jsize)len);
155 if (bytes != NULL) {
156 (*env)->SetByteArrayRegion(env, bytes, 0, (jint)len, (jbyte *)data);
157 }
158 return bytes;
159 }
160
tcn_new_arrays(JNIEnv * env,size_t len)161 jobjectArray tcn_new_arrays(JNIEnv *env, size_t len)
162 {
163 return (*env)->NewObjectArray(env, (jsize)len, jString_class, NULL);
164 }
165
tcn_new_string(JNIEnv * env,const char * str)166 jstring tcn_new_string(JNIEnv *env, const char *str)
167 {
168 if (!str)
169 return NULL;
170 else
171 return (*env)->NewStringUTF(env, str);
172 }
173
tcn_get_string(JNIEnv * env,jstring jstr)174 char *tcn_get_string(JNIEnv *env, jstring jstr)
175 {
176 jbyteArray bytes = NULL;
177 jthrowable exc;
178 char *result = NULL;
179
180 if ((*env)->EnsureLocalCapacity(env, 2) < 0) {
181 return NULL; /* out of memory error */
182 }
183 bytes = (*env)->CallObjectMethod(env, jstr, jString_getBytes);
184 exc = (*env)->ExceptionOccurred(env);
185 if (!exc) {
186 jint len = (*env)->GetArrayLength(env, bytes);
187 result = (char *)malloc(len + 1);
188 if (result == NULL) {
189 TCN_THROW_OS_ERROR(env);
190 (*env)->DeleteLocalRef(env, bytes);
191 return 0;
192 }
193 (*env)->GetByteArrayRegion(env, bytes, 0, len, (jbyte *)result);
194 result[len] = '\0'; /* NULL-terminate */
195 }
196 else {
197 (*env)->DeleteLocalRef(env, exc);
198 }
199 (*env)->DeleteLocalRef(env, bytes);
200
201 return result;
202 }
203
tcn_strdup(JNIEnv * env,jstring jstr)204 char *tcn_strdup(JNIEnv *env, jstring jstr)
205 {
206 char *result = NULL;
207 const char *cjstr;
208
209 cjstr = (const char *)((*env)->GetStringUTFChars(env, jstr, 0));
210 if (cjstr) {
211 result = strdup(cjstr);
212 (*env)->ReleaseStringUTFChars(env, jstr, cjstr);
213 }
214 return result;
215 }
216
tcn_pstrdup(JNIEnv * env,jstring jstr,apr_pool_t * pool)217 char *tcn_pstrdup(JNIEnv *env, jstring jstr, apr_pool_t *pool)
218 {
219 char *result = NULL;
220 const char *cjstr;
221
222 cjstr = (const char *)((*env)->GetStringUTFChars(env, jstr, 0));
223 if (cjstr) {
224 result = apr_pstrdup(pool, cjstr);
225 (*env)->ReleaseStringUTFChars(env, jstr, cjstr);
226 }
227 return result;
228 }
229
TCN_IMPLEMENT_CALL(jboolean,Library,initialize)230 TCN_IMPLEMENT_CALL(jboolean, Library, initialize)(TCN_STDARGS)
231 {
232
233 UNREFERENCED_STDARGS;
234 if (!tcn_global_pool) {
235 apr_initialize();
236 if (apr_pool_create(&tcn_global_pool, NULL) != APR_SUCCESS) {
237 return JNI_FALSE;
238 }
239 apr_atomic_init(tcn_global_pool);
240 }
241 return JNI_TRUE;
242 }
243
TCN_IMPLEMENT_CALL(void,Library,terminate)244 TCN_IMPLEMENT_CALL(void, Library, terminate)(TCN_STDARGS)
245 {
246
247 UNREFERENCED_STDARGS;
248 if (tcn_global_pool) {
249 apr_pool_t *p = tcn_global_pool;
250 tcn_global_pool = NULL;
251 #ifdef TCN_DO_STATISTICS
252 fprintf(stderr, "APR Statistical data ....\n");
253 #endif
254 apr_pool_destroy(p);
255 #ifdef TCN_DO_STATISTICS
256 sp_poll_dump_statistics();
257 sp_network_dump_statistics();
258 ssl_network_dump_statistics();
259 fprintf(stderr, "APR Terminated\n");
260 #endif
261 apr_terminate();
262 }
263 }
264
TCN_IMPLEMENT_CALL(jlong,Library,globalPool)265 TCN_IMPLEMENT_CALL(jlong, Library, globalPool)(TCN_STDARGS)
266 {
267 UNREFERENCED_STDARGS;
268 return P2J(tcn_global_pool);
269 }
270
TCN_IMPLEMENT_CALL(jint,Library,version)271 TCN_IMPLEMENT_CALL(jint, Library, version)(TCN_STDARGS, jint what)
272 {
273 apr_version_t apv;
274
275 UNREFERENCED_STDARGS;
276 apr_version(&apv);
277
278 switch (what) {
279 case 0x01:
280 return TCN_MAJOR_VERSION;
281 break;
282 case 0x02:
283 return TCN_MINOR_VERSION;
284 break;
285 case 0x03:
286 return TCN_PATCH_VERSION;
287 break;
288 case 0x04:
289 return TCN_IS_DEV_VERSION;
290 break;
291 case 0x11:
292 return apv.major;
293 break;
294 case 0x12:
295 return apv.minor;
296 break;
297 case 0x13:
298 return apv.patch;
299 break;
300 case 0x14:
301 return apv.is_dev;
302 break;
303 }
304 return 0;
305 }
306
TCN_IMPLEMENT_CALL(jstring,Library,versionString)307 TCN_IMPLEMENT_CALL(jstring, Library, versionString)(TCN_STDARGS)
308 {
309 UNREFERENCED(o);
310 return AJP_TO_JSTRING(TCN_VERSION_STRING);
311 }
312
TCN_IMPLEMENT_CALL(jstring,Library,aprVersionString)313 TCN_IMPLEMENT_CALL(jstring, Library, aprVersionString)(TCN_STDARGS)
314 {
315 UNREFERENCED(o);
316 return AJP_TO_JSTRING(apr_version_string());
317 }
318
TCN_IMPLEMENT_CALL(jboolean,Library,has)319 TCN_IMPLEMENT_CALL(jboolean, Library, has)(TCN_STDARGS, jint what)
320 {
321 jboolean rv = JNI_FALSE;
322 UNREFERENCED_STDARGS;
323 switch (what) {
324 case 0:
325 #if APR_HAVE_IPV6
326 rv = JNI_TRUE;
327 #endif
328 break;
329 case 1:
330 #if APR_HAS_SHARED_MEMORY
331 rv = JNI_TRUE;
332 #endif
333 break;
334 case 2:
335 #if APR_HAS_THREADS
336 rv = JNI_TRUE;
337 #endif
338 break;
339 case 3:
340 #if APR_HAS_SENDFILE
341 rv = JNI_TRUE;
342 #endif
343 break;
344 case 4:
345 #if APR_HAS_MMAP
346 rv = JNI_TRUE;
347 #endif
348 break;
349 case 5:
350 #if APR_HAS_FORK
351 rv = JNI_TRUE;
352 #endif
353 break;
354 case 6:
355 #if APR_HAS_RANDOM
356 rv = JNI_TRUE;
357 #endif
358 break;
359 case 7:
360 #if APR_HAS_OTHER_CHILD
361 rv = JNI_TRUE;
362 #endif
363 break;
364 case 8:
365 #if APR_HAS_DSO
366 rv = JNI_TRUE;
367 #endif
368 break;
369 case 9:
370 #if APR_HAS_SO_ACCEPTFILTER
371 rv = JNI_TRUE;
372 #endif
373 break;
374 case 10:
375 #if APR_HAS_UNICODE_FS
376 rv = JNI_TRUE;
377 #endif
378 break;
379 case 11:
380 #if APR_HAS_PROC_INVOKED
381 rv = JNI_TRUE;
382 #endif
383 break;
384 case 12:
385 #if APR_HAS_USER
386 rv = JNI_TRUE;
387 #endif
388 break;
389 case 13:
390 #if APR_HAS_LARGE_FILES
391 rv = JNI_TRUE;
392 #endif
393 break;
394 case 14:
395 #if APR_HAS_XTHREAD_FILES
396 rv = JNI_TRUE;
397 #endif
398 break;
399 case 15:
400 #if APR_HAS_OS_UUID
401 rv = JNI_TRUE;
402 #endif
403 break;
404 case 16:
405 #if APR_IS_BIGENDIAN
406 rv = JNI_TRUE;
407 #endif
408 break;
409
410 case 17:
411 #if APR_FILES_AS_SOCKETS
412 rv = JNI_TRUE;
413 #endif
414 break;
415 case 18:
416 #if APR_CHARSET_EBCDIC
417 rv = JNI_TRUE;
418 #endif
419 break;
420 case 19:
421 #if APR_TCP_NODELAY_INHERITED
422 rv = JNI_TRUE;
423 #endif
424 break;
425 case 20:
426 #if APR_O_NONBLOCK_INHERITED
427 rv = JNI_TRUE;
428 #endif
429 break;
430 case 21:
431 #if defined(APR_POLLSET_WAKEABLE)
432 rv = JNI_TRUE;
433 #endif
434 break;
435 }
436 return rv;
437 }
438
TCN_IMPLEMENT_CALL(jint,Library,size)439 TCN_IMPLEMENT_CALL(jint, Library, size)(TCN_STDARGS, jint what)
440 {
441
442 UNREFERENCED_STDARGS;
443
444 switch (what) {
445 case 1:
446 return APR_SIZEOF_VOIDP;
447 break;
448 case 2:
449 return APR_PATH_MAX;
450 break;
451 case 3:
452 return APRMAXHOSTLEN;
453 break;
454 case 4:
455 return APR_MAX_IOVEC_SIZE;
456 break;
457 case 5:
458 return APR_MAX_SECS_TO_LINGER;
459 break;
460 case 6:
461 return APR_MMAP_THRESHOLD;
462 break;
463 case 7:
464 return APR_MMAP_LIMIT;
465 break;
466
467 }
468 return 0;
469 }
470
tcn_get_global_pool()471 apr_pool_t *tcn_get_global_pool()
472 {
473 if (!tcn_global_pool) {
474 if (apr_pool_create(&tcn_global_pool, NULL) != APR_SUCCESS) {
475 return NULL;
476 }
477 apr_atomic_init(tcn_global_pool);
478 }
479 return tcn_global_pool;
480 }
481
tcn_get_string_class()482 jclass tcn_get_string_class()
483 {
484 return jString_class;
485 }
486
tcn_get_java_vm()487 JavaVM * tcn_get_java_vm()
488 {
489 return tcn_global_vm;
490 }
491
tcn_get_java_env(JNIEnv ** env)492 jint tcn_get_java_env(JNIEnv **env)
493 {
494 if ((*tcn_global_vm)->GetEnv(tcn_global_vm, (void **)env,
495 JNI_VERSION_1_4)) {
496 return JNI_ERR;
497 }
498 return JNI_OK;
499 }
500
tcn_get_thread_id(void)501 unsigned long tcn_get_thread_id(void)
502 {
503 /* OpenSSL needs this to return an unsigned long. On OS/390, the pthread
504 * id is a structure twice that big. Use the TCB pointer instead as a
505 * unique unsigned long.
506 */
507 #ifdef __MVS__
508 struct PSA {
509 char unmapped[540];
510 unsigned long PSATOLD;
511 } *psaptr = 0;
512
513 return psaptr->PSATOLD;
514 #elif defined(WIN32)
515 return (unsigned long)GetCurrentThreadId();
516 #elif defined(DARWIN)
517 uint64_t tid;
518 pthread_threadid_np(NULL, &tid);
519 return (unsigned long)tid;
520 #elif defined(__FreeBSD__)
521 return (unsigned long)pthread_getthreadid_np();
522 #elif defined(__linux__)
523 return (unsigned long)syscall(SYS_gettid);
524 #else
525 return (unsigned long)pthread_self();
526 #endif
527 }
528
529