Dealing with i/o streams while using Runtime.exec - java

I am trying to run an scp command within Java. Here is my code,
try {
Process p = Runtime.getRuntime().exec("scp -P" + PORT + " " + FILEPATH + " " + USERNAME + "#" + HOST + ":somefolder/");
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
String line;
while ((line=br.readLine()) != null) {
System.out.println("LINE: " + line);
if (line.contains(USERNAME+"#"+HOST+"'s password:")) {
bw.write(PASSWORD);
bw.newLine();
}
}
System.out.println("end of while.");
} catch (IOException e) {
e.printStackTrace();
}
As you can see, I do not want to print the password blindly to the p.getOutputStream(), which is suggested in almost all similar questions. I want to read the prompt and act accordingly. For example, scp may prompt something like "The authenticity of host 'SOME HOST' can't be established...", which prompts for a yes/no. Or something else that I cannot imagine right now.
The problem with my code is that it never reads a line, although the scp prompts for password, which is seen on the cli. Any suggestion?
EDIT:
I changed the code to use ProcessBuilder, with String[] constructor, as Andrew Thompson suggested. Here is the complete code:
package scptest;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class STest {
// args => filepath, username, host, port, password
public static void main(String[] args) {
try {
ProcessBuilder pb = new ProcessBuilder("scp", "-P", args[3],
args[0], args[1] + "#" + args[2] + ":folder/");
Process p = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(
p.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
p.getOutputStream()));
String line;
bw.write(args[4]);
bw.newLine();
bw.flush();
System.out.println("RETURN: " + p.waitFor());
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
As you can see, I quitted checking the prompts of the scp, and sent the password directly to the input stream of Process. This does not work either! I mean, when I execute the jar file in the commad line, I see the prompt "username#host's password:", and it waits for the password. When I hit enter without entering the password, it just re-asks the password without any error message, which means no string is given to the input stream of the Process, i.e. My BufferedWriter seems doing nothing.
I am compiling my program on a w8 maching with jdk 1.7.0_45, where I run the program on an Ubuntu Server x64 with jre 1.7.0_51.

I found that its easier to configure the rsa files and use the -i option in the scp command.
Just use -i where is the private key of the HOST machine. You'll also need to add the public key from the machine running the command to the authorized keys file in the HOST machine.
That way he won't prompt you for a password a all. Furthermore if you use the -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null options you'll rarely have anything to worry about (which isn't always a good thing!)

Related

Printing commands to bat/cmd file with Java

How to print commands to bat/cmd file using Java? I have created a method that opens this bat file and now the program should write commands to this bat file. For instance, I have a string variable "Command" and the program must write this command to bat file.
Here I attach the code.
private static void openBat(){
File file = new File(lockerPath);
try {
if (file.exists()) {
Process pro = Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + lockerPath);
pro.waitFor();
} else {
System.out.println("file does not exist");
}
} catch (Exception e) {
System.out.println(e);
}
}
This is the code to open bat file, and the next code is to write commands:
private static void printing(int password ){
try {
ProcessBuilder processBuilder = new ProcessBuilder("cmd", "/C", lockerPath);
Process process = processBuilder.start();
process.waitFor();
List<String> commands = new ArrayList<>();
commands.add(String.valueOf(password));
processBuilder.command(commands);
}catch (Exception ex){
ex.printStackTrace();
}
}
It doesn't write anything to the file.
I will be very grateful for your help.
I still don't think I completely understand your question. If you just want to simulate the user entering a value to a batch file via a java program, then the below code does that.
First I wrote a batch file.
#echo off
set /P pw=
echo You entered: %pw%
It simply waits for the user to enter a value and assigns that value to a variable named pw. After the user enters the value, the batch file displays the entered value.
Here is the java code that runs the above batch file and enters a value.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/C", "getusrpw.bat");
try {
Process p = pb.start(); // throws java.io.IOException
BufferedReader stdout = p.inputReader();
BufferedReader stderr = p.errorReader();
BufferedWriter stdin = p.outputWriter();
stdin.write("secret");
stdin.newLine();
stdin.flush();
String output = stdout.readLine();
while (output != null) {
System.out.println("OUT> " + output);
output = stdout.readLine();
}
String error = stderr.readLine();
while (error != null) {
System.out.println("ERR> " + error);
error = stderr.readLine();
}
int exitStatus = p.waitFor(); // throws java.lang.InterruptedException
System.out.println("Process exit status = " + exitStatus);
}
catch (InterruptedException | IOException x) {
x.printStackTrace();
}
}
}
The name of the batch file is getusrpw.bat.
stdout is for reading output generated by the batch file.
stderr is for reading error output. Note that there may be no error output.
stdin is for sending input to the batch file.
Note that methods inputReader, outputWriter and errorReader were added in JDK 17. If you are using an earlier version, use methods getInputStream, getOutputStream and getErrorStream, respectively.
The Java program enters the value secret.
The Java program prints the output generated by the batch file.
When I run the above Java code, it produces the following output:
OUT> You entered: secret
Process exit status = 0

