mirror of https://github.com/termux/termux-app
157 lines
6.9 KiB
Java
157 lines
6.9 KiB
Java
package com.termux.shared.shell;
|
|
|
|
import android.content.Context;
|
|
|
|
import com.termux.shared.termux.TermuxConstants;
|
|
import com.termux.shared.file.FileUtils;
|
|
import com.termux.shared.logger.Logger;
|
|
import com.termux.shared.packages.PackageUtils;
|
|
import com.termux.shared.termux.TermuxUtils;
|
|
|
|
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.List;
|
|
|
|
public class ShellUtils {
|
|
|
|
public static String[] buildEnvironment(Context currentPackageContext, boolean isFailSafe, String workingDirectory) {
|
|
TermuxConstants.TERMUX_HOME_DIR.mkdirs();
|
|
|
|
if (workingDirectory == null || workingDirectory.isEmpty()) workingDirectory = TermuxConstants.TERMUX_HOME_DIR_PATH;
|
|
|
|
List<String> environment = new ArrayList<>();
|
|
|
|
// This function may be called by a different package like a plugin, so we get version for Termux package via its context
|
|
Context termuxPackageContext = TermuxUtils.getTermuxPackageContext(currentPackageContext);
|
|
if(termuxPackageContext != null) {
|
|
String termuxVersionName = PackageUtils.getVersionNameForPackage(termuxPackageContext);
|
|
if(termuxVersionName != null)
|
|
environment.add("TERMUX_VERSION=" + termuxVersionName);
|
|
}
|
|
|
|
environment.add("TERM=xterm-256color");
|
|
environment.add("COLORTERM=truecolor");
|
|
environment.add("HOME=" + TermuxConstants.TERMUX_HOME_DIR_PATH);
|
|
environment.add("PREFIX=" + TermuxConstants.TERMUX_PREFIX_DIR_PATH);
|
|
environment.add("BOOTCLASSPATH=" + System.getenv("BOOTCLASSPATH"));
|
|
environment.add("ANDROID_ROOT=" + System.getenv("ANDROID_ROOT"));
|
|
environment.add("ANDROID_DATA=" + System.getenv("ANDROID_DATA"));
|
|
// EXTERNAL_STORAGE is needed for /system/bin/am to work on at least
|
|
// Samsung S7 - see https://plus.google.com/110070148244138185604/posts/gp8Lk3aCGp3.
|
|
environment.add("EXTERNAL_STORAGE=" + System.getenv("EXTERNAL_STORAGE"));
|
|
|
|
// These variables are needed if running on Android 10 and higher.
|
|
addToEnvIfPresent(environment, "ANDROID_ART_ROOT");
|
|
addToEnvIfPresent(environment, "DEX2OATBOOTCLASSPATH");
|
|
addToEnvIfPresent(environment, "ANDROID_I18N_ROOT");
|
|
addToEnvIfPresent(environment, "ANDROID_RUNTIME_ROOT");
|
|
addToEnvIfPresent(environment, "ANDROID_TZDATA_ROOT");
|
|
|
|
if (isFailSafe) {
|
|
// Keep the default path so that system binaries can be used in the failsafe session.
|
|
environment.add("PATH= " + System.getenv("PATH"));
|
|
} else {
|
|
environment.add("LANG=en_US.UTF-8");
|
|
environment.add("PATH=" + TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH);
|
|
environment.add("PWD=" + workingDirectory);
|
|
environment.add("TMPDIR=" + TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH);
|
|
}
|
|
|
|
return environment.toArray(new String[0]);
|
|
}
|
|
|
|
public static void addToEnvIfPresent(List<String> environment, String name) {
|
|
String value = System.getenv(name);
|
|
if (value != null) {
|
|
environment.add(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;
|
|
}
|
|
}
|
|
|
|
public static String[] setupProcessArgs(String fileToExecute, 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(fileToExecute);
|
|
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 executable = builder.toString();
|
|
if (executable.startsWith("/usr") || executable.startsWith("/bin")) {
|
|
String[] parts = executable.split("/");
|
|
String binary = parts[parts.length - 1];
|
|
interpreter = TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/" + binary;
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
builder.append(c);
|
|
}
|
|
}
|
|
} else {
|
|
// No shebang and no ELF, use standard shell.
|
|
interpreter = TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/sh";
|
|
}
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
// Ignore.
|
|
}
|
|
|
|
List<String> result = new ArrayList<>();
|
|
if (interpreter != null) result.add(interpreter);
|
|
result.add(fileToExecute);
|
|
if (arguments != null) Collections.addAll(result, arguments);
|
|
return result.toArray(new String[0]);
|
|
}
|
|
|
|
public static String getExecutableBasename(String executable) {
|
|
if(executable == null) return null;
|
|
int lastSlash = executable.lastIndexOf('/');
|
|
return (lastSlash == -1) ? executable : executable.substring(lastSlash + 1);
|
|
}
|
|
|
|
public static void clearTermuxTMPDIR(Context context) {
|
|
String errmsg;
|
|
errmsg = FileUtils.clearDirectory(context, "$TMPDIR", FileUtils.getCanonicalPath(TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH, null, false));
|
|
if (errmsg != null) {
|
|
Logger.logErrorAndShowToast(context, errmsg);
|
|
}
|
|
}
|
|
|
|
}
|