(original) (raw)
diff --git a/src/solaris/native/java/lang/UNIXProcess_md.c b/src/solaris/native/java/lang/UNIXProcess_md.c --- a/src/solaris/native/java/lang/UNIXProcess_md.c +++ b/src/solaris/native/java/lang/UNIXProcess_md.c @@ -48,6 +48,8 @@ #include #include #include +#include +#include #ifndef STDIN_FILENO #define STDIN_FILENO 0 @@ -362,11 +364,12 @@ free(errmsg); } -#ifdef DEBUG_PROCESS +#if 1 /* Debugging process code is difficult; where to write debug output? */ static void debugPrint(char *format, ...) { + return; FILE *tty = fopen("/dev/tty", "w"); va_list ap; va_start(ap, format); @@ -516,6 +519,103 @@ } } +struct ChildAttributes +{ + int in[2]; + int out[2]; + int err[2]; + int fail[2]; + int fds[3]; + const char *pdir; + const char **argv; + const char ** envv; + jboolean redirectErrorStream; +}; + +static void +copyPipe(int from[2], int to[2]) +{ + to[0] = from[0]; + to[1] = from[1]; +} + +static int +childProcess(void *arg) +{ + const char * const * argv; + int argc = 0; + struct ChildAttributes* p = (struct ChildAttributes*) arg; + debugPrint("--------------- hello child --------------\n"); + for (argv = p->argv; *argv != NULL; argv++, argc++) + debugPrint("argv[%d]=%s\n", argc, *argv); + debugPrint("pdir=%s\n", p->pdir != NULL ? p->pdir : "NULL"); + debugPrint("in[0]=%d in[1]=%d\n", p->in[0], p->in[1]); + debugPrint("out[0]=%d out[1]=%d\n", p->out[0], p->out[1]); + debugPrint("err[0]=%d err[1]=%d\n", p->err[0], p->err[1]); + debugPrint("fail[0]=%d fail[1]=%d\n", p->fail[0], p->fail[1]); + debugPrint("fds[0]=%d fds[1]=%d fds[2]=%d\n", p->fds[0], p->fds[1], p->fds[2]); + debugPrint("redirectErrorStream=%d\n", p->redirectErrorStream); + debugPrint("getpid()=%d\n", getpid()); + debugPrint("gettid()=%d\n", syscall(SYS_gettid)); + /* Close the parent sides of the pipes. + Closing pipe fds here is redundant, since closeDescriptors() + would do it anyways, but a little paranoia is a good thing. */ + closeSafely(p->in[1]); + closeSafely(p->out[0]); + closeSafely(p->err[0]); + closeSafely(p->fail[0]); + + /* Give the child sides of the pipes the right fileno's. */ + /* Note: it is possible for in[0] == 0 */ + moveDescriptor(p->in[0] != -1 ? p->in[0] : p->fds[0], STDIN_FILENO); + moveDescriptor(p->out[1]!= -1 ? p->out[1] : p->fds[1], STDOUT_FILENO); + + if (p->redirectErrorStream) { + closeSafely(p->err[1]); + dup2(STDOUT_FILENO, STDERR_FILENO); + } else { + moveDescriptor(p->err[1] != -1 ? p->err[1] : p->fds[2], STDERR_FILENO); + } + + moveDescriptor(p->fail[1], FAIL_FILENO); + + /* close everything */ + if (closeDescriptors() == 0) { /* failed, close the old way */ + int max_fd = (int)sysconf(_SC_OPEN_MAX); + int i; + for (i = FAIL_FILENO + 1; i < max_fd; i++) + close(i); + } + + /* change to the new working directory */ + if (p->pdir != NULL && chdir(p->pdir) < 0) + goto WhyCantJohnnyExec; + + if (fcntl(FAIL_FILENO, F_SETFD, FD_CLOEXEC) == -1) + goto WhyCantJohnnyExec; + + execvpe(p->argv[0], p->argv, p->envv); + + WhyCantJohnnyExec: + /* We used to go to an awful lot of trouble to predict whether the + * child would fail, but there is no reliable way to predict the + * success of an operation without *trying* it, and there's no way + * to try a chdir or exec in the parent. Instead, all we need is a + * way to communicate any failure back to the parent. Easy; we just + * send the errno back to the parent over a pipe in case of failure. + * The tricky thing is, how do we communicate the *success* of exec? + * We use FD_CLOEXEC together with the fact that a read() on a pipe + * yields EOF when the write ends (we have two of them!) are closed. + */ + { + int errnum = errno; + write(FAIL_FILENO, &errnum, sizeof(errnum)); + } + close(FAIL_FILENO); + _exit(-1); +} + + #ifndef __solaris__ #undef fork1 #define fork1() fork() @@ -533,14 +633,24 @@ { int errnum; int resultPid = -1; + void * volatile clone_stack = NULL; + struct ChildAttributes * volatile c = malloc(sizeof (struct ChildAttributes)); int in[2], out[2], err[2], fail[2]; - const char **argv = NULL; - const char **envv = NULL; + //const char **argv = NULL; + //const char **envv = NULL; const char *pprog = getBytes(env, prog); const char *pargBlock = getBytes(env, argBlock); const char *penvBlock = getBytes(env, envBlock); - const char *pdir = getBytes(env, dir); + //const char *pdir = getBytes(env, dir); + c->pdir = getBytes(env, dir); jint *fds = NULL; + //in[0] = in[1] = -1; + //c->out[0] = c->out[1] = -1; + //c->err[0] = c->err[1] = -1; + //c->fail[0] = c->fail[1] = -1; + c->argv = NULL; + c->envv = NULL; + //c->fds = NULL; in[0] = in[1] = out[0] = out[1] = err[0] = err[1] = fail[0] = fail[1] = -1; @@ -548,19 +658,20 @@ if (pprog == NULL) goto Catch; if (pargBlock == NULL) goto Catch; if (envBlock != NULL && penvBlock == NULL) goto Catch; - if (dir != NULL && pdir == NULL) goto Catch; + //if (dir != NULL && pdir == NULL) goto Catch; + if (dir != NULL && c->pdir == NULL) goto Catch; /* Convert pprog + pargBlock into a char ** argv */ - if ((argv = NEW(const char *, argc + 2)) == NULL) + if ((c->argv = NEW(const char *, argc + 2)) == NULL) goto Catch; - argv[0] = pprog; - initVectorFromBlock(argv+1, pargBlock, argc); + c->argv[0] = pprog; + initVectorFromBlock(c->argv+1, pargBlock, argc); if (envBlock != NULL) { /* Convert penvBlock into a char ** envv */ - if ((envv = NEW(const char *, envc + 1)) == NULL) + if ((c->envv = NEW(const char *, envc + 1)) == NULL) goto Catch; - initVectorFromBlock(envv, penvBlock, envc); + initVectorFromBlock(c->envv, penvBlock, envc); } assert(std_fds != NULL); @@ -574,70 +685,47 @@ throwIOException(env, errno, "Bad file descriptor"); goto Catch; } + c->fds[0] = fds[0]; + c->fds[1] = fds[1]; + c->fds[2] = fds[2]; - resultPid = fork1(); + copyPipe(in, c->in); + copyPipe(out, c->out); + copyPipe(err, c->err); + copyPipe(fail, c->fail); + +// c->in[0] = in[0]; +// c->in[1] = in[1]; +// c->out[0] = out[0]; +// c->out[1] = out[1]; +// c->err[0] = err[0]; +// c->err[1] = err[1]; +// c->fail[0] = fail[0]; +// c->fail[1] = fail[1]; + //c->fds = fds; + //c->pdir = pdir; + //c->argv = argv; + //c->envv = envv; + c->redirectErrorStream = redirectErrorStream; + + if (c->envv == NULL) { + const int stack_size = 1024 * 1024; + clone_stack = malloc(2 * stack_size); +// struct ChildAttributes *x = calloc(1, sizeof(struct ChildAttributes)); + resultPid = clone(childProcess, clone_stack + stack_size, + //CLONE_VFORK | + CLONE_VM | SIGCHLD, c); + debugPrint("parent resultPid=%d\n", resultPid); + } else { + resultPid = fork1(); + } if (resultPid < 0) { throwIOException(env, errno, "Fork failed"); goto Catch; } if (resultPid == 0) { - /* Child process */ - - /* Close the parent sides of the pipes. - Closing pipe fds here is redundant, since closeDescriptors() - would do it anyways, but a little paranoia is a good thing. */ - closeSafely(in[1]); - closeSafely(out[0]); - closeSafely(err[0]); - closeSafely(fail[0]); - - /* Give the child sides of the pipes the right fileno's. */ - /* Note: it is possible for in[0] == 0 */ - moveDescriptor(in[0] != -1 ? in[0] : fds[0], STDIN_FILENO); - moveDescriptor(out[1]!= -1 ? out[1] : fds[1], STDOUT_FILENO); - - if (redirectErrorStream) { - closeSafely(err[1]); - dup2(STDOUT_FILENO, STDERR_FILENO); - } else { - moveDescriptor(err[1] != -1 ? err[1] : fds[2], STDERR_FILENO); - } - - moveDescriptor(fail[1], FAIL_FILENO); - - /* close everything */ - if (closeDescriptors() == 0) { /* failed, close the old way */ - int max_fd = (int)sysconf(_SC_OPEN_MAX); - int i; - for (i = FAIL_FILENO + 1; i < max_fd; i++) - close(i); - } - - /* change to the new working directory */ - if (pdir != NULL && chdir(pdir) < 0) - goto WhyCantJohnnyExec; - - if (fcntl(FAIL_FILENO, F_SETFD, FD_CLOEXEC) == -1) - goto WhyCantJohnnyExec; - - execvpe(argv[0], argv, envv); - - WhyCantJohnnyExec: - /* We used to go to an awful lot of trouble to predict whether the - * child would fail, but there is no reliable way to predict the - * success of an operation without *trying* it, and there's no way - * to try a chdir or exec in the parent. Instead, all we need is a - * way to communicate any failure back to the parent. Easy; we just - * send the errno back to the parent over a pipe in case of failure. - * The tricky thing is, how do we communicate the *success* of exec? - * We use FD_CLOEXEC together with the fact that a read() on a pipe - * yields EOF when the write ends (we have two of them!) are closed. - */ - errnum = errno; - write(FAIL_FILENO, &errnum, sizeof(errnum)); - close(FAIL_FILENO); - _exit(-1); + childProcess(c); } /* parent process */ @@ -660,6 +748,8 @@ fds[2] = (err[0] != -1) ? err[0] : -1; Finally: + free(clone_stack); + /* Always clean up the child's side of the pipes */ closeSafely(in [0]); closeSafely(out[1]); @@ -669,13 +759,13 @@ closeSafely(fail[0]); closeSafely(fail[1]); - free(argv); - free(envv); + free(c->argv); + free(c->envv); releaseBytes(env, prog, pprog); releaseBytes(env, argBlock, pargBlock); releaseBytes(env, envBlock, penvBlock); - releaseBytes(env, dir, pdir); + releaseBytes(env, dir, c->pdir); if (fds != NULL) (*env)->ReleaseIntArrayElements(env, std_fds, fds, 0);