Commit b682cb3a authored by chenhuaqing's avatar chenhuaqing

fixed restart

parent 0b81449f
...@@ -39,18 +39,23 @@ import kotlin.concurrent.thread ...@@ -39,18 +39,23 @@ import kotlin.concurrent.thread
class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : CoroutineScope { class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : CoroutineScope {
companion object { companion object {
private val pid by lazy { private val pid by lazy {
Class.forName("java.lang.ProcessManager\$ProcessImpl").getDeclaredField("pid").apply { isAccessible = true } Class.forName("java.lang.ProcessManager\$ProcessImpl").getDeclaredField("pid")
.apply { isAccessible = true }
} }
} }
private inner class Guard(private val cmd: List<String>) { private inner class Guard(private val cmdSupplier: suspend () -> List<String>) {
private lateinit var process: Process private lateinit var process: Process
private lateinit var cmd: List<String>
private fun streamLogger(input: InputStream, logger: (String) -> Unit) = try { private fun streamLogger(input: InputStream, logger: (String) -> Unit) = try {
input.bufferedReader().forEachLine(logger) input.bufferedReader().forEachLine(logger)
} catch (_: IOException) { } // ignore } catch (_: IOException) {
} // ignore
fun start() { suspend fun start() {
cmd = cmdSupplier.invoke()
Timber.i("start process: ${Commandline.toString(cmd)}")
process = ProcessBuilder(cmd).directory(Core.deviceStorage.noBackupFilesDir).start() process = ProcessBuilder(cmd).directory(Core.deviceStorage.noBackupFilesDir).start()
} }
...@@ -74,7 +79,8 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C ...@@ -74,7 +79,8 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C
when { when {
exitCode == OsConstants.ECONNREFUSED -> throw IOException("connection refused by server") exitCode == OsConstants.ECONNREFUSED -> throw IOException("connection refused by server")
SystemClock.elapsedRealtime() - startTime < 1000 -> throw IOException( SystemClock.elapsedRealtime() - startTime < 1000 -> throw IOException(
"$cmdName exits too fast (exit code: $exitCode)") "$cmdName exits too fast (exit code: $exitCode)"
)
exitCode == 128 + OsConstants.SIGKILL -> Timber.w("$cmdName was killed") exitCode == 128 + OsConstants.SIGKILL -> Timber.w("$cmdName was killed")
else -> Timber.w(IOException("$cmdName unexpectedly exits with code $exitCode")) else -> Timber.w(IOException("$cmdName unexpectedly exits with code $exitCode"))
} }
...@@ -112,11 +118,15 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C ...@@ -112,11 +118,15 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C
override val coroutineContext = Dispatchers.Main.immediate + Job() override val coroutineContext = Dispatchers.Main.immediate + Job()
@MainThread @MainThread
fun start(cmd: List<String>, onRestartCallback: (suspend () -> Unit)? = null) { fun start(
Timber.i("start process: ${Commandline.toString(cmd)}") cmdSupplier: suspend () -> List<String>,
Guard(cmd).apply { onRestartCallback: (suspend () -> Unit)? = null
start() // if start fails, IOException will be thrown directly ) {
launch { looper(onRestartCallback) } Guard(cmdSupplier).apply {
launch {
start() // if start fails, IOException will be thrown directly
looper(onRestartCallback)
}
} }
} }
......
...@@ -59,73 +59,77 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro ...@@ -59,73 +59,77 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
* Sensitive shadowsocks configuration file requires extra protection. It may be stored in encrypted storage or * Sensitive shadowsocks configuration file requires extra protection. It may be stored in encrypted storage or
* device storage, depending on which is currently available. * device storage, depending on which is currently available.
*/ */
fun start(service: BaseService.Interface, stat: File, configFile: File, mode: String, dnsRelay: Boolean = true) { suspend fun start(service: BaseService.Interface, stat: File, configFile: File, mode: String, dnsRelay: Boolean = true) {
// setup traffic monitor path // setup traffic monitor path
trafficMonitor = TrafficMonitor(stat) trafficMonitor = TrafficMonitor(stat)
val cmdSupplier: suspend () -> List<String> = {
// init JSON config AuthManager.initAuthProfile(profile)
this.configFile = configFile // init JSON config
val config = profile.toJson() this.configFile = configFile
plugin?.let { (path, opts, isV2) -> val config = profile.toJson()
if (service.isVpnService) { plugin?.let { (path, opts, isV2) ->
if (isV2) opts["__android_vpn"] = "" else config.put("plugin_args", JSONArray(arrayOf("-V"))) if (service.isVpnService) {
if (isV2) opts["__android_vpn"] = "" else config.put("plugin_args", JSONArray(arrayOf("-V")))
}
config.put("plugin", path).put("plugin_opts", opts.toString())
} }
config.put("plugin", path).put("plugin_opts", opts.toString()) config.put("dns", "unix://local_dns_path")
} config.put("locals", JSONArray().apply {
config.put("dns", "unix://local_dns_path") // local SOCKS5 proxy
config.put("locals", JSONArray().apply {
// local SOCKS5 proxy
put(JSONObject().apply {
put("local_address", DataStore.listenAddress)
put("local_port", DataStore.portProxy)
put("local_udp_address", DataStore.listenAddress)
put("local_udp_port", DataStore.portProxy)
put("mode", mode)
})
// local DNS proxy
if (dnsRelay) try {
URI("dns://${profile.remoteDns}")
} catch (e: URISyntaxException) {
throw BaseService.ExpectedExceptionWrapper(e)
}.let { dns ->
put(JSONObject().apply { put(JSONObject().apply {
put("local_address", DataStore.listenAddress) put("local_address", DataStore.listenAddress)
put("local_port", DataStore.portLocalDns) put("local_port", DataStore.portProxy)
put("local_dns_address", "local_dns_path") put("local_udp_address", DataStore.listenAddress)
put("remote_dns_address", dns.host ?: "0.0.0.0") put("local_udp_port", DataStore.portProxy)
put("remote_dns_port", if (dns.port < 0) 53 else dns.port) put("mode", mode)
put("protocol", "dns")
put("mode", "udp_only")
}) })
}
})
configFile.writeText(config.toString())
Timber.d("prepare start service with config: $config") // local DNS proxy
if (dnsRelay) try {
URI("dns://${profile.remoteDns}")
} catch (e: URISyntaxException) {
throw BaseService.ExpectedExceptionWrapper(e)
}.let { dns ->
put(JSONObject().apply {
put("local_address", DataStore.listenAddress)
put("local_port", DataStore.portLocalDns)
put("local_dns_address", "local_dns_path")
put("remote_dns_address", dns.host ?: "0.0.0.0")
put("remote_dns_port", if (dns.port < 0) 53 else dns.port)
put("protocol", "dns")
put("mode", "udp_only")
})
}
})
configFile.writeText(config.toString())
Timber.d("prepare start service with config: $config")
// build the command line // build the command line
val cmd = arrayListOf( val cmd = arrayListOf(
File((service as Context).applicationInfo.nativeLibraryDir, Executable.SS_LOCAL).absolutePath, File((service as Context).applicationInfo.nativeLibraryDir, Executable.SS_LOCAL).absolutePath,
"--stat-path", stat.absolutePath, "--stat-path", stat.absolutePath,
"-c", configFile.absolutePath, "-c", configFile.absolutePath,
) )
if (service.isVpnService) cmd += "--vpn" if (service.isVpnService) cmd += "--vpn"
if (route != Acl.ALL) { if (route != Acl.ALL) {
cmd += "--acl" cmd += "--acl"
cmd += Acl.getFile(route).absolutePath cmd += Acl.getFile(route).absolutePath
} Timber.d("start vpn service with acl ${Acl.getFile(route).readText()}")
}
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
cmd += "-v" cmd += "-v"
cmd += "-v" cmd += "-v"
cmd += "-v" cmd += "-v"
cmd += "-v" cmd += "-v"
}
cmd
} }
service.data.processes!!.start(cmd) service.data.processes!!.start(cmdSupplier)
} }
fun scheduleUpdate() { fun scheduleUpdate() {
......
...@@ -224,7 +224,7 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -224,7 +224,7 @@ class VpnService : BaseVpnService(), BaseService.Interface {
cmd += PRIVATE_VLAN6_ROUTER cmd += PRIVATE_VLAN6_ROUTER
} }
cmd += "--enable-udprelay" cmd += "--enable-udprelay"
data.processes!!.start(cmd, onRestartCallback = { data.processes!!.start(cmdSupplier = { cmd }, onRestartCallback = {
try { try {
sendFd(conn.fileDescriptor) sendFd(conn.fileDescriptor)
} catch (e: ErrnoException) { } catch (e: ErrnoException) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment