jdk
1/*
2* Copyright (c) 1999, 2023, 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
26/*
27* jexec for J2SE
28*
29* jexec is used by the system to allow execution of JAR files.
30* Essentially jexec needs to run java and
31* needs to be a native ISA executable (not a shell script), although
32* this native ISA executable requirement was a mistake that will be fixed.
33* (<ISA> is sparc or i386 or amd64).
34*
35* When you execute a jar file, jexec is executed by the system as follows:
36* /usr/java/jre/lib/<ISA>/jexec -jar JARFILENAME
37* so this just needs to be turned into:
38* /usr/java/jre/bin/java -jar JARFILENAME
39*
40* Solaris systems (new 7's and all 8's) will be looking for jexec at:
41* /usr/java/jre/lib/<ISA>/jexec
42* Older systems may need to add this to their /etc/system file:
43* set javaexec:jexec="/usr/java/jre/lib/<ISA>/jexec"
44* and reboot the machine for this to work.
45*
46* This source should be compiled as:
47* cc -o jexec jexec.c
48*
49* And jexec should be placed at the following location of the installation:
50* <INSTALLATIONDIR>/jre/lib/<ISA>/jexec (for Solaris)
51* <INSTALLATIONDIR>/lib/jexec (for Linux)
52*
53* NOTE: Unless <INSTALLATIONDIR> is the "default" JDK on the system
54* (i.e. /usr/java -> <INSTALLATIONDIR>), this jexec will not be
55* found. The 1.2 java is only the default on Solaris 8 and
56* on systems where the 1.2 packages were installed and no 1.1
57* java was found.
58*
59* NOTE: You must use 1.2 jar to build your jar files. The system
60* doesn't seem to pick up 1.1 jar files.
61*
62* NOTE: We don't need to set LD_LIBRARY_PATH here, even though we
63* are running the actual java binary because the java binary will
64* look for it's libraries through it's own runpath, which uses
65* $ORIGIN.
66*
67* NOTE: This jexec should NOT have any special .so library needs because
68* it appears that this executable will NOT get the $ORIGIN of jexec
69* but the $ORIGIN of the jar file being executed. Be careful to keep
70* this program simple and with no .so dependencies.
71*/
72
73#include <stdlib.h>74#include <stdio.h>75#include <unistd.h>76#include <string.h>77#include <limits.h>78#include <errno.h>79#ifdef __linux__80# include <sys/types.h>81# include <sys/stat.h>82# include <fcntl.h>83# include "jni.h"84# include "manifest_info.h"85#endif86
87static const int CRAZY_EXEC = ENOEXEC;88static const int BAD_MAGIC = ENOEXEC;89
90static const char * BAD_EXEC_MSG = "jexec failed";91static const char * CRAZY_EXEC_MSG = "missing args";92static const char * MISSING_JAVA_MSG = "can't locate java";93static const char * BAD_ARG_MSG = "incorrect number of arguments";94static const char * MEM_FAILED_MSG = "memory allocation failed";95#ifdef __linux__96static const char * BAD_PATHNAME_MSG = "invalid path";97static const char * BAD_FILE_MSG = "invalid file";98static const char * BAD_MAGIC_MSG = "invalid file (bad magic number)";99#endif100static const char * UNKNOWN_ERROR = "unknown error";101
102/* Define a constant that represents the number of directories to pop off the
103* current location to find the java binary */
104#ifdef __linux__105static const int RELATIVE_DEPTH = 2;106#else /* Solaris */107static const int RELATIVE_DEPTH = 3;108#endif109
110/* path to java after popping */
111static const char * BIN_PATH = "/bin/java";112
113/* flag used when running JAR files */
114static const char * JAR_FLAG = "-jar";115
116
117#ifdef __linux__118/* largest possible size for a local file header */
119static const size_t CHUNK_SIZE = 65535;120
121/* smallest possible size for a local file header */
122static const ssize_t MIN_SIZE = LOCHDR + 1 + 4;123#endif124
125
126int main(int argc, const char * argv[]);127void errorExit(int error, const char * message);128int getJavaPath(const char * path, char * buf, int depth);129#ifdef __linux__130const char * isJar(const char * path);131#endif132
133
134/*
135* This is the main entry point. This program (jexec) will attempt to execute
136* a JAR file by finding the Java program (java), relative to its own location.
137* The exact location of the Java program depends on the platform, i.e.
138*
139* <INSTALLATIONDIR>/jre/lib/<ISA>/jexec (for Solaris)
140* <INSTALLATIONDIR>/lib/jexec (for Linux JDK)
141*
142* Once the Java program is found, this program copies any remaining arguments
143* into another array, which is then used to exec the Java program.
144*
145* On Linux this program does some additional steps. When copying the array of
146* args, it is necessary to insert the "-jar" flag between arg[0], the program
147* name, and the original arg[1], which is presumed to be a path to a JAR file.
148* It is also necessary to verify that the original arg[1] really is a JAR file.
149* (These steps are unnecessary on Solaris because they are taken care of by
150* the kernel.)
151*/
152int main(int argc, const char * argv[]) {153/* We need to exec the original arguments using java, instead of jexec.154* Also, for Linux, it is necessary to add the "-jar" argument between
155* the new arg[0], and the old arg[1]. To do this we will create a new
156* args array. */
157char java[PATH_MAX + 1]; /* path to java binary */158const char ** nargv = NULL; /* new args array */159int nargc = 0; /* new args array count */160int argi = 0; /* index into old array */161size_t alen = 0; /* length of new array */162
163/* Make sure we have something to work with */164if ((argc < 1) || (argv == NULL)) {165/* Shouldn't happen... */166errorExit(CRAZY_EXEC, CRAZY_EXEC_MSG);167}168
169/* Get the path to the java binary, which is in a known position relative170* to our current position, which is in argv[0]. */
171int error = getJavaPath(argv[argi++], java, RELATIVE_DEPTH);172#ifdef __linux__173/* Try to read the symbolic link to the current binary174* if the java path can not be resolved from argv[0]. */
175if (error != 0) {176error = getJavaPath("/proc/self/exe", java, RELATIVE_DEPTH);177}178#endif179
180if (error != 0) {181errorExit(errno, MISSING_JAVA_MSG);182}183alen = (argc + 2) * (sizeof (const char *));184if (alen <= 0 || alen > INT_MAX / sizeof(char *)) {185errorExit(errno, BAD_ARG_MSG);186}187nargv = (const char **) malloc(alen);188if (nargv == NULL) {189errorExit(errno, MEM_FAILED_MSG);190}191nargv[nargc++] = java;192
193#ifdef __linux__194/* The "-jar" flag is already in the original args list on Solaris,195* so it only needs to be added on Linux. */
196nargv[nargc++] = JAR_FLAG;197#endif198
199if (argc >= 2) {200const char * jarfile = argv[argi++];201const char * message = NULL;202
203#ifdef __linux__204/* On Linux we also need to make sure argv[1] is really a JAR205* file (this will also resolve any symlinks, which helps). */
206char jarPath[PATH_MAX + 1];207
208if (realpath(jarfile, jarPath) == NULL) {209errorExit(errno, BAD_PATHNAME_MSG);210}211
212message = isJar(jarPath);213if (message != NULL) {214errorExit(errno, message);215}216
217jarfile = jarPath;218#endif219/* the next argument is the path to the JAR file */220nargv[nargc++] = jarfile;221}222
223/* finally copy any remaining arguments */224while (argi < argc) {225nargv[nargc++] = argv[argi++];226}227
228/* finally add one last terminating null */229nargv[nargc++] = NULL;230
231/* It's time to exec the java binary with the new arguments. It232* is possible that we've reached this point without actually
233* having a JAR file argument (i.e. if argc < 2), but we still
234* want to exec the java binary, since that will take care of
235* displaying the correct usage. */
236execv(java, (char * const *) nargv);237
238/* If the exec worked, this process would have been replaced239* by the new process. So any code reached beyond this point
240* implies an error in the exec. */
241free(nargv);242errorExit(errno, BAD_EXEC_MSG);243return 0; // keep the compiler happy244}
245
246
247/*
248* Exit the application by setting errno, and writing a message.
249*
250* Parameters:
251* error - errno is set to this value, and it is used to exit.
252* message - the message to write.
253*/
254void errorExit(int error, const char * message) {255if (error != 0) {256errno = error;257perror((message != NULL) ? message : UNKNOWN_ERROR);258}259
260exit((error == 0) ? 0 : 1);261}
262
263
264/*
265* Get the path to the java binary that should be relative to the current path.
266*
267* Parameters:
268* path - the input path that the java binary that should be relative to.
269* buf - a buffer of size PATH_MAX or greater that the java path is
270* copied to.
271* depth - the number of names to trim off the current path, including the
272* name of this program.
273*
274* Returns:
275* This function returns 0 on success; otherwise it returns the value of
276* errno.
277*/
278int getJavaPath(const char * path, char * buf, int depth) {279int result = 0;280
281/* Get the full path to this program. Depending on whether this is Solaris282* or Linux, this will be something like,
283*
284* <FOO>/jre/lib/<ISA>/jexec (for Solaris)
285* <FOO>/lib/jexec (for Linux)
286*/
287if (realpath(path, buf) != NULL) {288int count = 0;289
290/* Pop off the filename, and then subdirectories for each level of291* depth */
292for (count = 0; count < depth; count++) {293*(strrchr(buf, '/')) = '\0';294}295
296/* Append the relative location of java, creating something like,297*
298* <FOO>/jre/bin/java (for Solaris)
299* <FOO>/bin/java (for Linux)
300*/
301strcat(buf, BIN_PATH);302}303else {304/* Failed to get the path */305result = errno;306}307
308return (result);309}
310
311
312#ifdef __linux__313/*
314* Check if the given file is a JAR file.
315*
316* Parameters:
317* path - the path to the file to check for JAR magic.
318*
319* Returns:
320* This function return NULL on success. Otherwise, errno is set, and it
321* returns a message that indicates what caused the failure.
322*/
323const char * isJar(const char * path) {324const char * result = BAD_FILE_MSG;325
326int fd = open(path, O_RDONLY);327if (fd != -1) {328unsigned char buf[CHUNK_SIZE];329
330ssize_t count = read(fd, buf, CHUNK_SIZE);331if (count >= MIN_SIZE) {332result = BAD_MAGIC_MSG;333
334// be sure the file is at least a ZIP file335if (LOCSIG_AT(buf)) {336
337off_t flen = LOCNAM(buf);338off_t xlen = LOCEXT(buf);339off_t start = LOCHDR + flen;340off_t end = start + xlen;341
342if (end <= count) {343end -= 4; // make sure there are 4 bytes to read at start344while (start <= end) {345off_t xhid = SH(buf, start);346off_t xdlen = SH(buf, start + 2);347
348start += 4 + xdlen;349if (xhid == 0xcafe) {350// found the JAR magic351result = NULL;352break;353}354}355}356}357}358
359if (result != NULL) {360errno = BAD_MAGIC;361}362
363close (fd);364}365
366return (result);367}
368#endif369