1 /* 2 * Copyright (c) 2003, 2020, 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 int length = sizeof(addr.sun_path); 237 unlink(nameUtf); 238 memset(&addr, 0, sizeof(addr)); 239 addr.sun_family = AF_UNIX; 240 strncpy(addr.sun_path, nameUtf, length); 241 addr.sun_path[length - 1] = '\0'; 242 ret = bind(sock, (const struct sockaddr*)&addr, sizeof(addr)); 243 if (ret == -1) { 244 ThrowException(env, "java/io/IOException", "socket bind error"); 245 } 246 ret = listen(sock, 5); 247 if (ret == -1) { 248 ThrowException(env, "java/io/IOException", "socket bind error"); 249 } 250 (*env)->ReleaseStringUTFChars(env, name, nameUtf); 251 } 252 253 JNIEXPORT jint JNICALL Java_UnixDomainSocket_accept0 254 (JNIEnv *env, jclass cls, jint sock) 255 { 256 struct sockaddr_storage addr; 257 socklen_t len = sizeof(addr); 258 int ret = accept(sock, (struct sockaddr *)&addr, &len); 259 if (ret == -1) 260 ThrowException(env, "java/io/IOException", "socket accept error"); 261 return ret; 262 } 263 264 JNIEXPORT void JNICALL Java_UnixDomainSocket_connect0 265 (JNIEnv *env, jclass cls, jint fd, jstring name) 266 { 267 struct sockaddr_un addr; 268 const char *nameUtf = (*env)->GetStringUTFChars(env, name, NULL); 269 int ret = -1; 270 int length = sizeof(addr.sun_path); 271 memset(&addr, 0, sizeof(addr)); 272 addr.sun_family = AF_UNIX; 273 strncpy(addr.sun_path, nameUtf, length); 274 addr.sun_path[length - 1] = '\0'; 275 ret = connect(fd, (const struct sockaddr*)&addr, sizeof(addr)); 276 if (ret == -1) { 277 ThrowException(env, "java/io/IOException", "socket connect error"); 278 } 279 (*env)->ReleaseStringUTFChars(env, name, nameUtf); 280 } 281 282 283 JNIEXPORT jint JNICALL Java_UnixDomainSocket_read0 284 (JNIEnv *env, jclass cls, jint fd) 285 { 286 int ret; 287 unsigned char res; 288 ret = read(fd, &res, 1); 289 if (ret == 0) 290 return -1; /* EOF */ 291 else if (ret < 0) { 292 ThrowException(env, "java/io/IOException", "read error"); 293 return -1; 294 } 295 return res; 296 } 297 298 JNIEXPORT void JNICALL Java_UnixDomainSocket_write0 299 (JNIEnv *env, jclass cls, jint fd, jint byte) 300 { 301 int ret; 302 unsigned char w = (unsigned char)byte; 303 ret = write(fd, &w, 1); 304 if (ret < 0) { 305 ThrowException(env, "java/io/IOException", "write error"); 306 } 307 } 308 309 JNIEXPORT void JNICALL Java_UnixDomainSocket_close0 310 (JNIEnv *env, jclass cls, jint fd, jstring name) 311 { 312 close(fd); 313 if (name != NULL) { 314 const char *nameUtf = (*env)->GetStringUTFChars(env, name, NULL); 315 unlink(nameUtf); 316 (*env)->ReleaseStringUTFChars(env, name, nameUtf); 317 } 318 }