Running an Executable from a JAVA API in LINUX

  • Thread starter dhaval dalal via JavaKB.com
  • Start date
D

dhaval dalal via JavaKB.com

Hi,

I want to run a C++ executable from my JAVA API in Linux. This is the
sample of the code which i have used
////////
String[] arguments = new String[5] ;

arguments[0] = "/usr/local/code/fun/dcmdump";
arguments[1] = "+f";
arguments[2] = "/usr/local/code/fun/240.dcm";
arguments[3] = "+W";
arguments[4] = "/usr/local/code/fun";

Process p = Runtime.getRuntime().exec(arguments);

///////////
On running the test file, the program seems to hang there. dcmdump is the
name of my c++ executable and arguments [1] - [4] are its inputs. dcmdump
should accepts these inputs and dump the contents of the file 240.dcm into
the path mentioned by the last argument.
But my program jst hangs. Is the the correct way to go about??
Any suggestions or ideas!!
Please let me know
Thanks
Dhaval
 
T

Tilman Bohn

In message <[email protected]>,
dhaval dalal via JavaKB.com wrote on Tue, 01 Mar 2005 21:22:48 GMT:

[...]
On running the test file, the program seems to hang there. dcmdump is the
name of my c++ executable and arguments [1] - [4] are its inputs. dcmdump
should accepts these inputs and dump the contents of the file 240.dcm into
the path mentioned by the last argument.
But my program jst hangs. Is the the correct way to go about??
Any suggestions or ideas!!

Does dcmdump also print anything to STDIN or STDERR? If it does, you
want to read from them, because otherwise it will block when the corres-
ponding buffers are full, leading to what you observe.
 
K

klynn47

As the first two elements of arguments use

arguments[0] = "/bin/sh"
arguments[1] = "-c"
 
D

dhaval dalal via JavaKB.com

I did change the arguements so that i had a total of [7] with the first two
being what u said... It did not work. Can you enlighten me on that?
 
D

dhaval dalal via JavaKB.com

I also read a very nice article by Java world on this topic
http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html

But still am confused.
do i need to capture the output from my executable even though dcmdump is
supp to write back the file to the hd. i dont need to use this file in my
app later. all i need is for the dcmdump to generate the file. thts all
any suggestions..
also the article mentions for creating an input stream to pass parameters
to the executable..
do i need to use one or is the code which i posted for passing arguements
to the executable fine?
let me know
 
T

Tilman Bohn

In message <[email protected]>,
dhaval dalal via JavaKB.com wrote on Tue, 01 Mar 2005 22:38:18 GMT:

[...]
But still am confused.
do i need to capture the output from my executable even though dcmdump is
supp to write back the file to the hd.

It doesn't matter how many files the program produces. If it _also_
writes to STDOUT and/or STDERR, it _will_ block unless someone (i.e.,
you) consume that output. You don't have to do anything with it, but
you do have to empty the buffers used for that.
i dont need to use this file in my
app later. all i need is for the dcmdump to generate the file. thts all
any suggestions..

When you run the program from the console, does _any_ output appear?
If yes, that's your problem.
also the article mentions for creating an input stream to pass parameters
to the executable..
do i need to use one or is the code which i posted for passing arguements
to the executable fine?

That was fine as far as I remember. I didn't read the article (no
time), but I've never heard of using an OutputStream for positional
parameters. And getOutputStream() only connects you the process's STDIN,
which is something entirely different. Are you sure you didn't
misunderstand?
 
D

dhaval dalal via JavaKB.com

