1 /*
   2  * Copyright (c) 2012, 2018, 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  * Find the last occurrence of a string
  29  */
  30 char* findLastPathComponent(char *buffer, const char *comp) {
  31     char* t = buffer;
  32     char* p = NULL;
  33     size_t l = JLI_StrLen(comp);
  34     t = JLI_StrStr(t, comp);
  35 
  36     while (t != NULL) {
  37         p = t;
  38         t += l;
  39         t = JLI_StrStr(t, comp);
  40     }
  41     return p;
  42 }
  43 
  44 /*
  45  * Removes the trailing file name and any intermediate platform
  46  * directories, if any, and its enclosing directory.
  47  * Ex: if a buffer contains "/foo/bin/javac" or "/foo/bin/x64/javac", the
  48  * truncated resulting buffer will contain "/foo".
  49  */
  50 jboolean
  51 TruncatePath(char *buf)
  52 {
  53     // try bin directory, maybe an executable
  54     char *p = findLastPathComponent(buf, "/bin/");
  55     if (p != NULL) {
  56         *p = '\0';
  57         return JNI_TRUE;
  58     }
  59     // try lib directory, maybe a library
  60     p = findLastPathComponent(buf, "/lib/");
  61     if (p != NULL) {
  62         *p = '\0';
  63         return JNI_TRUE;
  64     }
  65     return JNI_FALSE;
  66 }
  67 
  68 /*
  69  * Retrieves the path to the JRE home by locating the executable file
  70  * of the current process and then truncating the path to the executable
  71  */
  72 jboolean
  73 GetApplicationHome(char *buf, jint bufsize)
  74 {
  75     const char *execname = GetExecName();
  76     if (execname != NULL) {
  77         JLI_Snprintf(buf, bufsize, "%s", execname);
  78         buf[bufsize-1] = '\0';
  79     } else {
  80         return JNI_FALSE;
  81     }
  82     return TruncatePath(buf);
  83 }
  84 
  85 /*
  86  * Retrieves the path to the JRE home by locating the
  87  * shared library and then truncating the path to it.
  88  */
  89 jboolean
  90 GetApplicationHomeFromDll(char *buf, jint bufsize)
  91 {
  92     /* try to find ourselves instead */
  93     Dl_info info;
  94     if (dladdr((void*)&GetApplicationHomeFromDll, &info) != 0) {
  95         char *path = realpath(info.dli_fname, buf);
  96         if (path == buf) {
  97             return TruncatePath(buf);
  98         }
  99     }
 100     return JNI_FALSE;
 101 }
 102 
 103 /*
 104  * Return true if the named program exists
 105  */
 106 static int
 107 ProgramExists(char *name)
 108 {
 109     struct stat sb;
 110     if (stat(name, &sb) != 0) return 0;
 111     if (S_ISDIR(sb.st_mode)) return 0;
 112     return (sb.st_mode & S_IEXEC) != 0;
 113 }
 114 
 115 /*
 116  * Find a command in a directory, returning the path.
 117  */
 118 static char *
 119 Resolve(char *indir, char *cmd)
 120 {
 121     char name[PATH_MAX + 2], *real;
 122 
 123     if ((JLI_StrLen(indir) + JLI_StrLen(cmd) + 1)  > PATH_MAX) return 0;
 124     JLI_Snprintf(name, sizeof(name), "%s%c%s", indir, FILE_SEPARATOR, cmd);
 125     if (!ProgramExists(name)) return 0;
 126     real = JLI_MemAlloc(PATH_MAX + 2);
 127     if (!realpath(name, real))
 128         JLI_StrCpy(real, name);
 129     return real;
 130 }
 131 
 132 /*
 133  * Find a path for the executable
 134  */
 135 char *
 136 FindExecName(char *program)
 137 {
 138     char cwdbuf[PATH_MAX+2];
 139     char *path;
 140     char *tmp_path;
 141     char *f;
 142     char *result = NULL;
 143 
 144     /* absolute path? */
 145     if (*program == FILE_SEPARATOR ||
 146         (FILE_SEPARATOR=='\\' && JLI_StrRChr(program, ':')))
 147         return Resolve("", program+1);
 148 
 149     /* relative path? */
 150     if (JLI_StrRChr(program, FILE_SEPARATOR) != 0) {
 151         char buf[PATH_MAX+2];
 152         return Resolve(getcwd(cwdbuf, sizeof(cwdbuf)), program);
 153     }
 154 
 155     /* from search path? */
 156     path = getenv("PATH");
 157     if (!path || !*path) path = ".";
 158     tmp_path = JLI_MemAlloc(JLI_StrLen(path) + 2);
 159     JLI_StrCpy(tmp_path, path);
 160 
 161     for (f=tmp_path; *f && result==0; ) {
 162         char *s = f;
 163         while (*f && (*f != PATH_SEPARATOR)) ++f;
 164         if (*f) *f++ = 0;
 165         if (*s == FILE_SEPARATOR)
 166             result = Resolve(s, program);
 167         else {
 168             /* relative path element */
 169             char dir[2*PATH_MAX];
 170             JLI_Snprintf(dir, sizeof(dir), "%s%c%s", getcwd(cwdbuf, sizeof(cwdbuf)),
 171                     FILE_SEPARATOR, s);
 172             result = Resolve(dir, program);
 173         }
 174         if (result != 0) break;
 175     }
 176 
 177     JLI_MemFree(tmp_path);
 178     return result;
 179 }
 180 
 181 JNIEXPORT void JNICALL
 182 JLI_ReportErrorMessage(const char* fmt, ...) {
 183     va_list vl;
 184     va_start(vl, fmt);
 185     vfprintf(stderr, fmt, vl);
 186     fprintf(stderr, "\n");
 187     va_end(vl);
 188 }
 189 
 190 JNIEXPORT void JNICALL
 191 JLI_ReportErrorMessageSys(const char* fmt, ...) {
 192     va_list vl;
 193     char *emsg;
 194 
 195     /*
 196      * TODO: its safer to use strerror_r but is not available on
 197      * Solaris 8. Until then....
 198      */
 199     emsg = strerror(errno);
 200     if (emsg != NULL) {
 201         fprintf(stderr, "%s\n", emsg);
 202     }
 203 
 204     va_start(vl, fmt);
 205     vfprintf(stderr, fmt, vl);
 206     fprintf(stderr, "\n");
 207     va_end(vl);
 208 }
 209 
 210 JNIEXPORT void JNICALL
 211 JLI_ReportExceptionDescription(JNIEnv * env) {
 212   (*env)->ExceptionDescribe(env);
 213 }
 214 
 215 /*
 216  *      Since using the file system as a registry is a bit risky, perform
 217  *      additional sanity checks on the identified directory to validate
 218  *      it as a valid jre/sdk.
 219  *
 220  *      Return 0 if the tests fail; otherwise return non-zero (true).
 221  *
 222  *      Note that checking for anything more than the existence of an
 223  *      executable object at bin/java relative to the path being checked
 224  *      will break the regression tests.
 225  */
 226 static int
 227 CheckSanity(char *path, char *dir)
 228 {
 229     char    buffer[PATH_MAX];
 230 
 231     if (JLI_StrLen(path) + JLI_StrLen(dir) + 11 > PATH_MAX)
 232         return (0);     /* Silently reject "impossibly" long paths */
 233 
 234     JLI_Snprintf(buffer, sizeof(buffer), "%s/%s/bin/java", path, dir);
 235     return ((access(buffer, X_OK) == 0) ? 1 : 0);
 236 }
 237 
 238 /*
 239  * "Borrowed" from Solaris 10 where the unsetenv() function is being added
 240  * to libc thanks to SUSv3 (Standard Unix Specification, version 3). As
 241  * such, in the fullness of time this will appear in libc on all relevant
 242  * Solaris/Linux platforms and maybe even the Windows platform.  At that
 243  * time, this stub can be removed.
 244  *
 245  * This implementation removes the environment locking for multithreaded
 246  * applications.  (We don't have access to these mutexes within libc and
 247  * the launcher isn't multithreaded.)  Note that what remains is platform
 248  * independent, because it only relies on attributes that a POSIX environment
 249  * defines.
 250  *
 251  * Returns 0 on success, -1 on failure.
 252  *
 253  * Also removed was the setting of errno.  The only value of errno set
 254  * was EINVAL ("Invalid Argument").
 255  */
 256 
 257 /*
 258  * s1(environ) is name=value
 259  * s2(name) is name(not the form of name=value).
 260  * if names match, return value of 1, else return 0
 261  */
 262 static int
 263 match_noeq(const char *s1, const char *s2)
 264 {
 265         while (*s1 == *s2++) {
 266                 if (*s1++ == '=')
 267                         return (1);
 268         }
 269         if (*s1 == '=' && s2[-1] == '\0')
 270                 return (1);
 271         return (0);
 272 }
 273 
 274 /*
 275  * added for SUSv3 standard
 276  *
 277  * Delete entry from environ.
 278  * Do not free() memory!  Other threads may be using it.
 279  * Keep it around forever.
 280  */
 281 static int
 282 borrowed_unsetenv(const char *name)
 283 {
 284         long    idx;            /* index into environ */
 285 
 286         if (name == NULL || *name == '\0' ||
 287             JLI_StrChr(name, '=') != NULL) {
 288                 return (-1);
 289         }
 290 
 291         for (idx = 0; environ[idx] != NULL; idx++) {
 292                 if (match_noeq(environ[idx], name))
 293                         break;
 294         }
 295         if (environ[idx] == NULL) {
 296                 /* name not found but still a success */
 297                 return (0);
 298         }
 299         /* squeeze up one entry */
 300         do {
 301                 environ[idx] = environ[idx+1];
 302         } while (environ[++idx] != NULL);
 303 
 304         return (0);
 305 }
 306 /* --- End of "borrowed" code --- */
 307 
 308 /*
 309  * Wrapper for unsetenv() function.
 310  */
 311 int
 312 UnsetEnv(char *name)
 313 {
 314     return(borrowed_unsetenv(name));
 315 }
 316 
 317 jboolean
 318 IsJavaw()
 319 {
 320     /* noop on UNIX */
 321     return JNI_FALSE;
 322 }
 323 
 324 void
 325 InitLauncher(jboolean javaw)
 326 {
 327     JLI_SetTraceLauncher();
 328 }
 329 
 330 /*
 331  * The implementation for finding classes from the bootstrap
 332  * class loader, refer to java.h
 333  */
 334 static FindClassFromBootLoader_t *findBootClass = NULL;
 335 
 336 jclass
 337 FindBootStrapClass(JNIEnv *env, const char* classname)
 338 {
 339    if (findBootClass == NULL) {
 340        findBootClass = (FindClassFromBootLoader_t *)dlsym(RTLD_DEFAULT,
 341           "JVM_FindClassFromBootLoader");
 342        if (findBootClass == NULL) {
 343            JLI_ReportErrorMessage(DLL_ERROR4,
 344                "JVM_FindClassFromBootLoader");
 345            return NULL;
 346        }
 347    }
 348    return findBootClass(env, classname);
 349 }
 350 
 351 JNIEXPORT StdArg JNICALL
 352 *JLI_GetStdArgs()
 353 {
 354     return NULL;
 355 }
 356 
 357 JNIEXPORT int JNICALL
 358 JLI_GetStdArgc() {
 359     return 0;
 360 }
 361 
 362 jobjectArray
 363 CreateApplicationArgs(JNIEnv *env, char **strv, int argc)
 364 {
 365     return NewPlatformStringArray(env, strv, argc);
 366 }