Commit bb8cc7d4 authored by Mygod's avatar Mygod

Replace kill calls

parent 011ed722
......@@ -48,27 +48,6 @@ public class JniHelper {
System.loadLibrary("jni-helper");
}
@Deprecated // Use Process.destroy() since API 24
public static void sigtermCompat(@NonNull Process process) throws Exception {
if (Build.VERSION.SDK_INT >= 24) throw new UnsupportedOperationException("Never call this method in OpenJDK!");
int errno = sigterm(process);
if (errno != 0) throw new ErrnoException("kill", errno);
}
@Deprecated // only implemented for before API 24
public static boolean waitForCompat(@NonNull Process process, long millis) throws Exception {
if (Build.VERSION.SDK_INT >= 24) throw new UnsupportedOperationException("Never call this method in OpenJDK!");
final Object mutex = getExitValueMutex(process);
synchronized (mutex) {
if (getExitValue(process) == null) mutex.wait(millis);
return getExitValue(process) != null;
}
}
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);
public static native int sendFd(int fd, @NonNull String path);
@Nullable
public static native byte[] parseNumericAddress(@NonNull String str);
......
......@@ -20,11 +20,12 @@
package com.github.shadowsocks.bg
import android.system.ErrnoException
import android.system.Os
import android.text.TextUtils
import android.util.Log
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.Core.app
import com.github.shadowsocks.JniHelper
import java.io.File
import java.io.FileNotFoundException
......@@ -44,11 +45,13 @@ object Executable {
} catch (ignore: FileNotFoundException) {
continue
}.split(Character.MIN_VALUE, limit = 2).first())
if (exe.parent == app.applicationInfo.nativeLibraryDir && EXECUTABLES.contains(exe.name)) {
val errno = JniHelper.sigkill(process.name.toInt())
if (errno != 0) {
Crashlytics.log(Log.WARN, "kill",
"SIGKILL ${exe.absolutePath} (${process.name}) failed with $errno")
if (exe.parent == app.applicationInfo.nativeLibraryDir && EXECUTABLES.contains(exe.name)) try {
Os.kill(process.name.toInt(), 9) // SIGKILL
} catch (e: ErrnoException) {
if (e.errno != 3) { // ESRCH
e.printStackTrace()
Crashlytics.log(Log.WARN, "kill", "SIGKILL ${exe.absolutePath} (${process.name}) failed")
Crashlytics.logException(e)
}
}
}
......
......@@ -22,10 +22,11 @@ package com.github.shadowsocks.bg
import android.os.Build
import android.os.SystemClock
import android.system.ErrnoException
import android.system.Os
import android.util.Log
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.Core
import com.github.shadowsocks.JniHelper
import com.github.shadowsocks.utils.Commandline
import com.github.shadowsocks.utils.thread
import java.io.File
......@@ -37,6 +38,9 @@ import java.util.concurrent.atomic.AtomicReference
class GuardedProcessPool {
companion object Dummy : IOException("Oopsie the developer has made a no-no") {
private const val TAG = "GuardedProcessPool"
private val ProcessImpl by lazy { Class.forName("java/lang/ProcessManager\$ProcessImpl") }
private val pid by lazy { ProcessImpl.getField("pid").apply { isAccessible = true } }
private val exitValueMutex by lazy { ProcessImpl.getField("exitValueMutex").apply { isAccessible = true } }
}
private inner class Guard(private val cmd: List<String>, private val onRestartCallback: (() -> Unit)?) {
......@@ -88,9 +92,21 @@ class GuardedProcessPool {
pushException(e)
} finally {
if (process != null) {
if (Build.VERSION.SDK_INT < 24) @Suppress("DEPRECATION") {
JniHelper.sigtermCompat(process)
JniHelper.waitForCompat(process, 500)
if (Build.VERSION.SDK_INT < 24) {
val pid = pid.get(process) as Int
try {
Os.kill(pid, 15) // SIGTERM
} catch (e: ErrnoException) {
if (e.errno != 3) throw e // ESRCH
}
val mutex = exitValueMutex.get(process) as Object
synchronized(mutex) {
try {
process.exitValue()
} catch (e: IllegalThreadStateException) {
mutex.wait(500)
}
}
}
process.destroy()
process.waitFor() // ensure the process is destroyed
......
......@@ -2,20 +2,13 @@
#include "jni.h"
#include <android/log.h>
#include <sys/system_properties.h>
#include <algorithm>
#include <cerrno>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ancillary.h>
......@@ -24,50 +17,9 @@ using namespace std;
#define LOGI(...) do { __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__); } while(0)
#define LOGW(...) do { __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__); } while(0)
#define LOGE(...) do { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__); } while(0)
#define THROW(env, clazz, msg) do { env->ThrowNew(env->FindClass(clazz), msg); } while (0)
static int sdk_version;
static jclass ProcessImpl;
static jfieldID ProcessImpl_pid, ProcessImpl_exitValue, ProcessImpl_exitValueMutex;
#pragma clang diagnostic ignored "-Wunused-parameter"
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.");
return -1;
}
jint pid = env->GetIntField(process, ProcessImpl_pid);
// Suppress "No such process" errors. We just want the process killed. It's fine if it's already killed.
return kill(pid, SIGTERM) == -1 && errno != ESRCH ? errno : 0;
}
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.");
return NULL;
}
return env->GetObjectField(process, ProcessImpl_exitValue);
}
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.");
return NULL;
}
return env->GetObjectField(process, ProcessImpl_exitValueMutex);
}
JNIEXPORT jint JNICALL
Java_com_github_shadowsocks_JniHelper_sendFd(JNIEnv *env, jobject thiz, jint tun_fd, jstring path) {
int fd;
......@@ -120,50 +72,7 @@ Java_com_github_shadowsocks_JniHelper_parseNumericAddress(JNIEnv *env, jobject t
/*
* This is called by the VM when the shared library is first loaded.
*/
typedef union {
JNIEnv* env;
void* venv;
} UnionJNIEnvToVoid;
#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection"
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv* env = NULL;
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
THROW(env, "java/lang/RuntimeException", "GetEnv failed");
goto bail;
}
env = uenv.env;
char version[PROP_VALUE_MAX + 1];
__system_property_get("ro.build.version.sdk", version);
sdk_version = atoi(version);
#define FIND_CLASS(out, name) \
if (!(out = env->FindClass(name))) { \
THROW(env, "java/lang/RuntimeException", name " not found"); \
goto bail; \
} \
out = reinterpret_cast<jclass>(env->NewGlobalRef(reinterpret_cast<jobject>(out)))
#define GET_FIELD(out, clazz, name, sig) \
if (!(out = env->GetFieldID(clazz, name, sig))) { \
THROW(env, "java/lang/RuntimeException", "Field " #clazz "." name " with type " sig " not found"); \
goto bail; \
}
if (sdk_version < 24) {
FIND_CLASS(ProcessImpl, "java/lang/ProcessManager$ProcessImpl");
GET_FIELD(ProcessImpl_pid, ProcessImpl, "pid", "I")
GET_FIELD(ProcessImpl_exitValue, ProcessImpl, "exitValue", "Ljava/lang/Integer;")
GET_FIELD(ProcessImpl_exitValueMutex, ProcessImpl, "exitValueMutex", "Ljava/lang/Object;")
}
result = JNI_VERSION_1_6;
bail:
return result;
return JNI_VERSION_1_6;
}
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