Hi
I did the following
String[] cmd ={"/bin/sh","-
c","/usr/local/code/fun/dcmdump","+f","/usr/local/code/fun/240.dcm","+W","/usr/local/code/fun"};
Process p = Runtime.getRuntime().exec(cmd);
When i execute my application, i see the following output
[root@voltaire fun]# java TestRead2
"
$dcmtk: dcmdump v3.5.3 2004-05-27 $

dcmdump: Dump DICOM file and data set
usage: dcmdump [options] dcmfile-in...

parameters:
dcmfile-in DICOM input filename to be dumped

general options:
"
I try to write the arguments in the command line when i execute but still
get the same output.
I dont think dcmdump is receving any arguements..
Executing /bin/sh does it mean you have to mention the arguements in the
command in the same manner that i have the String[] cmd??
PLease let me know..
Also i have put a Buffered reader to capture the output of dcmdump but I
dont think i have reached my application has reached thr as yet. The code i
have written is

// put a BufferedReader on the output

InputStream inputstream = p.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

// read the output

String line;
while ((line = bufferedreader.readLine()) != null) {
System.out.println(line);
}

Please let me know your suggestions/ideas.
thanks
 
T

Tilman Bohn

Hi
I did the following
String[] cmd ={"/bin/sh","-
c","/usr/local/code/fun/dcmdump","+f","/usr/local/code/fun/240.dcm","+W","/usr/local/code/fun"};
Process p = Runtime.getRuntime().exec(cmd);
When i execute my application, i see the following output
[root@voltaire fun]# java TestRead2
"
$dcmtk: dcmdump v3.5.3 2004-05-27 $

dcmdump: Dump DICOM file and data set
usage: dcmdump [options] dcmfile-in...
[...]

Are you _sure_ you are calling it the right way? That is, you _know_ that
when you type `/usr/local/code/fun/dcmdump +f /usr/local/code/fun/240.dcm
+W /usr/local/code/fun' at the command line it works as intended? Ten bucks
says you'll get this same message.

Based on the above usage message I'd try `/usr/local/code/fun/dcmdump +f
+W /usr/local/code/fun /usr/local/code/fun/240.dcm' instead.
 
Z

Zoe Stephenson

dhaval dalal via JavaKB.com said:
Hi
I did the following
String[] cmd ={"/bin/sh","-
c","/usr/local/code/fun/dcmdump","+f","/usr/local/code/fun/240.dcm","+W","/usr/local/code/fun"};
Process p = Runtime.getRuntime().exec(cmd);
When i execute my application, i see the following output
[root@voltaire fun]# java TestRead2
"
$dcmtk: dcmdump v3.5.3 2004-05-27 $

dcmdump: Dump DICOM file and data set
usage: dcmdump [options] dcmfile-in...

parameters:
dcmfile-in DICOM input filename to be dumped

general options:
"
I try to write the arguments in the command line when i execute but still
get the same output.
I dont think dcmdump is receving any arguements..
Executing /bin/sh does it mean you have to mention the arguements in the
command in the same manner that i have the String[] cmd??

When you pass a string to the -c argument of sh, the shell expects
one argument which it will then parse itself. So, you'll need to
make a single string out of the original command if you want to run
it via another shell:

String[] cmd ={"/bin/sh","-c",
"/usr/local/code/fun/dcmdump +f /usr/local/code/fun/240.dcm +W /usr/local/code/fun"};

The shell will then turn that string into the separate arguments for
your dcmdump program. This will make your code even more dependent
on a UNIX type environment than it was before, of course.
 
G

Gordon Beaton

When you pass a string to the -c argument of sh, the shell expects
one argument which it will then parse itself.

I'd just like to add that there is usually no reason to explicitly run
a shell at all in order to execute a child process.

If you don't need shell features like redirection or expansion of
command line arguments, you can write the command like this:

String[] cmd = {
"/usr/local/code/fun/dcmdump",
"+f",
"/usr/local/code/fun/240.dcm",
"+W",
"/usr/local/code/fun"
};

/gordon
 
T

Tilman Bohn

In message <[email protected]>,
Zoe Stephenson wrote on 2 Mar 2005 11:04:49 GMT:

[calling sh -c instead of the executable itself]
The shell will then turn that string into the separate arguments for
your dcmdump program. This will make your code even more dependent
on a UNIX type environment than it was before, of course.

And make you vulnerable to shell injection attacks, if a part of that
single argument that now represents your command line is to be supplied
externally. Unless you are _very_ sure you're only ever passing sane and
sanitary stuff, you do not want to go through the shell if you can at
all avoid it.
 
T

Tilman Bohn

In message <[email protected]>,
Gordon Beaton wrote on 2 Mar 2005 12:32:07 +0100:

[...]
I'd just like to add that there is usually no reason to explicitly run
a shell at all in order to execute a child process.

This is true. All it will buy you in most cases is a security
hole.
If you don't need shell features like redirection or expansion of
command line arguments, you can write the command like this:

String[] cmd = {
"/usr/local/code/fun/dcmdump",
"+f",
"/usr/local/code/fun/240.dcm",
"+W",
"/usr/local/code/fun"
};

IIRC, that's what the OP originally did, but he got sidetracked by
someone suggesting his problems might be solved by going through the
shell. Which they weren't. I think he's just passing the parameters
in the wrong order.
 
D

dhaval dalal via JavaKB.com

Hi Tilman, GOrdon and Zoe.
Thnaks for yr help... Well i got got my code to wrk. This is what i did
////////
String[] cmd = {
"/usr/local/code/fun/dcmdump",
"+f",
"/usr/local/code/fun/240.dcm",
"+W",
"/usr/local"
};

Process p = Runtime.getRuntime().exec(cmd);

// put a BufferedReader on the output

InputStream inputstream = p.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

// read the output

String line;
while ((line = bufferedreader.readLine()) != null) {
System.out.println(line);
}

// check for dcmdump failure

try {
if (p.waitFor() != 0) {
System.err.println("exit value = " + p.exitValue());
}
}
catch (InterruptedException e) {
System.err.println(e);
}
//////

I dont have to go though the shell command now.:) So much for security..
Well if i remove the buffered reader it again hangs. I guess thats what
tilman was talking abt earlier cause my dcmdump does print something out to
stdout.
I figured, when java generated process, it provides computer resource for
new subprocess( dcmdump) and when subprocess is created ,this will replace
daemon process genearated java. java now waits endlessly (you can see
Process has a method waitFor()) until subprocess terminated..
IS this correct.. any pointers
Dhaval
 
G

Gordon Beaton

I dont have to go though the shell command now.:) So much for
security.. Well if i remove the buffered reader it again hangs. I
guess thats what tilman was talking abt earlier cause my dcmdump
does print something out to stdout.

I figured, when java generated process, it provides computer
resource for new subprocess( dcmdump) and when subprocess is created
,this will replace daemon process genearated java. java now waits
endlessly (you can see Process has a method waitFor()) until
subprocess terminated.. IS this correct.. any pointers

When Java creates the child process, a pipe connects the input and
output streams of the process with the streams available to your
application in the Process object returned by Runtime.exec(). Each of
those pipes has only limited buffer space.

If the child process writes to stdout or stderr and your application
does not read from the corresponding stream, the pipe buffer will fill
causing the child process to block until your application reads from
it.

In other words, while you are waiting for the child process to
terminate, it is waiting for you to read. Your processes are
deadlocked.

You *must* read any output the child process provides, or make sure it
doesn't print to either stdout or stderr.

/gordon
 
T

Tilman Bohn

In message <[email protected]>,
dhaval dalal via JavaKB.com wrote on Wed, 02 Mar 2005 15:50:44 GMT:

[...]
// put a BufferedReader on the output

Finally! ;-)

