From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- libjava/java/lang/natPosixProcess.cc | 515 +++++++++++++++++++++++++++++++++++ 1 file changed, 515 insertions(+) create mode 100644 libjava/java/lang/natPosixProcess.cc (limited to 'libjava/java/lang/natPosixProcess.cc') diff --git a/libjava/java/lang/natPosixProcess.cc b/libjava/java/lang/natPosixProcess.cc new file mode 100644 index 000000000..fbd3f6a0a --- /dev/null +++ b/libjava/java/lang/natPosixProcess.cc @@ -0,0 +1,515 @@ +// 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 + +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} -- cgit v1.2.3