jdk/src/macosx/bin/jexec.c
changeset 12047 320a714614e9
equal deleted inserted replaced
12046:378aa3362868 12047:320a714614e9
       
     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 }