I have a simple GUI program where one of the features is to ping a destination from a file. I have the ping running fine when using normal ping x.x.x.x however when running this with the -t command i have noticed that even after closing the command window ping.exe is still showing in the process list. I know the process can be ended using ctrl+c but is there a different way to get the process to end when the user closes the cmd window?
i am currently using this code:
try {
ipPing = VNC.getIp().concat(ext);
String command = "ping " + ipPing;
Runtime rt = Runtime.getRuntime();
rt.exec(command);
rt.exec(new String[]{"cmd.exe", "/C", "\"start;" + command + "\""});
} catch (IOException e) {
}
any advice and tips would be greatly appreciated
I'm not sure will it work, but you can try Process.destroy(). Something like this:
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/C", "\"start;" + command + "\"");
Process p = pb.start();
//...
p.destroy();
Also, don't write empty catch blocks:
} catch (IOException e) {
}
because if an exception gets thrown it'll be hard to notice. Unless of course, you know that you can ignore the exception.
UPDATE:
proof of concept for linux os:
public static void main(String[] args) throws IOException {
ProcessBuilder pb = new ProcessBuilder("ping","localhost");
pb.redirectErrorStream(true);
Process p = pb.start();
InputStreamReader isr = new InputStreamReader(p.getInputStream());
int ch,count = 0;
StringBuffer sb = new StringBuffer();
while((ch =isr.read()) > -1) {
sb.append((char)ch);
if ((char)ch == '\n') {
System.out.println( sb.toString());
sb = new StringBuffer();
}
if (count++ == 2) {
System.out.println("destroying process");
p.destroy();
}
}
}
outputs:
destroying process
PING localhost (127.0.0.1) 56(84) bytes of data.
Exception in thread "main" java.io.IOException: Stream closed
64 bytes from localhost (127.0.0.1): icmp_req=1 ttl=64 time=0.031 ms
at java.io.BufferedInputStream.getBufIfOpen(BufferedInputStream.java:145)
at java.io.BufferedInputStream.read(BufferedInputStream.java:308)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
at sun.nio.cs.StreamDecoder.read0(StreamDecoder.java:107)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:93)
at java.io.InputStreamReader.read(InputStreamReader.java:151)
at com.infobip.rhino.Killer.main(Killer.java:24)
Java Result: 1
the lines are messed up because the error stream is redirected to output stream
Related
I need to build a java program to reset network in windows 10, this command needs cmd to be opened as administrator I tried to build it, but it gives me this error
Cannot run program "runas /profile /user:Administrator "cmd.exe /c Powrprof.dll,SetSuspendState"": CreateProcess error=2, The system cannot find the file specified
this is my code
try {
String[] command
= {
"runas /profile /user:Administrator \"cmd.exe /c Powrprof.dll,SetSuspendState\"",};
Process p = Runtime.getRuntime().exec(command);
new Thread(new SyncPipe(p.getErrorStream(), System.err)).start();
new Thread(new SyncPipe(p.getInputStream(), System.out)).start();
PrintWriter stdin = new PrintWriter(p.getOutputStream());
stdin.println("netsh winsock reset");
stdin.close();
int returnCode = p.waitFor();
System.out.println("Return code = " + returnCode);
stat_lbl.setText("Network reset Successfully");
} catch (IOException | InterruptedException e) {
System.out.println(e.getMessage());
}
I don't understand what the problem is and how can I resolve it
You're giving the command as an array with a single element, which is treated as a single command. You're already giving the command as an array - split it accordingly, where runas is the command, and everything else is an argument to runas:
String[] command = {
"runas",
"/profile",
"/user:Administrator",
"cmd.exe /c Powrprof.dll,SetSuspendState",
};
Note that you don't have to add quotes to the last argument.
You can make your program a bit better by using ProcessBuilder. Now you're redirecting streams yourself, but you can easily let ProcessBuilder handle that for you:
Process p = new ProcessBuilder(command).inheritIO().start();
PrintWriter stdin = new PrintWriter(p.getOutputStream());
stdin.println("netsh winsock reset");
stdin.close();
int returnCode = p.waitFor();
System.out.println("Return code = " + returnCode);
I'm having trouble with ProcessBuilder not running a command on the server.
Early in my project I use Runtime.exec() just to retrieve output from a program which works fine:
private List<SatelliteCode> getSatelliteCodes() {
List<SatelliteCode> codes = new ArrayList<>();
Runtime runtime = Runtime.getRuntime();
String[] commands = { "w_scan", "-s?" };
Process process;
try {
process = runtime.exec(commands);
BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String s = error.readLine(); // discard first line
while ((s = error.readLine()) != null) {
s = s.trim();
int i = s.indexOf('\t'); // separated by a tab!?!?
codes.add(new SatelliteCode(s.substring(0, i), s.substring(i)));
}
} catch (IOException e) {
e.printStackTrace();
}
return codes;
}
Running this in the terminal works fine and I get all the output I need:
w_scan -fs -cGB -sS19E2 > channels.conf
However, the server needs to grab the ongoing output from the 'process.getErrorStream()' to display in the web interface. What is actually happening is the ProcessBuilder is failing and returning an exit code of 1.
The function that initialises the ProcessBuilder and to start the scan running is [EDIT 1]:
private static StringBuilder scan_error_output = null;
#Override
public boolean startSatelliteScan(String user, String country_code, String satellite_code) {
UserAccountPermissions perm = validateUserEdit(user);
if (perm == null) return false;
Shared.writeUserLog(user, Shared.getTimeStamp() +
": DVB satellite scan started " +
country_code + " - " + satellite_code +
System.lineSeparator() + System.lineSeparator());
scan_error_output = new StringBuilder();
new ScanThread(country_code, satellite_code).start();
// write out country code and satellite code to prefs file
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(satellite_last_scan_codes));
bw.write(country_code); bw.newLine();
bw.write(satellite_code); bw.newLine();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
That will then run two other threads on the server, one that will run the scan itself and wait for it to finish so that it can get the final scan data. And the other which constantly updates the output from the std error stream which is then polled at intervals from the clients browser. This is much like showing the ongoing output from the terminal.
The scan thread (which fails to start the process) [EDIT 1]:
private static class ScanThread extends Thread {
private String cc, sc;
public ScanThread(String country_code, String satellite_code) {
cc = country_code;
sc = satellite_code;
}
public void run() {
ProcessBuilder pb = new ProcessBuilder("/usr/bin/w_scan",
"-fs", "-c" + cc, "-s" + sc);
pb.redirectOutput(new File(satellite_scan_file));
Process process;
try {
System.out.println("Scan thread started");
process = pb.start();
IOScanErrorOutputHandler error_output_handler = new IOScanErrorOutputHandler(process.getErrorStream());
error_output_handler.start();
int result = process.waitFor();
System.out.println(cc + " - " + sc + " - " +
"Process.waitFor() result " + result);
} catch (IOException e) {
System.out.println(e.getMessage());
e.printStackTrace();
} catch (InterruptedException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
System.out.println("Scan thread finished");
}
}
The error output stream thread which captures the output which obviously doesn't start due to the scan thread failing:
private static class IOScanErrorOutputHandler extends Thread {
private InputStream inputStream;
IOScanErrorOutputHandler(InputStream inputStream) {
this.inputStream = inputStream;
}
public void run() {
Scanner br = null;
try {
System.out.println("Scan thread Error IO capture running");
br = new Scanner(new InputStreamReader(inputStream));
String line = null;
while (br.hasNextLine()) {
line = br.nextLine();
scan_error_output.append(line + System.getProperty("line.separator"));
}
} finally {
br.close();
}
System.out.println("Scan thread Error IO capture finished");
scan_error_output = null;
}
}
And the server function which returns the std error output progress:
#Override
public String pollScanResult(String user) {
if (validateUserEdit(user) == null) return null;
StringBuilder sb = scan_error_output; // grab instance
if (sb == null) return null;
return sb.toString();
}
As mentioned above, Runtime.exec() works fine, but the ProcessBuilder is failing.
NB: I'm on Linux Mint 18.1, using Apache Tomcat 8 as the server, linux default JDK 8 and GWT 2.7 [Correction from 2.8] in Eclipse Neon.
Can anyone see what I am doing wrong?
Many thanks in advance...
[EDIT 1]
Whilst developing this on another machine, Linux Mint 17.2, JDK 8 and Apache Tomcat 7, for DVB-T, this method worked fine and polling for the scan output showed up in the client's browser.
The ProcessBuilder.start still returns 1 and an empty file is created for the output scan file.
[EDIT 2]
It appears that the reason the ProcessBuilder is failing is because the user 'tomcat8' doesn't have permissions to run 'w_scan'. 'w_scan' works from the terminal, but not from the tomcat server. Somehow I've got to fix that now.
[SOLUTIONS]
After being put in the right direction by VGR for getting the error stream from the ProcessBuilder, I started digging further and found I was getting:
main:3909: FATAL: failed to open '/dev/dvb/adapter0/frontend0': 13 Permission denied
Apache tomcat 8 didn't have permission to access the DVB-S frontend to run a scan. This was fixed in two ways:
1 - 03catalina.policy I added the extra permissions (whether they made a difference I do not know).
grant codeBase "file:/dev/dvb/-" {
permission java.io.FilePermission "file:/dev/dvb/-", "read, write";
permission java.security.AllPermission;
};
2 - The dvb frontends belong to the 'video' group. So I needed to add the user tomcat8 to that group.
usermod -a -G video tomcat8
All works for now...
You are not doing the same thing with ProcessBuilder that you’re doing with Runtime.exec, so I don't know why you think ProcessBuilder is the problem.
You have a few problems with how you’re writing the command’s output to a file.
First, the presence of ">", satellite_scan_temp_file in your ProcessBuilder command is incorrect. Output redirection is not part of any command; it is handled by a shell. But when you run with Runtime.exec or ProcessBuilder, you are not running in a shell, you are executing the process directly. Neither w_scan nor any other command considers > a special character.
The correct way to redirect to a file is with the redirectOutput method:
ProcessBuilder pb = new ProcessBuilder(
"/usr/bin/w_scan", "-fs", "-s" + satellite_code, "-c" + country_code);
pb.redirectOutput(new File(satellite_scan_temp_file));
Second, your ScanThread code is ignoring the (currently incorrect) redirect, and is attempting to read the command’s output. But there is no output, because you are redirecting it all to a file.
Once you are properly redirecting output to a file, you can remove your BufferedReader and BufferedWriter loops completely.
Finally, it is worth noting that the error output you captured probably told you that > is not a valid argument to the w_scan process.
I already create the .sh file, and the inside is:
sudo iptables --flush
sudo iptables -A INPUT -m mac --mac-source 00:00:00:00:00:00 -j DROP
It works normally when I run it on the terminal, but when I use processbuilder, it didn't do anything. No error, but didn't happen anything, this is the code on my java:
Process pb = new ProcessBuilder("/bin/bash","/my/file.sh").start();
I already looking for the answer, but I still failed to run the .sh file, even I do the same thing with people that already done it.
Sorry if this is a bad question, thank you.
Are you sure that the bash is not run? Do you checked the Process object returned by the startmethod? You can get the output value, the output stream, etc. from this objects.
Check your streams and exitvalue for errors... sudo is probably the problem here.
Not necessarily the best code but it gets the job done. Executes a process, takes the process.streams and prints them to System.out. Might helpt to find out what the issue actually is atlest.
ProcessBuilder pb = new ProcessBuilder(args);
pb.redirectErrorStream(true);
final Process proc = pb.start();
final StringBuilder builder = new StringBuilder("Process output");
final Thread logThread = new Thread() {
#Override
public void run() {
InputStream is = proc.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
try {
String line;
do {
line = reader.readLine();
builder.append("");
builder.append(line == null ? "" : line);
builder.append("<br/>");
} while(line != null);
} catch (IOException e) {
builder.append("Exception! ").append(e.getMessage());
} finally {
try {
reader.close();
} catch (IOException e) {
builder.append("Exception! ").append(e.getMessage());
}
}
}
};
logThread.start();
int retVal = proc.waitFor();
System.out.println(builder.toString());
From Java API Runtime : http://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html
// Java runtime
Runtime runtime = Runtime.getRuntime();
// Command
String[] command = {"/bin/bash", "/my/file.sh"};
// Process
Process process = runtime.exec(command);
Also you should be careful with sudo commands that may ask for root password.
I want to create a backup of my database using pg_dump in java. The creation of backup works fine, but it doesn't start before the program exits.
Is there any way to start the backup instantly?
public static void backupDb() throws IOException, InterruptedException {
String path = "C:\\HACENE\\test.backup";
Runtime r = Runtime.getRuntime();
//PostgreSQL variables
String host = "localhost";
String user = "postgres";
String dbase = "gtr_bd";
String password = "postgres";
Process p;
ProcessBuilder pb;
r = Runtime.getRuntime();
pb = new ProcessBuilder("C:\\Program Files\\PostgreSQL\\9.3\\bin\\pg_dump", "-v", "-h", host, "-f", path, "-U", user, dbase);
pb.environment().put("PGPASSWORD", password);
pb.redirectErrorStream(true);
p = pb.start();
System.out.println("end of backup");
}
You should check the state of the Process instance returned from ProcessBuilder.start(). As already mentioned in the comments you can use the Process.waitFor() method to wait for the process to finish. Furthermore you should consider inspecting the exit value of the started process to determine if your backup was successful or not.
This is what I did in a project of mine:
private void validateExitValue(final Process aProcess, final String aCommand,
final String aResult) throws IllegalStateException {
do {
try {
if (aProcess.waitFor() != 0) {
final String errorMessage = String.format("Command '%s' terminated with exit status '%s': %s",
aCommand, aProcess.exitValue(), aResult);
throw new IllegalStateException(errorMessage);
}
break;
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
} while (true);
}
This method will wait for the process to finish and throw an IllegalStateException, if the process finished with an exit code other than 0.
The observation that the backup process currently is started just as your Java program terminates is just by coincidence I suspect, as starting the backup process is very likely the last action that is performed by your Java program. Starting the external process will take some time and your program continues and terminates, that is it looks like the the process is being started at termination of the Java program.
// Comment -> p = pb.start();
// add this at the end:
final Process process = pb.start();
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
// because you put "-v" in your command. Another way is to remove "-v" from your command
-v Specifies verbose mode. This will cause pg_dump to output detailed object comments and start/stop times to the dump file, and progress messages to standard error. From this LINK
If you execute this command in cmd this work fine:
"C:\Program Files\PostgreSQL\9.3\bin\pg_dump.exe" -U postgres -h localhost -p 5432 database_name > "C:\Test\test.buckup"
So you can put this lines in a .bat file like this:
#echo off
cd "C:\Program Files\PostgreSQL\9.3\bin\"
pg_dump.exe -U postgres -h localhost -p 5432 bd_suivi > "C:\Test\test.buckup"
exit
And execute this file.bat with java, like this:
public static void main(String[] args) {
try {
ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", "start script.bat");
builder.redirectErrorStream(true);
Process p = builder.start();
BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while (true) {
line = r.readLine();
if (line == null) {
break;
}
System.out.println(line);
}
System.out.println("I finished the creation of the buckup!");
} catch (Exception e) {
System.out.println("Exception = " + e);
}
}
good luck.
The program I'm working on uses ADB (Android Debug Bridge) to send files to my phone:
for (String s : files)
String cmd = "adb -s 0123456789ABCDEF push " + s + " /mnt/sdcard/" + s;
try {
InputStream is = Runtime.getRuntime().exec(cmd).getInputStream();
while (is.read() != -1) {}
} catch (IOException e) {
e.printStackTrace();
}
I want the program to wait until ADB finished the transmission, but ADB runs as a daemon and therefore never finishes. But the program continues immideately and somehow the files aren't sent to my phone (no exceptions in log). When I run the command from console, it's working without problems.
What am I doing wrong? How do I send files via ADB correctly?
NOTE: the is.read() == -1 won't work, because the ADB daemon writes all output to the system standard output. I've tried forwarding it into a textfile. It stayed empty and the output was still written to the terminal instead
EDIT: Reading the ErrorStream of the ADB process returned the adb help for each adb push-command. Again: The exact commands (copied from Eclipse console) work in a terminal
EDIT 2: Using a ProcessBuilder instead of RUntime.getRuntime.exec() resulted in the following error:
java.io.IOException: Cannot run program "adb -s 0123456789ABCDEF push "inputfile "outputfile""": error=2, File or directory not found
at the ProcessBuilder's start()-method
The same happens when using an absolute path for ADB (/usr/bin/adb). The inputfile and outputfile Strings are also absolute paths, like /home/sebastian/testfile and definitely exist. When running the commands from terminal (string "cmd" printed, copy&paste), evreything still works fine.
I finally got it working:
ProcessBuilder pb = new ProcessBuilder("adb", "-s", "0123456789ABCDEF", "push", inputfile, outputfile);
Process pc = pb.start();
pc.waitFor();
System.out.println("Done");
I don't know what problems ProcessBuilder has with spaces in a string, but finally, it's working...
I've solved in this way:
public class Utils {
private static final String[] WIN_RUNTIME = { "cmd.exe", "/C" };
private static final String[] OS_LINUX_RUNTIME = { "/bin/bash", "-l", "-c" };
private Utils() {
}
private static <T> T[] concat(T[] first, T[] second) {
T[] result = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, result, first.length, second.length);
return result;
}
public static List<String> runProcess(boolean isWin, String... command) {
System.out.print("command to run: ");
for (String s : command) {
System.out.print(s);
}
System.out.print("\n");
String[] allCommand = null;
try {
if (isWin) {
allCommand = concat(WIN_RUNTIME, command);
} else {
allCommand = concat(OS_LINUX_RUNTIME, command);
}
ProcessBuilder pb = new ProcessBuilder(allCommand);
pb.redirectErrorStream(true);
Process p = pb.start();
p.waitFor();
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String _temp = null;
List<String> line = new ArrayList<String>();
while ((_temp = in.readLine()) != null) {
System.out.println("temp line: " + _temp);
line.add(_temp);
}
System.out.println("result after command: " + line);
return line;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
If you don't need env variables in your .bash_profile cut "-l" parameter.
I have a Mac but it should work on Linux also.
public static void adbpush() {
System.out.println("adb push....");
String[] aCommand = new String[] { adbPath, "push", inputFile(String),OutputDirectory };
try {
// Process process = new ProcessBuilder(aCommand).start();
Process process = Runtime.getRuntime().exec(aCommand);
process.waitFor(3, TimeUnit.SECONDS);
System.out.println("file pushed");
} catch (Exception e) {
e.printStackTrace();
}
}
It will be better to give full path for ADB execution: like this $ANDROID_HOME/platform-tools/adb devices
This is the full code you can use:
String cmd = "$ANDROID_HOME/platform-tools/adb devices";
ProcessBuilder processBuilder = new ProcessBuilder();
if (Config.osName.contains("Windows"))
processBuilder.command("cmd.exe", "/c", cmd);
else
processBuilder.command("bash", "-c", cmd);
Process process = processBuilder.start();
Process process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
string cmd = "/system/bin/input keyevent 23\n";
os.writeBytes(cmd);
the phone must be rooted. here I have executed adb command "input keyevent 23". remember when you execute adb command through su you does not need to add "adb shell input keyevent 23"