I would like to read and process the output of tcpdump line by line as connections come in. So far I am using the Process class. I also want to run as a non-root user, so I have configured sudo:
user1 ALL= NOPASSWD:/usr/sbin/sudo
I am running on RHEL 6.4.
My Java code looks like this:
String[] tcpdumpCmd = {"/usr/bin/sudo", "-n", "/usr/sbin/tcpdump", "-i", "eth0", "port 8561"};
Process p = new ProcessBuilder(tcpdumpCmd).start();
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
tcpdumpOut = null;
tcpdumpLineCnt = 0;
while ((tcpdumpOut = in.readLine()) != null ) {
System.out.println(tcpdumpOut);
tcpdumpLineCnt++;
}
System.out.println("Output lines: " + tcpdumpLineCnt+"\nCommand exit code: " + p.exitValue());
The command works fine, so it is obvious to me what is happening because when I Ctrl-C I get the output printed. So it doing a sort of "buffering" until I interrupt it (at which point the remaining code isn't executed, so I don't get to the RC part.)
I assume that either my problem is in how I handle the output in my while loop, or it just cannot be done in this way. So I kindly ask if anyone has any advice for me.
Related
I´ve run into problem. I want to convert video using ffmpeg but it gives me no output
public void convert(String inputFile, String outputFile, String ... optionalParams) {
ProcessBuilder processBuilder = new ProcessBuilder("ffmpeg", "-i", "\"" + inputFile.trim() +"\"", "\""+ outputFile.trim() + "\"");
DownloadRecord downloadRecord = table.getItems().get(0);
downloadRecord.setStatus("Downloading");
// Try to execute process
try {
// Set the working directory
processBuilder.directory(new File(workingDirectory));
//Start the process
Process process = processBuilder.start();
// Read the output from cmd
BufferedReader r = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader ra = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line;
String errline;
while ((line = r.readLine()) != null) {
System.out.println(line);
}
while ((errline = ra.readLine()) != null) {
System.out.println(errline);
}
process.waitFor();
System.out.println("the end");
} catch(IOException | InterruptedException e) {
System.out.println(e.toString());
}
}
I've been searching on stackoverflow and find some solutions, none worked. What I tried and figured out so far
No output or error output
I tried to remove backslashes from ProcessBuilder, it
also gives me no output
I tried to let the program running, but it never finishes
I tried to use full path to the ffmpeg, no changes
I tried to run the video, no error
I am using
Netbeans IDE so I tried clean and rebuild project, no change
process also never finishes
I would like from it an output. Does someone know what I am doing wrong here ?
I fixed it by reinstalling the ffmpeg. Just went ffmpeg website downloaded newest version, replaced files in folder and it works
Edit:
It just works for files with less thatn 2 mins for some reason, more thatn 2 mins files are behaving like this
I start converting, it will not convert entirely until program runs. After I exit the program it will finish. It´s strange behaviour.
In the following program am giving name as "don" so the command will search activedirectory
with all the names starting with don (like donald etc). But the line2 variable becomes null after the assignment from reader object and it never goes into the loop. What am i doing wrong? FYI: the command works when i give it on the command line.
try {
Process p = Runtime.getRuntime().exec(
"dsquery user -name " + name + "* -limit 200|dsget user -samid -display");
p.waitFor();
BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream()));
String line2 = reader.readLine();
HashMap<String,String> hmap = new HashMap<String,String>();
while (line2 != null) {
line2 = line2.trim();
if (line2.startsWith("dsget")||line2.startsWith("samid")) {
continue;
}
String[] arr = line2.split(" ",1);
hmap.put(arr[0].toLowerCase(),arr[1].toLowerCase());
line2 = reader.readLine();
}
reader.close();
line2 = reader.readLine();
}
If I am not mistaken, the pipe (or redirection) requires to launch the programs with cmd.exe.
Something like:
Process p = Runtime.getRuntime().exec("cmd /c dsquery user -name " + name + "* -limit 200|dsget user -samid -display");
I can see at least some possible problems:
1) as PhiLho wrote: pipe and redirection is done by the shell (sh, bash,... or cmd.exe on Windows). You must handle it in the Java code or run your commands in a shell.
2) after calling waitFor() the Thread is blocked until the process terminates, the process only terminates if you "consume" it's InputStream. This is not happening since waitFor() is still waiting... Better to read and process the InputStream in an additional Thread (or call waitFor after reading the InputStream).
3) reading after closing (2 last lines) should throw an Exception.
Reading the ErrorStream could help find some errors, and checking the return of waitFor is also indicated.
EDIT:
actually there should be some Exceptions being throw by that code.
Are the Exceptions being reported (printStackTrace) or just ignored?
I can run this command from the command line without any problem (the validation script executes):
c:/Python27/python ../feedvalidator/feedvalidator/src/demo.py https://das.dynalias.org:8080/das_core/das/2.16.840.1.113883.4.349/1012581676V377802/otherAdminData/careCoordinators
and from java if I leave off the URL parameter and just do:
String[] args1 = {"c:/Python27/python", "../feedvalidator/feedvalidator/src/demo.py" };
Runtime r = Runtime.getRuntime();
Process p = r.exec(args1);
it works fine. If I use certain URLs for a parameter such as:
String[] args1 = {"c:/Python27/python", "../feedvalidator/feedvalidator/src/demo.py" , "http://www.intertwingly.net/blog/index.atom"};
// or
String[] args1 = {"c:/Python27/python", "../feedvalidator/feedvalidator/src/demo.py" , "http://www.cnn.com"};
it also works fine.
But if I use this particular URL https://das.dynalias.org:8080/das_core/das/2.16.840.1.113883.4.349/1012581676V377802/otherAdminData/careCoordinators, then the script just hangs (java waits for the process to finish). I’m not sure why it works from the command line for that URL but not from a java program. I tried adding quotes to surround the URL parameter but that didn’t work either. I don’t see any character in the URL that I think need to be escaped.
Full Code:
String urlToValidate = "https://das.dynalias.org:8080/das_core/das/2.16.840.1.113883.4.349/1012581676V377802/otherAdminData/careCoordinators";
String[] args1 = {"c:/Python27/python", "C:/Documents and Settings/vhaiswcaldej/DAS_Workspace/feedvalidator/feedvalidator/src/demo.py", urlToValidate };
System.out.println(args1[0] + " " + args1[1] + " " + args1[2]);
Runtime r = Runtime.getRuntime();
Process p = r.exec(args1);
BufferedReader br = new BufferedReader(new InputStreamReader(
p.getInputStream()));
int returnCode = p.waitFor();
System.out.println("Python Script or OS Return Code: " + Integer.toString(returnCode));
if (returnCode >= 2) {
.out.println("OS Error: Unable to Find File or other OS error.");
}
String line = "";
while (br.ready()) {
String str = br.readLine();
System.out.println(str);
if (str.startsWith("line")) {
//TODO: Report this error back to test tool.
//System.out.println("Error!");
}
}
You need to drain the output and error streams of the process, or else it will block when the executed program produces output.
From the Process documentation:
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.
People usually got caught by exec routine hangs in Java. I was cought by that once too. The problem is that the process you are trying to execute may (depending on lot of things) either first write to stdOut or stdErr. If you handle them in wrong order exec will hang. To handle this properly always you must create 2 threads to read stdErr and stdOut simulteneously. Sth like:
Process proc = Runtime.getRuntime().exec( cmd );
// handle process' stdout stream
Thread out = new StreamHandlerThread( stdOut, proc.getInputStream() );
out.start();
// handle process' stderr stream
Thread err = new StreamHandlerThread( stdErr, proc.getErrorStream() );
err.start();
exitVal = proc.waitFor(); // InterruptedException
...
out.join();
err.join();
Read (and close) p.getInputStream() and p.getErrorStream().
For example:
// com.google.common.io.CharStreams
CharStreams.toString(new InputStreamReader(p.getInputStream()));
CharStreams.toString(new InputStreamReader(p.getErrorStream()));
I need to interact with a command line process, e.g. diskpart on windows. The problem: input.readLine() in the following sample leads to a blocking while.
public static void main(String[] args) throws IOException
{
ProcessBuilder processBuilder = new ProcessBuilder("C:\\Windows\\system32\\diskpart.exe");
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
input = new BufferedReader(new InputStreamReader(process.getInputStream()));
output = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
// read #1 code position
String line = null;
while((line = input.readLine())!= null)
System.out.println(line);
// code position #2
System.out.println("This line is never executed");
output.write("list disk" + System.lineSeparator());
output.flush(); // important
}
The output (from read #1 code position) is
Microsoft DiskPart-Version 6.1.7601
Copyright (C) 1999-2008 Microsoft Corporation.
Auf Computer: MYPC
This is correct, however after that nothing happens, e.g. code position #2
System.out.println("This line is never executed");
is never reached. Can anyone tell me, why and how to fix this? Thanks!
Update:
Trying to read byte by byte also seems not to work? ):
InputStreamReader input = new InputStreamReader(process.getInputStream());
int mychar = -1;
while((mychar = input.read()) != -1)
System.out.println(mychar);
System.out.println("This line is never executed");
Because the next thing Diskpart does is show the prompt, which doesn't include a newline:
Microsoft DiskPart version 6.1.7601
Copyright (C) 1999-2008 Microsoft Corporation.
On computer: PCNAME
DISKPART> _
So your code sits there waiting for the newline, which never appears.
You need to change your code to send the "list disk" command at the right time.
Diskpart has an interactive console that requires input from the user. Attempting to read its output like this:
while((line = input.readLine())!= null)
System.out.println(line);
will cause you to wait indefinitely as the application itself requires input.
You need to wait for input first from the windows command so you need to add CMD /C to your command.
As diskpart is interactive, you could try running your list command as a script, so you would have instead:
String[] command = {"CMD", "/C", "C:\\Windows\\system32\\diskpart.exe", "/s", "diskpart.txt"};
ProcessBuilder processBuilder = new ProcessBuilder(command);
with diskpart.txt containing:
list disk
I recommend you getting this working in a standard batch file first though to check that the output is correct.
I am hoping to leverage the unix sort command to sort a large text file in Java. I've tried executing sort with the process builder, but with no luck. However when I print the exact command it is going to execute and copy and paste it into the terminal, it works fine.
So far I've tried executing with /bin/sh -c "", making sure the directory the input file is and where the output file will be is fully permissioned (chmod 777) but with no luck.
Here is the code (if it looks funny, note is using some functions found in Guava)
File inputFile = new File(inputFileName);
//build the command (optional number of sort columns)
List<String> command = new LinkedList<String>();
command.addAll(ImmutableList.<String>of("sort","-t"+delimiter));
for (int i : sortFieldPositions) {
command.add("-k"+i+","+i);
}
command.addAll(ImmutableList.<String>of(inputFileName,">",outputFileName));
//for debugging: output the command that will be executed
System.out.println("Executing: "+Joiner.on(" ").join(command));
//construct and start the process
Process process = new ProcessBuilder(command).redirectErrorStream(true).directory(inputFile.getParentFile()).start();
//for debugging: save process output
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder outputStringBuilder = new StringBuilder();
for (String line; (line = bufferedReader.readLine()) != null; /*reading taking place in check */) {
System.out.println("FROM PROCESS: "+line);
outputStringBuilder.append(line);
}
bufferedReader.close();
if (process.exitValue() != 0) {
//something went wrong
throw new RuntimeException("Error code "+process.exitValue()+" executing command: "+Joiner.on(" ").join(command)+"\n"+outputStringBuilder.toString());
}
Unfortunately this does not work, with the following output:
Executing: sort -t, -k2,2 -k1,1 /tmp/java/TestDataSorterImporterInput.txt /tmp/java/TestDataSorterImporterOutput.txt
FROM PROCESS: sort: stat failed: >: No such file or directory
Edit: It may be helpful to note that if I remove saving the output (> outputfile) from the command, then the command executes without complaint and the sorted version appears in the output from the Processes' input stream)
It is the shell that knows how to perform output redirection. The sort program cannot do it on its own. So if you want redirection, you need to do /bin/sh -c ... to let she shell into the loop.
(You write that you have tried this, but something else must have gone wrong with that).
Try this:
String whatever = "filename";
Process p = Runtime.getRuntime().exec("sort -t -k2 2 -k1 1 " + whatever);
See this site.
Process process = new ProcessBuilder("/bin/bash", "-c", "sort -t'|' -k2").start();