java_lang_VMProcess.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. /* java_lang_VMProcess.c -- native code for java.lang.VMProcess
  2. Copyright (C) 1998, 1999, 2000, 2002, 2004, 2005 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. #include <config.h>
  32. #include "java_lang_VMProcess.h"
  33. #include "gnu_java_nio_FileChannelImpl.h"
  34. #include <sys/types.h>
  35. #include <sys/wait.h>
  36. #include <signal.h>
  37. #include <stdlib.h>
  38. #include <unistd.h>
  39. #include <string.h>
  40. #include <errno.h>
  41. #include <fcntl.h>
  42. #include <stdio.h>
  43. #include "cpnative.h"
  44. #include "cpproc.h"
  45. /* Internal functions */
  46. static char *copy_string (JNIEnv * env, jobject string);
  47. static char *copy_elem (JNIEnv * env, jobject stringArray, jint i);
  48. /*
  49. * Internal helper function to copy a String in UTF-8 format.
  50. */
  51. static char *
  52. copy_string (JNIEnv * env, jobject string)
  53. {
  54. const char *utf;
  55. jclass clazz;
  56. char *copy;
  57. /* Check for null */
  58. if (string == NULL)
  59. {
  60. clazz = (*env)->FindClass (env, "java/lang/NullPointerException");
  61. if ((*env)->ExceptionOccurred (env))
  62. return NULL;
  63. (*env)->ThrowNew (env, clazz, NULL);
  64. (*env)->DeleteLocalRef (env, clazz);
  65. return NULL;
  66. }
  67. /* Extract UTF-8 */
  68. utf = (*env)->GetStringUTFChars (env, string, NULL);
  69. if ((*env)->ExceptionOccurred (env))
  70. return NULL;
  71. /* Copy it */
  72. if ((copy = strdup (utf)) == NULL)
  73. {
  74. clazz = (*env)->FindClass (env, "java/lang/InternalError");
  75. if ((*env)->ExceptionOccurred (env))
  76. return NULL;
  77. (*env)->ThrowNew (env, clazz, "strdup returned NULL");
  78. (*env)->DeleteLocalRef (env, clazz);
  79. }
  80. /* Done */
  81. (*env)->ReleaseStringUTFChars (env, string, utf);
  82. return copy;
  83. }
  84. /*
  85. * Internal helper function to copy a String[] element in UTF-8 format.
  86. */
  87. static char *
  88. copy_elem (JNIEnv * env, jobject stringArray, jint i)
  89. {
  90. jobject elem;
  91. char *rtn;
  92. elem = (*env)->GetObjectArrayElement (env, stringArray, i);
  93. if ((*env)->ExceptionOccurred (env))
  94. return NULL;
  95. if ((rtn = copy_string (env, elem)) == NULL)
  96. return NULL;
  97. (*env)->DeleteLocalRef (env, elem);
  98. return rtn;
  99. }
  100. /*
  101. * private final native void nativeSpawn(String[], String[], File)
  102. * throws java/io/IOException
  103. */
  104. JNIEXPORT void JNICALL
  105. Java_java_lang_VMProcess_nativeSpawn (JNIEnv * env, jobject this,
  106. jobjectArray cmdArray,
  107. jobjectArray envArray, jobject dirFile,
  108. jboolean redirect)
  109. {
  110. int fds[CPIO_EXEC_NUM_PIPES];
  111. jobject streams[CPIO_EXEC_NUM_PIPES] = { NULL, NULL, NULL };
  112. jobject dirString = NULL;
  113. char **newEnviron = NULL;
  114. jsize cmdArrayLen = 0;
  115. jsize envArrayLen = 0;
  116. char **strings = NULL;
  117. int num_strings = 0;
  118. char *dir = NULL;
  119. pid_t pid = -1;
  120. char errbuf[64];
  121. jmethodID method, vmmethod;
  122. jclass clazz, vmclazz;
  123. int i;
  124. int pipe_count = redirect ? 2 : 3;
  125. int err;
  126. /* Check for null */
  127. if (cmdArray == NULL)
  128. goto null_pointer_exception;
  129. /* Invoke dirFile.getPath() */
  130. if (dirFile != NULL)
  131. {
  132. clazz = (*env)->FindClass (env, "java/io/File");
  133. if ((*env)->ExceptionOccurred (env))
  134. return;
  135. method = (*env)->GetMethodID (env,
  136. clazz, "getPath", "()Ljava/lang/String;");
  137. if ((*env)->ExceptionOccurred (env))
  138. return;
  139. dirString = (*env)->CallObjectMethod (env, dirFile, method);
  140. if ((*env)->ExceptionOccurred (env))
  141. return;
  142. (*env)->DeleteLocalRef (env, clazz);
  143. }
  144. /*
  145. * Allocate array of C strings. We put all the C strings we need to
  146. * handle the command parameters, the new environment, and the new
  147. * directory into a single array for simplicity of (de)allocation.
  148. */
  149. cmdArrayLen = (*env)->GetArrayLength (env, cmdArray);
  150. if (cmdArrayLen == 0)
  151. goto null_pointer_exception;
  152. if (envArray != NULL)
  153. envArrayLen = (*env)->GetArrayLength (env, envArray);
  154. if ((strings = malloc (((cmdArrayLen + 1)
  155. + (envArray != NULL ? envArrayLen + 1 : 0)
  156. + (dirString !=
  157. NULL ? 1 : 0)) * sizeof (*strings))) == NULL)
  158. {
  159. strncpy (errbuf, "malloc failed", sizeof(errbuf));
  160. goto out_of_memory;
  161. }
  162. /* Extract C strings from the various String parameters */
  163. for (i = 0; i < cmdArrayLen; i++)
  164. {
  165. if ((strings[num_strings++] = copy_elem (env, cmdArray, i)) == NULL)
  166. goto done;
  167. }
  168. strings[num_strings++] = NULL; /* terminate array with NULL */
  169. if (envArray != NULL)
  170. {
  171. newEnviron = strings + num_strings;
  172. for (i = 0; i < envArrayLen; i++)
  173. {
  174. if ((strings[num_strings++] = copy_elem (env, envArray, i)) == NULL)
  175. goto done;
  176. }
  177. strings[num_strings++] = NULL; /* terminate array with NULL */
  178. }
  179. if (dirString != NULL)
  180. {
  181. if ((dir = copy_string (env, dirString)) == NULL)
  182. goto done;
  183. }
  184. /* Create inter-process pipes */
  185. err = cpproc_forkAndExec(strings, newEnviron, fds, pipe_count, &pid, dir);
  186. if (err != 0)
  187. {
  188. strncpy(errbuf, cpnative_getErrorString (err), sizeof(errbuf));
  189. goto system_error;
  190. }
  191. /* Create Input/OutputStream objects around parent file descriptors */
  192. vmclazz = (*env)->FindClass (env, "gnu/java/nio/VMChannel");
  193. clazz = (*env)->FindClass (env, "gnu/java/nio/FileChannelImpl");
  194. if ((*env)->ExceptionOccurred (env))
  195. goto done;
  196. vmmethod = (*env)->GetMethodID (env, vmclazz, "<init>", "(I)V");
  197. method = (*env)->GetMethodID (env, clazz, "<init>", "(Lgnu/java/nio/VMChannel;I)V");
  198. if ((*env)->ExceptionOccurred (env))
  199. goto done;
  200. for (i = 0; i < pipe_count; i++)
  201. {
  202. /* Mode is WRITE (2) for in and READ (1) for out and err. */
  203. const int fd = fds[i];
  204. const int mode = ((i == CPIO_EXEC_STDIN) ? 2 : 1);
  205. jclass sclazz;
  206. jmethodID smethod;
  207. jobject vmchannel;
  208. jobject channel;
  209. vmchannel = (*env)->NewObject (env, vmclazz, vmmethod, fd);
  210. if ((*env)->ExceptionOccurred (env))
  211. goto done;
  212. channel = (*env)->NewObject (env, clazz, method, vmchannel, mode);
  213. if ((*env)->ExceptionOccurred (env))
  214. goto done;
  215. if (mode == gnu_java_nio_FileChannelImpl_WRITE)
  216. sclazz = (*env)->FindClass (env, "java/io/FileOutputStream");
  217. else
  218. sclazz = (*env)->FindClass (env, "java/io/FileInputStream");
  219. if ((*env)->ExceptionOccurred (env))
  220. goto done;
  221. smethod = (*env)->GetMethodID (env, sclazz, "<init>",
  222. "(Lgnu/java/nio/FileChannelImpl;)V");
  223. if ((*env)->ExceptionOccurred (env))
  224. goto done;
  225. streams[i] = (*env)->NewObject (env, sclazz, smethod, channel);
  226. if ((*env)->ExceptionOccurred (env))
  227. goto done;
  228. (*env)->DeleteLocalRef (env, sclazz);
  229. }
  230. (*env)->DeleteLocalRef (env, clazz);
  231. /* Invoke VMProcess.setProcessInfo() to update VMProcess object */
  232. method = (*env)->GetMethodID (env,
  233. (*env)->GetObjectClass (env, this),
  234. "setProcessInfo",
  235. "(Ljava/io/OutputStream;Ljava/io/InputStream;Ljava/io/InputStream;J)V");
  236. if ((*env)->ExceptionOccurred (env))
  237. goto done;
  238. (*env)->CallVoidMethod (env, this, method,
  239. streams[CPIO_EXEC_STDIN],
  240. streams[CPIO_EXEC_STDOUT],
  241. streams[CPIO_EXEC_STDERR],
  242. (jlong) pid);
  243. if ((*env)->ExceptionOccurred (env))
  244. goto done;
  245. done:
  246. /*
  247. * We get here in both the success and failure cases in the
  248. * parent process. Our goal is to clean up the mess we created.
  249. */
  250. /*
  251. * Close parent's ends of pipes if Input/OutputStreams never got created.
  252. * This can only happen in a failure case. If a Stream object
  253. * was created for a file descriptor, we don't close it because it
  254. * will get closed when the Stream object is finalized.
  255. */
  256. for (i = 0; i < pipe_count; i++)
  257. {
  258. const int fd = fds[i];
  259. if (fd != -1 && streams[i] == NULL)
  260. close (fd);
  261. }
  262. /* Free C strings */
  263. while (num_strings > 0)
  264. free (strings[--num_strings]);
  265. free (strings);
  266. if (dir != NULL)
  267. free(dir);
  268. /* Done */
  269. return;
  270. null_pointer_exception:
  271. clazz = (*env)->FindClass (env, "java/lang/NullPointerException");
  272. if ((*env)->ExceptionOccurred (env))
  273. goto done;
  274. (*env)->ThrowNew (env, clazz, NULL);
  275. (*env)->DeleteLocalRef (env, clazz);
  276. goto done;
  277. out_of_memory:
  278. clazz = (*env)->FindClass (env, "java/lang/InternalError");
  279. if ((*env)->ExceptionOccurred (env))
  280. goto done;
  281. (*env)->ThrowNew (env, clazz, errbuf);
  282. (*env)->DeleteLocalRef (env, clazz);
  283. goto done;
  284. system_error:
  285. clazz = (*env)->FindClass (env, "java/io/IOException");
  286. if ((*env)->ExceptionOccurred (env))
  287. goto done;
  288. (*env)->ThrowNew (env, clazz, errbuf);
  289. (*env)->DeleteLocalRef (env, clazz);
  290. goto done;
  291. }
  292. /*
  293. * private static final native boolean nativeReap()
  294. */
  295. JNIEXPORT jboolean JNICALL
  296. Java_java_lang_VMProcess_nativeReap (JNIEnv * env, jclass clazz)
  297. {
  298. char ebuf[64];
  299. jfieldID field;
  300. jint status;
  301. pid_t pid;
  302. int err;
  303. /* Try to reap a child process, but don't block */
  304. err = cpproc_waitpid((pid_t)-1, &status, &pid, WNOHANG);
  305. if (err == 0 && pid == 0)
  306. return JNI_FALSE;
  307. /* Check result from waitpid() */
  308. if (err != 0)
  309. {
  310. if (err == ECHILD || err == EINTR)
  311. return JNI_FALSE;
  312. snprintf(ebuf, sizeof (ebuf), "waitpid(%ld): %s",
  313. (long) pid, cpnative_getErrorString(errno));
  314. clazz = (*env)->FindClass (env, "java/lang/InternalError");
  315. if ((*env)->ExceptionOccurred (env))
  316. return JNI_FALSE;
  317. (*env)->ThrowNew (env, clazz, ebuf);
  318. (*env)->DeleteLocalRef (env, clazz);
  319. return JNI_FALSE;
  320. }
  321. /* Get exit code; for signal termination return negative signal value XXX */
  322. if (WIFEXITED (status))
  323. status = (jint) (jbyte) WEXITSTATUS (status);
  324. else if (WIFSIGNALED (status))
  325. status = -(jint) WTERMSIG (status);
  326. else
  327. return JNI_FALSE; /* process merely stopped; ignore */
  328. /* Return process pid and exit status */
  329. field = (*env)->GetStaticFieldID (env, clazz, "reapedPid", "J");
  330. if ((*env)->ExceptionOccurred (env))
  331. return JNI_FALSE;
  332. (*env)->SetStaticLongField (env, clazz, field, (jlong) pid);
  333. if ((*env)->ExceptionOccurred (env))
  334. return JNI_FALSE;
  335. field = (*env)->GetStaticFieldID (env, clazz, "reapedExitValue", "I");
  336. if ((*env)->ExceptionOccurred (env))
  337. return JNI_FALSE;
  338. (*env)->SetStaticIntField (env, clazz, field, status);
  339. if ((*env)->ExceptionOccurred (env))
  340. return JNI_FALSE;
  341. /* Done */
  342. return JNI_TRUE;
  343. }
  344. /*
  345. * private static final native void nativeKill(long)
  346. */
  347. JNIEXPORT void JNICALL
  348. Java_java_lang_VMProcess_nativeKill (JNIEnv * env, jclass clazz, jlong pid)
  349. {
  350. char ebuf[64];
  351. int err;
  352. err = cpproc_kill((pid_t) pid, SIGKILL);
  353. if (err != 0)
  354. {
  355. snprintf (ebuf, sizeof (ebuf), "kill(%ld): %s",
  356. (long) pid, cpnative_getErrorString (err));
  357. clazz = (*env)->FindClass (env, "java/lang/InternalError");
  358. if ((*env)->ExceptionOccurred (env))
  359. return;
  360. (*env)->ThrowNew (env, clazz, ebuf);
  361. (*env)->DeleteLocalRef (env, clazz);
  362. }
  363. }