Skip to content

Commit

Permalink
Use SIGKILL to kill old processes
Browse files Browse the repository at this point in the history
Fix shadowsocks#1455.

Note that in later Android versions, only processes under same uid will be listed and this will be quite efficient.
  • Loading branch information
Mygod committed Nov 18, 2017
1 parent ae171a7 commit dc2a5ca
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 68 deletions.
1 change: 1 addition & 0 deletions mobile/src/main/java/com/github/shadowsocks/JniHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public static boolean waitForCompat(Process process, long millis) throws Excepti
}
}

public static native int sigkill(int pid);
private static native int sigterm(Process process);
private static native Integer getExitValue(Process process);
private static native Object getExitValueMutex(Process process);
Expand Down
74 changes: 14 additions & 60 deletions mobile/src/main/jni/jni-helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ static int sdk_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)) {
THROW(env, "java/lang/ClassCastException",
"Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted.");
Expand All @@ -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;
}

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)) {
THROW(env, "java/lang/ClassCastException",
"Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted.");
Expand All @@ -51,7 +58,8 @@ jobject Java_com_github_shadowsocks_jnihelper_getExitValue(JNIEnv *env, jobject
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)) {
THROW(env, "java/lang/ClassCastException",
"Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted.");
Expand All @@ -60,11 +68,12 @@ jobject Java_com_github_shadowsocks_jnihelper_getExitValueMutex(JNIEnv *env, job
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);
}

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;
struct sockaddr_un addr;
const char *sock_str = env->GetStringUTFChars(path, 0);
Expand Down Expand Up @@ -94,56 +103,6 @@ jint Java_com_github_shadowsocks_jnihelper_sendfd(JNIEnv *env, jobject thiz, jin
env->ReleaseStringUTFChars(path, sock_str);
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;
}

/*
Expand All @@ -167,11 +126,6 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
}
env = uenv.env;

if (registerNatives(env) != JNI_TRUE) {
THROW(env, "java/lang/RuntimeException", "registerNativeMethods failed");
goto bail;
}

if (sdk_version() < 24) {
if (!(ProcessImpl = env->FindClass("java/lang/ProcessManager$ProcessImpl"))) {
THROW(env, "java/lang/RuntimeException", "ProcessManager$ProcessImpl not found");
Expand Down
25 changes: 17 additions & 8 deletions mobile/src/main/scala/com/github/shadowsocks/bg/Executable.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package com.github.shadowsocks.bg

import java.io.File
import java.util.Locale
import java.util.concurrent.TimeUnit

import android.text.TextUtils
import android.util.Log
import com.github.shadowsocks.JniHelper
import com.github.shadowsocks.ShadowsocksApplication.app

import scala.io.Source

/**
* @author Mygod
Expand All @@ -16,12 +21,16 @@ object Executable {
val TUN2SOCKS = "libtun2socks.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() {
val killer = new ProcessBuilder("killall" +: EXECUTABLES: _*).start()
if (!killer.waitFor(1, TimeUnit.SECONDS))
Log.w("killall", "%s didn't exit within 1s. Post-crash clean-up may have failed."
.formatLocal(Locale.ENGLISH, killer.toString))
}
def killAll(): Unit =
for (process <- new File("/proc").listFiles((_, name) => TextUtils.isDigitsOnly(name))) {
val exe = new File(Source.fromFile(new File(process, "cmdline")).mkString.split("\0", 2).head)
if (exe.getParent == app.getApplicationInfo.nativeLibraryDir && EXECUTABLES.contains(exe.getName))
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))
}
}
}

0 comments on commit dc2a5ca

Please sign in to comment.