Commit dc2a5ca1 authored by Mygod's avatar Mygod

Use SIGKILL to kill old processes

Fix #1455.

Note that in later Android versions, only processes under same uid will be listed and this will be quite efficient.
parent ae171a70
...@@ -64,6 +64,7 @@ public class JniHelper { ...@@ -64,6 +64,7 @@ public class JniHelper {
} }
} }
public static native int sigkill(int pid);
private static native int sigterm(Process process); private static native int sigterm(Process process);
private static native Integer getExitValue(Process process); private static native Integer getExitValue(Process process);
private static native Object getExitValueMutex(Process process); private static native Object getExitValueMutex(Process process);
......
...@@ -31,7 +31,13 @@ static int sdk_version() { ...@@ -31,7 +31,13 @@ static int sdk_version() {
return atoi(version); return atoi(version);
} }
jint Java_com_github_shadowsocks_jnihelper_sigterm(JNIEnv *env, jobject thiz, jobject process) { extern "C" {
JNIEXPORT jint JNICALL Java_com_github_shadowsocks_JniHelper_sigkill(JNIEnv *env, jobject thiz, jint pid) {
// Suppress "No such process" errors. We just want the process killed. It's fine if it's already killed.
return kill(pid, SIGKILL) == -1 && errno != ESRCH ? errno : 0;
}
JNIEXPORT jint JNICALL Java_com_github_shadowsocks_JniHelper_sigterm(JNIEnv *env, jobject thiz, jobject process) {
if (!env->IsInstanceOf(process, ProcessImpl)) { if (!env->IsInstanceOf(process, ProcessImpl)) {
THROW(env, "java/lang/ClassCastException", THROW(env, "java/lang/ClassCastException",
"Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted."); "Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted.");
...@@ -42,7 +48,8 @@ jint Java_com_github_shadowsocks_jnihelper_sigterm(JNIEnv *env, jobject thiz, jo ...@@ -42,7 +48,8 @@ jint Java_com_github_shadowsocks_jnihelper_sigterm(JNIEnv *env, jobject thiz, jo
return kill(pid, SIGTERM) == -1 && errno != ESRCH ? errno : 0; return kill(pid, SIGTERM) == -1 && errno != ESRCH ? errno : 0;
} }
jobject Java_com_github_shadowsocks_jnihelper_getExitValue(JNIEnv *env, jobject thiz, jobject process) { JNIEXPORT jobject JNICALL
Java_com_github_shadowsocks_JniHelper_getExitValue(JNIEnv *env, jobject thiz, jobject process) {
if (!env->IsInstanceOf(process, ProcessImpl)) { if (!env->IsInstanceOf(process, ProcessImpl)) {
THROW(env, "java/lang/ClassCastException", THROW(env, "java/lang/ClassCastException",
"Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted."); "Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted.");
...@@ -51,7 +58,8 @@ jobject Java_com_github_shadowsocks_jnihelper_getExitValue(JNIEnv *env, jobject ...@@ -51,7 +58,8 @@ jobject Java_com_github_shadowsocks_jnihelper_getExitValue(JNIEnv *env, jobject
return env->GetObjectField(process, ProcessImpl_exitValue); return env->GetObjectField(process, ProcessImpl_exitValue);
} }
jobject Java_com_github_shadowsocks_jnihelper_getExitValueMutex(JNIEnv *env, jobject thiz, jobject process) { JNIEXPORT jobject JNICALL
Java_com_github_shadowsocks_JniHelper_getExitValueMutex(JNIEnv *env, jobject thiz, jobject process) {
if (!env->IsInstanceOf(process, ProcessImpl)) { if (!env->IsInstanceOf(process, ProcessImpl)) {
THROW(env, "java/lang/ClassCastException", THROW(env, "java/lang/ClassCastException",
"Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted."); "Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted.");
...@@ -60,11 +68,12 @@ jobject Java_com_github_shadowsocks_jnihelper_getExitValueMutex(JNIEnv *env, job ...@@ -60,11 +68,12 @@ jobject Java_com_github_shadowsocks_jnihelper_getExitValueMutex(JNIEnv *env, job
return env->GetObjectField(process, ProcessImpl_exitValueMutex); return env->GetObjectField(process, ProcessImpl_exitValueMutex);
} }
void Java_com_github_shadowsocks_jnihelper_close(JNIEnv *env, jobject thiz, jint fd) { JNIEXPORT void JNICALL Java_com_github_shadowsocks_JniHelper_close(JNIEnv *env, jobject thiz, jint fd) {
close(fd); close(fd);
} }
jint Java_com_github_shadowsocks_jnihelper_sendfd(JNIEnv *env, jobject thiz, jint tun_fd, jstring path) { JNIEXPORT jint JNICALL
Java_com_github_shadowsocks_JniHelper_sendFd(JNIEnv *env, jobject thiz, jint tun_fd, jstring path) {
int fd; int fd;
struct sockaddr_un addr; struct sockaddr_un addr;
const char *sock_str = env->GetStringUTFChars(path, 0); const char *sock_str = env->GetStringUTFChars(path, 0);
...@@ -94,56 +103,6 @@ jint Java_com_github_shadowsocks_jnihelper_sendfd(JNIEnv *env, jobject thiz, jin ...@@ -94,56 +103,6 @@ jint Java_com_github_shadowsocks_jnihelper_sendfd(JNIEnv *env, jobject thiz, jin
env->ReleaseStringUTFChars(path, sock_str); env->ReleaseStringUTFChars(path, sock_str);
return 0; return 0;
} }
static const char *classPathName = "com/github/shadowsocks/JniHelper";
static JNINativeMethod method_table[] = {
{ "close", "(I)V",
(void*) Java_com_github_shadowsocks_jnihelper_close },
{ "sendFd", "(ILjava/lang/String;)I",
(void*) Java_com_github_shadowsocks_jnihelper_sendfd },
{ "sigterm", "(Ljava/lang/Process;)I",
(void*) Java_com_github_shadowsocks_jnihelper_sigterm },
{ "getExitValue", "(Ljava/lang/Process;)Ljava/lang/Integer;",
(void*) Java_com_github_shadowsocks_jnihelper_getExitValue },
{ "getExitValueMutex", "(Ljava/lang/Process;)Ljava/lang/Object;",
(void*) Java_com_github_shadowsocks_jnihelper_getExitValueMutex }
};
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
LOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
LOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* Register native methods for all classes we know about.
*
* returns JNI_TRUE on success.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName, method_table,
sizeof(method_table) / sizeof(method_table[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
} }
/* /*
...@@ -167,11 +126,6 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { ...@@ -167,11 +126,6 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
} }
env = uenv.env; env = uenv.env;
if (registerNatives(env) != JNI_TRUE) {
THROW(env, "java/lang/RuntimeException", "registerNativeMethods failed");
goto bail;
}
if (sdk_version() < 24) { if (sdk_version() < 24) {
if (!(ProcessImpl = env->FindClass("java/lang/ProcessManager$ProcessImpl"))) { if (!(ProcessImpl = env->FindClass("java/lang/ProcessManager$ProcessImpl"))) {
THROW(env, "java/lang/RuntimeException", "ProcessManager$ProcessImpl not found"); THROW(env, "java/lang/RuntimeException", "ProcessManager$ProcessImpl not found");
......
package com.github.shadowsocks.bg package com.github.shadowsocks.bg
import java.io.File
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit
import android.text.TextUtils
import android.util.Log import android.util.Log
import com.github.shadowsocks.JniHelper
import com.github.shadowsocks.ShadowsocksApplication.app
import scala.io.Source
/** /**
* @author Mygod * @author Mygod
...@@ -16,12 +21,16 @@ object Executable { ...@@ -16,12 +21,16 @@ object Executable {
val TUN2SOCKS = "libtun2socks.so" val TUN2SOCKS = "libtun2socks.so"
val OVERTURE = "liboverture.so" val OVERTURE = "liboverture.so"
val EXECUTABLES = Array(SS_LOCAL, SS_TUNNEL, PDNSD, REDSOCKS, TUN2SOCKS, OVERTURE) val EXECUTABLES = Set(SS_LOCAL, SS_TUNNEL, PDNSD, REDSOCKS, TUN2SOCKS, OVERTURE)
def killAll() { def killAll(): Unit =
val killer = new ProcessBuilder("killall" +: EXECUTABLES: _*).start() for (process <- new File("/proc").listFiles((_, name) => TextUtils.isDigitsOnly(name))) {
if (!killer.waitFor(1, TimeUnit.SECONDS)) val exe = new File(Source.fromFile(new File(process, "cmdline")).mkString.split("\0", 2).head)
Log.w("killall", "%s didn't exit within 1s. Post-crash clean-up may have failed." if (exe.getParent == app.getApplicationInfo.nativeLibraryDir && EXECUTABLES.contains(exe.getName))
.formatLocal(Locale.ENGLISH, killer.toString)) JniHelper.sigkill(process.getName.toInt) match {
} case 0 =>
case errno => Log.w("kill", "SIGKILL %s (%d) failed with %d"
.formatLocal(Locale.ENGLISH, exe.getAbsolutePath, process.getName, errno))
}
}
} }
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