Merge from mainline.
[official-gcc.git] / libjava / classpath / native / jni / java-lang / java_lang_VMProcess.c
blobf13a94f1889e9be08723f500ef80f11f3fd4e8e8
1 /* java_lang_VMProcess.c -- native code for java.lang.VMProcess
2 Copyright (C) 1998, 1999, 2000, 2002, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 #include <config.h>
40 #include "java_lang_VMProcess.h"
41 #include "gnu_java_nio_channels_FileChannelImpl.h"
43 #include <sys/types.h>
44 #include <sys/wait.h>
45 #include <signal.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <stdio.h>
53 #include <jcl.h>
55 #include "target_native.h"
56 #include "target_native_misc.h"
58 /* Internal functions */
59 static char *copy_string (JNIEnv * env, jobject string);
60 static char *copy_elem (JNIEnv * env, jobject stringArray, jint i);
63 * Internal helper function to copy a String in UTF-8 format.
65 static char *
66 copy_string (JNIEnv * env, jobject string)
68 char errbuf[64];
69 const char *utf;
70 jclass clazz;
71 char *copy;
73 /* Check for null */
74 if (string == NULL)
76 clazz = (*env)->FindClass (env, "java/lang/NullPointerException");
77 if ((*env)->ExceptionOccurred (env))
78 return NULL;
79 (*env)->ThrowNew (env, clazz, NULL);
80 (*env)->DeleteLocalRef (env, clazz);
81 return NULL;
84 /* Extract UTF-8 */
85 utf = (*env)->GetStringUTFChars (env, string, NULL);
86 if ((*env)->ExceptionOccurred (env))
87 return NULL;
89 /* Copy it */
90 if ((copy = strdup (utf)) == NULL)
92 TARGET_NATIVE_MISC_FORMAT_STRING1 (errbuf, sizeof (errbuf),
93 "strdup: %s", strerror (errno));
94 clazz = (*env)->FindClass (env, "java/lang/InternalError");
95 if ((*env)->ExceptionOccurred (env))
96 return NULL;
97 (*env)->ThrowNew (env, clazz, errbuf);
98 (*env)->DeleteLocalRef (env, clazz);
101 /* Done */
102 (*env)->ReleaseStringUTFChars (env, string, utf);
103 return copy;
107 * Internal helper function to copy a String[] element in UTF-8 format.
109 static char *
110 copy_elem (JNIEnv * env, jobject stringArray, jint i)
112 jobject elem;
113 char *rtn;
115 elem = (*env)->GetObjectArrayElement (env, stringArray, i);
116 if ((*env)->ExceptionOccurred (env))
117 return NULL;
118 if ((rtn = copy_string (env, elem)) == NULL)
119 return NULL;
120 (*env)->DeleteLocalRef (env, elem);
121 return rtn;
125 * private final native void nativeSpawn(String[], String[], File)
126 * throws java/io/IOException
128 JNIEXPORT void JNICALL
129 Java_java_lang_VMProcess_nativeSpawn (JNIEnv * env, jobject this,
130 jobjectArray cmdArray,
131 jobjectArray envArray, jobject dirFile,
132 jboolean redirect)
134 int fds[3][2] = { {-1, -1}, {-1, -1}, {-1, -1} };
135 jobject streams[3] = { NULL, NULL, NULL };
136 jobject dirString = NULL;
137 char **newEnviron = NULL;
138 jsize cmdArrayLen = 0;
139 jsize envArrayLen = 0;
140 char **strings = NULL;
141 int num_strings = 0;
142 char *dir = NULL;
143 pid_t pid = -1;
144 char errbuf[64];
145 jmethodID method;
146 jclass clazz;
147 int i;
148 int pipe_count = redirect ? 2 : 3;
150 /* Check for null */
151 if (cmdArray == NULL)
152 goto null_pointer_exception;
154 /* Invoke dirFile.getPath() */
155 if (dirFile != NULL)
157 clazz = (*env)->FindClass (env, "java/io/File");
158 if ((*env)->ExceptionOccurred (env))
159 return;
160 method = (*env)->GetMethodID (env,
161 clazz, "getPath", "()Ljava/lang/String;");
162 if ((*env)->ExceptionOccurred (env))
163 return;
164 dirString = (*env)->CallObjectMethod (env, dirFile, method);
165 if ((*env)->ExceptionOccurred (env))
166 return;
167 (*env)->DeleteLocalRef (env, clazz);
171 * Allocate array of C strings. We put all the C strings we need to
172 * handle the command parameters, the new environment, and the new
173 * directory into a single array for simplicity of (de)allocation.
175 cmdArrayLen = (*env)->GetArrayLength (env, cmdArray);
176 if (cmdArrayLen == 0)
177 goto null_pointer_exception;
178 if (envArray != NULL)
179 envArrayLen = (*env)->GetArrayLength (env, envArray);
180 if ((strings = malloc (((cmdArrayLen + 1)
181 + (envArray != NULL ? envArrayLen + 1 : 0)
182 + (dirString !=
183 NULL ? 1 : 0)) * sizeof (*strings))) == NULL)
185 TARGET_NATIVE_MISC_FORMAT_STRING1 (errbuf,
186 sizeof (errbuf), "malloc: %s",
187 strerror (errno));
188 goto out_of_memory;
191 /* Extract C strings from the various String parameters */
192 for (i = 0; i < cmdArrayLen; i++)
194 if ((strings[num_strings++] = copy_elem (env, cmdArray, i)) == NULL)
195 goto done;
197 strings[num_strings++] = NULL; /* terminate array with NULL */
198 if (envArray != NULL)
200 newEnviron = strings + num_strings;
201 for (i = 0; i < envArrayLen; i++)
203 if ((strings[num_strings++] = copy_elem (env, envArray, i)) == NULL)
204 goto done;
206 strings[num_strings++] = NULL; /* terminate array with NULL */
208 if (dirString != NULL)
210 if ((dir = copy_string (env, dirString)) == NULL)
211 goto done;
212 strings[num_strings++] = dir;
215 /* Create inter-process pipes */
216 for (i = 0; i < pipe_count; i++)
218 if (pipe (fds[i]) == -1)
220 TARGET_NATIVE_MISC_FORMAT_STRING1 (errbuf,
221 sizeof (errbuf), "pipe: %s",
222 strerror (errno));
223 goto system_error;
227 /* Set close-on-exec flag for parent's ends of pipes */
228 (void) fcntl (fds[0][1], F_SETFD, 1);
229 (void) fcntl (fds[1][0], F_SETFD, 1);
230 if (pipe_count == 3)
231 (void) fcntl (fds[2][0], F_SETFD, 1);
233 /* Fork into parent and child processes */
234 if ((pid = fork ()) == (pid_t) - 1)
236 TARGET_NATIVE_MISC_FORMAT_STRING1 (errbuf,
237 sizeof (errbuf), "fork: %s",
238 strerror (errno));
239 goto system_error;
242 /* Child becomes the new process */
243 if (pid == 0)
245 char *const path = strings[0];
247 /* Move file descriptors to standard locations */
248 if (fds[0][0] != 0)
250 if (dup2 (fds[0][0], 0) == -1)
252 fprintf (stderr, "dup2: %s", strerror (errno));
253 exit (127);
255 close (fds[0][0]);
257 if (fds[1][1] != 1)
259 if (dup2 (fds[1][1], 1) == -1)
261 fprintf (stderr, "dup2: %s", strerror (errno));
262 exit (127);
264 close (fds[1][1]);
266 if (pipe_count == 2)
268 /* Duplicate stdout to stderr. */
269 if (dup2 (1, 2) == -1)
271 fprintf (stderr, "dup2: %s", strerror (errno));
272 exit (127);
275 else if (fds[2][1] != 2)
277 if (dup2 (fds[2][1], 2) == -1)
279 fprintf (stderr, "dup2: %s", strerror (errno));
280 exit (127);
282 close (fds[2][1]);
285 /* Change into destination directory */
286 if (dir != NULL && chdir (dir) == -1)
288 fprintf (stderr, "%s: %s", dir, strerror (errno));
289 exit (127);
292 /* Make argv[0] last component of executable pathname */
293 /* XXX should use "file.separator" property here XXX */
294 for (i = strlen (path); i > 0 && path[i - 1] != '/'; i--);
295 strings[0] = path + i;
297 /* Set new environment */
298 if (newEnviron != NULL)
299 environ = newEnviron;
301 /* Execute new program (this will close the parent end of the pipes) */
302 execvp (path, strings);
304 /* Failed */
305 fprintf (stderr, "%s: %s", path, strerror (errno));
306 exit (127);
309 /* Create Input/OutputStream objects around parent file descriptors */
310 clazz = (*env)->FindClass (env, "gnu/java/nio/channels/FileChannelImpl");
311 if ((*env)->ExceptionOccurred (env))
312 goto done;
313 method = (*env)->GetMethodID (env, clazz, "<init>", "(II)V");
314 if ((*env)->ExceptionOccurred (env))
315 goto done;
316 for (i = 0; i < pipe_count; i++)
318 /* Mode is WRITE (2) for in and READ (1) for out and err. */
319 const int fd = fds[i][i == 0];
320 const int mode = ((i == 0)
321 ? gnu_java_nio_channels_FileChannelImpl_WRITE
322 : gnu_java_nio_channels_FileChannelImpl_READ);
323 jclass sclazz;
324 jmethodID smethod;
326 jobject channel = (*env)->NewObject (env, clazz, method, fd, mode);
327 if ((*env)->ExceptionOccurred (env))
328 goto done;
330 if (mode == gnu_java_nio_channels_FileChannelImpl_WRITE)
331 sclazz = (*env)->FindClass (env, "java/io/FileOutputStream");
332 else
333 sclazz = (*env)->FindClass (env, "java/io/FileInputStream");
334 if ((*env)->ExceptionOccurred (env))
335 goto done;
337 smethod = (*env)->GetMethodID (env, sclazz, "<init>",
338 "(Lgnu/java/nio/channels/FileChannelImpl;)V");
339 if ((*env)->ExceptionOccurred (env))
340 goto done;
342 streams[i] = (*env)->NewObject (env, sclazz, smethod, channel);
343 if ((*env)->ExceptionOccurred (env))
344 goto done;
346 (*env)->DeleteLocalRef (env, sclazz);
348 (*env)->DeleteLocalRef (env, clazz);
350 /* Invoke VMProcess.setProcessInfo() to update VMProcess object */
351 method = (*env)->GetMethodID (env,
352 (*env)->GetObjectClass (env, this),
353 "setProcessInfo",
354 "(Ljava/io/OutputStream;Ljava/io/InputStream;Ljava/io/InputStream;J)V");
355 if ((*env)->ExceptionOccurred (env))
356 goto done;
357 (*env)->CallVoidMethod (env, this, method,
358 streams[0], streams[1], streams[2], (jlong) pid);
359 if ((*env)->ExceptionOccurred (env))
360 goto done;
362 done:
364 * We get here in both the success and failure cases in the
365 * parent process. Our goal is to clean up the mess we created.
368 /* Close child's ends of pipes */
369 for (i = 0; i < pipe_count; i++)
371 const int fd = fds[i][i != 0];
373 if (fd != -1)
374 close (fd);
378 * Close parent's ends of pipes if Input/OutputStreams never got created.
379 * This can only happen in a failure case. If a Stream object
380 * was created for a file descriptor, we don't close it because it
381 * will get closed when the Stream object is finalized.
383 for (i = 0; i < pipe_count; i++)
385 const int fd = fds[i][i == 0];
387 if (fd != -1 && streams[i] == NULL)
388 close (fd);
391 /* Free C strings */
392 while (num_strings > 0)
393 free (strings[--num_strings]);
394 free (strings);
396 /* Done */
397 return;
399 null_pointer_exception:
400 clazz = (*env)->FindClass (env, "java/lang/NullPointerException");
401 if ((*env)->ExceptionOccurred (env))
402 goto done;
403 (*env)->ThrowNew (env, clazz, NULL);
404 (*env)->DeleteLocalRef (env, clazz);
405 goto done;
407 out_of_memory:
408 clazz = (*env)->FindClass (env, "java/lang/InternalError");
409 if ((*env)->ExceptionOccurred (env))
410 goto done;
411 (*env)->ThrowNew (env, clazz, errbuf);
412 (*env)->DeleteLocalRef (env, clazz);
413 goto done;
415 system_error:
416 clazz = (*env)->FindClass (env, "java/io/IOException");
417 if ((*env)->ExceptionOccurred (env))
418 goto done;
419 (*env)->ThrowNew (env, clazz, errbuf);
420 (*env)->DeleteLocalRef (env, clazz);
421 goto done;
425 * private static final native boolean nativeReap()
427 JNIEXPORT jboolean JNICALL
428 Java_java_lang_VMProcess_nativeReap (JNIEnv * env, jclass clazz)
430 char ebuf[64];
431 jfieldID field;
432 jint status;
433 pid_t pid;
435 /* Try to reap a child process, but don't block */
436 if ((pid = waitpid ((pid_t) - 1, &status, WNOHANG)) == 0)
437 return JNI_FALSE;
439 /* Check result from waitpid() */
440 if (pid == (pid_t) - 1)
442 if (errno == ECHILD || errno == EINTR)
443 return JNI_FALSE;
444 TARGET_NATIVE_MISC_FORMAT_STRING2 (ebuf,
445 sizeof (ebuf), "waitpid(%ld): %s",
446 (long) pid, strerror (errno));
447 clazz = (*env)->FindClass (env, "java/lang/InternalError");
448 if ((*env)->ExceptionOccurred (env))
449 return JNI_FALSE;
450 (*env)->ThrowNew (env, clazz, ebuf);
451 (*env)->DeleteLocalRef (env, clazz);
452 return JNI_FALSE;
455 /* Get exit code; for signal termination return negative signal value XXX */
456 if (WIFEXITED (status))
457 status = (jint) (jbyte) WEXITSTATUS (status);
458 else if (WIFSIGNALED (status))
459 status = -(jint) WTERMSIG (status);
460 else
461 return JNI_FALSE; /* process merely stopped; ignore */
463 /* Return process pid and exit status */
464 field = (*env)->GetStaticFieldID (env, clazz, "reapedPid", "J");
465 if ((*env)->ExceptionOccurred (env))
466 return JNI_FALSE;
467 (*env)->SetStaticLongField (env, clazz, field, (jlong) pid);
468 if ((*env)->ExceptionOccurred (env))
469 return JNI_FALSE;
470 field = (*env)->GetStaticFieldID (env, clazz, "reapedExitValue", "I");
471 if ((*env)->ExceptionOccurred (env))
472 return JNI_FALSE;
473 (*env)->SetStaticIntField (env, clazz, field, status);
474 if ((*env)->ExceptionOccurred (env))
475 return JNI_FALSE;
477 /* Done */
478 return JNI_TRUE;
482 * private static final native void nativeKill(long)
484 JNIEXPORT void JNICALL
485 Java_java_lang_VMProcess_nativeKill (JNIEnv * env, jclass clazz, jlong pid)
487 char ebuf[64];
489 if (kill ((pid_t) pid, SIGKILL) == -1)
491 TARGET_NATIVE_MISC_FORMAT_STRING2 (ebuf,
492 sizeof (ebuf), "kill(%ld): %s",
493 (long) pid, strerror (errno));
494 clazz = (*env)->FindClass (env, "java/lang/InternalError");
495 if ((*env)->ExceptionOccurred (env))
496 return;
497 (*env)->ThrowNew (env, clazz, ebuf);
498 (*env)->DeleteLocalRef (env, clazz);