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)
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
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
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. */
40 #include "java_lang_VMProcess.h"
41 #include "gnu_java_nio_channels_FileChannelImpl.h"
43 #include <sys/types.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.
66 copy_string (JNIEnv
* env
, jobject string
)
76 clazz
= (*env
)->FindClass (env
, "java/lang/NullPointerException");
77 if ((*env
)->ExceptionOccurred (env
))
79 (*env
)->ThrowNew (env
, clazz
, NULL
);
80 (*env
)->DeleteLocalRef (env
, clazz
);
85 utf
= (*env
)->GetStringUTFChars (env
, string
, NULL
);
86 if ((*env
)->ExceptionOccurred (env
))
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
))
97 (*env
)->ThrowNew (env
, clazz
, errbuf
);
98 (*env
)->DeleteLocalRef (env
, clazz
);
102 (*env
)->ReleaseStringUTFChars (env
, string
, utf
);
107 * Internal helper function to copy a String[] element in UTF-8 format.
110 copy_elem (JNIEnv
* env
, jobject stringArray
, jint i
)
115 elem
= (*env
)->GetObjectArrayElement (env
, stringArray
, i
);
116 if ((*env
)->ExceptionOccurred (env
))
118 if ((rtn
= copy_string (env
, elem
)) == NULL
)
120 (*env
)->DeleteLocalRef (env
, elem
);
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
,
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
;
148 int pipe_count
= redirect
? 2 : 3;
151 if (cmdArray
== NULL
)
152 goto null_pointer_exception
;
154 /* Invoke dirFile.getPath() */
157 clazz
= (*env
)->FindClass (env
, "java/io/File");
158 if ((*env
)->ExceptionOccurred (env
))
160 method
= (*env
)->GetMethodID (env
,
161 clazz
, "getPath", "()Ljava/lang/String;");
162 if ((*env
)->ExceptionOccurred (env
))
164 dirString
= (*env
)->CallObjectMethod (env
, dirFile
, method
);
165 if ((*env
)->ExceptionOccurred (env
))
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)
183 NULL
? 1 : 0)) * sizeof (*strings
))) == NULL
)
185 TARGET_NATIVE_MISC_FORMAT_STRING1 (errbuf
,
186 sizeof (errbuf
), "malloc: %s",
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
)
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
)
206 strings
[num_strings
++] = NULL
; /* terminate array with NULL */
208 if (dirString
!= NULL
)
210 if ((dir
= copy_string (env
, dirString
)) == NULL
)
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",
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);
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",
242 /* Child becomes the new process */
245 char *const path
= strings
[0];
247 /* Move file descriptors to standard locations */
250 if (dup2 (fds
[0][0], 0) == -1)
252 fprintf (stderr
, "dup2: %s", strerror (errno
));
259 if (dup2 (fds
[1][1], 1) == -1)
261 fprintf (stderr
, "dup2: %s", strerror (errno
));
268 /* Duplicate stdout to stderr. */
269 if (dup2 (1, 2) == -1)
271 fprintf (stderr
, "dup2: %s", strerror (errno
));
275 else if (fds
[2][1] != 2)
277 if (dup2 (fds
[2][1], 2) == -1)
279 fprintf (stderr
, "dup2: %s", strerror (errno
));
285 /* Change into destination directory */
286 if (dir
!= NULL
&& chdir (dir
) == -1)
288 fprintf (stderr
, "%s: %s", dir
, strerror (errno
));
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
);
305 fprintf (stderr
, "%s: %s", path
, strerror (errno
));
309 /* Create Input/OutputStream objects around parent file descriptors */
310 clazz
= (*env
)->FindClass (env
, "gnu/java/nio/channels/FileChannelImpl");
311 if ((*env
)->ExceptionOccurred (env
))
313 method
= (*env
)->GetMethodID (env
, clazz
, "<init>", "(II)V");
314 if ((*env
)->ExceptionOccurred (env
))
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
);
326 jobject channel
= (*env
)->NewObject (env
, clazz
, method
, fd
, mode
);
327 if ((*env
)->ExceptionOccurred (env
))
330 if (mode
== gnu_java_nio_channels_FileChannelImpl_WRITE
)
331 sclazz
= (*env
)->FindClass (env
, "java/io/FileOutputStream");
333 sclazz
= (*env
)->FindClass (env
, "java/io/FileInputStream");
334 if ((*env
)->ExceptionOccurred (env
))
337 smethod
= (*env
)->GetMethodID (env
, sclazz
, "<init>",
338 "(Lgnu/java/nio/channels/FileChannelImpl;)V");
339 if ((*env
)->ExceptionOccurred (env
))
342 streams
[i
] = (*env
)->NewObject (env
, sclazz
, smethod
, channel
);
343 if ((*env
)->ExceptionOccurred (env
))
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),
354 "(Ljava/io/OutputStream;Ljava/io/InputStream;Ljava/io/InputStream;J)V");
355 if ((*env
)->ExceptionOccurred (env
))
357 (*env
)->CallVoidMethod (env
, this, method
,
358 streams
[0], streams
[1], streams
[2], (jlong
) pid
);
359 if ((*env
)->ExceptionOccurred (env
))
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];
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
)
392 while (num_strings
> 0)
393 free (strings
[--num_strings
]);
399 null_pointer_exception
:
400 clazz
= (*env
)->FindClass (env
, "java/lang/NullPointerException");
401 if ((*env
)->ExceptionOccurred (env
))
403 (*env
)->ThrowNew (env
, clazz
, NULL
);
404 (*env
)->DeleteLocalRef (env
, clazz
);
408 clazz
= (*env
)->FindClass (env
, "java/lang/InternalError");
409 if ((*env
)->ExceptionOccurred (env
))
411 (*env
)->ThrowNew (env
, clazz
, errbuf
);
412 (*env
)->DeleteLocalRef (env
, clazz
);
416 clazz
= (*env
)->FindClass (env
, "java/io/IOException");
417 if ((*env
)->ExceptionOccurred (env
))
419 (*env
)->ThrowNew (env
, clazz
, errbuf
);
420 (*env
)->DeleteLocalRef (env
, clazz
);
425 * private static final native boolean nativeReap()
427 JNIEXPORT jboolean JNICALL
428 Java_java_lang_VMProcess_nativeReap (JNIEnv
* env
, jclass clazz
)
435 /* Try to reap a child process, but don't block */
436 if ((pid
= waitpid ((pid_t
) - 1, &status
, WNOHANG
)) == 0)
439 /* Check result from waitpid() */
440 if (pid
== (pid_t
) - 1)
442 if (errno
== ECHILD
|| errno
== EINTR
)
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
))
450 (*env
)->ThrowNew (env
, clazz
, ebuf
);
451 (*env
)->DeleteLocalRef (env
, clazz
);
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
);
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
))
467 (*env
)->SetStaticLongField (env
, clazz
, field
, (jlong
) pid
);
468 if ((*env
)->ExceptionOccurred (env
))
470 field
= (*env
)->GetStaticFieldID (env
, clazz
, "reapedExitValue", "I");
471 if ((*env
)->ExceptionOccurred (env
))
473 (*env
)->SetStaticIntField (env
, clazz
, field
, status
);
474 if ((*env
)->ExceptionOccurred (env
))
482 * private static final native void nativeKill(long)
484 JNIEXPORT
void JNICALL
485 Java_java_lang_VMProcess_nativeKill (JNIEnv
* env
, jclass clazz
, jlong pid
)
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
))
497 (*env
)->ThrowNew (env
, clazz
, ebuf
);
498 (*env
)->DeleteLocalRef (env
, clazz
);