--- /dev/null 2010-09-21 13:20:11.506531901 +0200 +++ new/src/os/windows/launcher/java.c 2010-09-22 14:34:24.185460591 +0200 @@ -0,0 +1,2090 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * Gamma (Hotspot internal engineering test) launcher based on 6.0u22-b01 JDK, + * search "GAMMA" for gamma specific changes. + * + * GAMMA: gamma launcher is much simpler than regular java launcher in that + * JVM is either statically linked in or it is installed in the + * same directory where the launcher exists, so we don't have to + * worry about choosing the right JVM based on command line flag, jar + * file and/or ergonomics. Intead of removing unused logic from source + * they are commented out with #ifndef GAMMA, hopefully it'll be easier + * to maintain this file in sync with regular JDK launcher. + */ + +/* + * Shared source for 'java' command line tool. + * + * If JAVA_ARGS is defined, then it acts as a launcher for applications. For + * instance, the JDK command line tools such as javac and javadoc (see + * makefiles for more details) are built with this program. Any arguments + * prefixed with '-J' will be passed directly to the 'java' command. + */ + +#ifdef GAMMA +# ifdef JAVA_ARGS +# error Do NOT define JAVA_ARGS when building gamma launcher +# endif +#endif + +/* + * One job of the launcher is to remove command line options which the + * vm does not understand and will not process. These options include + * options which select which style of vm is run (e.g. -client and + * -server) as well as options which select the data model to use. + * Additionally, for tools which invoke an underlying vm "-J-foo" + * options are turned into "-foo" options to the vm. This option + * filtering is handled in a number of places in the launcher, some of + * it in machine-dependent code. In this file, the function + * CheckJVMType removes vm style options and TranslateApplicationArgs + * removes "-J" prefixes. On unix platforms, the + * CreateExecutionEnvironment function from the unix java_md.c file + * processes and removes -d options. However, in case + * CreateExecutionEnvironment does not need to exec because + * LD_LIBRARY_PATH is set acceptably and the data model does not need + * to be changed, ParseArguments will screen out the redundant -d + * options and prevent them from being passed to the vm; this is done + * by using the machine-dependent call + * RemovableMachineDependentOption. + */ + +#include +#include +#include + +#include +#include +#include "java.h" + +#ifndef GAMMA +#include "manifest_info.h" +#include "version_comp.h" +#include "wildcard.h" +#include "splashscreen.h" +#endif + +#ifndef FULL_VERSION +#define FULL_VERSION JDK_MAJOR_VERSION "." JDK_MINOR_VERSION +#endif + +/* + * The following environment variable is used to influence the behavior + * of the jre exec'd through the SelectVersion routine. The command line + * options which specify the version are not passed to the exec'd version, + * because that jre may be an older version which wouldn't recognize them. + * This environment variable is known to this (and later) version and serves + * to suppress the version selection code. This is not only for efficiency, + * but also for correctness, since any command line options have been + * removed which would cause any value found in the manifest to be used. + * This would be incorrect because the command line options are defined + * to take precedence. + * + * The value associated with this environment variable is the MainClass + * name from within the executable jar file (if any). This is strictly a + * performance enhancement to avoid re-reading the jar file manifest. + * + * A NOTE TO DEVELOPERS: For performance reasons it is important that + * the program image remain relatively small until after SelectVersion + * CreateExecutionEnvironment have finished their possibly recursive + * processing. Watch everything, but resist all temptations to use Java + * interfaces. + */ +#define ENV_ENTRY "_JAVA_VERSION_SET" + +#ifndef GAMMA +#define SPLASH_FILE_ENV_ENTRY "_JAVA_SPLASH_FILE" +#define SPLASH_JAR_ENV_ENTRY "_JAVA_SPLASH_JAR" +#endif + +static jboolean printVersion = JNI_FALSE; /* print and exit */ +static jboolean showVersion = JNI_FALSE; /* print but continue */ +static char *progname; +jboolean _launcher_debug = JNI_FALSE; + +#ifndef GAMMA +/* + * Entries for splash screen environment variables. + * putenv is performed in SelectVersion. We need + * them in memory until UnsetEnv, so they are made static + * global instead of auto local. + */ +static char* splash_file_entry = NULL; +static char* splash_jar_entry = NULL; +#endif + +/* + * List of VM options to be specified when the VM is created. + */ +static JavaVMOption *options; +static int numOptions, maxOptions; + +/* + * Prototypes for functions internal to launcher. + */ +static void SetClassPath(const char *s); +static void SelectVersion(int argc, char **argv, char **main_class); +static jboolean ParseArguments(int *pargc, char ***pargv, char **pjarfile, + char **pclassname, int *pret, const char *jvmpath); +static jboolean InitializeJVM(JavaVM **pvm, JNIEnv **penv, + InvocationFunctions *ifn); +static jstring NewPlatformString(JNIEnv *env, char *s); +static jobjectArray NewPlatformStringArray(JNIEnv *env, char **strv, int strc); +static jclass LoadClass(JNIEnv *env, char *name); +static jstring GetMainClassName(JNIEnv *env, char *jarname); +static void SetJavaCommandLineProp(char* classname, char* jarfile, int argc, char** argv); +#ifdef GAMMA +static void SetJavaLauncherProp(void); +#endif + +#ifdef JAVA_ARGS +static void TranslateApplicationArgs(int *pargc, char ***pargv); +static jboolean AddApplicationOptions(void); +#endif + +static void PrintJavaVersion(JNIEnv *env); +static void PrintUsage(void); +static jint PrintXUsage(const char *jvmpath); + +static void SetPaths(int argc, char **argv); + +/* Maximum supported entries from jvm.cfg. */ +#define INIT_MAX_KNOWN_VMS 10 +/* Values for vmdesc.flag */ +#define VM_UNKNOWN -1 +#define VM_KNOWN 0 +#define VM_ALIASED_TO 1 +#define VM_WARN 2 +#define VM_ERROR 3 +#define VM_IF_SERVER_CLASS 4 +#define VM_IGNORE 5 +struct vmdesc { + char *name; + int flag; + char *alias; + char *server_class; +}; +static struct vmdesc *knownVMs = NULL; +static int knownVMsCount = 0; +static int knownVMsLimit = 0; + +static void GrowKnownVMs(); +static int KnownVMIndex(const char* name); +static void FreeKnownVMs(); +#ifndef GAMMA +static void ShowSplashScreen(); +#endif + +jboolean ServerClassMachine(); + +/* flag which if set suppresses error messages from the launcher */ +static int noExitErrorMessage = 0; + +/* + * Running Java code in primordial thread caused many problems. We will + * create a new thread to invoke JVM. See 6316197 for more information. + */ +static jlong threadStackSize = 0; /* stack size of the new thread */ + +int JNICALL JavaMain(void * args); /* entry point */ + +struct JavaMainArgs { + int argc; + char ** argv; + char * jarfile; + char * classname; + InvocationFunctions ifn; +}; + +/* + * Entry point. + */ +int +main(int argc, char ** argv) +{ + char *jarfile = 0; + char *classname = 0; + char *s = 0; + char *main_class = NULL; + int ret; + InvocationFunctions ifn; + jlong start, end; + char jrepath[MAXPATHLEN], jvmpath[MAXPATHLEN]; + char ** original_argv = argv; + + if (getenv("_JAVA_LAUNCHER_DEBUG") != 0) { + _launcher_debug = JNI_TRUE; + printf("----_JAVA_LAUNCHER_DEBUG----\n"); + } + +#ifndef GAMMA + /* + * Make sure the specified version of the JRE is running. + * + * There are three things to note about the SelectVersion() routine: + * 1) If the version running isn't correct, this routine doesn't + * return (either the correct version has been exec'd or an error + * was issued). + * 2) Argc and Argv in this scope are *not* altered by this routine. + * It is the responsibility of subsequent code to ignore the + * arguments handled by this routine. + * 3) As a side-effect, the variable "main_class" is guaranteed to + * be set (if it should ever be set). This isn't exactly the + * poster child for structured programming, but it is a small + * price to pay for not processing a jar file operand twice. + * (Note: This side effect has been disabled. See comment on + * bugid 5030265 below.) + */ + SelectVersion(argc, argv, &main_class); +#endif /* ifndef GAMMA */ + + /* copy original argv */ + { + int i; + original_argv = (char**)JLI_MemAlloc(sizeof(char*)*(argc+1)); + for(i = 0; i < argc+1; i++) + original_argv[i] = argv[i]; + } + + CreateExecutionEnvironment(&argc, &argv, + jrepath, sizeof(jrepath), + jvmpath, sizeof(jvmpath), + original_argv); + + ifn.CreateJavaVM = 0; + ifn.GetDefaultJavaVMInitArgs = 0; + + if (_launcher_debug) + start = CounterGet(); + if (!LoadJavaVM(jvmpath, &ifn)) { + exit(6); + } + if (_launcher_debug) { + end = CounterGet(); + printf("%ld micro seconds to LoadJavaVM\n", + (long)(jint)Counter2Micros(end-start)); + } + +#ifdef JAVA_ARGS /* javac, jar and friends. */ + progname = "java"; +#else /* java, oldjava, javaw and friends */ +#ifdef PROGNAME + progname = PROGNAME; +#else + progname = *argv; + if ((s = strrchr(progname, FILE_SEPARATOR)) != 0) { + progname = s + 1; + } +#endif /* PROGNAME */ +#endif /* JAVA_ARGS */ + ++argv; + --argc; + +#ifdef JAVA_ARGS + /* Preprocess wrapper arguments */ + TranslateApplicationArgs(&argc, &argv); + if (!AddApplicationOptions()) { + exit(1); + } +#endif + + /* Set default CLASSPATH */ + if ((s = getenv("CLASSPATH")) == 0) { + s = "."; + } +#ifndef JAVA_ARGS + SetClassPath(s); +#endif + + /* + * Parse command line options; if the return value of + * ParseArguments is false, the program should exit. + */ + if (!ParseArguments(&argc, &argv, &jarfile, &classname, &ret, jvmpath)) { + exit(ret); + } + + /* Override class path if -jar flag was specified */ + if (jarfile != 0) { + SetClassPath(jarfile); + } + + /* set the -Dsun.java.command pseudo property */ + SetJavaCommandLineProp(classname, jarfile, argc, argv); + +#ifdef GAMMA + /* Set the -Dsun.java.launcher pseudo property */ + SetJavaLauncherProp(); +#endif + + /* set the -Dsun.java.launcher.* platform properties */ + SetJavaLauncherPlatformProps(); + +#ifndef GAMMA + /* Show the splash screen if needed */ + ShowSplashScreen(); +#endif + + /* + * Done with all command line processing and potential re-execs so + * clean up the environment. + */ + (void)UnsetEnv(ENV_ENTRY); +#ifndef GAMMA + (void)UnsetEnv(SPLASH_FILE_ENV_ENTRY); + (void)UnsetEnv(SPLASH_JAR_ENV_ENTRY); + + JLI_MemFree(splash_jar_entry); + JLI_MemFree(splash_file_entry); +#endif + + /* + * If user doesn't specify stack size, check if VM has a preference. + * Note that HotSpot no longer supports JNI_VERSION_1_1 but it will + * return its default stack size through the init args structure. + */ + if (threadStackSize == 0) { + struct JDK1_1InitArgs args1_1; + memset((void*)&args1_1, 0, sizeof(args1_1)); + args1_1.version = JNI_VERSION_1_1; + ifn.GetDefaultJavaVMInitArgs(&args1_1); /* ignore return value */ + if (args1_1.javaStackSize > 0) { + threadStackSize = args1_1.javaStackSize; + } + } + + { /* Create a new thread to create JVM and invoke main method */ + struct JavaMainArgs args; + + args.argc = argc; + args.argv = argv; + args.jarfile = jarfile; + args.classname = classname; + args.ifn = ifn; + + return ContinueInNewThread(JavaMain, threadStackSize, (void*)&args); + } +} + +int JNICALL +JavaMain(void * _args) +{ + struct JavaMainArgs *args = (struct JavaMainArgs *)_args; + int argc = args->argc; + char **argv = args->argv; + char *jarfile = args->jarfile; + char *classname = args->classname; + InvocationFunctions ifn = args->ifn; + + JavaVM *vm = 0; + JNIEnv *env = 0; + jstring mainClassName; + jclass mainClass; + jmethodID mainID; + jobjectArray mainArgs; + int ret = 0; + jlong start, end; + + /* + * Error message to print or display; by default the message will + * only be displayed in a window. + */ + char * message = "Fatal exception occurred. Program will exit."; + jboolean messageDest = JNI_FALSE; + + /* Initialize the virtual machine */ + + if (_launcher_debug) + start = CounterGet(); + if (!InitializeJVM(&vm, &env, &ifn)) { + ReportErrorMessage("Could not create the Java virtual machine.", + JNI_TRUE); + exit(1); + } + + if (printVersion || showVersion) { + PrintJavaVersion(env); + if ((*env)->ExceptionOccurred(env)) { + ReportExceptionDescription(env); + goto leave; + } + if (printVersion) { + ret = 0; + message = NULL; + goto leave; + } + if (showVersion) { + fprintf(stderr, "\n"); + } + } + + /* If the user specified neither a class name nor a JAR file */ + if (jarfile == 0 && classname == 0) { + PrintUsage(); + message = NULL; + goto leave; + } + +#ifndef GAMMA + FreeKnownVMs(); /* after last possible PrintUsage() */ +#endif + + if (_launcher_debug) { + end = CounterGet(); + printf("%ld micro seconds to InitializeJVM\n", + (long)(jint)Counter2Micros(end-start)); + } + + /* At this stage, argc/argv have the applications' arguments */ + if (_launcher_debug) { + int i = 0; + printf("Main-Class is '%s'\n", classname ? classname : ""); + printf("Apps' argc is %d\n", argc); + for (; i < argc; i++) { + printf(" argv[%2d] = '%s'\n", i, argv[i]); + } + } + + ret = 1; + + /* + * Get the application's main class. + * + * See bugid 5030265. The Main-Class name has already been parsed + * from the manifest, but not parsed properly for UTF-8 support. + * Hence the code here ignores the value previously extracted and + * uses the pre-existing code to reextract the value. This is + * possibly an end of release cycle expedient. However, it has + * also been discovered that passing some character sets through + * the environment has "strange" behavior on some variants of + * Windows. Hence, maybe the manifest parsing code local to the + * launcher should never be enhanced. + * + * Hence, future work should either: + * 1) Correct the local parsing code and verify that the + * Main-Class attribute gets properly passed through + * all environments, + * 2) Remove the vestages of maintaining main_class through + * the environment (and remove these comments). + */ + if (jarfile != 0) { + mainClassName = GetMainClassName(env, jarfile); + if ((*env)->ExceptionOccurred(env)) { + ReportExceptionDescription(env); + goto leave; + } + if (mainClassName == NULL) { + const char * format = "Failed to load Main-Class manifest " + "attribute from\n%s"; + message = (char*)JLI_MemAlloc((strlen(format) + strlen(jarfile)) * + sizeof(char)); + sprintf(message, format, jarfile); + messageDest = JNI_TRUE; + goto leave; + } + classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0); + if (classname == NULL) { + ReportExceptionDescription(env); + goto leave; + } + mainClass = LoadClass(env, classname); + if(mainClass == NULL) { /* exception occured */ + const char * format = "Could not find the main class: %s. Program will exit."; + ReportExceptionDescription(env); + message = (char *)JLI_MemAlloc((strlen(format) + + strlen(classname)) * sizeof(char) ); + messageDest = JNI_TRUE; + sprintf(message, format, classname); + goto leave; + } + (*env)->ReleaseStringUTFChars(env, mainClassName, classname); + } else { + mainClassName = NewPlatformString(env, classname); + if (mainClassName == NULL) { + const char * format = "Failed to load Main Class: %s"; + message = (char *)JLI_MemAlloc((strlen(format) + strlen(classname)) * + sizeof(char) ); + sprintf(message, format, classname); + messageDest = JNI_TRUE; + goto leave; + } + classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0); + if (classname == NULL) { + ReportExceptionDescription(env); + goto leave; + } + mainClass = LoadClass(env, classname); + if(mainClass == NULL) { /* exception occured */ + const char * format = "Could not find the main class: %s. Program will exit."; + ReportExceptionDescription(env); + message = (char *)JLI_MemAlloc((strlen(format) + + strlen(classname)) * sizeof(char) ); + messageDest = JNI_TRUE; + sprintf(message, format, classname); + goto leave; + } + (*env)->ReleaseStringUTFChars(env, mainClassName, classname); + } + + /* Get the application's main method */ + mainID = (*env)->GetStaticMethodID(env, mainClass, "main", + "([Ljava/lang/String;)V"); + if (mainID == NULL) { + if ((*env)->ExceptionOccurred(env)) { + ReportExceptionDescription(env); + } else { + message = "No main method found in specified class."; + messageDest = JNI_TRUE; + } + goto leave; + } + + { /* Make sure the main method is public */ + jint mods; + jmethodID mid; + jobject obj = (*env)->ToReflectedMethod(env, mainClass, + mainID, JNI_TRUE); + + if( obj == NULL) { /* exception occurred */ + ReportExceptionDescription(env); + goto leave; + } + + mid = + (*env)->GetMethodID(env, + (*env)->GetObjectClass(env, obj), + "getModifiers", "()I"); + if ((*env)->ExceptionOccurred(env)) { + ReportExceptionDescription(env); + goto leave; + } + + mods = (*env)->CallIntMethod(env, obj, mid); + if ((mods & 1) == 0) { /* if (!Modifier.isPublic(mods)) ... */ + message = "Main method not public."; + messageDest = JNI_TRUE; + goto leave; + } + } + + /* Build argument array */ + mainArgs = NewPlatformStringArray(env, argv, argc); + if (mainArgs == NULL) { + ReportExceptionDescription(env); + goto leave; + } + + /* Invoke main method. */ + (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs); + + /* + * The launcher's exit code (in the absence of calls to + * System.exit) will be non-zero if main threw an exception. + */ + ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1; + + /* + * Detach the main thread so that it appears to have ended when + * the application's main method exits. This will invoke the + * uncaught exception handler machinery if main threw an + * exception. An uncaught exception handler cannot change the + * launcher's return code except by calling System.exit. + */ + if ((*vm)->DetachCurrentThread(vm) != 0) { + message = "Could not detach main thread."; + messageDest = JNI_TRUE; + ret = 1; + goto leave; + } + + message = NULL; + + leave: + /* + * Wait for all non-daemon threads to end, then destroy the VM. + * This will actually create a trivial new Java waiter thread + * named "DestroyJavaVM", but this will be seen as a different + * thread from the one that executed main, even though they are + * the same C thread. This allows mainThread.join() and + * mainThread.isAlive() to work as expected. + */ + (*vm)->DestroyJavaVM(vm); + + if(message != NULL && !noExitErrorMessage) + ReportErrorMessage(message, messageDest); + return ret; +} + + +#ifndef GAMMA +/* + * Checks the command line options to find which JVM type was + * specified. If no command line option was given for the JVM type, + * the default type is used. The environment variable + * JDK_ALTERNATE_VM and the command line option -XXaltjvm= are also + * checked as ways of specifying which JVM type to invoke. + */ +char * +CheckJvmType(int *pargc, char ***argv, jboolean speculative) { + int i, argi; + int argc; + char **newArgv; + int newArgvIdx = 0; + int isVMType; + int jvmidx = -1; + char *jvmtype = getenv("JDK_ALTERNATE_VM"); + + argc = *pargc; + + /* To make things simpler we always copy the argv array */ + newArgv = JLI_MemAlloc((argc + 1) * sizeof(char *)); + + /* The program name is always present */ + newArgv[newArgvIdx++] = (*argv)[0]; + + for (argi = 1; argi < argc; argi++) { + char *arg = (*argv)[argi]; + isVMType = 0; + +#ifdef JAVA_ARGS + if (arg[0] != '-') { + newArgv[newArgvIdx++] = arg; + continue; + } +#else + if (strcmp(arg, "-classpath") == 0 || + strcmp(arg, "-cp") == 0) { + newArgv[newArgvIdx++] = arg; + argi++; + if (argi < argc) { + newArgv[newArgvIdx++] = (*argv)[argi]; + } + continue; + } + if (arg[0] != '-') break; +#endif + + /* Did the user pass an explicit VM type? */ + i = KnownVMIndex(arg); + if (i >= 0) { + jvmtype = knownVMs[jvmidx = i].name + 1; /* skip the - */ + isVMType = 1; + *pargc = *pargc - 1; + } + + /* Did the user specify an "alternate" VM? */ + else if (strncmp(arg, "-XXaltjvm=", 10) == 0 || strncmp(arg, "-J-XXaltjvm=", 12) == 0) { + isVMType = 1; + jvmtype = arg+((arg[1]=='X')? 10 : 12); + jvmidx = -1; + } + + if (!isVMType) { + newArgv[newArgvIdx++] = arg; + } + } + + /* + * Finish copying the arguments if we aborted the above loop. + * NOTE that if we aborted via "break" then we did NOT copy the + * last argument above, and in addition argi will be less than + * argc. + */ + while (argi < argc) { + newArgv[newArgvIdx++] = (*argv)[argi]; + argi++; + } + + /* argv is null-terminated */ + newArgv[newArgvIdx] = 0; + + /* Copy back argv */ + *argv = newArgv; + *pargc = newArgvIdx; + + /* use the default VM type if not specified (no alias processing) */ + if (jvmtype == NULL) { + char* result = knownVMs[0].name+1; + /* Use a different VM type if we are on a server class machine? */ + if ((knownVMs[0].flag == VM_IF_SERVER_CLASS) && + (ServerClassMachine() == JNI_TRUE)) { + result = knownVMs[0].server_class+1; + } + if (_launcher_debug) { + printf("Default VM: %s\n", result); + } + return result; + } + + /* if using an alternate VM, no alias processing */ + if (jvmidx < 0) + return jvmtype; + + /* Resolve aliases first */ + { + int loopCount = 0; + while (knownVMs[jvmidx].flag == VM_ALIASED_TO) { + int nextIdx = KnownVMIndex(knownVMs[jvmidx].alias); + + if (loopCount > knownVMsCount) { + if (!speculative) { + ReportErrorMessage("Error: Corrupt jvm.cfg file; cycle in alias list.", + JNI_TRUE); + exit(1); + } else { + return "ERROR"; + /* break; */ + } + } + + if (nextIdx < 0) { + if (!speculative) { + ReportErrorMessage2("Error: Unable to resolve VM alias %s", + knownVMs[jvmidx].alias, JNI_TRUE); + exit(1); + } else { + return "ERROR"; + } + } + jvmidx = nextIdx; + jvmtype = knownVMs[jvmidx].name+1; + loopCount++; + } + } + + switch (knownVMs[jvmidx].flag) { + case VM_WARN: + if (!speculative) { + fprintf(stderr, "Warning: %s VM not supported; %s VM will be used\n", + jvmtype, knownVMs[0].name + 1); + } + /* fall through */ + case VM_IGNORE: + jvmtype = knownVMs[jvmidx=0].name + 1; + /* fall through */ + case VM_KNOWN: + break; + case VM_ERROR: + if (!speculative) { + ReportErrorMessage2("Error: %s VM not supported", jvmtype, JNI_TRUE); + exit(1); + } else { + return "ERROR"; + } + } + + return jvmtype; +} +#endif /* ifndef GAMMA */ + +# define KB (1024UL) +# define MB (1024UL * KB) +# define GB (1024UL * MB) + +/* copied from HotSpot function "atomll()" */ +static int +parse_stack_size(const char *s, jlong *result) { + jlong n = 0; + int args_read = sscanf(s, jlong_format_specifier(), &n); + if (args_read != 1) { + return 0; + } + while (*s != '\0' && *s >= '0' && *s <= '9') { + s++; + } + // 4705540: illegal if more characters are found after the first non-digit + if (strlen(s) > 1) { + return 0; + } + switch (*s) { + case 'T': case 't': + *result = n * GB * KB; + return 1; + case 'G': case 'g': + *result = n * GB; + return 1; + case 'M': case 'm': + *result = n * MB; + return 1; + case 'K': case 'k': + *result = n * KB; + return 1; + case '\0': + *result = n; + return 1; + default: + /* Create JVM with default stack and let VM handle malformed -Xss string*/ + return 0; + } +} + +/* + * Adds a new VM option with the given given name and value. + */ +void +AddOption(char *str, void *info) +{ + /* + * Expand options array if needed to accommodate at least one more + * VM option. + */ + if (numOptions >= maxOptions) { + if (options == 0) { + maxOptions = 4; + options = JLI_MemAlloc(maxOptions * sizeof(JavaVMOption)); + } else { + JavaVMOption *tmp; + maxOptions *= 2; + tmp = JLI_MemAlloc(maxOptions * sizeof(JavaVMOption)); + memcpy(tmp, options, numOptions * sizeof(JavaVMOption)); + JLI_MemFree(options); + options = tmp; + } + } + options[numOptions].optionString = str; + options[numOptions++].extraInfo = info; + + if (strncmp(str, "-Xss", 4) == 0) { + jlong tmp; + if (parse_stack_size(str + 4, &tmp)) { + threadStackSize = tmp; + } + } +} + +static void +SetClassPath(const char *s) +{ + char *def; +#ifndef GAMMA + s = JLI_WildcardExpandClasspath(s); +#endif + def = JLI_MemAlloc(strlen(s) + 40); + sprintf(def, "-Djava.class.path=%s", s); + AddOption(def, NULL); +} + +#ifndef GAMMA +/* + * The SelectVersion() routine ensures that an appropriate version of + * the JRE is running. The specification for the appropriate version + * is obtained from either the manifest of a jar file (preferred) or + * from command line options. + * The routine also parses splash screen command line options and + * passes on their values in private environment variables. + */ +static void +SelectVersion(int argc, char **argv, char **main_class) +{ + char *arg; + char **new_argv; + char **new_argp; + char *operand; + char *version = NULL; + char *jre = NULL; + int jarflag = 0; + int headlessflag = 0; + int restrict_search = -1; /* -1 implies not known */ + manifest_info info; + char env_entry[MAXNAMELEN + 24] = ENV_ENTRY "="; + char *splash_file_name = NULL; + char *splash_jar_name = NULL; + char *env_in; + int res; + + /* + * If the version has already been selected, set *main_class + * with the value passed through the environment (if any) and + * simply return. + */ + if ((env_in = getenv(ENV_ENTRY)) != NULL) { + if (*env_in != '\0') + *main_class = JLI_StringDup(env_in); + return; + } + + /* + * Scan through the arguments for options relevant to multiple JRE + * support. For reference, the command line syntax is defined as: + * + * SYNOPSIS + * java [options] class [argument...] + * + * java [options] -jar file.jar [argument...] + * + * As the scan is performed, make a copy of the argument list with + * the version specification options (new to 1.5) removed, so that + * a version less than 1.5 can be exec'd. + * + * Note that due to the syntax of the native Windows interface + * CreateProcess(), processing similar to the following exists in + * the Windows platform specific routine ExecJRE (in java_md.c). + * Changes here should be reproduced there. + */ + new_argv = JLI_MemAlloc((argc + 1) * sizeof(char*)); + new_argv[0] = argv[0]; + new_argp = &new_argv[1]; + argc--; + argv++; + while ((arg = *argv) != 0 && *arg == '-') { + if (strncmp(arg, "-version:", 9) == 0) { + version = arg + 9; + } else if (strcmp(arg, "-jre-restrict-search") == 0) { + restrict_search = 1; + } else if (strcmp(arg, "-no-jre-restrict-search") == 0) { + restrict_search = 0; + } else { + if (strcmp(arg, "-jar") == 0) + jarflag = 1; + /* deal with "unfortunate" classpath syntax */ + if ((strcmp(arg, "-classpath") == 0 || strcmp(arg, "-cp") == 0) && + (argc >= 2)) { + *new_argp++ = arg; + argc--; + argv++; + arg = *argv; + } + + /* + * Checking for headless toolkit option in the some way as AWT does: + * "true" means true and any other value means false + */ + if (strcmp(arg, "-Djava.awt.headless=true") == 0) { + headlessflag = 1; + } else if (strncmp(arg, "-Djava.awt.headless=", 20) == 0) { + headlessflag = 0; + } else if (strncmp(arg, "-splash:", 8) == 0) { + splash_file_name = arg+8; + } + *new_argp++ = arg; + } + argc--; + argv++; + } + if (argc <= 0) { /* No operand? Possibly legit with -[full]version */ + operand = NULL; + } else { + argc--; + *new_argp++ = operand = *argv++; + } + while (argc-- > 0) /* Copy over [argument...] */ + *new_argp++ = *argv++; + *new_argp = NULL; + + /* + * If there is a jar file, read the manifest. If the jarfile can't be + * read, the manifest can't be read from the jar file, or the manifest + * is corrupt, issue the appropriate error messages and exit. + * + * Even if there isn't a jar file, construct a manifest_info structure + * containing the command line information. It's a convenient way to carry + * this data around. + */ + if (jarflag && operand) { + if ((res = JLI_ParseManifest(operand, &info)) != 0) { + if (res == -1) + ReportErrorMessage2("Unable to access jarfile %s", + operand, JNI_TRUE); + else + ReportErrorMessage2("Invalid or corrupt jarfile %s", + operand, JNI_TRUE); + exit(1); + } + + /* + * Command line splash screen option should have precedence + * over the manifest, so the manifest data is used only if + * splash_file_name has not been initialized above during command + * line parsing + */ + if (!headlessflag && !splash_file_name && info.splashscreen_image_file_name) { + splash_file_name = info.splashscreen_image_file_name; + splash_jar_name = operand; + } + } else { + info.manifest_version = NULL; + info.main_class = NULL; + info.jre_version = NULL; + info.jre_restrict_search = 0; + } + + /* + * Passing on splash screen info in environment variables + */ + if (splash_file_name && !headlessflag) { + char* splash_file_entry = JLI_MemAlloc(strlen(SPLASH_FILE_ENV_ENTRY "=")+strlen(splash_file_name)+1); + strcpy(splash_file_entry, SPLASH_FILE_ENV_ENTRY "="); + strcat(splash_file_entry, splash_file_name); + putenv(splash_file_entry); + } + if (splash_jar_name && !headlessflag) { + char* splash_jar_entry = JLI_MemAlloc(strlen(SPLASH_JAR_ENV_ENTRY "=")+strlen(splash_jar_name)+1); + strcpy(splash_jar_entry, SPLASH_JAR_ENV_ENTRY "="); + strcat(splash_jar_entry, splash_jar_name); + putenv(splash_jar_entry); + } + + /* + * The JRE-Version and JRE-Restrict-Search values (if any) from the + * manifest are overwritten by any specified on the command line. + */ + if (version != NULL) + info.jre_version = version; + if (restrict_search != -1) + info.jre_restrict_search = restrict_search; + + /* + * "Valid" returns (other than unrecoverable errors) follow. Set + * main_class as a side-effect of this routine. + */ + if (info.main_class != NULL) + *main_class = JLI_StringDup(info.main_class); + + /* + * If no version selection information is found either on the command + * line or in the manifest, simply return. + */ + if (info.jre_version == NULL) { + JLI_FreeManifest(); + JLI_MemFree(new_argv); + return; + } + + /* + * Check for correct syntax of the version specification (JSR 56). + */ + if (!JLI_ValidVersionString(info.jre_version)) { + ReportErrorMessage2("Syntax error in version specification \"%s\"", + info.jre_version, JNI_TRUE); + exit(1); + } + + /* + * Find the appropriate JVM on the system. Just to be as forgiving as + * possible, if the standard algorithms don't locate an appropriate + * jre, check to see if the one running will satisfy the requirements. + * This can happen on systems which haven't been set-up for multiple + * JRE support. + */ + jre = LocateJRE(&info); + if (_launcher_debug) + printf("JRE-Version = %s, JRE-Restrict-Search = %s Selected = %s\n", + (info.jre_version?info.jre_version:"null"), + (info.jre_restrict_search?"true":"false"), (jre?jre:"null")); + if (jre == NULL) { + if (JLI_AcceptableRelease(FULL_VERSION, info.jre_version)) { + JLI_FreeManifest(); + JLI_MemFree(new_argv); + return; + } else { + ReportErrorMessage2( + "Unable to locate JRE meeting specification \"%s\"", + info.jre_version, JNI_TRUE); + exit(1); + } + } + + /* + * If I'm not the chosen one, exec the chosen one. Returning from + * ExecJRE indicates that I am indeed the chosen one. + * + * The private environment variable _JAVA_VERSION_SET is used to + * prevent the chosen one from re-reading the manifest file and + * using the values found within to override the (potential) command + * line flags stripped from argv (because the target may not + * understand them). Passing the MainClass value is an optimization + * to avoid locating, expanding and parsing the manifest extra + * times. + */ + if (info.main_class != NULL) { + if (strlen(info.main_class) <= MAXNAMELEN) { + (void)strcat(env_entry, info.main_class); + } else { + ReportErrorMessage("Error: main-class: attribute exceeds system limits\n", JNI_TRUE); + exit(1); + } + } + (void)putenv(env_entry); + ExecJRE(jre, new_argv); + JLI_FreeManifest(); + JLI_MemFree(new_argv); + return; +} +#endif /* ifndef GAMMA */ + +/* + * Parses command line arguments. Returns JNI_FALSE if launcher + * should exit without starting vm (e.g. certain version and usage + * options); returns JNI_TRUE if vm needs to be started to process + * given options. *pret (the launcher process return value) is set to + * 0 for a normal exit. + */ +static jboolean +ParseArguments(int *pargc, char ***pargv, char **pjarfile, + char **pclassname, int *pret, const char *jvmpath) +{ + int argc = *pargc; + char **argv = *pargv; + jboolean jarflag = JNI_FALSE; + char *arg; + + *pret = 1; + while ((arg = *argv) != 0 && *arg == '-') { + argv++; --argc; + if (strcmp(arg, "-classpath") == 0 || strcmp(arg, "-cp") == 0) { + if (argc < 1) { + ReportErrorMessage2("%s requires class path specification", + arg, JNI_TRUE); + PrintUsage(); + return JNI_FALSE; + } + SetClassPath(*argv); + argv++; --argc; + } else if (strcmp(arg, "-jar") == 0) { + jarflag = JNI_TRUE; + } else if (strcmp(arg, "-help") == 0 || + strcmp(arg, "-h") == 0 || + strcmp(arg, "-?") == 0) { + PrintUsage(); + *pret = 0; + return JNI_FALSE; + } else if (strcmp(arg, "-version") == 0) { + printVersion = JNI_TRUE; + return JNI_TRUE; + } else if (strcmp(arg, "-showversion") == 0) { + showVersion = JNI_TRUE; + } else if (strcmp(arg, "-X") == 0) { + *pret = PrintXUsage(jvmpath); + return JNI_FALSE; +/* + * The following case provide backward compatibility with old-style + * command line options. + */ + } else if (strcmp(arg, "-fullversion") == 0) { + fprintf(stderr, "%s full version \"%s\"\n", progname, + FULL_VERSION); + *pret = 0; + return JNI_FALSE; + } else if (strcmp(arg, "-verbosegc") == 0) { + AddOption("-verbose:gc", NULL); + } else if (strcmp(arg, "-t") == 0) { + AddOption("-Xt", NULL); + } else if (strcmp(arg, "-tm") == 0) { + AddOption("-Xtm", NULL); + } else if (strcmp(arg, "-debug") == 0) { + AddOption("-Xdebug", NULL); + } else if (strcmp(arg, "-noclassgc") == 0) { + AddOption("-Xnoclassgc", NULL); + } else if (strcmp(arg, "-Xfuture") == 0) { + AddOption("-Xverify:all", NULL); + } else if (strcmp(arg, "-verify") == 0) { + AddOption("-Xverify:all", NULL); + } else if (strcmp(arg, "-verifyremote") == 0) { + AddOption("-Xverify:remote", NULL); + } else if (strcmp(arg, "-noverify") == 0) { + AddOption("-Xverify:none", NULL); + } else if (strcmp(arg, "-XXsuppressExitMessage") == 0) { + noExitErrorMessage = 1; + } else if (strncmp(arg, "-prof", 5) == 0) { + char *p = arg + 5; + char *tmp = JLI_MemAlloc(strlen(arg) + 50); + if (*p) { + sprintf(tmp, "-Xrunhprof:cpu=old,file=%s", p + 1); + } else { + sprintf(tmp, "-Xrunhprof:cpu=old,file=java.prof"); + } + AddOption(tmp, NULL); + } else if (strncmp(arg, "-ss", 3) == 0 || + strncmp(arg, "-oss", 4) == 0 || + strncmp(arg, "-ms", 3) == 0 || + strncmp(arg, "-mx", 3) == 0) { + char *tmp = JLI_MemAlloc(strlen(arg) + 6); + sprintf(tmp, "-X%s", arg + 1); /* skip '-' */ + AddOption(tmp, NULL); + } else if (strcmp(arg, "-checksource") == 0 || + strcmp(arg, "-cs") == 0 || + strcmp(arg, "-noasyncgc") == 0) { + /* No longer supported */ + fprintf(stderr, + "Warning: %s option is no longer supported.\n", + arg); + } else if (strncmp(arg, "-version:", 9) == 0 || + strcmp(arg, "-no-jre-restrict-search") == 0 || + strcmp(arg, "-jre-restrict-search") == 0 || + strncmp(arg, "-splash:", 8) == 0) { + ; /* Ignore machine independent options already handled */ + } else if (RemovableMachineDependentOption(arg) ) { + ; /* Do not pass option to vm. */ + } + else { + AddOption(arg, NULL); + } + } + + if (--argc >= 0) { + if (jarflag) { + *pjarfile = *argv++; + *pclassname = 0; + } else { + *pjarfile = 0; + *pclassname = *argv++; + } + *pargc = argc; + *pargv = argv; + } + + return JNI_TRUE; +} + +/* + * Initializes the Java Virtual Machine. Also frees options array when + * finished. + */ +static jboolean +InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn) +{ + JavaVMInitArgs args; + jint r; + + memset(&args, 0, sizeof(args)); + args.version = JNI_VERSION_1_2; + args.nOptions = numOptions; + args.options = options; + args.ignoreUnrecognized = JNI_FALSE; + + if (_launcher_debug) { + int i = 0; + printf("JavaVM args:\n "); + printf("version 0x%08lx, ", (long)args.version); + printf("ignoreUnrecognized is %s, ", + args.ignoreUnrecognized ? "JNI_TRUE" : "JNI_FALSE"); + printf("nOptions is %ld\n", (long)args.nOptions); + for (i = 0; i < numOptions; i++) + printf(" option[%2d] = '%s'\n", + i, args.options[i].optionString); + } + + r = ifn->CreateJavaVM(pvm, (void **)penv, &args); + JLI_MemFree(options); + return r == JNI_OK; +} + + +#define NULL_CHECK0(e) if ((e) == 0) return 0 +#define NULL_CHECK(e) if ((e) == 0) return + +static jstring platformEncoding = NULL; +static jstring getPlatformEncoding(JNIEnv *env) { + if (platformEncoding == NULL) { + jstring propname = (*env)->NewStringUTF(env, "sun.jnu.encoding"); + if (propname) { + jclass cls; + jmethodID mid; + NULL_CHECK0 (cls = (*env)->FindClass(env, "java/lang/System")); + NULL_CHECK0 (mid = (*env)->GetStaticMethodID( + env, cls, + "getProperty", + "(Ljava/lang/String;)Ljava/lang/String;")); + platformEncoding = (*env)->CallStaticObjectMethod ( + env, cls, mid, propname); + } + } + return platformEncoding; +} + +static jboolean isEncodingSupported(JNIEnv *env, jstring enc) { + jclass cls; + jmethodID mid; + NULL_CHECK0 (cls = (*env)->FindClass(env, "java/nio/charset/Charset")); + NULL_CHECK0 (mid = (*env)->GetStaticMethodID( + env, cls, + "isSupported", + "(Ljava/lang/String;)Z")); + return (*env)->CallStaticBooleanMethod(env, cls, mid, enc); +} + +/* + * Returns a new Java string object for the specified platform string. + */ +static jstring +NewPlatformString(JNIEnv *env, char *s) +{ + int len = (int)strlen(s); + jclass cls; + jmethodID mid; + jbyteArray ary; + jstring enc; + + if (s == NULL) + return 0; + enc = getPlatformEncoding(env); + + ary = (*env)->NewByteArray(env, len); + if (ary != 0) { + jstring str = 0; + (*env)->SetByteArrayRegion(env, ary, 0, len, (jbyte *)s); + if (!(*env)->ExceptionOccurred(env)) { +#ifdef GAMMA + /* We support running JVM with older JDK, so here we have to deal */ + /* with the case that sun.jnu.encoding is undefined (enc == NULL) */ + if (enc != NULL && isEncodingSupported(env, enc) == JNI_TRUE) { +#else + if (isEncodingSupported(env, enc) == JNI_TRUE) { +#endif + NULL_CHECK0(cls = (*env)->FindClass(env, "java/lang/String")); + NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "", + "([BLjava/lang/String;)V")); + str = (*env)->NewObject(env, cls, mid, ary, enc); + } else { + /*If the encoding specified in sun.jnu.encoding is not + endorsed by "Charset.isSupported" we have to fall back + to use String(byte[]) explicitly here without specifying + the encoding name, in which the StringCoding class will + pickup the iso-8859-1 as the fallback converter for us. + */ + NULL_CHECK0(cls = (*env)->FindClass(env, "java/lang/String")); + NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "", + "([B)V")); + str = (*env)->NewObject(env, cls, mid, ary); + } + (*env)->DeleteLocalRef(env, ary); + return str; + } + } + return 0; +} + +/* + * Returns a new array of Java string objects for the specified + * array of platform strings. + */ +static jobjectArray +NewPlatformStringArray(JNIEnv *env, char **strv, int strc) +{ + jarray cls; + jarray ary; + int i; + + NULL_CHECK0(cls = (*env)->FindClass(env, "java/lang/String")); + NULL_CHECK0(ary = (*env)->NewObjectArray(env, strc, cls, 0)); + for (i = 0; i < strc; i++) { + jstring str = NewPlatformString(env, *strv++); + NULL_CHECK0(str); + (*env)->SetObjectArrayElement(env, ary, i, str); + (*env)->DeleteLocalRef(env, str); + } + return ary; +} + +/* + * Loads a class, convert the '.' to '/'. + */ +static jclass +LoadClass(JNIEnv *env, char *name) +{ + char *buf = JLI_MemAlloc(strlen(name) + 1); + char *s = buf, *t = name, c; + jclass cls; + jlong start, end; + + if (_launcher_debug) + start = CounterGet(); + + do { + c = *t++; + *s++ = (c == '.') ? '/' : c; + } while (c != '\0'); + cls = (*env)->FindClass(env, buf); + JLI_MemFree(buf); + + if (_launcher_debug) { + end = CounterGet(); + printf("%ld micro seconds to load main class\n", + (long)(jint)Counter2Micros(end-start)); + printf("----_JAVA_LAUNCHER_DEBUG----\n"); + } + + return cls; +} + + +/* + * Returns the main class name for the specified jar file. + */ +static jstring +GetMainClassName(JNIEnv *env, char *jarname) +{ +#define MAIN_CLASS "Main-Class" + jclass cls; + jmethodID mid; + jobject jar, man, attr; + jstring str, result = 0; + + NULL_CHECK0(cls = (*env)->FindClass(env, "java/util/jar/JarFile")); + NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "", + "(Ljava/lang/String;)V")); + NULL_CHECK0(str = NewPlatformString(env, jarname)); + NULL_CHECK0(jar = (*env)->NewObject(env, cls, mid, str)); + NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "getManifest", + "()Ljava/util/jar/Manifest;")); + man = (*env)->CallObjectMethod(env, jar, mid); + if (man != 0) { + NULL_CHECK0(mid = (*env)->GetMethodID(env, + (*env)->GetObjectClass(env, man), + "getMainAttributes", + "()Ljava/util/jar/Attributes;")); + attr = (*env)->CallObjectMethod(env, man, mid); + if (attr != 0) { + NULL_CHECK0(mid = (*env)->GetMethodID(env, + (*env)->GetObjectClass(env, attr), + "getValue", + "(Ljava/lang/String;)Ljava/lang/String;")); + NULL_CHECK0(str = NewPlatformString(env, MAIN_CLASS)); + result = (*env)->CallObjectMethod(env, attr, mid, str); + } + } + return result; +} + +#ifdef JAVA_ARGS +static char *java_args[] = JAVA_ARGS; +static char *app_classpath[] = APP_CLASSPATH; + +/* + * For tools, convert command line args thus: + * javac -cp foo:foo/"*" -J-ms32m ... + * java -ms32m -cp JLI_WildcardExpandClasspath(foo:foo/"*") ... + */ +static void +TranslateApplicationArgs(int *pargc, char ***pargv) +{ + const int NUM_ARGS = (sizeof(java_args) / sizeof(char *)); + int argc = *pargc; + char **argv = *pargv; + int nargc = argc + NUM_ARGS; + char **nargv = JLI_MemAlloc((nargc + 1) * sizeof(char *)); + int i; + + *pargc = nargc; + *pargv = nargv; + + /* Copy the VM arguments (i.e. prefixed with -J) */ + for (i = 0; i < NUM_ARGS; i++) { + char *arg = java_args[i]; + if (arg[0] == '-' && arg[1] == 'J') { + *nargv++ = arg + 2; + } + } + + for (i = 0; i < argc; i++) { + char *arg = argv[i]; + if (arg[0] == '-' && arg[1] == 'J') { + if (arg[2] == '\0') { + ReportErrorMessage("Error: the -J option should not be " + "followed by a space.", JNI_TRUE); + exit(1); + } + *nargv++ = arg + 2; + } + } + + /* Copy the rest of the arguments */ + for (i = 0; i < NUM_ARGS; i++) { + char *arg = java_args[i]; + if (arg[0] != '-' || arg[1] != 'J') { + *nargv++ = arg; + } + } + for (i = 0; i < argc; i++) { + char *arg = argv[i]; + if (arg[0] == '-') { + if (arg[1] == 'J') + continue; +#ifdef EXPAND_CLASSPATH_WILDCARDS + if (arg[1] == 'c' + && (strcmp(arg, "-cp") == 0 || + strcmp(arg, "-classpath") == 0) + && i < argc - 1) { + *nargv++ = arg; + *nargv++ = (char *) JLI_WildcardExpandClasspath(argv[i+1]); + i++; + continue; + } +#endif + } + *nargv++ = arg; + } + *nargv = 0; +} + +/* + * For our tools, we try to add 3 VM options: + * -Denv.class.path= + * -Dapplication.home= + * -Djava.class.path= + * is the user's setting of CLASSPATH -- for instance the user + * tells javac where to find binary classes through this environment + * variable. Notice that users will be able to compile against our + * tools classes (sun.tools.javac.Main) only if they explicitly add + * tools.jar to CLASSPATH. + * is the directory where the application is installed. + * is the classpath to where our apps' classfiles are. + */ +static jboolean +AddApplicationOptions() +{ + const int NUM_APP_CLASSPATH = (sizeof(app_classpath) / sizeof(char *)); + char *envcp, *appcp, *apphome; + char home[MAXPATHLEN]; /* application home */ + char separator[] = { PATH_SEPARATOR, '\0' }; + int size, i; + int strlenHome; + + { + const char *s = getenv("CLASSPATH"); + if (s) { + s = (char *) JLI_WildcardExpandClasspath(s); + /* 40 for -Denv.class.path= */ + envcp = (char *)JLI_MemAlloc(strlen(s) + 40); + sprintf(envcp, "-Denv.class.path=%s", s); + AddOption(envcp, NULL); + } + } + + if (!GetApplicationHome(home, sizeof(home))) { + ReportErrorMessage("Can't determine application home", JNI_TRUE); + return JNI_FALSE; + } + + /* 40 for '-Dapplication.home=' */ + apphome = (char *)JLI_MemAlloc(strlen(home) + 40); + sprintf(apphome, "-Dapplication.home=%s", home); + AddOption(apphome, NULL); + + /* How big is the application's classpath? */ + size = 40; /* 40: "-Djava.class.path=" */ + strlenHome = (int)strlen(home); + for (i = 0; i < NUM_APP_CLASSPATH; i++) { + size += strlenHome + (int)strlen(app_classpath[i]) + 1; /* 1: separator */ + } + appcp = (char *)JLI_MemAlloc(size + 1); + strcpy(appcp, "-Djava.class.path="); + for (i = 0; i < NUM_APP_CLASSPATH; i++) { + strcat(appcp, home); /* c:\program files\myapp */ + strcat(appcp, app_classpath[i]); /* \lib\myapp.jar */ + strcat(appcp, separator); /* ; */ + } + appcp[strlen(appcp)-1] = '\0'; /* remove trailing path separator */ + AddOption(appcp, NULL); + return JNI_TRUE; +} +#endif /* JAVA_ARGS */ + +/* + * inject the -Dsun.java.command pseudo property into the args structure + * this pseudo property is used in the HotSpot VM to expose the + * Java class name and arguments to the main method to the VM. The + * HotSpot VM uses this pseudo property to store the Java class name + * (or jar file name) and the arguments to the class's main method + * to the instrumentation memory region. The sun.java.command pseudo + * property is not exported by HotSpot to the Java layer. + */ +void +SetJavaCommandLineProp(char *classname, char *jarfile, + int argc, char **argv) +{ + + int i = 0; + size_t len = 0; + char* javaCommand = NULL; + char* dashDstr = "-Dsun.java.command="; + + if (classname == NULL && jarfile == NULL) { + /* unexpected, one of these should be set. just return without + * setting the property + */ + return; + } + + /* if the class name is not set, then use the jarfile name */ + if (classname == NULL) { + classname = jarfile; + } + + /* determine the amount of memory to allocate assuming + * the individual components will be space separated + */ + len = strlen(classname); + for (i = 0; i < argc; i++) { + len += strlen(argv[i]) + 1; + } + + /* allocate the memory */ + javaCommand = (char*) JLI_MemAlloc(len + strlen(dashDstr) + 1); + + /* build the -D string */ + *javaCommand = '\0'; + strcat(javaCommand, dashDstr); + strcat(javaCommand, classname); + + for (i = 0; i < argc; i++) { + /* the components of the string are space separated. In + * the case of embedded white space, the relationship of + * the white space separated components to their true + * positional arguments will be ambiguous. This issue may + * be addressed in a future release. + */ + strcat(javaCommand, " "); + strcat(javaCommand, argv[i]); + } + + AddOption(javaCommand, NULL); +} + +/* + * JVM would like to know if it's created by a standard Sun launcher, or by + * user native application, the following property indicates the former. + */ +#ifdef GAMMA +void SetJavaLauncherProp() { + AddOption("-Dsun.java.launcher=" LAUNCHER_TYPE, NULL); +} +#endif + +/* + * Prints the version information from the java.version and other properties. + */ +static void +PrintJavaVersion(JNIEnv *env) +{ + jclass ver; + jmethodID print; + + NULL_CHECK(ver = (*env)->FindClass(env, "sun/misc/Version")); + NULL_CHECK(print = (*env)->GetStaticMethodID(env, ver, "print", "()V")); + + (*env)->CallStaticVoidMethod(env, ver, print); +} + +/* + * Prints default usage message. + */ +static void +PrintUsage(void) +{ +#ifndef GAMMA + int i; +#endif + + fprintf(stdout, + "Usage: %s [-options] class [args...]\n" + " (to execute a class)\n" + " or %s [-options] -jar jarfile [args...]\n" + " (to execute a jar file)\n" + "\n" + "where options include:\n", + progname, + progname); + +#ifndef GAMMA + PrintMachineDependentOptions(); + + if ((knownVMs[0].flag == VM_KNOWN) || + (knownVMs[0].flag == VM_IF_SERVER_CLASS)) { + fprintf(stdout, " %s\t to select the \"%s\" VM\n", + knownVMs[0].name, knownVMs[0].name+1); + } + for (i=1; i\n" +" -classpath \n" +" A %c separated list of directories, JAR archives,\n" +" and ZIP archives to search for class files.\n" +" -D=\n" +" set a system property\n" +" -verbose[:class|gc|jni]\n" +" enable verbose output\n" +" -version print product version and exit\n" +" -version:\n" +" require the specified version to run\n" +" -showversion print product version and continue\n" +" -jre-restrict-search | -jre-no-restrict-search\n" +" include/exclude user private JREs in the version search\n" +" -? -help print this help message\n" +" -X print help on non-standard options\n" +" -ea[:...|:]\n" +" -enableassertions[:...|:]\n" +" enable assertions\n" +" -da[:...|:]\n" +" -disableassertions[:...|:]\n" +" disable assertions\n" +" -esa | -enablesystemassertions\n" +" enable system assertions\n" +" -dsa | -disablesystemassertions\n" +" disable system assertions\n" +" -agentlib:[=]\n" +" load native agent library , e.g. -agentlib:hprof\n" +" see also, -agentlib:jdwp=help and -agentlib:hprof=help\n" +" -agentpath:[=]\n" +" load native agent library by full pathname\n" +" -javaagent:[=]\n" +" load Java programming language agent, see java.lang.instrument\n" +" -splash:\n" +" show splash screen with specified image\n" + + ,PATH_SEPARATOR); +} + +/* + * Print usage message for -X options. + */ +static jint +PrintXUsage(const char *jvmpath) +{ + /* + A 32 bit cushion to prevent buffer overrun, noting that + fopen(3C) may fail if the buffer exceeds MAXPATHLEN. + */ + char path[MAXPATHLEN+32]; + char buf[128]; + size_t n; + FILE *fp; + static const char Xusage_txt[] = "/Xusage.txt"; + + strcpy(path, jvmpath); + /* Note the FILE_SEPARATOR is platform dependent */ + strcpy(strrchr(path, FILE_SEPARATOR), Xusage_txt); + fp = fopen(path, "r"); + if (fp == 0) { + fprintf(stderr, "Can't open %s\n", path); + return 1; + } + while ((n = fread(buf, 1, sizeof(buf), fp)) != 0) { + fwrite(buf, 1, n, stdout); + } + fclose(fp); + return 0; +} + +#ifndef GAMMA + +/* + * Read the jvm.cfg file and fill the knownJVMs[] array. + * + * The functionality of the jvm.cfg file is subject to change without + * notice and the mechanism will be removed in the future. + * + * The lexical structure of the jvm.cfg file is as follows: + * + * jvmcfg := { vmLine } + * vmLine := knownLine + * | aliasLine + * | warnLine + * | ignoreLine + * | errorLine + * | predicateLine + * | commentLine + * knownLine := flag "KNOWN" EOL + * warnLine := flag "WARN" EOL + * ignoreLine := flag "IGNORE" EOL + * errorLine := flag "ERROR" EOL + * aliasLine := flag "ALIASED_TO" flag EOL + * predicateLine := flag "IF_SERVER_CLASS" flag EOL + * commentLine := "#" text EOL + * flag := "-" identifier + * + * The semantics are that when someone specifies a flag on the command line: + * - if the flag appears on a knownLine, then the identifier is used as + * the name of the directory holding the JVM library (the name of the JVM). + * - if the flag appears as the first flag on an aliasLine, the identifier + * of the second flag is used as the name of the JVM. + * - if the flag appears on a warnLine, the identifier is used as the + * name of the JVM, but a warning is generated. + * - if the flag appears on an ignoreLine, the identifier is recognized as the + * name of a JVM, but the identifier is ignored and the default vm used + * - if the flag appears on an errorLine, an error is generated. + * - if the flag appears as the first flag on a predicateLine, and + * the machine on which you are running passes the predicate indicated, + * then the identifier of the second flag is used as the name of the JVM, + * otherwise the identifier of the first flag is used as the name of the JVM. + * If no flag is given on the command line, the first vmLine of the jvm.cfg + * file determines the name of the JVM. + * PredicateLines are only interpreted on first vmLine of a jvm.cfg file, + * since they only make sense if someone hasn't specified the name of the + * JVM on the command line. + * + * The intent of the jvm.cfg file is to allow several JVM libraries to + * be installed in different subdirectories of a single JRE installation, + * for space-savings and convenience in testing. + * The intent is explicitly not to provide a full aliasing or predicate + * mechanism. + */ +jint +ReadKnownVMs(const char *jrepath, char * arch, jboolean speculative) +{ + FILE *jvmCfg; + char jvmCfgName[MAXPATHLEN+20]; + char line[MAXPATHLEN+20]; + int cnt = 0; + int lineno = 0; + jlong start, end; + int vmType; + char *tmpPtr; + char *altVMName = NULL; + char *serverClassVMName = NULL; + static char *whiteSpace = " \t"; + if (_launcher_debug) { + start = CounterGet(); + } + + strcpy(jvmCfgName, jrepath); + strcat(jvmCfgName, FILESEP "lib" FILESEP); + strcat(jvmCfgName, arch); + strcat(jvmCfgName, FILESEP "jvm.cfg"); + + jvmCfg = fopen(jvmCfgName, "r"); + if (jvmCfg == NULL) { + if (!speculative) { + ReportErrorMessage2("Error: could not open `%s'", jvmCfgName, + JNI_TRUE); + exit(1); + } else { + return -1; + } + } + while (fgets(line, sizeof(line), jvmCfg) != NULL) { + vmType = VM_UNKNOWN; + lineno++; + if (line[0] == '#') + continue; + if (line[0] != '-') { + fprintf(stderr, "Warning: no leading - on line %d of `%s'\n", + lineno, jvmCfgName); + } + if (cnt >= knownVMsLimit) { + GrowKnownVMs(cnt); + } + line[strlen(line)-1] = '\0'; /* remove trailing newline */ + tmpPtr = line + strcspn(line, whiteSpace); + if (*tmpPtr == 0) { + fprintf(stderr, "Warning: missing VM type on line %d of `%s'\n", + lineno, jvmCfgName); + } else { + /* Null-terminate this string for JLI_StringDup below */ + *tmpPtr++ = 0; + tmpPtr += strspn(tmpPtr, whiteSpace); + if (*tmpPtr == 0) { + fprintf(stderr, "Warning: missing VM type on line %d of `%s'\n", + lineno, jvmCfgName); + } else { + if (!strncmp(tmpPtr, "KNOWN", strlen("KNOWN"))) { + vmType = VM_KNOWN; + } else if (!strncmp(tmpPtr, "ALIASED_TO", strlen("ALIASED_TO"))) { + tmpPtr += strcspn(tmpPtr, whiteSpace); + if (*tmpPtr != 0) { + tmpPtr += strspn(tmpPtr, whiteSpace); + } + if (*tmpPtr == 0) { + fprintf(stderr, "Warning: missing VM alias on line %d of `%s'\n", + lineno, jvmCfgName); + } else { + /* Null terminate altVMName */ + altVMName = tmpPtr; + tmpPtr += strcspn(tmpPtr, whiteSpace); + *tmpPtr = 0; + vmType = VM_ALIASED_TO; + } + } else if (!strncmp(tmpPtr, "WARN", strlen("WARN"))) { + vmType = VM_WARN; + } else if (!strncmp(tmpPtr, "IGNORE", strlen("IGNORE"))) { + vmType = VM_IGNORE; + } else if (!strncmp(tmpPtr, "ERROR", strlen("ERROR"))) { + vmType = VM_ERROR; + } else if (!strncmp(tmpPtr, + "IF_SERVER_CLASS", + strlen("IF_SERVER_CLASS"))) { + tmpPtr += strcspn(tmpPtr, whiteSpace); + if (*tmpPtr != 0) { + tmpPtr += strspn(tmpPtr, whiteSpace); + } + if (*tmpPtr == 0) { + fprintf(stderr, "Warning: missing server class VM on line %d of `%s'\n", + lineno, jvmCfgName); + } else { + /* Null terminate server class VM name */ + serverClassVMName = tmpPtr; + tmpPtr += strcspn(tmpPtr, whiteSpace); + *tmpPtr = 0; + vmType = VM_IF_SERVER_CLASS; + } + } else { + fprintf(stderr, "Warning: unknown VM type on line %d of `%s'\n", + lineno, &jvmCfgName[0]); + vmType = VM_KNOWN; + } + } + } + + if (_launcher_debug) + printf("jvm.cfg[%d] = ->%s<-\n", cnt, line); + if (vmType != VM_UNKNOWN) { + knownVMs[cnt].name = JLI_StringDup(line); + knownVMs[cnt].flag = vmType; + switch (vmType) { + default: + break; + case VM_ALIASED_TO: + knownVMs[cnt].alias = JLI_StringDup(altVMName); + if (_launcher_debug) { + printf(" name: %s vmType: %s alias: %s\n", + knownVMs[cnt].name, "VM_ALIASED_TO", knownVMs[cnt].alias); + } + break; + case VM_IF_SERVER_CLASS: + knownVMs[cnt].server_class = JLI_StringDup(serverClassVMName); + if (_launcher_debug) { + printf(" name: %s vmType: %s server_class: %s\n", + knownVMs[cnt].name, "VM_IF_SERVER_CLASS", knownVMs[cnt].server_class); + } + break; + } + cnt++; + } + } + fclose(jvmCfg); + knownVMsCount = cnt; + + if (_launcher_debug) { + end = CounterGet(); + printf("%ld micro seconds to parse jvm.cfg\n", + (long)(jint)Counter2Micros(end-start)); + } + + return cnt; +} + + +static void +GrowKnownVMs(int minimum) +{ + struct vmdesc* newKnownVMs; + int newMax; + + newMax = (knownVMsLimit == 0 ? INIT_MAX_KNOWN_VMS : (2 * knownVMsLimit)); + if (newMax <= minimum) { + newMax = minimum; + } + newKnownVMs = (struct vmdesc*) JLI_MemAlloc(newMax * sizeof(struct vmdesc)); + if (knownVMs != NULL) { + memcpy(newKnownVMs, knownVMs, knownVMsLimit * sizeof(struct vmdesc)); + } + JLI_MemFree(knownVMs); + knownVMs = newKnownVMs; + knownVMsLimit = newMax; +} + + +/* Returns index of VM or -1 if not found */ +static int +KnownVMIndex(const char* name) +{ + int i; + if (strncmp(name, "-J", 2) == 0) name += 2; + for (i = 0; i < knownVMsCount; i++) { + if (!strcmp(name, knownVMs[i].name)) { + return i; + } + } + return -1; +} + +static void +FreeKnownVMs() +{ + int i; + for (i = 0; i < knownVMsCount; i++) { + JLI_MemFree(knownVMs[i].name); + knownVMs[i].name = NULL; + } + JLI_MemFree(knownVMs); +} + + +/* + * Displays the splash screen according to the jar file name + * and image file names stored in environment variables + */ +static void +ShowSplashScreen() +{ + const char *jar_name = getenv(SPLASH_JAR_ENV_ENTRY); + const char *file_name = getenv(SPLASH_FILE_ENV_ENTRY); + int data_size; + void *image_data; + if (jar_name) { + image_data = JLI_JarUnpackFile(jar_name, file_name, &data_size); + if (image_data) { + DoSplashInit(); + DoSplashLoadMemory(image_data, data_size); + JLI_MemFree(image_data); + } + } else if (file_name) { + DoSplashInit(); + DoSplashLoadFile(file_name); + } else { + return; + } + DoSplashSetFileJarName(file_name, jar_name); +} + +#endif /* ifndef GAMMA */