ProcessBuilder.start() with "su" command blocks thread - java

I'm implementing a terminal "emulator"/"launcher" in my app. I want to let the user to use all android shell commands. This works great, until I use the "su" command. here is the source code:
Code:
ProcessBuilder processBuilder = new ProcessBuilder(input);
processBuilder.directory(info.currentDirectory);
Process process;
try {
process = processBuilder.start();
} catch (IOException e) {
return 1;
}
process.waitFor();
InputStream i = process.getInputStream();
InputStream e = process.getErrorStream();
BufferedReader ir = new BufferedReader(new InputStreamReader(i));
BufferedReader er = new BufferedReader(new InputStreamReader(e));
String s = null;
output = "";
for(int count = 0; count == 0 || s != null; count++) {
s = ir.readLine();
if(s != null) {
if(count == 0)
output = output.concat(mContext.getString(R.string.output_label) + "\n");
output = output.concat(s + "\n");
}
}
s = null;
for(int count = 0; count == 0 || s != null; count++) {
s = er.readLine();
if(s != null) {
if(count == 0)
output = output.concat(mContext.getString(R.string.error_label) + "\n");
output = output.concat(s + "\n");
}
}
process.destroy();
Main thread is blocked forever in any case: if I call only process.waitFor, and if I use one of the InputStream objects.
What's the problem? SU permissions are granted normally...

Related

Java Runtime.getRuntime().exec different output format

I have a simple python program (hw1.py) as follows:
x = int(input("Enter x value: "))
y = int(input("Enter y value: "))
print("x is " + str(x))
print("y is " + str(y))
print("Output is " + str(x+y))
When I run it from terminal, I get following output as expected:
Enter x value: 10
Enter y value: 20
x is 10
y is 20
Output is 30
However, when I run it from Java with providing inputs ("10" and "20") I get slightly different output. Here is the Java code to compile and run python file:
String osName = System.getProperty("os.name").toLowerCase();
boolean isMacOs = osName.startsWith("mac os x");
String macPythonPath = "/Library/Frameworks/Python.framework/Versions/3.6/bin/python3";
String unixPythonPath = "/usr/bin/python3";
Process p;
if (isMacOs) {
p = Runtime.getRuntime().exec(macPythonPath + " -m py_compile " + "/Users/inanc/Desktop/pythonDocs/hw1.py");
} else {
p = Runtime.getRuntime().exec(unixPythonPath + " -m py_compile " + "/Users/inanc/Desktop/pythonDocs/hw31.py");
}
BufferedReader stdError = new BufferedReader(new
InputStreamReader(p.getErrorStream()));
String resError = "", s;
// read any errors from the attempted command
while ((s = stdError.readLine()) != null) {
resError = resError + s + "\n";
}
resError = resError.trim();
stdError.close();
if (resError.equals("")) {
if (isMacOs) {
p = Runtime.getRuntime().exec(macPythonPath + " " + "/Users/inanc/Desktop/pythonDocs/hw1.py");
} else {
p = Runtime.getRuntime().exec(unixPythonPath + " " + "/Users/inanc/Desktop/pythonDocs/hw1.py");
}
String[] inputs = {"10", "20"};
OutputStream out = p.getOutputStream();
for (String input: inputs) {
if (input.equals("") == false)
out.write((input+"\n").getBytes());
}
out.flush();
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));
stdError = new BufferedReader(new
InputStreamReader(p.getErrorStream()));
// read the output from the command
String res = "";
s = null;
while ((s = stdInput.readLine()) != null) {
res = res + s + "\n" ;
}
res = res.trim();
resError = "";
// read any errors from the attempted command
while ((s = stdError.readLine()) != null) {
resError = resError + s + "\n";
}
resError = resError.trim();
stdInput.close();
stdError.close();
p = null;
System.out.println(res);
} else {
System.err.println(resError);
}
After calling this code, the output is as follows:
Enter x value: Enter y value: x is 10
y is 20
Output is 30
How can I get the exactly same output with terminal execution? At least I want to keep the newlines after entering inputs.
Then there is a way only when you make your program such that argument 10 is written to process, then next line is read from the process and then 20 is written to process, and the another line is read from the process,so on
Whereas, in your code, you are writing all the inputs the process needs in a single loop
I solved the problem, here is the solution:
String osName = System.getProperty("os.name").toLowerCase();
boolean isMacOs = osName.startsWith("mac os x");
String macPythonPath = "/Library/Frameworks/Python.framework/Versions/3.6/bin/python3";
String unixPythonPath = "/usr/bin/python3";
String filepath = "/Users/inanc/Desktop/pythonDocs/hw1additionalInputs.py ";
Process p;
if (isMacOs){
p = Runtime.getRuntime().exec(macPythonPath + " -m py_compile " + filepath);
}
else{
p = Runtime.getRuntime().exec(unixPythonPath + " -m py_compile " + filepath);
}
BufferedReader stdError = new BufferedReader(new
InputStreamReader(p.getErrorStream()));
String resError = "", s;
// read any errors from the attempted command
while ((s = stdError.readLine()) != null) {
resError = resError + s + "\n";
}
resError = resError.trim();
stdError.close();
if(resError.equals("")) {
if (isMacOs){
p = Runtime.getRuntime().exec(macPythonPath + " " + filepath);
}
else{
p = Runtime.getRuntime().exec(unixPythonPath + " " + filepath);
}
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));
stdError = new BufferedReader(new
InputStreamReader(p.getErrorStream()));
// read the output from the command
String res = "";
s = null;
String[] inputs = {"10", "20"};
OutputStream out = p.getOutputStream();
for(String input:inputs){
if(input.equals("") == false)
{
Thread.sleep(200);
while (stdInput.ready() && (s = "" + (char) stdInput.read()) != null) {
res = res + s ;
}
// read any errors from the attempted command
Thread.sleep(200);
while (stdError.ready() && (s = "" + (char) stdError.read()) != null) {
resError = resError + s;
}
if(resError.equals("") == false)
break;
out.write((input+"\n").getBytes());
out.flush();
res = res + input + "\n";
Thread.sleep(200);
while (stdInput.ready() && (s = "" + (char) stdInput.read()) != null) {
res = res + s ;
}
//res = res.trim();
// read any errors from the attempted command
Thread.sleep(200);
while (stdError.ready() && (s = "" + (char) stdError.read()) != null) {
resError = resError + s;
}
if(resError.equals("") == false)
break;
}
}
Thread.sleep(200);
while (stdInput.ready() && (s = "" + (char) stdInput.read()) != null) {
res = res + s ;
}
//res = res.trim();
// this part is for in case of no inputs
Thread.sleep(200);
while (stdError.ready() && (s = "" + (char) stdError.read()) != null) {
resError = resError + s;
}
stdInput.close();
stdError.close();
if(p.isAlive())
{
System.err.println("Timeout");
p.destroy();
}
p = null;
System.out.println(res);
System.out.println(resError);
}
else {
System.err.println(resError);
}