[...]
I dont have to go though the shell command now.:) So much for security..
Well if i remove the buffered reader it again hangs.

I hate to say this but -- I told ya! :)
I guess thats what
tilman was talking abt earlier cause my dcmdump does print something out to
stdout.
I figured, when java generated process, it provides computer resource for
new subprocess( dcmdump)

Yes. This process will have three special file descriptors allocated
for it for STDIN, STDOUT and STDERR, including corresponding buffers. If
no-one reads from the latter two or otherwise flushes them, the process
has no recourse but to block on writing to them, waiting for buffer
space to become available.

And this is nothing to do with Java, really. You might wonder why the
VM doesn't silently flush the buffer if you're not reading, but how
should it know (in general) whether your Java program might not in five
seconds decide to spawn another thread that starts trying to read from
those streams. You wouldn't like that to find the buffer having been
flushed behind it's back, now would you? So it can only wait patiently
for the application that started the process to deal with what the
process is doing.
and when subprocess is created ,this will replace
daemon process genearated java. java now waits endlessly (you can see
Process has a method waitFor()) until subprocess terminated..
IS this correct.. any pointers

Sorry, I don't quite understand what you mean here.
 
D

dhaval dalal via JavaKB.com

Thnaks Gordon
This short code i did was for testing Purposes..
I went on to integrate it with my Main JAva Application.
I problem is I have an input stream in Java which i need to pass to the
executable in place of the filename i.e replace
/usr/local/code/fun/240.dcm with source(input stream) as one of the
arguements.
I think pipe is a good option but i am not forking a child here right?? its
just a system call so my parent **is not** piped to my child??
Whats the best way to go about this..
Thanks
Dhaval
 
G

Gordon Beaton

I problem is I have an input stream in Java which i need to pass to
the executable in place of the filename i.e replace
/usr/local/code/fun/240.dcm with source(input stream) as one of the
arguements.

You have at least two ways to solve that:

1. write the complete contents of the stream to a temporary file, then
pass that filename as an argument when you start the child process.
Afterwards, delete the temporary file.

2. if the child process can read from standard input, then start the
child with appropriate options to do so. While the child is
running, copy from your stream to the one returned by
Process.getOutputStream().
I think pipe is a good option but i am not forking a child here
right?? its just a system call so my parent **is not** piped to my
child??

The pipes are *not* an option, they are always there. It's the way
Java sets up the child process, and you have no choice in this matter.
No, it's *not* just a system call, you *are* forking a child here.

/gordon
 

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

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top