JNI Problem

A

Avijit Samal

Hi All,
We are faced with a very peculiar problem involving JNI as follows..

We have C function which closes stdout and stderr and then execs
/sbin/mkfs for a disk partition. This function ,when

executed from C ,is going through fine. But when executed from JNI, it
goes in an infinite loop while writing to stderr.

strace showed that write returns a 0 and it keeps retrying infinitely.


The Code :
========


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <ctype.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <jni.h>
#include "NodeTest.h"




JNIEXPORT void JNICALL Java_NodeTest_nodeTest
(JNIEnv *env, jclass clazz,jstring nodeName)
{
main();
}




int
closeStdOutErr(int *new_out, int *new_err)
{

#if 0
*new_out = dup(STDOUT_FILENO);
if (*new_out == -1) {
return (-1);
/* NOTREACHED */
}
#endif

*new_err = dup(STDERR_FILENO);
if (*new_err == -1) {
return (-1);
/* NOTREACHED
* */
}
close(STDOUT_FILENO);
close(STDERR_FILENO);
return (0);
/* NOTREACHED * */
}

int
reopenStdOutErr(int prev_out, int prev_err)
{
int std_out, std_err;

std_out = dup2(prev_out, STDOUT_FILENO);
if (std_out == -1) {
return (-1);
/* NOTREACHED */
}
std_err = dup2(prev_err, STDERR_FILENO);
if (std_err == -1) {
return (-1);
/* NOTREACHED */
}

return (0);
/* NOTREACHED */
}

main ()
{
int num;
int fd1, fd2;
int stdoutFD, stderrFD, status, result;
char *args[] = {"/sbin/mkfs", "-t", "ext3", "/dev/sdb6", NULL};
int pid;


pid = fork ();
if (pid < 0) { //fork error
//log error
return -errno;
}

if (pid == 0) { //child
/* Execute the given command */
closeStdOutErr(&stdoutFD, &stderrFD);
result = execv("/sbin/mkfs", args);
if (result < 0) {
//log error
reopenStdOutErr(stdoutFD, stderrFD);
printf ("Exec failed\n");
}
} else { //parent
/* wait for the exe to exit*/
result = waitpid(pid, &status, 0);
if (result < 0) {
printf ("Abnormal exit. Errno: %d\n", errno);
exit (1);
}
result = WIFEXITED(status);
if (result != 0) { //exe exited normally
/* return the exit status*/
result = WEXITSTATUS(status);
}
}
fprintf (stdout, "ret: %d\n", result);
}






////////////////////////////////////////




strace -p <pid>

write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
..........
..........<continues infinitely>
 
J

Joseph Millar

We have C function which closes stdout and stderr and then execs
/sbin/mkfs for a disk partition. This function ,when

Oh, that's bad. Very bad. Very very bad. Shall I go on?
executed from C ,is going through fine. But when executed from JNI, it
goes in an infinite loop while writing to stderr.

You're running in two very different environments. While in
the context of the C program, you have control of stdout
and stderr, but in a JVM environment, the JVM has control.
By pulling out OS resources from underneath the JVM, you've
angered the Java gods.

What you have here is a typical unix style C program that
forks the mkfs command. Why you would want to take that
program, which makes all kinds of assumptions about it's
environment, and try to execution in the context of a JNI
function call is beyond me. Especially when Java already
provides the Runtime.exec() method which does exactly what
this program does. There is no need for JNI at all here.
It's just vastly complicating the situation and is
completely unnecessary.

Forget JNI and look at the java.lang.Runtime class and its
various exec() methods. If you need samples, there are
lots of them floating around in this forum and others,
do a simple google search to find them.

If you are just do this to paly with JNI, then stick with
something more suited to JNI's purpose. Think of a JNI
method as a function in a shared library. It is not
standalone, it's designed to be called from inside another
program and to be well behaved and not steal things out
from under the caller, unless it understands what it's
doing. You must be aware that any C functions you call
have to play nicely inside a JVM environment. For example,
it very common practice to set a SIGINT handler in Unix
programs but if you try this from inside a JNI method, you
will put yourself in a world of hurt since the JVM already
has a whole series of handlers in place and needs them for
correct operation, SIGINT itself is used to detect and
handle NullPointerException's.

Now I've never played with the fork() and exec() functions
on Unix before (just never had the need), so I'm not
saying it can't be done from inside JNI, just that this code
messing with stdout and sterr is going to cause problems
in a JVM environment.

Hope this helps.

--Joe
 
S

Steve W. Jackson

