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