How do I print the list of files and folders in a root directory in linux using java

I am new to both Java and Linux, I was trying to use some Runtime.exec() commands that would allow my program to execute commands in Linux such as "cd /mnt/" and "ls --group-directories-first" to list files and directories contained in /mnt/ but I think I am making a problem with the execution.
I tried my code to only include the "ls --group-directories-first" and it worked like a charm, only problem was, it only listed subdirectories and files in the projects folder. I wanted to make my program go to /mnt/ first so I made my command line to a command array by using exec(String[] cmdarray) format as process1 = Runtime.getRuntime().exec(new String[]{"cd /mnt/","ls --group-directories-first"}); and when I ran it on linux, it just got executed without any printed runtime errors but also without any feedback/printed lines.
Here is my code:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class linCom {
public static void main(String args[]) {
String s;
Process p;
try {
p = Runtime.getRuntime().exec("ls --group-directories-first");
BufferedReader br = new BufferedReader(
new InputStreamReader(p.getInputStream()));
while ((s = br.readLine()) != null)
System.out.println("line: " + s);
p.waitFor();
System.out.println ("exit: " + p.exitValue());
p.destroy();
} catch (Exception e) {}
}
}
This worked and printed out:
"line: DummyFolder1
line: linCom.class
line: linCom.java
exit: 0"
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class linCom {
public static void main(String args[]) {
String s;
Process p;
try {
p = Runtime.getRuntime().exec(new String[]{"cd /mnt/","ls --group-directories-first"});
BufferedReader br = new BufferedReader(
new InputStreamReader(p.getInputStream()));
while ((s = br.readLine()) != null)
System.out.println("line: " + s);
p.waitFor();
System.out.println ("exit: " + p.exitValue());
p.destroy();
} catch (Exception e) {}
}
}
This just got executed with no printed lines.
I expected my program to just go to the /mnt/ directory and print out subdirectories and files on there, but it just got executed with no visible runtime errors and no printed lines.
I have looked at other entries but could not find any answer to my problem.
EDIT: I changed "no errors" with "no error messages" to make it clear that if program had any errors, I did not get any feedback about it.
Here's where the UNIX process model can be confusing.
What you have tries to run the program named cd /mnt/ with the first parameter of ls --group-directories-first . Unix programs can be named anything (they're just filenames) but there's no program named cd /mnt. And anyway, the cd operation is actually performed by a shell, not as a forked/execed program.
You hope to run this shell command from your Java program: cd /mnt/; ls --group-directories-first . The trouble is, Java's .exec() method does not give you a shell, so shell commands don't work.
You can try this instead. It's like running the shell command
/bin/sh -c "cd /mnt/; ls --group-directories-first"
With this, you start a shell, then tell it to run -cthe command you want.
Process p = Runtime.getRuntime().exec(new String[]{"/bin/sh",
"-c",
"cd /mnt/; ls --group-directories-first"});
But it's quite dependent on the machine where your Java program runs, so be careful.
Reference: How to invoke a Linux shell command from Java
Do not use an external process just to list files. Java has plenty of ways to do that. All of them are in the Files class. For example:
Path dir = Paths.get("/mnt");
try (Stream<Path> files = Files.list(dir).sorted(
Comparator.comparing((Path p) -> !Files.isDirectory(p))
.thenComparing(Comparator.naturalOrder()))) {
files.forEach(System.out::println);
}
Do you really need to use Runtime.exec()comands? That would make your code platafform dependent.
You could use File.listFiles():
File folder = new File("/mnt");
for (File f : folder.listFiles()) {
System.out.println(f.getName());
}
That would make the code less plataform dependent

Executing grep command on Linux from Java always returning null

I am executing grep command from java on a linux file. Its always returning null for the following code.
Process p;
String matchStr="testmatch";
String output = null;
try {
String command = "grep \""+matchStr+"\" "+ filename;
System.out.println("Running command: " + command);
p = Runtime.getRuntime().exec(command);
System.out.println("***********************************");
System.out.println("***********************************");
System.out.println("***********************************");
p.waitFor();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
while (br.readLine() != null) {
System.out.println("in while loop");
System.out.println("in while loop");
System.out.println("in while loop");
System.out.println(output);
System.out.println("***********************************");
System.out.println("***********************************");
System.out.println("***********************************");
System.out.println("***********************************");
// Process your output here
}
System.out.println("exit: " + p.exitValue());
p.destroy();
} catch (Exception e) {
e.printStackTrace();
}
If i grep it directly it shows output but from java it never gets into while loop.
Please suggest whats wrong here.
The problem is that you do not write anything to output so it stays null. I guess you have to rewrite your while loop like this
while ((output = br.readLine()) != null) {
// Process your output here
}
Take a note that this syntax is discouraged by most style check due to it's abmiguity
Also it's a good idea to place p.waitFor() after while loop so grep would not hang on flushig std(err|out).
UPDATE
Also it is a good idea to use ProcessBuilder (available since java-7) instead of Runtime.getRuntime().exec(...) because you will have more control over the process i.e
final ProcessBuilder builder = new ProcessBuilder();
builder.command("grep", matchStr, filename);
// redirect stderr to stdout
builder.redirectErrorStream(true);
final Process process = builder.start();
BufferedReader br = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String output = null;
while ((output = br.readLine()) != null) {
System.out.println(output);
// Process your output here
}
process.waitFor();
After turning your code into a https://stackoverflow.com/help/mcve it works for me.
Here the file does not exist:
robert#habanero:~$ rm /home/robert/greptest.txt
robert#habanero:~$ javac GrepTest.java && java GrepTest
Running command: grep test /home/robert/greptest.txt
exit: 2
Now the file does exist but does not contain the text to be found:
robert#habanero:~$ echo not found > /home/robert/greptest.txt
robert#habanero:~$ javac GrepTest.java && java GrepTest
Running command: grep test /home/robert/greptest.txt
exit: 1
Now the file exists and contains the text:
robert#habanero:~$ echo test this > /home/robert/greptest.txt
robert#habanero:~$ javac GrepTest.java && java GrepTest
Running command: grep test /home/robert/greptest.txt
test this
exit: 0
Here is the code:
import java.io.*;
public class GrepTest {
public static void main(String[] args) throws Exception {
String command = "grep test /home/robert/greptest.txt";
System.out.println("Running command: " + command);
Process p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String output;
while ((output = br.readLine()) != null) {
System.out.println(output);
}
System.out.println("exit: " + p.exitValue());
p.destroy();
}
}
I was recently struggling with a similar issue, and I believe I the solution I found is an answer also to your problem (though your question is a bit malformed as others have pointed out).
The issue pertrains to the quote marks around your search string,
\""+matchStr+"\"
The java exec command will literally deliver these to the grep command, and instead of searching for matchStr, grep will be looking for "matchStr", and the results will not be what you are expecting.
This applies also in case one is executing the command as an array like
final Process process = Runtime.getRuntime().exec(new String[] { "grep", "-C1000", searchString, fileName } );
Pass the plain searchString without including quotation marks into the string.

Java getting bash root access

I am trying to install missing dependencies on a Linux for a program I am making. I however am failing at getting the root access required to install the missing dependencies. Here is what I have so far:
My logic is as follows:
1) Check if the dependency is installed using pacapt (npm in this case)
2) if so then get the user password using a text prompt
3) then continue further instructions like so: echo [userpass] | sudo -S ...
Right now the 'echo [userpass] | sudo -S ...' command gets printed out to the shell like so; [userpass] | sudo -S ... (where the user password is displayed in place of [userpass]), but does not execute.
And here is my code:
public class LinuxDependencyCheck extends Application{
public static void main (String [] args){
launch(args);
}
#Override
public void start(Stage mainWindow){
String userPass = null;
String terminalOut = null;
terminalOut = runBash("./LinuxScripts/pacapt -Qqe npm");
if (terminalOut.equals("npm")){
userPass = getUserPass();
if (userPass != null){
System.out.println("runing");
runBash("echo " + userPass + " | sudo -S npm install" +
" phantomjs2");
}
}
}
public String runBash(String runCommand){
String result = null;
String returnVal = null;
try {
Runtime r = Runtime.getRuntime();
Process p = r.exec(runCommand);
BufferedReader in =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println(inputLine);
result += inputLine;
returnVal = inputLine;
}
in.close();
} catch (IOException e) {
System.out.println(e);
}
return returnVal;
}
public String getUserPass(){
TextInputDialog dialog = new TextInputDialog("Password");
dialog.setTitle("Installation helper");
dialog.setHeaderText("It looks like you are missing" +
" dependecies to complete this action" +
" would you like to try to install" +
" them now");
dialog.setContentText("Please enter your password :");
// Traditional way to get the response value.
Optional<String> result = dialog.showAndWait();
if (result.isPresent()){
return result.get().toString();
}
return result.get();
}
}
Your runBash() method is poorly named, as it does nothing to cause the given command to be run via bash. It is therefore also inappropriate for use with a command string such as you are specifying, which relies on the shell's pipe operator to string two separate commands together.
When you do this ...
runBash("echo " + userPass + " | sudo -S npm install" +
" phantomjs2");
... Java splits the string on whitespace, takes the first substring ("echo") as the command, and executes that command with all the other substrings strings as arguments. Needless to say, that will run without error, but also without the effect you intended.
If you really want to execute the command string via bash (as it appears you do), then in your runBash() method you could change this ...
Process p = r.exec(runCommand);
... to this ...
Process p = r.exec("/bin/bash", "-c", runCommand);
. That should at least get you past your first hurdle.
You also should close the Process's OutputStream (by which you could have piped data into the process), and drain the Process's error stream. In general, you need to drain the input and error streams in parallel, because if either one's buffer fills up then the process can block. Perhaps that's not a risk for this particular command, but you'll need to judge. It's also good form to waitFor() the Process; doing so may avoid Java accumulating zombie child processes.

