Read command output inside su process
Asked Answered
E

3

13

firstly I will present my situation. I need to execute "su" command in my android app and it works well. Then I need to execute "ls" command and read the output. I'm doing it by getting the output stream from the "su" process and writing my command into it.

And here goes the question. How to read the output of the "ls" process? All I have is the "su" Process object. Getting the input stream from it gives nothing, because "su" doesn't write anything. But "ls" does and I don't know how to access its output messages.

I have searched many sites but I didn't find any solution. Maybe someone will help me:)

Regards

Escent answered 1/8, 2011 at 9:41 Comment(0)
E
24

Ok, I've found a solution. It should look like this:

Process p = Runtime.getRuntime().exec(new String[]{"su", "-c", "system/bin/sh"});
DataOutputStream stdin = new DataOutputStream(p.getOutputStream());
//from here all commands are executed with su permissions
stdin.writeBytes("ls /data\n"); // \n executes the command
InputStream stdout = p.getInputStream();
byte[] buffer = new byte[BUFF_LEN];
int read;
String out = new String();
//read method will wait forever if there is nothing in the stream
//so we need to read it in another way than while((read=stdout.read(buffer))>0)
while(true){
    read = stdout.read(buffer);
    out += new String(buffer, 0, read);
    if(read<BUFF_LEN){
        //we have read everything
        break;
    }
}
//do something with the output

Hope it will be helpful for someone

Escent answered 5/8, 2011 at 8:24 Comment(4)
The call to read() stalls if the system call doesn't return anything. In this particular case, "ls" should always return something but just keep in mind it could stall with other commands.Portly
@Escent Is there a way to do this without specifying BUFF_LEN? That is, read a line at a time until there are no more lines?Jose
Hi, I've been using your example successfully but I'm struggling to get this to work on Android Nougat. Anyone have any ideas for this?Hassiehassin
Calling Runtime.getRuntime().exec("su"); seems to be enough for me, as the su binary will probably start a shell by itself anyways.Sturtevant
G
5
public String ls () {
    Class<?> execClass = Class.forName("android.os.Exec");
    Method createSubprocess = execClass.getMethod("createSubprocess", String.class, String.class, String.class, int[].class);
    int[] pid = new int[1];
    FileDescriptor fd = (FileDescriptor)createSubprocess.invoke(null, "/system/bin/ls", "/", null, pid);

    BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(fd)));
    String output = "";
    try {
        String line;
        while ((line = reader.readLine()) != null) {
            output += line + "\n";
        }
    }
    catch (IOException e) {}
    return output;
}

Check this code mentioned here:

How to run terminal command in Android application?


try {
// Executes the command.
Process process = Runtime.getRuntime().exec("/system/bin/ls /sdcard");

// Reads stdout.
// NOTE: You can write to stdin of the command using
//       process.getOutputStream().
BufferedReader reader = new BufferedReader(
        new InputStreamReader(process.getInputStream()));
int read;
char[] buffer = new char[4096];
StringBuffer output = new StringBuffer();
while ((read = reader.read(buffer)) > 0) {
    output.append(buffer, 0, read);
}
reader.close();

// Waits for the command to finish.
process.waitFor();

return output.toString();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}

References

this code GScript

Guessrope answered 1/8, 2011 at 10:12 Comment(2)
This code doesn't going to work. There is no such class like "android.os.Exec". I have read that it doesn't work anymore on Froyo and higher.Escent
I tried something like that but with "su" before the command, but then system asks for permission on every execution which somehow differ from previous (ex. listing another directory). All I need to do is to gain permission for "su" command only once (user clicks remember and system never asks again ) and then execute my commands inside this process and read their output. To be clear: 1. Create su process. 2. Write "ls" command to the output stream (ls executes with su permission). 3. Read "ls" output. I am unable to achieve point 3Escent
P
3

I modified accepted answer by @glodos for following problems:

  1. the streams are closed, otherwise the exec process hangs forever, on the opened stream. If you execute ps in shell (ie adb shell) after several executions then you'll see several su processes alive. They needs to be properly terminated.
  2. added waitFor() to make sure the process is terminated.
  3. Added handling for read=-1, now commands with empty stdout can be executed. Previously they crashed on new String(buffer, 0, read)
  4. Using StringBuffer for more efficient strings handling.

    private String execCommand(String cmd) throws IOException, InterruptedException {
        Process p = Runtime.getRuntime().exec(new String[]{"su", "-c", "system/bin/sh"});
        DataOutputStream stdout = new DataOutputStream(p.getOutputStream());
    
        stdout.writeBytes(cmd);
        stdout.writeByte('\n');
        stdout.flush();
        stdout.close();
    
        BufferedReader stdin = new BufferedReader(new InputStreamReader(p.getInputStream()));
        char[] buffer = new char[1024];
        int read;
        StringBuffer out = new StringBuffer();
    
        while((read = stdin.read(buffer)) > 0) {
            out.append(buffer, 0, read);
        }
        stdin.close();
        p.waitFor();
        return out.toString();
    }
    

Some credits go to @Sherif elKhatib ))

Posting answered 3/11, 2017 at 0:13 Comment(1)
Unfortunately this does not work for me... I just don't get any result.Rasure

© 2022 - 2024 — McMap. All rights reserved.