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