Download an exe file and run

I've been trying to make it where I can download a .exe file from the web, read it, and write it to a file locally, and then execute.
URL url = new URL("http://www.ddlands.com/downloads/Calc.exe");
URLConnection c = url.openConnection();
BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
String line = "";
File file = new File("analbread"+".exe");
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
while((line = br.readLine()) != null){
bw.write(line + "\n");
}
br.close();
bw.close();
Process r = Runtime.getRuntime().exec("analbread" + ".exe");
System.out.println(r.toString());
System.out.println("WORKS!");
Although I know that doesn't work due to using BufferedWriter, and i'm not sure if it runs the exe.
For the downloading part, you'll need to use binary read/write. See this for further details: Working unbuffered streams.
For the executing part, the problem is that the Runtime.exec()-method can't launch your executable file.
At least under Linux (I can't test it on Windows), you'll need the full path to the executable file (or use ./[file] when the file is in the same directory as your application) to be able to execute it.
Only giving the command works for executables which are part of your systems PATH-variable.
Have a look at ClickOnce: http://en.wikipedia.org/wiki/ClickOnce
We've used that succesfully.
Ive used the following with good results to run command line scripts. You can create a batch script that runs the executable or run it directly using the exec method - probably pass "cmd ". This opens a command prompt from which you can run anything.
public static void runScript(String batchFile, boolean waitForExit0, int retryTime)
{
try
{
String runString = "cmd /c start " + (waitForExit0?"/wait ":"") + "/MIN " + batchFile;
Process p = Runtime.getRuntime().exec(runString); // /c start /wait
while (true)
{
try
{
int exit = p.exitValue();
if (exit == 0)
{
System.out.println("completed: " + runString);
return;
}
}
catch(Exception e)
{
String s = "";
}
Thread.sleep(retryTime);
}
}
catch(Exception e)
{
String s = "";
}
}

Categories