1
0
mirror of https://github.com/termux/termux-app synced 2024-06-17 22:57:08 +00:00

Added|Fixed: NativeShell now works, fixed the Termux task notification not updating for NativeShell tasks. Added taskFinished callback for plugins and signalTask method for plugins to call.

This commit is contained in:
tareksander 2022-06-18 20:11:17 +02:00
parent 44267d6582
commit f459ee481e
5 changed files with 94 additions and 44 deletions

View File

@ -14,6 +14,7 @@ import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.Process;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -291,7 +292,7 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
List<NativeShell> termuxNativeTasks = new ArrayList<>(mShellManager.mTermuxNativeTasks);
for (int i = 0; i < termuxNativeTasks.size(); i++) {
termuxNativeTasks.get(i).kill();
termuxNativeTasks.get(i).kill(Process.SIGNAL_KILL);
}
for (int i = 0; i < pendingPluginExecutionCommands.size(); i++) {
@ -442,7 +443,14 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
});
client.terminated(exitCode, error);
}, environment);
shell[0].execute();
mShellManager.mTermuxNativeTasks.add(shell[0]);
if (shell[0].execute()) {
updateNotification();
} else {
// gets removed automatically by the callback
return null;
}
return shell[0];
}

View File

@ -35,15 +35,13 @@ import com.termux.shared.net.socket.local.PeerCred;
import com.termux.shared.shell.command.ExecutionCommand;
import com.termux.shared.shell.command.runner.nativerunner.NativeShell;
import com.termux.shared.termux.plugins.TermuxPluginUtils;
import com.termux.shared.termux.shell.TermuxShellEnvironmentClient;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@ -284,13 +282,24 @@ public class PluginService extends Service
public Task runTask(String commandPath, String[] arguments, ParcelFileDescriptor stdin, String workdir, String[] environment) {
externalAppsOrThrow();
if (commandPath == null) throw new NullPointerException("Passed commandPath is null");
if (workdir == null) throw new NullPointerException("Passed workdir is null");
if (stdin == null) throw new NullPointerException("Passed stdin is null");
Plugin p = checkClient();
BinderUtils.enforceRunCommandPermission(PluginService.this);
List<String> env = (environment != null) ? Arrays.asList(environment) : new ArrayList<>();
env.addAll(Arrays.asList(new TermuxShellEnvironmentClient().
buildEnvironment(PluginService.this,
false,
workdir)));
String[] realEnvironment = env.toArray(new String[0]);
final Object sync = new Object();
final RuntimeException[] ex = new RuntimeException[1];
final Exception[] ex = new Exception[1];
final boolean[] finished = {false};
final NativeShell[] shell = new NativeShell[1];
// create pipes
@ -327,7 +336,8 @@ public class PluginService extends Service
}
in[1] = pipes[0];
out[1] = pipes[1];
// start the Task on the main thread, TermuxService is not synchronized itself
mMainThreadHandler.post(() -> {
TermuxService s = mTermuxService;
if (s == null) {
@ -346,31 +356,27 @@ public class PluginService extends Service
cmd.stdinFD = stdin;
cmd.stdoutFD = out[0];
cmd.stderrFD = out[1];
/*
try {
ParcelFileDescriptor od = out[0].dup();
new Thread(() -> {
shell[0] = s.executeNativeShell(cmd, realEnvironment, (exitCode, error) -> {
if (error != null) {
ex[0] = error;
} else {
p.tasks.remove(shell[0].getPid());
try {
BufferedWriter w = new BufferedWriter(new FileWriter(od.getFileDescriptor()));
w.write("test");
w.flush();
w.close();
} catch (Exception ignored) {ignored.printStackTrace();}
}).start();
}
catch (IOException e) {
e.printStackTrace();
}
*/
shell[0] = s.executeNativeShell(cmd, environment, (exitCode, error) -> {
try {
Logger.logDebug("NativeShell", "exit: "+exitCode);
// TODO callback
} catch (Exception ignored) {}
p.callback.taskFinished(shell[0].getPid(), exitCode);
} catch (Exception ignored) {}
}
});
p.tasks.put(shell[0].getPid(), shell[0]);
if (shell[0] != null) {
p.tasks.put(shell[0].getPid(), shell[0]);
} else {
while (ex[0] == null) {
// wait until the exception is caught if the Task could not be started
try {
Thread.sleep(1);
} catch (InterruptedException ignored) {}
}
}
synchronized (sync) {
finished[0] = true;
sync.notifyAll();
@ -411,7 +417,7 @@ public class PluginService extends Service
try {
in[1].close();
} catch (IOException ignored) {}
throw ex[0];
throw new RuntimeException(ex[0]);
}
Task t = new Task();
@ -421,7 +427,22 @@ public class PluginService extends Service
return t;
}
@Override
public boolean signalTask(int pid, int signal) {
Plugin p = checkClient();
BinderUtils.enforceRunCommandPermission(PluginService.this);
// only allow to signal processes that were started as Tasks by the plugin
NativeShell shell = p.tasks.get(pid);
if (shell != null) {
shell.kill(signal);
return true;
} else {
return false;
}
}
@Override
public void listenOnSocketFile(String name) {
externalAppsOrThrow();

View File

@ -34,7 +34,13 @@ interface IPluginCallback {
void socketConnection(String sockname, in ParcelFileDescriptor connection) = 2;
/**
* Gets called when a started Task exits.
*
* @param pid The pid of the task that exited.
* @param code The exit code of the Task. Negative values indicate the Task was killed by a signal. The signal number is then {@code -code}.
*/
void taskFinished(int pid, int code) = 3;

View File

@ -43,17 +43,33 @@ interface IPluginService {
/**
* Runs a command in a Termux task in the background.
* stdin, commandPath and workdir are required parameters.
* stdin, commandPath and workdir are required parameters.<br>
* When a Task exists, the {@link com.termux.plugin_aidl.IPluginCallback#taskFinished} callback gets invoked.
*
* @param commandPath The full absolute path of the command binary to run.
* @param arguments The command line arguments for the command. The first argument should be the same as {@code commandPath} (this fill be the 0th argument).
* @param stdin The {@link android.os.ParcelFileDescriptor} used for the standard input of the command. You can e.g. create a pipe and use the read end here.
* @param workdir The working directory of the command, also as an absolute path.
* @param environment Additional environment variables for the command. Can be {@code null}.
* @return A {@link com.termux.plugin_aidl.Task} that represents the running Task. It contains pipes for reading stdout and stderr.
*/
Task runTask(String commandPath, in String[] arguments, in ParcelFileDescriptor stdin, String workdir, in String[] environment) = 3;
/**
* Send a signal to a Task.
* @param pid The pid of the Task to kill.
* @param signal The signal number to use. Using {@code kill -l} in bash and other shells lists the signals with their numbers.
* @return Returns true if there was a Task with this pid, false if there was none.
*/
boolean signalTask(int pid, int signal) = 4;
/**
* This creates a socket file with name under {@link com.termux.shared.termux.TermuxConstants#TERMUX_PLUGINS_DIR_PATH}/&lt;package name of caller&gt;.
* Connections are transferred to the plugin via the {@link com.termux.plugin_aidl.IPluginService#socketConnection} method.
*
* @param name Name of the socket file.
*/
void listenOnSocketFile(String name) = 4;
void listenOnSocketFile(String name) = 5;
/**
@ -62,7 +78,7 @@ interface IPluginService {
* @param name Name of the file.
* @þaram mode Mode to use.
*/
ParcelFileDescriptor openFile(String name, String mode) = 5;
ParcelFileDescriptor openFile(String name, String mode) = 6;

View File

@ -1,11 +1,8 @@
package com.termux.shared.shell.command.runner.nativerunner;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import androidx.annotation.Nullable;
import com.termux.shared.shell.command.ExecutionCommand;
import com.termux.terminal.JNI;
@ -42,7 +39,7 @@ public final class NativeShell
void terminated(int exitCode, Exception error);
}
public synchronized void execute() {
public synchronized boolean execute() {
try {
pid = JNI.createTask(exe.executable, exe.workingDirectory, exe.arguments, env, exe.stdinFD.getFd(), exe.stdoutFD.getFd(), exe.stderrFD.getFd());
new Thread(() -> {
@ -50,7 +47,8 @@ public final class NativeShell
client.terminated(exit, null);
pid = -1;
}).start();
} catch (RuntimeException e) {
return true;
} catch (Exception e) {
client.terminated(0, e);
} finally {
// close the ParcelFileDescriptors
@ -64,11 +62,12 @@ public final class NativeShell
exe.stderrFD.close();
} catch (IOException ignored) {}
}
return false;
}
public synchronized void kill() {
public synchronized void kill(int signal) {
if (pid != -1)
Process.killProcess(pid);
Process.sendSignal(pid, signal);
}
public synchronized int getPid() {