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 }