mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			410 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			410 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
| /* java_lang_VMProcess.c -- native code for java.lang.VMProcess
 | |
|    Copyright (C) 1998, 1999, 2000, 2002, 2004, 2005 Free Software Foundation, Inc.
 | |
| 
 | |
| This file is part of GNU Classpath.
 | |
| 
 | |
| GNU Classpath is free software; you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation; either version 2, or (at your option)
 | |
| any later version.
 | |
|  
 | |
| GNU Classpath is distributed in the hope that it will be useful, but
 | |
| WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with GNU Classpath; see the file COPYING.  If not, write to the
 | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | |
| 02110-1301 USA.
 | |
| 
 | |
| Linking this library statically or dynamically with other modules is
 | |
| making a combined work based on this library.  Thus, the terms and
 | |
| conditions of the GNU General Public License cover the whole
 | |
| combination.
 | |
| 
 | |
| As a special exception, the copyright holders of this library give you
 | |
| permission to link this library with independent modules to produce an
 | |
| executable, regardless of the license terms of these independent
 | |
| modules, and to copy and distribute the resulting executable under
 | |
| terms of your choice, provided that you also meet, for each linked
 | |
| independent module, the terms and conditions of the license of that
 | |
| module.  An independent module is a module which is not derived from
 | |
| or based on this library.  If you modify this library, you may extend
 | |
| this exception to your version of the library, but you are not
 | |
| obligated to do so.  If you do not wish to do so, delete this
 | |
| exception statement from your version. */
 | |
| 
 | |
| #include <config.h>
 | |
| 
 | |
| #include "java_lang_VMProcess.h"
 | |
| #include "gnu_java_nio_FileChannelImpl.h"
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <sys/wait.h>
 | |
| #include <signal.h>
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <stdio.h>
 | |
| 
 | |
| #include "cpnative.h"
 | |
| #include "cpproc.h"
 | |
| 
 | |
| /* Internal functions */
 | |
| static char *copy_string (JNIEnv * env, jobject string);
 | |
| static char *copy_elem (JNIEnv * env, jobject stringArray, jint i);
 | |
| 
 | |
| /*
 | |
|  * Internal helper function to copy a String in UTF-8 format.
 | |
|  */
 | |
| static char *
 | |
| copy_string (JNIEnv * env, jobject string)
 | |
