1 /* 2 * Copyright (c) 2003, 2019, 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 <string.h> 30 #include <sys/types.h> 31 #include <sys/socket.h> 32 #include <sys/un.h> 33 #include <unistd.h> 34 #include <dirent.h> 35 #include <sys/stat.h> 36 #include <fcntl.h> 37 #include <ctype.h> 38 39 #include "jni.h" 40 41 #define CHECK(X) if ((X) == 0) {printf("JNI init error line %d\n", __LINE__); _exit(1);} 42 43 static jclass unixSocketClass; 44 static jmethodID unixSocketCtor; 45 46 /* 47 * Throws the exception of the given class name and detail message 48 */ 49 static void ThrowException(JNIEnv *env, const char *name, const char *msg) { 50 jclass cls = (*env)->FindClass(env, name); 51 if (cls != NULL) { 52 (*env)->ThrowNew(env, cls, msg); 53 } 54 } 55 56 /* 57 * Convert a jstring to an ISO 8859_1 encoded C string 58 */ 59 static char* getString8859_1Chars(JNIEnv *env, jstring jstr) { 60 int i; 61 char *result; 62 jint len = (*env)->GetStringLength(env, jstr); 63 const jchar *str = (*env)->GetStringCritical(env, jstr, 0); 64 if (str == 0) { 65 return NULL; 66 } 67 68 result = (char*)malloc(len+1); 69 if (result == 0) { 70 (*env)->ReleaseStringCritical(env, jstr, str); 71 ThrowException(env, "java/lang/OutOfMemoryError", NULL); 72 return NULL; 73 } 74 75 for (i=0; i<len; i++) { 76 jchar unicode = str[i]; 77 if (unicode <= 0x00ff) 78 result[i] = unicode; 79 else 80 result[i] = '?'; 81 } 82 83 result[len] = 0; 84 (*env)->ReleaseStringCritical(env, jstr, str); 85 return result; 86 } 87 88 89 /* 90 * Class: Launcher 91 * Method: launch0 92 * Signature: ([Ljava/lang/String;I)V 93 */ 94 JNIEXPORT void JNICALL Java_Launcher_launch0 95 (JNIEnv *env, jclass cls, jobjectArray cmdarray, jint serviceFd) 96 { 97 pid_t pid; 98 DIR* dp; 99 struct dirent* dirp; 100 int thisFd; 101 char** cmdv; 102 int i, cmdlen; 103 104 /* 105 * Argument 0 of the command array is the program name. 106 * Here we just extract the program name and any arguments into 107 * a command array suitable for use with execvp. 108 */ 109 cmdlen = (*env)->GetArrayLength(env, cmdarray); 110 if (cmdlen == 0) { 111 ThrowException(env, "java/lang/IllegalArgumentException", 112 "command array must at least include the program name"); 113 return; 114 } 115 cmdv = (char **)malloc((cmdlen + 1) * sizeof(char *)); 116 if (cmdv == NULL) { 117 ThrowException(env, "java/lang/OutOfMemoryError", NULL); 118 return; 119 } 120 121 for (i=0; i<cmdlen; i++) { 122 jstring str = (*env)->GetObjectArrayElement(env, cmdarray, i); 123 cmdv[i] = (char *) getString8859_1Chars(env, str); 124 if (cmdv[i] == NULL) { 125 return; 126 } 127 } 128 129 /* 130 * Command array must have NULL as the last entry 131 */ 132 cmdv[cmdlen] = NULL; 133 134 /* 135 * Launch the program. As this isn't a complete inetd or Runtime.exec 136 * implementation we don't have a reaper to pick up child exit status. 137 */ 138 #ifdef __solaris__ 139 pid = fork1(); 140 #else 141 pid = fork(); 142 #endif 143 if (pid != 0) { 144 if (pid < 0) { 145 ThrowException(env, "java/io/IOException", "fork failed"); 146 } 147 return; 148 } 149 150 /* 151 * We need to close all file descriptors except for serviceFd. To 152 * get the list of open file descriptos we read through /proc/self/fd (/dev/fd) 153 * but to open this requires a file descriptor. We could use a specific 154 * file descriptor and fdopendir but Linux doesn't seem to support 155 * fdopendir. Instead we use opendir and make an assumption on the 156 * file descriptor that is used (by opening & closing a file). 157 */ 158 thisFd = open("/dev/fd", O_RDONLY); 159 if (thisFd < 0) { 160 _exit(-1); 161 } 162 163 if ((dp = fdopendir(thisFd)) == NULL) { 164 _exit(-1); 165 } 166 167 while ((dirp = readdir(dp)) != NULL) { 168 if (isdigit(dirp->d_name[0])) { 169 int fd = strtol(dirp->d_name, NULL, 10); 170 if (fd != serviceFd && fd != thisFd) { 171 close(fd); 172 } 173 } 174 } 175 closedir(dp); 176 177 /* 178 * At this point all file descriptors are closed except for 179 * serviceFd. We not dup 0,1,2 to this file descriptor and 180 * close serviceFd. This should leave us with only 0,1,2 181 * open and all connected to the same socket. 182 */ 183 dup2(serviceFd, STDIN_FILENO); 184 dup2(serviceFd, STDOUT_FILENO); 185 dup2(serviceFd, STDERR_FILENO); 186 close(serviceFd); 187 188 execvp(cmdv[0], cmdv); 189 _exit(-1); 190 } 191 192 JNIEXPORT void JNICALL Java_UnixDomainSocket_init(JNIEnv *env, jclass cls) { 193 CHECK(unixSocketClass = (*env)->FindClass(env, "UnixDomainSocket")); 194 CHECK(unixSocketClass = (*env)->NewGlobalRef(env, unixSocketClass)); 195 CHECK(unixSocketCtor = (*env)->GetMethodID(env, unixSocketClass, "<init>", "(I)V")); 196 } 197 198 /* 199 * Class: UnixDomainSocket 200 * Method: socketpair 201 * Signature: ()[LUnixDomainSocket 202 */ 203 JNIEXPORT jobjectArray JNICALL Java_UnixDomainSocket_socketpair 204 (JNIEnv *env, jclass cls) 205 { 206 int fds[2]; 207 jobject socket; 208 jobjectArray result = (*env)->NewObjectArray(env, 2, unixSocketClass, 0); 209 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { 210 perror("socketpair"); 211 return result; 212 } 213 socket = (*env)->NewObject(env, unixSocketClass, unixSocketCtor, fds[0]); 214 (*env)->SetObjectArrayElement(env, result, 0, socket); 215 socket = (*env)->NewObject(env, unixSocketClass, unixSocketCtor, fds[1]); 216 (*env)->SetObjectArrayElement(env, result, 1, socket); 217 return result; 218 } 219 220 JNIEXPORT jint JNICALL Java_UnixDomainSocket_create 221 (JNIEnv *env, jclass cls) 222 { 223 int sock = socket(AF_UNIX, SOCK_STREAM, 0); 224 if (sock == -1) { 225 ThrowException(env, "java/io/IOException", "socket create error"); 226 } 227 return sock; 228 } 229 230 JNIEXPORT void JNICALL Java_UnixDomainSocket_bind0 231 (JNIEnv *env, jclass cls, jint sock, jstring name) 232 { 233 struct sockaddr_un addr; 234 const char *nameUtf = (*env)->GetStringUTFChars(env, name, NULL); 235 int ret = -1; 236 unlink(nameUtf); 237 memset(&addr, 0, sizeof(addr)); 238 addr.sun_family = AF_UNIX; 239 strncpy(addr.sun_path, nameUtf, strlen(nameUtf)); 240 ret = bind(sock, (const struct sockaddr*)&addr, sizeof(addr)); 241 if (ret == -1) { 242 ThrowException(env, "java/io/IOException", "socket bind error"); 243 } 244 ret = listen(sock, 5); 245 if (ret == -1) { 246 ThrowException(env, "java/io/IOException", "socket bind error"); 247 } 248 (*env)->ReleaseStringUTFChars(env, name, nameUtf); 249 } 250 251 JNIEXPORT jint JNICALL Java_UnixDomainSocket_accept0 252 (JNIEnv *env, jclass cls, jint sock) 253 { 254 struct sockaddr_storage addr; 255 socklen_t len = sizeof(addr); 256 int ret = accept(sock, (struct sockaddr *)&addr, &len); 257 if (ret == -1) 258 ThrowException(env, "java/io/IOException", "socket accept error"); 259 return ret; 260 } 261 262 JNIEXPORT void JNICALL Java_UnixDomainSocket_connect0 263 (JNIEnv *env, jclass cls, jint fd, jstring name) 264 { 265 struct sockaddr_un addr; 266 const char *nameUtf = (*env)->GetStringUTFChars(env, name, NULL); 267 int ret = -1; 268 memset(&addr, 0, sizeof(addr)); 269 addr.sun_family = AF_UNIX; 270 strncpy(addr.sun_path, nameUtf, strlen(nameUtf)); 271 ret = connect(fd, (const struct sockaddr*)&addr, sizeof(addr)); 272 if (ret == -1) { 273 ThrowException(env, "java/io/IOException", "socket connect error"); 274 } 275 (*env)->ReleaseStringUTFChars(env, name, nameUtf); 276 } 277 278 279 JNIEXPORT jint JNICALL Java_UnixDomainSocket_read0 280 (JNIEnv *env, jclass cls, jint fd) 281 { 282 int ret; 283 unsigned char res; 284 ret = read(fd, &res, 1); 285 if (ret == 0) 286 return -1; /* EOF */ 287 else if (ret < 0) { 288 ThrowException(env, "java/io/IOException", "read error"); 289 return -1; 290 } 291 return res; 292 } 293 294 JNIEXPORT void JNICALL Java_UnixDomainSocket_write0 295 (JNIEnv *env, jclass cls, jint fd, jint byte) 296 { 297 int ret; 298 unsigned char w = (unsigned char)byte; 299 ret = write(fd, &w, 1); 300 if (ret < 0) { 301 ThrowException(env, "java/io/IOException", "write error"); 302 } 303 } 304 305 JNIEXPORT void JNICALL Java_UnixDomainSocket_close0 306 (JNIEnv *env, jclass cls, jint fd, jstring name) 307 { 308 close(fd); 309 if (name != NULL) { 310 const char *nameUtf = (*env)->GetStringUTFChars(env, name, NULL); 311 unlink(nameUtf); 312 (*env)->ReleaseStringUTFChars(env, name, nameUtf); 313 } 314 }