From ad0f9dd8b090fc1c0f7b2e9ba897091b3f554f3f Mon Sep 17 00:00:00 2001 From: nonlin-lin-chaos-order-etc-etal Date: Wed, 10 Apr 2024 06:30:11 +0800 Subject: [PATCH] Improve external process handling --- .../org/purplei2p/i2pd/AbstractProcess.java | 4 +- .../org/purplei2p/i2pd/DaemonWrapper.java | 11 +++- .../purplei2p/i2pd/ExternalProcessImpl.java | 7 ++- .../java/org/purplei2p/i2pd/I2PDActivity.java | 5 +- .../main/java/org/purplei2p/i2pd/I2pdApi.java | 59 +++++++++++++++---- 5 files changed, 67 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/org/purplei2p/i2pd/AbstractProcess.java b/app/src/main/java/org/purplei2p/i2pd/AbstractProcess.java index 829161a..a5c66b6 100644 --- a/app/src/main/java/org/purplei2p/i2pd/AbstractProcess.java +++ b/app/src/main/java/org/purplei2p/i2pd/AbstractProcess.java @@ -1,5 +1,7 @@ package org.purplei2p.i2pd; public interface AbstractProcess { - void kill(); + /** @param tr can be null + */ + void kill(Throwable tr); } \ No newline at end of file diff --git a/app/src/main/java/org/purplei2p/i2pd/DaemonWrapper.java b/app/src/main/java/org/purplei2p/i2pd/DaemonWrapper.java index 531ff79..faac79c 100644 --- a/app/src/main/java/org/purplei2p/i2pd/DaemonWrapper.java +++ b/app/src/main/java/org/purplei2p/i2pd/DaemonWrapper.java @@ -153,13 +153,18 @@ public class DaemonWrapper { return getState().isStartedOkay(); } - public synchronized void stopDaemon() { + public void stopDaemon() { + stopDaemon(null); + } + + public synchronized void stopDaemon(final Throwable throwable) { if (isStartedOkay()) { try { - I2pdApi.stopDaemon(); + I2pdApi.stopDaemon(throwable); } catch (Throwable tr) { Log.e(TAG, "", tr); } + if (throwable != null) lastThrowable = throwable; setState(State.stopped); } } @@ -179,7 +184,7 @@ public class DaemonWrapper { String locale = getAppLocale(); Log.i(TAG, "setting webconsole language to " + locale); - daemonStartResult = I2pdApi.startDaemon(ctx, i2pdpath, locale); + daemonStartResult = I2pdApi.startDaemon(ctx, i2pdpath, locale, DaemonWrapper.this); if ("ok".equals(daemonStartResult)) { setState(State.startedOkay); } else diff --git a/app/src/main/java/org/purplei2p/i2pd/ExternalProcessImpl.java b/app/src/main/java/org/purplei2p/i2pd/ExternalProcessImpl.java index 38c53d6..1a3ae11 100644 --- a/app/src/main/java/org/purplei2p/i2pd/ExternalProcessImpl.java +++ b/app/src/main/java/org/purplei2p/i2pd/ExternalProcessImpl.java @@ -3,17 +3,20 @@ package org.purplei2p.i2pd; import android.util.Log; public class ExternalProcessImpl implements AbstractProcess { + public static final String TAG = "ExtProcKill"; private final int pid; public ExternalProcessImpl(int pid) { this.pid = pid; } - public void kill() { + public void kill(Throwable reasonThrowable) { + if(reasonThrowable!=null) Log.e(TAG,"reasonThrowable", reasonThrowable); + else Log.e(TAG,"reasonThrowable==null"); try { Runtime.getRuntime().exec(new String[]{ "/system/bin/sh", "-c", "kill " + pid }); }catch(Throwable tr){ - Log.e("ExtProcKill","",tr); + Log.e(TAG,"",tr); } } } diff --git a/app/src/main/java/org/purplei2p/i2pd/I2PDActivity.java b/app/src/main/java/org/purplei2p/i2pd/I2PDActivity.java index c06dab8..a8dac86 100644 --- a/app/src/main/java/org/purplei2p/i2pd/I2PDActivity.java +++ b/app/src/main/java/org/purplei2p/i2pd/I2PDActivity.java @@ -83,9 +83,12 @@ public class I2PDActivity extends Activity { // I2CPState.setChecked(I2pdApi.getI2CPState()); // } + final Throwable lastThrowable = daemon.getLastThrowable(); String startResultStr = DaemonWrapper.State.startFailed.equals(state) ? String.format(": %s", daemon.getDaemonStartResult()) : ""; + String stopReasonStr = DaemonWrapper.State.stopped.equals(state) && lastThrowable != null ? + String.format(": %s", lastThrowable) : ""; String graceStr = DaemonWrapper.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", formatGraceTimeRemaining(), getText(R.string.remaining)) : ""; - textView.setText(String.format("%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr)); + textView.setText(String.format("%s%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr, stopReasonStr)); } catch (Throwable tr) { Log.e(TAG,"error ignored",tr); } diff --git a/app/src/main/java/org/purplei2p/i2pd/I2pdApi.java b/app/src/main/java/org/purplei2p/i2pd/I2pdApi.java index 830ce7c..7b86c21 100644 --- a/app/src/main/java/org/purplei2p/i2pd/I2pdApi.java +++ b/app/src/main/java/org/purplei2p/i2pd/I2pdApi.java @@ -29,19 +29,32 @@ public class I2pdApi { * returns error info if failed * returns "ok" if daemon initialized and started okay */ - public static String startDaemon(Context ctx, String dataDir, String language){ + public static String startDaemon(final Context ctx, final String dataDir, String language, final DaemonWrapper daemonWrapper){ try { i2pdProcess = null; I2pdApi.dataDir = dataDir; - Process p = Runtime.getRuntime().exec(new String[]{ + File pidFile = new File(dataDir, "i2pd.pid"); + final Process p = Runtime.getRuntime().exec(new String[]{ ctx.getApplicationInfo().nativeLibraryDir + "/libi2pd.so", - "--datadir=" + dataDir + "--datadir=" + dataDir, + "--pidfile=" + pidFile.getAbsolutePath() }); - i2pdProcess = () -> { + i2pdProcess = (Throwable tr) -> { try { - p.destroy(); - } catch (Throwable tr) { - Log.e(TAG, "", tr); + if (p.isAlive()) { + if (tr != null) + Log.e(TAG, "destroying the subprocess \"i2pd\", reason: " + tr, tr); + else + Log.e(TAG, "destroying the subprocess \"i2pd\", reason: null"); + p.destroy(); + }else{ + if (tr != null) + Log.e(TAG, "skipping destroy of a dead subprocess \"i2pd\", reason: " + tr, tr); + else + Log.e(TAG, "skipping destroy of a dead subprocess \"i2pd\", reason: null"); + } + } catch (Throwable tr2) { + Log.e(TAG, "", tr2); } }; new Thread(() -> { @@ -60,7 +73,7 @@ public class I2pdApi { } catch (Throwable tr) { Log.e(TAG, "", tr); } - }, "i2pd-stdout"); + }, "i2pd-stdout").start(); new Thread(() -> { try { try (BufferedInputStream bis = new BufferedInputStream(p.getErrorStream())) { @@ -77,7 +90,29 @@ public class I2pdApi { } catch (Throwable tr) { Log.e(TAG, "", tr); } - }, "i2pd-stderr"); + try { + p.waitFor(); + } catch (Throwable tr) { + Log.e(TAG, "", tr); + } + final int errorLevel = p.exitValue(); + Log.i(TAG, "i2pd process exit code: " + errorLevel); + final Throwable trReason = new Throwable("subprocess \"i2pd\" exited with exit code " + errorLevel); + try { + stopDaemon(trReason); + Log.i(TAG, "stopDaemon completed"); + } catch (Throwable tr) { + Log.e(TAG, "Called stopDaemon, got exception", tr); + } + new Thread(() -> { + try { + daemonWrapper.stopDaemon(trReason); + Log.i(TAG, "daemonWrapper.stopDaemon completed"); + } catch (Throwable tr) { + Log.e(TAG, "Called daemonWrapper.stopDaemon, got exception", tr); + } + }, "stop the daemonWrapper thread").start(); + }, "i2pd-stderr").start(); new Thread(() -> { try { try (BufferedOutputStream bos = new BufferedOutputStream(p.getOutputStream())) { @@ -94,7 +129,7 @@ public class I2pdApi { } catch (Throwable tr) { Log.e(TAG, "", tr); } - }, "i2pd-stdin"); + }, "i2pd-stdin").start(); return "ok"; } catch (Throwable tr) { Log.e(TAG, "", tr); @@ -102,10 +137,10 @@ public class I2pdApi { } } - public static void stopDaemon(){ + public static void stopDaemon(Throwable tr){ AbstractProcess p = i2pdProcess; if (p != null) { - p.kill(); + p.kill(tr); i2pdProcess = null; } }