Run multiple system commands

I have been playing with java processes for a while and am stuck. What i want to do is run multiple system commands at the same time and print their output to console.
For example, ls -l ; cat someFile ; quit ; grep foo someOtherFile should all be running at the same time. I have read somewhere that the output of these commands should be intermixed. In addition, if there's a quit command anywhere in the string, continue executing other commands and then exit.
Right now, they are executing sequentially. How do I run them concurrently and print their output as it arrive.
String st = "ls -l ; cat someFile ; quit ; grep foo someOtherFile";
String[] rows = st.split(";");
String[][] strArray = new String[rows.length][];
int index = 0;
for(int i = 0; i < rows.length; i++) {
rows[index] = rows[index].trim();
strArray[index] = rows[index].split(" ");
index++;
}
for(int i = 0; i < strArray.length; i++) {
if(rows[i].equalsIgnoreCase("quit")) {
System.out.println("Abort");
break;
}
if(rows[i].equals("")) {
continue;
}
ProcessBuilder pb = new ProcessBuilder(strArray[i]);
pb.redirectErrorStream(true);
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);
}
br.close();
}
Just put the guts of your loop inside the run() function of a new thread, and each iteration of the loop will run in a separate thread:
new Thread() {
public void run() {
// loop guts go here
}
}.start();
You may have to declare a few variables as finals in order to access them inside this anonymous inner class.
You can try code similar to this:
// command list
String st = "ls -la; cat someFile";
String[] commands = st.split(";");
for (int i = 0; i < commands.length; i++) {
String currentCommand = commands[i].trim();
System.out.println("Command: " + currentCommand);
Thread thread = new Thread(() -> {
try {
ProcessBuilder command = new ProcessBuilder(currentCommand);
Process process = command.start();
InputStream is = process.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
while((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
});
thread.start();
}
Disclaimer: not tested on a Linux machine. Windows machines will probably not work. See this link regarding Windows command line process execution.
You should look to some documentation about concurrency, threads an such http://docs.oracle.com/javase/tutorial/essential/concurrency/.
Here an edit to your code that may work, not tested.
String st = "ls -l ; cat someFile ; quit ; grep foo someOtherFile";
String[] rows = st.split(";");
String[][] strArray = new String[rows.length][];
int index = 0;
for(int i = 0; i < rows.length; i++) {
rows[index] = rows[index].trim();
strArray[index] = rows[index].split(" ");
index++;
}
List<Thread> threads = new ArrayList<Thread>();
for(int i = 0; i < strArray.length; i++) {
if(rows[i].equalsIgnoreCase("quit")) {
System.out.println("Abort");
break;
}
if(rows[i].equals("")) {
continue;
}
final int iForThread = i;
Thread thread = new Thread() {
public void run(){
try{
ProcessBuilder pb = new ProcessBuilder(strArray[iForThread]);
pb.redirectErrorStream(true);
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);
}
br.close();
}catch(IOException e){
//Log some awesome error
//Clean up
//Do whatever
}
}
};
threads.add(thread);
}
final CyclicBarrier gate = new CyclicBarrier(threads.size() + 1); //+1 is a tip from other post
for(Thread thread : threads){
thread.start();
}
try {
gate.await();
System.out.println("all threads started");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
/* RONALDO OF ERROS
* MESSI OF HANDLERS*/
}
}
It creates an tread and executed it at the spot.
I if you are just messing around I think this enough.
Edit: Added start threads at "same time"
Based on: How to start two threads at "exactly" the same time
See:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html

