Java hangs even if the execution of the script is complete
I'm trying to execute a script from my java code, as follows:
Process p = Runtime.getRuntime().exec(cmdarray,envp,dir); // cmdarray is a String array // consisting details of the script and its arguments final Thread err = new Thread(...); // Start reading error stream err.start(); final Thread out = new Thread(...); // Start reading output stream out.start(); p.waitFor(); // Close resources
The execution of the script is over (its PID no longer exists), but Java still stays on the waitfor () method of the process! Yes, I'm reading the output and error streams in 2 separate threads Yes, they finally joined (after waitfor())
The script basically installs several RPMs (such as about 10) and configures them So the script ran for more than 60 seconds
It looks similar to the following:
#!/bin/sh #exec 3>&1 >/var/log/some_log 2>&1 # If the above line is uncommented,Java recognizes that the # process is over and terminates fine. tar xzf a-package-having-rpms.tar.gz cd unpacked-folder (sh installer-script.sh) #This installs 10 odd rpms in a subshell and configures them cd .. rm -rf unpacked-folder exit 0
Surprisingly, if I put the following line in the script (at the top), Java understands that the script is over and it terminates the process perfectly
exec 3>&1 > /var/log/some_log 2>&1
For records, the script does not generate any output Zero characters! So it makes no sense to put the exec statement there!
But, magically, putting exec statements in scripts makes Java work! Why?
How can I avoid that illogical exec statement in the script?
If you are interested in installer script If you are interested in the content of SH, then:
#!/bin/sh exec 3>&1 >>/var/log/another-log.log 2>&1 INSDIR=$PWD RPMSDIR=$INSDIR/RPMS cd $RPMSDIR # rpm -i java-3.7.5-1.x86_64.rpm rpm -i --force perl-3.7.5-1.x86_64.rpm rpm -i --nodeps MysqL-libs-5.0.51a-1.vs.i386.rpm rpm -i --nodeps MysqL-5.0.51a-1.vs.i386.rpm rpm -i --nodeps MysqL-server-5.0.51a-1.vs.i386.rpm rpm -i --nodeps perl-DBD-MysqL-3.0007-2.el5.i386.rpm rpm -i --nodeps perl-XML-Parser-2.34-6.1.2.2.1.i386.rpm . . .
Now, why does Java need the exec command to know that the process is over? How can I avoid that exec?, Especially because the first script does not produce any output
Hold your breath and wait for the answer!
Solution
My guess is that Java won't think the script is over until the pipeline it passes to it through stdin / stdout / stderr is closed by the child process That is, there are no more active reader processes on stdin and no more active writer processes on stdout / stderr
When you read on the pipeline, you will not receive the end of file instruction until no more processes open the pipeline output Therefore, if the process forks and the new process inherits an open file handle, the original process terminates, there is still an open file process, and the reader will still wait
Similar to the pipeline you are writing, you will not receive a "pipe break" signal until the last reader closes the pipeline
This problem usually occurs when your script separates background tasks that inherit stdin / stdout / stderr, such as newly installed services
By using exec, you explicitly break the inheritance chain of these pipes so that the background process does not use them
If on Linux, check whether / proc / * / FD has any new services and whether their stdin / stdout / stderr is the same as the pipeline passed by the java process to the script
Run / etc / init The same situation often occurs when you run D / xxx scripts: they complete normally when you run them from the command line, but they seem to hang when you run them from some monitor
Edit:
You said the installer script contains the following lines:
exec 3>&1 >>/var/log/another-log.log 2>&1
The first item, 3 > & 1, clones stdout to file descriptor 3 (see redirections in man bash) As far as I know, FD 3 has no special significance It then opens / var / log / other - log Log replaces stdout and replaces stderr by cloning stdout See the "redirection" section of the bash man page
This means that the new file descriptor 3 is opened for writing on the pipe originally passed in as stdout Programs that expect to become system service daemons typically close file descriptors 0 (stdin), 1 (stdout), and 2 (stderr), but may not disturb any other file descriptors In addition, now that the shell has opened FD - 3, it passes the open file to any commands it executes, including background commands
Do you know any special reason why the installer opens FD 3? My guess is that if you just remove the term "3 > & 1" from the installer, your problem will be solved This will allow you to completely remove exec from the script