aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.2.1/libjava/java/lang/natPosixProcess.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-4.2.1/libjava/java/lang/natPosixProcess.cc')
-rw-r--r--gcc-4.2.1/libjava/java/lang/natPosixProcess.cc455
1 files changed, 455 insertions, 0 deletions
diff --git a/gcc-4.2.1/libjava/java/lang/natPosixProcess.cc b/gcc-4.2.1/libjava/java/lang/natPosixProcess.cc
new file mode 100644
index 000000000..77ac69253
--- /dev/null
+++ b/gcc-4.2.1/libjava/java/lang/natPosixProcess.cc
@@ -0,0 +1,455 @@
+// natPosixProcess.cc - Native side of POSIX process code.
+
+/* Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006 Free Software Foundation
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+
+#include <config.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <gcj/cni.h>
+#include <jvm.h>
+
+#include <java/lang/ConcreteProcess$ProcessManager.h>
+#include <java/lang/ConcreteProcess.h>
+#include <java/lang/IllegalThreadStateException.h>
+#include <java/lang/InternalError.h>
+#include <java/lang/InterruptedException.h>
+#include <java/lang/NullPointerException.h>
+#include <java/lang/Thread.h>
+#include <java/io/File.h>
+#include <java/io/FileDescriptor.h>
+#include <gnu/java/nio/channels/FileChannelImpl.h>
+#include <java/io/FileInputStream.h>
+#include <java/io/FileOutputStream.h>
+#include <java/io/IOException.h>
+#include <java/lang/OutOfMemoryError.h>
+
+using gnu::java::nio::channels::FileChannelImpl;
+
+extern char **environ;
+
+static char *
+new_string (jstring string)
+{
+ jsize s = _Jv_GetStringUTFLength (string);
+ char *buf = (char *) _Jv_Malloc (s + 1);
+ _Jv_GetStringUTFRegion (string, 0, string->length(), buf);
+ buf[s] = '\0';
+ return buf;
+}
+
+static void
+cleanup (char **args, char **env, char *path)
+{
+ if (args != NULL)
+ {
+ for (int i = 0; args[i] != NULL; ++i)
+ _Jv_Free (args[i]);
+ _Jv_Free (args);
+ }
+ if (env != NULL)
+ {
+ for (int i = 0; env[i] != NULL; ++i)
+ _Jv_Free (env[i]);
+ _Jv_Free (env);
+ }
+ if (path != NULL)
+ _Jv_Free (path);
+}
+
+// This makes our error handling a bit simpler and it lets us avoid
+// thread bugs where we close a possibly-reopened file descriptor for
+// a second time.
+static void
+myclose (int &fd)
+{
+ if (fd != -1)
+ close (fd);
+ fd = -1;
+}
+
+// There has to be a signal handler in order to be able to
+// sigwait() on SIGCHLD. The information passed is ignored as it
+// will be recovered by the waitpid() call.
+static void
+sigchld_handler (int)
+{
+ // Ignore.
+}
+
+
+// Get ready to enter the main reaper thread loop.
+void
+java::lang::ConcreteProcess$ProcessManager::init ()
+{
+ using namespace java::lang;
+ // Remenber our PID so other threads can kill us.
+ reaperPID = (jlong) pthread_self ();
+
+ // SIGCHLD is blocked in all threads in posix-threads.cc.
+ // Setup the SIGCHLD handler.
+ struct sigaction sa;
+ memset (&sa, 0, sizeof (sa));
+
+ sa.sa_handler = sigchld_handler;
+ // We only want signals when the things exit.
+ sa.sa_flags = SA_NOCLDSTOP;
+
+ if (-1 == sigaction (SIGCHLD, &sa, NULL))
+ goto error;
+
+ // All OK.
+ return;
+
+error:
+ throw new InternalError (JvNewStringUTF (strerror (errno)));
+}
+
+void
+java::lang::ConcreteProcess$ProcessManager::waitForSignal ()
+{
+ // Wait for SIGCHLD
+ sigset_t mask;
+ pthread_sigmask (0, NULL, &mask);
+ sigdelset (&mask, SIGCHLD);
+
+ // Use sigsuspend() instead of sigwait() as sigwait() doesn't play
+ // nicely with the GC's use of signals.
+ sigsuspend (&mask);
+
+ // Do not check sigsuspend return value. The only legitimate return
+ // is EINTR, but there is a known kernel bug affecting alpha-linux
+ // wrt sigsuspend+handler+sigreturn that can result in a return value
+ // of __NR_sigsuspend and errno unset. Don't fail unnecessarily on
+ // older kernel versions.
+
+ // All OK.
+ return;
+}
+
+jboolean java::lang::ConcreteProcess$ProcessManager::reap ()
+{
+ using namespace java::lang;
+
+ pid_t pid;
+
+ for (;;)
+ {
+ // Get the return code from a dead child process.
+ int status;
+ pid = waitpid ((pid_t) - 1, &status, WNOHANG);
+ if (pid == -1)
+ {
+ if (errno == ECHILD)
+ return false;
+ else
+ goto error;
+ }
+
+ if (pid == 0)
+ return true; // No children to wait for.
+
+ // Look up the process in our pid map.
+ ConcreteProcess * process = removeProcessFromMap ((jlong) pid);
+
+ // Note that if process==NULL, then we have an unknown child.
+ // This is not common, but can happen, and isn't an error.
+ if (process)
+ {
+ JvSynchronize sync (process);
+ process->status = WIFEXITED (status) ? WEXITSTATUS (status) : -1;
+ process->state = ConcreteProcess::STATE_TERMINATED;
+ process->processTerminationCleanup();
+ process->notifyAll ();
+ }
+ }
+
+error:
+ throw new InternalError (JvNewStringUTF (strerror (errno)));
+}
+
+void
+java::lang::ConcreteProcess$ProcessManager::signalReaper ()
+{
+ int c = pthread_kill ((pthread_t) reaperPID, SIGCHLD);
+ if (c == 0)
+ return;
+ // pthread_kill() failed.
+ throw new InternalError (JvNewStringUTF (strerror (c)));
+}
+
+void
+java::lang::ConcreteProcess::nativeDestroy ()
+{
+ int c = kill ((pid_t) pid, SIGKILL);
+ if (c == 0)
+ return;
+ // kill() failed.
+ throw new InternalError (JvNewStringUTF (strerror (errno)));
+}
+
+void
+java::lang::ConcreteProcess::nativeSpawn ()
+{
+ using namespace java::io;
+
+ // Initialize all locals here to make cleanup simpler.
+ char **args = NULL;
+ char **env = NULL;
+ char *path = NULL;
+ int inp[2], outp[2], errp[2], msgp[2];
+ inp[0] = -1;
+ inp[1] = -1;
+ outp[0] = -1;
+ outp[1] = -1;
+ errp[0] = -1;
+ errp[1] = -1;
+ msgp[0] = -1;
+ msgp[1] = -1;
+ errorStream = NULL;
+ inputStream = NULL;
+ outputStream = NULL;
+
+ try
+ {
+ // Transform arrays to native form.
+ args = (char **) _Jv_Malloc ((progarray->length + 1) * sizeof (char *));
+
+ // Initialize so we can gracefully recover.
+ jstring *elts = elements (progarray);
+ for (int i = 0; i <= progarray->length; ++i)
+ args[i] = NULL;
+
+ for (int i = 0; i < progarray->length; ++i)
+ args[i] = new_string (elts[i]);
+ args[progarray->length] = NULL;
+
+ if (envp)
+ {
+ env = (char **) _Jv_Malloc ((envp->length + 1) * sizeof (char *));
+ elts = elements (envp);
+
+ // Initialize so we can gracefully recover.
+ for (int i = 0; i <= envp->length; ++i)
+ env[i] = NULL;
+
+ for (int i = 0; i < envp->length; ++i)
+ env[i] = new_string (elts[i]);
+ env[envp->length] = NULL;
+ }
+
+ // We allocate this here because we can't call malloc() after
+ // the fork.
+ if (dir != NULL)
+ path = new_string (dir->getPath ());
+
+ // Create pipes for I/O. MSGP is for communicating exec()
+ // status.
+ if (pipe (inp) || pipe (outp) || pipe (errp) || pipe (msgp)
+ || fcntl (msgp[1], F_SETFD, FD_CLOEXEC))
+ throw new IOException (JvNewStringUTF (strerror (errno)));
+
+ // We create the streams before forking. Otherwise if we had an
+ // error while creating the streams we would have run the child
+ // with no way to communicate with it.
+ errorStream =
+ new FileInputStream (new
+ FileChannelImpl (errp[0], FileChannelImpl::READ));
+ inputStream =
+ new FileInputStream (new
+ FileChannelImpl (inp[0], FileChannelImpl::READ));
+ outputStream =
+ new FileOutputStream (new FileChannelImpl (outp[1],
+ FileChannelImpl::WRITE));
+
+ // We don't use vfork() because that would cause the local
+ // environment to be set by the child.
+
+ // Use temporary for fork result to avoid dirtying an extra page.
+ pid_t pid_tmp;
+ if ((pid_tmp = fork ()) == -1)
+ throw new IOException (JvNewStringUTF (strerror (errno)));
+
+ if (pid_tmp == 0)
+ {
+ // Child process, so remap descriptors, chdir and exec.
+ if (envp)
+ {
+ // Preserve PATH and LD_LIBRARY_PATH unless specified
+ // explicitly.
+ char *path_val = getenv ("PATH");
+ char *ld_path_val = getenv ("LD_LIBRARY_PATH");
+ environ = env;
+ if (path_val && getenv ("PATH") == NULL)
+ {
+ char *path_env =
+ (char *) _Jv_Malloc (strlen (path_val) + 5 + 1);
+ strcpy (path_env, "PATH=");
+ strcat (path_env, path_val);
+ putenv (path_env);
+ }
+ if (ld_path_val && getenv ("LD_LIBRARY_PATH") == NULL)
+ {
+ char *ld_path_env =
+ (char *) _Jv_Malloc (strlen (ld_path_val) + 16 + 1);
+ strcpy (ld_path_env, "LD_LIBRARY_PATH=");
+ strcat (ld_path_env, ld_path_val);
+ putenv (ld_path_env);
+ }
+ }
+
+ // We ignore errors from dup2 because they should never occur.
+ dup2 (outp[0], 0);
+ dup2 (inp[1], 1);
+ dup2 (errp[1], 2);
+
+ // Use close and not myclose -- we're in the child, and we
+ // aren't worried about the possible race condition.
+ close (inp[0]);
+ close (inp[1]);
+ close (errp[0]);
+ close (errp[1]);
+ close (outp[0]);
+ close (outp[1]);
+ close (msgp[0]);
+
+ // Change directory.
+ if (path != NULL)
+ {
+ if (chdir (path) != 0)
+ {
+ char c = errno;
+ write (msgp[1], &c, 1);
+ _exit (127);
+ }
+ }
+ // Make sure all file descriptors are closed. In
+ // multi-threaded programs, there is a race between when a
+ // descriptor is obtained, when we can set FD_CLOEXEC, and
+ // fork(). If the fork occurs before FD_CLOEXEC is set, the
+ // descriptor would leak to the execed process if we did not
+ // manually close it. So that is what we do. Since we
+ // close all the descriptors, it is redundant to set
+ // FD_CLOEXEC on them elsewhere.
+ int max_fd;
+#ifdef HAVE_GETRLIMIT
+ rlimit rl;
+ int rv = getrlimit(RLIMIT_NOFILE, &rl);
+ if (rv == 0)
+ max_fd = rl.rlim_max - 1;
+ else
+ max_fd = 1024 - 1;
+#else
+ max_fd = 1024 - 1;
+#endif
+ while(max_fd > 2)
+ {
+ if (max_fd != msgp[1])
+ close (max_fd);
+ max_fd--;
+ }
+ // Make sure that SIGCHLD is unblocked for the new process.
+ sigset_t mask;
+ sigemptyset (&mask);
+ sigaddset (&mask, SIGCHLD);
+ sigprocmask (SIG_UNBLOCK, &mask, NULL);
+
+ execvp (args[0], args);
+
+ // Send the parent notification that the exec failed.
+ char c = errno;
+ write (msgp[1], &c, 1);
+ _exit (127);
+ }
+
+ // Parent. Close extra file descriptors and mark ours as
+ // close-on-exec.
+ pid = (jlong) pid_tmp;
+
+ myclose (outp[0]);
+ myclose (inp[1]);
+ myclose (errp[1]);
+ myclose (msgp[1]);
+
+ char c;
+ int r = read (msgp[0], &c, 1);
+ if (r == -1)
+ throw new IOException (JvNewStringUTF (strerror (errno)));
+ else if (r != 0)
+ throw new IOException (JvNewStringUTF (strerror (c)));
+ }
+ catch (java::lang::Throwable *thrown)
+ {
+ // Do some cleanup we only do on failure. If a stream object
+ // has been created, we must close the stream itself (to avoid
+ // duplicate closes when the stream object is collected).
+ // Otherwise we simply close the underlying file descriptor.
+ // We ignore errors here as they are uninteresting.
+
+ try
+ {
+ if (inputStream != NULL)
+ inputStream->close ();
+ else
+ myclose (inp[0]);
+ }
+ catch (java::lang::Throwable *ignore)
+ {
+ }
+
+ try
+ {
+ if (outputStream != NULL)
+ outputStream->close ();
+ else
+ myclose (outp[1]);
+ }
+ catch (java::lang::Throwable *ignore)
+ {
+ }
+
+ try
+ {
+ if (errorStream != NULL)
+ errorStream->close ();
+ else
+ myclose (errp[0]);
+ }
+ catch (java::lang::Throwable *ignore)
+ {
+ }
+
+ // These are potentially duplicate, but it doesn't matter due to
+ // the use of myclose.
+ myclose (outp[0]);
+ myclose (inp[1]);
+ myclose (errp[1]);
+ myclose (msgp[1]);
+
+ exception = thrown;
+ }
+
+ myclose (msgp[0]);
+ cleanup (args, env, path);
+}