2016-08-24 21:58:15 +00:00
|
|
|
package com.topjohnwu.magisk.utils;
|
|
|
|
|
2017-08-06 16:15:46 +00:00
|
|
|
import android.text.TextUtils;
|
2017-06-05 19:06:23 +00:00
|
|
|
|
2017-08-03 15:33:08 +00:00
|
|
|
import com.topjohnwu.magisk.MagiskManager;
|
|
|
|
|
2017-07-17 19:34:06 +00:00
|
|
|
import java.io.BufferedReader;
|
2016-08-24 21:58:15 +00:00
|
|
|
import java.io.IOException;
|
2017-09-27 19:33:56 +00:00
|
|
|
import java.io.InputStream;
|
2017-07-17 19:34:06 +00:00
|
|
|
import java.io.InputStreamReader;
|
2017-10-15 15:02:44 +00:00
|
|
|
import java.io.OutputStream;
|
2016-08-24 21:58:15 +00:00
|
|
|
import java.util.ArrayList;
|
2017-07-19 10:01:22 +00:00
|
|
|
import java.util.Collection;
|
2016-08-24 21:58:15 +00:00
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Modified by topjohnwu, based on Chainfire's libsuperuser
|
|
|
|
*/
|
|
|
|
|
|
|
|
public class Shell {
|
|
|
|
|
2017-10-19 12:37:58 +00:00
|
|
|
// -2 = not initialized; -1 = no shell; 0 = non root shell; 1 = root shell
|
|
|
|
public static int status = -2;
|
2016-08-24 21:58:15 +00:00
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
private final Process process;
|
|
|
|
private final OutputStream STDIN;
|
|
|
|
private final InputStream STDOUT;
|
2017-07-15 19:32:29 +00:00
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
private static void testRootShell(Shell shell) throws IOException {
|
|
|
|
shell.STDIN.write(("id\n").getBytes("UTF-8"));
|
|
|
|
shell.STDIN.flush();
|
|
|
|
String s = new BufferedReader(new InputStreamReader(shell.STDOUT)).readLine();
|
2017-08-12 09:07:28 +00:00
|
|
|
if (TextUtils.isEmpty(s) || !s.contains("uid=0")) {
|
2017-10-15 15:02:44 +00:00
|
|
|
shell.STDIN.close();
|
|
|
|
shell.STDIN.close();
|
2017-08-12 09:07:28 +00:00
|
|
|
throw new IOException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
public Shell(String command) throws IOException {
|
|
|
|
process = Runtime.getRuntime().exec(command);
|
|
|
|
STDIN = process.getOutputStream();
|
|
|
|
STDOUT = process.getInputStream();
|
|
|
|
}
|
2017-07-17 19:34:06 +00:00
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
public static Shell getShell() {
|
2017-10-15 16:54:48 +00:00
|
|
|
MagiskManager mm = MagiskManager.get();
|
2017-10-15 15:02:44 +00:00
|
|
|
boolean needNewShell = mm.shell == null;
|
|
|
|
|
|
|
|
if (!needNewShell) {
|
2017-08-12 09:07:28 +00:00
|
|
|
try {
|
2017-10-15 15:02:44 +00:00
|
|
|
mm.shell.process.exitValue();
|
|
|
|
// The process is dead
|
|
|
|
needNewShell = true;
|
|
|
|
} catch (IllegalThreadStateException ignored) {
|
|
|
|
// This should be the expected result
|
2017-08-12 09:07:28 +00:00
|
|
|
}
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
if (needNewShell) {
|
|
|
|
status = 1;
|
2017-08-12 09:07:28 +00:00
|
|
|
try {
|
2017-10-15 15:02:44 +00:00
|
|
|
mm.shell = new Shell("su --mount-master");
|
|
|
|
testRootShell(mm.shell);
|
2017-08-12 09:07:28 +00:00
|
|
|
} catch (IOException e) {
|
2017-10-15 15:02:44 +00:00
|
|
|
// Mount master not implemented
|
|
|
|
try {
|
|
|
|
mm.shell = new Shell("su");
|
|
|
|
testRootShell(mm.shell);
|
|
|
|
} catch (IOException e1) {
|
|
|
|
// No root exists
|
|
|
|
status = 0;
|
|
|
|
try {
|
|
|
|
mm.shell = new Shell("sh");
|
|
|
|
} catch (IOException e2) {
|
|
|
|
status = -1;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|
2017-11-14 20:39:05 +00:00
|
|
|
if (rootAccess()) {
|
|
|
|
// Load utility shell scripts
|
|
|
|
try (InputStream in = mm.getAssets().open(Const.UTIL_FUNCTIONS)) {
|
|
|
|
mm.shell.loadInputStream(in);
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Root shell initialization
|
|
|
|
mm.shell.run_raw(false,
|
|
|
|
"export PATH=" + Const.BUSYBOXPATH + ":$PATH",
|
|
|
|
"mount_partitions",
|
|
|
|
"find_boot_image",
|
|
|
|
"migrate_boot_backup"
|
|
|
|
);
|
|
|
|
}
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|
2017-07-17 19:34:06 +00:00
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
return mm.shell;
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
public static boolean rootAccess() {
|
2017-10-19 12:37:58 +00:00
|
|
|
if (status == -2) getShell();
|
2017-10-15 15:02:44 +00:00
|
|
|
return status > 0;
|
2017-09-04 20:07:33 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
public void run(Collection<String> output, String... commands) {
|
|
|
|
synchronized (process) {
|
2017-10-19 12:37:58 +00:00
|
|
|
try {
|
|
|
|
StreamGobbler out = new StreamGobbler(STDOUT, output);
|
|
|
|
out.start();
|
|
|
|
run_raw(true, commands);
|
|
|
|
STDIN.write("echo \'-shell-done-\'\n".getBytes("UTF-8"));
|
|
|
|
STDIN.flush();
|
|
|
|
try {
|
|
|
|
out.join();
|
|
|
|
} catch (InterruptedException ignored) {}
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
process.destroy();
|
|
|
|
}
|
2017-08-03 15:33:08 +00:00
|
|
|
}
|
2017-07-15 17:20:39 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
public void run_raw(boolean stdout, String... commands) {
|
|
|
|
synchronized (process) {
|
|
|
|
try {
|
|
|
|
for (String command : commands) {
|
2017-10-19 12:37:58 +00:00
|
|
|
Logger.shell(true, command);
|
2017-10-15 15:02:44 +00:00
|
|
|
STDIN.write((command + (stdout ? "\n" : " >/dev/null\n")).getBytes("UTF-8"));
|
|
|
|
STDIN.flush();
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
process.destroy();
|
|
|
|
}
|
|
|
|
}
|
2016-08-25 10:08:07 +00:00
|
|
|
}
|
|
|
|
|
2017-09-27 19:33:56 +00:00
|
|
|
public void loadInputStream(InputStream in) {
|
2017-10-19 12:37:58 +00:00
|
|
|
synchronized (process) {
|
|
|
|
try {
|
|
|
|
int read;
|
|
|
|
byte[] bytes = new byte[4096];
|
|
|
|
while ((read = in.read(bytes)) != -1) {
|
|
|
|
STDIN.write(bytes, 0, read);
|
|
|
|
}
|
|
|
|
STDIN.flush();
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
2017-09-27 19:33:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
public static List<String> sh(String... commands) {
|
2017-07-15 17:20:39 +00:00
|
|
|
List<String> res = new ArrayList<>();
|
2017-07-17 19:34:06 +00:00
|
|
|
sh(res, commands);
|
2017-07-15 17:20:39 +00:00
|
|
|
return res;
|
2016-09-25 13:31:38 +00:00
|
|
|
}
|
2016-08-24 21:58:15 +00:00
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
public static void sh(Collection<String> output, String... commands) {
|
|
|
|
Shell shell = getShell();
|
|
|
|
if (shell == null)
|
|
|
|
return;
|
|
|
|
shell.run(output, commands);
|
2017-06-05 19:06:23 +00:00
|
|
|
}
|
2017-07-15 19:32:29 +00:00
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
public static void sh_raw(String... commands) {
|
|
|
|
Shell shell = getShell();
|
|
|
|
if (shell == null)
|
|
|
|
return;
|
|
|
|
shell.run_raw(false, commands);
|
2017-07-15 19:32:29 +00:00
|
|
|
}
|
2017-07-17 19:34:06 +00:00
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
public static List<String> su(String... commands) {
|
2017-07-17 19:34:06 +00:00
|
|
|
if (!rootAccess()) return sh();
|
|
|
|
return sh(commands);
|
|
|
|
}
|
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
public static void su(Collection<String> output, String... commands) {
|
2017-07-17 19:34:06 +00:00
|
|
|
if (!rootAccess()) return;
|
2017-10-15 15:02:44 +00:00
|
|
|
sh(output, commands);
|
2017-07-17 19:34:06 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 15:02:44 +00:00
|
|
|
public static void su_raw(String... commands) {
|
2017-07-17 19:34:06 +00:00
|
|
|
if (!rootAccess()) return;
|
2017-10-15 15:02:44 +00:00
|
|
|
sh_raw(commands);
|
2017-07-17 19:34:06 +00:00
|
|
|
}
|
2017-07-19 10:01:22 +00:00
|
|
|
|
|
|
|
public static abstract class AbstractList<E> extends java.util.AbstractList<E> {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public abstract boolean add(E e);
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public E get(int i) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int size() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|