| {
 | |
|   const char *utf;
 | |
|   jclass clazz;
 | |
|   char *copy;
 | |
| 
 | |
|   /* Check for null */
 | |
|   if (string == NULL)
 | |
|     {
 | |
|       clazz = (*env)->FindClass (env, "java/lang/NullPointerException");
 | |
|       if ((*env)->ExceptionOccurred (env))
 | |
| 	return NULL;
 | |
|       (*env)->ThrowNew (env, clazz, NULL);
 | |
|       (*env)->DeleteLocalRef (env, clazz);
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   /* Extract UTF-8 */
 | |
|   utf = (*env)->GetStringUTFChars (env, string, NULL);
 | |
|   if ((*env)->ExceptionOccurred (env))
 | |
|     return NULL;
 | |
| 
 | |
|   /* Copy it */
 | |
|   if ((copy = strdup (utf)) == NULL)
 | |
|     {
 | |
|       clazz = (*env)->FindClass (env, "java/lang/InternalError");
 | |
|       if ((*env)->ExceptionOccurred (env))
 | |
| 	return NULL;
 | |
|       (*env)->ThrowNew (env, clazz, "strdup returned NULL");
 | |
|       (*env)->DeleteLocalRef (env, clazz);
 | |
|     }
 | |
| 
 | |
|   /* Done */
 | |
|   (*env)->ReleaseStringUTFChars (env, string, utf);
 | |
|   return copy;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Internal helper function to copy a String[] element in UTF-8 format.
 | |
|  */
 | |
| static char *
 | |
| copy_elem (JNIEnv * env, jobject stringArray, jint i)
 | |
| {
 | |
|   jobject elem;
 | |
|   char *rtn;
 | |
| 
 | |
|   elem = (*env)->GetObjectArrayElement (env, stringArray, i);
 | |
|   if ((*env)->ExceptionOccurred (env))
 | |
|     return NULL;
 | |
|   if ((rtn = copy_string (env, elem)) == NULL)
 | |
|     return NULL;
 | |
|   (*env)->DeleteLocalRef (env, elem);
 | |
|   return rtn;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * private final native void nativeSpawn(String[], String[], File)
 | |
|  *	throws java/io/IOException
 | |
|  */
 | |
| JNIEXPORT void JNICALL
 | |
| Java_java_lang_VMProcess_nativeSpawn (JNIEnv * env, jobject this,
 | |
| 				      jobjectArray cmdArray,
 | |
| 				      jobjectArray envArray, jobject dirFile,
 | |
| 				      jboolean redirect)
 | |
| {
 | |
|   int fds[CPIO_EXEC_NUM_PIPES];
 | |
|   jobject streams[CPIO_EXEC_NUM_PIPES] = { NULL, NULL, NULL };
 | |
|   jobject dirString = NULL;
 | |
|   char **newEnviron = NULL;
 | |
|   jsize cmdArrayLen = 0;
 | |
|   jsize envArrayLen = 0;
 | |
|   char **strings = NULL;
 | |
|   int num_strings = 0;
 | |
|   char *dir = NULL;
 | |
|   pid_t pid = -1;
 | |
|   char errbuf[64];
 | |
|   jmethodID method, vmmethod;
 | |
|   jclass clazz, vmclazz;
 | |
|   int i;
 | |
|   int pipe_count = redirect ? 2 : 3;
 | |
|   int err;
 | |
| 
 | |
|   /* Check for null */
 | |
|   if (cmdArray == NULL)
 | |
|     goto null_pointer_exception;
 | |
| 
 | |
|   /* Invoke dirFile.getPath() */
 | |
|   if (dirFile != NULL)
 | |
|     {
 | |
|       clazz = (*env)->FindClass (env, "java/io/File");
 | |
|       if ((*env)->ExceptionOccurred (env))
 | |
| 	return;
 | |
|       method = (*env)->GetMethodID (env,
 | |
| 				    clazz, "getPath", "()Ljava/lang/String;");
 | |
|       if ((*env)->ExceptionOccurred (env))
 | |
| 	return;
 | |
|       dirString = (*env)->CallObjectMethod (env, dirFile, method);
 | |
|       if ((*env)->ExceptionOccurred (env))
 | |
| 	return;
 | |
|       (*env)->DeleteLocalRef (env, clazz);
 | |
|     }
 | |
| 
 | |
|   /*
 | |
|    * Allocate array of C strings. We put all the C strings we need to
 | |
|    * handle the command parameters, the new environment, and the new
 | |
|    * directory into a single array for simplicity of (de)allocation.
 | |
|    */
 | |
|   cmdArrayLen = (*env)->GetArrayLength (env, cmdArray);
 | |
|   if (cmdArrayLen == 0)
 | |
|     goto null_pointer_exception;
 | |
|   if (envArray != NULL)
 | |
|     envArrayLen = (*env)->GetArrayLength (env, envArray);
 | |
|   if ((strings = malloc (((cmdArrayLen + 1)
 | |
| 			  + (envArray != NULL ? envArrayLen + 1 : 0)
 | |
| 			  + (dirString !=
 | |
| 			     NULL ? 1 : 0)) * sizeof (*strings))) == NULL)
 | |
|     {
 | |
|       strncpy (errbuf, "malloc failed", sizeof(errbuf));
 | |
|       goto out_of_memory;
 | |
|     }
 | |
| 
 | |
|   /* Extract C strings from the various String parameters */
 | |
|   for (i = 0; i < cmdArrayLen; i++)
 | |
|     {
 | |
|       if ((strings[num_strings++] = copy_elem (env, cmdArray, i)) == NULL)
 | |
| 	goto done;
 | |
|     }
 | |
|   strings[num_strings++] = NULL;	/* terminate array with NULL */
 | |
|   if (envArray != NULL)
 | |
|     {
 | |
|       newEnviron = strings + num_strings;
 | |
|       for (i = 0; i < envArrayLen; i++)
 | |
| 	{
 | |
| 	  if ((strings[num_strings++] = copy_elem (env, envArray, i)) == NULL)
 | |
| 	    goto done;
 | |
| 	}
 | |
|       strings[num_strings++] = NULL;	/* terminate array with NULL */
 | |
|     }
 | |
|   if (dirString != NULL)
 | |
|     {
 | |
|       if ((dir = copy_string (env, dirString)) == NULL)
 | |
| 	goto done;
 | |
|     }
 | |
| 
 | |
|   /* Create inter-process pipes */
 | |
|   err = cpproc_forkAndExec(strings, newEnviron, fds, pipe_count, &pid, dir);
 | |
|   if (err != 0)
 | |
|     {
 | |
|       strncpy(errbuf, cpnative_getErrorString (err), sizeof(errbuf));
 | |
|       goto system_error;
 | |
|     }
 | |
| 
 | |
|   /* Create Input/OutputStream objects around parent file descriptors */
 | |
|   vmclazz = (*env)->FindClass (env, "gnu/java/nio/VMChannel");
 | |
|   clazz = (*env)->FindClass (env, "gnu/java/nio/FileChannelImpl");
 | |
|   if ((*env)->ExceptionOccurred (env))
 | |
|     goto done;
 | |
|   vmmethod = (*env)->GetMethodID (env, vmclazz, "<init>", "(I)V");
 | |
|   method = (*env)->GetMethodID (env, clazz, "<init>", "(Lgnu/java/nio/VMChannel;I)V");
 | |
|   if ((*env)->ExceptionOccurred (env))
 | |
|     goto done;
 | |
|   for (i = 0; i < pipe_count; i++)
 | |
|     {
 | |
|       /* Mode is WRITE (2) for in and READ (1) for out and err. */
 | |
|       const int fd = fds[i];
 | |
|       const int mode = ((i == CPIO_EXEC_STDIN) ? 2 : 1);
 | |
|       jclass sclazz;
 | |
|       jmethodID smethod;
 | |
| 
 | |
|       jobject vmchannel;
 | |
|       jobject channel;
 | |
|       vmchannel = (*env)->NewObject (env, vmclazz, vmmethod, fd);
 | |
|       if ((*env)->ExceptionOccurred (env))
 | |
| 	goto done;
 | |
|       channel = (*env)->NewObject (env, clazz, method, vmchannel, mode);
 | |
|       if ((*env)->ExceptionOccurred (env))
 | |
| 	goto done;
 | |
| 
 | |
|       if (mode == gnu_java_nio_FileChannelImpl_WRITE)
 | |
| 	sclazz = (*env)->FindClass (env, "java/io/FileOutputStream");
 | |
|       else
 | |
| 	sclazz = (*env)->FindClass (env, "java/io/FileInputStream");
 | |
|       if ((*env)->ExceptionOccurred (env))
 | |
| 	goto done;
 | |
| 
 | |
|       smethod = (*env)->GetMethodID (env, sclazz, "<init>",
 | |
| 				     "(Lgnu/java/nio/FileChannelImpl;)V");
 | |
|       if ((*env)->ExceptionOccurred (env))
 | |
| 	goto done;
 | |
| 
 | |
|       streams[i] = (*env)->NewObject (env, sclazz, smethod, channel);
 | |
|       if ((*env)->ExceptionOccurred (env))
 | |
| 	goto done;
 | |
| 
 | |
|       (*env)->DeleteLocalRef (env, sclazz);
 | |
|     }
 | |
|   (*env)->DeleteLocalRef (env, clazz);
 | |
| 
 | |
|   /* Invoke VMProcess.setProcessInfo() to update VMProcess object */
 | |
|   method = (*env)->GetMethodID (env,
 | |
| 				(*env)->GetObjectClass (env, this),
 | |
| 				"setProcessInfo",
 | |
| 				"(Ljava/io/OutputStream;Ljava/io/InputStream;Ljava/io/InputStream;J)V");
 | |
|   if ((*env)->ExceptionOccurred (env))
 | |
|     goto done;
 | |
|   (*env)->CallVoidMethod (env, this, method,
 | |
| 			  streams[CPIO_EXEC_STDIN],
 | |
| 			  streams[CPIO_EXEC_STDOUT],
 | |
| 			  streams[CPIO_EXEC_STDERR],
 | |
| 			  (jlong) pid);
 | |
|   if ((*env)->ExceptionOccurred (env))
 | |
|     goto done;
 | |
| 
 | |
| done:
 | |
|   /*
 | |
|    * We get here in both the success and failure cases in the
 | |
|    * parent process. Our goal is to clean up the mess we created.
 | |
|    */
 | |
| 
 | |
|   /*
 | |
|    * Close parent's ends of pipes if Input/OutputStreams never got created.
 | |
|    * This can only happen in a failure case. If a Stream object
 | |
|    * was created for a file descriptor, we don't close it because it
 | |
|    * will get closed when the Stream object is finalized.
 | |
|    */
 | |
|   for (i = 0; i < pipe_count; i++)
 | |
|     {
 | |
|       const int fd = fds[i];
 | |
| 
 | |
|       if (fd != -1 && streams[i] == NULL)
 | |
| 	close (fd);
 | |
|     }
 | |
| 
 | |
|   /* Free C strings */
 | |
|   while (num_strings > 0)
 | |
|     free (strings[--num_strings]);
 | |
|   free (strings);
 | |
|   if (dir != NULL)
 | |
|     free(dir);
 | |
|   /* Done */
 | |
|   return;
 | |
| 
 | |
| null_pointer_exception:
 | |
|   clazz = (*env)->FindClass (env, "java/lang/NullPointerException");
 | |
|   if ((*env)->ExceptionOccurred (env))
 | |
|     goto done;
 | |
|   (*env)->ThrowNew (env, clazz, NULL);
 | |
|   (*env)->DeleteLocalRef (env, clazz);
 | |
|   goto done;
 | |
| 
 | |
| out_of_memory:
 | |
|   clazz = (*env)->FindClass (env, "java/lang/InternalError");
 | |
|   if ((*env)->ExceptionOccurred (env))
 | |
|     goto done;
 | |
|   (*env)->ThrowNew (env, clazz, errbuf);
 | |
|   (*env)->DeleteLocalRef (env, clazz);
 | |
|   goto done;
 | |
| 
 | |
| system_error:
 | |
|   clazz = (*env)->FindClass (env, "java/io/IOException");
 | |
|   if ((*env)->ExceptionOccurred (env))
 | |
|     goto done;
 | |
|   (*env)->ThrowNew (env, clazz, errbuf);
 | |
|   (*env)->DeleteLocalRef (env, clazz);
 | |
|   goto done;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * private static final native boolean nativeReap()
 | |
|  */
 | |
| JNIEXPORT jboolean JNICALL
 | |
| Java_java_lang_VMProcess_nativeReap (JNIEnv * env, jclass clazz)
 | |
| {
 | |
|   char ebuf[64];
 | |
|   jfieldID field;
 | |
|   jint status;
 | |
|   pid_t pid;
 | |
|   int err;
 | |
| 
 | |
|   /* Try to reap a child process, but don't block */
 | |
|   err = cpproc_waitpid((pid_t)-1, &status, &pid, WNOHANG);
 | |
|   if (err == 0 && pid == 0)
 | |
|     return JNI_FALSE;
 | |
| 
 | |
|   /* Check result from waitpid() */
 | |
|   if (err != 0)
 | |
|     {
 | |
|       if (err == ECHILD || err == EINTR)
 | |
| 	return JNI_FALSE;
 | |
|       snprintf(ebuf, sizeof (ebuf), "waitpid(%ld): %s",
 | |
| 	       (long) pid, cpnative_getErrorString(errno));
 | |
|       clazz = (*env)->FindClass (env, "java/lang/InternalError");
 | |
|       if ((*env)->ExceptionOccurred (env))
 | |
| 	return JNI_FALSE;
 | |
|       (*env)->ThrowNew (env, clazz, ebuf);
 | |
|       (*env)->DeleteLocalRef (env, clazz);
 | |
|       return JNI_FALSE;
 | |
|     }
 | |
| 
 | |
|   /* Get exit code; for signal termination return negative signal value XXX */
 | |
|   if (WIFEXITED (status))
 | |
|     status = (jint) (jbyte) WEXITSTATUS (status);
 | |
|   else if (WIFSIGNALED (status))
 | |
|     status = -(jint) WTERMSIG (status);
 | |
|   else
 | |
|     return JNI_FALSE;		/* process merely stopped; ignore */
 | |
| 
 | |
|   /* Return process pid and exit status */
 | |
|   field = (*env)->GetStaticFieldID (env, clazz, "reapedPid", "J");
 | |
|   if ((*env)->ExceptionOccurred (env))
 | |
|     return JNI_FALSE;
 | |
|   (*env)->SetStaticLongField (env, clazz, field, (jlong) pid);
 | |
|   if ((*env)->ExceptionOccurred (env))
 | |
|     return JNI_FALSE;
 | |
|   field = (*env)->GetStaticFieldID (env, clazz, "reapedExitValue", "I");
 | |
|   if ((*env)->ExceptionOccurred (env))
 | |
|     return JNI_FALSE;
 | |
|   (*env)->SetStaticIntField (env, clazz, field, status);
 | |
|   if ((*env)->ExceptionOccurred (env))
 | |
|     return JNI_FALSE;
 | |
| 
 | |
|   /* Done */
 | |
|   return JNI_TRUE;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * private static final native void nativeKill(long)
 | |
|  */
 | |
| JNIEXPORT void JNICALL
 | |
| Java_java_lang_VMProcess_nativeKill (JNIEnv * env, jclass clazz, jlong pid)
 | |
| {
 | |
|   char ebuf[64];
 | |
|   int err;
 | |
|   
 | |
|   err = cpproc_kill((pid_t) pid, SIGKILL);
 | |
|   if (err != 0)
 | |
|     {
 | |
|       snprintf (ebuf, sizeof (ebuf), "kill(%ld): %s",
 | |
| 		(long) pid, cpnative_getErrorString (err));
 | |
|       clazz = (*env)->FindClass (env, "java/lang/InternalError");
 | |
|       if ((*env)->ExceptionOccurred (env))
 | |
| 	return;
 | |
|       (*env)->ThrowNew (env, clazz, ebuf);
 | |
|       (*env)->DeleteLocalRef (env, clazz);
 | |
|     }
 | |
| }
 |