|
1 /* |
|
2 * Copyright (c) 1999, 2012, 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 |
|
80 static const int CRAZY_EXEC = ENOEXEC; |
|
81 static const int BAD_MAGIC = ENOEXEC; |
|
82 |
|
83 static const char * BAD_EXEC_MSG = "jexec failed"; |
|
84 static const char * CRAZY_EXEC_MSG = "missing args"; |
|
85 static const char * MISSING_JAVA_MSG = "can't locate java"; |
|
86 static const char * UNKNOWN_ERROR = "unknown error"; |
|
87 |
|
88 /* Define a constant that represents the number of directories to pop off the |
|
89 * current location to find the java binary */ |
|
90 static const int RELATIVE_DEPTH = 3; |
|
91 |
|
92 /* path to java after popping */ |
|
93 static const char * BIN_PATH = "/bin/java"; |
|
94 |
|
95 /* flag used when running JAR files */ |
|
96 static const char * JAR_FLAG = "-jar"; |
|
97 |
|
98 int main(int argc, const char * argv[]); |
|
99 void errorExit(int error, const char * message); |
|
100 int getJavaPath(const char * path, char * buf, int depth); |
|
101 |
|
102 /* |
|
103 * This is the main entry point. This program (jexec) will attempt to execute |
|
104 * a JAR file by finding the Java program (java), relative to its own location. |
|
105 * The exact location of the Java program depends on the platform, i.e. |
|
106 * |
|
107 * <INSTALLATIONDIR>/jre/lib/<ISA>/jexec (for Solaris) |
|
108 * <INSTALLATIONDIR>/lib/jexec (for Linux JDK) |
|
109 * |
|
110 * Once the Java program is found, this program copies any remaining arguments |
|
111 * into another array, which is then used to exec the Java program. |
|
112 * |
|
113 * On Linux this program does some additional steps. When copying the array of |
|
114 * args, it is necessary to insert the "-jar" flag between arg[0], the program |
|
115 * name, and the original arg[1], which is presumed to be a path to a JAR file. |
|
116 * It is also necessary to verify that the original arg[1] really is a JAR file. |
|
117 * (These steps are unnecessary on Solaris because they are taken care of by |
|
118 * the kernel.) |
|
119 */ |
|
120 int main(int argc, const char * argv[]) { |
|
121 /* We need to exec the original arguments using java, instead of jexec. |
|
122 * Also, for Linux, it is necessary to add the "-jar" argument between |
|
123 * the new arg[0], and the old arg[1]. To do this we will create a new |
|
124 * args array. */ |
|
125 char java[PATH_MAX + 1]; /* path to java binary */ |
|
126 const char ** nargv = NULL; /* new args array */ |
|
127 int nargc = 0; /* new args array count */ |
|
128 int argi = 0; /* index into old array */ |
|
129 |
|
130 /* Make sure we have something to work with */ |
|
131 if ((argc < 1) || (argv == NULL)) { |
|
132 /* Shouldn't happen... */ |
|
133 errorExit(CRAZY_EXEC, CRAZY_EXEC_MSG); |
|
134 } |
|
135 |
|
136 /* Get the path to the java binary, which is in a known position relative |
|
137 * to our current position, which is in argv[0]. */ |
|
138 if (getJavaPath(argv[argi++], java, RELATIVE_DEPTH) != 0) { |
|
139 errorExit(errno, MISSING_JAVA_MSG); |
|
140 } |
|
141 |
|
142 nargv = (const char **) malloc((argc + 2) * (sizeof (const char *))); |
|
143 nargv[nargc++] = java; |
|
144 |
|
145 if (argc >= 2) { |
|
146 const char * jarfile = argv[argi++]; |
|
147 const char * message = NULL; |
|
148 |
|
149 /* the next argument is the path to the JAR file */ |
|
150 nargv[nargc++] = jarfile; |
|
151 } |
|
152 |
|
153 /* finally copy any remaining arguments */ |
|
154 while (argi < argc) { |
|
155 nargv[nargc++] = argv[argi++]; |
|
156 } |
|
157 |
|
158 /* finally add one last terminating null */ |
|
159 nargv[nargc++] = NULL; |
|
160 |
|
161 /* It's time to exec the java binary with the new arguments. It |
|
162 * is possible that we've reached this point without actually |
|
163 * having a JAR file argument (i.e. if argc < 2), but we still |
|
164 * want to exec the java binary, since that will take care of |
|
165 * displaying the correct usage. */ |
|
166 execv(java, (char * const *) nargv); |
|
167 |
|
168 /* If the exec worked, this process would have been replaced |
|
169 * by the new process. So any code reached beyond this point |
|
170 * implies an error in the exec. */ |
|
171 free(nargv); |
|
172 errorExit(errno, BAD_EXEC_MSG); |
|
173 return 0; // keep the compiler happy |
|
174 } |
|
175 |
|
176 |
|
177 /* |
|
178 * Exit the application by setting errno, and writing a message. |
|
179 * |
|
180 * Parameters: |
|
181 * error - errno is set to this value, and it is used to exit. |
|
182 * message - the message to write. |
|
183 */ |
|
184 void errorExit(int error, const char * message) { |
|
185 if (error != 0) { |
|
186 errno = error; |
|
187 perror((message != NULL) ? message : UNKNOWN_ERROR); |
|
188 } |
|
189 |
|
190 exit((error == 0) ? 0 : 1); |
|
191 } |
|
192 |
|
193 |
|
194 /* |
|
195 * Get the path to the java binary that should be relative to the current path. |
|
196 * |
|
197 * Parameters: |
|
198 * path - the input path that the java binary that should be relative to. |
|
199 * buf - a buffer of size PATH_MAX or greater that the java path is |
|
200 * copied to. |
|
201 * depth - the number of names to trim off the current path, including the |
|
202 * name of this program. |
|
203 * |
|
204 * Returns: |
|
205 * This function returns 0 on success; otherwise it returns the value of |
|
206 * errno. |
|
207 */ |
|
208 int getJavaPath(const char * path, char * buf, int depth) { |
|
209 int result = 0; |
|
210 |
|
211 /* Get the full path to this program. Depending on whether this is Solaris |
|
212 * or Linux, this will be something like, |
|
213 * |
|
214 * <FOO>/jre/lib/<ISA>/jexec (for Solaris) |
|
215 * <FOO>/lib/jexec (for Linux) |
|
216 */ |
|
217 if (realpath(path, buf) != NULL) { |
|
218 int count = 0; |
|
219 |
|
220 /* Pop off the filename, and then subdirectories for each level of |
|
221 * depth */ |
|
222 for (count = 0; count < depth; count++) { |
|
223 *(strrchr(buf, '/')) = '\0'; |
|
224 } |
|
225 |
|
226 /* Append the relative location of java, creating something like, |
|
227 * |
|
228 * <FOO>/jre/bin/java (for Solaris) |
|
229 * <FOO>/bin/java (for Linux) |
|
230 */ |
|
231 strcat(buf, BIN_PATH); |
|
232 } |
|
233 else { |
|
234 /* Failed to get the path */ |
|
235 result = errno; |
|
236 } |
|
237 |
|
238 return (result); |
|
239 } |