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 "apxwin.h"
18 #include "handles.h"
19 #include "javajni.h"
20 #include "private.h"
21 
22 #include <jni.h>
23 
24 #ifndef JNI_VERSION_1_6
25 #error -------------------------------------------------------
26 #error JAVA 5 AND EARLIER ARE NO LONGER SUPPORTED
27 #error -------------------------------------------------------
28 #endif
29 
30 #define JNI_VERSION_DEFAULT JNI_VERSION_1_6
31 
32 /* Need to be able to detect Java 9 without requiring compilation against Java 9
33  * headers.
34  */
35 #ifndef JNI_VERSION_9
36 #define JNI_VERSION_9  0x00090000
37 #endif
38 
39 /* Standard jvm.dll prototypes
40  * since only single jvm can exist per process
41  * make those global
42  */
43 
44 DYNLOAD_TYPE_DECLARE(JNI_GetDefaultJavaVMInitArgs, JNICALL, jint)(void *);
45 static DYNLOAD_FPTR_DECLARE(JNI_GetDefaultJavaVMInitArgs) = NULL;
46 
47 DYNLOAD_TYPE_DECLARE(JNI_CreateJavaVM, JNICALL, jint)(JavaVM **, void **, void *);
48 static DYNLOAD_FPTR_DECLARE(JNI_CreateJavaVM) = NULL;
49 
50 DYNLOAD_TYPE_DECLARE(JNI_GetCreatedJavaVMs, JNICALL, jint)(JavaVM **, jsize, jsize *);
51 static DYNLOAD_FPTR_DECLARE(JNI_GetCreatedJavaVMs) = NULL;
52 
53 DYNLOAD_TYPE_DECLARE(JVM_DumpAllStacks, JNICALL, void)(JNIEnv *, jclass);
54 static DYNLOAD_FPTR_DECLARE(JVM_DumpAllStacks) = NULL;
55 
56 static HANDLE  _st_sys_jvmDllHandle = NULL;
57 static JavaVM *_st_sys_jvm = NULL;
58 
59 DYNLOAD_TYPE_DECLARE(SetDllDirectoryW, WINAPI, BOOL)(LPCWSTR);
60 static DYNLOAD_FPTR_DECLARE(SetDllDirectoryW) = NULL;
61 
62 #define JVM_DELETE_CLAZZ(jvm, cl)                                               \
63     APXMACRO_BEGIN                                                              \
64     if ((jvm)->lpEnv && (jvm)->cl.jClazz) {                                   \
65         (*((jvm)->lpEnv))->DeleteGlobalRef((jvm)->lpEnv, (jvm)->cl.jClazz);   \
66         (jvm)->cl.jClazz = NULL;                                              \
67     } APXMACRO_END
68 
69 #define JVM_EXCEPTION_CHECK(jvm) \
70     ((*((jvm)->lpEnv))->ExceptionCheck((jvm)->lpEnv) != JNI_OK)
71 
72 #define JVM_EXCEPTION_CLEAR(jvm) \
73     APXMACRO_BEGIN                                              \
74     if ((jvm)->lpEnv) {                                         \
75         if ((*((jvm)->lpEnv))->ExceptionCheck((jvm)->lpEnv)) {  \
76             (*((jvm)->lpEnv))->ExceptionDescribe((jvm)->lpEnv); \
77             (*((jvm)->lpEnv))->ExceptionClear((jvm)->lpEnv);    \
78         }                                                       \
79     } APXMACRO_END
80 
81 #define JNI_LOCAL_UNREF(obj) \
82         (*(lpJava->lpEnv))->DeleteLocalRef(lpJava->lpEnv, obj)
83 
84 #define JNICALL_0(fName)  \
85         ((*(lpJava->lpEnv))->fName(lpJava->lpEnv))
86 
87 #define JNICALL_1(fName, a1)  \
88         ((*(lpJava->lpEnv))->fName(lpJava->lpEnv, (a1)))
89 
90 #define JNICALL_2(fName, a1, a2)  \
91         ((*(lpJava->lpEnv))->fName(lpJava->lpEnv, (a1), (a2)))
92 
93 #define JNICALL_3(fName, a1, a2, a3)  \
94         ((*(lpJava->lpEnv))->fName(lpJava->lpEnv, (a1), (a2), (a3)))
95 
96 #define JNICALL_4(fName, a1, a2, a3, a4)  \
97         ((*(lpJava->lpEnv))->fName(lpJava->lpEnv, (a1), (a2), (a3), (a4)))
98 
99 typedef struct APXJAVASTDCLAZZ {
100     CHAR        sClazz[1024];
101     CHAR        sMethod[512];
102     jclass      jClazz;
103     jmethodID   jMethod;
104     jobject     jObject;
105     jarray      jArgs;
106 } APXJAVASTDCLAZZ, *LPAPXJAVASTDCLAZZ;
107 
108 typedef struct APXJAVAVM {
109     DWORD           dwOptions;
110     APXJAVASTDCLAZZ clString;
111     APXJAVASTDCLAZZ clWorker;
112     jint            iVersion;
113     jsize           iVmCount;
114     JNIEnv          *lpEnv;
115     JavaVM          *lpJvm;
116     /* JVM worker thread info */
117     HANDLE          hWorkerThread;
118     DWORD           iWorkerThread;
119     DWORD           dwWorkerStatus;
120     SIZE_T          szStackSize;
121     HANDLE          hWorkerSync;
122     HANDLE          hWorkerInit;
123 } APXJAVAVM, *LPAPXJAVAVM;
124 
125 /* This is no longer exported in jni.h
126  * However Java uses it internally to get
127  * the default stack size
128  */
129 typedef struct APX_JDK1_1InitArgs {
130     jint version;
131 
132     char **properties;
133     jint checkSource;
134     jint nativeStackSize;
135     jint javaStackSize;
136     jint minHeapSize;
137     jint maxHeapSize;
138     jint verifyMode;
139     char *classpath;
140 
141     char padding[128];
142 } APX_JDK1_1InitArgs;
143 
144 #define JAVA_CLASSPATH      "-Djava.class.path="
145 #define JAVA_CLASSPATH_W    L"-Djava.class.path="
146 #define JAVA_CLASSSTRING    "java/lang/String"
147 #define MSVCRT71_DLLNAME    L"\\msvcrt71.dll"
148 
149 static DWORD vmExitCode = 0;
150 
__apxJvmAttachEnv(LPAPXJAVAVM lpJava,JNIEnv ** lpEnv,LPBOOL lpAttached)151 static __inline BOOL __apxJvmAttachEnv(LPAPXJAVAVM lpJava, JNIEnv **lpEnv,
152                                        LPBOOL lpAttached)
153 {
154     jint _iStatus;
155 
156     if (!_st_sys_jvm || !lpJava->lpJvm)
157       return FALSE;
158     _iStatus = (*(lpJava->lpJvm))->GetEnv(lpJava->lpJvm,
159                                           (void **)lpEnv,
160                                           lpJava->iVersion);
161     if (_iStatus != JNI_OK) {
162         if (_iStatus == JNI_EDETACHED) {
163             _iStatus = (*(lpJava->lpJvm))->AttachCurrentThread(lpJava->lpJvm,
164                                                 (void **)lpEnv, NULL);
165             if (lpAttached)
166                 *lpAttached = TRUE;
167         }
168     }
169     if (_iStatus != JNI_OK) {
170         *lpEnv = NULL;
171         return FALSE;
172     }
173     else
174         return TRUE;
175 }
176 
__apxJvmAttach(LPAPXJAVAVM lpJava)177 static __inline BOOL __apxJvmAttach(LPAPXJAVAVM lpJava)
178 {
179     return __apxJvmAttachEnv(lpJava, &lpJava->lpEnv, NULL);
180 }
181 
__apxJvmDetach(LPAPXJAVAVM lpJava)182 static __inline BOOL __apxJvmDetach(LPAPXJAVAVM lpJava)
183 {
184     if (!_st_sys_jvm || !lpJava->lpJvm)
185       return FALSE;
186     if ((*(lpJava->lpJvm))->DetachCurrentThread(lpJava->lpJvm) != JNI_OK) {
187         lpJava->lpEnv = NULL;
188         return FALSE;
189     }
190     else
191         return TRUE;
192 }
193 
__apxLoadJvmDll(APXHANDLE hPool,LPCWSTR szJvmDllPath,LPCWSTR szJavaHome)194 static BOOL __apxLoadJvmDll(APXHANDLE hPool, LPCWSTR szJvmDllPath, LPCWSTR szJavaHome)
195 {
196     UINT errMode;
197     WCHAR  jreAltPath[SIZ_PATHLEN];
198     LPWSTR dllJvmPath = (LPWSTR)szJvmDllPath;
199     DYNLOAD_FPTR_DECLARE(SetDllDirectoryW);
200     DWORD  i, l = 0;
201     WCHAR  jreBinPath[SIZ_PATHLEN];
202 
203     if (!IS_INVALID_HANDLE(_st_sys_jvmDllHandle))
204         return TRUE;    /* jvm.dll is already loaded */
205 
206     if (dllJvmPath && *dllJvmPath) {
207         /* Explicit JVM path.
208          * Check if provided argument is valid
209          */
210         if (GetFileAttributesW(dllJvmPath) == INVALID_FILE_ATTRIBUTES) {
211             /* DAEMON-247: Invalid RuntimeLib explicitly specified is error.
212              */
213             apxLogWrite(APXLOG_MARK_ERROR "Invalid RuntimeLib specified '%S'", dllJvmPath);
214             return FALSE;
215         }
216         apxLogWrite(APXLOG_MARK_DEBUG "Explicit RuntimeLib specified '%S'", dllJvmPath);
217     }
218     else {
219         // No explicit JVM path. Use the standard registry locations.
220         dllJvmPath = apxGetJavaSoftRuntimeLib(NULL);
221         apxLogWrite(APXLOG_MARK_DEBUG "No explicit RuntimeLib specified. Checking registry. Found '%S'", dllJvmPath);
222     }
223 
224     if (GetFileAttributesW(dllJvmPath) == INVALID_FILE_ATTRIBUTES) {
225         /* DAEMON-184: RuntimeLib registry key is invalid.
226          * Check from Jre JavaHome registry key directly
227          */
228         LPWSTR szJreHome = apxGetJavaSoftHome(NULL, TRUE);
229         if (szJreHome) {
230             apxLogWrite(APXLOG_MARK_DEBUG "Invalid RuntimeLib '%S', Checking registry for JRE home. Found '%S'", dllJvmPath, szJreHome);
231             lstrlcpyW(jreAltPath, SIZ_PATHLEN, szJreHome);
232             lstrlcatW(jreAltPath, SIZ_PATHLEN, L"\\bin\\server\\jvm.dll");
233             dllJvmPath = jreAltPath;
234         } else {
235             apxLogWrite(APXLOG_MARK_DEBUG "Invalid RuntimeLib '%S', Checking registry for JRE home. None found.", dllJvmPath);
236         }
237     }
238 
239     if (GetFileAttributesW(dllJvmPath) == INVALID_FILE_ATTRIBUTES) {
240         /* DAEMON-247: JavaSoft JRE registry keys are invalid / not present
241          * Check from Procrun's JavaHome registry key
242          */
243         if (szJavaHome) {
244             apxLogWrite(APXLOG_MARK_DEBUG "Using explicitly configured JavaHome '%S'", szJavaHome);
245             lstrlcpyW(jreAltPath, SIZ_PATHLEN, szJavaHome);
246             lstrlcatW(jreAltPath, SIZ_PATHLEN, L"\\bin\\server\\jvm.dll");
247             dllJvmPath = jreAltPath;
248         }
249     }
250 
251     if (GetFileAttributesW(dllJvmPath) == INVALID_FILE_ATTRIBUTES) {
252         /* DAEMON-404: JRE home in registry invalid / not present.
253          * Explicit JavaHome invalid / not present
254          * Check from JDK JavaHome registry key directly
255          */
256         LPWSTR szJdkHome = apxGetJavaSoftHome(NULL, FALSE);
257         if (szJdkHome) {
258             apxLogWrite(APXLOG_MARK_DEBUG "Invalid RuntimeLib '%S', Checking registry for JDK home. Found '%S'", dllJvmPath, szJdkHome);
259             lstrlcpyW(jreAltPath, SIZ_PATHLEN, szJdkHome);
260             lstrlcatW(jreAltPath, SIZ_PATHLEN, L"\\bin\\server\\jvm.dll");
261             dllJvmPath = jreAltPath;
262         } else {
263             apxLogWrite(APXLOG_MARK_DEBUG "Invalid RuntimeLib '%S', Checking registry for JDK home. None found.", dllJvmPath);
264         }
265     }
266 
267     /* Suppress the not found system popup message */
268     errMode = SetErrorMode(SEM_FAILCRITICALERRORS);
269 
270     lstrlcpyW(jreBinPath, SIZ_PATHLEN, dllJvmPath);
271 
272     for (i = lstrlenW(jreBinPath); i > 0, l < 2; i--) {
273         if (jreBinPath[i] == L'\\' || jreBinPath[i] == L'/') {
274             jreBinPath[i] = L'\0';
275             l++;
276         }
277     }
278 
279     /* Add Java bin path to the PATH to fix loading of awt.dll */
280     apxLogWrite(APXLOG_MARK_DEBUG "Adding Java bin path to the PATH to fix loading of awt.dll: '%S'", jreBinPath);
281     apxAddToPathW(hPool, jreBinPath);
282 
283     /* Set the environment using putenv, so JVM can use it */
284     apxSetInprocEnvironment();
285 
286     apxLogWrite(APXLOG_MARK_DEBUG "Loading JVM DLL '%S'", dllJvmPath);
287     _st_sys_jvmDllHandle = LoadLibraryExW(dllJvmPath, NULL, 0);
288     if (IS_INVALID_HANDLE(_st_sys_jvmDllHandle) &&
289         GetFileAttributesW(dllJvmPath) != INVALID_FILE_ATTRIBUTES) {
290         WCHAR  crtBinPath[SIZ_PATHLEN];
291 
292         /* There is a file but cannot be loaded.
293          * Try to load the MSVCRTxx.dll before JVM.dll
294          */
295         apxLogWrite(APXLOG_MARK_ERROR "Found '%S' but couldn't load it.", dllJvmPath);
296 
297         lstrlcpyW(jreBinPath, SIZ_PATHLEN, dllJvmPath);
298         if(l == 2) {
299             lstrlcpyW(crtBinPath, SIZ_PATHLEN, jreBinPath);
300             lstrlcatW(crtBinPath, SIZ_PATHLEN, MSVCRT71_DLLNAME);
301             if (GetFileAttributesW(crtBinPath) != INVALID_FILE_ATTRIBUTES) {
302                 apxLogWrite(APXLOG_MARK_DEBUG "Loading '%S'.", crtBinPath);
303                 if (LoadLibraryW(crtBinPath)) {
304                     /* Found MSVCRTxx.dll
305                      */
306                     apxLogWrite(APXLOG_MARK_DEBUG "Preloaded '%S'", crtBinPath);
307                 }
308                 else {
309                     apxLogWrite(APXLOG_MARK_DEBUG "Failed preloading '%S'.", crtBinPath);
310                 }
311             }
312         }
313     }
314     /* This shouldn't happen, but try to search in %PATH% */
315     if (IS_INVALID_HANDLE(_st_sys_jvmDllHandle)) {
316         apxLogWrite(APXLOG_MARK_DEBUG "Invalid JVM DLL handle.");
317         apxLogWrite(APXLOG_MARK_DEBUG "Loading JVM DLL '%S' using LOAD_WITH_ALTERED_SEARCH_PATH.", dllJvmPath);
318         _st_sys_jvmDllHandle = LoadLibraryExW(dllJvmPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
319     }
320     if (IS_INVALID_HANDLE(_st_sys_jvmDllHandle)) {
321         apxLogWrite(APXLOG_MARK_DEBUG "Invalid JVM DLL handle.");
322         DYNLOAD_FPTR_ADDRESS(SetDllDirectoryW, KERNEL32);
323         if (l == 2) {
324             apxLogWrite(APXLOG_MARK_DEBUG "Setting DLL search path to '%S'", jreBinPath);
325             DYNLOAD_CALL(SetDllDirectoryW)(jreBinPath);
326         }
327         apxLogWrite(APXLOG_MARK_DEBUG "Loading JVM DLL '%S'.", dllJvmPath);
328         _st_sys_jvmDllHandle = LoadLibraryExW(dllJvmPath, NULL, 0);
329         if (IS_INVALID_HANDLE(_st_sys_jvmDllHandle)) {
330             apxLogWrite(APXLOG_MARK_DEBUG "Invalid JVM DLL handle.");
331             apxLogWrite(APXLOG_MARK_DEBUG "Loading JVM DLL '%S' using LOAD_WITH_ALTERED_SEARCH_PATH.", dllJvmPath);
332             _st_sys_jvmDllHandle = LoadLibraryExW(dllJvmPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
333         }
334     }
335     /* Restore the error mode signalization */
336     SetErrorMode(errMode);
337     if (IS_INVALID_HANDLE(_st_sys_jvmDllHandle)) {
338         apxLogWrite(APXLOG_MARK_ERROR "Invalid JVM DLL handle.");
339         apxLogWrite(APXLOG_MARK_SYSERR);
340         return FALSE;
341     }
342     DYNLOAD_FPTR_LOAD(JNI_GetDefaultJavaVMInitArgs, _st_sys_jvmDllHandle);
343     DYNLOAD_FPTR_LOAD(JNI_CreateJavaVM,             _st_sys_jvmDllHandle);
344     DYNLOAD_FPTR_LOAD(JNI_GetCreatedJavaVMs,        _st_sys_jvmDllHandle);
345     DYNLOAD_FPTR_LOAD(JVM_DumpAllStacks,            _st_sys_jvmDllHandle);
346 
347     if (!DYNLOAD_FPTR(JNI_GetDefaultJavaVMInitArgs) ||
348         !DYNLOAD_FPTR(JNI_CreateJavaVM) ||
349         !DYNLOAD_FPTR(JNI_GetCreatedJavaVMs)) {
350         apxLogWrite(APXLOG_MARK_SYSERR);
351         apxLogWrite(APXLOG_MARK_DEBUG "Freeing JVM DLL.");
352         FreeLibrary(_st_sys_jvmDllHandle);
353         _st_sys_jvmDllHandle = NULL;
354         apxLogWrite(APXLOG_MARK_ERROR "Failed loading JNI function pointers.");
355         return FALSE;
356     }
357 
358     /* Real voodo ... */
359     return TRUE;
360 }
361 
__apxJavaJniCallback(APXHANDLE hObject,UINT uMsg,WPARAM wParam,LPARAM lParam)362 static BOOL __apxJavaJniCallback(APXHANDLE hObject, UINT uMsg,
363                                  WPARAM wParam, LPARAM lParam)
364 {
365     LPAPXJAVAVM lpJava;
366     DWORD       dwJvmRet = 0;
367 
368     lpJava = APXHANDLE_DATA(hObject);
369     switch (uMsg) {
370         case WM_CLOSE:
371             if (_st_sys_jvm && lpJava->lpJvm) {
372                 if (!IS_INVALID_HANDLE(lpJava->hWorkerThread)) {
373                     if (GetExitCodeThread(lpJava->hWorkerThread, &dwJvmRet) &&
374                         dwJvmRet == STILL_ACTIVE) {
375                         TerminateThread(lpJava->hWorkerThread, 5);
376                     }
377                 }
378                 SAFE_CLOSE_HANDLE(lpJava->hWorkerThread);
379                 __apxJvmAttach(lpJava);
380                 JVM_DELETE_CLAZZ(lpJava, clWorker);
381                 JVM_DELETE_CLAZZ(lpJava, clString);
382                 __apxJvmDetach(lpJava);
383                 /* Check if this is the jvm loader */
384                 if (!lpJava->iVmCount && _st_sys_jvmDllHandle) {
385                     /* Unload JVM dll */
386                     FreeLibrary(_st_sys_jvmDllHandle);
387                     _st_sys_jvmDllHandle = NULL;
388                 }
389                 lpJava->lpJvm = NULL;
390             }
391         break;
392         default:
393         break;
394     }
395     return TRUE;
396     UNREFERENCED_PARAMETER(wParam);
397     UNREFERENCED_PARAMETER(lParam);
398 }
399 
400 APXHANDLE
apxCreateJava(APXHANDLE hPool,LPCWSTR szJvmDllPath,LPCWSTR szJavaHome)401 apxCreateJava(APXHANDLE hPool, LPCWSTR szJvmDllPath, LPCWSTR szJavaHome)
402 {
403 
404     APXHANDLE    hJava;
405     LPAPXJAVAVM  lpJava;
406     jsize        iVmCount;
407     JavaVM       *lpJvm = NULL;
408     struct       APX_JDK1_1InitArgs jArgs1_1;
409 
410     if (!__apxLoadJvmDll(hPool, szJvmDllPath, szJavaHome)) {
411         apxLogWrite(APXLOG_MARK_ERROR "Failed to load JVM DLL '%S', home '%S'.", szJvmDllPath, szJavaHome);
412         return NULL;
413     }
414     apxLogWrite(APXLOG_MARK_DEBUG "Loaded JVM DLL '%S', home '%S'.", szJvmDllPath, szJavaHome);
415 
416     /*
417      * JNI_GetCreatedJavaVMs
418      */
419     apxLogWrite(APXLOG_MARK_DEBUG "JNI_GetCreatedJavaVMs...");
420     if (DYNLOAD_FPTR(JNI_GetCreatedJavaVMs)(&lpJvm, 1, &iVmCount) != JNI_OK) {
421         apxLogWrite(APXLOG_MARK_ERROR "JNI_GetCreatedJavaVMs failed.");
422         return NULL;
423     }
424     if (iVmCount && !lpJvm) {
425         apxLogWrite(APXLOG_MARK_ERROR "JNI_GetCreatedJavaVMs OK but JavaVM pointer is NULL.");
426         return NULL;
427     }
428 
429     hJava = apxHandleCreate(hPool, 0,
430                             NULL, sizeof(APXJAVAVM),
431                             __apxJavaJniCallback);
432     if (IS_INVALID_HANDLE(hJava)) {
433         apxLogWrite(APXLOG_MARK_ERROR "Failed to create handle.");
434         return NULL;
435     }
436     hJava->dwType = APXHANDLE_TYPE_JVM;
437     lpJava = APXHANDLE_DATA(hJava);
438     lpJava->lpJvm = lpJvm;
439     lpJava->iVmCount = iVmCount;
440 
441     /* Guess the stack size
442      */
443     AplZeroMemory(&jArgs1_1, sizeof(jArgs1_1));
444     jArgs1_1.version = JNI_VERSION_1_1;
445     DYNLOAD_FPTR(JNI_GetDefaultJavaVMInitArgs)(&jArgs1_1);
446     if (jArgs1_1.javaStackSize < 0 || jArgs1_1.javaStackSize > (2048 * 1024))
447         jArgs1_1.javaStackSize = 0;
448     lpJava->szStackSize = (SIZE_T)jArgs1_1.javaStackSize;
449 
450     if (!_st_sys_jvm)
451         _st_sys_jvm = lpJvm;
452     return hJava;
453 }
454 
__apxJavaDestroyThread(LPVOID lpParameter)455 static DWORD WINAPI __apxJavaDestroyThread(LPVOID lpParameter)
456 {
457     JavaVM *lpJvm = (JavaVM *)lpParameter;
458     (*lpJvm)->DestroyJavaVM(lpJvm);
459     return 0;
460 }
461 
462 BOOL
apxDestroyJvm(DWORD dwTimeout)463 apxDestroyJvm(DWORD dwTimeout)
464 {
465     if (_st_sys_jvm) {
466         DWORD  tid;
467         HANDLE hWaiter;
468         BOOL   rv = FALSE;
469         JavaVM *lpJvm = _st_sys_jvm;
470 
471         _st_sys_jvm = NULL;
472         (*lpJvm)->DetachCurrentThread(lpJvm);
473         hWaiter = CreateThread(NULL, 0, __apxJavaDestroyThread,
474                                (void *)lpJvm, 0, &tid);
475         if (IS_INVALID_HANDLE(hWaiter)) {
476             apxLogWrite(APXLOG_MARK_SYSERR);
477             return FALSE;
478         }
479         if (WaitForSingleObject(hWaiter, dwTimeout) == WAIT_OBJECT_0)
480             rv = TRUE;
481         CloseHandle(hWaiter);
482         return rv;
483     }
484     else
485         return FALSE;
486 }
487 
__apxIsJava9()488 static BOOL __apxIsJava9()
489 {
490     JavaVMInitArgs  vmArgs;
491     vmArgs.version = JNI_VERSION_9;
492     /* Returns an error if requested version is not supported */
493     if (DYNLOAD_FPTR(JNI_GetDefaultJavaVMInitArgs)(&vmArgs) != JNI_OK) {
494         return FALSE;
495     }
496     else {
497         return TRUE;
498     }
499 }
500 
__apxMultiSzToJvmOptions(APXHANDLE hPool,LPCSTR lpString,LPCSTR lpString9,JavaVMOption ** lppArray,DWORD nExtra,DWORD bJniVfprintf)501 static DWORD __apxMultiSzToJvmOptions(APXHANDLE hPool,
502                                       LPCSTR lpString,
503                                       LPCSTR lpString9,
504                                       JavaVMOption **lppArray,
505                                       DWORD  nExtra,
506                                       DWORD bJniVfprintf)
507 {
508     DWORD i = 0, n = 0, n9 = 0, nTotal, l = 0, l9 = 0, lTotal;
509     char *buff;
510     LPSTR p;
511 
512     if (lpString) {
513         l = __apxGetMultiSzLengthA(lpString, &n);
514     }
515     if (__apxIsJava9() && lpString9) {
516         l9 = __apxGetMultiSzLengthA(lpString9, &n9);
517     }
518 
519     nTotal = n + n9 + nExtra;
520     lTotal = l + l9;
521 
522     buff = apxPoolAlloc(hPool, (nTotal + 1) * sizeof(JavaVMOption) + (lTotal + 1));
523 
524     *lppArray = (JavaVMOption *)buff;
525     p = (LPSTR)(buff + (nTotal + 1) * sizeof(JavaVMOption));
526     if (lpString)
527         AplCopyMemory(p, lpString, l + 1);
528     if (bJniVfprintf) {
529         // If present, vfprintf is set first so it can be used to report errors
530         // in later options. Increment indexes to account for this.
531         i++;
532         n++;
533     }
534     for (; i < n; i++) {
535         (*lppArray)[i].optionString = p;
536         while (*p)
537             p++;
538         p++;
539     }
540     if (lpString9)
541         AplCopyMemory(p, lpString9, l9 + 1);
542     for (; i < (n + n9); i++) {
543         (*lppArray)[i].optionString = p;
544         while (*p)
545             p++;
546         p++;
547     }
548 
549     return nTotal;
550 }
551 
552 /* a hook for a function that redirects all VM messages. */
__apxJniVfprintf(FILE * fp,const char * format,va_list args)553 static jint JNICALL __apxJniVfprintf(FILE *fp, const char *format, va_list args)
554 {
555     jint rv;
556     CHAR sBuf[1024+16];
557     rv = wvsprintfA(sBuf, format, args);
558     if (apxLogWrite(APXLOG_MARK_INFO "%s", sBuf) == 0)
559         fputs(sBuf, stdout);
560     return rv;
561     UNREFERENCED_PARAMETER(fp);
562 }
563 
__apxJniExit(jint exitCode)564 static void JNICALL __apxJniExit(jint exitCode)
565 {
566     apxLogWrite(APXLOG_MARK_DEBUG "Exit hook with exit code %d", exitCode);
567     vmExitCode = exitCode;
568     return;
569 }
570 
__apxJniAbort()571 static void JNICALL __apxJniAbort()
572 {
573     apxLogWrite(APXLOG_MARK_DEBUG "JVM aborted");
574     // Set the exit code to a non-zero value to indicate a non-standard exit
575     vmExitCode = 1;
576     return;
577 }
578 
__apxStrIndexA(LPCSTR szStr,int nCh)579 static LPSTR __apxStrIndexA(LPCSTR szStr, int nCh)
580 {
581     LPSTR pStr;
582 
583     for (pStr = (LPSTR)szStr; *pStr; pStr++) {
584         if (*pStr == nCh)
585             return pStr;
586     }
587     return NULL;
588 }
589 
__apxStrnCatA(APXHANDLE hPool,LPSTR pOrg,LPCSTR szStr,LPCSTR szAdd)590 static LPSTR __apxStrnCatA(APXHANDLE hPool, LPSTR pOrg, LPCSTR szStr, LPCSTR szAdd)
591 {
592     DWORD len = 1;
593     DWORD nas = pOrg == NULL;
594     if (pOrg)
595         len += lstrlenA(pOrg);
596     if (szStr)
597         len += lstrlenA(szStr);
598     if (szAdd)
599         len += lstrlenA(szAdd);
600     pOrg = (LPSTR)apxPoolRealloc(hPool, pOrg, len);
601     if (pOrg) {
602         if (nas)
603             *pOrg = '\0';
604         if (szStr)
605             lstrcatA(pOrg, szStr);
606         if (szAdd)
607             lstrcatA(pOrg, szAdd);
608     }
609     return pOrg;
610 }
611 
__apxEvalPathPart(APXHANDLE hPool,LPSTR pStr,LPCSTR szPattern)612 static LPSTR __apxEvalPathPart(APXHANDLE hPool, LPSTR pStr, LPCSTR szPattern)
613 {
614     HANDLE           hFind;
615     WIN32_FIND_DATAA stGlob;
616     char       szJars[MAX_PATH + 1];
617     char       szPath[MAX_PATH + 1];
618 
619     if (lstrlenA(szPattern) > (sizeof(szJars) - 5)) {
620         return __apxStrnCatA(hPool, pStr, szPattern, NULL);
621     }
622     lstrcpyA(szJars, szPattern);
623     szPath[0] = ';';
624     szPath[1] = '\0';
625     lstrcatA(szPath, szPattern);
626     lstrcatA(szJars, ".jar");
627     /* Remove the trailing asterisk
628      */
629     szPath[lstrlenA(szPath) - 1] = '\0';
630     if ((hFind = FindFirstFileA(szJars, &stGlob)) == INVALID_HANDLE_VALUE) {
631         /* Find failed
632          */
633         return pStr;
634     }
635     pStr = __apxStrnCatA(hPool, pStr, &szPath[1], stGlob.cFileName);
636     if (pStr == NULL) {
637         FindClose(hFind);
638         return NULL;
639     }
640     while (FindNextFileA(hFind, &stGlob) != 0) {
641         pStr = __apxStrnCatA(hPool, pStr, szPath, stGlob.cFileName);
642         if (pStr == NULL)
643             break;
644     }
645     FindClose(hFind);
646     return pStr;
647 }
648 
649 /**
650  * Call glob on each PATH like string path.
651  * Glob is called only if the part ends with asterisk in which
652  * case asterisk is replaced by *.jar when searching
653  */
__apxEvalClasspath(APXHANDLE hPool,LPCSTR szCp)654 static LPSTR __apxEvalClasspath(APXHANDLE hPool, LPCSTR szCp)
655 {
656     LPSTR pCpy = __apxStrnCatA(hPool, NULL, JAVA_CLASSPATH, szCp);
657     LPSTR pGcp = NULL;
658     LPSTR pPos;
659     LPSTR pPtr;
660 
661     if (!pCpy)
662         return NULL;
663     pPtr = pCpy + sizeof(JAVA_CLASSPATH) - 1;
664     while ((pPos = __apxStrIndexA(pPtr, ';'))) {
665         *pPos = '\0';
666         if (pGcp)
667             pGcp = __apxStrnCatA(hPool, pGcp, ";", NULL);
668         else
669             pGcp = __apxStrnCatA(hPool, NULL, JAVA_CLASSPATH, NULL);
670         if ((pPos > pPtr) && (*(pPos - 1) == '*')) {
671             if (!(pGcp = __apxEvalPathPart(hPool, pGcp, pPtr))) {
672                 /* Error.
673                 * Return the original string processed so far.
674                 */
675                 return pCpy;
676             }
677         }
678         else {
679             /* Standard path element */
680             if (!(pGcp = __apxStrnCatA(hPool, pGcp, pPtr, NULL))) {
681                 /* Error.
682                 * Return the original string processed so far.
683                 */
684                 return pCpy;
685             }
686         }
687         pPtr = pPos + 1;
688     }
689     if (*pPtr) {
690         int end = lstrlenA(pPtr);
691         if (pGcp)
692             pGcp = __apxStrnCatA(hPool, pGcp, ";", NULL);
693         else
694             pGcp = __apxStrnCatA(hPool, NULL, JAVA_CLASSPATH, NULL);
695         if (end > 0 && pPtr[end - 1] == '*') {
696             /* Last path elemet ends with star
697              * Do a globbing.
698              */
699             pGcp = __apxEvalPathPart(hPool, pGcp, pPtr);
700         }
701         else {
702             /* Just add the part */
703             pGcp = __apxStrnCatA(hPool, pGcp, pPtr, NULL);
704         }
705     }
706     /* Free the allocated copy */
707     if (pGcp) {
708         apxFree(pCpy);
709         return pGcp;
710     }
711     else
712         return pCpy;
713 }
714 
715 /* ANSI version only */
716 BOOL
apxJavaInitialize(APXHANDLE hJava,LPCSTR szClassPath,LPCVOID lpOptions,LPCVOID lpOptions9,DWORD dwMs,DWORD dwMx,DWORD dwSs,DWORD bJniVfprintf)717 apxJavaInitialize(APXHANDLE hJava, LPCSTR szClassPath,
718                   LPCVOID lpOptions, LPCVOID lpOptions9,
719                   DWORD dwMs, DWORD dwMx, DWORD dwSs,
720                   DWORD bJniVfprintf)
721 {
722     LPAPXJAVAVM     lpJava;
723     JavaVMInitArgs  vmArgs;
724     JavaVMOption    *lpJvmOptions;
725     DWORD           i, nOptions, sOptions = 0;
726     BOOL            rv = FALSE;
727 
728     if (hJava->dwType != APXHANDLE_TYPE_JVM)
729         return FALSE;
730 
731     lpJava = APXHANDLE_DATA(hJava);
732 
733     if (lpJava->iVmCount) {
734         if (!lpJava->lpEnv && !__apxJvmAttach(lpJava)) {
735             if (lpJava->iVersion == JNI_VERSION_1_6) {
736                 apxLogWrite(APXLOG_MARK_ERROR "Unable To Attach the JVM");
737                 return FALSE;
738             }
739             else
740                 lpJava->iVersion = JNI_VERSION_1_6;
741             if (!__apxJvmAttach(lpJava)) {
742                 apxLogWrite(APXLOG_MARK_ERROR "Unable To Attach the JVM");
743                 return FALSE;
744             }
745         }
746         lpJava->iVersion = JNICALL_0(GetVersion);
747         if (lpJava->iVersion < JNI_VERSION_1_6) {
748             apxLogWrite(APXLOG_MARK_ERROR "Unsupported JNI version %#08x", lpJava->iVersion);
749             return FALSE;
750         }
751         rv = TRUE;
752     }
753     else {
754         CHAR  iB[3][64];
755         LPSTR szCp = NULL;
756         int result;
757         lpJava->iVersion = JNI_VERSION_DEFAULT;
758         if (dwMs)
759             ++sOptions;
760         if (dwMx)
761             ++sOptions;
762         if (dwSs)
763             ++sOptions;
764         if (bJniVfprintf)
765             ++sOptions;
766         if (szClassPath && *szClassPath)
767             ++sOptions;
768 
769         sOptions++; /* unconditionally set for extraInfo exit  */
770         sOptions++; /* unconditionally set for extraInfo abort */
771 
772         nOptions = __apxMultiSzToJvmOptions(hJava->hPool, lpOptions, lpOptions9,
773                                             &lpJvmOptions, sOptions, bJniVfprintf);
774         if (bJniVfprintf) {
775             /* Default JNI error printer. Hard-coded to position zero if present */
776             lpJvmOptions[0].optionString = "vfprintf";
777             lpJvmOptions[0].extraInfo    = __apxJniVfprintf;
778             --sOptions;
779         }
780 
781         if (szClassPath && *szClassPath) {
782             szCp = __apxEvalClasspath(hJava->hPool, szClassPath);
783             if (szCp == NULL) {
784                 apxLogWrite(APXLOG_MARK_ERROR "Invalid classpath %s", szClassPath);
785                 return FALSE;
786             }
787             lpJvmOptions[nOptions - sOptions].optionString = szCp;
788             --sOptions;
789         }
790 
791         /* unconditionally add hook for System.exit() in order to store exit code */
792         lpJvmOptions[nOptions - sOptions].optionString = "exit";
793         lpJvmOptions[nOptions - sOptions].extraInfo    = __apxJniExit;
794         --sOptions;
795 
796         /* unconditionally add hook for abort in order to store exit code */
797         lpJvmOptions[nOptions - sOptions].optionString = "abort";
798         lpJvmOptions[nOptions - sOptions].extraInfo    = __apxJniAbort;
799         --sOptions;
800 
801         if (dwMs) {
802             wsprintfA(iB[0], "-Xms%dm", dwMs);
803             lpJvmOptions[nOptions - sOptions].optionString = iB[0];
804             --sOptions;
805         }
806         if (dwMx) {
807             wsprintfA(iB[1], "-Xmx%dm", dwMx);
808             lpJvmOptions[nOptions - sOptions].optionString = iB[1];
809             --sOptions;
810         }
811         if (dwSs) {
812             wsprintfA(iB[2], "-Xss%dk", dwSs);
813             lpJvmOptions[nOptions - sOptions].optionString = iB[2];
814             --sOptions;
815         }
816         for (i = 0; i < nOptions; i++) {
817             apxLogWrite(APXLOG_MARK_DEBUG "Jvm Option[%d] %s", i,
818                         lpJvmOptions[i].optionString);
819         }
820         vmArgs.options  = lpJvmOptions;
821         vmArgs.nOptions = nOptions;
822         vmArgs.version  = lpJava->iVersion;
823         vmArgs.ignoreUnrecognized = JNI_FALSE;
824         result = DYNLOAD_FPTR(JNI_CreateJavaVM)(&(lpJava->lpJvm),
825                                            (void **)&(lpJava->lpEnv),
826                                            &vmArgs);
827         if (result != JNI_OK) {
828             apxLogWrite(APXLOG_MARK_ERROR "CreateJavaVM Failed with error [%d]", result);
829             rv = FALSE;
830         }
831         else {
832             rv = TRUE;
833             if (!_st_sys_jvm)
834                 _st_sys_jvm = lpJava->lpJvm;
835         }
836         apxFree(szCp);
837         apxFree(lpJvmOptions);
838     }
839     if (rv)
840         return TRUE;
841     else
842         return FALSE;
843 }
844 
845 /* ANSI version only */
846 DWORD
apxJavaCmdInitialize(APXHANDLE hPool,LPCWSTR szClassPath,LPCWSTR szClass,LPCWSTR szOptions,DWORD dwMs,DWORD dwMx,DWORD dwSs,LPCWSTR szCmdArgs,LPWSTR ** lppArray)847 apxJavaCmdInitialize(APXHANDLE hPool, LPCWSTR szClassPath, LPCWSTR szClass,
848                      LPCWSTR szOptions, DWORD dwMs, DWORD dwMx,
849                      DWORD dwSs, LPCWSTR szCmdArgs, LPWSTR **lppArray)
850 {
851 
852     DWORD i, nJVM, nCmd, nTotal, lJVM, lCmd;
853     LPWSTR p;
854 
855     /* Calculate the number of all arguments */
856     nTotal = 0;
857     if (szClassPath)
858         ++nTotal;
859     if (szClass)
860         ++nTotal;
861     lJVM = __apxGetMultiSzLengthW(szOptions, &nJVM);
862     nTotal += nJVM;
863     lCmd = __apxGetMultiSzLengthW(szCmdArgs, &nCmd);
864     nTotal += nCmd;
865     if (dwMs)
866         ++nTotal;
867     if (dwMx)
868         ++nTotal;
869     if (dwSs)
870         ++nTotal;
871 
872     if (nTotal == 0)
873         return 0;
874 
875     /* Allocate the array to store all arguments' pointers
876      */
877     *lppArray = (LPWSTR *)apxPoolAlloc(hPool, (nTotal + 2) * sizeof(LPWSTR));
878 
879     /* Process JVM options */
880     i = 0;
881     if (nJVM && lJVM) {
882         p = (LPWSTR)apxPoolAlloc(hPool, (lJVM + 1) * sizeof(WCHAR));
883         AplCopyMemory(p, szOptions, (lJVM + 1) * sizeof(WCHAR) + sizeof(WCHAR));
884         for (; i < nJVM; i++) {
885             (*lppArray)[i] = p;
886             while (*p)
887                 p++;
888             p++;
889         }
890     }
891 
892     /* Process the 3 extra JVM options */
893     if (dwMs) {
894         p = (LPWSTR)apxPoolAlloc(hPool, 64 * sizeof(WCHAR));
895         wsprintfW(p, L"-Xms%dm", dwMs);
896         (*lppArray)[i++] = p;
897     }
898     if (dwMx) {
899         p = (LPWSTR)apxPoolAlloc(hPool, 64 * sizeof(WCHAR));
900         wsprintfW(p, L"-Xmx%dm", dwMx);
901         (*lppArray)[i++] = p;
902     }
903     if (dwSs) {
904         p = (LPWSTR)apxPoolAlloc(hPool, 64 * sizeof(WCHAR));
905         wsprintfW(p, L"-Xss%dk", dwSs);
906         (*lppArray)[i++] = p;
907     }
908 
909     /* Process the classpath and class */
910     if (szClassPath) {
911         p = (LPWSTR)apxPoolAlloc(hPool, (lstrlenW(JAVA_CLASSPATH_W) + lstrlenW(szClassPath)) * sizeof(WCHAR));
912         lstrcpyW(p, JAVA_CLASSPATH_W);
913         lstrcatW(p, szClassPath);
914         (*lppArray)[i++] = p;
915     }
916     if (szClass) {
917         p = (LPWSTR)apxPoolAlloc(hPool, (lstrlenW(szClass)) * sizeof(WCHAR));
918         lstrcpyW(p, szClass);
919         (*lppArray)[i++] = p;
920     }
921 
922     /* Process command arguments */
923     if (nCmd && lCmd) {
924         p = (LPWSTR)apxPoolAlloc(hPool, (lCmd + 1) * sizeof(WCHAR));
925         AplCopyMemory(p, szCmdArgs, (lCmd + 1) * sizeof(WCHAR) + sizeof(WCHAR));
926         for (; i < nTotal; i++) {
927             (*lppArray)[i] = p;
928             while (*p)
929                 p++;
930             p++;
931         }
932     }
933 
934     (*lppArray)[++i] = NULL;
935 
936     return nTotal;
937 }
938 
939 
940 BOOL
apxJavaLoadMainClass(APXHANDLE hJava,LPCSTR szClassName,LPCSTR szMethodName,LPCVOID lpArguments)941 apxJavaLoadMainClass(APXHANDLE hJava, LPCSTR szClassName,
942                      LPCSTR szMethodName,
943                      LPCVOID lpArguments)
944 {
945     LPWSTR      *lpArgs = NULL;
946     DWORD       nArgs;
947     LPAPXJAVAVM lpJava;
948     jclass      jClazz;
949     LPCSTR      szSignature = "([Ljava/lang/String;)V";
950 
951     if (hJava->dwType != APXHANDLE_TYPE_JVM)
952         return FALSE;
953     lpJava = APXHANDLE_DATA(hJava);
954     if (!lpJava)
955         return FALSE;
956     if (IS_EMPTY_STRING(szMethodName))
957         szMethodName = "main";
958     if (lstrcmpA(szClassName, "java/lang/System") == 0) {
959         /* Usable only for exit method, so force */
960         szSignature  = "(I)V";
961         szMethodName = "exit";
962     }
963     lstrlcpyA(lpJava->clWorker.sClazz, 1024, szClassName);
964     lstrlcpyA(lpJava->clWorker.sMethod, 512, szMethodName);
965 
966     jClazz = JNICALL_1(FindClass, JAVA_CLASSSTRING);
967     if (!jClazz) {
968         JVM_EXCEPTION_CLEAR(lpJava);
969         apxLogWrite(APXLOG_MARK_ERROR "FindClass "  JAVA_CLASSSTRING " failed");
970         return FALSE;
971     }
972     lpJava->clString.jClazz = JNICALL_1(NewGlobalRef, jClazz);
973     JNI_LOCAL_UNREF(jClazz);
974     /* Find the class */
975     jClazz  = JNICALL_1(FindClass, szClassName);
976     if (!jClazz) {
977         JVM_EXCEPTION_CLEAR(lpJava);
978         apxLogWrite(APXLOG_MARK_ERROR "FindClass %s failed", szClassName);
979         return FALSE;
980     }
981     /* Make the class global so that worker thread can attach */
982     lpJava->clWorker.jClazz  = JNICALL_1(NewGlobalRef, jClazz);
983     JNI_LOCAL_UNREF(jClazz);
984 
985     lpJava->clWorker.jMethod = JNICALL_3(GetStaticMethodID,
986                                          lpJava->clWorker.jClazz,
987                                          szMethodName, szSignature);
988     if (!lpJava->clWorker.jMethod) {
989         JVM_EXCEPTION_CLEAR(lpJava);
990         apxLogWrite(APXLOG_MARK_ERROR "Method 'static void %s(String[])' not found in Class %s",
991                 szMethodName, szClassName);
992         return FALSE;
993     }
994     if (lstrcmpA(szClassName, "java/lang/System")) {
995         nArgs = apxMultiSzToArrayW(hJava->hPool, lpArguments, &lpArgs);
996         lpJava->clWorker.jArgs = JNICALL_3(NewObjectArray, nArgs,
997                                            lpJava->clString.jClazz, NULL);
998         if (nArgs) {
999             DWORD i;
1000             for (i = 0; i < nArgs; i++) {
1001                 jstring arg = JNICALL_2(NewString, lpArgs[i], lstrlenW(lpArgs[i]));
1002                 JNICALL_3(SetObjectArrayElement, lpJava->clWorker.jArgs, i, arg);
1003                 apxLogWrite(APXLOG_MARK_DEBUG "argv[%d] = %S", i, lpArgs[i]);
1004             }
1005         }
1006         apxFree(lpArgs);
1007     }
1008     return TRUE;
1009 }
1010 
1011 /* Main Java application worker thread
1012  * It will launch Java main and wait until
1013  * it finishes.
1014  */
__apxJavaWorkerThread(LPVOID lpParameter)1015 static DWORD WINAPI __apxJavaWorkerThread(LPVOID lpParameter)
1016 {
1017 #define WORKER_EXIT(x)  do { rv = x; goto finished; } while(0)
1018     DWORD rv = 0;
1019     LPAPXJAVAVM lpJava = NULL;
1020     LPAPXJAVA_THREADARGS pArgs = (LPAPXJAVA_THREADARGS)lpParameter;
1021     APXHANDLE hJava;
1022 
1023     hJava  = (APXHANDLE)pArgs->hJava;
1024     if (hJava->dwType != APXHANDLE_TYPE_JVM)
1025         WORKER_EXIT(1);
1026     lpJava = APXHANDLE_DATA(pArgs->hJava);
1027     if (!lpJava)
1028         WORKER_EXIT(1);
1029     if (!apxJavaInitialize(pArgs->hJava,
1030                            pArgs->szClassPath,
1031                            pArgs->lpOptions, pArgs->lpOptions9,
1032                            pArgs->dwMs, pArgs->dwMx, pArgs->dwSs,
1033                            pArgs->bJniVfprintf)) {
1034         WORKER_EXIT(2);
1035     }
1036     if (pArgs->szLibraryPath && *pArgs->szLibraryPath) {
1037         DYNLOAD_FPTR_ADDRESS(SetDllDirectoryW, KERNEL32);
1038         DYNLOAD_CALL(SetDllDirectoryW)(pArgs->szLibraryPath);
1039         apxLogWrite(APXLOG_MARK_DEBUG "DLL search path set to '%S'",
1040                     pArgs->szLibraryPath);
1041     }
1042     if (!apxJavaLoadMainClass(pArgs->hJava,
1043                               pArgs->szClassName,
1044                               pArgs->szMethodName,
1045                               pArgs->lpArguments)) {
1046         WORKER_EXIT(3);
1047     }
1048     apxJavaSetOut(pArgs->hJava, TRUE,  pArgs->szStdErrFilename);
1049     apxJavaSetOut(pArgs->hJava, FALSE, pArgs->szStdOutFilename);
1050 
1051     /* Check if we have a class and a method */
1052     if (!lpJava->clWorker.jClazz || !lpJava->clWorker.jMethod) {
1053         WORKER_EXIT(4);
1054     }
1055     if (!__apxJvmAttach(lpJava)) {
1056         WORKER_EXIT(5);
1057     }
1058     apxLogWrite(APXLOG_MARK_DEBUG "Java worker thread started for %s:%s",
1059                 lpJava->clWorker.sClazz, lpJava->clWorker.sMethod);
1060     lpJava->dwWorkerStatus = 1;
1061     SetEvent(lpJava->hWorkerInit);
1062     /* Ensure apxJavaStart worker has read our status */
1063     WaitForSingleObject(lpJava->hWorkerSync, INFINITE);
1064     apxLogWrite(APXLOG_MARK_DEBUG "JNI calling static void method %s:%s",
1065                 lpJava->clWorker.sClazz, lpJava->clWorker.sMethod);
1066     JNICALL_3(CallStaticVoidMethod,
1067               lpJava->clWorker.jClazz,
1068               lpJava->clWorker.jMethod,
1069               lpJava->clWorker.jArgs);
1070     if (JVM_EXCEPTION_CHECK(lpJava)) {
1071         apxLogWrite(APXLOG_MARK_DEBUG "Exception has been thrown");
1072         vmExitCode = 1;
1073         (*((lpJava)->lpEnv))->ExceptionDescribe((lpJava)->lpEnv);
1074         __apxJvmDetach(lpJava);
1075         WORKER_EXIT(6);
1076     }
1077     else {
1078         __apxJvmDetach(lpJava);
1079     }
1080 finished:
1081     if (lpJava) {
1082         lpJava->dwWorkerStatus = 0;
1083         apxLogWrite(APXLOG_MARK_DEBUG "Java worker thread finished %s:%s with status = %d",
1084                     lpJava->clWorker.sClazz, lpJava->clWorker.sMethod, rv);
1085         SetEvent(lpJava->hWorkerInit);
1086     }
1087     ExitThread(rv);
1088     /* Never gets here but keep the compiler happy */
1089     return rv;
1090 }
1091 
1092 BOOL
apxJavaStart(LPAPXJAVA_THREADARGS pArgs)1093 apxJavaStart(LPAPXJAVA_THREADARGS pArgs)
1094 {
1095     LPAPXJAVAVM lpJava;
1096     lpJava = APXHANDLE_DATA(pArgs->hJava);
1097     if (!lpJava)
1098         return FALSE;
1099     lpJava->dwWorkerStatus = 0;
1100     lpJava->hWorkerInit    = CreateEvent(NULL, FALSE, FALSE, NULL);
1101     lpJava->hWorkerSync    = CreateEvent(NULL, FALSE, FALSE, NULL);
1102     lpJava->hWorkerThread  = CreateThread(NULL,
1103                                           lpJava->szStackSize,
1104                                           __apxJavaWorkerThread,
1105                                           pArgs, CREATE_SUSPENDED,
1106                                           &lpJava->iWorkerThread);
1107     if (IS_INVALID_HANDLE(lpJava->hWorkerThread)) {
1108         apxLogWrite(APXLOG_MARK_SYSERR);
1109         return FALSE;
1110     }
1111     ResumeThread(lpJava->hWorkerThread);
1112     /* Wait until the worker thread initializes */
1113     WaitForSingleObject(lpJava->hWorkerInit, INFINITE);
1114     if (lpJava->dwWorkerStatus == 0)
1115         return FALSE;
1116     SetEvent(lpJava->hWorkerSync);
1117     if (lstrcmpA(lpJava->clWorker.sClazz, "java/lang/System")) {
1118         /* Give some time to initialize the thread
1119          * Unless we are calling System.exit(0).
1120          * This will be handled by _onexit hook.
1121          */
1122         Sleep(1000);
1123     }
1124     return TRUE;
1125 }
1126 
1127 DWORD
apxJavaSetOptions(APXHANDLE hJava,DWORD dwOptions)1128 apxJavaSetOptions(APXHANDLE hJava, DWORD dwOptions)
1129 {
1130     DWORD dwOrgOptions;
1131     LPAPXJAVAVM lpJava;
1132 
1133     if (hJava->dwType != APXHANDLE_TYPE_JVM)
1134         return 0;
1135     lpJava = APXHANDLE_DATA(hJava);
1136     dwOrgOptions = lpJava->dwOptions;
1137     lpJava->dwOptions = dwOptions;
1138     return dwOrgOptions;
1139 }
1140 
1141 DWORD
apxJavaWait(APXHANDLE hJava,DWORD dwMilliseconds,BOOL bKill)1142 apxJavaWait(APXHANDLE hJava, DWORD dwMilliseconds, BOOL bKill)
1143 {
1144     DWORD rv;
1145     LPAPXJAVAVM lpJava;
1146 
1147     if (hJava->dwType != APXHANDLE_TYPE_JVM)
1148         return FALSE;
1149     lpJava = APXHANDLE_DATA(hJava);
1150 
1151     if (!lpJava->dwWorkerStatus && lpJava->hWorkerThread)
1152         return WAIT_OBJECT_0;
1153     apxLogWrite(APXLOG_MARK_DEBUG "WaitForSingleObject 0x%p %d milliseconds (INFINITE=%d)...",
1154         lpJava->hWorkerThread, dwMilliseconds, INFINITE);
1155     rv = WaitForSingleObject(lpJava->hWorkerThread, dwMilliseconds);
1156     apxLogWrite(APXLOG_MARK_DEBUG "WaitForSingleObject 0x%p = %d (WAIT_TIMEOUT=%d)",
1157         lpJava->hWorkerThread, rv, WAIT_TIMEOUT);
1158     if (rv == WAIT_TIMEOUT && bKill) {
1159         __apxJavaJniCallback(hJava, WM_CLOSE, 0, 0);
1160     }
1161 
1162     return rv;
1163 }
1164 
1165 LPVOID
apxJavaCreateClassV(APXHANDLE hJava,LPCSTR szClassName,LPCSTR szSignature,va_list lpArgs)1166 apxJavaCreateClassV(APXHANDLE hJava, LPCSTR szClassName,
1167                     LPCSTR szSignature, va_list lpArgs)
1168 {
1169     LPAPXJAVAVM     lpJava;
1170     jclass          clazz;
1171     jmethodID       ccont;
1172     jobject         cinst;
1173 
1174     if (hJava->dwType != APXHANDLE_TYPE_JVM)
1175         return NULL;
1176     lpJava = APXHANDLE_DATA(hJava);
1177     if (!__apxJvmAttach(lpJava))
1178         return NULL;
1179 
1180     clazz = JNICALL_1(FindClass, szClassName);
1181     if (clazz == NULL || (JVM_EXCEPTION_CHECK(lpJava))) {
1182         JVM_EXCEPTION_CLEAR(lpJava);
1183         apxLogWrite(APXLOG_MARK_ERROR "Could not FindClass %s", szClassName);
1184         return NULL;
1185     }
1186 
1187     ccont = JNICALL_3(GetMethodID, clazz, "<init>", szSignature);
1188     if (ccont == NULL || (JVM_EXCEPTION_CHECK(lpJava))) {
1189         JVM_EXCEPTION_CLEAR(lpJava);
1190         apxLogWrite(APXLOG_MARK_ERROR "Could not find Constructor %s for %s",
1191                     szSignature, szClassName);
1192         return NULL;
1193     }
1194 
1195     cinst = JNICALL_3(NewObjectV, clazz, ccont, lpArgs);
1196     if (cinst == NULL || (JVM_EXCEPTION_CHECK(lpJava))) {
1197         JVM_EXCEPTION_CLEAR(lpJava);
1198         apxLogWrite(APXLOG_MARK_ERROR "Could not create instance of %s",
1199                     szClassName);
1200         return NULL;
1201     }
1202 
1203     return cinst;
1204 }
1205 
1206 LPVOID
apxJavaCreateClass(APXHANDLE hJava,LPCSTR szClassName,LPCSTR szSignature,...)1207 apxJavaCreateClass(APXHANDLE hJava, LPCSTR szClassName,
1208                    LPCSTR szSignature, ...)
1209 {
1210     LPVOID rv;
1211     va_list args;
1212 
1213     va_start(args, szSignature);
1214     rv = apxJavaCreateClassV(hJava, szClassName, szSignature, args);
1215     va_end(args);
1216 
1217     return rv;
1218 }
1219 
1220 LPVOID
apxJavaCreateStringA(APXHANDLE hJava,LPCSTR szString)1221 apxJavaCreateStringA(APXHANDLE hJava, LPCSTR szString)
1222 {
1223     LPAPXJAVAVM     lpJava;
1224     jstring str;
1225 
1226     if (hJava->dwType != APXHANDLE_TYPE_JVM)
1227         return NULL;
1228     lpJava = APXHANDLE_DATA(hJava);
1229 
1230     str = JNICALL_1(NewStringUTF, szString);
1231     if (str == NULL || (JVM_EXCEPTION_CHECK(lpJava))) {
1232         JVM_EXCEPTION_CLEAR(lpJava);
1233         apxLogWrite(APXLOG_MARK_ERROR "Could not create string for %s",
1234                     szString);
1235         return NULL;
1236     }
1237 
1238     return str;
1239 }
1240 
1241 LPVOID
apxJavaCreateStringW(APXHANDLE hJava,LPCWSTR szString)1242 apxJavaCreateStringW(APXHANDLE hJava, LPCWSTR szString)
1243 {
1244     LPAPXJAVAVM     lpJava;
1245     jstring str;
1246 
1247     if (hJava->dwType != APXHANDLE_TYPE_JVM)
1248         return NULL;
1249     lpJava = APXHANDLE_DATA(hJava);
1250 
1251     str = JNICALL_2(NewString, szString, lstrlenW(szString));
1252     if (str == NULL || (JVM_EXCEPTION_CHECK(lpJava))) {
1253         JVM_EXCEPTION_CLEAR(lpJava);
1254         apxLogWrite(APXLOG_MARK_ERROR "Could not create string for %S",
1255                     szString);
1256         return NULL;
1257     }
1258 
1259     return str;
1260 }
1261 
1262 jvalue
apxJavaCallStaticMethodV(APXHANDLE hJava,jclass lpClass,LPCSTR szMethodName,LPCSTR szSignature,va_list lpArgs)1263 apxJavaCallStaticMethodV(APXHANDLE hJava, jclass lpClass, LPCSTR szMethodName,
1264                          LPCSTR szSignature, va_list lpArgs)
1265 {
1266     LPAPXJAVAVM     lpJava;
1267     jmethodID       method;
1268     jvalue          rv;
1269     LPCSTR          s = szSignature;
1270     rv.l = 0;
1271     if (hJava->dwType != APXHANDLE_TYPE_JVM)
1272         return rv;
1273     lpJava = APXHANDLE_DATA(hJava);
1274 
1275     while (*s && *s != ')')
1276         ++s;
1277     if (*s != ')') {
1278         return rv;
1279     }
1280     else
1281         ++s;
1282     method = JNICALL_3(GetStaticMethodID, lpClass, szMethodName, szSignature);
1283     if (method == NULL || (JVM_EXCEPTION_CHECK(lpJava))) {
1284         JVM_EXCEPTION_CLEAR(lpJava);
1285         apxLogWrite(APXLOG_MARK_ERROR "Could not find method %s with signature %s",
1286                     szMethodName, szSignature);
1287         return rv;
1288     }
1289     switch (*s) {
1290         case 'V':
1291             JNICALL_3(CallStaticVoidMethodV, lpClass, method, lpArgs);
1292         break;
1293         case 'L':
1294         case '[':
1295             rv.l = JNICALL_3(CallStaticObjectMethodV, lpClass, method, lpArgs);
1296         break;
1297         case 'Z':
1298             rv.z = JNICALL_3(CallStaticBooleanMethodV, lpClass, method, lpArgs);
1299         break;
1300         case 'B':
1301             rv.b = JNICALL_3(CallStaticByteMethodV, lpClass, method, lpArgs);
1302         break;
1303         case 'C':
1304             rv.c = JNICALL_3(CallStaticCharMethodV, lpClass, method, lpArgs);
1305         break;
1306         case 'S':
1307             rv.i = JNICALL_3(CallStaticShortMethodV, lpClass, method, lpArgs);
1308         break;
1309         case 'I':
1310             rv.i = JNICALL_3(CallStaticIntMethodV, lpClass, method, lpArgs);
1311         break;
1312         case 'J':
1313             rv.j = JNICALL_3(CallStaticLongMethodV, lpClass, method, lpArgs);
1314         break;
1315         case 'F':
1316             rv.f = JNICALL_3(CallStaticFloatMethodV, lpClass, method, lpArgs);
1317         break;
1318         case 'D':
1319             rv.d = JNICALL_3(CallStaticDoubleMethodV, lpClass, method, lpArgs);
1320         break;
1321         default:
1322             apxLogWrite(APXLOG_MARK_ERROR "Invalid signature %s for method %s",
1323                         szSignature, szMethodName);
1324             return rv;
1325         break;
1326     }
1327 
1328     return rv;
1329 }
1330 
1331 jvalue
apxJavaCallStaticMethod(APXHANDLE hJava,jclass lpClass,LPCSTR szMethodName,LPCSTR szSignature,...)1332 apxJavaCallStaticMethod(APXHANDLE hJava, jclass lpClass, LPCSTR szMethodName,
1333                         LPCSTR szSignature, ...)
1334 {
1335     jvalue rv;
1336     va_list args;
1337 
1338     va_start(args, szSignature);
1339     rv = apxJavaCallStaticMethodV(hJava, lpClass, szMethodName, szSignature, args);
1340     va_end(args);
1341 
1342     return rv;
1343 }
1344 
1345 /* Call the Java:
1346  * System.setOut(new PrintStream(new FileOutputStream(filename)));
1347  */
1348 BOOL
apxJavaSetOut(APXHANDLE hJava,BOOL setErrorOrOut,LPCWSTR szFilename)1349 apxJavaSetOut(APXHANDLE hJava, BOOL setErrorOrOut, LPCWSTR szFilename)
1350 {
1351     LPAPXJAVAVM lpJava;
1352     jobject     fs;
1353     jobject     ps;
1354     jstring     fn;
1355     jclass      sys;
1356 
1357     if (hJava->dwType != APXHANDLE_TYPE_JVM || !szFilename)
1358         return FALSE;
1359     lpJava = APXHANDLE_DATA(hJava);
1360     if (!__apxJvmAttach(lpJava))
1361         return FALSE;
1362 
1363     if ((fn = apxJavaCreateStringW(hJava, szFilename)) == NULL)
1364         return FALSE;
1365     if ((fs = apxJavaCreateClass(hJava, "java/io/FileOutputStream",
1366                                  "(Ljava/lang/String;Z)V", fn, JNI_TRUE)) == NULL)
1367         return FALSE;
1368     if ((ps = apxJavaCreateClass(hJava, "java/io/PrintStream",
1369                                  "(Ljava/io/OutputStream;)V", fs)) == NULL)
1370         return FALSE;
1371     sys = JNICALL_1(FindClass, "java/lang/System");
1372     if (sys == NULL || (JVM_EXCEPTION_CHECK(lpJava))) {
1373         JVM_EXCEPTION_CLEAR(lpJava);
1374         apxLogWrite(APXLOG_MARK_ERROR "Could not FindClass java/lang/System");
1375         return FALSE;
1376     }
1377 
1378     if (setErrorOrOut)
1379         apxJavaCallStaticMethod(hJava, sys, "setErr", "(Ljava/io/PrintStream;)V", ps);
1380     else
1381         apxJavaCallStaticMethod(hJava, sys, "setOut", "(Ljava/io/PrintStream;)V", ps);
1382 
1383     if (JVM_EXCEPTION_CHECK(lpJava)) {
1384         JVM_EXCEPTION_CLEAR(lpJava);
1385         apxLogWrite(APXLOG_MARK_ERROR "Error calling set method for java/lang/System");
1386         return FALSE;
1387     }
1388     else
1389         return TRUE;
1390 
1391 }
1392 
apxGetVmExitCode(void)1393 DWORD apxGetVmExitCode(void) {
1394     return vmExitCode;
1395 }
1396 
apxSetVmExitCode(DWORD exitCode)1397 void apxSetVmExitCode(DWORD exitCode) {
1398     vmExitCode = exitCode;
1399     return;
1400 }
1401 
1402 void
apxJavaDumpAllStacks(APXHANDLE hJava)1403 apxJavaDumpAllStacks(APXHANDLE hJava)
1404 {
1405     BOOL bAttached;
1406     LPAPXJAVAVM lpJava;
1407     JNIEnv *lpEnv = NULL;
1408 
1409     if (DYNLOAD_FPTR(JVM_DumpAllStacks) == NULL ||
1410         hJava == NULL ||
1411         hJava->dwType != APXHANDLE_TYPE_JVM)
1412         return;
1413     lpJava = APXHANDLE_DATA(hJava);
1414     if (__apxJvmAttachEnv(lpJava, &lpEnv, &bAttached)) {
1415         DYNLOAD_FPTR(JVM_DumpAllStacks)(lpEnv, NULL);
1416         if (bAttached)
1417             (*(lpJava->lpJvm))->DetachCurrentThread(lpJava->lpJvm);
1418     }
1419 }
1420