From 89da9abda3fd866124cd9cb86ebcc2d71ca4100b Mon Sep 17 00:00:00 2001 From: Max Lv Date: Wed, 19 Dec 2012 15:32:31 +0800 Subject: [PATCH] refine libexec --- AndroidManifest.xml | 4 +- jni/Application.mk | 2 +- jni/termExec.cpp | 63 +++++---- libs/armeabi/libexec.so | Bin 17576 -> 17576 bytes pom.xml | 2 +- src/com/github/shadowsocks/Exec.java | 12 +- src/com/github/shadowsocks/Shadowsocks.java | 0 .../shadowsocks/ShadowsocksApplication.java | 8 ++ src/com/github/shadowsocks/Utils.java | 122 +++++++----------- 9 files changed, 103 insertions(+), 110 deletions(-) mode change 100644 => 100755 src/com/github/shadowsocks/Shadowsocks.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4d378c7b..89d4d320 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="5" + android:versionName="1.1.1"> diff --git a/jni/Application.mk b/jni/Application.mk index 05e88a76..2539ffd4 100644 --- a/jni/Application.mk +++ b/jni/Application.mk @@ -1 +1 @@ -APP_PLATFORM = anrdoid-8 +APP_PLATFORM = android-9 diff --git a/jni/termExec.cpp b/jni/termExec.cpp index e2c83d2b..d7089e11 100644 --- a/jni/termExec.cpp +++ b/jni/termExec.cpp @@ -49,6 +49,7 @@ #include #include #include +#include static jclass class_fileDescriptor; static jfieldID field_fileDescriptor_descriptor; @@ -98,24 +99,15 @@ static int throwOutOfMemoryError(JNIEnv *env, const char *message) return env->ThrowNew(exClass, message); } -static int create_subprocess(const char *cmd, - char *const argv[], char *const envp[], int* pProcessId) +static int create_subprocess(const char *cmd, char *const argv[], + char *const envp[], const char* scripts, int* pProcessId) { - char filename[] = "/data/data/com.github.shadowsocks/defout"; - char script[] = "/data/data/com.github.shadowsocks/script"; pid_t pid; + int pfds[2]; + int pfds2[2]; - if (chmod(script, 0755) < 0) { - LOGE("error to chmod\n"); - exit(-1); - } - - int defout = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0600); - - if(defout < 0) { - LOGE("open file error\n"); - exit(-1); - } + pipe(pfds); + pipe(pfds2); pid = fork(); @@ -126,28 +118,41 @@ static int create_subprocess(const char *cmd, if(pid == 0){ - //setsid(); - - dup2(defout, 1); - dup2(defout, 2); - if (envp) { for (; *envp; ++envp) { putenv(*envp); } } + dup2(pfds[0], 0); + close(pfds[1]); + + dup2(pfds2[1], 1); + dup2(pfds2[1], 2); + close(pfds2[0]); + execv(cmd, argv); - exit(-1); + + fflush(NULL); + + exit(0); + } else { *pProcessId = (int) pid; - return defout; + + dup2(pfds[1], 1); + close(pfds[0]); + + write(pfds[1], scripts, strlen(scripts)+1); + + close(pfds2[1]); + return pfds2[0]; } } static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz, - jstring cmd, jobjectArray args, jobjectArray envVars, + jstring cmd, jobjectArray args, jobjectArray envVars, jstring scripts, jintArray processIdArray) { const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0; @@ -157,6 +162,13 @@ static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz, env->ReleaseStringCritical(cmd, str); } + const jchar* str_scripts = scripts ? env->GetStringCritical(scripts, 0) : 0; + String8 scripts_8; + if (str_scripts) { + scripts_8.set(str_scripts, env->GetStringLength(scripts)); + env->ReleaseStringCritical(scripts, str_scripts); + } + jsize size = args ? env->GetArrayLength(args) : 0; char **argv = NULL; String8 tmp_8; @@ -203,7 +215,8 @@ static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz, } int procId; - int ptm = create_subprocess(cmd_8.string(), argv, envp, &procId); + int ptm = create_subprocess(cmd_8.string(), argv, envp, scripts_8.string(), + &procId); if (argv) { for (char **tmp = argv; *tmp; ++tmp) { @@ -311,7 +324,7 @@ static int register_FileDescriptor(JNIEnv *env) static const char *classPathName = "com/github/shadowsocks/Exec"; static JNINativeMethod method_table[] = { - { "createSubprocess", "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;[I)Ljava/io/FileDescriptor;", + { "createSubprocess", "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[I)Ljava/io/FileDescriptor;", (void*) android_os_Exec_createSubProcess }, { "waitFor", "(I)I", (void*) android_os_Exec_waitFor}, diff --git a/libs/armeabi/libexec.so b/libs/armeabi/libexec.so index e1bb3dd7304984bee1ac19f129612314fae18cdc..a9659fc7f245281b1f20f9816a614a497eef1e7b 100755 GIT binary patch delta 3480 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 diff --git a/pom.xml b/pom.xml index 2fd1e3fa..c4d95fa2 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.github.shadowsocks shadowsocks - 1.1 + 1.1.1 apk Shadowsocks diff --git a/src/com/github/shadowsocks/Exec.java b/src/com/github/shadowsocks/Exec.java index 090723ad..edc892bd 100755 --- a/src/com/github/shadowsocks/Exec.java +++ b/src/com/github/shadowsocks/Exec.java @@ -43,16 +43,18 @@ public class Exec { * Callers are responsible for calling Exec.close() on the returned file * descriptor. * - * @param cmd The command to execute - * @param args An array of arguments to the command + * @param cmd The command to execute. + * @param args An array of arguments to the command. * @param envVars An array of strings of the form "VAR=value" to be added to the - * environment of the process + * environment of the process. + * @param scripts The scripts to execute. * @param processId A one-element array to which the process ID of the started * process will be written. - * @return the file descriptor of the started process. + * @return File descriptor */ public static native FileDescriptor createSubprocess(String cmd, - String[] args, String[] envVars, int[] processId); + String[] args, String[] envVars, + String scripts, int[] processId); /** * Send SIGHUP to a process group. diff --git a/src/com/github/shadowsocks/Shadowsocks.java b/src/com/github/shadowsocks/Shadowsocks.java old mode 100644 new mode 100755 diff --git a/src/com/github/shadowsocks/ShadowsocksApplication.java b/src/com/github/shadowsocks/ShadowsocksApplication.java index b0ac76c6..ccb2a208 100755 --- a/src/com/github/shadowsocks/ShadowsocksApplication.java +++ b/src/com/github/shadowsocks/ShadowsocksApplication.java @@ -1,13 +1,21 @@ package com.github.shadowsocks; import android.app.Application; +import android.content.Context; import com.google.analytics.tracking.android.EasyTracker; public class ShadowsocksApplication extends Application { + private static String sTmpDir; + + public static String getTmpDir() { + return sTmpDir; + } + @Override public void onCreate() { EasyTracker.getInstance().setContext(this); + sTmpDir = getCacheDir().getAbsolutePath(); } } diff --git a/src/com/github/shadowsocks/Utils.java b/src/com/github/shadowsocks/Utils.java index 5470ce6a..390b9717 100755 --- a/src/com/github/shadowsocks/Utils.java +++ b/src/com/github/shadowsocks/Utils.java @@ -8,7 +8,10 @@ import android.graphics.drawable.Drawable; import android.os.Environment; import android.util.Log; -import java.io.*; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.InputStream; import java.util.ArrayList; public class Utils { @@ -17,51 +20,32 @@ public class Utils { * Internal thread used to execute scripts (as root or not). */ private static final class ScriptRunner extends Thread { - private final File file; - private final String script; - private final StringBuilder res; + private final String scripts; + private final StringBuilder result; private final boolean asroot; public int exitcode = -1; - // private Process exec; - private int mProcId; - private FileDescriptor mTermFd; /** * Creates a new script runner. * - * @param file temporary script file * @param script script to run - * @param res response output + * @param result result output * @param asroot if true, executes the script as root */ - public ScriptRunner(File file, String script, StringBuilder res, + public ScriptRunner(String script, StringBuilder result, boolean asroot) { - this.file = file; - this.script = script; - this.res = res; + this.scripts = script; + this.result = result; this.asroot = asroot; } - private int createSubprocess(int[] processId, String cmd) { + private FileDescriptor createSubprocess(int[] processId, String cmd) { ArrayList argList = parse(cmd); String arg0 = argList.get(0); String[] args = argList.toArray(new String[1]); - mTermFd = Exec.createSubprocess(arg0, args, null, processId); - return processId[0]; - } - - /** - * Destroy this script runner - */ - @Override - public synchronized void destroy() { - try { - Exec.hangupProcessGroup(mProcId); - Exec.close(mTermFd); - } catch (NoClassDefFoundError ignore) { - // Nothing - } + return Exec.createSubprocess(arg0, args, null, + scripts + "\nexit\n", processId); } private ArrayList parse(String cmd) { @@ -114,61 +98,48 @@ public class Utils { @Override public void run() { + FileDescriptor pipe = null; + int pid[] = new int[1]; + pid[0] = -1; + try { - new File(DEFOUT_FILE).createNewFile(); - file.createNewFile(); - final String abspath = file.getAbsolutePath(); - - // TODO: Rewrite this line - // make sure we have execution permission on the script file - // Runtime.getRuntime().exec("chmod 755 " + abspath).waitFor(); - - // Write the script to be executed - final OutputStreamWriter out = new OutputStreamWriter( - new FileOutputStream(file)); - out.write("#!/system/bin/sh\n"); - out.write(script); - if (!script.endsWith("\n")) - out.write("\n"); - out.write("exit\n"); - out.flush(); - out.close(); if (this.asroot) { // Create the "su" request to run the script - // exec = Runtime.getRuntime().exec( - // root_shell + " -c " + abspath); - - int pid[] = new int[1]; - mProcId = createSubprocess(pid, root_shell + " -c " - + abspath); + pipe = createSubprocess(pid, root_shell); } else { // Create the "sh" request to run the script - // exec = Runtime.getRuntime().exec(getShell() + " " + - // abspath); - - int pid[] = new int[1]; - mProcId = createSubprocess(pid, getShell() + " " + abspath); + pipe = createSubprocess(pid, getShell()); } - final InputStream stdout = new FileInputStream(DEFOUT_FILE); + if (pipe == null) return; + + if (pid[0] != -1) { + Exec.waitFor(pid[0]); + } + + final InputStream stdout = new FileInputStream(pipe); final byte buf[] = new byte[8192]; int read = 0; - exitcode = Exec.waitFor(mProcId); - // Read stdout while (stdout.available() > 0) { read = stdout.read(buf); - if (res != null) - res.append(new String(buf, 0, read)); + if (result != null) + result.append(new String(buf, 0, read)); } } catch (Exception ex) { - if (res != null) - res.append("\n" + ex); + Log.e(TAG, "Cannot execute command", ex); + if (result != null) + result.append("\n").append(ex); } finally { - destroy(); + if (pipe != null) { + Exec.close(pipe); + } + if (pid[0] != -1) { + Exec.hangupProcessGroup(pid[0]); + } } } } @@ -181,8 +152,6 @@ public class Utils { public final static String ALTERNATIVE_ROOT = "/system/xbin/su"; public final static String DEFAULT_IPTABLES = "/data/data/com.github.shadowsocks/iptables"; public final static String ALTERNATIVE_IPTABLES = "/system/bin/iptables"; - public final static String SCRIPT_FILE = "/data/data/com.github.shadowsocks/script"; - public final static String DEFOUT_FILE = "/data/data/com.github.shadowsocks/defout"; public final static int TIME_OUT = -99; private static boolean initialized = false; @@ -281,7 +250,7 @@ public class Utils { public static boolean getHasRedirectSupport() { if (hasRedirectSupport == -1) initHasRedirectSupported(); - return hasRedirectSupport == 1 ? true : false; + return hasRedirectSupport == 1; } public static String getIptables() { @@ -338,7 +307,7 @@ public class Utils { public static boolean isRoot() { if (isRoot != -1) - return isRoot == 1 ? true : false; + return isRoot == 1; // switch between binaries if (new File(DEFAULT_ROOT).exists()) { @@ -352,7 +321,7 @@ public class Utils { String lines = null; StringBuilder sb = new StringBuilder(); - String command = "ls /\n" + "exit\n"; + String command = "ls /\n exit\n"; int exitcode = runScript(command, sb, 10 * 1000, true); @@ -366,7 +335,7 @@ public class Utils { isRoot = 1; } - return isRoot == 1 ? true : false; + return isRoot == 1; } public static boolean runCommand(String command) { @@ -392,8 +361,10 @@ public class Utils { public static boolean runRootCommand(String command, int timeout) { - if (!isRoot()) + if (!isRoot()) { + Log.e(TAG, "Cannot get ROOT permission: " + root_shell); return false; + } Log.d(TAG, command); @@ -402,10 +373,9 @@ public class Utils { return true; } - private synchronized static int runScript(String script, StringBuilder res, + private synchronized static int runScript(String script, StringBuilder result, long timeout, boolean asroot) { - final File file = new File(SCRIPT_FILE); - final ScriptRunner runner = new ScriptRunner(file, script, res, asroot); + final ScriptRunner runner = new ScriptRunner(script, result, asroot); runner.start(); try { if (timeout > 0) {