diff --git a/app/src/main/java/com/termux/app/TermuxService.java b/app/src/main/java/com/termux/app/TermuxService.java
index 26e89309..135dcc85 100644
--- a/app/src/main/java/com/termux/app/TermuxService.java
+++ b/app/src/main/java/com/termux/app/TermuxService.java
@@ -392,8 +392,8 @@ public final class TermuxService extends Service implements TermuxTask.TermuxTas
/** Create a {@link TermuxTask}. */
@Nullable
- public TermuxTask createTermuxTask(String executablePath, String[] arguments, String workingDirectory) {
- return createTermuxTask(new ExecutionCommand(getNextExecutionId(), executablePath, arguments, workingDirectory, true, false));
+ public TermuxTask createTermuxTask(String executablePath, String[] arguments, String stdin, String workingDirectory) {
+ return createTermuxTask(new ExecutionCommand(getNextExecutionId(), executablePath, arguments, stdin, workingDirectory, true, false));
}
/** Create a {@link TermuxTask}. */
@@ -479,8 +479,8 @@ public final class TermuxService extends Service implements TermuxTask.TermuxTas
* Currently called by {@link TermuxTerminalSessionClient#addNewSession(boolean, String)} to add a new {@link TermuxSession}.
*/
@Nullable
- public TermuxSession createTermuxSession(String executablePath, String[] arguments, String workingDirectory, boolean isFailSafe, String sessionName) {
- return createTermuxSession(new ExecutionCommand(getNextExecutionId(), executablePath, arguments, workingDirectory, false, isFailSafe), sessionName);
+ public TermuxSession createTermuxSession(String executablePath, String[] arguments, String stdin, String workingDirectory, boolean isFailSafe, String sessionName) {
+ return createTermuxSession(new ExecutionCommand(getNextExecutionId(), executablePath, arguments, stdin, workingDirectory, false, isFailSafe), sessionName);
}
/** Create a {@link TermuxSession}. */
diff --git a/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionClient.java b/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionClient.java
index 13d5934e..8beb7971 100644
--- a/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionClient.java
+++ b/app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionClient.java
@@ -206,7 +206,7 @@ public class TermuxTerminalSessionClient extends TermuxTerminalSessionClientBase
workingDirectory = currentSession.getCwd();
}
- TermuxSession newTermuxSession = mActivity.getTermuxService().createTermuxSession(null, null, workingDirectory, isFailSafe, sessionName);
+ TermuxSession newTermuxSession = mActivity.getTermuxService().createTermuxSession(null, null, null, workingDirectory, isFailSafe, sessionName);
if (newTermuxSession == null) return;
TerminalSession newTerminalSession = newTermuxSession.getTerminalSession();
diff --git a/termux-shared/src/main/java/com/termux/shared/models/ExecutionCommand.java b/termux-shared/src/main/java/com/termux/shared/models/ExecutionCommand.java
index 5aaf585b..76e58532 100644
--- a/termux-shared/src/main/java/com/termux/shared/models/ExecutionCommand.java
+++ b/termux-shared/src/main/java/com/termux/shared/models/ExecutionCommand.java
@@ -74,6 +74,8 @@ public class ExecutionCommand {
public Uri executableUri;
/** The executable arguments array for the {@link ExecutionCommand}. */
public String[] arguments;
+ /** The stdin string for the {@link ExecutionCommand}. */
+ public String stdin;
/** The current working directory for the {@link ExecutionCommand}. */
public String workingDirectory;
@@ -138,10 +140,11 @@ public class ExecutionCommand {
this.id = id;
}
- public ExecutionCommand(Integer id, String executable, String[] arguments, String workingDirectory, boolean inBackground, boolean isFailsafe) {
+ public ExecutionCommand(Integer id, String executable, String[] arguments, String stdin, String workingDirectory, boolean inBackground, boolean isFailsafe) {
this.id = id;
this.executable = executable;
this.arguments = arguments;
+ this.stdin = stdin;
this.workingDirectory = workingDirectory;
this.inBackground = inBackground;
this.isFailsafe = isFailsafe;
@@ -560,4 +563,8 @@ public class ExecutionCommand {
return currentState == ExecutionState.EXECUTING;
}
+ public synchronized boolean isSuccessful() {
+ return currentState == ExecutionState.SUCCESS;
+ }
+
}
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 17d6795f..d8525430 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
@@ -13,8 +13,10 @@ import com.termux.shared.termux.TermuxConstants;
import com.termux.shared.logger.Logger;
import com.termux.shared.models.ExecutionCommand.ExecutionState;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
/**
* A class that maintains info for background Termux tasks run with {@link Runtime#exec(String[], String[], File)}.
@@ -92,7 +94,7 @@ public final class TermuxTask {
if (isSynchronous) {
try {
- termuxTask.executeInner();
+ termuxTask.executeInner(context);
} catch (IllegalThreadStateException | InterruptedException e) {
// TODO: Should either of these be handled or returned?
}
@@ -101,7 +103,7 @@ public final class TermuxTask {
@Override
public void run() {
try {
- termuxTask.executeInner();
+ termuxTask.executeInner(context);
} catch (IllegalThreadStateException | InterruptedException e) {
// TODO: Should either of these be handled or returned?
}
@@ -118,8 +120,10 @@ public final class TermuxTask {
* If the processes finishes, then sets {@link ExecutionCommand#stdout}, {@link ExecutionCommand#stderr}
* and {@link ExecutionCommand#exitCode} for the {@link #mExecutionCommand} of the {@code termuxTask}
* and then calls {@link #processTermuxTaskResult(TermuxTask, ExecutionCommand) to process the result}.
+ *
+ * @param context The {@link Context} for operations.
*/
- private void executeInner() throws IllegalThreadStateException, InterruptedException {
+ private void executeInner(@NonNull final Context context) throws IllegalThreadStateException, InterruptedException {
final int pid = ShellUtils.getPid(mProcess);
Logger.logDebug(LOG_TAG, "Running \"" + mExecutionCommand.getCommandIdAndLabelLogString() + "\" TermuxTask with pid " + pid);
@@ -129,8 +133,8 @@ public final class TermuxTask {
mExecutionCommand.exitCode = null;
-
- // setup stdout and stderr gobblers
+ // setup stdin, and stdout and stderr gobblers
+ DataOutputStream STDIN = new DataOutputStream(mProcess.getOutputStream());
StreamGobbler STDOUT = new StreamGobbler(pid + "-stdout", mProcess.getInputStream(), mStdout);
StreamGobbler STDERR = new StreamGobbler(pid + "-stderr", mProcess.getErrorStream(), mStderr);
@@ -138,6 +142,33 @@ public final class TermuxTask {
STDOUT.start();
STDERR.start();
+ if (mExecutionCommand.stdin != null && !mExecutionCommand.stdin.isEmpty()) {
+ try {
+ STDIN.write((mExecutionCommand.stdin + "\n").getBytes(StandardCharsets.UTF_8));
+ STDIN.flush();
+ STDIN.close();
+ //STDIN.write("exit\n".getBytes(StandardCharsets.UTF_8));
+ //STDIN.flush();
+ } catch(IOException e){
+ if (e.getMessage().contains("EPIPE") || e.getMessage().contains("Stream closed")) {
+ // Method most horrid to catch broken pipe, in which case we
+ // do nothing. The command is not a shell, the shell closed
+ // STDIN, the script already contained the exit command, etc.
+ // these cases we want the output instead of returning null.
+ } else {
+ // other issues we don't know how to handle, leads to
+ // returning null
+ mExecutionCommand.setStateFailed(ExecutionCommand.RESULT_CODE_FAILED, context.getString(R.string.error_exception_received_while_executing_termux_task_command, mExecutionCommand.getCommandIdAndLabelLogString(), e.getMessage()), e);
+ mExecutionCommand.stdout = mStdout.toString();
+ mExecutionCommand.stderr = mStderr.toString();
+ mExecutionCommand.exitCode = -1;
+ TermuxTask.processTermuxTaskResult(this, null);
+ kill();
+ return;
+ }
+ }
+ }
+
// wait for our process to finish, while we gobble away in the background
int exitCode = mProcess.waitFor();
@@ -146,6 +177,11 @@ public final class TermuxTask {
// needed in theory, and may even produce warnings, in "normal" Java
// they are required for guaranteed cleanup of resources, so lets be
// safe and do this on Android as well
+ try {
+ STDIN.close();
+ } catch (IOException e) {
+ // might be closed already
+ }
STDOUT.join();
STDERR.join();
mProcess.destroy();
@@ -201,13 +237,20 @@ public final class TermuxTask {
}
if (mExecutionCommand.isExecuting()) {
- int pid = ShellUtils.getPid(mProcess);
- try {
- // Send SIGKILL to process
- Os.kill(pid, OsConstants.SIGKILL);
- } catch (ErrnoException e) {
- Logger.logWarn(LOG_TAG, "Failed to send SIGKILL to \"" + mExecutionCommand.getCommandIdAndLabelLogString() + "\" TermuxTask with pid " + pid + ": " + e.getMessage());
- }
+ kill();
+ }
+ }
+
+ /**
+ * Kill this {@link TermuxTask} by sending a {@link OsConstants#SIGILL} to its {@link #mProcess}.
+ */
+ public void kill() {
+ int pid = ShellUtils.getPid(mProcess);
+ try {
+ // Send SIGKILL to process
+ Os.kill(pid, OsConstants.SIGKILL);
+ } catch (ErrnoException e) {
+ Logger.logWarn(LOG_TAG, "Failed to send SIGKILL to \"" + mExecutionCommand.getCommandIdAndLabelLogString() + "\" TermuxTask with pid " + pid + ": " + e.getMessage());
}
}
diff --git a/termux-shared/src/main/res/values/strings.xml b/termux-shared/src/main/res/values/strings.xml
index a4b77fc5..d1b4e494 100644
--- a/termux-shared/src/main/res/values/strings.xml
+++ b/termux-shared/src/main/res/values/strings.xml
@@ -82,6 +82,8 @@
Execution has been cancelled since execution service is being killed
"Failed to execute \"%1$s\" termux session command"
"Failed to execute \"%1$s\" termux task command"
+ Exception received while to executing \"%1$s\" termux session command.\nException: %2$s
+ Exception received while to executing \"%1$s\" termux task command.\nException: %2$s"