Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/bin/java_md_common.c
32285 views
/*1* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24#include "java.h"2526/*27* If app is "/foo/bin/javac", or "/foo/bin/sparcv9/javac" then put28* "/foo" into buf.29*/30jboolean31GetApplicationHome(char *buf, jint bufsize)32{33const char *execname = GetExecName();34if (execname != NULL) {35JLI_Snprintf(buf, bufsize, "%s", execname);36buf[bufsize-1] = '\0';37} else {38return JNI_FALSE;39}4041if (JLI_StrRChr(buf, '/') == 0) {42buf[0] = '\0';43return JNI_FALSE;44}45*(JLI_StrRChr(buf, '/')) = '\0'; /* executable file */46if (JLI_StrLen(buf) < 4 || JLI_StrRChr(buf, '/') == 0) {47buf[0] = '\0';48return JNI_FALSE;49}50if (JLI_StrCmp("/bin", buf + JLI_StrLen(buf) - 4) != 0)51*(JLI_StrRChr(buf, '/')) = '\0'; /* sparcv9 or amd64 */52if (JLI_StrLen(buf) < 4 || JLI_StrCmp("/bin", buf + JLI_StrLen(buf) - 4) != 0) {53buf[0] = '\0';54return JNI_FALSE;55}56*(JLI_StrRChr(buf, '/')) = '\0'; /* bin */5758return JNI_TRUE;59}60/*61* Return true if the named program exists62*/63static int64ProgramExists(char *name)65{66struct stat sb;67if (stat(name, &sb) != 0) return 0;68if (S_ISDIR(sb.st_mode)) return 0;69#ifndef __ANDROID__70return (sb.st_mode & S_IEXEC) != 0;71#else72return (sb.st_mode & S_IXUSR) != 0;73#endif74}7576/*77* Find a command in a directory, returning the path.78*/79static char *80Resolve(char *indir, char *cmd)81{82char name[PATH_MAX + 2], *real;8384if ((JLI_StrLen(indir) + JLI_StrLen(cmd) + 1) > PATH_MAX) return 0;85JLI_Snprintf(name, sizeof(name), "%s%c%s", indir, FILE_SEPARATOR, cmd);86if (!ProgramExists(name)) return 0;87real = JLI_MemAlloc(PATH_MAX + 2);88if (!realpath(name, real))89JLI_StrCpy(real, name);90return real;91}9293/*94* Find a path for the executable95*/96char *97FindExecName(char *program)98{99char cwdbuf[PATH_MAX+2];100char *path;101char *tmp_path;102char *f;103char *result = NULL;104105/* absolute path? */106if (*program == FILE_SEPARATOR ||107(FILE_SEPARATOR=='\\' && JLI_StrRChr(program, ':')))108return Resolve("", program+1);109110/* relative path? */111if (JLI_StrRChr(program, FILE_SEPARATOR) != 0) {112char buf[PATH_MAX+2];113return Resolve(getcwd(cwdbuf, sizeof(cwdbuf)), program);114}115116/* from search path? */117path = getenv("PATH");118if (!path || !*path) path = ".";119tmp_path = JLI_MemAlloc(JLI_StrLen(path) + 2);120JLI_StrCpy(tmp_path, path);121122for (f=tmp_path; *f && result==0; ) {123char *s = f;124while (*f && (*f != PATH_SEPARATOR)) ++f;125if (*f) *f++ = 0;126if (*s == FILE_SEPARATOR)127result = Resolve(s, program);128else {129/* relative path element */130char dir[2*PATH_MAX];131JLI_Snprintf(dir, sizeof(dir), "%s%c%s", getcwd(cwdbuf, sizeof(cwdbuf)),132FILE_SEPARATOR, s);133result = Resolve(dir, program);134}135if (result != 0) break;136}137138JLI_MemFree(tmp_path);139return result;140}141142void JLI_ReportErrorMessage(const char* fmt, ...) {143va_list vl;144va_start(vl, fmt);145vfprintf(stderr, fmt, vl);146fprintf(stderr, "\n");147va_end(vl);148}149150void JLI_ReportErrorMessageSys(const char* fmt, ...) {151va_list vl;152char *emsg;153154/*155* TODO: its safer to use strerror_r but is not available on156* Solaris 8. Until then....157*/158emsg = strerror(errno);159if (emsg != NULL) {160fprintf(stderr, "%s\n", emsg);161}162163va_start(vl, fmt);164vfprintf(stderr, fmt, vl);165fprintf(stderr, "\n");166va_end(vl);167}168169void JLI_ReportExceptionDescription(JNIEnv * env) {170(*env)->ExceptionDescribe(env);171}172173/*174* Since using the file system as a registry is a bit risky, perform175* additional sanity checks on the identified directory to validate176* it as a valid jre/sdk.177*178* Return 0 if the tests fail; otherwise return non-zero (true).179*180* Note that checking for anything more than the existence of an181* executable object at bin/java relative to the path being checked182* will break the regression tests.183*/184static int185CheckSanity(char *path, char *dir)186{187char buffer[PATH_MAX];188189if (JLI_StrLen(path) + JLI_StrLen(dir) + 11 > PATH_MAX)190return (0); /* Silently reject "impossibly" long paths */191192JLI_Snprintf(buffer, sizeof(buffer), "%s/%s/bin/java", path, dir);193return ((access(buffer, X_OK) == 0) ? 1 : 0);194}195196/*197* Determine if there is an acceptable JRE in the directory dirname.198* Upon locating the "best" one, return a fully qualified path to199* it. "Best" is defined as the most advanced JRE meeting the200* constraints contained in the manifest_info. If no JRE in this201* directory meets the constraints, return NULL.202*203* Note that we don't check for errors in reading the directory204* (which would be done by checking errno). This is because it205* doesn't matter if we get an error reading the directory, or206* we just don't find anything interesting in the directory. We207* just return NULL in either case.208*209* The historical names of j2sdk and j2re were changed to jdk and210* jre respecively as part of the 1.5 rebranding effort. Since the211* former names are legacy on Linux, they must be recognized for212* all time. Fortunately, this is a minor cost.213*/214static char215*ProcessDir(manifest_info *info, char *dirname)216{217DIR *dirp;218struct dirent *dp;219char *best = NULL;220int offset;221int best_offset = 0;222char *ret_str = NULL;223char buffer[PATH_MAX];224225if ((dirp = opendir(dirname)) == NULL)226return (NULL);227228do {229if ((dp = readdir(dirp)) != NULL) {230offset = 0;231if ((JLI_StrNCmp(dp->d_name, "jre", 3) == 0) ||232(JLI_StrNCmp(dp->d_name, "jdk", 3) == 0))233offset = 3;234else if (JLI_StrNCmp(dp->d_name, "j2re", 4) == 0)235offset = 4;236else if (JLI_StrNCmp(dp->d_name, "j2sdk", 5) == 0)237offset = 5;238if (offset > 0) {239if ((JLI_AcceptableRelease(dp->d_name + offset,240info->jre_version)) && CheckSanity(dirname, dp->d_name))241if ((best == NULL) || (JLI_ExactVersionId(242dp->d_name + offset, best + best_offset) > 0)) {243if (best != NULL)244JLI_MemFree(best);245best = JLI_StringDup(dp->d_name);246best_offset = offset;247}248}249}250} while (dp != NULL);251(void) closedir(dirp);252if (best == NULL)253return (NULL);254else {255ret_str = JLI_MemAlloc(JLI_StrLen(dirname) + JLI_StrLen(best) + 2);256sprintf(ret_str, "%s/%s", dirname, best);257JLI_MemFree(best);258return (ret_str);259}260}261262/*263* This is the global entry point. It examines the host for the optimal264* JRE to be used by scanning a set of directories. The set of directories265* is platform dependent and can be overridden by the environment266* variable JAVA_VERSION_PATH.267*268* This routine itself simply determines the set of appropriate269* directories before passing control onto ProcessDir().270*/271char*272LocateJRE(manifest_info* info)273{274char *path;275char *home;276char *target = NULL;277char *dp;278char *cp;279280/*281* Start by getting JAVA_VERSION_PATH282*/283if (info->jre_restrict_search) {284path = JLI_StringDup(system_dir);285} else if ((path = getenv("JAVA_VERSION_PATH")) != NULL) {286path = JLI_StringDup(path);287} else {288if ((home = getenv("HOME")) != NULL) {289path = (char *)JLI_MemAlloc(JLI_StrLen(home) + \290JLI_StrLen(system_dir) + JLI_StrLen(user_dir) + 2);291sprintf(path, "%s%s:%s", home, user_dir, system_dir);292} else {293path = JLI_StringDup(system_dir);294}295}296297/*298* Step through each directory on the path. Terminate the scan with299* the first directory with an acceptable JRE.300*/301cp = dp = path;302while (dp != NULL) {303cp = JLI_StrChr(dp, (int)':');304if (cp != NULL)305*cp = '\0';306if ((target = ProcessDir(info, dp)) != NULL)307break;308dp = cp;309if (dp != NULL)310dp++;311}312JLI_MemFree(path);313return (target);314}315316/*317* Given a path to a jre to execute, this routine checks if this process318* is indeed that jre. If not, it exec's that jre.319*320* We want to actually check the paths rather than just the version string321* built into the executable, so that given version specification (and322* JAVA_VERSION_PATH) will yield the exact same Java environment, regardless323* of the version of the arbitrary launcher we start with.324*/325void326ExecJRE(char *jre, char **argv)327{328char wanted[PATH_MAX];329const char* progname = GetProgramName();330const char* execname = NULL;331332/*333* Resolve the real path to the directory containing the selected JRE.334*/335if (realpath(jre, wanted) == NULL) {336JLI_ReportErrorMessage(JRE_ERROR9, jre);337exit(1);338}339340/*341* Resolve the real path to the currently running launcher.342*/343SetExecname(argv);344execname = GetExecName();345if (execname == NULL) {346JLI_ReportErrorMessage(JRE_ERROR10);347exit(1);348}349350/*351* If the path to the selected JRE directory is a match to the initial352* portion of the path to the currently executing JRE, we have a winner!353* If so, just return.354*/355if (JLI_StrNCmp(wanted, execname, JLI_StrLen(wanted)) == 0)356return; /* I am the droid you were looking for */357358359/*360* This should never happen (because of the selection code in SelectJRE),361* but check for "impossibly" long path names just because buffer overruns362* can be so deadly.363*/364if (JLI_StrLen(wanted) + JLI_StrLen(progname) + 6 > PATH_MAX) {365JLI_ReportErrorMessage(JRE_ERROR11);366exit(1);367}368369/*370* Construct the path and exec it.371*/372(void)JLI_StrCat(JLI_StrCat(wanted, "/bin/"), progname);373argv[0] = JLI_StringDup(progname);374if (JLI_IsTraceLauncher()) {375int i;376printf("ReExec Command: %s (%s)\n", wanted, argv[0]);377printf("ReExec Args:");378for (i = 1; argv[i] != NULL; i++)379printf(" %s", argv[i]);380printf("\n");381}382JLI_TraceLauncher("TRACER_MARKER:About to EXEC\n");383(void)fflush(stdout);384(void)fflush(stderr);385execv(wanted, argv);386JLI_ReportErrorMessageSys(JRE_ERROR12, wanted);387exit(1);388}389390/*391* "Borrowed" from Solaris 10 where the unsetenv() function is being added392* to libc thanks to SUSv3 (Standard Unix Specification, version 3). As393* such, in the fullness of time this will appear in libc on all relevant394* Solaris/Linux platforms and maybe even the Windows platform. At that395* time, this stub can be removed.396*397* This implementation removes the environment locking for multithreaded398* applications. (We don't have access to these mutexes within libc and399* the launcher isn't multithreaded.) Note that what remains is platform400* independent, because it only relies on attributes that a POSIX environment401* defines.402*403* Returns 0 on success, -1 on failure.404*405* Also removed was the setting of errno. The only value of errno set406* was EINVAL ("Invalid Argument").407*/408409/*410* s1(environ) is name=value411* s2(name) is name(not the form of name=value).412* if names match, return value of 1, else return 0413*/414static int415match_noeq(const char *s1, const char *s2)416{417while (*s1 == *s2++) {418if (*s1++ == '=')419return (1);420}421if (*s1 == '=' && s2[-1] == '\0')422return (1);423return (0);424}425426/*427* added for SUSv3 standard428*429* Delete entry from environ.430* Do not free() memory! Other threads may be using it.431* Keep it around forever.432*/433static int434borrowed_unsetenv(const char *name)435{436long idx; /* index into environ */437438if (name == NULL || *name == '\0' ||439JLI_StrChr(name, '=') != NULL) {440return (-1);441}442443for (idx = 0; environ[idx] != NULL; idx++) {444if (match_noeq(environ[idx], name))445break;446}447if (environ[idx] == NULL) {448/* name not found but still a success */449return (0);450}451/* squeeze up one entry */452do {453environ[idx] = environ[idx+1];454} while (environ[++idx] != NULL);455456return (0);457}458/* --- End of "borrowed" code --- */459460/*461* Wrapper for unsetenv() function.462*/463int464UnsetEnv(char *name)465{466return(borrowed_unsetenv(name));467}468469const char *470jlong_format_specifier() {471return "%lld";472}473474jboolean475IsJavaw()476{477/* noop on UNIX */478return JNI_FALSE;479}480481void482InitLauncher(jboolean javaw)483{484JLI_SetTraceLauncher();485}486487/*488* The implementation for finding classes from the bootstrap489* class loader, refer to java.h490*/491static FindClassFromBootLoader_t *findBootClass = NULL;492493jclass494FindBootStrapClass(JNIEnv *env, const char* classname)495{496if (findBootClass == NULL) {497findBootClass = (FindClassFromBootLoader_t *)dlsym(RTLD_DEFAULT,498"JVM_FindClassFromBootLoader");499if (findBootClass == NULL) {500JLI_ReportErrorMessage(DLL_ERROR4,501"JVM_FindClassFromBootLoader");502return NULL;503}504}505return findBootClass(env, classname);506}507508StdArg509*JLI_GetStdArgs()510{511return NULL;512}513514int515JLI_GetStdArgc() {516return 0;517}518519jobjectArray520CreateApplicationArgs(JNIEnv *env, char **strv, int argc)521{522return NewPlatformStringArray(env, strv, argc);523}524525526