1 /*
   2  * Copyright (c) 1995, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * Shared source for 'java' command line tool.
  28  *
  29  * If JAVA_ARGS is defined, then acts as a launcher for applications. For
  30  * instance, the JDK command line tools such as javac and javadoc (see
  31  * makefiles for more details) are built with this program.  Any arguments
  32  * prefixed with '-J' will be passed directly to the 'java' command.
  33  */
  34 
  35 /*
  36  * One job of the launcher is to remove command line options which the
  37  * vm does not understand and will not process.  These options include
  38  * options which select which style of vm is run (e.g. -client and
  39  * -server) as well as options which select the data model to use.
  40  * Additionally, for tools which invoke an underlying vm "-J-foo"
  41  * options are turned into "-foo" options to the vm.  This option
  42  * filtering is handled in a number of places in the launcher, some of
  43  * it in machine-dependent code.  In this file, the function
  44  * CheckJvmType removes vm style options and TranslateApplicationArgs
  45  * removes "-J" prefixes.  The CreateExecutionEnvironment function processes
  46  * and removes -d<n> options. On unix, there is a possibility that the running
  47  * data model may not match to the desired data model, in this case an exec is
  48  * required to start the desired model. If the data models match, then
  49  * ParseArguments will remove the -d<n> flags. If the data models do not match
  50  * the CreateExecutionEnviroment will remove the -d<n> flags.
  51  */
  52 
  53 
  54 #include "java.h"
  55 
  56 /*
  57  * A NOTE TO DEVELOPERS: For performance reasons it is important that
  58  * the program image remain relatively small until after SelectVersion
  59  * CreateExecutionEnvironment have finished their possibly recursive
  60  * processing. Watch everything, but resist all temptations to use Java
  61  * interfaces.
  62  */
  63 
  64 #define USE_STDERR JNI_TRUE     /* we usually print to stderr */
  65 #define USE_STDOUT JNI_FALSE
  66 
  67 static jboolean printVersion = JNI_FALSE; /* print and exit */
  68 static jboolean showVersion = JNI_FALSE;  /* print but continue */
  69 static jboolean printUsage = JNI_FALSE;   /* print and exit*/
  70 static jboolean printTo = USE_STDERR;     /* where to print version/usage */
  71 static jboolean printXUsage = JNI_FALSE;  /* print and exit*/
  72 static jboolean dryRun = JNI_FALSE;       /* initialize VM and exit */
  73 static char     *showSettings = NULL;     /* print but continue */
  74 static jboolean showResolvedModules = JNI_FALSE;
  75 static jboolean listModules = JNI_FALSE;
  76 static char     *describeModule = NULL;
  77 static jboolean validateModules = JNI_FALSE;
  78 
  79 static const char *_program_name;
  80 static const char *_launcher_name;
  81 static jboolean _is_java_args = JNI_FALSE;
  82 static jboolean _have_classpath = JNI_FALSE;
  83 static const char *_fVersion;
  84 static jboolean _wc_enabled = JNI_FALSE;
  85 
  86 /*
  87  * Entries for splash screen environment variables.
  88  * putenv is performed in SelectVersion. We need
  89  * them in memory until UnsetEnv, so they are made static
  90  * global instead of auto local.
  91  */
  92 static char* splash_file_entry = NULL;
  93 static char* splash_jar_entry = NULL;
  94 
  95 /*
  96  * List of VM options to be specified when the VM is created.
  97  */
  98 static JavaVMOption *options;
  99 static int numOptions, maxOptions;
 100 
 101 /*
 102  * Prototypes for functions internal to launcher.
 103  */
 104 static void SetClassPath(const char *s);
 105 static void SetMainModule(const char *s);
 106 static void SelectVersion(int argc, char **argv, char **main_class);
 107 static void SetJvmEnvironment(int argc, char **argv);
 108 static jboolean ParseArguments(int *pargc, char ***pargv,
 109                                int *pmode, char **pwhat,
 110                                int *pret, const char *jrepath);
 111 static jboolean InitializeJVM(JavaVM **pvm, JNIEnv **penv,
 112                               InvocationFunctions *ifn);
 113 static jstring NewPlatformString(JNIEnv *env, char *s);
 114 static jclass LoadMainClass(JNIEnv *env, int mode, char *name);
 115 static jclass GetApplicationClass(JNIEnv *env);
 116 
 117 static void TranslateApplicationArgs(int jargc, const char **jargv, int *pargc, char ***pargv);
 118 static jboolean AddApplicationOptions(int cpathc, const char **cpathv);
 119 static void SetApplicationClassPath(const char**);
 120 
 121 static void PrintJavaVersion(JNIEnv *env, jboolean extraLF);
 122 static void PrintUsage(JNIEnv* env, jboolean doXUsage);
 123 static void ShowSettings(JNIEnv* env, char *optString);
 124 static void ShowResolvedModules(JNIEnv* env);
 125 static void ListModules(JNIEnv* env);
 126 static void DescribeModule(JNIEnv* env, char* optString);
 127 static jboolean ValidateModules(JNIEnv* env);
 128 
 129 static void SetPaths(int argc, char **argv);
 130 
 131 static void DumpState();
 132 
 133 enum OptionKind {
 134     LAUNCHER_OPTION = 0,
 135     LAUNCHER_OPTION_WITH_ARGUMENT,
 136     LAUNCHER_MAIN_OPTION,
 137     VM_LONG_OPTION,
 138     VM_LONG_OPTION_WITH_ARGUMENT,
 139     VM_OPTION
 140 };
 141 
 142 static int GetOpt(int *pargc, char ***pargv, char **poption, char **pvalue);
 143 static jboolean IsOptionWithArgument(int argc, char **argv);
 144 
 145 /* Maximum supported entries from jvm.cfg. */
 146 #define INIT_MAX_KNOWN_VMS      10
 147 
 148 /* Values for vmdesc.flag */
 149 enum vmdesc_flag {
 150     VM_UNKNOWN = -1,
 151     VM_KNOWN,
 152     VM_ALIASED_TO,
 153     VM_WARN,
 154     VM_ERROR,
 155     VM_IF_SERVER_CLASS,
 156     VM_IGNORE
 157 };
 158 
 159 struct vmdesc {
 160     char *name;
 161     int flag;
 162     char *alias;
 163     char *server_class;
 164 };
 165 static struct vmdesc *knownVMs = NULL;
 166 static int knownVMsCount = 0;
 167 static int knownVMsLimit = 0;
 168 
 169 static void GrowKnownVMs(int minimum);
 170 static int  KnownVMIndex(const char* name);
 171 static void FreeKnownVMs();
 172 static jboolean IsWildCardEnabled();
 173 
 174 /*
 175  * This reports error.  VM will not be created and no usage is printed.
 176  */
 177 #define REPORT_ERROR(AC_ok, AC_failure_message, AC_questionable_arg) \
 178     do { \
 179         if (!AC_ok) { \
 180             JLI_ReportErrorMessage(AC_failure_message, AC_questionable_arg); \
 181             printUsage = JNI_FALSE; \
 182             *pret = 1; \
 183             return JNI_FALSE; \
 184         } \
 185     } while (JNI_FALSE)
 186 
 187 #define ARG_CHECK(AC_arg_count, AC_failure_message, AC_questionable_arg) \
 188     do { \
 189         if (AC_arg_count < 1) { \
 190             JLI_ReportErrorMessage(AC_failure_message, AC_questionable_arg); \
 191             printUsage = JNI_TRUE; \
 192             *pret = 1; \
 193             return JNI_TRUE; \
 194         } \
 195     } while (JNI_FALSE)
 196 
 197 /*
 198  * Running Java code in primordial thread caused many problems. We will
 199  * create a new thread to invoke JVM. See 6316197 for more information.
 200  */
 201 static jlong threadStackSize    = 0;  /* stack size of the new thread */
 202 static jlong maxHeapSize        = 0;  /* max heap size */
 203 static jlong initialHeapSize    = 0;  /* inital heap size */
 204 
 205 /*
 206  * A minimum -Xss stack size suitable for all platforms.
 207  */
 208 #ifndef STACK_SIZE_MINIMUM
 209 #define STACK_SIZE_MINIMUM (32 * KB)
 210 #endif
 211 
 212 /*
 213  * Entry point.
 214  */
 215 int
 216 JLI_Launch(int argc, char ** argv,              /* main argc, argc */
 217         int jargc, const char** jargv,          /* java args */
 218         int appclassc, const char** appclassv,  /* app classpath */
 219         const char* fullversion,                /* full version defined */
 220         const char* dotversion,                 /* UNUSED dot version defined */
 221         const char* pname,                      /* program name */
 222         const char* lname,                      /* launcher name */
 223         jboolean javaargs,                      /* JAVA_ARGS */
 224         jboolean cpwildcard,                    /* classpath wildcard*/
 225         jboolean javaw,                         /* windows-only javaw */
 226         jint ergo                               /* unused */
 227 )
 228 {
 229     int mode = LM_UNKNOWN;
 230     char *what = NULL;
 231     char *main_class = NULL;
 232     int ret;
 233     InvocationFunctions ifn;
 234     jlong start, end;
 235     char jvmpath[MAXPATHLEN];
 236     char jrepath[MAXPATHLEN];
 237     char jvmcfg[MAXPATHLEN];
 238 
 239     _fVersion = fullversion;
 240     _launcher_name = lname;
 241     _program_name = pname;
 242     _is_java_args = javaargs;
 243     _wc_enabled = cpwildcard;
 244 
 245     InitLauncher(javaw);
 246     DumpState();
 247     if (JLI_IsTraceLauncher()) {
 248         int i;
 249         printf("Command line args:\n");
 250         for (i = 0; i < argc ; i++) {
 251             printf("argv[%d] = %s\n", i, argv[i]);
 252         }
 253         AddOption("-Dsun.java.launcher.diag=true", NULL);
 254     }
 255 
 256     /*
 257      * SelectVersion() has several responsibilities:
 258      *
 259      *  1) Disallow specification of another JRE.  With 1.9, another
 260      *     version of the JRE cannot be invoked.
 261      *  2) Allow for a JRE version to invoke JDK 1.9 or later.  Since
 262      *     all mJRE directives have been stripped from the request but
 263      *     the pre 1.9 JRE [ 1.6 thru 1.8 ], it is as if 1.9+ has been
 264      *     invoked from the command line.
 265      */
 266     SelectVersion(argc, argv, &main_class);
 267 
 268     CreateExecutionEnvironment(&argc, &argv,
 269                                jrepath, sizeof(jrepath),
 270                                jvmpath, sizeof(jvmpath),
 271                                jvmcfg,  sizeof(jvmcfg));
 272 
 273     if (!IsJavaArgs()) {
 274         SetJvmEnvironment(argc,argv);
 275     }
 276 
 277     ifn.CreateJavaVM = 0;
 278     ifn.GetDefaultJavaVMInitArgs = 0;
 279 
 280     if (JLI_IsTraceLauncher()) {
 281         start = CounterGet();
 282     }
 283 
 284     if (!LoadJavaVM(jvmpath, &ifn)) {
 285         return(6);
 286     }
 287 
 288     if (JLI_IsTraceLauncher()) {
 289         end   = CounterGet();
 290     }
 291 
 292     JLI_TraceLauncher("%ld micro seconds to LoadJavaVM\n",
 293              (long)(jint)Counter2Micros(end-start));
 294 
 295     ++argv;
 296     --argc;
 297 
 298     if (IsJavaArgs()) {
 299         /* Preprocess wrapper arguments */
 300         TranslateApplicationArgs(jargc, jargv, &argc, &argv);
 301         if (!AddApplicationOptions(appclassc, appclassv)) {
 302             return(1);
 303         }
 304     } else {
 305         /* Set default CLASSPATH */
 306         char* cpath = getenv("CLASSPATH");
 307         if (cpath != NULL) {
 308             SetClassPath(cpath);
 309         }
 310     }
 311 
 312     /* Parse command line options; if the return value of
 313      * ParseArguments is false, the program should exit.
 314      */
 315     if (!ParseArguments(&argc, &argv, &mode, &what, &ret, jrepath))
 316     {
 317         return(ret);
 318     }
 319 
 320     /* Override class path if -jar flag was specified */
 321     if (mode == LM_JAR) {
 322         SetClassPath(what);     /* Override class path */
 323     }
 324 
 325     /* set the -Dsun.java.command pseudo property */
 326     SetJavaCommandLineProp(what, argc, argv);
 327 
 328     /* Set the -Dsun.java.launcher pseudo property */
 329     SetJavaLauncherProp();
 330 
 331     /* set the -Dsun.java.launcher.* platform properties */
 332     SetJavaLauncherPlatformProps();
 333 
 334     return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);
 335 }
 336 /*
 337  * Always detach the main thread so that it appears to have ended when
 338  * the application's main method exits.  This will invoke the
 339  * uncaught exception handler machinery if main threw an
 340  * exception.  An uncaught exception handler cannot change the
 341  * launcher's return code except by calling System.exit.
 342  *
 343  * Wait for all non-daemon threads to end, then destroy the VM.
 344  * This will actually create a trivial new Java waiter thread
 345  * named "DestroyJavaVM", but this will be seen as a different
 346  * thread from the one that executed main, even though they are
 347  * the same C thread.  This allows mainThread.join() and
 348  * mainThread.isAlive() to work as expected.
 349  */
 350 #define LEAVE() \
 351     do { \
 352         if ((*vm)->DetachCurrentThread(vm) != JNI_OK) { \
 353             JLI_ReportErrorMessage(JVM_ERROR2); \
 354             ret = 1; \
 355         } \
 356         if (JNI_TRUE) { \
 357             (*vm)->DestroyJavaVM(vm); \
 358             return ret; \
 359         } \
 360     } while (JNI_FALSE)
 361 
 362 #define CHECK_EXCEPTION_NULL_LEAVE(CENL_exception) \
 363     do { \
 364         if ((*env)->ExceptionOccurred(env)) { \
 365             JLI_ReportExceptionDescription(env); \
 366             LEAVE(); \
 367         } \
 368         if ((CENL_exception) == NULL) { \
 369             JLI_ReportErrorMessage(JNI_ERROR); \
 370             LEAVE(); \
 371         } \
 372     } while (JNI_FALSE)
 373 
 374 #define CHECK_EXCEPTION_LEAVE(CEL_return_value) \
 375     do { \
 376         if ((*env)->ExceptionOccurred(env)) { \
 377             JLI_ReportExceptionDescription(env); \
 378             ret = (CEL_return_value); \
 379             LEAVE(); \
 380         } \
 381     } while (JNI_FALSE)
 382 
 383 
 384 int JNICALL
 385 JavaMain(void * _args)
 386 {
 387     JavaMainArgs *args = (JavaMainArgs *)_args;
 388     int argc = args->argc;
 389     char **argv = args->argv;
 390     int mode = args->mode;
 391     char *what = args->what;
 392     InvocationFunctions ifn = args->ifn;
 393 
 394     JavaVM *vm = 0;
 395     JNIEnv *env = 0;
 396     jclass mainClass = NULL;
 397     jclass appClass = NULL; // actual application class being launched
 398     jmethodID mainID;
 399     jobjectArray mainArgs;
 400     int ret = 0;
 401     jlong start, end;
 402 
 403     RegisterThread();
 404 
 405     /* Initialize the virtual machine */
 406     start = CounterGet();
 407     if (!InitializeJVM(&vm, &env, &ifn)) {
 408         JLI_ReportErrorMessage(JVM_ERROR1);
 409         exit(1);
 410     }
 411 
 412     if (showSettings != NULL) {
 413         ShowSettings(env, showSettings);
 414         CHECK_EXCEPTION_LEAVE(1);
 415     }
 416 
 417     // show resolved modules and continue
 418     if (showResolvedModules) {
 419         ShowResolvedModules(env);
 420         CHECK_EXCEPTION_LEAVE(1);
 421     }
 422 
 423     // list observable modules, then exit
 424     if (listModules) {
 425         ListModules(env);
 426         CHECK_EXCEPTION_LEAVE(1);
 427         LEAVE();
 428     }
 429 
 430     // describe a module, then exit
 431     if (describeModule != NULL) {
 432         DescribeModule(env, describeModule);
 433         CHECK_EXCEPTION_LEAVE(1);
 434         LEAVE();
 435     }
 436 
 437     // validate modules on the module path, then exit
 438     if (validateModules) {
 439         jboolean okay = ValidateModules(env);
 440         CHECK_EXCEPTION_LEAVE(1);
 441         if (!okay) ret = 1;
 442         LEAVE();
 443     }
 444 
 445     if (printVersion || showVersion) {
 446         PrintJavaVersion(env, showVersion);
 447         CHECK_EXCEPTION_LEAVE(0);
 448         if (printVersion) {
 449             LEAVE();
 450         }
 451     }
 452 
 453     /* If the user specified neither a class name nor a JAR file */
 454     if (printXUsage || printUsage || what == 0 || mode == LM_UNKNOWN) {
 455         PrintUsage(env, printXUsage);
 456         CHECK_EXCEPTION_LEAVE(1);
 457         LEAVE();
 458     }
 459 
 460     FreeKnownVMs(); /* after last possible PrintUsage */
 461 
 462     if (JLI_IsTraceLauncher()) {
 463         end = CounterGet();
 464         JLI_TraceLauncher("%ld micro seconds to InitializeJVM\n",
 465                (long)(jint)Counter2Micros(end-start));
 466     }
 467 
 468     /* At this stage, argc/argv have the application's arguments */
 469     if (JLI_IsTraceLauncher()){
 470         int i;
 471         printf("%s is '%s'\n", launchModeNames[mode], what);
 472         printf("App's argc is %d\n", argc);
 473         for (i=0; i < argc; i++) {
 474             printf("    argv[%2d] = '%s'\n", i, argv[i]);
 475         }
 476     }
 477 
 478     ret = 1;
 479 
 480     /*
 481      * Get the application's main class. It also checks if the main
 482      * method exists.
 483      *
 484      * See bugid 5030265.  The Main-Class name has already been parsed
 485      * from the manifest, but not parsed properly for UTF-8 support.
 486      * Hence the code here ignores the value previously extracted and
 487      * uses the pre-existing code to reextract the value.  This is
 488      * possibly an end of release cycle expedient.  However, it has
 489      * also been discovered that passing some character sets through
 490      * the environment has "strange" behavior on some variants of
 491      * Windows.  Hence, maybe the manifest parsing code local to the
 492      * launcher should never be enhanced.
 493      *
 494      * Hence, future work should either:
 495      *     1)   Correct the local parsing code and verify that the
 496      *          Main-Class attribute gets properly passed through
 497      *          all environments,
 498      *     2)   Remove the vestages of maintaining main_class through
 499      *          the environment (and remove these comments).
 500      *
 501      * This method also correctly handles launching existing JavaFX
 502      * applications that may or may not have a Main-Class manifest entry.
 503      */
 504     mainClass = LoadMainClass(env, mode, what);
 505     CHECK_EXCEPTION_NULL_LEAVE(mainClass);
 506     /*
 507      * In some cases when launching an application that needs a helper, e.g., a
 508      * JavaFX application with no main method, the mainClass will not be the
 509      * applications own main class but rather a helper class. To keep things
 510      * consistent in the UI we need to track and report the application main class.
 511      */
 512     appClass = GetApplicationClass(env);
 513     NULL_CHECK_RETURN_VALUE(appClass, -1);
 514 
 515     /* Build platform specific argument array */
 516     mainArgs = CreateApplicationArgs(env, argv, argc);
 517     CHECK_EXCEPTION_NULL_LEAVE(mainArgs);
 518 
 519     if (dryRun) {
 520         ret = 0;
 521         LEAVE();
 522     }
 523 
 524     /*
 525      * PostJVMInit uses the class name as the application name for GUI purposes,
 526      * for example, on OSX this sets the application name in the menu bar for
 527      * both SWT and JavaFX. So we'll pass the actual application class here
 528      * instead of mainClass as that may be a launcher or helper class instead
 529      * of the application class.
 530      */
 531     PostJVMInit(env, appClass, vm);
 532     CHECK_EXCEPTION_LEAVE(1);
 533 
 534     /*
 535      * The LoadMainClass not only loads the main class, it will also ensure
 536      * that the main method's signature is correct, therefore further checking
 537      * is not required. The main method is invoked here so that extraneous java
 538      * stacks are not in the application stack trace.
 539      */
 540     mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
 541                                        "([Ljava/lang/String;)V");
 542     CHECK_EXCEPTION_NULL_LEAVE(mainID);
 543 
 544     /* Invoke main method. */
 545     (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
 546 
 547     /*
 548      * The launcher's exit code (in the absence of calls to
 549      * System.exit) will be non-zero if main threw an exception.
 550      */
 551     ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
 552 
 553     LEAVE();
 554 }
 555 
 556 /*
 557  * Test if the given name is one of the class path options.
 558  */
 559 static jboolean
 560 IsClassPathOption(const char* name) {
 561     return JLI_StrCmp(name, "-classpath") == 0 ||
 562            JLI_StrCmp(name, "-cp") == 0 ||
 563            JLI_StrCmp(name, "--class-path") == 0;
 564 }
 565 
 566 /*
 567  * Test if the given name is a launcher option taking the main entry point.
 568  */
 569 static jboolean
 570 IsLauncherMainOption(const char* name) {
 571     return JLI_StrCmp(name, "--module") == 0 ||
 572            JLI_StrCmp(name, "-m") == 0;
 573 }
 574 
 575 /*
 576  * Test if the given name is a white-space launcher option.
 577  */
 578 static jboolean
 579 IsLauncherOption(const char* name) {
 580     return IsClassPathOption(name) ||
 581            IsLauncherMainOption(name) ||
 582            JLI_StrCmp(name, "--describe-module") == 0 ||
 583            JLI_StrCmp(name, "-d") == 0;
 584 }
 585 
 586 /*
 587  * Test if the given name is a module-system white-space option that
 588  * will be passed to the VM with its corresponding long-form option
 589  * name and "=" delimiter.
 590  */
 591 static jboolean
 592 IsModuleOption(const char* name) {
 593     return JLI_StrCmp(name, "--module-path") == 0 ||
 594            JLI_StrCmp(name, "-p") == 0 ||
 595            JLI_StrCmp(name, "--upgrade-module-path") == 0 ||
 596            JLI_StrCmp(name, "--add-modules") == 0 ||
 597            JLI_StrCmp(name, "--limit-modules") == 0 ||
 598            JLI_StrCmp(name, "--add-exports") == 0 ||
 599            JLI_StrCmp(name, "--add-opens") == 0 ||
 600            JLI_StrCmp(name, "--add-reads") == 0 ||
 601            JLI_StrCmp(name, "--patch-module") == 0;
 602 }
 603 
 604 static jboolean
 605 IsLongFormModuleOption(const char* name) {
 606     return JLI_StrCCmp(name, "--module-path=") == 0 ||
 607            JLI_StrCCmp(name, "--upgrade-module-path=") == 0 ||
 608            JLI_StrCCmp(name, "--add-modules=") == 0 ||
 609            JLI_StrCCmp(name, "--limit-modules=") == 0 ||
 610            JLI_StrCCmp(name, "--add-exports=") == 0 ||
 611            JLI_StrCCmp(name, "--add-reads=") == 0 ||
 612            JLI_StrCCmp(name, "--patch-module=") == 0;
 613 }
 614 
 615 /*
 616  * Test if the given name has a white space option.
 617  */
 618 jboolean
 619 IsWhiteSpaceOption(const char* name) {
 620     return IsModuleOption(name) ||
 621            IsLauncherOption(name);
 622 }
 623 
 624 /*
 625  * Checks the command line options to find which JVM type was
 626  * specified.  If no command line option was given for the JVM type,
 627  * the default type is used.  The environment variable
 628  * JDK_ALTERNATE_VM and the command line option -XXaltjvm= are also
 629  * checked as ways of specifying which JVM type to invoke.
 630  */
 631 char *
 632 CheckJvmType(int *pargc, char ***argv, jboolean speculative) {
 633     int i, argi;
 634     int argc;
 635     char **newArgv;
 636     int newArgvIdx = 0;
 637     int isVMType;
 638     int jvmidx = -1;
 639     char *jvmtype = getenv("JDK_ALTERNATE_VM");
 640 
 641     argc = *pargc;
 642 
 643     /* To make things simpler we always copy the argv array */
 644     newArgv = JLI_MemAlloc((argc + 1) * sizeof(char *));
 645 
 646     /* The program name is always present */
 647     newArgv[newArgvIdx++] = (*argv)[0];
 648 
 649     for (argi = 1; argi < argc; argi++) {
 650         char *arg = (*argv)[argi];
 651         isVMType = 0;
 652 
 653         if (IsJavaArgs()) {
 654             if (arg[0] != '-') {
 655                 newArgv[newArgvIdx++] = arg;
 656                 continue;
 657             }
 658         } else {
 659             if (IsWhiteSpaceOption(arg)) {
 660                 newArgv[newArgvIdx++] = arg;
 661                 argi++;
 662                 if (argi < argc) {
 663                     newArgv[newArgvIdx++] = (*argv)[argi];
 664                 }
 665                 continue;
 666             }
 667             if (arg[0] != '-') break;
 668         }
 669 
 670         /* Did the user pass an explicit VM type? */
 671         i = KnownVMIndex(arg);
 672         if (i >= 0) {
 673             jvmtype = knownVMs[jvmidx = i].name + 1; /* skip the - */
 674             isVMType = 1;
 675             *pargc = *pargc - 1;
 676         }
 677 
 678         /* Did the user specify an "alternate" VM? */
 679         else if (JLI_StrCCmp(arg, "-XXaltjvm=") == 0 || JLI_StrCCmp(arg, "-J-XXaltjvm=") == 0) {
 680             isVMType = 1;
 681             jvmtype = arg+((arg[1]=='X')? 10 : 12);
 682             jvmidx = -1;
 683         }
 684 
 685         if (!isVMType) {
 686             newArgv[newArgvIdx++] = arg;
 687         }
 688     }
 689 
 690     /*
 691      * Finish copying the arguments if we aborted the above loop.
 692      * NOTE that if we aborted via "break" then we did NOT copy the
 693      * last argument above, and in addition argi will be less than
 694      * argc.
 695      */
 696     while (argi < argc) {
 697         newArgv[newArgvIdx++] = (*argv)[argi];
 698         argi++;
 699     }
 700 
 701     /* argv is null-terminated */
 702     newArgv[newArgvIdx] = 0;
 703 
 704     /* Copy back argv */
 705     *argv = newArgv;
 706     *pargc = newArgvIdx;
 707 
 708     /* use the default VM type if not specified (no alias processing) */
 709     if (jvmtype == NULL) {
 710       char* result = knownVMs[0].name+1;
 711       JLI_TraceLauncher("Default VM: %s\n", result);
 712       return result;
 713     }
 714 
 715     /* if using an alternate VM, no alias processing */
 716     if (jvmidx < 0)
 717       return jvmtype;
 718 
 719     /* Resolve aliases first */
 720     {
 721       int loopCount = 0;
 722       while (knownVMs[jvmidx].flag == VM_ALIASED_TO) {
 723         int nextIdx = KnownVMIndex(knownVMs[jvmidx].alias);
 724 
 725         if (loopCount > knownVMsCount) {
 726           if (!speculative) {
 727             JLI_ReportErrorMessage(CFG_ERROR1);
 728             exit(1);
 729           } else {
 730             return "ERROR";
 731             /* break; */
 732           }
 733         }
 734 
 735         if (nextIdx < 0) {
 736           if (!speculative) {
 737             JLI_ReportErrorMessage(CFG_ERROR2, knownVMs[jvmidx].alias);
 738             exit(1);
 739           } else {
 740             return "ERROR";
 741           }
 742         }
 743         jvmidx = nextIdx;
 744         jvmtype = knownVMs[jvmidx].name+1;
 745         loopCount++;
 746       }
 747     }
 748 
 749     switch (knownVMs[jvmidx].flag) {
 750     case VM_WARN:
 751         if (!speculative) {
 752             JLI_ReportErrorMessage(CFG_WARN1, jvmtype, knownVMs[0].name + 1);
 753         }
 754         /* fall through */
 755     case VM_IGNORE:
 756         jvmtype = knownVMs[jvmidx=0].name + 1;
 757         /* fall through */
 758     case VM_KNOWN:
 759         break;
 760     case VM_ERROR:
 761         if (!speculative) {
 762             JLI_ReportErrorMessage(CFG_ERROR3, jvmtype);
 763             exit(1);
 764         } else {
 765             return "ERROR";
 766         }
 767     }
 768 
 769     return jvmtype;
 770 }
 771 
 772 /*
 773  * This method must be called before the VM is loaded, primarily
 774  * used to parse and set any VM related options or env variables.
 775  * This function is non-destructive leaving the argument list intact.
 776  */
 777 static void
 778 SetJvmEnvironment(int argc, char **argv) {
 779 
 780     static const char*  NMT_Env_Name    = "NMT_LEVEL_";
 781     int i;
 782     /* process only the launcher arguments */
 783     for (i = 0; i < argc; i++) {
 784         char *arg = argv[i];
 785         /*
 786          * Since this must be a VM flag we stop processing once we see
 787          * an argument the launcher would not have processed beyond (such
 788          * as -version or -h), or an argument that indicates the following
 789          * arguments are for the application (i.e. the main class name, or
 790          * the -jar argument).
 791          */
 792         if (i > 0) {
 793             char *prev = argv[i - 1];
 794             // skip non-dash arg preceded by class path specifiers
 795             if (*arg != '-' && IsWhiteSpaceOption(prev)) {
 796                 continue;
 797             }
 798 
 799             if (*arg != '-' || isTerminalOpt(arg)) {
 800                 return;
 801             }
 802         }
 803         /*
 804          * The following case checks for "-XX:NativeMemoryTracking=value".
 805          * If value is non null, an environmental variable set to this value
 806          * will be created to be used by the JVM.
 807          * The argument is passed to the JVM, which will check validity.
 808          * The JVM is responsible for removing the env variable.
 809          */
 810         if (JLI_StrCCmp(arg, "-XX:NativeMemoryTracking=") == 0) {
 811             int retval;
 812             // get what follows this parameter, include "="
 813             size_t pnlen = JLI_StrLen("-XX:NativeMemoryTracking=");
 814             if (JLI_StrLen(arg) > pnlen) {
 815                 char* value = arg + pnlen;
 816                 size_t pbuflen = pnlen + JLI_StrLen(value) + 10; // 10 max pid digits
 817 
 818                 /*
 819                  * ensures that malloc successful
 820                  * DONT JLI_MemFree() pbuf.  JLI_PutEnv() uses system call
 821                  *   that could store the address.
 822                  */
 823                 char * pbuf = (char*)JLI_MemAlloc(pbuflen);
 824 
 825                 JLI_Snprintf(pbuf, pbuflen, "%s%d=%s", NMT_Env_Name, JLI_GetPid(), value);
 826                 retval = JLI_PutEnv(pbuf);
 827                 if (JLI_IsTraceLauncher()) {
 828                     char* envName;
 829                     char* envBuf;
 830 
 831                     // ensures that malloc successful
 832                     envName = (char*)JLI_MemAlloc(pbuflen);
 833                     JLI_Snprintf(envName, pbuflen, "%s%d", NMT_Env_Name, JLI_GetPid());
 834 
 835                     printf("TRACER_MARKER: NativeMemoryTracking: env var is %s\n",envName);
 836                     printf("TRACER_MARKER: NativeMemoryTracking: putenv arg %s\n",pbuf);
 837                     envBuf = getenv(envName);
 838                     printf("TRACER_MARKER: NativeMemoryTracking: got value %s\n",envBuf);
 839                     free(envName);
 840                 }
 841             }
 842         }
 843     }
 844 }
 845 
 846 /* copied from HotSpot function "atomll()" */
 847 static int
 848 parse_size(const char *s, jlong *result) {
 849   jlong n = 0;
 850   int args_read = sscanf(s, JLONG_FORMAT_SPECIFIER, &n);
 851   if (args_read != 1) {
 852     return 0;
 853   }
 854   while (*s != '\0' && *s >= '0' && *s <= '9') {
 855     s++;
 856   }
 857   // 4705540: illegal if more characters are found after the first non-digit
 858   if (JLI_StrLen(s) > 1) {
 859     return 0;
 860   }
 861   switch (*s) {
 862     case 'T': case 't':
 863       *result = n * GB * KB;
 864       return 1;
 865     case 'G': case 'g':
 866       *result = n * GB;
 867       return 1;
 868     case 'M': case 'm':
 869       *result = n * MB;
 870       return 1;
 871     case 'K': case 'k':
 872       *result = n * KB;
 873       return 1;
 874     case '\0':
 875       *result = n;
 876       return 1;
 877     default:
 878       /* Create JVM with default stack and let VM handle malformed -Xss string*/
 879       return 0;
 880   }
 881 }
 882 
 883 /*
 884  * Adds a new VM option with the given name and value.
 885  */
 886 void
 887 AddOption(char *str, void *info)
 888 {
 889     /*
 890      * Expand options array if needed to accommodate at least one more
 891      * VM option.
 892      */
 893     if (numOptions >= maxOptions) {
 894         if (options == 0) {
 895             maxOptions = 4;
 896             options = JLI_MemAlloc(maxOptions * sizeof(JavaVMOption));
 897         } else {
 898             JavaVMOption *tmp;
 899             maxOptions *= 2;
 900             tmp = JLI_MemAlloc(maxOptions * sizeof(JavaVMOption));
 901             memcpy(tmp, options, numOptions * sizeof(JavaVMOption));
 902             JLI_MemFree(options);
 903             options = tmp;
 904         }
 905     }
 906     options[numOptions].optionString = str;
 907     options[numOptions++].extraInfo = info;
 908 
 909     if (JLI_StrCCmp(str, "-Xss") == 0) {
 910         jlong tmp;
 911         if (parse_size(str + 4, &tmp)) {
 912             threadStackSize = tmp;
 913             /*
 914              * Make sure the thread stack size is big enough that we won't get a stack
 915              * overflow before the JVM startup code can check to make sure the stack
 916              * is big enough.
 917              */
 918             if (threadStackSize < (jlong)STACK_SIZE_MINIMUM) {
 919                 threadStackSize = STACK_SIZE_MINIMUM;
 920             }
 921         }
 922     }
 923 
 924     if (JLI_StrCCmp(str, "-Xmx") == 0) {
 925         jlong tmp;
 926         if (parse_size(str + 4, &tmp)) {
 927             maxHeapSize = tmp;
 928         }
 929     }
 930 
 931     if (JLI_StrCCmp(str, "-Xms") == 0) {
 932         jlong tmp;
 933         if (parse_size(str + 4, &tmp)) {
 934            initialHeapSize = tmp;
 935         }
 936     }
 937 }
 938 
 939 static void
 940 SetClassPath(const char *s)
 941 {
 942     char *def;
 943     const char *orig = s;
 944     static const char format[] = "-Djava.class.path=%s";
 945     /*
 946      * usually we should not get a null pointer, but there are cases where
 947      * we might just get one, in which case we simply ignore it, and let the
 948      * caller deal with it
 949      */
 950     if (s == NULL)
 951         return;
 952     s = JLI_WildcardExpandClasspath(s);
 953     if (sizeof(format) - 2 + JLI_StrLen(s) < JLI_StrLen(s))
 954         // s is became corrupted after expanding wildcards
 955         return;
 956     def = JLI_MemAlloc(sizeof(format)
 957                        - 2 /* strlen("%s") */
 958                        + JLI_StrLen(s));
 959     sprintf(def, format, s);
 960     AddOption(def, NULL);
 961     if (s != orig)
 962         JLI_MemFree((char *) s);
 963     _have_classpath = JNI_TRUE;
 964 }
 965 
 966 static void
 967 AddLongFormOption(const char *option, const char *arg)
 968 {
 969     static const char format[] = "%s=%s";
 970     char *def;
 971     size_t def_len;
 972 
 973     def_len = JLI_StrLen(option) + 1 + JLI_StrLen(arg) + 1;
 974     def = JLI_MemAlloc(def_len);
 975     JLI_Snprintf(def, def_len, format, option, arg);
 976     AddOption(def, NULL);
 977 }
 978 
 979 static void
 980 SetMainModule(const char *s)
 981 {
 982     static const char format[] = "-Djdk.module.main=%s";
 983     char* slash = JLI_StrChr(s, '/');
 984     size_t s_len, def_len;
 985     char *def;
 986 
 987     /* value may be <module> or <module>/<mainclass> */
 988     if (slash == NULL) {
 989         s_len = JLI_StrLen(s);
 990     } else {
 991         s_len = (size_t) (slash - s);
 992     }
 993     def_len = sizeof(format)
 994                - 2 /* strlen("%s") */
 995                + s_len;
 996     def = JLI_MemAlloc(def_len);
 997     JLI_Snprintf(def, def_len, format, s);
 998     AddOption(def, NULL);
 999 }
