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 <sys/types.h>
51 #include "target_native.h"
52 #include "target_native_misc.h"
54 /* Internal functions */
55 static char *copy_string (JNIEnv
* env
, jobject string
);
56 static char *copy_elem (JNIEnv
* env
, jobject stringArray
, jint i
);
58 /* Some O/S's don't declare 'environ' */
59 #if HAVE_CRT_EXTERNS_H
60 /* Darwin does not have a variable named environ
61 but has a function which you can get the environ
63 #include <crt_externs.h>
64 #define environ (*_NSGetEnviron())
66 extern char **environ
;
67 #endif /* HAVE_CRT_EXTERNS_H */
70 * Internal helper function to copy a String in UTF-8 format.
73 copy_string (JNIEnv
* env
, jobject string
)
83 clazz
= (*env
)->FindClass (env
, "java/lang/NullPointerException");
84 if ((*env
)->ExceptionOccurred (env
))
86 (*env
)->ThrowNew (env
, clazz
, NULL
);
87 (*env
)->DeleteLocalRef (env
, clazz
);
92 utf
= (*env
)->GetStringUTFChars (env
, string
, NULL
);
93 if ((*env
)->ExceptionOccurred (env
))
97 if ((copy
= strdup (utf
)) == NULL
)
99 TARGET_NATIVE_MISC_FORMAT_STRING1 (errbuf
, sizeof (errbuf
),
100 "strdup: %s", strerror (errno
));
101 clazz
= (*env
)->FindClass (env
, "java/lang/InternalError");
102 if ((*env
)->ExceptionOccurred (env
))
104 (*env
)->ThrowNew (env
, clazz
, errbuf
);
105 (*env
)->DeleteLocalRef (env
, clazz
);
109 (*env
)->ReleaseStringUTFChars (env
, string
, utf
);
114 * Internal helper function to copy a String[] element in UTF-8 format.
117 copy_elem (JNIEnv
* env
, jobject stringArray
, jint i
)
122 elem
= (*env
)->GetObjectArrayElement (env
, stringArray
, i
);
123 if ((*env
)->ExceptionOccurred (env
))
125 if ((rtn
= copy_string (env
, elem
)) == NULL
)
127 (*env
)->DeleteLocalRef (env
, elem
);
132 * private final native void nativeSpawn(String[], String[], File)
133 * throws java/io/IOException
135 JNIEXPORT
void JNICALL
136 Java_java_lang_VMProcess_nativeSpawn (JNIEnv
* env
, jobject
this,
137 jobjectArray cmdArray
,
138 jobjectArray envArray
, jobject dirFile
)
140 int fds
[3][2] = { {-1, -1}, {-1, -1}, {-1, -1} };
141 jobject streams
[3] = { NULL
, NULL
, NULL
};
142 jobject dirString
= NULL
;
143 char **newEnviron
= NULL
;
144 jsize cmdArrayLen
= 0;
145 jsize envArrayLen
= 0;
146 char **strings
= NULL
;
156 if (cmdArray
== NULL
)
157 goto null_pointer_exception
;
159 /* Invoke dirFile.getPath() */
162 clazz
= (*env
)->FindClass (env
, "java/io/File");
163 if ((*env
)->ExceptionOccurred (env
))
165 method
= (*env
)->GetMethodID (env
,
166 clazz
, "getPath", "()Ljava/lang/String;");
167 if ((*env
)->ExceptionOccurred (env
))
169 dirString
= (*env
)->CallObjectMethod (env
, dirFile
, method
);
170 if ((*env
)->ExceptionOccurred (env
))
172 (*env
)->DeleteLocalRef (env
, clazz
);
176 * Allocate array of C strings. We put all the C strings we need to
177 * handle the command parameters, the new environment, and the new
178 * directory into a single array for simplicity of (de)allocation.
180 cmdArrayLen
= (*env
)->GetArrayLength (env
, cmdArray
);
181 if (cmdArrayLen
== 0)
182 goto null_pointer_exception
;
183 if (envArray
!= NULL
)
184 envArrayLen
= (*env
)->GetArrayLength (env
, envArray
);
185 if ((strings
= malloc (((cmdArrayLen
+ 1)
186 + (envArray
!= NULL
? envArrayLen
+ 1 : 0)
188 NULL
? 1 : 0)) * sizeof (*strings
))) == NULL
)
190 TARGET_NATIVE_MISC_FORMAT_STRING1 (errbuf
,
191 sizeof (errbuf
), "malloc: %s",
196 /* Extract C strings from the various String parameters */
197 for (i
= 0; i
< cmdArrayLen
; i
++)
199 if ((strings
[num_strings
++] = copy_elem (env
, cmdArray
, i
)) == NULL
)
202 strings
[num_strings
++] = NULL
; /* terminate array with NULL */
203 if (envArray
!= NULL
)
205 newEnviron
= strings
+ num_strings
;
206 for (i
= 0; i
< envArrayLen
; i
++)
208 if ((strings
[num_strings
++] = copy_elem (env
, envArray
, i
)) == NULL
)
211 strings
[num_strings
++] = NULL
; /* terminate array with NULL */
213 if (dirString
!= NULL
)
215 if ((dir
= copy_string (env
, dirString
)) == NULL
)
217 strings
[num_strings
++] = dir
;
220 /* Create inter-process pipes */
221 for (i
= 0; i
< 3; i
++)
223 if (pipe (fds
[i
]) == -1)
225 TARGET_NATIVE_MISC_FORMAT_STRING1 (errbuf
,
226 sizeof (errbuf
), "pipe: %s",
232 /* Set close-on-exec flag for parent's ends of pipes */
233 (void) fcntl (fds
[0][1], F_SETFD
, 1);
234 (void) fcntl (fds
[1][0], F_SETFD
, 1);
235 (void) fcntl (fds
[2][0], F_SETFD
, 1);
237 /* Fork into parent and child processes */
238 if ((pid
= fork ()) == (pid_t
) - 1)
240 TARGET_NATIVE_MISC_FORMAT_STRING1 (errbuf
,
241 sizeof (errbuf
), "fork: %s",
246 /* Child becomes the new process */
249 char *const path
= strings
[0];
251 /* Move file descriptors to standard locations */
254 if (dup2 (fds
[0][0], 0) == -1)
256 fprintf (stderr
, "dup2: %s", strerror (errno
));
263 if (dup2 (fds
[1][1], 1) == -1)
265 fprintf (stderr
, "dup2: %s", strerror (errno
));
272 if (dup2 (fds
[2][1], 2) == -1)
274 fprintf (stderr
, "dup2: %s", strerror (errno
));
280 /* Change into destination directory */
281 if (dir
!= NULL
&& chdir (dir
) == -1)
283 fprintf (stderr
, "%s: %s", dir
, strerror (errno
));
287 /* Make argv[0] last component of executable pathname */
288 /* XXX should use "file.separator" property here XXX */
289 for (i
= strlen (path
); i
> 0 && path
[i
- 1] != '/'; i
--);
290 strings
[0] = path
+ i
;
292 /* Set new environment */
293 if (newEnviron
!= NULL
)
294 environ
= newEnviron
;
296 /* Execute new program (this will close the parent end of the pipes) */
297 execvp (path
, strings
);
300 fprintf (stderr
, "%s: %s", path
, strerror (errno
));
304 /* Create Input/OutputStream objects around parent file descriptors */
305 clazz
= (*env
)->FindClass (env
, "gnu/java/nio/channels/FileChannelImpl");
306 if ((*env
)->ExceptionOccurred (env
))
308 method
= (*env
)->GetMethodID (env
, clazz
, "<init>", "(II)V");
309 if ((*env
)->ExceptionOccurred (env
))
311 for (i
= 0; i
< 3; i
++)
313 /* Mode is WRITE (2) for in and READ (1) for out and err. */
314 const int fd
= fds
[i
][i
== 0];
315 const int mode
= (i
== 0) ? 2 : 1;
319 jobject channel
= (*env
)->NewObject (env
, clazz
, method
, fd
, mode
);
320 if ((*env
)->ExceptionOccurred (env
))
324 sclazz
= (*env
)->FindClass (env
, "java/io/FileOutputStream");
326 sclazz
= (*env
)->FindClass (env
, "java/io/FileInputStream");
327 if ((*env
)->ExceptionOccurred (env
))
330 smethod
= (*env
)->GetMethodID (env
, sclazz
, "<init>",
331 "(Lgnu/java/nio/channels/FileChannelImpl;)V");
332 if ((*env
)->ExceptionOccurred (env
))
335 streams
[i
] = (*env
)->NewObject (env
, sclazz
, smethod
, channel
);
336 if ((*env
)->ExceptionOccurred (env
))
339 (*env
)->DeleteLocalRef (env
, sclazz
);
341 (*env
)->DeleteLocalRef (env
, clazz
);
343 /* Invoke VMProcess.setProcessInfo() to update VMProcess object */
344 method
= (*env
)->GetMethodID (env
,
345 (*env
)->GetObjectClass (env
, this),
347 "(Ljava/io/OutputStream;Ljava/io/InputStream;Ljava/io/InputStream;J)V");
348 if ((*env
)->ExceptionOccurred (env
))
350 (*env
)->CallVoidMethod (env
, this, method
,
351 streams
[0], streams
[1], streams
[2], (jlong
) pid
);
352 if ((*env
)->ExceptionOccurred (env
))
357 * We get here in both the success and failure cases in the
358 * parent process. Our goal is to clean up the mess we created.
361 /* Close child's ends of pipes */
362 for (i
= 0; i
< 3; i
++)
364 const int fd
= fds
[i
][i
!= 0];
371 * Close parent's ends of pipes if Input/OutputStreams never got created.
372 * This can only happen in a failure case. If a Stream object
373 * was created for a file descriptor, we don't close it because it
374 * will get closed when the Stream object is finalized.
376 for (i
= 0; i
< 3; i
++)
378 const int fd
= fds
[i
][i
== 0];
380 if (fd
!= -1 && streams
[i
] == NULL
)
385 while (num_strings
> 0)
386 free (strings
[--num_strings
]);
392 null_pointer_exception
:
393 clazz
= (*env
)->FindClass (env
, "java/lang/NullPointerException");
394 if ((*env
)->ExceptionOccurred (env
))
396 (*env
)->ThrowNew (env
, clazz
, NULL
);
397 (*env
)->DeleteLocalRef (env
, clazz
);
401 clazz
= (*env
)->FindClass (env
, "java/lang/InternalError");
402 if ((*env
)->ExceptionOccurred (env
))
404 (*env
)->ThrowNew (env
, clazz
, errbuf
);
405 (*env
)->DeleteLocalRef (env
, clazz
);
409 clazz
= (*env
)->FindClass (env
, "java/io/IOException");
410 if ((*env
)->ExceptionOccurred (env
))
412 (*env
)->ThrowNew (env
, clazz
, errbuf
);
413 (*env
)->DeleteLocalRef (env
, clazz
);
418 * private static final native boolean nativeReap()
420 JNIEXPORT jboolean JNICALL
421 Java_java_lang_VMProcess_nativeReap (JNIEnv
* env
, jclass clazz
)
428 /* Try to reap a child process, but don't block */
429 if ((pid
= waitpid ((pid_t
) - 1, &status
, WNOHANG
)) == 0)
432 /* Check result from waitpid() */
433 if (pid
== (pid_t
) - 1)
435 if (errno
== ECHILD
|| errno
== EINTR
)
437 TARGET_NATIVE_MISC_FORMAT_STRING2 (ebuf
,
438 sizeof (ebuf
), "waitpid(%ld): %s",
439 (long) pid
, strerror (errno
));
440 clazz
= (*env
)->FindClass (env
, "java/lang/InternalError");
441 if ((*env
)->ExceptionOccurred (env
))
443 (*env
)->ThrowNew (env
, clazz
, ebuf
);
444 (*env
)->DeleteLocalRef (env
, clazz
);
448 /* Get exit code; for signal termination return negative signal value XXX */
449 if (WIFEXITED (status
))
450 status
= (jint
) (jbyte
) WEXITSTATUS (status
);
451 else if (WIFSIGNALED (status
))
452 status
= -(jint
) WTERMSIG (status
);
454 return JNI_FALSE
; /* process merely stopped; ignore */
456 /* Return process pid and exit status */
457 field
= (*env
)->GetStaticFieldID (env
, clazz
, "reapedPid", "J");
458 if ((*env
)->ExceptionOccurred (env
))
460 (*env
)->SetStaticLongField (env
, clazz
, field
, (jlong
) pid
);
461 if ((*env
)->ExceptionOccurred (env
))
463 field
= (*env
)->GetStaticFieldID (env
, clazz
, "reapedExitValue", "I");
464 if ((*env
)->ExceptionOccurred (env
))
466 (*env
)->SetStaticIntField (env
, clazz
, field
, status
);
467 if ((*env
)->ExceptionOccurred (env
))
475 * private static final native void nativeKill(long)
477 JNIEXPORT
void JNICALL
478 Java_java_lang_VMProcess_nativeKill (JNIEnv
* env
, jclass clazz
, jlong pid
)
482 if (kill ((pid_t
) pid
, SIGKILL
) == -1)
484 TARGET_NATIVE_MISC_FORMAT_STRING2 (ebuf
,
485 sizeof (ebuf
), "kill(%ld): %s",
486 (long) pid
, strerror (errno
));
487 clazz
= (*env
)->FindClass (env
, "java/lang/InternalError");
488 if ((*env
)->ExceptionOccurred (env
))
490 (*env
)->ThrowNew (env
, clazz
, ebuf
);
491 (*env
)->DeleteLocalRef (env
, clazz
);