diff options
author | pacien | 2018-02-24 01:37:36 +0100 |
---|---|---|
committer | pacien | 2018-02-24 01:37:36 +0100 |
commit | 3c1a29e2b8717a20948773bbc21abdc723ce5dee (patch) | |
tree | d802e31724a27ac8d9a1c9e0d2ab730a225d5031 | |
parent | c29970d29bd3c4831fc2f80077d1d8548ed0fc15 (diff) | |
download | tincapp-3c1a29e2b8717a20948773bbc21abdc723ce5dee.tar.gz |
Handle daemon startup failures
-rw-r--r-- | app/src/main/c/exec.c | 4 | ||||
-rw-r--r-- | app/src/main/java/org/pacien/tincapp/commands/Executor.kt | 17 | ||||
-rw-r--r-- | app/src/main/java/org/pacien/tincapp/context/App.kt | 6 | ||||
-rw-r--r-- | app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt | 14 | ||||
-rw-r--r-- | app/src/main/res/values/strings.xml | 1 |
5 files changed, 31 insertions, 11 deletions
diff --git a/app/src/main/c/exec.c b/app/src/main/c/exec.c index d665341..5a76177 100644 --- a/app/src/main/c/exec.c +++ b/app/src/main/c/exec.c | |||
@@ -36,5 +36,7 @@ Java_org_pacien_tincapp_commands_Executor_forkExec(JNIEnv *env, jclass class, jo | |||
36 | 36 | ||
37 | JNIEXPORT jint JNICALL | 37 | JNIEXPORT jint JNICALL |
38 | Java_org_pacien_tincapp_commands_Executor_wait(JNIEnv *env, jclass class, jint pid) { | 38 | Java_org_pacien_tincapp_commands_Executor_wait(JNIEnv *env, jclass class, jint pid) { |
39 | return waitpid(pid, NULL, 0); | 39 | int status; |
40 | waitpid(pid, &status, 0); | ||
41 | return WIFEXITED(status) ? WEXITSTATUS(status) : -1; | ||
40 | } | 42 | } |
diff --git a/app/src/main/java/org/pacien/tincapp/commands/Executor.kt b/app/src/main/java/org/pacien/tincapp/commands/Executor.kt index eb04f6d..fedd0d2 100644 --- a/app/src/main/java/org/pacien/tincapp/commands/Executor.kt +++ b/app/src/main/java/org/pacien/tincapp/commands/Executor.kt | |||
@@ -13,6 +13,7 @@ import java.io.InputStreamReader | |||
13 | */ | 13 | */ |
14 | internal object Executor { | 14 | internal object Executor { |
15 | private const val FAILED = -1 | 15 | private const val FAILED = -1 |
16 | private const val SUCCESS = 0 | ||
16 | 17 | ||
17 | class CommandExecutionException(msg: String) : Exception(msg) | 18 | class CommandExecutionException(msg: String) : Exception(msg) |
18 | 19 | ||
@@ -26,17 +27,23 @@ internal object Executor { | |||
26 | private external fun forkExec(argcv: Array<String>): Int | 27 | private external fun forkExec(argcv: Array<String>): Int |
27 | 28 | ||
28 | /** | 29 | /** |
29 | * @return FAILED (-1) on error, 0 on no-op, the supplied PID otherwise | 30 | * @return FAILED (-1) on error, the exit status of the process otherwise |
30 | */ | 31 | */ |
31 | private external fun wait(pid: Int): Int | 32 | private external fun wait(pid: Int): Int |
32 | 33 | ||
33 | private fun read(stream: InputStream) = BufferedReader(InputStreamReader(stream)).readLines() | 34 | private fun read(stream: InputStream) = BufferedReader(InputStreamReader(stream)).readLines() |
34 | 35 | ||
35 | fun forkExec(cmd: Command): CompletableFuture<Void> { | 36 | fun forkExec(cmd: Command): CompletableFuture<Void> { |
36 | val pid = forkExec(cmd.asArray()) | 37 | val pid = forkExec(cmd.asArray()).also { |
37 | return when (pid) { | 38 | if (it == FAILED) throw CommandExecutionException("Could not fork child process.") |
38 | FAILED -> CompletableFuture.failedFuture(CommandExecutionException("Could not fork child process.")) | 39 | } |
39 | else -> CompletableFuture.runAsync { wait(pid) } | 40 | |
41 | return CompletableFuture.runAsync { | ||
42 | when (wait(pid)) { | ||
43 | SUCCESS -> Unit | ||
44 | FAILED -> throw CommandExecutionException("Process terminated abnormally.") | ||
45 | else -> throw CommandExecutionException("Non-zero exit status code.") | ||
46 | } | ||
40 | } | 47 | } |
41 | } | 48 | } |
42 | 49 | ||
diff --git a/app/src/main/java/org/pacien/tincapp/context/App.kt b/app/src/main/java/org/pacien/tincapp/context/App.kt index 9fb910b..4a4a475 100644 --- a/app/src/main/java/org/pacien/tincapp/context/App.kt +++ b/app/src/main/java/org/pacien/tincapp/context/App.kt | |||
@@ -4,6 +4,7 @@ import android.app.Application | |||
4 | import android.content.Context | 4 | import android.content.Context |
5 | import android.content.Intent | 5 | import android.content.Intent |
6 | import android.net.Uri | 6 | import android.net.Uri |
7 | import android.os.Handler | ||
7 | import android.support.annotation.StringRes | 8 | import android.support.annotation.StringRes |
8 | import android.support.v7.app.AlertDialog | 9 | import android.support.v7.app.AlertDialog |
9 | import android.view.WindowManager | 10 | import android.view.WindowManager |
@@ -16,20 +17,23 @@ class App : Application() { | |||
16 | override fun onCreate() { | 17 | override fun onCreate() { |
17 | super.onCreate() | 18 | super.onCreate() |
18 | appContext = applicationContext | 19 | appContext = applicationContext |
20 | handler = Handler() | ||
19 | } | 21 | } |
20 | 22 | ||
21 | companion object { | 23 | companion object { |
22 | private var appContext: Context? = null | 24 | private var appContext: Context? = null |
25 | private var handler: Handler? = null | ||
23 | 26 | ||
24 | fun getContext() = appContext!! | 27 | fun getContext() = appContext!! |
25 | fun getResources() = getContext().resources!! | 28 | fun getResources() = getContext().resources!! |
26 | 29 | ||
27 | fun alert(@StringRes title: Int, msg: String, manualLink: String? = null) = | 30 | fun alert(@StringRes title: Int, msg: String, manualLink: String? = null) = handler!!.post { |
28 | AlertDialog.Builder(getContext(), R.style.Theme_AppCompat_Dialog) | 31 | AlertDialog.Builder(getContext(), R.style.Theme_AppCompat_Dialog) |
29 | .setTitle(title).setMessage(msg) | 32 | .setTitle(title).setMessage(msg) |
30 | .apply { if (manualLink != null) setNeutralButton(R.string.action_open_manual) { _, _ -> openURL(manualLink) } } | 33 | .apply { if (manualLink != null) setNeutralButton(R.string.action_open_manual) { _, _ -> openURL(manualLink) } } |
31 | .setPositiveButton(R.string.action_close, { _, _ -> Unit }) | 34 | .setPositiveButton(R.string.action_close, { _, _ -> Unit }) |
32 | .create().apply { window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR) }.show() | 35 | .create().apply { window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR) }.show() |
36 | } | ||
33 | 37 | ||
34 | fun openURL(url: String) = | 38 | fun openURL(url: String) = |
35 | appContext?.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) | 39 | appContext?.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) |
diff --git a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt index dfdbb32..223763d 100644 --- a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt +++ b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt | |||
@@ -94,12 +94,18 @@ class TincVpnService : VpnService() { | |||
94 | 94 | ||
95 | val daemon = Tincd.start(netName, deviceFd.fd, privateKeys.first?.fd, privateKeys.second?.fd) | 95 | val daemon = Tincd.start(netName, deviceFd.fd, privateKeys.first?.fd, privateKeys.second?.fd) |
96 | setState(netName, interfaceCfg, deviceFd, daemon) | 96 | setState(netName, interfaceCfg, deviceFd, daemon) |
97 | waitForDaemonStartup().thenRun { | 97 | |
98 | waitForDaemonStartup().whenComplete { _, exception -> | ||
98 | deviceFd.close() | 99 | deviceFd.close() |
99 | privateKeys.first?.close() | 100 | privateKeys.first?.close() |
100 | privateKeys.second?.close() | 101 | privateKeys.second?.close() |
101 | Log.i(TAG, "tinc daemon started.") | 102 | |
102 | broadcastEvent(Actions.EVENT_CONNECTED) | 103 | if (exception != null) { |
104 | reportError(resources.getString(R.string.message_daemon_exited, exception.cause!!.message!!), exception) | ||
105 | } else { | ||
106 | Log.i(TAG, "tinc daemon started.") | ||
107 | broadcastEvent(Actions.EVENT_CONNECTED) | ||
108 | } | ||
103 | } | 109 | } |
104 | } | 110 | } |
105 | 111 | ||
@@ -132,7 +138,7 @@ class TincVpnService : VpnService() { | |||
132 | private fun waitForDaemonStartup() = | 138 | private fun waitForDaemonStartup() = |
133 | CompletableFuture | 139 | CompletableFuture |
134 | .runAsync { Thread.sleep(SETUP_DELAY) } | 140 | .runAsync { Thread.sleep(SETUP_DELAY) } |
135 | .thenCompose { netName?.let { Tinc.pid(it) } ?: CompletableFuture.completedFuture(0) } | 141 | .thenCompose { if (daemon!!.isDone) daemon!! else CompletableFuture.runAsync { } } |
136 | 142 | ||
137 | companion object { | 143 | companion object { |
138 | private const val SETUP_DELAY = 500L // ms | 144 | private const val SETUP_DELAY = 500L // ms |
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index da64285..fed15f0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml | |||
@@ -79,6 +79,7 @@ | |||
79 | <string name="message_starting_vpn">Starting VPN…</string> | 79 | <string name="message_starting_vpn">Starting VPN…</string> |
80 | <string name="message_disconnecting_vpn">Disconnecting VPN…</string> | 80 | <string name="message_disconnecting_vpn">Disconnecting VPN…</string> |
81 | <string name="message_passphrase_required">A passphrase is required to unlock the keyring.</string> | 81 | <string name="message_passphrase_required">A passphrase is required to unlock the keyring.</string> |
82 | <string name="message_daemon_exited">Tinc daemon exited during startup:\n%1$s\n\nCheck the logs for more details.</string> | ||
82 | 83 | ||
83 | <string name="value_none">none</string> | 84 | <string name="value_none">none</string> |
84 | <string name="value_yes">yes</string> | 85 | <string name="value_yes">yes</string> |