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 */
30char* 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 */
50jboolean
51TruncatePath(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 */
72jboolean
73GetApplicationHome(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 */
89jboolean
90GetApplicationHomeFromDll(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 */
106static int
107ProgramExists(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 */
118static char *
119Resolve(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 */
135char *
136FindExecName(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
181JNIEXPORT void JNICALL
182JLI_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
190JNIEXPORT void JNICALL
191JLI_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
210JNIEXPORT void JNICALL
211JLI_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 */
226static int
227CheckSanity(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 */
262static int
263match_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 */
281static int
282borrowed_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 */
311int
312UnsetEnv(char *name)
313{
314 return(borrowed_unsetenv(name));
315}
316
317jboolean
318IsJavaw()
319{
320 /* noop on UNIX */
321 return JNI_FALSE;
322}
323
324void
325InitLauncher(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 */
334static FindClassFromBootLoader_t *findBootClass = NULL;
335
336jclass
337FindBootStrapClass(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
351JNIEXPORT StdArg JNICALL
352*JLI_GetStdArgs()
353{
354 return NULL;
355}
356
357JNIEXPORT int JNICALL
358JLI_GetStdArgc() {
359 return 0;
360}
361
362jobjectArray
363CreateApplicationArgs(JNIEnv *env, char **strv, int argc)
364{
365 return NewPlatformStringArray(env, strv, argc);
366}
367