From 53c1a49b5baaa57b8777daeb962fdfee841304d8 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Mon, 28 Jun 2021 05:57:45 +0500 Subject: [PATCH] Make TermuxTask and TermuxSession agnostic to termux environment Those classes shouldn't be tied to termux environment like variables, interpreters and working directory since commands may need to be executed with a different environment like android's or with a different logic. Now both classes use the ShellEnvironmentClient interface to dynamically get the environment to be used which currently for Termux's case is implemented by TermuxShellEnvironmentClient which is just a wrapper for TermuxShellUtils since later implements static functions. --- .../java/com/termux/app/TermuxService.java | 10 +- .../shared/shell/ShellEnvironmentClient.java | 47 ++++++ .../com/termux/shared/shell/ShellUtils.java | 134 ---------------- .../termux/shared/shell/TermuxSession.java | 22 ++- .../shell/TermuxShellEnvironmentClient.java | 33 ++++ .../termux/shared/shell/TermuxShellUtils.java | 150 ++++++++++++++++++ .../com/termux/shared/shell/TermuxTask.java | 19 ++- .../com/termux/shared/termux/TermuxUtils.java | 3 +- 8 files changed, 265 insertions(+), 153 deletions(-) create mode 100644 termux-shared/src/main/java/com/termux/shared/shell/ShellEnvironmentClient.java create mode 100644 termux-shared/src/main/java/com/termux/shared/shell/TermuxShellEnvironmentClient.java create mode 100644 termux-shared/src/main/java/com/termux/shared/shell/TermuxShellUtils.java diff --git a/app/src/main/java/com/termux/app/TermuxService.java b/app/src/main/java/com/termux/app/TermuxService.java index 6404bf46..d79f8e5c 100644 --- a/app/src/main/java/com/termux/app/TermuxService.java +++ b/app/src/main/java/com/termux/app/TermuxService.java @@ -25,6 +25,9 @@ import com.termux.app.terminal.TermuxTerminalSessionClient; import com.termux.app.utils.PluginUtils; import com.termux.shared.data.IntentUtils; import com.termux.shared.models.errors.Errno; +import com.termux.shared.shell.ShellUtils; +import com.termux.shared.shell.TermuxShellEnvironmentClient; +import com.termux.shared.shell.TermuxShellUtils; import com.termux.shared.termux.TermuxConstants; import com.termux.shared.termux.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY; import com.termux.shared.termux.TermuxConstants.TERMUX_APP.TERMUX_SERVICE; @@ -34,7 +37,6 @@ import com.termux.shared.terminal.TermuxTerminalSessionClientBase; import com.termux.shared.logger.Logger; import com.termux.shared.notification.NotificationUtils; import com.termux.shared.packages.PermissionUtils; -import com.termux.shared.shell.ShellUtils; import com.termux.shared.data.DataUtils; import com.termux.shared.models.ExecutionCommand; import com.termux.shared.shell.TermuxTask; @@ -162,7 +164,7 @@ public final class TermuxService extends Service implements TermuxTask.TermuxTas public void onDestroy() { Logger.logVerbose(LOG_TAG, "onDestroy"); - ShellUtils.clearTermuxTMPDIR(true); + TermuxShellUtils.clearTermuxTMPDIR(true); actionReleaseWakeLock(false); if (!mWantsToStop) @@ -426,7 +428,7 @@ public final class TermuxService extends Service implements TermuxTask.TermuxTas if (Logger.getLogLevel() >= Logger.LOG_LEVEL_VERBOSE) Logger.logVerbose(LOG_TAG, executionCommand.toString()); - TermuxTask newTermuxTask = TermuxTask.execute(this, executionCommand, this, false); + TermuxTask newTermuxTask = TermuxTask.execute(this, executionCommand, this, new TermuxShellEnvironmentClient(), false); if (newTermuxTask == null) { Logger.logError(LOG_TAG, "Failed to execute new TermuxTask command for:\n" + executionCommand.getCommandIdAndLabelLogString()); // If the execution command was started for a plugin, then process the error @@ -522,7 +524,7 @@ public final class TermuxService extends Service implements TermuxTask.TermuxTas // Otherwise if command was manually started by the user like by adding a new terminal session, // then no need to set stdout executionCommand.terminalTranscriptRows = getTerminalTranscriptRows(); - TermuxSession newTermuxSession = TermuxSession.execute(this, executionCommand, getTermuxTerminalSessionClient(), this, sessionName, executionCommand.isPluginExecutionCommand); + TermuxSession newTermuxSession = TermuxSession.execute(this, executionCommand, getTermuxTerminalSessionClient(), this, new TermuxShellEnvironmentClient(), sessionName, executionCommand.isPluginExecutionCommand); if (newTermuxSession == null) { Logger.logError(LOG_TAG, "Failed to execute new TermuxSession command for:\n" + executionCommand.getCommandIdAndLabelLogString()); // If the execution command was started for a plugin, then process the error diff --git a/termux-shared/src/main/java/com/termux/shared/shell/ShellEnvironmentClient.java b/termux-shared/src/main/java/com/termux/shared/shell/ShellEnvironmentClient.java new file mode 100644 index 00000000..f8ed9a10 --- /dev/null +++ b/termux-shared/src/main/java/com/termux/shared/shell/ShellEnvironmentClient.java @@ -0,0 +1,47 @@ +package com.termux.shared.shell; + +import android.content.Context; + +import androidx.annotation.NonNull; + +public interface ShellEnvironmentClient { + + /** + * Get the default working directory path for the environment in case the path that was passed + * was {@code null} or empty. + * + * @return Should return the default working directory path. + */ + @NonNull + String getDefaultWorkingDirectoryPath(); + + /** + * Get the default "/bin" path, likely $PREFIX/bin. + * + * @return Should return the "/bin" path. + */ + @NonNull + String getDefaultBinPath(); + + /** + * Build the shell environment to be used for commands. + * + * @param currentPackageContext The {@link Context} for the current package. + * @param isFailSafe If running a failsafe session. + * @param workingDirectory The working directory for the environment. + * @return Should return the build environment. + */ + @NonNull + String[] buildEnvironment(Context currentPackageContext, boolean isFailSafe, String workingDirectory); + + /** + * Setup process arguments for the file to execute, like interpreter, etc. + * + * @param fileToExecute The file to execute. + * @param arguments The arguments to pass to the executable. + * @return Should return the final process arguments. + */ + @NonNull + String[] setupProcessArgs(@NonNull String fileToExecute, String[] arguments); + +} diff --git a/termux-shared/src/main/java/com/termux/shared/shell/ShellUtils.java b/termux-shared/src/main/java/com/termux/shared/shell/ShellUtils.java index 89c66f3c..d6687e05 100644 --- a/termux-shared/src/main/java/com/termux/shared/shell/ShellUtils.java +++ b/termux-shared/src/main/java/com/termux/shared/shell/ShellUtils.java @@ -1,82 +1,13 @@ package com.termux.shared.shell; -import android.content.Context; - -import androidx.annotation.NonNull; - -import com.termux.shared.models.errors.Error; -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 com.termux.terminal.TerminalBuffer; import com.termux.terminal.TerminalEmulator; import com.termux.terminal.TerminalSession; -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 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 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"); @@ -91,77 +22,12 @@ public class ShellUtils { } } - public static String[] setupProcessArgs(@NonNull 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 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(boolean onlyIfExists) { - if(onlyIfExists && !FileUtils.directoryFileExists(TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH, false)) - return; - - Error error; - error = FileUtils.clearDirectory("$TMPDIR", FileUtils.getCanonicalPath(TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH, null)); - if (error != null) { - Logger.logErrorExtended(error.toString()); - } - } - public static String getTerminalSessionTranscriptText(TerminalSession terminalSession, boolean linesJoined, boolean trim) { if (terminalSession == null) return null; diff --git a/termux-shared/src/main/java/com/termux/shared/shell/TermuxSession.java b/termux-shared/src/main/java/com/termux/shared/shell/TermuxSession.java index e40e3e31..e67ab9fd 100644 --- a/termux-shared/src/main/java/com/termux/shared/shell/TermuxSession.java +++ b/termux-shared/src/main/java/com/termux/shared/shell/TermuxSession.java @@ -9,7 +9,6 @@ import com.termux.shared.R; import com.termux.shared.models.ExecutionCommand; import com.termux.shared.models.ResultData; import com.termux.shared.models.errors.Errno; -import com.termux.shared.termux.TermuxConstants; import com.termux.shared.logger.Logger; import com.termux.terminal.TerminalSession; import com.termux.terminal.TerminalSessionClient; @@ -52,6 +51,7 @@ public class TermuxSession { * @param executionCommand The {@link ExecutionCommand} containing the information for execution command. * @param terminalSessionClient The {@link TerminalSessionClient} interface implementation. * @param termuxSessionClient The {@link TermuxSessionClient} interface implementation. + * @param shellEnvironmentClient The {@link ShellEnvironmentClient} interface implementation. * @param sessionName The optional {@link TerminalSession} name. * @param setStdoutOnExit If set to {@code true}, then the {@link ResultData#stdout} * available in the {@link TermuxSessionClient#onTermuxSessionExited(TermuxSession)} @@ -64,16 +64,24 @@ public class TermuxSession { */ public static TermuxSession execute(@NonNull final Context context, @NonNull ExecutionCommand executionCommand, @NonNull final TerminalSessionClient terminalSessionClient, final TermuxSessionClient termuxSessionClient, + @NonNull final ShellEnvironmentClient shellEnvironmentClient, final String sessionName, final boolean setStdoutOnExit) { - if (executionCommand.workingDirectory == null || executionCommand.workingDirectory.isEmpty()) executionCommand.workingDirectory = TermuxConstants.TERMUX_HOME_DIR_PATH; + if (executionCommand.workingDirectory == null || executionCommand.workingDirectory.isEmpty()) + executionCommand.workingDirectory = shellEnvironmentClient.getDefaultWorkingDirectoryPath(); + if (executionCommand.workingDirectory.isEmpty()) + executionCommand.workingDirectory = "/"; - String[] environment = ShellUtils.buildEnvironment(context, executionCommand.isFailsafe, executionCommand.workingDirectory); + String[] environment = shellEnvironmentClient.buildEnvironment(context, executionCommand.isFailsafe, executionCommand.workingDirectory); + + String defaultBinPath = shellEnvironmentClient.getDefaultBinPath(); + if (defaultBinPath.isEmpty()) + defaultBinPath = "/system/bin"; boolean isLoginShell = false; if (executionCommand.executable == null) { if (!executionCommand.isFailsafe) { for (String shellBinary : new String[]{"login", "bash", "zsh"}) { - File shellFile = new File(TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH, shellBinary); + File shellFile = new File(defaultBinPath, shellBinary); if (shellFile.canExecute()) { executionCommand.executable = shellFile.getAbsolutePath(); break; @@ -88,7 +96,7 @@ public class TermuxSession { isLoginShell = true; } - String[] processArgs = ShellUtils.setupProcessArgs(executionCommand.executable, executionCommand.arguments); + String[] processArgs = shellEnvironmentClient.setupProcessArgs(executionCommand.executable, executionCommand.arguments); executionCommand.executable = processArgs[0]; String processName = (isLoginShell ? "-" : "") + ShellUtils.getExecutableBasename(executionCommand.executable); @@ -199,10 +207,10 @@ public class TermuxSession { * callback will be called. * * @param termuxSession The {@link TermuxSession}, which should be set if - * {@link #execute(Context, ExecutionCommand, TerminalSessionClient, TermuxSessionClient, String, boolean)} + * {@link #execute(Context, ExecutionCommand, TerminalSessionClient, TermuxSessionClient, ShellEnvironmentClient, String, boolean)} * successfully started the process. * @param executionCommand The {@link ExecutionCommand}, which should be set if - * {@link #execute(Context, ExecutionCommand, TerminalSessionClient, TermuxSessionClient, String, boolean)} + * {@link #execute(Context, ExecutionCommand, TerminalSessionClient, TermuxSessionClient, ShellEnvironmentClient, String, boolean)} * failed to start the process. */ private static void processTermuxSessionResult(final TermuxSession termuxSession, ExecutionCommand executionCommand) { diff --git a/termux-shared/src/main/java/com/termux/shared/shell/TermuxShellEnvironmentClient.java b/termux-shared/src/main/java/com/termux/shared/shell/TermuxShellEnvironmentClient.java new file mode 100644 index 00000000..a2cb80c6 --- /dev/null +++ b/termux-shared/src/main/java/com/termux/shared/shell/TermuxShellEnvironmentClient.java @@ -0,0 +1,33 @@ +package com.termux.shared.shell; + +import android.content.Context; + +import androidx.annotation.NonNull; + +public class TermuxShellEnvironmentClient implements ShellEnvironmentClient { + + @NonNull + @Override + public String getDefaultWorkingDirectoryPath() { + return TermuxShellUtils.getDefaultWorkingDirectoryPath(); + } + + @NonNull + @Override + public String getDefaultBinPath() { + return TermuxShellUtils.getDefaultBinPath(); + } + + @NonNull + @Override + public String[] buildEnvironment(Context currentPackageContext, boolean isFailSafe, String workingDirectory) { + return TermuxShellUtils.buildEnvironment(currentPackageContext, isFailSafe, workingDirectory); + } + + @NonNull + @Override + public String[] setupProcessArgs(@NonNull String fileToExecute, String[] arguments) { + return TermuxShellUtils.setupProcessArgs(fileToExecute, arguments); + } + +} diff --git a/termux-shared/src/main/java/com/termux/shared/shell/TermuxShellUtils.java b/termux-shared/src/main/java/com/termux/shared/shell/TermuxShellUtils.java new file mode 100644 index 00000000..fade41db --- /dev/null +++ b/termux-shared/src/main/java/com/termux/shared/shell/TermuxShellUtils.java @@ -0,0 +1,150 @@ +package com.termux.shared.shell; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import com.termux.shared.models.errors.Error; +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.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class TermuxShellUtils { + + public static String getDefaultWorkingDirectoryPath() { + return TermuxConstants.TERMUX_HOME_DIR_PATH; + } + + public static String getDefaultBinPath() { + return TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH; + } + + public static String[] buildEnvironment(Context currentPackageContext, boolean isFailSafe, String workingDirectory) { + TermuxConstants.TERMUX_HOME_DIR.mkdirs(); + + if (workingDirectory == null || workingDirectory.isEmpty()) + workingDirectory = getDefaultWorkingDirectoryPath(); + + List 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 environment, String name) { + String value = System.getenv(name); + if (value != null) { + environment.add(name + "=" + value); + } + } + + public static String[] setupProcessArgs(@NonNull 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 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 void clearTermuxTMPDIR(boolean onlyIfExists) { + if(onlyIfExists && !FileUtils.directoryFileExists(TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH, false)) + return; + + Error error; + error = FileUtils.clearDirectory("$TMPDIR", FileUtils.getCanonicalPath(TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH, null)); + if (error != null) { + Logger.logErrorExtended(error.toString()); + } + } + +} diff --git a/termux-shared/src/main/java/com/termux/shared/shell/TermuxTask.java b/termux-shared/src/main/java/com/termux/shared/shell/TermuxTask.java index eac551d7..4b2801ea 100644 --- a/termux-shared/src/main/java/com/termux/shared/shell/TermuxTask.java +++ b/termux-shared/src/main/java/com/termux/shared/shell/TermuxTask.java @@ -11,7 +11,6 @@ import com.termux.shared.R; import com.termux.shared.models.ExecutionCommand; import com.termux.shared.models.ResultData; import com.termux.shared.models.errors.Errno; -import com.termux.shared.termux.TermuxConstants; import com.termux.shared.logger.Logger; import com.termux.shared.models.ExecutionCommand.ExecutionState; @@ -54,6 +53,7 @@ public final class TermuxTask { * be called regardless of {@code isSynchronous} value but not if * {@code null} is returned by this method. This can * optionally be {@code null}. + * @param shellEnvironmentClient The {@link ShellEnvironmentClient} interface implementation. * @param isSynchronous If set to {@code true}, then the command will be executed in the * caller thread and results returned synchronously in the {@link ExecutionCommand} * sub object of the {@link TermuxTask} returned. @@ -62,12 +62,17 @@ public final class TermuxTask { * @return Returns the {@link TermuxTask}. This will be {@code null} if failed to start the execution command. */ public static TermuxTask execute(@NonNull final Context context, @NonNull ExecutionCommand executionCommand, - final TermuxTaskClient termuxTaskClient, final boolean isSynchronous) { - if (executionCommand.workingDirectory == null || executionCommand.workingDirectory.isEmpty()) executionCommand.workingDirectory = TermuxConstants.TERMUX_HOME_DIR_PATH; + final TermuxTaskClient termuxTaskClient, + @NonNull final ShellEnvironmentClient shellEnvironmentClient, + final boolean isSynchronous) { + if (executionCommand.workingDirectory == null || executionCommand.workingDirectory.isEmpty()) + executionCommand.workingDirectory = shellEnvironmentClient.getDefaultWorkingDirectoryPath(); + if (executionCommand.workingDirectory.isEmpty()) + executionCommand.workingDirectory = "/"; - String[] env = ShellUtils.buildEnvironment(context, false, executionCommand.workingDirectory); + String[] env = shellEnvironmentClient.buildEnvironment(context, false, executionCommand.workingDirectory); - final String[] commandArray = ShellUtils.setupProcessArgs(executionCommand.executable, executionCommand.arguments); + final String[] commandArray = shellEnvironmentClient.setupProcessArgs(executionCommand.executable, executionCommand.arguments); if (!executionCommand.setState(ExecutionState.EXECUTING)) { executionCommand.setStateFailed(Errno.ERRNO_FAILED.getCode(), context.getString(R.string.error_failed_to_execute_termux_task_command, executionCommand.getCommandIdAndLabelLogString())); @@ -254,10 +259,10 @@ public final class TermuxTask { * then the {@link TermuxTaskClient#onTermuxTaskExited(TermuxTask)} callback will be called. * * @param termuxTask The {@link TermuxTask}, which should be set if - * {@link #execute(Context, ExecutionCommand, TermuxTaskClient, boolean)} + * {@link #execute(Context, ExecutionCommand, TermuxTaskClient, ShellEnvironmentClient, boolean)} * successfully started the process. * @param executionCommand The {@link ExecutionCommand}, which should be set if - * {@link #execute(Context, ExecutionCommand, TermuxTaskClient, boolean)} + * {@link #execute(Context, ExecutionCommand, TermuxTaskClient, ShellEnvironmentClient, boolean)} * failed to start the process. */ private static void processTermuxTaskResult(final TermuxTask termuxTask, ExecutionCommand executionCommand) { diff --git a/termux-shared/src/main/java/com/termux/shared/termux/TermuxUtils.java b/termux-shared/src/main/java/com/termux/shared/termux/TermuxUtils.java index 1296def9..71e6a3ae 100644 --- a/termux-shared/src/main/java/com/termux/shared/termux/TermuxUtils.java +++ b/termux-shared/src/main/java/com/termux/shared/termux/TermuxUtils.java @@ -16,6 +16,7 @@ import com.termux.shared.logger.Logger; import com.termux.shared.markdown.MarkdownUtils; import com.termux.shared.models.ExecutionCommand; import com.termux.shared.packages.PackageUtils; +import com.termux.shared.shell.TermuxShellEnvironmentClient; import com.termux.shared.shell.TermuxTask; import org.apache.commons.io.IOUtils; @@ -399,7 +400,7 @@ public class TermuxUtils { aptInfoScript = aptInfoScript.replaceAll(Pattern.quote("@TERMUX_PREFIX@"), TermuxConstants.TERMUX_PREFIX_DIR_PATH); ExecutionCommand executionCommand = new ExecutionCommand(1, TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/bash", null, aptInfoScript, null, true, false); - TermuxTask termuxTask = TermuxTask.execute(context, executionCommand, null, true); + TermuxTask termuxTask = TermuxTask.execute(context, executionCommand, null, new TermuxShellEnvironmentClient(), true); if (termuxTask == null || !executionCommand.isSuccessful() || executionCommand.resultData.exitCode != 0) { Logger.logError(LOG_TAG, executionCommand.toString()); return null;