1 /*
   2  * Copyright (c) 2012, 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 #include "java.h"
  26 
  27 /*
  28  * If app is "/foo/bin/javac", or "/foo/bin/sparcv9/javac" then put
  29  * "/foo" into buf.
  30  */
  31 jboolean
  32 GetApplicationHome(char *buf, jint bufsize)
  33 {
  34     const char *execname = GetExecName();
  35     if (execname != NULL) {
  36         JLI_Snprintf(buf, bufsize, "%s", execname);
  37         buf[bufsize-1] = '\0';
  38     } else {
  39         return JNI_FALSE;
  40     }
  41 
  42     if (JLI_StrRChr(buf, '/') == 0) {
  43         buf[0] = '\0';
  44         return JNI_FALSE;
  45     }
  46     *(JLI_StrRChr(buf, '/')) = '\0';    /* executable file      */
  47     if (JLI_StrLen(buf) < 4 || JLI_StrRChr(buf, '/') == 0) {
  48         buf[0] = '\0';
  49         return JNI_FALSE;
  50     }
  51     if (JLI_StrCmp("/bin", buf + JLI_StrLen(buf) - 4) != 0)
  52         *(JLI_StrRChr(buf, '/')) = '\0';        /* sparcv9 or amd64     */
  53     if (JLI_StrLen(buf) < 4 || JLI_StrCmp("/bin", buf + JLI_StrLen(buf) - 4) != 0) {
  54         buf[0] = '\0';
  55         return JNI_FALSE;
  56     }
  57     *(JLI_StrRChr(buf, '/')) = '\0';    /* bin                  */
  58 
  59     return JNI_TRUE;
  60 }
  61 /*
  62  * Return true if the named program exists
  63  */
  64 static int
  65 ProgramExists(char *name)
  66 {
  67     struct stat sb;
  68     if (stat(name, &sb) != 0) return 0;
  69     if (S_ISDIR(sb.st_mode)) return 0;
  70     return (sb.st_mode & S_IEXEC) != 0;
  71 }
  72 
  73 /*
  74  * Find a command in a directory, returning the path.
  75  */
  76 static char *
  77 Resolve(char *indir, char *cmd)
  78 {
  79     char name[PATH_MAX + 2], *real;
  80 
  81     if ((JLI_StrLen(indir) + JLI_StrLen(cmd) + 1)  > PATH_MAX) return 0;
  82     JLI_Snprintf(name, sizeof(name), "%s%c%s", indir, FILE_SEPARATOR, cmd);
  83     if (!ProgramExists(name)) return 0;
  84     real = JLI_MemAlloc(PATH_MAX + 2);
  85     if (!realpath(name, real))
  86         JLI_StrCpy(real, name);
  87     return real;
  88 }
  89 
  90 /*
  91  * Find a path for the executable
  92  */
  93 char *
  94 FindExecName(char *program)
  95 {
  96     char cwdbuf[PATH_MAX+2];
  97     char *path;
  98     char *tmp_path;
  99     char *f;
 100     char *result = NULL;
 101 
 102     /* absolute path? */
 103     if (*program == FILE_SEPARATOR ||
 104         (FILE_SEPARATOR=='\\' && JLI_StrRChr(program, ':')))
 105         return Resolve("", program+1);
 106 
 107     /* relative path? */
 108     if (JLI_StrRChr(program, FILE_SEPARATOR) != 0) {
 109         char buf[PATH_MAX+2];
 110         return Resolve(getcwd(cwdbuf, sizeof(cwdbuf)), program);
 111     }
 112 
 113     /* from search path? */
 114     path = getenv("PATH");
 115     if (!path || !*path) path = ".";
 116     tmp_path = JLI_MemAlloc(JLI_StrLen(path) + 2);
 117     JLI_StrCpy(tmp_path, path);
 118 
 119     for (f=tmp_path; *f && result==0; ) {
 120         char *s = f;
 121         while (*f && (*f != PATH_SEPARATOR)) ++f;
 122         if (*f) *f++ = 0;
 123         if (*s == FILE_SEPARATOR)
 124             result = Resolve(s, program);
 125         else {
 126             /* relative path element */
 127             char dir[2*PATH_MAX];
 128             JLI_Snprintf(dir, sizeof(dir), "%s%c%s", getcwd(cwdbuf, sizeof(cwdbuf)),
 129                     FILE_SEPARATOR, s);
 130             result = Resolve(dir, program);
 131         }
 132         if (result != 0) break;
 133     }
 134 
 135     JLI_MemFree(tmp_path);
 136     return result;
 137 }
 138 
 139 void JLI_ReportErrorMessage(const char* fmt, ...) {
 140     va_list vl;
 141     va_start(vl, fmt);
 142     vfprintf(stderr, fmt, vl);
 143     fprintf(stderr, "\n");
 144     va_end(vl);
 145 }
 146 
 147 void JLI_ReportErrorMessageSys(const char* fmt, ...) {
 148     va_list vl;
 149     char *emsg;
 150 
 151     /*
 152      * TODO: its safer to use strerror_r but is not available on
 153      * Solaris 8. Until then....
 154      */
 155     emsg = strerror(errno);
 156     if (emsg != NULL) {
 157         fprintf(stderr, "%s\n", emsg);
 158     }
 159 
 160     va_start(vl, fmt);
 161     vfprintf(stderr, fmt, vl);
 162     fprintf(stderr, "\n");
 163     va_end(vl);
 164 }
 165 
 166 void  JLI_ReportExceptionDescription(JNIEnv * env) {
 167   (*env)->ExceptionDescribe(env);
 168 }
 169 
 170 /*
 171  *      Since using the file system as a registry is a bit risky, perform
 172  *      additional sanity checks on the identified directory to validate
 173  *      it as a valid jre/sdk.
 174  *
 175  *      Return 0 if the tests fail; otherwise return non-zero (true).
 176  *
 177  *      Note that checking for anything more than the existence of an
 178  *      executable object at bin/java relative to the path being checked
 179  *      will break the regression tests.
 180  */
 181 static int
 182 CheckSanity(char *path, char *dir)
 183 {
 184     char    buffer[PATH_MAX];
 185 
 186     if (JLI_StrLen(path) + JLI_StrLen(dir) + 11 > PATH_MAX)
 187         return (0);     /* Silently reject "impossibly" long paths */
 188 
 189     JLI_Snprintf(buffer, sizeof(buffer), "%s/%s/bin/java", path, dir);
 190     return ((access(buffer, X_OK) == 0) ? 1 : 0);
 191 }
 192 
 193 /*
 194  *      Determine if there is an acceptable JRE in the directory dirname.
 195  *      Upon locating the "best" one, return a fully qualified path to
 196  *      it. "Best" is defined as the most advanced JRE meeting the
 197  *      constraints contained in the manifest_info. If no JRE in this
 198  *      directory meets the constraints, return NULL.
 199  *
 200  *      Note that we don't check for errors in reading the directory
 201  *      (which would be done by checking errno).  This is because it
 202  *      doesn't matter if we get an error reading the directory, or
 203  *      we just don't find anything interesting in the directory.  We
 204  *      just return NULL in either case.
 205  *
 206  *      The historical names of j2sdk and j2re were changed to jdk and
 207  *      jre respecively as part of the 1.5 rebranding effort.  Since the
 208  *      former names are legacy on Linux, they must be recognized for
 209  *      all time.  Fortunately, this is a minor cost.
 210  */
 211 static char
 212 *ProcessDir(manifest_info *info, char *dirname)
 213 {
 214     DIR     *dirp;
 215     struct dirent *dp;
 216     char    *best = NULL;
 217     int     offset;
 218     int     best_offset = 0;
 219     char    *ret_str = NULL;
 220     char    buffer[PATH_MAX];
 221 
 222     if ((dirp = opendir(dirname)) == NULL)
 223         return (NULL);
 224 
 225     do {
 226         if ((dp = readdir(dirp)) != NULL) {
 227             offset = 0;
 228             if ((JLI_StrNCmp(dp->d_name, "jre", 3) == 0) ||
 229                 (JLI_StrNCmp(dp->d_name, "jdk", 3) == 0))
 230                 offset = 3;
 231             else if (JLI_StrNCmp(dp->d_name, "j2re", 4) == 0)
 232                 offset = 4;
 233             else if (JLI_StrNCmp(dp->d_name, "j2sdk", 5) == 0)
 234                 offset = 5;
 235             if (offset > 0) {
 236                 if ((JLI_AcceptableRelease(dp->d_name + offset,
 237                     info->jre_version)) && CheckSanity(dirname, dp->d_name))
 238                     if ((best == NULL) || (JLI_ExactVersionId(
 239                       dp->d_name + offset, best + best_offset) > 0)) {
 240                         if (best != NULL)
 241                             JLI_MemFree(best);
 242                         best = JLI_StringDup(dp->d_name);
 243                         best_offset = offset;
 244                     }
 245             }
 246         }
 247     } while (dp != NULL);
 248     (void) closedir(dirp);
 249     if (best == NULL)
 250         return (NULL);
 251     else {
 252         ret_str = JLI_MemAlloc(JLI_StrLen(dirname) + JLI_StrLen(best) + 2);
 253         sprintf(ret_str, "%s/%s", dirname, best);
 254         JLI_MemFree(best);
 255         return (ret_str);
 256     }
 257 }
 258 
 259 /*
 260  *      This is the global entry point. It examines the host for the optimal
 261  *      JRE to be used by scanning a set of directories.  The set of directories
 262  *      is platform dependent and can be overridden by the environment
 263  *      variable JAVA_VERSION_PATH.
 264  *
 265  *      This routine itself simply determines the set of appropriate
 266  *      directories before passing control onto ProcessDir().
 267  */
 268 char*
 269 LocateJRE(manifest_info* info)
 270 {
 271     char        *path;
 272     char        *home;
 273     char        *target = NULL;
 274     char        *dp;
 275     char        *cp;
 276 
 277     /*
 278      * Start by getting JAVA_VERSION_PATH
 279      */
 280     if (info->jre_restrict_search) {
 281         path = JLI_StringDup(system_dir);
 282     } else if ((path = getenv("JAVA_VERSION_PATH")) != NULL) {
 283         path = JLI_StringDup(path);
 284     } else {
 285         if ((home = getenv("HOME")) != NULL) {
 286             path = (char *)JLI_MemAlloc(JLI_StrLen(home) + \
 287                         JLI_StrLen(system_dir) + JLI_StrLen(user_dir) + 2);
 288             sprintf(path, "%s%s:%s", home, user_dir, system_dir);
 289         } else {
 290             path = JLI_StringDup(system_dir);
 291         }
 292     }
 293 
 294     /*
 295      * Step through each directory on the path. Terminate the scan with
 296      * the first directory with an acceptable JRE.
 297      */
 298     cp = dp = path;
 299     while (dp != NULL) {
 300         cp = JLI_StrChr(dp, (int)':');
 301         if (cp != NULL)
 302             *cp = '\0';
 303         if ((target = ProcessDir(info, dp)) != NULL)
 304             break;
 305         dp = cp;
 306         if (dp != NULL)
 307             dp++;
 308     }
 309     JLI_MemFree(path);
 310     return (target);
 311 }
 312 
 313 /*
 314  * Given a path to a jre to execute, this routine checks if this process
 315  * is indeed that jre.  If not, it exec's that jre.
 316  *
 317  * We want to actually check the paths rather than just the version string
 318  * built into the executable, so that given version specification (and
 319  * JAVA_VERSION_PATH) will yield the exact same Java environment, regardless
 320  * of the version of the arbitrary launcher we start with.
 321  */
 322 void
 323 ExecJRE(char *jre, char **argv)
 324 {
 325     char    wanted[PATH_MAX];
 326     const char* progname = GetProgramName();
 327     const char* execname = NULL;
 328 
 329     /*
 330      * Resolve the real path to the directory containing the selected JRE.
 331      */
 332     if (realpath(jre, wanted) == NULL) {
 333         JLI_ReportErrorMessage(JRE_ERROR9, jre);
 334         exit(1);
 335     }
 336 
 337     /*
 338      * Resolve the real path to the currently running launcher.
 339      */
 340     SetExecname(argv);
 341     execname = GetExecName();
 342     if (execname == NULL) {
 343         JLI_ReportErrorMessage(JRE_ERROR10);
 344         exit(1);
 345     }
 346 
 347     /*
 348      * If the path to the selected JRE directory is a match to the initial
 349      * portion of the path to the currently executing JRE, we have a winner!
 350      * If so, just return.
 351      */
 352     if (JLI_StrNCmp(wanted, execname, JLI_StrLen(wanted)) == 0)
 353         return;                 /* I am the droid you were looking for */
 354 
 355 
 356     /*
 357      * This should never happen (because of the selection code in SelectJRE),
 358      * but check for "impossibly" long path names just because buffer overruns
 359      * can be so deadly.
 360      */
 361     if (JLI_StrLen(wanted) + JLI_StrLen(progname) + 6 > PATH_MAX) {
 362         JLI_ReportErrorMessage(JRE_ERROR11);
 363         exit(1);
 364     }
 365 
 366     /*
 367      * Construct the path and exec it.
 368      */
 369     (void)JLI_StrCat(JLI_StrCat(wanted, "/bin/"), progname);
 370     argv[0] = JLI_StringDup(progname);
 371     if (JLI_IsTraceLauncher()) {
 372         int i;
 373         printf("ReExec Command: %s (%s)\n", wanted, argv[0]);
 374         printf("ReExec Args:");
 375         for (i = 1; argv[i] != NULL; i++)
 376             printf(" %s", argv[i]);
 377         printf("\n");
 378     }
 379     JLI_TraceLauncher("TRACER_MARKER:About to EXEC\n");
 380     (void)fflush(stdout);
 381     (void)fflush(stderr);
 382     execv(wanted, argv);
 383     JLI_ReportErrorMessageSys(JRE_ERROR12, wanted);
 384     exit(1);
 385 }
 386 
 387 /*
 388  * "Borrowed" from Solaris 10 where the unsetenv() function is being added
 389  * to libc thanks to SUSv3 (Standard Unix Specification, version 3). As
 390  * such, in the fullness of time this will appear in libc on all relevant
 391  * Solaris/Linux platforms and maybe even the Windows platform.  At that
 392  * time, this stub can be removed.
 393  *
 394  * This implementation removes the environment locking for multithreaded
 395  * applications.  (We don't have access to these mutexes within libc and
 396  * the launcher isn't multithreaded.)  Note that what remains is platform
 397  * independent, because it only relies on attributes that a POSIX environment
 398  * defines.
 399  *
 400  * Returns 0 on success, -1 on failure.
 401  *
 402  * Also removed was the setting of errno.  The only value of errno set
 403  * was EINVAL ("Invalid Argument").
 404  */
 405 
 406 /*
 407  * s1(environ) is name=value
 408  * s2(name) is name(not the form of name=value).
 409  * if names match, return value of 1, else return 0
 410  */
 411 static int
 412 match_noeq(const char *s1, const char *s2)
 413 {
 414         while (*s1 == *s2++) {
 415                 if (*s1++ == '=')
 416                         return (1);
 417         }
 418         if (*s1 == '=' && s2[-1] == '\0')
 419                 return (1);
 420         return (0);
 421 }
 422 
 423 /*
 424  * added for SUSv3 standard
 425  *
 426  * Delete entry from environ.
 427  * Do not free() memory!  Other threads may be using it.
 428  * Keep it around forever.
 429  */
 430 static int
 431 borrowed_unsetenv(const char *name)
 432 {
 433         long    idx;            /* index into environ */
 434 
 435         if (name == NULL || *name == '\0' ||
 436             JLI_StrChr(name, '=') != NULL) {
 437                 return (-1);
 438         }
 439 
 440         for (idx = 0; environ[idx] != NULL; idx++) {
 441                 if (match_noeq(environ[idx], name))
 442                         break;
 443         }
 444         if (environ[idx] == NULL) {
 445                 /* name not found but still a success */
 446                 return (0);
 447         }
 448         /* squeeze up one entry */
 449         do {
 450                 environ[idx] = environ[idx+1];
 451         } while (environ[++idx] != NULL);
 452 
 453         return (0);
 454 }
 455 /* --- End of "borrowed" code --- */
 456 
 457 /*
 458  * Wrapper for unsetenv() function.
 459  */
 460 int
 461 UnsetEnv(char *name)
 462 {
 463     return(borrowed_unsetenv(name));
 464 }
 465 
 466 const char *
 467 jlong_format_specifier() {
 468     return "%lld";
 469 }
 470 
 471 jboolean
 472 IsJavaw()
 473 {
 474     /* noop on UNIX */
 475     return JNI_FALSE;
 476 }
 477 
 478 void
 479 InitLauncher(jboolean javaw)
 480 {
 481     JLI_SetTraceLauncher();
 482 }
 483 
 484 /*
 485  * The implementation for finding classes from the bootstrap
 486  * class loader, refer to java.h
 487  */
 488 static FindClassFromBootLoader_t *findBootClass = NULL;
 489 
 490 jclass
 491 FindBootStrapClass(JNIEnv *env, const char* classname)
 492 {
 493    if (findBootClass == NULL) {
 494        findBootClass = (FindClassFromBootLoader_t *)dlsym(RTLD_DEFAULT,
 495           "JVM_FindClassFromBootLoader");
 496        if (findBootClass == NULL) {
 497            JLI_ReportErrorMessage(DLL_ERROR4,
 498                "JVM_FindClassFromBootLoader");
 499            return NULL;
 500        }
 501    }
 502    return findBootClass(env, classname);
 503 }
 504 
 505 StdArg
 506 *JLI_GetStdArgs()
 507 {
 508     return NULL;
 509 }
 510 
 511 int
 512 JLI_GetStdArgc() {
 513     return 0;
 514 }
 515 
 516 jobjectArray
 517 CreateApplicationArgs(JNIEnv *env, char **strv, int argc)
 518 {
 519     return NewPlatformStringArray(env, strv, argc);
 520 }