URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [java/] [lang/] [natPosixProcess.cc] - Rev 769
Go to most recent revision | Compare with Previous | Blame | View Log
// natPosixProcess.cc - Native side of POSIX process code. /* Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007 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_TIME_H #include <sys/time.h> #endif #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 <posix.h> #include <posix-threads.h> #include <jvm.h> #include <java/lang/PosixProcess$ProcessManager.h> #include <java/lang/PosixProcess.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> #include <java/lang/PosixProcess$EOFInputStream.h> using gnu::java::nio::channels::FileChannelImpl; using namespace java::lang; 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; } namespace { struct ProcessManagerInternal { int pipe_ends[2]; struct sigaction old_sigaction; }; } // 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 #ifdef SA_SIGINFO sigchld_handler (int sig, siginfo_t *si, void *third) #else sigchld_handler (int sig) #endif { if (PosixProcess$ProcessManager::nativeData != NULL) { ProcessManagerInternal *pmi = (ProcessManagerInternal *)PosixProcess$ProcessManager::nativeData; char c = 0; ::write(pmi->pipe_ends[1], &c, 1); if (pmi->old_sigaction.sa_handler != SIG_DFL && pmi->old_sigaction.sa_handler != SIG_IGN) { #ifdef SA_SIGINFO if ((pmi->old_sigaction.sa_flags & SA_SIGINFO) != 0) pmi->old_sigaction.sa_sigaction(sig, si, third); else #endif (*pmi->old_sigaction.sa_handler)(sig); } } } // Get ready to enter the main reaper thread loop. void java::lang::PosixProcess$ProcessManager::init () { // The nativeData is static to avoid races installing the signal // handler in the case that it is chained. if (nativeData == NULL ) { ProcessManagerInternal *pmi = (ProcessManagerInternal *)JvAllocBytes(sizeof(ProcessManagerInternal)); if (0 != ::pipe(pmi->pipe_ends)) goto error; // Make writing non-blocking so that the signal handler will // never block. int fl = ::fcntl(pmi->pipe_ends[1], F_GETFL); ::fcntl(pmi->pipe_ends[1], F_SETFL, fl | O_NONBLOCK); nativeData = (::gnu::gcj::RawDataManaged *)pmi; // SIGCHLD is blocked in all threads in posix-threads.cc. // Setup the SIGCHLD handler. struct sigaction sa; memset (&sa, 0, sizeof (sa)); #ifdef SA_SIGINFO sa.sa_sigaction = sigchld_handler; // We only want signals when the things exit. sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; #else sa.sa_handler = sigchld_handler; // We only want signals when the things exit. sa.sa_flags = SA_NOCLDSTOP; #endif if (-1 == sigaction (SIGCHLD, &sa, &pmi->old_sigaction)) goto error; } // All OK. return; error: throw new InternalError (JvNewStringUTF (strerror (errno))); } void java::lang::PosixProcess$ProcessManager::waitForSignal () { // Wait for SIGCHLD _Jv_UnBlockSigchld(); ProcessManagerInternal *pmi = (ProcessManagerInternal *)nativeData; // Try to read multiple (64) notifications in one go. char c[64]; ::read(pmi->pipe_ends[0], c, sizeof (c)); _Jv_BlockSigchld(); return; } jboolean java::lang::PosixProcess$ProcessManager::reap (PosixProcess *p) { pid_t rv; // Try to get the return code from the child process. int status; rv = ::waitpid ((pid_t)p->pid, &status, WNOHANG); if (rv == -1) throw new InternalError (JvNewStringUTF (strerror (errno))); if (rv == 0) return false; // No children to wait for. JvSynchronize sync (p); p->status = WIFEXITED (status) ? WEXITSTATUS (status) : -1; p->state = PosixProcess::STATE_TERMINATED; p->processTerminationCleanup(); p->notifyAll (); return true; } void java::lang::PosixProcess$ProcessManager::signalReaper () { ProcessManagerInternal *pmi = (ProcessManagerInternal *)nativeData; char c = 0; ::write(pmi->pipe_ends[1], &c, 1); // Ignore errors. If EPIPE the reaper has already exited. } void java::lang::PosixProcess::nativeDestroy () { int c = ::kill ((pid_t) pid, SIGKILL); if (c == 0) return; // kill() failed. throw new InternalError (JvNewStringUTF (strerror (errno))); } void java::lang::PosixProcess::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) { bool need_path = true; bool need_ld_library_path = true; int i; // Preserve PATH and LD_LIBRARY_PATH unless specified // explicitly. We need three extra slots. Potentially PATH // and LD_LIBRARY_PATH will be added plus the NULL // termination. env = (char **) _Jv_Malloc ((envp->length + 3) * sizeof (char *)); elts = elements (envp); // Initialize so we can gracefully recover. for (i = 0; i < envp->length + 3; ++i) env[i] = NULL; for (i = 0; i < envp->length; ++i) { env[i] = new_string (elts[i]); if (!strncmp (env[i], "PATH=", sizeof("PATH="))) need_path = false; if (!strncmp (env[i], "LD_LIBRARY_PATH=", sizeof("LD_LIBRARY_PATH="))) need_ld_library_path = false; } if (need_path) { char *path_val = getenv ("PATH"); if (path_val) { env[i] = (char *) _Jv_Malloc (strlen (path_val) + sizeof("PATH=") + 1); strcpy (env[i], "PATH="); strcat (env[i], path_val); i++; } } if (need_ld_library_path) { char *path_val = getenv ("LD_LIBRARY_PATH"); if (path_val) { env[i] = (char *) _Jv_Malloc (strlen (path_val) + sizeof("LD_LIBRARY_PATH=") + 1); strcpy (env[i], "LD_LIBRARY_PATH="); strcat (env[i], path_val); i++; } } env[i] = 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 redirecting stderr to stdout, we don't need to // create the ERRP pipe. if (pipe (inp) || pipe (outp) || pipe (msgp) || fcntl (msgp[1], F_SETFD, FD_CLOEXEC)) throw new IOException (JvNewStringUTF (strerror (errno))); if (! redirect && pipe (errp)) 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. if (redirect) errorStream = PosixProcess$EOFInputStream::instance; else 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) environ = env; // We ignore errors from dup2 because they should never occur. dup2 (outp[0], 0); dup2 (inp[1], 1); dup2 (redirect ? inp[1] : 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]); if (! redirect) { 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]); if (! redirect) 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 if (! redirect) 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]); if (! redirect) myclose (errp[1]); myclose (msgp[1]); exception = thrown; } myclose (msgp[0]); cleanup (args, env, path); }
Go to most recent revision | Compare with Previous | Blame | View Log