2016-08-24 21:58:15 +00:00
|
|
|
package com.topjohnwu.magisk.utils;
|
|
|
|
|
|
|
|
import java.io.DataOutputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Modified by topjohnwu, based on Chainfire's libsuperuser
|
|
|
|
*/
|
|
|
|
|
|
|
|
public class Shell {
|
|
|
|
|
|
|
|
// -1 = problematic/unknown issue; 0 = not rooted; 1 = properly rooted; 2 = improperly rooted;
|
2016-08-27 11:02:41 +00:00
|
|
|
public static int rootStatus;
|
2016-08-24 21:58:15 +00:00
|
|
|
|
|
|
|
private static Process rootShell;
|
|
|
|
private static DataOutputStream rootSTDIN;
|
|
|
|
private static StreamGobbler rootSTDOUT;
|
|
|
|
private static List<String> rootOutList = new ArrayList<>();
|
|
|
|
|
|
|
|
static {
|
2016-08-25 10:08:07 +00:00
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void init() {
|
2016-08-24 21:58:15 +00:00
|
|
|
|
|
|
|
try {
|
2016-08-27 11:02:41 +00:00
|
|
|
rootShell = Runtime.getRuntime().exec(sh("getprop magisk.supath").get(0) + "/su");
|
|
|
|
rootStatus = 1;
|
2016-08-24 21:58:15 +00:00
|
|
|
} catch (IOException e) {
|
2016-08-27 11:02:41 +00:00
|
|
|
try {
|
|
|
|
// Improper root
|
|
|
|
rootShell = Runtime.getRuntime().exec("su");
|
|
|
|
rootStatus = 2;
|
|
|
|
} catch (IOException err) {
|
|
|
|
// No root
|
|
|
|
rootStatus = 0;
|
|
|
|
return;
|
|
|
|
}
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|
|
|
|
|
2016-08-27 11:02:41 +00:00
|
|
|
rootSTDIN = new DataOutputStream(rootShell.getOutputStream());
|
2016-09-30 02:41:40 +00:00
|
|
|
rootSTDOUT = new StreamGobbler(rootShell.getInputStream(), rootOutList, true);
|
2016-08-27 11:02:41 +00:00
|
|
|
rootSTDOUT.start();
|
|
|
|
|
2016-08-28 22:35:07 +00:00
|
|
|
List<String> ret = su("echo -BOC-", "id");
|
2016-08-24 21:58:15 +00:00
|
|
|
if (ret == null) {
|
2016-08-25 10:08:07 +00:00
|
|
|
// Something wrong with root, not allowed?
|
2016-08-24 21:58:15 +00:00
|
|
|
rootStatus = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (String line : ret) {
|
|
|
|
if (line.contains("uid=")) {
|
|
|
|
// id command is working, let's see if we are actually root
|
|
|
|
rootStatus = line.contains("uid=0") ? rootStatus : -1;
|
|
|
|
return;
|
|
|
|
} else if (!line.contains("-BOC-")) {
|
|
|
|
rootStatus = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-25 10:08:07 +00:00
|
|
|
public static boolean rootAccess() {
|
|
|
|
return rootStatus > 0;
|
|
|
|
}
|
|
|
|
|
2016-08-24 21:58:15 +00:00
|
|
|
public static List<String> sh(String... commands) {
|
|
|
|
List<String> res = Collections.synchronizedList(new ArrayList<String>());
|
|
|
|
|
|
|
|
try {
|
|
|
|
Process process = Runtime.getRuntime().exec("sh");
|
|
|
|
DataOutputStream STDIN = new DataOutputStream(process.getOutputStream());
|
|
|
|
StreamGobbler STDOUT = new StreamGobbler(process.getInputStream(), res);
|
|
|
|
|
|
|
|
STDOUT.start();
|
|
|
|
|
|
|
|
try {
|
|
|
|
for (String write : commands) {
|
|
|
|
STDIN.write((write + "\n").getBytes("UTF-8"));
|
|
|
|
STDIN.flush();
|
2016-09-30 02:41:40 +00:00
|
|
|
Logger.shell(false, write);
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|
|
|
|
STDIN.write("exit\n".getBytes("UTF-8"));
|
|
|
|
STDIN.flush();
|
|
|
|
} catch (IOException e) {
|
|
|
|
if (!e.getMessage().contains("EPIPE")) {
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
process.waitFor();
|
|
|
|
|
|
|
|
try {
|
|
|
|
STDIN.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
// might be closed already
|
|
|
|
}
|
|
|
|
STDOUT.join();
|
|
|
|
process.destroy();
|
|
|
|
|
|
|
|
} catch (IOException | InterruptedException e) {
|
|
|
|
// shell probably not found
|
|
|
|
res = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-09-25 13:31:38 +00:00
|
|
|
// Run with the same shell by default
|
2016-08-24 21:58:15 +00:00
|
|
|
public static List<String> su(String... commands) {
|
2016-09-25 13:31:38 +00:00
|
|
|
return su(false, commands);
|
|
|
|
}
|
2016-08-24 21:58:15 +00:00
|
|
|
|
2016-09-25 13:31:38 +00:00
|
|
|
public static List<String> su(boolean newShell, String... commands) {
|
|
|
|
List<String> res;
|
|
|
|
Process process;
|
|
|
|
DataOutputStream STDIN;
|
|
|
|
StreamGobbler STDOUT;
|
2016-08-24 21:58:15 +00:00
|
|
|
|
2016-09-25 13:31:38 +00:00
|
|
|
if (newShell) {
|
|
|
|
res = Collections.synchronizedList(new ArrayList<String>());
|
|
|
|
try {
|
|
|
|
process = Runtime.getRuntime().exec(sh("getprop magisk.supath").get(0) + "/su");
|
|
|
|
STDIN = new DataOutputStream(process.getOutputStream());
|
|
|
|
STDOUT = new StreamGobbler(process.getInputStream(), res);
|
|
|
|
} catch (IOException e) {
|
|
|
|
try {
|
|
|
|
// Improper root
|
|
|
|
process = Runtime.getRuntime().exec("su");
|
|
|
|
STDIN = new DataOutputStream(process.getOutputStream());
|
|
|
|
STDOUT = new StreamGobbler(process.getInputStream(), res);
|
|
|
|
} catch (IOException err) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
STDOUT.start();
|
|
|
|
} else {
|
|
|
|
process = rootShell;
|
|
|
|
STDIN = rootSTDIN;
|
|
|
|
STDOUT = rootSTDOUT;
|
|
|
|
res = rootOutList;
|
|
|
|
res.clear();
|
|
|
|
}
|
2016-08-24 21:58:15 +00:00
|
|
|
|
|
|
|
try {
|
2016-08-27 11:02:41 +00:00
|
|
|
for (String write : commands) {
|
2016-09-25 13:31:38 +00:00
|
|
|
STDIN.write((write + "\n").getBytes("UTF-8"));
|
|
|
|
STDIN.flush();
|
2016-09-30 02:41:40 +00:00
|
|
|
Logger.shell(true, write);
|
2016-08-27 11:02:41 +00:00
|
|
|
}
|
2016-09-25 13:31:38 +00:00
|
|
|
if (newShell) {
|
|
|
|
STDIN.write("exit\n".getBytes("UTF-8"));
|
|
|
|
STDIN.flush();
|
|
|
|
process.waitFor();
|
2016-08-24 21:58:15 +00:00
|
|
|
|
2016-09-25 13:31:38 +00:00
|
|
|
try {
|
|
|
|
STDIN.close();
|
|
|
|
} catch (IOException ignore) {
|
|
|
|
// might be closed already
|
|
|
|
}
|
|
|
|
|
|
|
|
STDOUT.join();
|
|
|
|
process.destroy();
|
|
|
|
} else {
|
2016-09-28 10:05:55 +00:00
|
|
|
STDIN.write(("echo\n").getBytes("UTF-8"));
|
2016-09-25 13:31:38 +00:00
|
|
|
STDIN.flush();
|
2016-09-28 10:05:55 +00:00
|
|
|
STDIN.write(("echo \'-root-done-\'\n").getBytes("UTF-8"));
|
2016-09-25 13:31:38 +00:00
|
|
|
STDIN.flush();
|
|
|
|
while (true) {
|
|
|
|
try {
|
|
|
|
// Process terminated, it means the interactive shell has some issues
|
|
|
|
process.exitValue();
|
|
|
|
return null;
|
|
|
|
} catch (IllegalThreadStateException e) {
|
|
|
|
// Process still running, gobble output until done
|
|
|
|
int end = res.size() - 1;
|
|
|
|
if (end > 0) {
|
2016-09-28 10:05:55 +00:00
|
|
|
if (res.get(end).equals("-root-done-")) {
|
2016-09-25 13:31:38 +00:00
|
|
|
res.remove(end);
|
2016-09-28 10:05:55 +00:00
|
|
|
if (res.get(end -1).isEmpty()) {
|
|
|
|
res.remove(end -1);
|
|
|
|
}
|
2016-09-25 13:31:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
try { STDOUT.join(100); } catch (InterruptedException err) { return null; }
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-09-25 13:31:38 +00:00
|
|
|
} catch (IOException e) {
|
|
|
|
if (!e.getMessage().contains("EPIPE")) {
|
2016-09-29 19:18:08 +00:00
|
|
|
Logger.dev("Shell: Root shell error...");
|
2016-09-25 13:31:38 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
} catch(InterruptedException e) {
|
2016-09-29 19:18:08 +00:00
|
|
|
Logger.dev("Shell: Root shell error...");
|
2016-09-25 13:31:38 +00:00
|
|
|
return null;
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|
|
|
|
|
2016-09-25 13:31:38 +00:00
|
|
|
return new ArrayList<>(res);
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|
|
|
|
}
|