/* * Given a path to a jre to execute, this routine checks if this process * is indeed that jre. If not, it exec's that jre. * * We want to actually check the paths rather than just the version string * built into the executable, so that given version specification will yield * the exact same Java environment, regardless of the version of the arbitrary * launcher we start with. */ void ExecJRE(char *jre, char **argv) { int len; char path[MAXPATHLEN + 1]; const char *progname = GetProgramName(); /* * Resolve the real path to the currently running launcher. */ len = GetModuleFileName(NULL, path, MAXPATHLEN + 1); if (len == 0 || len > MAXPATHLEN) { JLI_ReportErrorMessageSys(JRE_ERROR9, progname); exit(1); } JLI_TraceLauncher("ExecJRE: old: %s\n", path); JLI_TraceLauncher("ExecJRE: new: %s\n", jre); /* * If the path to the selected JRE directory is a match to the initial * portion of the path to the currently executing JRE, we have a winner! * If so, just return. */ if (JLI_StrNCaseCmp(jre, path, JLI_StrLen(jre)) == 0) return; /* I am the droid you were looking for */ /* * If this isn't the selected version, exec the selected version. */ JLI_Snprintf(path, sizeof(path), "%s\\bin\\%s.exe", jre, progname); /* * Although Windows has an execv() entrypoint, it doesn't actually * overlay a process: it can only create a new process and terminate * the old process. Therefore, any processes waiting on the initial * process wake up and they shouldn't. Hence, a chain of pseudo-zombie * processes must be retained to maintain the proper wait semantics. * Fortunately the image size of the launcher isn't too large at this * time. * * If it weren't for this semantic flaw, the code below would be ... * * execv(path, argv); * JLI_ReportErrorMessage("Error: Exec of %s failed\n", path); * exit(1); * * The incorrect exec semantics could be addressed by: * * exit((int)spawnv(_P_WAIT, path, argv)); * * Unfortunately, a bug in Windows spawn/exec impementation prevents * this from completely working. All the Windows POSIX process creation * interfaces are implemented as wrappers around the native Windows * function CreateProcess(). CreateProcess() takes a single string * to specify command line options and arguments, so the POSIX routine * wrappers build a single string from the argv[] array and in the * process, any quoting information is lost. * * The solution to this to get the original command line, to process it * to remove the new multiple JRE options (if any) as was done for argv * in the common SelectVersion() routine and finally to pass it directly * to the native CreateProcess() Windows process control interface. */ { char *cmdline; char *p; char *np; char *ocl; char *ccl; char *unquoted; DWORD exitCode; STARTUPINFO si; PROCESS_INFORMATION pi; /* * The following code block gets and processes the original command * line, replacing the argv[0] equivalent in the command line with * the path to the new executable and removing the appropriate * Multiple JRE support options. Note that similar logic exists * in the platform independent SelectVersion routine, but is * replicated here due to the syntax of CreateProcess(). * * The magic "+ 4" characters added to the command line length are * 2 possible quotes around the path (argv[0]), a space after the * path and a terminating null character. */ ocl = GetCommandLine(); np = ccl = JLI_StringDup(ocl); p = nextarg(&np); /* Discard argv[0] */ cmdline = (char *)JLI_MemAlloc(JLI_StrLen(path) + JLI_StrLen(np) + 4); if (JLI_StrChr(path, (int)' ') == NULL && JLI_StrChr(path, (int)'\t') == NULL) cmdline = JLI_StrCpy(cmdline, path); else cmdline = JLI_StrCat(JLI_StrCat(JLI_StrCpy(cmdline, "\""), path), "\""); while (*np != (char)0) { /* While more command-line */ p = nextarg(&np); if (*p != (char)0) { /* If a token was isolated */ unquoted = unquote(p); if (*unquoted == '-') { /* Looks like an option */ if (JLI_StrCmp(unquoted, "-classpath") == 0 || JLI_StrCmp(unquoted, "-cp") == 0) { /* Unique cp syntax */ cmdline = JLI_StrCat(JLI_StrCat(cmdline, " "), p); p = nextarg(&np); if (*p != (char)0) /* If a token was isolated */ cmdline = JLI_StrCat(JLI_StrCat(cmdline, " "), p); } else if (JLI_StrNCmp(unquoted, "-version:", 9) != 0 && JLI_StrCmp(unquoted, "-jre-restrict-search") != 0 && JLI_StrCmp(unquoted, "-no-jre-restrict-search") != 0) { cmdline = JLI_StrCat(JLI_StrCat(cmdline, " "), p); } } else { /* End of options */ cmdline = JLI_StrCat(JLI_StrCat(cmdline, " "), p); cmdline = JLI_StrCat(JLI_StrCat(cmdline, " "), np); JLI_MemFree((void *)unquoted); break; } JLI_MemFree((void *)unquoted); } } JLI_MemFree((void *)ccl); if (JLI_IsTraceLauncher()) { np = ccl = JLI_StringDup(cmdline); p = nextarg(&np); printf("ReExec Command: %s (%s)\n", path, p); printf("ReExec Args: %s\n", np); JLI_MemFree((void *)ccl); } (void)fflush(stdout); (void)fflush(stderr); /* * The following code is modeled after a model presented in the * Microsoft Technical Article "Moving Unix Applications to * Windows NT" (March 6, 1994) and "Creating Processes" on MSDN * (Februrary 2005). It approximates UNIX spawn semantics with * the parent waiting for termination of the child. */ memset(&si, 0, sizeof(si)); si.cb =sizeof(STARTUPINFO); memset(&pi, 0, sizeof(pi)); if (!CreateProcess((LPCTSTR)path, /* executable name */ (LPTSTR)cmdline, /* command line */ (LPSECURITY_ATTRIBUTES)NULL, /* process security attr. */ (LPSECURITY_ATTRIBUTES)NULL, /* thread security attr. */ (BOOL)TRUE, /* inherits system handles */ (DWORD)0, /* creation flags */ (LPVOID)NULL, /* environment block */ (LPCTSTR)NULL, /* current directory */ (LPSTARTUPINFO)&si, /* (in) startup information */ (LPPROCESS_INFORMATION)&pi)) { /* (out) process information */ JLI_ReportErrorMessageSys(SYS_ERROR1, path); exit(1); } if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_FAILED) { if (GetExitCodeProcess(pi.hProcess, &exitCode) == FALSE) exitCode = 1; } else { JLI_ReportErrorMessage(SYS_ERROR2); exitCode = 1; } CloseHandle(pi.hThread); CloseHandle(pi.hProcess); exit(exitCode); } }
/* * 使用指定的jvm版本来运行java程序 * * ****************************************************************************** * 函数原型: char *realpath(const char *path, char *resolved_path); * 函数说明: 用来将参数path所指的相对路径转换成绝对路径后存于参数resolved_path所指的字符串数组或指针中 * 函数返回: 成功则返回指向resolved_path的指针,失败返回NULL,错误代码存于errno * * ****************************************************************************** * 函数原型: int execve(const char * filename,char * const argv[],char * const envp[]); * 函数说明: execve用来执行参数filename字符串所代表的文件路径,第二个参数是利用数组指针来传递给执行文件,并且需要以 * 空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组.exec函数一共有六个,其中execve为内核 * 级系统调用,其他(execl,execle,execlp,execv,execvp)都是调用execve的库函数 * 函数返回: 如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中 */ void ExecJRE(char *jre, char **argv) { char wanted[PATH_MAX]; const char* progname = GetProgramName(); const char* execname = NULL; /* * 转换成绝对路径 */ if (realpath(jre, wanted) == NULL) { JLI_ReportErrorMessage(JRE_ERROR9, jre); exit(1); } /* * 获取启动当前JVM进程的命令 */ SetExecname(argv); execname = GetExecName(); if (execname == NULL) { JLI_ReportErrorMessage(JRE_ERROR10); exit(1); } /* * If the path to the selected JRE directory is a match to the initial * portion of the path to the currently executing JRE, we have a winner! * If so, just return. */ if (JLI_StrNCmp(wanted, execname, JLI_StrLen(wanted)) == 0) return; /* I am the droid you were looking for */ /* * This should never happen (because of the selection code in SelectJRE), * but check for "impossibly" long path names just because buffer overruns * can be so deadly. */ if (JLI_StrLen(wanted) + JLI_StrLen(progname) + 6 > PATH_MAX) { JLI_ReportErrorMessage(JRE_ERROR11); exit(1); } /* * Construct the path and exec it. */ (void)JLI_StrCat(JLI_StrCat(wanted, "/bin/"), progname); argv[0] = JLI_StringDup(progname); if (JLI_IsTraceLauncher()) { int i; printf("ReExec Command: %s (%s)\n", wanted, argv[0]); printf("ReExec Args:"); for (i = 1; argv[i] != NULL; i++) printf(" %s", argv[i]); printf("\n"); } JLI_TraceLauncher("TRACER_MARKER:About to EXEC\n"); //刷当前进程的标准输出/错误输出流 (void)fflush(stdout); (void)fflush(stderr); //执行新的可执行程序 execv(wanted, argv); JLI_ReportErrorMessageSys(JRE_ERROR12, wanted); exit(1); }
static jboolean GetPublicJREHome(char *buf, jint bufsize) { HKEY key, subkey; char version[MAXPATHLEN]; /* * Note: There is a very similar implementation of the following * registry reading code in the Windows java control panel (javacp.cpl). * If there are bugs here, a similar bug probably exists there. Hence, * changes here require inspection there. */ /* Find the current version of the JRE */ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, JRE_KEY, 0, KEY_READ, &key) != 0) { JLI_ReportErrorMessage(REG_ERROR1, JRE_KEY); return JNI_FALSE; } if (!GetStringFromRegistry(key, "CurrentVersion", version, sizeof(version))) { JLI_ReportErrorMessage(REG_ERROR2, JRE_KEY); RegCloseKey(key); return JNI_FALSE; } if (JLI_StrCmp(version, GetDotVersion()) != 0) { JLI_ReportErrorMessage(REG_ERROR3, JRE_KEY, version, GetDotVersion() ); RegCloseKey(key); return JNI_FALSE; } /* Find directory where the current version is installed. */ if (RegOpenKeyEx(key, version, 0, KEY_READ, &subkey) != 0) { JLI_ReportErrorMessage(REG_ERROR1, JRE_KEY, version); RegCloseKey(key); return JNI_FALSE; } if (!GetStringFromRegistry(subkey, "JavaHome", buf, bufsize)) { JLI_ReportErrorMessage(REG_ERROR4, JRE_KEY, version); RegCloseKey(key); RegCloseKey(subkey); return JNI_FALSE; } if (JLI_IsTraceLauncher()) { char micro[MAXPATHLEN]; if (!GetStringFromRegistry(subkey, "MicroVersion", micro, sizeof(micro))) { printf("Warning: Can't read MicroVersion\n"); micro[0] = '\0'; } printf("Version major.minor.micro = %s.%s\n", version, micro); } RegCloseKey(key); RegCloseKey(subkey); return JNI_TRUE; }