mirror of https://github.com/termux/termux-app
138 lines
6.0 KiB
Java
138 lines
6.0 KiB
Java
package com.termux.app;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.IOException;
|
|
import java.lang.reflect.Field;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
public class TermuxShellUtils {
|
|
|
|
@NonNull
|
|
public static String[] setupShellCommandArguments(@NonNull String executable, @Nullable String[] arguments) {
|
|
// The file to execute may either be:
|
|
// - An elf file, in which we execute it directly.
|
|
// - A script file without shebang, which we execute with our standard shell $PREFIX/bin/sh instead of the
|
|
// system /system/bin/sh. The system shell may vary and may not work at all due to LD_LIBRARY_PATH.
|
|
// - A file with shebang, which we try to handle with e.g. /bin/foo -> $PREFIX/bin/foo.
|
|
String interpreter = null;
|
|
try {
|
|
File file = new File(executable);
|
|
try (FileInputStream in = new FileInputStream(file)) {
|
|
byte[] buffer = new byte[256];
|
|
int bytesRead = in.read(buffer);
|
|
if (bytesRead > 4) {
|
|
if (buffer[0] == 0x7F && buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F') {
|
|
// Elf file, do nothing.
|
|
} else if (buffer[0] == '#' && buffer[1] == '!') {
|
|
// Try to parse shebang.
|
|
StringBuilder builder = new StringBuilder();
|
|
for (int i = 2; i < bytesRead; i++) {
|
|
char c = (char) buffer[i];
|
|
if (c == ' ' || c == '\n') {
|
|
if (builder.length() == 0) {
|
|
// Skip whitespace after shebang.
|
|
} else {
|
|
// End of shebang.
|
|
String shebangExecutable = builder.toString();
|
|
if (shebangExecutable.startsWith("/usr") || shebangExecutable.startsWith("/bin")) {
|
|
String[] parts = shebangExecutable.split("/");
|
|
String binary = parts[parts.length - 1];
|
|
interpreter = TermuxConstants.BIN_PATH + "/" + binary;
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
builder.append(c);
|
|
}
|
|
}
|
|
} else {
|
|
// No shebang and no ELF, use standard shell.
|
|
interpreter = TermuxConstants.BIN_PATH + "/sh";
|
|
}
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
// Ignore.
|
|
}
|
|
|
|
List<String> result = new ArrayList<>();
|
|
if (interpreter != null) result.add(interpreter);
|
|
result.add(executable);
|
|
if (arguments != null) Collections.addAll(result, arguments);
|
|
return result.toArray(new String[0]);
|
|
}
|
|
|
|
|
|
public static String[] setupEnvironment(boolean failsafe) {
|
|
Map<String, String> environment = new HashMap<>();
|
|
environment.put("HOME", TermuxConstants.HOME_PATH);
|
|
environment.put("LANG", "en_US.UTF-8");
|
|
String tmpDir = TermuxConstants.PREFIX_PATH + "/tmp";
|
|
environment.put("TMP", tmpDir);
|
|
environment.put("TMPDIR", tmpDir);
|
|
environment.put("COLORTERM", "truecolor");
|
|
environment.put("TERM", "xterm-256color");
|
|
putToEnvIfInSystemEnv(environment, "PATH");
|
|
putToEnvIfInSystemEnv(environment, "ANDROID_ASSETS");
|
|
putToEnvIfInSystemEnv(environment, "ANDROID_DATA");
|
|
putToEnvIfInSystemEnv(environment, "ANDROID_ROOT");
|
|
putToEnvIfInSystemEnv(environment, "ANDROID_STORAGE");
|
|
// EXTERNAL_STORAGE is needed for /system/bin/am to work on at least
|
|
// Samsung S7 - see https://plus.google.com/110070148244138185604/posts/gp8Lk3aCGp3.
|
|
// https://cs.android.com/android/_/android/platform/system/core/+/fc000489
|
|
putToEnvIfInSystemEnv(environment, "EXTERNAL_STORAGE");
|
|
putToEnvIfInSystemEnv(environment, "ASEC_MOUNTPOINT");
|
|
putToEnvIfInSystemEnv(environment, "LOOP_MOUNTPOINT");
|
|
putToEnvIfInSystemEnv(environment, "ANDROID_RUNTIME_ROOT");
|
|
putToEnvIfInSystemEnv(environment, "ANDROID_ART_ROOT");
|
|
putToEnvIfInSystemEnv(environment, "ANDROID_I18N_ROOT");
|
|
putToEnvIfInSystemEnv(environment, "ANDROID_TZDATA_ROOT");
|
|
putToEnvIfInSystemEnv(environment, "BOOTCLASSPATH");
|
|
putToEnvIfInSystemEnv(environment, "DEX2OATBOOTCLASSPATH");
|
|
putToEnvIfInSystemEnv(environment, "SYSTEMSERVERCLASSPATH");
|
|
|
|
if (!failsafe) {
|
|
environment.put("LD_PRELOAD", TermuxConstants.PREFIX_PATH + "/lib/libtermux-exec.so");
|
|
environment.put("PATH", TermuxConstants.PREFIX_PATH + "/bin:" + System.getenv("PATH"));
|
|
}
|
|
|
|
List<String> environmentList = new ArrayList<>(environment.values());
|
|
for (Map.Entry<String, String> entry : environment.entrySet()) {
|
|
environmentList.add(entry.getKey() + "=" + entry.getValue());
|
|
}
|
|
Collections.sort(environmentList);
|
|
return environmentList.toArray(new String[0]);
|
|
}
|
|
|
|
private static void putToEnvIfInSystemEnv(@NonNull Map<String, String> environment, @NonNull String name) {
|
|
String value = System.getenv(name);
|
|
if (value != null) {
|
|
environment.put(name, value);
|
|
}
|
|
}
|
|
|
|
public static int getPid(Process p) {
|
|
try {
|
|
Field f = p.getClass().getDeclaredField("pid");
|
|
f.setAccessible(true);
|
|
try {
|
|
return f.getInt(p);
|
|
} finally {
|
|
f.setAccessible(false);
|
|
}
|
|
} catch (Throwable e) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
}
|