1 /* 2 * Copyright (c) 2003, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * A simple launcher to launch a program as if it was launched by inetd. 26 */ 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <sys/types.h> 30 #include <sys/socket.h> 31 #include <unistd.h> 32 #include <dirent.h> 33 #include <sys/stat.h> 34 #include <fcntl.h> 35 #include <ctype.h> 36 37 #include "jni.h" 38 39 /* 40 * Throws the exception of the given class name and detail message 41 */ 42 static void ThrowException(JNIEnv *env, const char *name, const char *msg) { 43 jclass cls = (*env)->FindClass(env, name); 44 if (cls != NULL) { 45 (*env)->ThrowNew(env, cls, msg); 46 } 47 } 48 49 /* 50 * Convert a jstring to an ISO 8859_1 encoded C string 51 */ 52 static char* getString8859_1Chars(JNIEnv *env, jstring jstr) { 53 int i; 54 char *result; 55 jint len = (*env)->GetStringLength(env, jstr); 56 const jchar *str = (*env)->GetStringCritical(env, jstr, 0); 57 if (str == 0) { 58 return NULL; 59 } 60 61 result = (char*)malloc(len+1); 62 if (result == 0) { 63 (*env)->ReleaseStringCritical(env, jstr, str); 64 ThrowException(env, "java/lang/OutOfMemoryError", NULL); 65 return NULL; 66 } 67 68 for (i=0; i<len; i++) { 69 jchar unicode = str[i]; 70 if (unicode <= 0x00ff) 71 result[i] = unicode; 72 else 73 result[i] = '?'; 74 } 75 76 result[len] = 0; 77 (*env)->ReleaseStringCritical(env, jstr, str); 78 return result; 79 } 80 81 82 /* 83 * Class: Launcher 84 * Method: launch0 85 * Signature: ([Ljava/lang/String;I)V 86 */ 87 JNIEXPORT void JNICALL Java_Launcher_launch0 88 (JNIEnv *env, jclass cls, jobjectArray cmdarray, jint serviceFd) 89 { 90 pid_t pid; 91 DIR* dp; 92 struct dirent* dirp; 93 int thisFd; 94 char** cmdv; 95 int i, cmdlen; 96 97 /* 98 * Argument 0 of the command array is the program name. 99 * Here we just extract the program name and any arguments into 100 * a command array suitable for use with execvp. 101 */ 102 cmdlen = (*env)->GetArrayLength(env, cmdarray); 103 if (cmdlen == 0) { 104 ThrowException(env, "java/lang/IllegalArgumentException", 105 "command array must at least include the program name"); 106 return; 107 } 108 cmdv = (char **)malloc((cmdlen + 1) * sizeof(char *)); 109 if (cmdv == NULL) { 110 ThrowException(env, "java/lang/OutOfMemoryError", NULL); 111 return; 112 } 113 114 for (i=0; i<cmdlen; i++) { 115 jstring str = (*env)->GetObjectArrayElement(env, cmdarray, i); 116 cmdv[i] = (char *) getString8859_1Chars(env, str); 117 if (cmdv[i] == NULL) { 118 return; 119 } 120 } 121 122 /* 123 * Command array must have NULL as the last entry 124 */ 125 cmdv[cmdlen] = NULL; 126 127 /* 128 * Launch the program. As this isn't a complete inetd or Runtime.exec 129 * implementation we don't have a reaper to pick up child exit status. 130 */ 131 #ifdef __solaris__ 132 pid = fork1(); 133 #else 134 pid = fork(); 135 #endif 136 if (pid != 0) { 137 if (pid < 0) { 138 ThrowException(env, "java/io/IOException", "fork failed"); 139 } 140 return; 141 } 142 143 /* 144 * We need to close all file descriptors except for serviceFd. To 145 * get the list of open file descriptos we read through /proc/self/fd 146 * but to open this requires a file descriptor. We could use a specific 147 * file descriptor and fdopendir but Linux doesn't seem to support 148 * fdopendir. Instead we use opendir and make an assumption on the 149 * file descriptor that is used (by opening & closing a file). 150 */ 151 thisFd = open("/dev/null", O_RDONLY); 152 if (thisFd < 0) { 153 _exit(-1); 154 } 155 close(thisFd); 156 157 if ((dp = opendir("/proc/self/fd")) == NULL) { 158 _exit(-1); 159 } 160 161 while ((dirp = readdir(dp)) != NULL) { 162 if (isdigit(dirp->d_name[0])) { 163 int fd = strtol(dirp->d_name, NULL, 10); 164 if (fd != serviceFd && fd != thisFd) { 165 close(fd); 166 } 167 } 168 } 169 closedir(dp); 170 171 /* 172 * At this point all file descriptors are closed except for 173 * serviceFd. We not dup 0,1,2 to this file descriptor and 174 * close serviceFd. This should leave us with only 0,1,2 175 * open and all connected to the same socket. 176 */ 177 dup2(serviceFd, STDIN_FILENO); 178 dup2(serviceFd, STDOUT_FILENO); 179 dup2(serviceFd, STDERR_FILENO); 180 close(serviceFd); 181 182 execvp(cmdv[0], cmdv); 183 _exit(-1); 184 }