1 /*
   2  * Copyright (c) 2012, 2016, 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 void JLI_ReportErrorMessage(const char* fmt, ...) {
 182     va_list vl;
 183     va_start(vl, fmt);
 184     vfprintf(stderr, fmt, vl);
 185     fprintf(stderr, "\n");
 186     va_end(vl);
 187 }
 188 
 189 void JLI_ReportErrorMessageSys(const char* fmt, ...) {
 190     va_list vl;
 191     char *emsg;
 192 
 193     /*
 194      * TODO: its safer to use strerror_r but is not available on
 195      * Solaris 8. Until then....
 196      */
 197     emsg = strerror(errno);
 198     if (emsg != NULL) {
 199         fprintf(stderr, "%s\n", emsg);
 200     }
 201 
 202     va_start(vl, fmt);
 203     vfprintf(stderr, fmt, vl);
 204     fprintf(stderr, "\n");
 205     va_end(vl);
 206 }
 207 
 208 void  JLI_ReportExceptionDescription(JNIEnv * env) {
 209   (*env)->ExceptionDescribe(env);
 210 }
 211 
 212 /*
 213  *      Since using the file system as a registry is a bit risky, perform
 214  *      additional sanity checks on the identified directory to validate
 215  *      it as a valid jre/sdk.
 216  *
 217  *      Return 0 if the tests fail; otherwise return non-zero (true).
 218  *
 219  *      Note that checking for anything more than the existence of an
 220  *      executable object at bin/java relative to the path being checked
 221  *      will break the regression tests.
 222  */
 223 static int
 224 CheckSanity(char *path, char *dir)
 225 {
 226     char    buffer[PATH_MAX];
 227 
 228     if (JLI_StrLen(path) + JLI_StrLen(dir) + 11 > PATH_MAX)
 229         return (0);     /* Silently reject "impossibly" long paths */
 230 
 231     JLI_Snprintf(buffer, sizeof(buffer), "%s/%s/bin/java", path, dir);
 232     return ((access(buffer, X_OK) == 0) ? 1 : 0);
 233 }
 234 
 235 /*
 236  * "Borrowed" from Solaris 10 where the unsetenv() function is being added
 237  * to libc thanks to SUSv3 (Standard Unix Specification, version 3). As
 238  * such, in the fullness of time this will appear in libc on all relevant
 239  * Solaris/Linux platforms and maybe even the Windows platform.  At that
 240  * time, this stub can be removed.
 241  *
 242  * This implementation removes the environment locking for multithreaded
 243  * applications.  (We don't have access to these mutexes within libc and
 244  * the launcher isn't multithreaded.)  Note that what remains is platform
 245  * independent, because it only relies on attributes that a POSIX environment
 246  * defines.
 247  *
 248  * Returns 0 on success, -1 on failure.
 249  *
 250  * Also removed was the setting of errno.  The only value of errno set
 251  * was EINVAL ("Invalid Argument").
 252  */
 253 
 254 /*
 255  * s1(environ) is name=value
 256  * s2(name) is name(not the form of name=value).
 257  * if names match, return value of 1, else return 0
 258  */
 259 static int
 260 match_noeq(const char *s1, const char *s2)
 261 {
 262         while (*s1 == *s2++) {
 263                 if (*s1++ == '=')
 264                         return (1);
 265         }
 266         if (*s1 == '=' && s2[-1] == '\0')
 267                 return (1);
 268         return (0);
 269 }
 270 
 271 /*
 272  * added for SUSv3 standard
 273  *
 274  * Delete entry from environ.
 275  * Do not free() memory!  Other threads may be using it.
 276  * Keep it around forever.
 277  */
 278 static int
 279 borrowed_unsetenv(const char *name)
 280 {
 281         long    idx;            /* index into environ */
 282 
 283         if (name == NULL || *name == '\0' ||
 284             JLI_StrChr(name, '=') != NULL) {
 285                 return (-1);
 286         }
 287 
 288         for (idx = 0; environ[idx] != NULL; idx++) {
 289                 if (match_noeq(environ[idx], name))
 290                         break;
 291         }
 292         if (environ[idx] == NULL) {
 293                 /* name not found but still a success */
 294                 return (0);
 295         }
 296         /* squeeze up one entry */
 297         do {
 298                 environ[idx] = environ[idx+1];
 299         } while (environ[++idx] != NULL);
 300 
 301         return (0);
 302 }
 303 /* --- End of "borrowed" code --- */
 304 
 305 /*
 306  * Wrapper for unsetenv() function.
 307  */
 308 int
 309 UnsetEnv(char *name)
 310 {
 311     return(borrowed_unsetenv(name));
 312 }
 313 
 314 jboolean
 315 IsJavaw()
 316 {
 317     /* noop on UNIX */
 318     return JNI_FALSE;
 319 }
 320 
 321 void
 322 InitLauncher(jboolean javaw)
 323 {
 324     JLI_SetTraceLauncher();
 325 }
 326 
 327 /*
 328  * The implementation for finding classes from the bootstrap
 329  * class loader, refer to java.h
 330  */
 331 static FindClassFromBootLoader_t *findBootClass = NULL;
 332 
 333 jclass
 334 FindBootStrapClass(JNIEnv *env, const char* classname)
 335 {
 336    if (findBootClass == NULL) {
 337        findBootClass = (FindClassFromBootLoader_t *)dlsym(RTLD_DEFAULT,
 338           "JVM_FindClassFromBootLoader");
 339        if (findBootClass == NULL) {
 340            JLI_ReportErrorMessage(DLL_ERROR4,
 341                "JVM_FindClassFromBootLoader");
 342            return NULL;
 343        }
 344    }
 345    return findBootClass(env, classname);
 346 }
 347 
 348 StdArg
 349 *JLI_GetStdArgs()
 350 {
 351     return NULL;
 352 }
 353 
 354 int
 355 JLI_GetStdArgc() {
 356     return 0;
 357 }
 358 
 359 jobjectArray
 360 CreateApplicationArgs(JNIEnv *env, char **strv, int argc)
 361 {
 362     return NewPlatformStringArray(env, strv, argc);
 363 }