mirror of
https://github.com/termux/termux-app
synced 2024-06-23 17:47:07 +00:00
Added|Changed!: Implement new design for shell environment generation and add support for MIT
licensed shell environment client
- `ShellEnvironmentClient` has been renamed to `IShellEnvironment` with certain changes to its interface methods, including requirement for `Execution` command itself for `setupShellCommandEnvironment()`. - `UnixShellEnvironment` implements the `IShellEnvironment` interface as is the abstract base class of all other shell environments. - `AndroidShellEnvironment` extends from the `UnixShellEnvironment` class and provides an environment that would work for Android shells. This is `MIT` licensed and can be used by users importing the `termux-shared` library or the library itself to run `AppShell` shells. Previously, `TermuxShellEnvironmentClient` existed which was `GPLv3` licensed and it would not have been possible to use it for non-GPL code. - `TermuxShellEnvironment` extends from the `AndroidShellEnvironment` class and adds/overrides additional environment variables required for Termux shells to work, including setting `HOME`, `TMPDIR`, `PATH` and `LD_LIBRARY_PATH` appropriately. Termux app related variables will be added in a later commit. `TermuxShellEnvironment` replaces `TermuxShellEnvironmentClient` and is `GPLv3` licensed.
This commit is contained in:
parent
0328d15ea7
commit
f102ea20b2
|
@ -28,7 +28,7 @@ import com.termux.shared.errors.Errno;
|
||||||
import com.termux.shared.shell.ShellUtils;
|
import com.termux.shared.shell.ShellUtils;
|
||||||
import com.termux.shared.shell.command.runner.app.AppShell;
|
import com.termux.shared.shell.command.runner.app.AppShell;
|
||||||
import com.termux.shared.termux.settings.properties.TermuxAppSharedProperties;
|
import com.termux.shared.termux.settings.properties.TermuxAppSharedProperties;
|
||||||
import com.termux.shared.termux.shell.TermuxShellEnvironmentClient;
|
import com.termux.shared.termux.shell.command.environment.TermuxShellEnvironment;
|
||||||
import com.termux.shared.termux.shell.TermuxShellUtils;
|
import com.termux.shared.termux.shell.TermuxShellUtils;
|
||||||
import com.termux.shared.termux.TermuxConstants;
|
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_ACTIVITY;
|
||||||
|
@ -469,7 +469,8 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
|
||||||
if (Logger.getLogLevel() >= Logger.LOG_LEVEL_VERBOSE)
|
if (Logger.getLogLevel() >= Logger.LOG_LEVEL_VERBOSE)
|
||||||
Logger.logVerboseExtended(LOG_TAG, executionCommand.toString());
|
Logger.logVerboseExtended(LOG_TAG, executionCommand.toString());
|
||||||
|
|
||||||
AppShell newTermuxTask = AppShell.execute(this, executionCommand, this, new TermuxShellEnvironmentClient(), false);
|
AppShell newTermuxTask = AppShell.execute(this, executionCommand, this,
|
||||||
|
new TermuxShellEnvironment(),false);
|
||||||
if (newTermuxTask == null) {
|
if (newTermuxTask == null) {
|
||||||
Logger.logError(LOG_TAG, "Failed to execute new TermuxTask command for:\n" + executionCommand.getCommandIdAndLabelLogString());
|
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
|
// If the execution command was started for a plugin, then process the error
|
||||||
|
@ -578,7 +579,8 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
|
||||||
// Otherwise if command was manually started by the user like by adding a new terminal session,
|
// Otherwise if command was manually started by the user like by adding a new terminal session,
|
||||||
// then no need to set stdout
|
// then no need to set stdout
|
||||||
executionCommand.terminalTranscriptRows = mProperties.getTerminalTranscriptRows();
|
executionCommand.terminalTranscriptRows = mProperties.getTerminalTranscriptRows();
|
||||||
TermuxSession newTermuxSession = TermuxSession.execute(this, executionCommand, getTermuxTerminalSessionClient(), this, new TermuxShellEnvironmentClient(), executionCommand.isPluginExecutionCommand);
|
TermuxSession newTermuxSession = TermuxSession.execute(this, executionCommand, getTermuxTerminalSessionClient(),
|
||||||
|
this, new TermuxShellEnvironment(), executionCommand.isPluginExecutionCommand);
|
||||||
if (newTermuxSession == null) {
|
if (newTermuxSession == null) {
|
||||||
Logger.logError(LOG_TAG, "Failed to execute new TermuxSession command for:\n" + executionCommand.getCommandIdAndLabelLogString());
|
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
|
// If the execution command was started for a plugin, then process the error
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,5 +1,8 @@
|
||||||
package com.termux.shared.shell;
|
package com.termux.shared.shell;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.termux.shared.file.FileUtils;
|
import com.termux.shared.file.FileUtils;
|
||||||
import com.termux.terminal.TerminalBuffer;
|
import com.termux.terminal.TerminalBuffer;
|
||||||
import com.termux.terminal.TerminalEmulator;
|
import com.termux.terminal.TerminalEmulator;
|
||||||
|
@ -7,8 +10,13 @@ import com.termux.terminal.TerminalSession;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class ShellUtils {
|
public class ShellUtils {
|
||||||
|
|
||||||
|
/** Get process id of {@link Process}. */
|
||||||
public static int getPid(Process p) {
|
public static int getPid(Process p) {
|
||||||
try {
|
try {
|
||||||
Field f = p.getClass().getDeclaredField("pid");
|
Field f = p.getClass().getDeclaredField("pid");
|
||||||
|
@ -23,10 +31,24 @@ public class ShellUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getExecutableBasename(String executable) {
|
/** Setup shell command arguments for the execute. */
|
||||||
|
@NonNull
|
||||||
|
public static String[] setupShellCommandArguments(@NonNull String executable, @Nullable String[] arguments) {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
result.add(executable);
|
||||||
|
if (arguments != null) Collections.addAll(result, arguments);
|
||||||
|
return result.toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get basename for executable. */
|
||||||
|
@Nullable
|
||||||
|
public static String getExecutableBasename(@Nullable String executable) {
|
||||||
return FileUtils.getFileBasename(executable);
|
return FileUtils.getFileBasename(executable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Get transcript for {@link TerminalSession}. */
|
||||||
public static String getTerminalSessionTranscriptText(TerminalSession terminalSession, boolean linesJoined, boolean trim) {
|
public static String getTerminalSessionTranscriptText(TerminalSession terminalSession, boolean linesJoined, boolean trim) {
|
||||||
if (terminalSession == null) return null;
|
if (terminalSession == null) return null;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
package com.termux.shared.shell.command.environment;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.termux.shared.shell.command.ExecutionCommand;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Environment for Android.
|
||||||
|
*
|
||||||
|
* https://cs.android.com/android/platform/superproject/+/android-12.0.0_r32:frameworks/base/core/java/android/os/Environment.java
|
||||||
|
* https://cs.android.com/android/platform/superproject/+/android-12.0.0_r32:system/core/rootdir/init.environ.rc.in
|
||||||
|
* https://cs.android.com/android/platform/superproject/+/android-5.0.0_r1.0.1:system/core/rootdir/init.environ.rc.in
|
||||||
|
* https://cs.android.com/android/_/android/platform/system/core/+/refs/tags/android-12.0.0_r32:rootdir/init.rc;l=910
|
||||||
|
* https://cs.android.com/android/platform/superproject/+/android-12.0.0_r32:packages/modules/SdkExtensions/derive_classpath/derive_classpath.cpp;l=96
|
||||||
|
*/
|
||||||
|
public class AndroidShellEnvironment extends UnixShellEnvironment {
|
||||||
|
|
||||||
|
/** Get shell environment for Android. */
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public HashMap<String, String> getEnvironment(@NonNull Context currentPackageContext, boolean isFailSafe) {
|
||||||
|
HashMap<String, String> environment = new HashMap<>();
|
||||||
|
|
||||||
|
environment.put(ENV_HOME, "/");
|
||||||
|
environment.put(ENV_LANG, "en_US.UTF-8");
|
||||||
|
environment.put(ENV_PATH, System.getenv(ENV_PATH));
|
||||||
|
environment.put(ENV_TMPDIR, "/data/local/tmp");
|
||||||
|
|
||||||
|
environment.put(ENV_COLORTERM, "truecolor");
|
||||||
|
environment.put(ENV_TERM, "xterm-256color");
|
||||||
|
|
||||||
|
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "ANDROID_ASSETS");
|
||||||
|
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "ANDROID_DATA");
|
||||||
|
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "ANDROID_ROOT");
|
||||||
|
ShellEnvironmentUtils.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
|
||||||
|
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "EXTERNAL_STORAGE");
|
||||||
|
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "ASEC_MOUNTPOINT");
|
||||||
|
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "LOOP_MOUNTPOINT");
|
||||||
|
|
||||||
|
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "ANDROID_RUNTIME_ROOT");
|
||||||
|
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "ANDROID_ART_ROOT");
|
||||||
|
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "ANDROID_I18N_ROOT");
|
||||||
|
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "ANDROID_TZDATA_ROOT");
|
||||||
|
|
||||||
|
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "BOOTCLASSPATH");
|
||||||
|
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "DEX2OATBOOTCLASSPATH");
|
||||||
|
ShellEnvironmentUtils.putToEnvIfInSystemEnv(environment, "SYSTEMSERVERCLASSPATH");
|
||||||
|
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getDefaultWorkingDirectoryPath() {
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getDefaultBinPath() {
|
||||||
|
return "/system/bin";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public HashMap<String, String> setupShellCommandEnvironment(@NonNull Context currentPackageContext,
|
||||||
|
@NonNull ExecutionCommand executionCommand) {
|
||||||
|
HashMap<String, String> environment = getEnvironment(currentPackageContext, executionCommand.isFailsafe);
|
||||||
|
|
||||||
|
String workingDirectory = executionCommand.workingDirectory;
|
||||||
|
environment.put(ENV_PWD,
|
||||||
|
workingDirectory != null && !workingDirectory.isEmpty() ? new File(workingDirectory).getAbsolutePath() : // PWD must be absolute path
|
||||||
|
getDefaultWorkingDirectoryPath());
|
||||||
|
ShellEnvironmentUtils.createHomeDir(environment);
|
||||||
|
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.termux.shared.shell.command.environment;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.termux.shared.shell.command.ExecutionCommand;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public interface IShellEnvironment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, like $PREFIX/bin.
|
||||||
|
*
|
||||||
|
* @return Should return the "/bin" path.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
String getDefaultBinPath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup shell command 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[] setupShellCommandArguments(@NonNull String fileToExecute, @Nullable String[] arguments);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup shell command environment to be used for commands.
|
||||||
|
*
|
||||||
|
* @param currentPackageContext The {@link Context} for the current package.
|
||||||
|
* @param executionCommand The {@link ExecutionCommand} for which to set environment.
|
||||||
|
* @return Should return the shell environment.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
HashMap<String, String> setupShellCommandEnvironment(@NonNull Context currentPackageContext,
|
||||||
|
@NonNull ExecutionCommand executionCommand);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
package com.termux.shared.shell.command.environment;
|
||||||
|
|
||||||
|
import static com.termux.shared.shell.command.environment.UnixShellEnvironment.*;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.termux.shared.errors.Error;
|
||||||
|
import com.termux.shared.file.FileUtils;
|
||||||
|
import com.termux.shared.logger.Logger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ShellEnvironmentUtils {
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "ShellEnvironmentUtils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert environment {@link HashMap} to `environ` {@link List <String>}.
|
||||||
|
*
|
||||||
|
* The items in the environ will have the format `name=value`.
|
||||||
|
*
|
||||||
|
* Check {@link #isValidEnvironmentVariableName(String)} and {@link #isValidEnvironmentVariableValue(String)}
|
||||||
|
* for valid variable names and values.
|
||||||
|
*
|
||||||
|
* https://manpages.debian.org/testing/manpages/environ.7.en.html
|
||||||
|
* https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static List<String> convertEnvironmentToEnviron(@NonNull HashMap<String, String> environmentMap) {
|
||||||
|
List<String> environmentList = new ArrayList<>(environmentMap.size());
|
||||||
|
String value;
|
||||||
|
for (String name : environmentMap.keySet()) {
|
||||||
|
value = environmentMap.get(name);
|
||||||
|
if (isValidEnvironmentVariableNameValuePair(name, value, true))
|
||||||
|
environmentList.add(name + "=" + environmentMap.get(name));
|
||||||
|
}
|
||||||
|
return environmentList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if environment variable name and value pair is valid. Errors will be logged if
|
||||||
|
* {@code logErrors} is {@code true}.
|
||||||
|
*
|
||||||
|
* Check {@link #isValidEnvironmentVariableName(String)} and {@link #isValidEnvironmentVariableValue(String)}
|
||||||
|
* for valid variable names and values.
|
||||||
|
*/
|
||||||
|
public static boolean isValidEnvironmentVariableNameValuePair(@Nullable String name, @Nullable String value, boolean logErrors) {
|
||||||
|
if (!isValidEnvironmentVariableName(name)) {
|
||||||
|
if (logErrors)
|
||||||
|
Logger.logErrorPrivate(LOG_TAG, "Invalid environment variable name. name=`" + name + "`, value=`" + value + "`");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValidEnvironmentVariableValue(value)) {
|
||||||
|
if (logErrors)
|
||||||
|
Logger.logErrorPrivate(LOG_TAG, "Invalid environment variable value. name=`" + name + "`, value=`" + value + "`");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if environment variable name is valid. It must not be {@code null} and must not contain
|
||||||
|
* the null byte ('\0') and must only contain alphanumeric and underscore characters and must not
|
||||||
|
* start with a digit.
|
||||||
|
*/
|
||||||
|
public static boolean isValidEnvironmentVariableName(@Nullable String name) {
|
||||||
|
return name != null && !name.contains("\0") && name.matches("[a-zA-Z_][a-zA-Z0-9_]*");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if environment variable value is valid. It must not be {@code null} and must not contain
|
||||||
|
* the null byte ('\0').
|
||||||
|
*/
|
||||||
|
public static boolean isValidEnvironmentVariableValue(@Nullable String value) {
|
||||||
|
return value != null && !value.contains("\0");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Put value in environment if variable exists in {@link System) environment. */
|
||||||
|
public static void putToEnvIfInSystemEnv(@NonNull HashMap<String, String> environment,
|
||||||
|
@NonNull String name) {
|
||||||
|
String value = System.getenv(name);
|
||||||
|
if (value != null) {
|
||||||
|
environment.put(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Put {@link String} value in environment if value set. */
|
||||||
|
public static void putToEnvIfSet(@NonNull HashMap<String, String> environment, @NonNull String name,
|
||||||
|
@Nullable String value) {
|
||||||
|
if (value != null) {
|
||||||
|
environment.put(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Put {@link Boolean} value "true" or "false" in environment if value set. */
|
||||||
|
public static void putToEnvIfSet(@NonNull HashMap<String, String> environment, @NonNull String name,
|
||||||
|
@Nullable Boolean value) {
|
||||||
|
if (value != null) {
|
||||||
|
environment.put(name, String.valueOf(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Create HOME directory in environment {@link Map} if set. */
|
||||||
|
public static void createHomeDir(@NonNull HashMap<String, String> environment) {
|
||||||
|
String homeDirectory = environment.get(ENV_HOME);
|
||||||
|
if (homeDirectory != null && !homeDirectory.isEmpty()) {
|
||||||
|
Error error = FileUtils.createDirectoryFile("shell home", homeDirectory);
|
||||||
|
if (error != null) {
|
||||||
|
Logger.logErrorExtended(LOG_TAG, "Failed to create shell home directory\n" + error.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.termux.shared.shell.command.environment;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.termux.shared.shell.ShellUtils;
|
||||||
|
import com.termux.shared.shell.command.ExecutionCommand;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Environment for Unix-like systems.
|
||||||
|
*
|
||||||
|
* https://manpages.debian.org/testing/manpages/environ.7.en.html
|
||||||
|
* https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html
|
||||||
|
*/
|
||||||
|
public abstract class UnixShellEnvironment implements IShellEnvironment {
|
||||||
|
|
||||||
|
/** Environment variable for the terminal's colour capabilities. */
|
||||||
|
public static final String ENV_COLORTERM = "COLORTERM";
|
||||||
|
|
||||||
|
/** Environment variable for the path of the user's home directory. */
|
||||||
|
public static final String ENV_HOME = "HOME";
|
||||||
|
|
||||||
|
/** Environment variable for the locale category for native language, local customs, and coded
|
||||||
|
* character set in the absence of the LC_ALL and other LC_* environment variables. */
|
||||||
|
public static final String ENV_LANG = "LANG";
|
||||||
|
|
||||||
|
/** Environment variable for the represent the sequence of directory paths separated with
|
||||||
|
* colons ":" that should be searched in for dynamic shared libraries to link programs against. */
|
||||||
|
public static final String ENV_LD_LIBRARY_PATH = "LD_LIBRARY_PATH";
|
||||||
|
|
||||||
|
/** Environment variable for the represent the sequence of directory path prefixes separated with
|
||||||
|
* colons ":" that certain functions and utilities apply in searching for an executable file
|
||||||
|
* known only by a filename. */
|
||||||
|
public static final String ENV_PATH = "PATH";
|
||||||
|
|
||||||
|
/** Environment variable for the absolute path of the current working directory. It shall not
|
||||||
|
* contain any components that are dot or dot-dot. The value is set by the cd utility, and by
|
||||||
|
* the sh utility during initialization. */
|
||||||
|
public static final String ENV_PWD = "PWD";
|
||||||
|
|
||||||
|
/** Environment variable for the terminal type for which output is to be prepared. This information
|
||||||
|
* is used by utilities and application programs wishing to exploit special capabilities specific
|
||||||
|
* to a terminal. The format and allowable values of this environment variable are unspecified. */
|
||||||
|
public static final String ENV_TERM = "TERM";
|
||||||
|
|
||||||
|
/** Environment variable for the path of a directory made available for programs that need a place
|
||||||
|
* to create temporary files. */
|
||||||
|
public static final String ENV_TMPDIR = "TMPDIR";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public abstract HashMap<String, String> getEnvironment(@NonNull Context currentPackageContext,
|
||||||
|
boolean isFailSafe);
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public abstract String getDefaultWorkingDirectoryPath();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public abstract String getDefaultBinPath();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String[] setupShellCommandArguments(@NonNull String executable, @Nullable String[] arguments) {
|
||||||
|
return ShellUtils.setupShellCommandArguments(executable, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public abstract HashMap<String, String> setupShellCommandEnvironment(@NonNull Context currentPackageContext,
|
||||||
|
@NonNull ExecutionCommand executionCommand);
|
||||||
|
|
||||||
|
}
|
|
@ -7,14 +7,16 @@ import android.system.OsConstants;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
import com.termux.shared.R;
|
import com.termux.shared.R;
|
||||||
import com.termux.shared.data.DataUtils;
|
import com.termux.shared.data.DataUtils;
|
||||||
import com.termux.shared.shell.command.ExecutionCommand;
|
import com.termux.shared.shell.command.ExecutionCommand;
|
||||||
|
import com.termux.shared.shell.command.environment.ShellEnvironmentUtils;
|
||||||
import com.termux.shared.shell.command.result.ResultData;
|
import com.termux.shared.shell.command.result.ResultData;
|
||||||
import com.termux.shared.errors.Errno;
|
import com.termux.shared.errors.Errno;
|
||||||
import com.termux.shared.logger.Logger;
|
import com.termux.shared.logger.Logger;
|
||||||
import com.termux.shared.shell.command.ExecutionCommand.ExecutionState;
|
import com.termux.shared.shell.command.ExecutionCommand.ExecutionState;
|
||||||
import com.termux.shared.shell.ShellEnvironmentClient;
|
import com.termux.shared.shell.command.environment.IShellEnvironment;
|
||||||
import com.termux.shared.shell.ShellUtils;
|
import com.termux.shared.shell.ShellUtils;
|
||||||
import com.termux.shared.shell.StreamGobbler;
|
import com.termux.shared.shell.StreamGobbler;
|
||||||
|
|
||||||
|
@ -22,6 +24,9 @@ import java.io.DataOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that maintains info for background app shells run with {@link Runtime#exec(String[], String[], File)}.
|
* A class that maintains info for background app shells run with {@link Runtime#exec(String[], String[], File)}.
|
||||||
|
@ -50,14 +55,16 @@ public final class AppShell {
|
||||||
* The {@link ExecutionCommand#commandLabel}, {@link ExecutionCommand#arguments} and
|
* The {@link ExecutionCommand#commandLabel}, {@link ExecutionCommand#arguments} and
|
||||||
* {@link ExecutionCommand#workingDirectory} may optionally be set.
|
* {@link ExecutionCommand#workingDirectory} may optionally be set.
|
||||||
*
|
*
|
||||||
* @param context The {@link Context} for operations.
|
* @param currentPackageContext The {@link Context} for operations. This must be the context for
|
||||||
|
* the current package and not the context of a `sharedUserId` package,
|
||||||
|
* since environment setup may be dependent on current package.
|
||||||
* @param executionCommand The {@link ExecutionCommand} containing the information for execution command.
|
* @param executionCommand The {@link ExecutionCommand} containing the information for execution command.
|
||||||
* @param appShellClient The {@link AppShellClient} interface implementation.
|
* @param appShellClient The {@link AppShellClient} interface implementation.
|
||||||
* The {@link AppShellClient#onAppShellExited(AppShell)} will
|
* The {@link AppShellClient#onAppShellExited(AppShell)} will
|
||||||
* be called regardless of {@code isSynchronous} value but not if
|
* be called regardless of {@code isSynchronous} value but not if
|
||||||
* {@code null} is returned by this method. This can
|
* {@code null} is returned by this method. This can
|
||||||
* optionally be {@code null}.
|
* optionally be {@code null}.
|
||||||
* @param shellEnvironmentClient The {@link ShellEnvironmentClient} interface implementation.
|
* @param shellEnvironmentClient The {@link IShellEnvironment} interface implementation.
|
||||||
* @param isSynchronous If set to {@code true}, then the command will be executed in the
|
* @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}
|
* caller thread and results returned synchronously in the {@link ExecutionCommand}
|
||||||
* sub object of the {@link AppShell} returned.
|
* sub object of the {@link AppShell} returned.
|
||||||
|
@ -65,9 +72,9 @@ public final class AppShell {
|
||||||
* asynchronously in the background and control is returned to the caller thread.
|
* asynchronously in the background and control is returned to the caller thread.
|
||||||
* @return Returns the {@link AppShell}. This will be {@code null} if failed to start the execution command.
|
* @return Returns the {@link AppShell}. This will be {@code null} if failed to start the execution command.
|
||||||
*/
|
*/
|
||||||
public static AppShell execute(@NonNull final Context context, @NonNull ExecutionCommand executionCommand,
|
public static AppShell execute(@NonNull final Context currentPackageContext, @NonNull ExecutionCommand executionCommand,
|
||||||
final AppShellClient appShellClient,
|
final AppShellClient appShellClient,
|
||||||
@NonNull final ShellEnvironmentClient shellEnvironmentClient,
|
@NonNull final IShellEnvironment shellEnvironmentClient,
|
||||||
final boolean isSynchronous) {
|
final boolean isSynchronous) {
|
||||||
if (executionCommand.executable == null || executionCommand.executable.isEmpty()) {
|
if (executionCommand.executable == null || executionCommand.executable.isEmpty()) {
|
||||||
executionCommand.setStateFailed(Errno.ERRNO_FAILED.getCode(),
|
executionCommand.setStateFailed(Errno.ERRNO_FAILED.getCode(),
|
||||||
|
@ -81,19 +88,27 @@ public final class AppShell {
|
||||||
if (executionCommand.workingDirectory.isEmpty())
|
if (executionCommand.workingDirectory.isEmpty())
|
||||||
executionCommand.workingDirectory = "/";
|
executionCommand.workingDirectory = "/";
|
||||||
|
|
||||||
String[] env = shellEnvironmentClient.buildEnvironment(context, executionCommand.isFailsafe, executionCommand.workingDirectory);
|
|
||||||
// Transform executable path to shell/session name, e.g. "/bin/do-something.sh" => "do-something.sh".
|
// Transform executable path to shell/session name, e.g. "/bin/do-something.sh" => "do-something.sh".
|
||||||
String executableBasename = ShellUtils.getExecutableBasename(executionCommand.executable);
|
String executableBasename = ShellUtils.getExecutableBasename(executionCommand.executable);
|
||||||
|
|
||||||
if (executionCommand.shellName == null)
|
if (executionCommand.shellName == null)
|
||||||
executionCommand.shellName = executableBasename;
|
executionCommand.shellName = executableBasename;
|
||||||
|
|
||||||
final String[] commandArray = shellEnvironmentClient.setupProcessArgs(executionCommand.executable, executionCommand.arguments);
|
|
||||||
if (executionCommand.commandLabel == null)
|
if (executionCommand.commandLabel == null)
|
||||||
executionCommand.commandLabel = executableBasename;
|
executionCommand.commandLabel = executableBasename;
|
||||||
|
|
||||||
|
// Setup command args
|
||||||
|
final String[] commandArray = shellEnvironmentClient.setupShellCommandArguments(executionCommand.executable, executionCommand.arguments);
|
||||||
|
|
||||||
|
// Setup command environment
|
||||||
|
HashMap<String, String> environment = shellEnvironmentClient.setupShellCommandEnvironment(currentPackageContext,
|
||||||
|
executionCommand);
|
||||||
|
List<String> environmentList = ShellEnvironmentUtils.convertEnvironmentToEnviron(environment);
|
||||||
|
Collections.sort(environmentList);
|
||||||
|
String[] environmentArray = environmentList.toArray(new String[0]);
|
||||||
|
|
||||||
if (!executionCommand.setState(ExecutionState.EXECUTING)) {
|
if (!executionCommand.setState(ExecutionState.EXECUTING)) {
|
||||||
executionCommand.setStateFailed(Errno.ERRNO_FAILED.getCode(), context.getString(R.string.error_failed_to_execute_app_shell_command, executionCommand.getCommandIdAndLabelLogString()));
|
executionCommand.setStateFailed(Errno.ERRNO_FAILED.getCode(), currentPackageContext.getString(R.string.error_failed_to_execute_app_shell_command, executionCommand.getCommandIdAndLabelLogString()));
|
||||||
AppShell.processAppShellResult(null, executionCommand);
|
AppShell.processAppShellResult(null, executionCommand);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -101,22 +116,23 @@ public final class AppShell {
|
||||||
// No need to log stdin if logging is disabled, like for app internal scripts
|
// No need to log stdin if logging is disabled, like for app internal scripts
|
||||||
Logger.logDebugExtended(LOG_TAG, ExecutionCommand.getExecutionInputLogString(executionCommand,
|
Logger.logDebugExtended(LOG_TAG, ExecutionCommand.getExecutionInputLogString(executionCommand,
|
||||||
true, Logger.shouldEnableLoggingForCustomLogLevel(executionCommand.backgroundCustomLogLevel)));
|
true, Logger.shouldEnableLoggingForCustomLogLevel(executionCommand.backgroundCustomLogLevel)));
|
||||||
|
Logger.logVerboseExtended(LOG_TAG, "\"" + executionCommand.getCommandIdAndLabelLogString() + "\" AppShell Environment:\n" +
|
||||||
|
Joiner.on("\n").join(environmentArray));
|
||||||
|
|
||||||
// Exec the process
|
// Exec the process
|
||||||
final Process process;
|
final Process process;
|
||||||
try {
|
try {
|
||||||
process = Runtime.getRuntime().exec(commandArray, env, new File(executionCommand.workingDirectory));
|
process = Runtime.getRuntime().exec(commandArray, environmentArray, new File(executionCommand.workingDirectory));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
executionCommand.setStateFailed(Errno.ERRNO_FAILED.getCode(), context.getString(R.string.error_failed_to_execute_app_shell_command, executionCommand.getCommandIdAndLabelLogString()), e);
|
executionCommand.setStateFailed(Errno.ERRNO_FAILED.getCode(), currentPackageContext.getString(R.string.error_failed_to_execute_app_shell_command, executionCommand.getCommandIdAndLabelLogString()), e);
|
||||||
AppShell.processAppShellResult(null, executionCommand);
|
AppShell.processAppShellResult(null, executionCommand);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final AppShell appShell = new AppShell(process, executionCommand, appShellClient);
|
final AppShell appShell = new AppShell(process, executionCommand, appShellClient);
|
||||||
|
|
||||||
if (isSynchronous) {
|
if (isSynchronous) {
|
||||||
try {
|
try {
|
||||||
appShell.executeInner(context);
|
appShell.executeInner(currentPackageContext);
|
||||||
} catch (IllegalThreadStateException | InterruptedException e) {
|
} catch (IllegalThreadStateException | InterruptedException e) {
|
||||||
// TODO: Should either of these be handled or returned?
|
// TODO: Should either of these be handled or returned?
|
||||||
}
|
}
|
||||||
|
@ -125,7 +141,7 @@ public final class AppShell {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
appShell.executeInner(context);
|
appShell.executeInner(currentPackageContext);
|
||||||
} catch (IllegalThreadStateException | InterruptedException e) {
|
} catch (IllegalThreadStateException | InterruptedException e) {
|
||||||
// TODO: Should either of these be handled or returned?
|
// TODO: Should either of these be handled or returned?
|
||||||
}
|
}
|
||||||
|
@ -274,10 +290,10 @@ public final class AppShell {
|
||||||
* then the {@link AppShellClient#onAppShellExited(AppShell)} callback will be called.
|
* then the {@link AppShellClient#onAppShellExited(AppShell)} callback will be called.
|
||||||
*
|
*
|
||||||
* @param appShell The {@link AppShell}, which should be set if
|
* @param appShell The {@link AppShell}, which should be set if
|
||||||
* {@link #execute(Context, ExecutionCommand, AppShellClient, ShellEnvironmentClient, boolean)}
|
* {@link #execute(Context, ExecutionCommand, AppShellClient, IShellEnvironment, HashMap, boolean)}
|
||||||
* successfully started the process.
|
* successfully started the process.
|
||||||
* @param executionCommand The {@link ExecutionCommand}, which should be set if
|
* @param executionCommand The {@link ExecutionCommand}, which should be set if
|
||||||
* {@link #execute(Context, ExecutionCommand, AppShellClient, ShellEnvironmentClient, boolean)}
|
* {@link #execute(Context, ExecutionCommand, AppShellClient, IShellEnvironment, HashMap, boolean)}
|
||||||
* failed to start the process.
|
* failed to start the process.
|
||||||
*/
|
*/
|
||||||
private static void processAppShellResult(final AppShell appShell, ExecutionCommand executionCommand) {
|
private static void processAppShellResult(final AppShell appShell, ExecutionCommand executionCommand) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ import com.termux.shared.shell.command.ExecutionCommand;
|
||||||
import com.termux.shared.errors.Error;
|
import com.termux.shared.errors.Error;
|
||||||
import com.termux.shared.android.PackageUtils;
|
import com.termux.shared.android.PackageUtils;
|
||||||
import com.termux.shared.termux.TermuxConstants.TERMUX_APP;
|
import com.termux.shared.termux.TermuxConstants.TERMUX_APP;
|
||||||
import com.termux.shared.termux.shell.TermuxShellEnvironmentClient;
|
import com.termux.shared.termux.shell.command.environment.TermuxShellEnvironment;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
@ -597,7 +597,7 @@ public class TermuxUtils {
|
||||||
null, ExecutionCommand.Runner.APP_SHELL.getName(), false);
|
null, ExecutionCommand.Runner.APP_SHELL.getName(), false);
|
||||||
executionCommand.commandLabel = "APT Info Command";
|
executionCommand.commandLabel = "APT Info Command";
|
||||||
executionCommand.backgroundCustomLogLevel = Logger.LOG_LEVEL_OFF;
|
executionCommand.backgroundCustomLogLevel = Logger.LOG_LEVEL_OFF;
|
||||||
AppShell appShell = AppShell.execute(context, executionCommand, null, new TermuxShellEnvironmentClient(), true);
|
AppShell appShell = AppShell.execute(context, executionCommand, null, new TermuxShellEnvironment(), true);
|
||||||
if (appShell == null || !executionCommand.isSuccessful() || executionCommand.resultData.exitCode != 0) {
|
if (appShell == null || !executionCommand.isSuccessful() || executionCommand.resultData.exitCode != 0) {
|
||||||
Logger.logErrorExtended(LOG_TAG, executionCommand.toString());
|
Logger.logErrorExtended(LOG_TAG, executionCommand.toString());
|
||||||
return null;
|
return null;
|
||||||
|
@ -656,7 +656,7 @@ public class TermuxUtils {
|
||||||
null, logcatScript + "\n", "/", ExecutionCommand.Runner.APP_SHELL.getName(), true);
|
null, logcatScript + "\n", "/", ExecutionCommand.Runner.APP_SHELL.getName(), true);
|
||||||
executionCommand.commandLabel = "Logcat dump command";
|
executionCommand.commandLabel = "Logcat dump command";
|
||||||
executionCommand.backgroundCustomLogLevel = Logger.LOG_LEVEL_OFF;
|
executionCommand.backgroundCustomLogLevel = Logger.LOG_LEVEL_OFF;
|
||||||
AppShell appShell = AppShell.execute(context, executionCommand, null, new TermuxShellEnvironmentClient(), true);
|
AppShell appShell = AppShell.execute(context, executionCommand, null, new TermuxShellEnvironment(), true);
|
||||||
if (appShell == null || !executionCommand.isSuccessful()) {
|
if (appShell == null || !executionCommand.isSuccessful()) {
|
||||||
Logger.logErrorExtended(LOG_TAG, executionCommand.toString());
|
Logger.logErrorExtended(LOG_TAG, executionCommand.toString());
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -11,7 +11,7 @@ import com.termux.shared.markdown.MarkdownUtils;
|
||||||
import com.termux.shared.shell.command.ExecutionCommand;
|
import com.termux.shared.shell.command.ExecutionCommand;
|
||||||
import com.termux.shared.errors.Error;
|
import com.termux.shared.errors.Error;
|
||||||
import com.termux.shared.file.FileUtilsErrno;
|
import com.termux.shared.file.FileUtilsErrno;
|
||||||
import com.termux.shared.termux.shell.TermuxShellEnvironmentClient;
|
import com.termux.shared.termux.shell.command.environment.TermuxShellEnvironment;
|
||||||
import com.termux.shared.shell.command.runner.app.AppShell;
|
import com.termux.shared.shell.command.runner.app.AppShell;
|
||||||
import com.termux.shared.android.AndroidUtils;
|
import com.termux.shared.android.AndroidUtils;
|
||||||
import com.termux.shared.termux.TermuxConstants;
|
import com.termux.shared.termux.TermuxConstants;
|
||||||
|
@ -364,7 +364,7 @@ public class TermuxFileUtils {
|
||||||
statScript.toString() + "\n", "/", ExecutionCommand.Runner.APP_SHELL.getName(), true);
|
statScript.toString() + "\n", "/", ExecutionCommand.Runner.APP_SHELL.getName(), true);
|
||||||
executionCommand.commandLabel = TermuxConstants.TERMUX_APP_NAME + " Files Stat Command";
|
executionCommand.commandLabel = TermuxConstants.TERMUX_APP_NAME + " Files Stat Command";
|
||||||
executionCommand.backgroundCustomLogLevel = Logger.LOG_LEVEL_OFF;
|
executionCommand.backgroundCustomLogLevel = Logger.LOG_LEVEL_OFF;
|
||||||
AppShell appShell = AppShell.execute(context, executionCommand, null, new TermuxShellEnvironmentClient(), true);
|
AppShell appShell = AppShell.execute(context, executionCommand, null, new TermuxShellEnvironment(), true);
|
||||||
if (appShell == null || !executionCommand.isSuccessful()) {
|
if (appShell == null || !executionCommand.isSuccessful()) {
|
||||||
Logger.logErrorExtended(LOG_TAG, executionCommand.toString());
|
Logger.logErrorExtended(LOG_TAG, executionCommand.toString());
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
package com.termux.shared.termux.shell;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.termux.shared.shell.ShellEnvironmentClient;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -3,10 +3,10 @@ package com.termux.shared.termux.shell;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.termux.shared.errors.Error;
|
import com.termux.shared.errors.Error;
|
||||||
import com.termux.shared.file.filesystem.FileTypes;
|
import com.termux.shared.file.filesystem.FileTypes;
|
||||||
import com.termux.shared.termux.TermuxBootstrap;
|
|
||||||
import com.termux.shared.termux.TermuxConstants;
|
import com.termux.shared.termux.TermuxConstants;
|
||||||
import com.termux.shared.file.FileUtils;
|
import com.termux.shared.file.FileUtils;
|
||||||
import com.termux.shared.logger.Logger;
|
import com.termux.shared.logger.Logger;
|
||||||
|
@ -25,31 +25,11 @@ import java.util.List;
|
||||||
|
|
||||||
public class TermuxShellUtils {
|
public class TermuxShellUtils {
|
||||||
|
|
||||||
public static String TERMUX_VERSION_NAME;
|
|
||||||
public static String TERMUX_IS_DEBUGGABLE_BUILD;
|
|
||||||
public static String TERMUX_APP_PID;
|
|
||||||
public static String TERMUX_APK_RELEASE;
|
|
||||||
public static Boolean TERMUX_APP_AM_SOCKET_SERVER_ENABLED;
|
|
||||||
|
|
||||||
public static String TERMUX_API_VERSION_NAME;
|
|
||||||
|
|
||||||
|
|
||||||
private static final String LOG_TAG = "TermuxShellUtils";
|
private static final String LOG_TAG = "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) {
|
public static String[] buildEnvironment(Context currentPackageContext, boolean isFailSafe, String workingDirectory) {
|
||||||
TermuxConstants.TERMUX_HOME_DIR.mkdirs();
|
TermuxConstants.TERMUX_HOME_DIR.mkdirs();
|
||||||
|
|
||||||
if (workingDirectory == null || workingDirectory.isEmpty())
|
|
||||||
workingDirectory = getDefaultWorkingDirectoryPath();
|
|
||||||
|
|
||||||
List<String> environment = new ArrayList<>();
|
List<String> environment = new ArrayList<>();
|
||||||
|
|
||||||
loadTermuxEnvVariables(currentPackageContext);
|
loadTermuxEnvVariables(currentPackageContext);
|
||||||
|
@ -71,51 +51,14 @@ public class TermuxShellUtils {
|
||||||
|
|
||||||
if (TERMUX_API_VERSION_NAME != null)
|
if (TERMUX_API_VERSION_NAME != null)
|
||||||
environment.add("TERMUX_API_VERSION=" + TERMUX_API_VERSION_NAME);
|
environment.add("TERMUX_API_VERSION=" + TERMUX_API_VERSION_NAME);
|
||||||
|
|
||||||
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("PWD=" + workingDirectory);
|
|
||||||
environment.add("TMPDIR=" + TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH);
|
|
||||||
if (TermuxBootstrap.isAppPackageVariantAPTAndroid5()) {
|
|
||||||
environment.add("PATH=" + TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + ":" + TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/applets");
|
|
||||||
environment.add("LD_LIBRARY_PATH=" + TermuxConstants.TERMUX_LIB_PREFIX_DIR_PATH);
|
|
||||||
} else {
|
|
||||||
environment.add("PATH=" + TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return environment.toArray(new String[0]);
|
return environment.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
public static void addToEnvIfPresent(List<String> environment, String name) {
|
* Setup shell command arguments for the execute. The file interpreter may be prefixed to
|
||||||
String value = System.getenv(name);
|
* command arguments if needed.
|
||||||
if (value != null) {
|
*/
|
||||||
environment.add(name + "=" + value);
|
@NonNull
|
||||||
}
|
public static String[] setupShellCommandArguments(@NonNull String executable, @Nullable String[] arguments) {
|
||||||
}
|
|
||||||
|
|
||||||
public static String[] setupProcessArgs(@NonNull String fileToExecute, String[] arguments) {
|
|
||||||
// The file to execute may either be:
|
// The file to execute may either be:
|
||||||
// - An elf file, in which we execute it directly.
|
// - 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
|
// - A script file without shebang, which we execute with our standard shell $PREFIX/bin/sh instead of the
|
||||||
|
@ -123,7 +66,7 @@ public class TermuxShellUtils {
|
||||||
// - A file with shebang, which we try to handle with e.g. /bin/foo -> $PREFIX/bin/foo.
|
// - A file with shebang, which we try to handle with e.g. /bin/foo -> $PREFIX/bin/foo.
|
||||||
String interpreter = null;
|
String interpreter = null;
|
||||||
try {
|
try {
|
||||||
File file = new File(fileToExecute);
|
File file = new File(executable);
|
||||||
try (FileInputStream in = new FileInputStream(file)) {
|
try (FileInputStream in = new FileInputStream(file)) {
|
||||||
byte[] buffer = new byte[256];
|
byte[] buffer = new byte[256];
|
||||||
int bytesRead = in.read(buffer);
|
int bytesRead = in.read(buffer);
|
||||||
|
@ -140,9 +83,9 @@ public class TermuxShellUtils {
|
||||||
// Skip whitespace after shebang.
|
// Skip whitespace after shebang.
|
||||||
} else {
|
} else {
|
||||||
// End of shebang.
|
// End of shebang.
|
||||||
String executable = builder.toString();
|
String shebangExecutable = builder.toString();
|
||||||
if (executable.startsWith("/usr") || executable.startsWith("/bin")) {
|
if (shebangExecutable.startsWith("/usr") || shebangExecutable.startsWith("/bin")) {
|
||||||
String[] parts = executable.split("/");
|
String[] parts = shebangExecutable.split("/");
|
||||||
String binary = parts[parts.length - 1];
|
String binary = parts[parts.length - 1];
|
||||||
interpreter = TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/" + binary;
|
interpreter = TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/" + binary;
|
||||||
}
|
}
|
||||||
|
@ -164,11 +107,12 @@ public class TermuxShellUtils {
|
||||||
|
|
||||||
List<String> result = new ArrayList<>();
|
List<String> result = new ArrayList<>();
|
||||||
if (interpreter != null) result.add(interpreter);
|
if (interpreter != null) result.add(interpreter);
|
||||||
result.add(fileToExecute);
|
result.add(executable);
|
||||||
if (arguments != null) Collections.addAll(result, arguments);
|
if (arguments != null) Collections.addAll(result, arguments);
|
||||||
return result.toArray(new String[0]);
|
return result.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Clear files under {@link TermuxConstants#TERMUX_TMP_PREFIX_DIR_PATH}. */
|
||||||
public static void clearTermuxTMPDIR(boolean onlyIfExists) {
|
public static void clearTermuxTMPDIR(boolean onlyIfExists) {
|
||||||
// Existence check before clearing may be required since clearDirectory() will automatically
|
// Existence check before clearing may be required since clearDirectory() will automatically
|
||||||
// re-create empty directory if doesn't exist, which should not be done for things like
|
// re-create empty directory if doesn't exist, which should not be done for things like
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.termux.shared.termux.shell.command.environment;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.termux.shared.shell.command.ExecutionCommand;
|
||||||
|
import com.termux.shared.shell.command.environment.AndroidShellEnvironment;
|
||||||
|
import com.termux.shared.termux.TermuxBootstrap;
|
||||||
|
import com.termux.shared.termux.TermuxConstants;
|
||||||
|
import com.termux.shared.termux.shell.TermuxShellUtils;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Environment for Termux.
|
||||||
|
*/
|
||||||
|
public class TermuxShellEnvironment extends AndroidShellEnvironment {
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "TermuxShellEnvironment";
|
||||||
|
|
||||||
|
/** Environment variable for the termux {@link TermuxConstants#TERMUX_PREFIX_DIR_PATH}. */
|
||||||
|
public static final String ENV_PREFIX = "PREFIX";
|
||||||
|
|
||||||
|
|
||||||
|
/** Get shell environment for Termux. */
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public HashMap<String, String> getEnvironment(@NonNull Context currentPackageContext, boolean isFailSafe) {
|
||||||
|
|
||||||
|
// Termux environment builds upon the Android environment
|
||||||
|
HashMap<String, String> environment = super.getEnvironment(currentPackageContext, isFailSafe);
|
||||||
|
|
||||||
|
environment.put(ENV_HOME, TermuxConstants.TERMUX_HOME_DIR_PATH);
|
||||||
|
environment.put(ENV_PREFIX, TermuxConstants.TERMUX_PREFIX_DIR_PATH);
|
||||||
|
|
||||||
|
// If failsafe is not enabled, then we keep default PATH and TMPDIR so that system binaries can be used
|
||||||
|
if (!isFailSafe) {
|
||||||
|
environment.put(ENV_TMPDIR, TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH);
|
||||||
|
if (TermuxBootstrap.isAppPackageVariantAPTAndroid5()) {
|
||||||
|
// Termux in android 5/6 era shipped busybox binaries in applets directory
|
||||||
|
environment.put(ENV_PATH, TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + ":" + TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + "/applets");
|
||||||
|
environment.put(ENV_LD_LIBRARY_PATH, TermuxConstants.TERMUX_LIB_PREFIX_DIR_PATH);
|
||||||
|
} else {
|
||||||
|
// Termux binaries on Android 7+ rely on DT_RUNPATH, so LD_LIBRARY_PATH should be unset by default
|
||||||
|
environment.put(ENV_PATH, TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH);
|
||||||
|
environment.remove(ENV_LD_LIBRARY_PATH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getDefaultWorkingDirectoryPath() {
|
||||||
|
return TermuxConstants.TERMUX_HOME_DIR_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getDefaultBinPath() {
|
||||||
|
return TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String[] setupShellCommandArguments(@NonNull String executable, String[] arguments) {
|
||||||
|
return TermuxShellUtils.setupShellCommandArguments(executable, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,17 +5,23 @@ import android.system.OsConstants;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
import com.termux.shared.R;
|
import com.termux.shared.R;
|
||||||
import com.termux.shared.shell.command.ExecutionCommand;
|
import com.termux.shared.shell.command.ExecutionCommand;
|
||||||
|
import com.termux.shared.shell.command.environment.ShellEnvironmentUtils;
|
||||||
|
import com.termux.shared.shell.command.environment.UnixShellEnvironment;
|
||||||
import com.termux.shared.shell.command.result.ResultData;
|
import com.termux.shared.shell.command.result.ResultData;
|
||||||
import com.termux.shared.errors.Errno;
|
import com.termux.shared.errors.Errno;
|
||||||
import com.termux.shared.logger.Logger;
|
import com.termux.shared.logger.Logger;
|
||||||
import com.termux.shared.shell.ShellEnvironmentClient;
|
import com.termux.shared.shell.command.environment.IShellEnvironment;
|
||||||
import com.termux.shared.shell.ShellUtils;
|
import com.termux.shared.shell.ShellUtils;
|
||||||
import com.termux.terminal.TerminalSession;
|
import com.termux.terminal.TerminalSession;
|
||||||
import com.termux.terminal.TerminalSessionClient;
|
import com.termux.terminal.TerminalSessionClient;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that maintains info for foreground Termux sessions.
|
* A class that maintains info for foreground Termux sessions.
|
||||||
|
@ -49,11 +55,13 @@ public class TermuxSession {
|
||||||
* If {@link ExecutionCommand#executable} is {@code null}, then a default shell is automatically
|
* If {@link ExecutionCommand#executable} is {@code null}, then a default shell is automatically
|
||||||
* chosen.
|
* chosen.
|
||||||
*
|
*
|
||||||
* @param context The {@link Context} for operations.
|
* @param currentPackageContext The {@link Context} for operations. This must be the context for
|
||||||
|
* the current package and not the context of a `sharedUserId` package,
|
||||||
|
* since environment setup may be dependent on current package.
|
||||||
* @param executionCommand The {@link ExecutionCommand} containing the information for execution command.
|
* @param executionCommand The {@link ExecutionCommand} containing the information for execution command.
|
||||||
* @param terminalSessionClient The {@link TerminalSessionClient} interface implementation.
|
* @param terminalSessionClient The {@link TerminalSessionClient} interface implementation.
|
||||||
* @param termuxSessionClient The {@link TermuxSessionClient} interface implementation.
|
* @param termuxSessionClient The {@link TermuxSessionClient} interface implementation.
|
||||||
* @param shellEnvironmentClient The {@link ShellEnvironmentClient} interface implementation.
|
* @param shellEnvironmentClient The {@link IShellEnvironment} interface implementation.
|
||||||
* @param setStdoutOnExit If set to {@code true}, then the {@link ResultData#stdout}
|
* @param setStdoutOnExit If set to {@code true}, then the {@link ResultData#stdout}
|
||||||
* available in the {@link TermuxSessionClient#onTermuxSessionExited(TermuxSession)}
|
* available in the {@link TermuxSessionClient#onTermuxSessionExited(TermuxSession)}
|
||||||
* callback will be set to the {@link TerminalSession} transcript. The session
|
* callback will be set to the {@link TerminalSession} transcript. The session
|
||||||
|
@ -63,9 +71,9 @@ public class TermuxSession {
|
||||||
* since this requires extra processing to get it.
|
* since this requires extra processing to get it.
|
||||||
* @return Returns the {@link TermuxSession}. This will be {@code null} if failed to start the execution command.
|
* @return Returns the {@link TermuxSession}. This will be {@code null} if failed to start the execution command.
|
||||||
*/
|
*/
|
||||||
public static TermuxSession execute(@NonNull final Context context, @NonNull ExecutionCommand executionCommand,
|
public static TermuxSession execute(@NonNull final Context currentPackageContext, @NonNull ExecutionCommand executionCommand,
|
||||||
@NonNull final TerminalSessionClient terminalSessionClient, final TermuxSessionClient termuxSessionClient,
|
@NonNull final TerminalSessionClient terminalSessionClient, final TermuxSessionClient termuxSessionClient,
|
||||||
@NonNull final ShellEnvironmentClient shellEnvironmentClient,
|
@NonNull final IShellEnvironment shellEnvironmentClient,
|
||||||
final boolean setStdoutOnExit) {
|
final boolean setStdoutOnExit) {
|
||||||
if (executionCommand.executable != null && executionCommand.executable.isEmpty())
|
if (executionCommand.executable != null && executionCommand.executable.isEmpty())
|
||||||
executionCommand.executable = null;
|
executionCommand.executable = null;
|
||||||
|
@ -74,8 +82,6 @@ public class TermuxSession {
|
||||||
if (executionCommand.workingDirectory.isEmpty())
|
if (executionCommand.workingDirectory.isEmpty())
|
||||||
executionCommand.workingDirectory = "/";
|
executionCommand.workingDirectory = "/";
|
||||||
|
|
||||||
String[] environment = shellEnvironmentClient.buildEnvironment(context, executionCommand.isFailsafe, executionCommand.workingDirectory);
|
|
||||||
|
|
||||||
String defaultBinPath = shellEnvironmentClient.getDefaultBinPath();
|
String defaultBinPath = shellEnvironmentClient.getDefaultBinPath();
|
||||||
if (defaultBinPath.isEmpty())
|
if (defaultBinPath.isEmpty())
|
||||||
defaultBinPath = "/system/bin";
|
defaultBinPath = "/system/bin";
|
||||||
|
@ -108,30 +114,42 @@ public class TermuxSession {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] processArgs = shellEnvironmentClient.setupProcessArgs(executionCommand.executable, executionCommand.arguments);
|
// Setup command args
|
||||||
|
String[] commandArgs = shellEnvironmentClient.setupShellCommandArguments(executionCommand.executable, executionCommand.arguments);
|
||||||
|
|
||||||
executionCommand.executable = processArgs[0];
|
executionCommand.executable = commandArgs[0];
|
||||||
String processName = (isLoginShell ? "-" : "") + ShellUtils.getExecutableBasename(executionCommand.executable);
|
String processName = (isLoginShell ? "-" : "") + ShellUtils.getExecutableBasename(executionCommand.executable);
|
||||||
|
|
||||||
String[] arguments = new String[processArgs.length];
|
String[] arguments = new String[commandArgs.length];
|
||||||
arguments[0] = processName;
|
arguments[0] = processName;
|
||||||
if (processArgs.length > 1) System.arraycopy(processArgs, 1, arguments, 1, processArgs.length - 1);
|
if (commandArgs.length > 1) System.arraycopy(commandArgs, 1, arguments, 1, commandArgs.length - 1);
|
||||||
|
|
||||||
executionCommand.arguments = arguments;
|
executionCommand.arguments = arguments;
|
||||||
|
|
||||||
if (executionCommand.commandLabel == null)
|
if (executionCommand.commandLabel == null)
|
||||||
executionCommand.commandLabel = processName;
|
executionCommand.commandLabel = processName;
|
||||||
|
|
||||||
|
// Setup command environment
|
||||||
|
HashMap<String, String> environment = shellEnvironmentClient.setupShellCommandEnvironment(currentPackageContext,
|
||||||
|
executionCommand);
|
||||||
|
List<String> environmentList = ShellEnvironmentUtils.convertEnvironmentToEnviron(environment);
|
||||||
|
Collections.sort(environmentList);
|
||||||
|
String[] environmentArray = environmentList.toArray(new String[0]);
|
||||||
|
|
||||||
if (!executionCommand.setState(ExecutionCommand.ExecutionState.EXECUTING)) {
|
if (!executionCommand.setState(ExecutionCommand.ExecutionState.EXECUTING)) {
|
||||||
executionCommand.setStateFailed(Errno.ERRNO_FAILED.getCode(), context.getString(R.string.error_failed_to_execute_termux_session_command, executionCommand.getCommandIdAndLabelLogString()));
|
executionCommand.setStateFailed(Errno.ERRNO_FAILED.getCode(), currentPackageContext.getString(R.string.error_failed_to_execute_termux_session_command, executionCommand.getCommandIdAndLabelLogString()));
|
||||||
TermuxSession.processTermuxSessionResult(null, executionCommand);
|
TermuxSession.processTermuxSessionResult(null, executionCommand);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.logDebugExtended(LOG_TAG, executionCommand.toString());
|
Logger.logDebugExtended(LOG_TAG, executionCommand.toString());
|
||||||
|
Logger.logVerboseExtended(LOG_TAG, "\"" + executionCommand.getCommandIdAndLabelLogString() + "\" TermuxSession Environment:\n" +
|
||||||
|
Joiner.on("\n").join(environmentArray));
|
||||||
|
|
||||||
Logger.logDebug(LOG_TAG, "Running \"" + executionCommand.getCommandIdAndLabelLogString() + "\" TermuxSession");
|
Logger.logDebug(LOG_TAG, "Running \"" + executionCommand.getCommandIdAndLabelLogString() + "\" TermuxSession");
|
||||||
TerminalSession terminalSession = new TerminalSession(executionCommand.executable, executionCommand.workingDirectory, executionCommand.arguments, environment, executionCommand.terminalTranscriptRows, terminalSessionClient);
|
TerminalSession terminalSession = new TerminalSession(executionCommand.executable,
|
||||||
|
executionCommand.workingDirectory, executionCommand.arguments, environmentArray,
|
||||||
|
executionCommand.terminalTranscriptRows, terminalSessionClient);
|
||||||
|
|
||||||
if (executionCommand.shellName != null) {
|
if (executionCommand.shellName != null) {
|
||||||
terminalSession.mSessionName = executionCommand.shellName;
|
terminalSession.mSessionName = executionCommand.shellName;
|
||||||
|
@ -219,10 +237,10 @@ public class TermuxSession {
|
||||||
* callback will be called.
|
* callback will be called.
|
||||||
*
|
*
|
||||||
* @param termuxSession The {@link TermuxSession}, which should be set if
|
* @param termuxSession The {@link TermuxSession}, which should be set if
|
||||||
* {@link #execute(Context, ExecutionCommand, TerminalSessionClient, TermuxSessionClient, ShellEnvironmentClient, boolean)}
|
* {@link #execute(Context, ExecutionCommand, TerminalSessionClient, TermuxSessionClient, IShellEnvironment, HashMap, boolean)}
|
||||||
* successfully started the process.
|
* successfully started the process.
|
||||||
* @param executionCommand The {@link ExecutionCommand}, which should be set if
|
* @param executionCommand The {@link ExecutionCommand}, which should be set if
|
||||||
* {@link #execute(Context, ExecutionCommand, TerminalSessionClient, TermuxSessionClient, ShellEnvironmentClient, boolean)}
|
* {@link #execute(Context, ExecutionCommand, TerminalSessionClient, TermuxSessionClient, IShellEnvironment, HashMap, boolean)}
|
||||||
* failed to start the process.
|
* failed to start the process.
|
||||||
*/
|
*/
|
||||||
private static void processTermuxSessionResult(final TermuxSession termuxSession, ExecutionCommand executionCommand) {
|
private static void processTermuxSessionResult(final TermuxSession termuxSession, ExecutionCommand executionCommand) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user