1000 
1001 /*
1002  * The SelectVersion() routine ensures that an appropriate version of
1003  * the JRE is running.  The specification for the appropriate version
1004  * is obtained from either the manifest of a jar file (preferred) or
1005  * from command line options.
1006  * The routine also parses splash screen command line options and
1007  * passes on their values in private environment variables.
1008  */
1009 static void
1010 SelectVersion(int argc, char **argv, char **main_class)
1011 {
1012     char    *arg;
1013     char    *operand;
1014     char    *version = NULL;
1015     char    *jre = NULL;
1016     int     jarflag = 0;
1017     int     headlessflag = 0;
1018     int     restrict_search = -1;               /* -1 implies not known */
1019     manifest_info info;
1020     char    env_entry[MAXNAMELEN + 24] = ENV_ENTRY "=";
1021     char    *splash_file_name = NULL;
1022     char    *splash_jar_name = NULL;
1023     char    *env_in;
1024     int     res;
1025     jboolean has_arg;
1026 
1027     /*
1028      * If the version has already been selected, set *main_class
1029      * with the value passed through the environment (if any) and
1030      * simply return.
1031      */
1032 
1033     /*
1034      * This environmental variable can be set by mJRE capable JREs
1035      * [ 1.5 thru 1.8 ].  All other aspects of mJRE processing have been
1036      * stripped by those JREs.  This environmental variable allows 1.9+
1037      * JREs to be started by these mJRE capable JREs.
1038      * Note that mJRE directives in the jar manifest file would have been
1039      * ignored for a JRE started by another JRE...
1040      * .. skipped for JRE 1.5 and beyond.
1041      * .. not even checked for pre 1.5.
1042      */
1043     if ((env_in = getenv(ENV_ENTRY)) != NULL) {
1044         if (*env_in != '\0')
1045             *main_class = JLI_StringDup(env_in);
1046         return;
1047     }
1048 
1049     /*
1050      * Scan through the arguments for options relevant to multiple JRE
1051      * support.  Multiple JRE support existed in JRE versions 1.5 thru 1.8.
1052      *
1053      * This capability is no longer available with JRE versions 1.9 and later.
1054      * These command line options are reported as errors.
1055      */
1056 
1057     argc--;
1058     argv++;
1059     while ((arg = *argv) != 0 && *arg == '-') {
1060         has_arg = IsOptionWithArgument(argc, argv);
1061         if (JLI_StrCCmp(arg, "-version:") == 0) {
1062             JLI_ReportErrorMessage(SPC_ERROR1);
1063         } else if (JLI_StrCmp(arg, "-jre-restrict-search") == 0) {
1064             JLI_ReportErrorMessage(SPC_ERROR2);
1065         } else if (JLI_StrCmp(arg, "-jre-no-restrict-search") == 0) {
1066             JLI_ReportErrorMessage(SPC_ERROR2);
1067         } else {
1068             if (JLI_StrCmp(arg, "-jar") == 0)
1069                 jarflag = 1;
1070             if (IsWhiteSpaceOption(arg)) {
1071                 if (has_arg) {
1072                     argc--;
1073                     argv++;
1074                     arg = *argv;
1075                 }
1076             }
1077 
1078             /*
1079              * Checking for headless toolkit option in the some way as AWT does:
1080              * "true" means true and any other value means false
1081              */
1082             if (JLI_StrCmp(arg, "-Djava.awt.headless=true") == 0) {
1083                 headlessflag = 1;
1084             } else if (JLI_StrCCmp(arg, "-Djava.awt.headless=") == 0) {
1085                 headlessflag = 0;
1086             } else if (JLI_StrCCmp(arg, "-splash:") == 0) {
1087                 splash_file_name = arg+8;
1088             }
1089         }
1090         argc--;
1091         argv++;
1092     }
1093     if (argc <= 0) {    /* No operand? Possibly legit with -[full]version */
1094         operand = NULL;
1095     } else {
1096         argc--;
1097         operand = *argv++;
1098     }
1099 
1100     /*
1101      * If there is a jar file, read the manifest. If the jarfile can't be
1102      * read, the manifest can't be read from the jar file, or the manifest
1103      * is corrupt, issue the appropriate error messages and exit.
1104      *
1105      * Even if there isn't a jar file, construct a manifest_info structure
1106      * containing the command line information.  It's a convenient way to carry
1107      * this data around.
1108      */
1109     if (jarflag && operand) {
1110         if ((res = JLI_ParseManifest(operand, &info)) != 0) {
1111             if (res == -1)
1112                 JLI_ReportErrorMessage(JAR_ERROR2, operand);
1113             else
1114                 JLI_ReportErrorMessage(JAR_ERROR3, operand);
1115             exit(1);
1116         }
1117 
1118         /*
1119          * Command line splash screen option should have precedence
1120          * over the manifest, so the manifest data is used only if
1121          * splash_file_name has not been initialized above during command
1122          * line parsing
1123          */
1124         if (!headlessflag && !splash_file_name && info.splashscreen_image_file_name) {
1125             splash_file_name = info.splashscreen_image_file_name;
1126             splash_jar_name = operand;
1127         }
1128     } else {
1129         info.manifest_version = NULL;
1130         info.main_class = NULL;
1131         info.jre_version = NULL;
1132         info.jre_restrict_search = 0;
1133     }
1134 
1135     /*
1136      * Passing on splash screen info in environment variables
1137      */
1138     if (splash_file_name && !headlessflag) {
1139         char* splash_file_entry = JLI_MemAlloc(JLI_StrLen(SPLASH_FILE_ENV_ENTRY "=")+JLI_StrLen(splash_file_name)+1);
1140         JLI_StrCpy(splash_file_entry, SPLASH_FILE_ENV_ENTRY "=");
1141         JLI_StrCat(splash_file_entry, splash_file_name);
1142         putenv(splash_file_entry);
1143     }
1144     if (splash_jar_name && !headlessflag) {
1145         char* splash_jar_entry = JLI_MemAlloc(JLI_StrLen(SPLASH_JAR_ENV_ENTRY "=")+JLI_StrLen(splash_jar_name)+1);
1146         JLI_StrCpy(splash_jar_entry, SPLASH_JAR_ENV_ENTRY "=");
1147         JLI_StrCat(splash_jar_entry, splash_jar_name);
1148         putenv(splash_jar_entry);
1149     }
1150 
1151 
1152     /*
1153      * "Valid" returns (other than unrecoverable errors) follow.  Set
1154      * main_class as a side-effect of this routine.
1155      */
1156     if (info.main_class != NULL)
1157         *main_class = JLI_StringDup(info.main_class);
1158 
1159     if (info.jre_version == NULL) {
1160         JLI_FreeManifest();
1161         return;
1162     }
1163 
1164 }
1165 
1166 /*
1167  * Test if the current argv is an option, i.e. with a leading `-`
1168  * and followed with an argument without a leading `-`.
1169  */
1170 static jboolean
1171 IsOptionWithArgument(int argc, char** argv) {
1172     char* option;
1173     char* arg;
1174 
1175     if (argc <= 1)
1176         return JNI_FALSE;
1177 
1178     option = *argv;
1179     arg = *(argv+1);
1180     return *option == '-' && *arg != '-';
1181 }
1182 
1183 /*
1184  * Gets the option, and its argument if the option has an argument.
1185  * It will update *pargc, **pargv to the next option.
1186  */
1187 static int
1188 GetOpt(int *pargc, char ***pargv, char **poption, char **pvalue) {
1189     int argc = *pargc;
1190     char** argv = *pargv;
1191     char* arg = *argv;
1192 
1193     char* option = arg;
1194     char* value = NULL;
1195     char* equals = NULL;
1196     int kind = LAUNCHER_OPTION;
1197     jboolean has_arg = JNI_FALSE;
1198 
1199     // check if this option may be a white-space option with an argument
1200     has_arg = IsOptionWithArgument(argc, argv);
1201 
1202     argv++; --argc;
1203     if (IsLauncherOption(arg)) {
1204         if (has_arg) {
1205             value = *argv;
1206             argv++; --argc;
1207         }
1208         kind = IsLauncherMainOption(arg) ? LAUNCHER_MAIN_OPTION
1209                                          : LAUNCHER_OPTION_WITH_ARGUMENT;
1210     } else if (IsModuleOption(arg)) {
1211         kind = VM_LONG_OPTION_WITH_ARGUMENT;
1212         if (has_arg) {
1213             value = *argv;
1214             argv++; --argc;
1215         }
1216 
1217         /*
1218          * Support short form alias
1219          */
1220         if (JLI_StrCmp(arg, "-p") == 0) {
1221             option = "--module-path";
1222         }
1223 
1224     } else if (JLI_StrCCmp(arg, "--") == 0 && (equals = JLI_StrChr(arg, '=')) != NULL) {
1225         value = equals+1;
1226         if (JLI_StrCCmp(arg, "--describe-module=") == 0 ||
1227             JLI_StrCCmp(arg, "--module=") == 0 ||
1228             JLI_StrCCmp(arg, "--class-path=") == 0) {
1229             kind = LAUNCHER_OPTION_WITH_ARGUMENT;
1230         } else {
1231             kind = VM_LONG_OPTION;
1232         }
1233     }
1234 
1235     *pargc = argc;
1236     *pargv = argv;
1237     *poption = option;
1238     *pvalue = value;
1239     return kind;
1240 }
1241 
1242 /*
1243  * Parses command line arguments.  Returns JNI_FALSE if launcher
1244  * should exit without starting vm, returns JNI_TRUE if vm needs
1245  * to be started to process given options.  *pret (the launcher
1246  * process return value) is set to 0 for a normal exit.
1247  */
1248 static jboolean
1249 ParseArguments(int *pargc, char ***pargv,
1250                int *pmode, char **pwhat,
1251                int *pret, const char *jrepath)
1252 {
1253     int argc = *pargc;
1254     char **argv = *pargv;
1255     int mode = LM_UNKNOWN;
1256     char *arg;
1257 
1258     *pret = 0;
1259 
1260     while ((arg = *argv) != 0 && *arg == '-') {
1261         char *option = NULL;
1262         char *value = NULL;
1263         int kind = GetOpt(&argc, &argv, &option, &value);
1264         jboolean has_arg = value != NULL && JLI_StrLen(value) > 0;
1265         jboolean has_arg_any_len = value != NULL;
1266 
1267 /*
1268  * Option to set main entry point
1269  */
1270         if (JLI_StrCmp(arg, "-jar") == 0) {
1271             ARG_CHECK(argc, ARG_ERROR2, arg);
1272             mode = LM_JAR;
1273         } else if (JLI_StrCmp(arg, "--module") == 0 ||
1274                    JLI_StrCCmp(arg, "--module=") == 0 ||
1275                    JLI_StrCmp(arg, "-m") == 0) {
1276             REPORT_ERROR (has_arg, ARG_ERROR5, arg);
1277             SetMainModule(value);
1278             mode = LM_MODULE;
1279             if (has_arg) {
1280                *pwhat = value;
1281                 break;
1282             }
1283         } else if (JLI_StrCmp(arg, "--class-path") == 0 ||
1284                    JLI_StrCCmp(arg, "--class-path=") == 0 ||
1285                    JLI_StrCmp(arg, "-classpath") == 0 ||
1286                    JLI_StrCmp(arg, "-cp") == 0) {
1287             REPORT_ERROR (has_arg_any_len, ARG_ERROR1, arg);
1288             SetClassPath(value);
1289             mode = LM_CLASS;
1290         } else if (JLI_StrCmp(arg, "--list-modules") == 0) {
1291             listModules = JNI_TRUE;
1292         } else if (JLI_StrCmp(arg, "--show-resolved-modules") == 0) {
1293             showResolvedModules = JNI_TRUE;
1294         } else if (JLI_StrCmp(arg, "--validate-modules") == 0) {
1295             AddOption("-Djdk.module.minimumBoot=true", NULL);
1296             validateModules = JNI_TRUE;
1297         } else if (JLI_StrCmp(arg, "--describe-module") == 0 ||
1298                    JLI_StrCCmp(arg, "--describe-module=") == 0 ||
1299                    JLI_StrCmp(arg, "-d") == 0) {
1300             REPORT_ERROR (has_arg_any_len, ARG_ERROR12, arg);
1301             describeModule = value;
1302 /*
1303  * Parse white-space options
1304  */
1305         } else if (has_arg) {
1306             if (kind == VM_LONG_OPTION) {
1307                 AddOption(option, NULL);
1308             } else if (kind == VM_LONG_OPTION_WITH_ARGUMENT) {
1309                 AddLongFormOption(option, value);
1310             }
1311 /*
1312  * Error missing argument
1313  */
1314         } else if (!has_arg && (JLI_StrCmp(arg, "--module-path") == 0 ||
1315                                 JLI_StrCmp(arg, "-p") == 0 ||
1316                                 JLI_StrCmp(arg, "--upgrade-module-path") == 0)) {
1317             REPORT_ERROR (has_arg, ARG_ERROR4, arg);
1318 
1319         } else if (!has_arg && (IsModuleOption(arg) || IsLongFormModuleOption(arg))) {
1320             REPORT_ERROR (has_arg, ARG_ERROR6, arg);
1321 /*
1322  * The following cases will cause the argument parsing to stop
1323  */
1324         } else if (JLI_StrCmp(arg, "-help") == 0 ||
1325                    JLI_StrCmp(arg, "-h") == 0 ||
1326                    JLI_StrCmp(arg, "-?") == 0) {
1327             printUsage = JNI_TRUE;
1328             return JNI_TRUE;
1329         } else if (JLI_StrCmp(arg, "--help") == 0) {
1330             printUsage = JNI_TRUE;
1331             printTo = USE_STDOUT;
1332             return JNI_TRUE;
1333         } else if (JLI_StrCmp(arg, "-version") == 0) {
1334             printVersion = JNI_TRUE;
1335             return JNI_TRUE;
1336         } else if (JLI_StrCmp(arg, "--version") == 0) {
1337             printVersion = JNI_TRUE;
1338             printTo = USE_STDOUT;
1339             return JNI_TRUE;
1340         } else if (JLI_StrCmp(arg, "-showversion") == 0) {
1341             showVersion = JNI_TRUE;
1342         } else if (JLI_StrCmp(arg, "--show-version") == 0) {
1343             showVersion = JNI_TRUE;
1344             printTo = USE_STDOUT;
1345         } else if (JLI_StrCmp(arg, "--dry-run") == 0) {
1346             dryRun = JNI_TRUE;
1347         } else if (JLI_StrCmp(arg, "-X") == 0) {
1348             printXUsage = JNI_TRUE;
1349             return JNI_TRUE;
1350         } else if (JLI_StrCmp(arg, "--help-extra") == 0) {
1351             printXUsage = JNI_TRUE;
1352             printTo = USE_STDOUT;
1353             return JNI_TRUE;
1354 /*
1355  * The following case checks for -XshowSettings OR -XshowSetting:SUBOPT.
1356  * In the latter case, any SUBOPT value not recognized will default to "all"
1357  */
1358         } else if (JLI_StrCmp(arg, "-XshowSettings") == 0 ||
1359                    JLI_StrCCmp(arg, "-XshowSettings:") == 0) {
1360             showSettings = arg;
1361         } else if (JLI_StrCmp(arg, "-Xdiag") == 0) {
1362             AddOption("-Dsun.java.launcher.diag=true", NULL);
1363         } else if (JLI_StrCmp(arg, "--show-module-resolution") == 0) {
1364             AddOption("-Djdk.module.showModuleResolution=true", NULL);
1365 /*
1366  * The following case provide backward compatibility with old-style
1367  * command line options.
1368  */
1369         } else if (JLI_StrCmp(arg, "-fullversion") == 0) {
1370             JLI_ReportMessage("%s full version \"%s\"", _launcher_name, GetFullVersion());
1371             return JNI_FALSE;
1372         } else if (JLI_StrCmp(arg, "--full-version") == 0) {
1373             JLI_ShowMessage("%s %s", _launcher_name, GetFullVersion());
1374             return JNI_FALSE;
1375         } else if (JLI_StrCmp(arg, "-verbosegc") == 0) {
1376             AddOption("-verbose:gc", NULL);
1377         } else if (JLI_StrCmp(arg, "-t") == 0) {
1378             AddOption("-Xt", NULL);
1379         } else if (JLI_StrCmp(arg, "-tm") == 0) {
1380             AddOption("-Xtm", NULL);
1381         } else if (JLI_StrCmp(arg, "-debug") == 0) {
1382             AddOption("-Xdebug", NULL);
1383         } else if (JLI_StrCmp(arg, "-noclassgc") == 0) {
1384             AddOption("-Xnoclassgc", NULL);
1385         } else if (JLI_StrCmp(arg, "-Xfuture") == 0) {
1386             AddOption("-Xverify:all", NULL);
1387         } else if (JLI_StrCmp(arg, "-verify") == 0) {
1388             AddOption("-Xverify:all", NULL);
1389         } else if (JLI_StrCmp(arg, "-verifyremote") == 0) {
1390             AddOption("-Xverify:remote", NULL);
1391         } else if (JLI_StrCmp(arg, "-noverify") == 0) {
1392             AddOption("-Xverify:none", NULL);
1393         } else if (JLI_StrCCmp(arg, "-ss") == 0 ||
1394                    JLI_StrCCmp(arg, "-oss") == 0 ||
1395                    JLI_StrCCmp(arg, "-ms") == 0 ||
1396                    JLI_StrCCmp(arg, "-mx") == 0) {
1397             char *tmp = JLI_MemAlloc(JLI_StrLen(arg) + 6);
1398             sprintf(tmp, "-X%s", arg + 1); /* skip '-' */
1399             AddOption(tmp, NULL);
1400         } else if (JLI_StrCmp(arg, "-checksource") == 0 ||
1401                    JLI_StrCmp(arg, "-cs") == 0 ||
1402                    JLI_StrCmp(arg, "-noasyncgc") == 0) {
1403             /* No longer supported */
1404             JLI_ReportErrorMessage(ARG_WARN, arg);
1405         } else if (JLI_StrCCmp(arg, "-splash:") == 0) {
1406             ; /* Ignore machine independent options already handled */
1407         } else if (ProcessPlatformOption(arg)) {
1408             ; /* Processing of platform dependent options */
1409         } else {
1410             /* java.class.path set on the command line */
1411             if (JLI_StrCCmp(arg, "-Djava.class.path=") == 0) {
1412                 _have_classpath = JNI_TRUE;
1413             }
1414             AddOption(arg, NULL);
1415         }
1416     }
1417 
1418     if (*pwhat == NULL && --argc >= 0) {
1419         *pwhat = *argv++;
1420     }
1421 
1422     if (*pwhat == NULL) {
1423         /* LM_UNKNOWN okay for options that exit */
1424         if (!listModules && !describeModule && !validateModules) {
1425             *pret = 1;
1426         }
1427     } else if (mode == LM_UNKNOWN) {
1428         /* default to LM_CLASS if -m, -jar and -cp options are
1429          * not specified */
1430         if (!_have_classpath) {
1431             SetClassPath(".");
1432         }
1433         mode = LM_CLASS;
1434     }
1435 
1436     if (argc >= 0) {
1437         *pargc = argc;
1438         *pargv = argv;
1439     }
1440 
1441     *pmode = mode;
1442 
1443     return JNI_TRUE;
1444 }
1445 
1446 /*
1447  * Initializes the Java Virtual Machine. Also frees options array when
1448  * finished.
1449  */
1450 static jboolean
1451 InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn)
1452 {
1453     JavaVMInitArgs args;
1454     jint r;
1455 
1456     memset(&args, 0, sizeof(args));
1457     args.version  = JNI_VERSION_1_2;
1458     args.nOptions = numOptions;
1459     args.options  = options;
1460     args.ignoreUnrecognized = JNI_FALSE;
1461 
1462     if (JLI_IsTraceLauncher()) {
1463         int i = 0;
1464         printf("JavaVM args:\n    ");
1465         printf("version 0x%08lx, ", (long)args.version);
1466         printf("ignoreUnrecognized is %s, ",
1467                args.ignoreUnrecognized ? "JNI_TRUE" : "JNI_FALSE");
1468         printf("nOptions is %ld\n", (long)args.nOptions);
1469         for (i = 0; i < numOptions; i++)
1470             printf("    option[%2d] = '%s'\n",
1471                    i, args.options[i].optionString);
1472     }
1473 
1474     r = ifn->CreateJavaVM(pvm, (void **)penv, &args);
1475     JLI_MemFree(options);
1476     return r == JNI_OK;
1477 }
1478 
1479 static jclass helperClass = NULL;
1480 
1481 jclass
1482 GetLauncherHelperClass(JNIEnv *env)
1483 {
1484     if (helperClass == NULL) {
1485         NULL_CHECK0(helperClass = FindBootStrapClass(env,
1486                 "sun/launcher/LauncherHelper"));
1487     }
1488     return helperClass;
1489 }
1490 
1491 static jmethodID makePlatformStringMID = NULL;
1492 /*
1493  * Returns a new Java string object for the specified platform string.
1494  */
1495 static jstring
1496 NewPlatformString(JNIEnv *env, char *s)
1497 {
1498     int len = (int)JLI_StrLen(s);
1499     jbyteArray ary;
1500     jclass cls = GetLauncherHelperClass(env);
1501     NULL_CHECK0(cls);
1502     if (s == NULL)
1503         return 0;
1504 
1505     ary = (*env)->NewByteArray(env, len);
1506     if (ary != 0) {
1507         jstring str = 0;
1508         (*env)->SetByteArrayRegion(env, ary, 0, len, (jbyte *)s);
1509         if (!(*env)->ExceptionOccurred(env)) {
1510             if (makePlatformStringMID == NULL) {
1511                 NULL_CHECK0(makePlatformStringMID = (*env)->GetStaticMethodID(env,
1512                         cls, "makePlatformString", "(Z[B)Ljava/lang/String;"));
1513             }
1514             str = (*env)->CallStaticObjectMethod(env, cls,
1515                     makePlatformStringMID, USE_STDERR, ary);
1516             (*env)->DeleteLocalRef(env, ary);
1517             return str;
1518         }
1519     }
1520     return 0;
1521 }
1522 
1523 /*
1524  * Returns a new array of Java string objects for the specified
1525  * array of platform strings.
1526  */
1527 jobjectArray
1528 NewPlatformStringArray(JNIEnv *env, char **strv, int strc)
1529 {
1530     jarray cls;
1531     jarray ary;
1532     int i;
1533 
1534     NULL_CHECK0(cls = FindBootStrapClass(env, "java/lang/String"));
1535     NULL_CHECK0(ary = (*env)->NewObjectArray(env, strc, cls, 0));
1536     CHECK_EXCEPTION_RETURN_VALUE(0);
1537     for (i = 0; i < strc; i++) {
1538         jstring str = NewPlatformString(env, *strv++);
1539         NULL_CHECK0(str);
1540         (*env)->SetObjectArrayElement(env, ary, i, str);
1541         (*env)->DeleteLocalRef(env, str);
1542     }
1543     return ary;
1544 }
1545 
1546 /*
1547  * Loads a class and verifies that the main class is present and it is ok to
1548  * call it for more details refer to the java implementation.
1549  */
1550 static jclass
1551 LoadMainClass(JNIEnv *env, int mode, char *name)
1552 {
1553     jmethodID mid;
1554     jstring str;
1555     jobject result;
1556     jlong start, end;
1557     jclass cls = GetLauncherHelperClass(env);
1558     NULL_CHECK0(cls);
1559     if (JLI_IsTraceLauncher()) {
1560         start = CounterGet();
1561     }
1562     NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls,
1563                 "checkAndLoadMain",
1564                 "(ZILjava/lang/String;)Ljava/lang/Class;"));
1565 
1566     NULL_CHECK0(str = NewPlatformString(env, name));
1567     NULL_CHECK0(result = (*env)->CallStaticObjectMethod(env, cls, mid,
1568                                                         USE_STDERR, mode, str));
1569 
1570     if (JLI_IsTraceLauncher()) {
1571         end   = CounterGet();
1572         printf("%ld micro seconds to load main class\n",
1573                (long)(jint)Counter2Micros(end-start));
1574         printf("----%s----\n", JLDEBUG_ENV_ENTRY);
1575     }
1576 
1577     return (jclass)result;
1578 }
1579 
1580 static jclass
1581 GetApplicationClass(JNIEnv *env)
1582 {
1583     jmethodID mid;
1584     jclass cls = GetLauncherHelperClass(env);
1585     NULL_CHECK0(cls);
1586     NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls,
1587                 "getApplicationClass",
1588                 "()Ljava/lang/Class;"));
1589 
1590     return (*env)->CallStaticObjectMethod(env, cls, mid);
1591 }
1592 
1593 static char* expandWildcardOnLongOpt(char* arg) {
1594     char *p, *value;
1595     size_t optLen, valueLen;
1596     p = JLI_StrChr(arg, '=');
1597 
1598     if (p == NULL || p[1] == '\0') {
1599         JLI_ReportErrorMessage(ARG_ERROR1, arg);
1600         exit(1);
1601     }
1602     p++;
1603     value = (char *) JLI_WildcardExpandClasspath(p);
1604     if (p == value) {
1605         // no wildcard
1606         return arg;
1607     }
1608 
1609     optLen = p - arg;
1610     valueLen = JLI_StrLen(value);
1611     p = JLI_MemAlloc(optLen + valueLen + 1);
1612     memcpy(p, arg, optLen);
1613     memcpy(p + optLen, value, valueLen);
1614     p[optLen + valueLen] = '\0';
1615     return p;
1616 }
1617 
1618 /*
1619  * For tools, convert command line args thus:
1620  *   javac -cp foo:foo/"*" -J-ms32m ...
1621  *   java -ms32m -cp JLI_WildcardExpandClasspath(foo:foo/"*") ...
1622  *
1623  * Takes 4 parameters, and returns the populated arguments
1624  */
1625 static void
1626 TranslateApplicationArgs(int jargc, const char **jargv, int *pargc, char ***pargv)
1627 {
1628     int argc = *pargc;
1629     char **argv = *pargv;
1630     int nargc = argc + jargc;
1631     char **nargv = JLI_MemAlloc((nargc + 1) * sizeof(char *));
1632     int i;
1633 
1634     *pargc = nargc;
1635     *pargv = nargv;
1636 
1637     /* Copy the VM arguments (i.e. prefixed with -J) */
1638     for (i = 0; i < jargc; i++) {
1639         const char *arg = jargv[i];
1640         if (arg[0] == '-' && arg[1] == 'J') {
1641             *nargv++ = ((arg + 2) == NULL) ? NULL : JLI_StringDup(arg + 2);
1642         }
1643     }
1644 
1645     for (i = 0; i < argc; i++) {
1646         char *arg = argv[i];
1647         if (arg[0] == '-' && arg[1] == 'J') {
1648             if (arg[2] == '\0') {
1649                 JLI_ReportErrorMessage(ARG_ERROR3);
1650                 exit(1);
1651             }
1652             *nargv++ = arg + 2;
1653         }
1654     }
1655 
1656     /* Copy the rest of the arguments */
1657     for (i = 0; i < jargc ; i++) {
1658         const char *arg = jargv[i];
1659         if (arg[0] != '-' || arg[1] != 'J') {
1660             *nargv++ = (arg == NULL) ? NULL : JLI_StringDup(arg);
1661         }
1662     }
1663     for (i = 0; i < argc; i++) {
1664         char *arg = argv[i];
1665         if (arg[0] == '-') {
1666             if (arg[1] == 'J')
1667                 continue;
1668             if (IsWildCardEnabled()) {
1669                 if (IsClassPathOption(arg) && i < argc - 1) {
1670                     *nargv++ = arg;
1671                     *nargv++ = (char *) JLI_WildcardExpandClasspath(argv[i+1]);
1672                     i++;
1673                     continue;
1674                 }
1675                 if (JLI_StrCCmp(arg, "--class-path=") == 0) {
1676                     *nargv++ = expandWildcardOnLongOpt(arg);
1677                     continue;
1678                 }
1679             }
1680         }
1681         *nargv++ = arg;
1682     }
1683     *nargv = 0;
1684 }
1685 
1686 /*
1687  * For our tools, we try to add 3 VM options:
1688  *      -Denv.class.path=<envcp>
1689  *      -Dapplication.home=<apphome>
1690  *      -Djava.class.path=<appcp>
1691  * <envcp>   is the user's setting of CLASSPATH -- for instance the user
1692  *           tells javac where to find binary classes through this environment
1693  *           variable.  Notice that users will be able to compile against our
1694  *           tools classes (sun.tools.javac.Main) only if they explicitly add
1695  *           tools.jar to CLASSPATH.
1696  * <apphome> is the directory where the application is installed.
1697  * <appcp>   is the classpath to where our apps' classfiles are.
1698  */
1699 static jboolean
1700 AddApplicationOptions(int cpathc, const char **cpathv)
1701 {
1702     char *envcp, *appcp, *apphome;
1703     char home[MAXPATHLEN]; /* application home */
1704     char separator[] = { PATH_SEPARATOR, '\0' };
1705     int size, i;
1706 
1707     {
1708         const char *s = getenv("CLASSPATH");
1709         if (s) {
1710             s = (char *) JLI_WildcardExpandClasspath(s);
1711             /* 40 for -Denv.class.path= */
1712             if (JLI_StrLen(s) + 40 > JLI_StrLen(s)) { // Safeguard from overflow
1713                 envcp = (char *)JLI_MemAlloc(JLI_StrLen(s) + 40);
1714                 sprintf(envcp, "-Denv.class.path=%s", s);
1715                 AddOption(envcp, NULL);
1716             }
1717         }
1718     }
1719 
1720     if (!GetApplicationHome(home, sizeof(home))) {
1721         JLI_ReportErrorMessage(CFG_ERROR5);
1722         return JNI_FALSE;
1723     }
1724 
1725     /* 40 for '-Dapplication.home=' */
1726     apphome = (char *)JLI_MemAlloc(JLI_StrLen(home) + 40);
1727     sprintf(apphome, "-Dapplication.home=%s", home);
1728     AddOption(apphome, NULL);
1729 
1730     /* How big is the application's classpath? */
1731     if (cpathc > 0) {
1732         size = 40;                                 /* 40: "-Djava.class.path=" */
1733         for (i = 0; i < cpathc; i++) {
1734             size += (int)JLI_StrLen(home) + (int)JLI_StrLen(cpathv[i]) + 1; /* 1: separator */
1735         }
1736         appcp = (char *)JLI_MemAlloc(size + 1);
1737         JLI_StrCpy(appcp, "-Djava.class.path=");
1738         for (i = 0; i < cpathc; i++) {
1739             JLI_StrCat(appcp, home);                        /* c:\program files\myapp */
1740             JLI_StrCat(appcp, cpathv[i]);           /* \lib\myapp.jar         */
1741             JLI_StrCat(appcp, separator);           /* ;                      */
1742         }
1743         appcp[JLI_StrLen(appcp)-1] = '\0';  /* remove trailing path separator */
1744         AddOption(appcp, NULL);
1745     }
1746     return JNI_TRUE;
1747 }
1748 
1749 /*
1750  * inject the -Dsun.java.command pseudo property into the args structure
1751  * this pseudo property is used in the HotSpot VM to expose the
1752  * Java class name and arguments to the main method to the VM. The
1753  * HotSpot VM uses this pseudo property to store the Java class name
1754  * (or jar file name) and the arguments to the class's main method
1755  * to the instrumentation memory region. The sun.java.command pseudo
1756  * property is not exported by HotSpot to the Java layer.
1757  */
1758 void
1759 SetJavaCommandLineProp(char *what, int argc, char **argv)
1760 {
1761 
1762     int i = 0;
1763     size_t len = 0;
1764     char* javaCommand = NULL;
1765     char* dashDstr = "-Dsun.java.command=";
1766 
1767     if (what == NULL) {
1768         /* unexpected, one of these should be set. just return without
1769          * setting the property
1770          */
1771         return;
1772     }
1773 
1774     /* determine the amount of memory to allocate assuming
1775      * the individual components will be space separated
1776      */
1777     len = JLI_StrLen(what);
1778     for (i = 0; i < argc; i++) {
1779         len += JLI_StrLen(argv[i]) + 1;
1780     }
1781 
1782     /* allocate the memory */
1783     javaCommand = (char*) JLI_MemAlloc(len + JLI_StrLen(dashDstr) + 1);
1784 
1785     /* build the -D string */
1786     *javaCommand = '\0';
1787     JLI_StrCat(javaCommand, dashDstr);
1788     JLI_StrCat(javaCommand, what);
1789 
1790     for (i = 0; i < argc; i++) {
1791         /* the components of the string are space separated. In
1792          * the case of embedded white space, the relationship of
1793          * the white space separated components to their true
1794          * positional arguments will be ambiguous. This issue may
1795          * be addressed in a future release.
1796          */
1797         JLI_StrCat(javaCommand, " ");
1798         JLI_StrCat(javaCommand, argv[i]);
1799     }
1800 
1801     AddOption(javaCommand, NULL);
1802 }
1803 
1804 /*
1805  * JVM would like to know if it's created by a standard Sun launcher, or by
1806  * user native application, the following property indicates the former.
1807  */
1808 void
1809 SetJavaLauncherProp() {
1810   AddOption("-Dsun.java.launcher=SUN_STANDARD", NULL);
1811 }
1812 
1813 /*
1814  * Prints the version information from the java.version and other properties.
1815  */
1816 static void
1817 PrintJavaVersion(JNIEnv *env, jboolean extraLF)
1818 {
1819     jclass ver;
1820     jmethodID print;
1821 
1822     NULL_CHECK(ver = FindBootStrapClass(env, "java/lang/VersionProps"));
1823     NULL_CHECK(print = (*env)->GetStaticMethodID(env,
1824                                                  ver,
1825                                                  (extraLF == JNI_TRUE) ? "println" : "print",
1826                                                  "(Z)V"
1827                                                  )
1828               );
1829 
1830     (*env)->CallStaticVoidMethod(env, ver, print, printTo);
1831 }
1832 
1833 /*
1834  * Prints all the Java settings, see the java implementation for more details.
1835  */
1836 static void
1837 ShowSettings(JNIEnv *env, char *optString)
1838 {
1839     jmethodID showSettingsID;
1840     jstring joptString;
1841     jclass cls = GetLauncherHelperClass(env);
1842     NULL_CHECK(cls);
1843     NULL_CHECK(showSettingsID = (*env)->GetStaticMethodID(env, cls,
1844             "showSettings", "(ZLjava/lang/String;JJJ)V"));
1845     NULL_CHECK(joptString = (*env)->NewStringUTF(env, optString));
1846     (*env)->CallStaticVoidMethod(env, cls, showSettingsID,
1847                                  USE_STDERR,
1848                                  joptString,
1849                                  (jlong)initialHeapSize,
1850                                  (jlong)maxHeapSize,
1851                                  (jlong)threadStackSize);
1852 }
1853 
1854 /**
1855  * Show resolved modules
1856  */
1857 static void
1858 ShowResolvedModules(JNIEnv *env)
1859 {
1860     jmethodID showResolvedModulesID;
1861     jclass cls = GetLauncherHelperClass(env);
1862     NULL_CHECK(cls);
1863     NULL_CHECK(showResolvedModulesID = (*env)->GetStaticMethodID(env, cls,
1864             "showResolvedModules", "()V"));
1865     (*env)->CallStaticVoidMethod(env, cls, showResolvedModulesID);
1866 }
1867 
1868 /**
1869  * List observable modules
1870  */
1871 static void
1872 ListModules(JNIEnv *env)
1873 {
1874     jmethodID listModulesID;
1875     jclass cls = GetLauncherHelperClass(env);
1876     NULL_CHECK(cls);
1877     NULL_CHECK(listModulesID = (*env)->GetStaticMethodID(env, cls,
1878             "listModules", "()V"));
1879     (*env)->CallStaticVoidMethod(env, cls, listModulesID);
1880 }
1881 
1882 /**
1883  * Describe a module
1884  */
1885 static void
1886 DescribeModule(JNIEnv *env, char *optString)
1887 {
1888     jmethodID describeModuleID;
1889     jstring joptString = NULL;
1890     jclass cls = GetLauncherHelperClass(env);
1891     NULL_CHECK(cls);
1892     NULL_CHECK(describeModuleID = (*env)->GetStaticMethodID(env, cls,
1893             "describeModule", "(Ljava/lang/String;)V"));
1894     NULL_CHECK(joptString = (*env)->NewStringUTF(env, optString));
1895     (*env)->CallStaticVoidMethod(env, cls, describeModuleID, joptString);
1896 }
1897 
1898 /**
1899  * Validate modules
1900  */
1901 static jboolean
1902 ValidateModules(JNIEnv *env)
1903 {
1904     jmethodID validateModulesID;
1905     jclass cls = GetLauncherHelperClass(env);
1906     NULL_CHECK_RETURN_VALUE(cls, JNI_FALSE);
1907     validateModulesID = (*env)->GetStaticMethodID(env, cls, "validateModules", "()Z");
1908     NULL_CHECK_RETURN_VALUE(cls, JNI_FALSE);
1909     return (*env)->CallStaticBooleanMethod(env, cls, validateModulesID);
1910 }
1911 
1912 /*
1913  * Prints default usage or the Xusage message, see sun.launcher.LauncherHelper.java
1914  */
1915 static void
1916 PrintUsage(JNIEnv* env, jboolean doXUsage)
1917 {
1918   jmethodID initHelp, vmSelect, vmSynonym, printHelp, printXUsageMessage;
1919   jstring jprogname, vm1, vm2;
1920   int i;
1921   jclass cls = GetLauncherHelperClass(env);
1922   NULL_CHECK(cls);
1923   if (doXUsage) {
1924     NULL_CHECK(printXUsageMessage = (*env)->GetStaticMethodID(env, cls,
1925                                         "printXUsageMessage", "(Z)V"));
1926     (*env)->CallStaticVoidMethod(env, cls, printXUsageMessage, printTo);
1927   } else {
1928     NULL_CHECK(initHelp = (*env)->GetStaticMethodID(env, cls,
1929                                         "initHelpMessage", "(Ljava/lang/String;)V"));
1930 
1931     NULL_CHECK(vmSelect = (*env)->GetStaticMethodID(env, cls, "appendVmSelectMessage",
1932                                         "(Ljava/lang/String;Ljava/lang/String;)V"));
1933 
1934     NULL_CHECK(vmSynonym = (*env)->GetStaticMethodID(env, cls,
1935                                         "appendVmSynonymMessage",
1936                                         "(Ljava/lang/String;Ljava/lang/String;)V"));
1937 
1938     NULL_CHECK(printHelp = (*env)->GetStaticMethodID(env, cls,
1939                                         "printHelpMessage", "(Z)V"));
1940 
1941     NULL_CHECK(jprogname = (*env)->NewStringUTF(env, _program_name));
1942 
1943     /* Initialize the usage message with the usual preamble */
1944     (*env)->CallStaticVoidMethod(env, cls, initHelp, jprogname);
1945     CHECK_EXCEPTION_RETURN();
1946 
1947 
1948     /* Assemble the other variant part of the usage */
1949     for (i=1; i<knownVMsCount; i++) {
1950       if (knownVMs[i].flag == VM_KNOWN) {
1951         NULL_CHECK(vm1 =  (*env)->NewStringUTF(env, knownVMs[i].name));
1952         NULL_CHECK(vm2 =  (*env)->NewStringUTF(env, knownVMs[i].name+1));
1953         (*env)->CallStaticVoidMethod(env, cls, vmSelect, vm1, vm2);
1954         CHECK_EXCEPTION_RETURN();
1955       }
1956     }
1957     for (i=1; i<knownVMsCount; i++) {
1958       if (knownVMs[i].flag == VM_ALIASED_TO) {
1959         NULL_CHECK(vm1 =  (*env)->NewStringUTF(env, knownVMs[i].name));
1960         NULL_CHECK(vm2 =  (*env)->NewStringUTF(env, knownVMs[i].alias+1));
1961         (*env)->CallStaticVoidMethod(env, cls, vmSynonym, vm1, vm2);
1962         CHECK_EXCEPTION_RETURN();
1963       }
1964     }
1965 
1966     /* Complete the usage message and print to stderr*/
1967     (*env)->CallStaticVoidMethod(env, cls, printHelp, printTo);
1968   }
1969   return;
1970 }
1971 
1972 /*
1973  * Read the jvm.cfg file and fill the knownJVMs[] array.
1974  *
1975  * The functionality of the jvm.cfg file is subject to change without
1976  * notice and the mechanism will be removed in the future.
1977  *
1978  * The lexical structure of the jvm.cfg file is as follows:
1979  *
1980  *     jvmcfg         :=  { vmLine }
1981  *     vmLine         :=  knownLine
1982  *                    |   aliasLine
1983  *                    |   warnLine
1984  *                    |   ignoreLine
1985  *                    |   errorLine
1986  *                    |   predicateLine
1987  *                    |   commentLine
1988  *     knownLine      :=  flag  "KNOWN"                  EOL
1989  *     warnLine       :=  flag  "WARN"                   EOL
1990  *     ignoreLine     :=  flag  "IGNORE"                 EOL
1991  *     errorLine      :=  flag  "ERROR"                  EOL
1992  *     aliasLine      :=  flag  "ALIASED_TO"       flag  EOL
1993  *     predicateLine  :=  flag  "IF_SERVER_CLASS"  flag  EOL
1994  *     commentLine    :=  "#" text                       EOL
1995  *     flag           :=  "-" identifier
1996  *
1997  * The semantics are that when someone specifies a flag on the command line:
1998  * - if the flag appears on a knownLine, then the identifier is used as
1999  *   the name of the directory holding the JVM library (the name of the JVM).
2000  * - if the flag appears as the first flag on an aliasLine, the identifier
2001  *   of the second flag is used as the name of the JVM.
2002  * - if the flag appears on a warnLine, the identifier is used as the
2003  *   name of the JVM, but a warning is generated.
2004  * - if the flag appears on an ignoreLine, the identifier is recognized as the
2005  *   name of a JVM, but the identifier is ignored and the default vm used
2006  * - if the flag appears on an errorLine, an error is generated.
2007  * - if the flag appears as the first flag on a predicateLine, and
2008  *   the machine on which you are running passes the predicate indicated,
2009  *   then the identifier of the second flag is used as the name of the JVM,
2010  *   otherwise the identifier of the first flag is used as the name of the JVM.
2011  * If no flag is given on the command line, the first vmLine of the jvm.cfg
2012  * file determines the name of the JVM.
2013  * PredicateLines are only interpreted on first vmLine of a jvm.cfg file,
2014  * since they only make sense if someone hasn't specified the name of the
2015  * JVM on the command line.
2016  *
2017  * The intent of the jvm.cfg file is to allow several JVM libraries to
2018  * be installed in different subdirectories of a single JRE installation,
2019  * for space-savings and convenience in testing.
2020  * The intent is explicitly not to provide a full aliasing or predicate
2021  * mechanism.
2022  */
2023 jint
2024 ReadKnownVMs(const char *jvmCfgName, jboolean speculative)
2025 {
2026     FILE *jvmCfg;
2027     char line[MAXPATHLEN+20];
2028     int cnt = 0;
2029     int lineno = 0;
2030     jlong start, end;
2031     int vmType;
2032     char *tmpPtr;
2033     char *altVMName = NULL;
2034     char *serverClassVMName = NULL;
2035     static char *whiteSpace = " \t";
2036     if (JLI_IsTraceLauncher()) {
2037         start = CounterGet();
2038     }
2039 
2040     jvmCfg = fopen(jvmCfgName, "r");
2041     if (jvmCfg == NULL) {
2042       if (!speculative) {
2043         JLI_ReportErrorMessage(CFG_ERROR6, jvmCfgName);
2044         exit(1);
2045       } else {
2046         return -1;
2047       }
2048     }
2049     while (fgets(line, sizeof(line), jvmCfg) != NULL) {
2050         vmType = VM_UNKNOWN;
2051         lineno++;
2052         if (line[0] == '#')
2053             continue;
2054         if (line[0] != '-') {
2055             JLI_ReportErrorMessage(CFG_WARN2, lineno, jvmCfgName);
2056         }
2057         if (cnt >= knownVMsLimit) {
2058             GrowKnownVMs(cnt);
2059         }
2060         line[JLI_StrLen(line)-1] = '\0'; /* remove trailing newline */
2061         tmpPtr = line + JLI_StrCSpn(line, whiteSpace);
2062         if (*tmpPtr == 0) {
2063             JLI_ReportErrorMessage(CFG_WARN3, lineno, jvmCfgName);
2064         } else {
2065             /* Null-terminate this string for JLI_StringDup below */
2066             *tmpPtr++ = 0;
2067             tmpPtr += JLI_StrSpn(tmpPtr, whiteSpace);
2068             if (*tmpPtr == 0) {
2069                 JLI_ReportErrorMessage(CFG_WARN3, lineno, jvmCfgName);
2070             } else {
2071                 if (!JLI_StrCCmp(tmpPtr, "KNOWN")) {
2072                     vmType = VM_KNOWN;
2073                 } else if (!JLI_StrCCmp(tmpPtr, "ALIASED_TO")) {
2074                     tmpPtr += JLI_StrCSpn(tmpPtr, whiteSpace);
2075                     if (*tmpPtr != 0) {
2076                         tmpPtr += JLI_StrSpn(tmpPtr, whiteSpace);
2077                     }
2078                     if (*tmpPtr == 0) {
2079                         JLI_ReportErrorMessage(CFG_WARN3, lineno, jvmCfgName);
2080                     } else {
2081                         /* Null terminate altVMName */
2082                         altVMName = tmpPtr;
2083                         tmpPtr += JLI_StrCSpn(tmpPtr, whiteSpace);
2084                         *tmpPtr = 0;
2085                         vmType = VM_ALIASED_TO;
2086                     }
2087                 } else if (!JLI_StrCCmp(tmpPtr, "WARN")) {
2088                     vmType = VM_WARN;
2089                 } else if (!JLI_StrCCmp(tmpPtr, "IGNORE")) {
2090                     vmType = VM_IGNORE;
2091                 } else if (!JLI_StrCCmp(tmpPtr, "ERROR")) {
2092                     vmType = VM_ERROR;
2093                 } else if (!JLI_StrCCmp(tmpPtr, "IF_SERVER_CLASS")) {
2094                     /* ignored */
2095                 } else {
2096                     JLI_ReportErrorMessage(CFG_WARN5, lineno, &jvmCfgName[0]);
2097                     vmType = VM_KNOWN;
2098                 }
2099             }
2100         }
2101 
2102         JLI_TraceLauncher("jvm.cfg[%d] = ->%s<-\n", cnt, line);
2103         if (vmType != VM_UNKNOWN) {
2104             knownVMs[cnt].name = JLI_StringDup(line);
2105             knownVMs[cnt].flag = vmType;
2106             switch (vmType) {
2107             default:
2108                 break;
2109             case VM_ALIASED_TO:
2110                 knownVMs[cnt].alias = JLI_StringDup(altVMName);
2111                 JLI_TraceLauncher("    name: %s  vmType: %s  alias: %s\n",
2112                    knownVMs[cnt].name, "VM_ALIASED_TO", knownVMs[cnt].alias);
2113                 break;
2114             }
2115             cnt++;
2116         }
2117     }
2118     fclose(jvmCfg);
2119     knownVMsCount = cnt;
2120 
2121     if (JLI_IsTraceLauncher()) {
2122         end   = CounterGet();
2123         printf("%ld micro seconds to parse jvm.cfg\n",
2124                (long)(jint)Counter2Micros(end-start));
2125     }
2126 
2127     return cnt;
2128 }
2129 
2130 
2131 static void
2132 GrowKnownVMs(int minimum)
2133 {
2134     struct vmdesc* newKnownVMs;
2135     int newMax;
2136 
2137     newMax = (knownVMsLimit == 0 ? INIT_MAX_KNOWN_VMS : (2 * knownVMsLimit));
2138     if (newMax <= minimum) {
2139         newMax = minimum;
2140     }
2141     newKnownVMs = (struct vmdesc*) JLI_MemAlloc(newMax * sizeof(struct vmdesc));
2142     if (knownVMs != NULL) {
2143         memcpy(newKnownVMs, knownVMs, knownVMsLimit * sizeof(struct vmdesc));
2144     }
2145     JLI_MemFree(knownVMs);
2146     knownVMs = newKnownVMs;
2147     knownVMsLimit = newMax;
2148 }
2149 
2150 
2151 /* Returns index of VM or -1 if not found */
2152 static int
2153 KnownVMIndex(const char* name)
2154 {
2155     int i;
2156     if (JLI_StrCCmp(name, "-J") == 0) name += 2;
2157     for (i = 0; i < knownVMsCount; i++) {
2158         if (!JLI_StrCmp(name, knownVMs[i].name)) {
2159             return i;
2160         }
2161     }
2162     return -1;
2163 }
2164 
2165 static void
2166 FreeKnownVMs()
2167 {
2168     int i;
2169     for (i = 0; i < knownVMsCount; i++) {
2170         JLI_MemFree(knownVMs[i].name);
2171         knownVMs[i].name = NULL;
2172     }
2173     JLI_MemFree(knownVMs);
2174 }
2175 
2176 /*
2177  * Displays the splash screen according to the jar file name
2178  * and image file names stored in environment variables
2179  */
2180 void
2181 ShowSplashScreen()
2182 {
2183     const char *jar_name = getenv(SPLASH_JAR_ENV_ENTRY);
2184     const char *file_name = getenv(SPLASH_FILE_ENV_ENTRY);
2185     int data_size;
2186     void *image_data = NULL;
2187     float scale_factor = 1;
2188     char *scaled_splash_name = NULL;
2189     jboolean isImageScaled = JNI_FALSE;
2190     size_t maxScaledImgNameLength = 0;
2191     if (file_name == NULL){
2192         return;
2193     }
2194     maxScaledImgNameLength = DoSplashGetScaledImgNameMaxPstfixLen(file_name);
2195 
2196     scaled_splash_name = JLI_MemAlloc(
2197                             maxScaledImgNameLength * sizeof(char));
2198     isImageScaled = DoSplashGetScaledImageName(jar_name, file_name,
2199                             &scale_factor,
2200                             scaled_splash_name, maxScaledImgNameLength);
2201     if (jar_name) {
2202 
2203         if (isImageScaled) {
2204             image_data = JLI_JarUnpackFile(
2205                     jar_name, scaled_splash_name, &data_size);
2206         }
2207 
2208         if (!image_data) {
2209             scale_factor = 1;
2210             image_data = JLI_JarUnpackFile(
2211                             jar_name, file_name, &data_size);
2212         }
2213         if (image_data) {
2214             DoSplashInit();
2215             DoSplashSetScaleFactor(scale_factor);
2216             DoSplashLoadMemory(image_data, data_size);
2217             JLI_MemFree(image_data);
2218         }
2219     } else {
2220         DoSplashInit();
2221         if (isImageScaled) {
2222             DoSplashSetScaleFactor(scale_factor);
2223             DoSplashLoadFile(scaled_splash_name);
2224         } else {
2225             DoSplashLoadFile(file_name);
2226         }
2227     }
2228     JLI_MemFree(scaled_splash_name);
2229 
2230     DoSplashSetFileJarName(file_name, jar_name);
2231 
2232     /*
2233      * Done with all command line processing and potential re-execs so
2234      * clean up the environment.
2235      */
2236     (void)UnsetEnv(ENV_ENTRY);
2237     (void)UnsetEnv(SPLASH_FILE_ENV_ENTRY);
2238     (void)UnsetEnv(SPLASH_JAR_ENV_ENTRY);
2239 
2240     JLI_MemFree(splash_jar_entry);
2241     JLI_MemFree(splash_file_entry);
2242 
2243 }
2244 
2245 const char*
2246 GetFullVersion()
2247 {
2248     return _fVersion;
2249 }
2250 
2251 const char*
2252 GetProgramName()
2253 {
2254     return _program_name;
2255 }
2256 
2257 const char*
2258 GetLauncherName()
2259 {
2260     return _launcher_name;
2261 }
2262 
2263 jboolean
2264 IsJavaArgs()
2265 {
2266     return _is_java_args;
2267 }
2268 
2269 static jboolean
2270 IsWildCardEnabled()
2271 {
2272     return _wc_enabled;
2273 }
2274 
2275 int
2276 ContinueInNewThread(InvocationFunctions* ifn, jlong threadStackSize,
2277                     int argc, char **argv,
2278                     int mode, char *what, int ret)
2279 {
2280 
2281     /*
2282      * If user doesn't specify stack size, check if VM has a preference.
2283      * Note that HotSpot no longer supports JNI_VERSION_1_1 but it will
2284      * return its default stack size through the init args structure.
2285      */
2286     if (threadStackSize == 0) {
2287       struct JDK1_1InitArgs args1_1;
2288       memset((void*)&args1_1, 0, sizeof(args1_1));
2289       args1_1.version = JNI_VERSION_1_1;
2290       ifn->GetDefaultJavaVMInitArgs(&args1_1);  /* ignore return value */
2291       if (args1_1.javaStackSize > 0) {
2292          threadStackSize = args1_1.javaStackSize;
2293       }
2294     }
2295 
2296     { /* Create a new thread to create JVM and invoke main method */
2297       JavaMainArgs args;
2298       int rslt;
2299 
2300       args.argc = argc;
2301       args.argv = argv;
2302       args.mode = mode;
2303       args.what = what;
2304       args.ifn = *ifn;
2305 
2306       rslt = ContinueInNewThread0(JavaMain, threadStackSize, (void*)&args);
2307       /* If the caller has deemed there is an error we
2308        * simply return that, otherwise we return the value of
2309        * the callee
2310        */
2311       return (ret != 0) ? ret : rslt;
2312     }
2313 }
2314 
2315 static void
2316 DumpState()
2317 {
2318     if (!JLI_IsTraceLauncher()) return ;
2319     printf("Launcher state:\n");
2320     printf("\tFirst application arg index: %d\n", JLI_GetAppArgIndex());
2321     printf("\tdebug:%s\n", (JLI_IsTraceLauncher() == JNI_TRUE) ? "on" : "off");
2322     printf("\tjavargs:%s\n", (_is_java_args == JNI_TRUE) ? "on" : "off");
2323     printf("\tprogram name:%s\n", GetProgramName());
2324     printf("\tlauncher name:%s\n", GetLauncherName());
2325     printf("\tjavaw:%s\n", (IsJavaw() == JNI_TRUE) ? "on" : "off");
2326     printf("\tfullversion:%s\n", GetFullVersion());
2327 }
2328 
2329 /*
2330  * A utility procedure to always print to stderr
2331  */
2332 void
2333 JLI_ReportMessage(const char* fmt, ...)
2334 {
2335     va_list vl;
2336     va_start(vl, fmt);
2337     vfprintf(stderr, fmt, vl);
2338     fprintf(stderr, "\n");
2339     va_end(vl);
2340 }
2341 
2342 /*
2343  * A utility procedure to always print to stdout
2344  */
2345 void
2346 JLI_ShowMessage(const char* fmt, ...)
2347 {
2348     va_list vl;
2349     va_start(vl, fmt);
2350     vfprintf(stdout, fmt, vl);
2351     fprintf(stdout, "\n");
2352     va_end(vl);
2353 }