Why my program is going in the infinite loop?

I am trying to compile and execute C , C++ and Java codes taken as argument to a Java file and then check that the generated solution is correct or not as most of the website judge the solutions.Please anybody can tell me why my code is going in infinite loop and no out put is coming in file_name_output.txt. My other all files are correct as i have tested them by running the program on terminal.Here is my code :
import java.io.*;
import java.util.Scanner;
class test
{
public static void main(String args[])
{
String s=null,file_name,extension;
int pos = args[0].lastIndexOf(".");
extension = args[0].substring(pos+1);
file_name = args[0].substring(0,pos);
int lang = 0; // 1 -> c,c++ , 2 -> java
try
{
Process compile = null;
switch(extension)
{
case "c" : compile = Runtime.getRuntime().exec("gcc -g "+ args[0] + " -o "+file_name+" -lm");
lang = 1;
break;
case "cpp" : compile = Runtime.getRuntime().exec("g++ -g "+ args[0] + " -o "+file_name);
lang = 1;
break;
case "java" : compile = Runtime.getRuntime().exec("javac "+ args[0]);
lang = 2;
}
BufferedReader stdError = new BufferedReader(new InputStreamReader(compile.getErrorStream()));
if((s = stdError.readLine()) != null)
{
System.out.println("Compile Time Error OR Warning : ");
System.out.println(s);
while((s = stdError.readLine()) != null)
{
System.out.println(s);
}
}
double startTime, run_time;
Process run;
if(lang == 1)
{
startTime = System.nanoTime();
run = Runtime.getRuntime().exec("./"+file_name+" < "+file_name+"_input.txt > "+file_name+"_output.txt");
run_time = (System.nanoTime()-startTime)/(double)Math.pow(10,6);
}
else
{
startTime = System.nanoTime();
run = Runtime.getRuntime().exec("java "+file_name+" < "+file_name+"_input.txt > "+file_name+"_output.txt");
run_time = (System.nanoTime()-startTime)/(double)Math.pow(10,6);
}
System.out.println("RunTime : "+ run_time+" ms");
BufferedReader out_put = new BufferedReader(new FileReader(new File(file_name+"_output.txt")));
BufferedReader run_stdError = new BufferedReader(new InputStreamReader(run.getErrorStream()));
if(( s = run_stdError.readLine()) != null)
{
System.out.println("Runtime Error : ");
System.out.println(s);
while((s = run_stdError.readLine()) != null )
{
System.out.println(s);
}
}
else if((s = out_put.readLine()) != null)
{
String s_string = null;
int failed = 0;
File fs = new File(file_name+".txt");
BufferedReader br = new BufferedReader(new FileReader(fs));
if((!s.equals(s_string = br.readLine())))
{
failed = 1;
}
while(((s = out_put.readLine()) != null) & ((s_string = br.readLine()) != null) & (failed == 0))
{
if(!s.equals(s_string) )
{
failed = 1;
break;
}
}
if((failed == 1) || s != null || s_string != null)
{
System.out.println("Submmision Failed : ");
System.out.println("Either Output Is Wrong.\nOR\nYour Output Is Not According To The Given Format. ");
System.exit(0);
}
else
{
System.out.println("Submission Successful.");
}
}
}
catch(IOException e)
{
System.out.println("Some Error Has Occured : ");
e.printStackTrace();
System.exit(-1);
}
}
}
Diagnosis
Your program is not in an endless loop, it is blocking, and this is the line where it happens:
s = run_stdError.readLine()
unless there's something on the subprocess's stderr, this is going to block until the process dies. However, while waiting here you don't consume the process's stdout. It fills its output buffer and blocks.
The result: an interprocess deadlock.
Suggested fix
Use a ProcessBuilder and use its API to achieve redirection into files with no effort of your own. You have the redirectOutput(File) and redirectError(File) methods in there.