:> Hi All,
:> We are faced with a very peculiar problem involving JNI as follows..
:>
:> We have C function which closes stdout and stderr and then execs
:> /sbin/mkfs for a disk partition. This function ,when
:>
:> executed from C ,is going through fine. But when executed from JNI, it
:> goes in an infinite loop while writing to stderr.
:>
:> strace showed that write returns a 0 and it keeps retrying infinitely.
:>
:>
:> The Code :
:> ========
:>
:>
:> #include <stdio.h>
:> #include <stdlib.h>
:> #include <string.h>
:> #include <unistd.h>
:> #include <limits.h>
:> #include <sys/types.h>
:> #include <sys/stat.h>
:> #include <sys/time.h>
:> #include <ctype.h>
:> #include <errno.h>
:> #include <sys/socket.h>
:> #include <netinet/in.h>
:> #include <sys/types.h>
:> #include <sys/wait.h>
:> #include <arpa/inet.h>
:> #include <netdb.h>
:> #include <errno.h>
:> #include <jni.h>
:> #include "NodeTest.h"
:>
:>
:>
:>
:> JNIEXPORT void JNICALL Java_NodeTest_nodeTest
:> (JNIEnv *env, jclass clazz,jstring nodeName)
:> {
:> main();
:> }
:>
:>
:>
:>
:> int
:> closeStdOutErr(int *new_out, int *new_err)
:> {
:>
:> #if 0
:> *new_out = dup(STDOUT_FILENO);
:> if (*new_out == -1) {
:> return (-1);
:> /* NOTREACHED */
:> }
:> #endif
:>
:> *new_err = dup(STDERR_FILENO);
:> if (*new_err == -1) {
:> return (-1);
:> /* NOTREACHED
:> * */
:> }
:> close(STDOUT_FILENO);
:> close(STDERR_FILENO);
:> return (0);
:> /* NOTREACHED * */
:> }
:>
:> int
:> reopenStdOutErr(int prev_out, int prev_err)
:> {
:> int std_out, std_err;
:>
:> std_out = dup2(prev_out, STDOUT_FILENO);
:> if (std_out == -1) {
:> return (-1);
:> /* NOTREACHED */
:> }
:> std_err = dup2(prev_err, STDERR_FILENO);
:> if (std_err == -1) {
:> return (-1);
:> /* NOTREACHED */
:> }
:>
:> return (0);
:> /* NOTREACHED */
:> }
:>
:> main ()
:> {
:> int num;
:> int fd1, fd2;
:> int stdoutFD, stderrFD, status, result;
:> char *args[] = {"/sbin/mkfs", "-t", "ext3", "/dev/sdb6", NULL};
:> int pid;
:>
:>
:> pid = fork ();
:> if (pid < 0) { //fork error
:> //log error
:> return -errno;
:> }
:>
:> if (pid == 0) { //child
:> /* Execute the given command */
:> closeStdOutErr(&stdoutFD, &stderrFD);
:> result = execv("/sbin/mkfs", args);
:> if (result < 0) {
:> //log error
:> reopenStdOutErr(stdoutFD, stderrFD);
:> printf ("Exec failed\n");
:> }
:> } else { //parent
:> /* wait for the exe to exit*/
:> result = waitpid(pid, &status, 0);
:> if (result < 0) {
:> printf ("Abnormal exit. Errno: %d\n", errno);
:> exit (1);
:> }
:> result = WIFEXITED(status);
:> if (result != 0) { //exe exited normally
:> /* return the exit status*/
:> result = WEXITSTATUS(status);
:> }
:> }
:> fprintf (stdout, "ret: %d\n", result);
:> }
:>
:>
:>
:>
:>
:>
:> ////////////////////////////////////////
:>
:>
:>
:>
:> strace -p <pid>
:>
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> write(2, "mke2fs 1.27 (8-Mar-2002)\n", 25) = 0
:> .........
:> .........<continues infinitely>

Have you considered the possibility that you may not really be able to
close either of those streams? The JVM itself opens them on startup.
You're able to redirect them. You're even be allowed to close them in
Java code -- I just tried. But it seems possible that there could be a
problem closing them in external JNI code.

Just a thought...

= Steve =
 
M

Manish Jethani

Joseph said:
JNI is a great tool and when you need it, it's a godsend. But
it shouldn't be your tool of first resort, unless you understand
the consequences.

When you have a 10-year old working program with a large user
base, and it needs to be integrated into an all-new Java/J2EE
environment, then JNI happens :)

When there's a lot of perfectly working black-magic code written
in C, and the programmers have left the company to become
Buddhist monks, then the C code cannot be thrown away, and new
code must be written Java, and JNI happens :)

Manish
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top