How to read first five character from buffered reader?

I have this code
Process p =Runtime.getRuntime().exec("busybox");
InputStream a = p.getInputStream();
InputStreamReader read = new InputStreamReader(a);
BufferedReader in = new BufferedReader(read);
Running it from the terminal the first lines of oupout return the version of Busybox. If I wanted to take for example the first 5 characters as I do?
While the other answers should work well too, the following will exit and close the stream after reading five characters:
Process p = Runtime.getRuntime().exec("busybox");
InputStream a = p.getInputStream();
InputStreamReader read = new InputStreamReader(a);
StringBuilder firstFiveChars = new StringBuilder();
int ch = read.read();
while (ch != -1 && firstFiveChars.length() < 5) {
firstFiveChars.append((char)ch);
ch = read.read();
}
read.close();
a.close();
System.out.println(firstFiveChars);
try
String line = in.readLine();
if(line!=null && line.length() >5)
line = line.substring(0, 5);
Do this way
Process p;
try {
p = Runtime.getRuntime().exec("busybox");
InputStream a = p.getInputStream();
InputStreamReader read = new InputStreamReader(a);
BufferedReader in = new BufferedReader(read);
StringBuilder buffer = new StringBuilder();
String line = null;
try {
while ((line = in.readLine()) != null) {
buffer.append(line);
}
} finally {
read.close();
in.close();
}
String result = buffer.toString().substring(0, 15);
System.out.println("Result : " + result);
} catch (Exception e) {
e.printStackTrace();
}
Output
Result : BusyBox v1.13.3

Java - Remote Console

For my server coded in java I want to add a console. I connect to my server using a socket.
Here is the code I've made for the console:
On my server:
public class ServerConsole
{
public String exec(String[] cmd)
{
try
{
Process child = Runtime.getRuntime().exec(cmd);
InputStream in = child.getInputStream();
StringBuffer buffer = new StringBuffer();
int c;
while ((c = in.read()) != -1)
{
buffer.append((char)c);
}
in.close();
return buffer.toString();
}
catch (Exception e) {}
return "FAILED";
}
}
This class execute the given command and returns a string that contains the content of the console after execution.
I call this method like that:
String cmd_data_cmd = inputStream.readUTF();
String[] dataCmd = cmd_data_cmd.split("#");
OSCmd osCmd = new OSCmd();
outputStream.writeUTF(osCmd.exec(dataCmd));
Where inputStream is the stream I use with my socket. It works well!
Now, on the client side, I've made that:
String[] cmd = cmd_input.getText().split(" ");
String new_cmd = "";
for (String part : cmd)
new_cmd += (new_cmd.equals("") ? "": "#") + part;
this.outputSocket.writeUTF(new_cmd);
DataInputStream result_input = new DataInputStream(this.input);
String tmp = result_input.readUTF();
System.out.println(tmp);
This should returns me the result displayed in the console but actually, nothing happens. It just freezes when I start that part of code.
Any idea how to do that?
Thanks.
Here is the solution:
String[] cmd_exec = {};
String os_name = System.getProperty("os.name").toLowerCase();
if (os_name.indexOf("win") >= 0)
cmd_exec = new String[]{"cmd.exe", "/c", cmd};
else if (os_name.indexOf("mac") >= 0)
cmd_exec = new String[]{"/usr/bin/open", "-a", cmd};
else if (os_name.indexOf("nix") >= 0 || os_name.indexOf("nux") >= 0)
cmd_exec = new String[]{"/bin/bash", cmd};
else if (os_name.indexOf("sunos") >= 0)
cmd_exec = new String[]{"/bin/bash", cmd};
Process child = Runtime.getRuntime().exec(cmd_exec);
String line;
while ((line = stdInput.readLine()) != null)
{
buffer.append("\t" + new String(line.getBytes("UTF-8"), "UTF-8") + "\n");
}
stdInput.close();
while ((line = stdError.readLine()) != null)
{
buffer.append("\t" + new String(line.getBytes("UTF-8"), "UTF-8") + "\n");
}
stdError.close();
child.destroy();
Hope this will help someone else.

Categories

Resources