Commit 89da9abd authored by Max Lv's avatar Max Lv

refine libexec

parent 5bb9ccd8
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.shadowsocks" package="com.github.shadowsocks"
android:installLocation="auto" android:installLocation="auto"
android:versionCode="4" android:versionCode="5"
android:versionName="1.1"> android:versionName="1.1.1">
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
......
APP_PLATFORM = anrdoid-8 APP_PLATFORM = android-9
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include <unistd.h> #include <unistd.h>
#include <termios.h> #include <termios.h>
#include <signal.h> #include <signal.h>
#include <stdio.h>
static jclass class_fileDescriptor; static jclass class_fileDescriptor;
static jfieldID field_fileDescriptor_descriptor; static jfieldID field_fileDescriptor_descriptor;
...@@ -98,24 +99,15 @@ static int throwOutOfMemoryError(JNIEnv *env, const char *message) ...@@ -98,24 +99,15 @@ static int throwOutOfMemoryError(JNIEnv *env, const char *message)
return env->ThrowNew(exClass, message); return env->ThrowNew(exClass, message);
} }
static int create_subprocess(const char *cmd, static int create_subprocess(const char *cmd, char *const argv[],
char *const argv[], char *const envp[], int* pProcessId) 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; pid_t pid;
int pfds[2];
int pfds2[2];
if (chmod(script, 0755) < 0) { pipe(pfds);
LOGE("error to chmod\n"); pipe(pfds2);
exit(-1);
}
int defout = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0600);
if(defout < 0) {
LOGE("open file error\n");
exit(-1);
}
pid = fork(); pid = fork();
...@@ -126,28 +118,41 @@ static int create_subprocess(const char *cmd, ...@@ -126,28 +118,41 @@ static int create_subprocess(const char *cmd,
if(pid == 0){ if(pid == 0){
//setsid();
dup2(defout, 1);
dup2(defout, 2);
if (envp) { if (envp) {
for (; *envp; ++envp) { for (; *envp; ++envp) {
putenv(*envp); putenv(*envp);
} }
} }
dup2(pfds[0], 0);
close(pfds[1]);
dup2(pfds2[1], 1);
dup2(pfds2[1], 2);
close(pfds2[0]);
execv(cmd, argv); execv(cmd, argv);
exit(-1);
fflush(NULL);
exit(0);
} else { } else {
*pProcessId = (int) pid; *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, 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) jintArray processIdArray)
{ {
const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0; const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0;
...@@ -157,6 +162,13 @@ static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz, ...@@ -157,6 +162,13 @@ static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz,
env->ReleaseStringCritical(cmd, str); 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; jsize size = args ? env->GetArrayLength(args) : 0;
char **argv = NULL; char **argv = NULL;
String8 tmp_8; String8 tmp_8;
...@@ -203,7 +215,8 @@ static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz, ...@@ -203,7 +215,8 @@ static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz,
} }
int procId; 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) { if (argv) {
for (char **tmp = argv; *tmp; ++tmp) { for (char **tmp = argv; *tmp; ++tmp) {
...@@ -311,7 +324,7 @@ static int register_FileDescriptor(JNIEnv *env) ...@@ -311,7 +324,7 @@ static int register_FileDescriptor(JNIEnv *env)
static const char *classPathName = "com/github/shadowsocks/Exec"; static const char *classPathName = "com/github/shadowsocks/Exec";
static JNINativeMethod method_table[] = { 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 }, (void*) android_os_Exec_createSubProcess },
{ "waitFor", "(I)I", { "waitFor", "(I)I",
(void*) android_os_Exec_waitFor}, (void*) android_os_Exec_waitFor},
......
No preview for this file type
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.github.shadowsocks</groupId> <groupId>com.github.shadowsocks</groupId>
<artifactId>shadowsocks</artifactId> <artifactId>shadowsocks</artifactId>
<version>1.1</version> <version>1.1.1</version>
<packaging>apk</packaging> <packaging>apk</packaging>
<name>Shadowsocks</name> <name>Shadowsocks</name>
......
...@@ -43,16 +43,18 @@ public class Exec { ...@@ -43,16 +43,18 @@ public class Exec {
* Callers are responsible for calling Exec.close() on the returned file * Callers are responsible for calling Exec.close() on the returned file
* descriptor. * descriptor.
* *
* @param cmd The command to execute * @param cmd The command to execute.
* @param args An array of arguments to the command * @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 * @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 * @param processId A one-element array to which the process ID of the started
* process will be written. * process will be written.
* @return the file descriptor of the started process. * @return File descriptor
*/ */
public static native FileDescriptor createSubprocess(String cmd, 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. * Send SIGHUP to a process group.
......
File mode changed from 100644 to 100755
package com.github.shadowsocks; package com.github.shadowsocks;
import android.app.Application; import android.app.Application;
import android.content.Context;
import com.google.analytics.tracking.android.EasyTracker; import com.google.analytics.tracking.android.EasyTracker;
public class ShadowsocksApplication extends Application { public class ShadowsocksApplication extends Application {
private static String sTmpDir;
public static String getTmpDir() {
return sTmpDir;
}
@Override @Override
public void onCreate() { public void onCreate() {
EasyTracker.getInstance().setContext(this); EasyTracker.getInstance().setContext(this);
sTmpDir = getCacheDir().getAbsolutePath();
} }
} }
...@@ -8,7 +8,10 @@ import android.graphics.drawable.Drawable; ...@@ -8,7 +8,10 @@ import android.graphics.drawable.Drawable;
import android.os.Environment; import android.os.Environment;
import android.util.Log; 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; import java.util.ArrayList;
public class Utils { public class Utils {
...@@ -17,51 +20,32 @@ public class Utils { ...@@ -17,51 +20,32 @@ public class Utils {
* Internal thread used to execute scripts (as root or not). * Internal thread used to execute scripts (as root or not).
*/ */
private static final class ScriptRunner extends Thread { private static final class ScriptRunner extends Thread {
private final File file; private final String scripts;
private final String script; private final StringBuilder result;
private final StringBuilder res;
private final boolean asroot; private final boolean asroot;
public int exitcode = -1; public int exitcode = -1;
// private Process exec;
private int mProcId;
private FileDescriptor mTermFd;
/** /**
* Creates a new script runner. * Creates a new script runner.
* *
* @param file temporary script file
* @param script script to run * @param script script to run
* @param res response output * @param result result output
* @param asroot if true, executes the script as root * @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) { boolean asroot) {
this.file = file; this.scripts = script;
this.script = script; this.result = result;
this.res = res;
this.asroot = asroot; this.asroot = asroot;
} }
private int createSubprocess(int[] processId, String cmd) { private FileDescriptor createSubprocess(int[] processId, String cmd) {
ArrayList<String> argList = parse(cmd); ArrayList<String> argList = parse(cmd);
String arg0 = argList.get(0); String arg0 = argList.get(0);
String[] args = argList.toArray(new String[1]); String[] args = argList.toArray(new String[1]);
mTermFd = Exec.createSubprocess(arg0, args, null, processId); return Exec.createSubprocess(arg0, args, null,
return processId[0]; scripts + "\nexit\n", processId);
}
/**
* Destroy this script runner
*/
@Override
public synchronized void destroy() {
try {
Exec.hangupProcessGroup(mProcId);
Exec.close(mTermFd);
} catch (NoClassDefFoundError ignore) {
// Nothing
}
} }
private ArrayList<String> parse(String cmd) { private ArrayList<String> parse(String cmd) {
...@@ -114,61 +98,48 @@ public class Utils { ...@@ -114,61 +98,48 @@ public class Utils {
@Override @Override
public void run() { public void run() {
FileDescriptor pipe = null;
int pid[] = new int[1];
pid[0] = -1;
try { 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) { if (this.asroot) {
// Create the "su" request to run the script // Create the "su" request to run the script
// exec = Runtime.getRuntime().exec( pipe = createSubprocess(pid, root_shell);
// root_shell + " -c " + abspath);
int pid[] = new int[1];
mProcId = createSubprocess(pid, root_shell + " -c "
+ abspath);
} else { } else {
// Create the "sh" request to run the script // Create the "sh" request to run the script
// exec = Runtime.getRuntime().exec(getShell() + " " + pipe = createSubprocess(pid, getShell());
// abspath); }
int pid[] = new int[1]; if (pipe == null) return;
mProcId = createSubprocess(pid, getShell() + " " + abspath);
if (pid[0] != -1) {
Exec.waitFor(pid[0]);
} }
final InputStream stdout = new FileInputStream(DEFOUT_FILE); final InputStream stdout = new FileInputStream(pipe);
final byte buf[] = new byte[8192]; final byte buf[] = new byte[8192];
int read = 0; int read = 0;
exitcode = Exec.waitFor(mProcId);
// Read stdout // Read stdout
while (stdout.available() > 0) { while (stdout.available() > 0) {
read = stdout.read(buf); read = stdout.read(buf);
if (res != null) if (result != null)
res.append(new String(buf, 0, read)); result.append(new String(buf, 0, read));
} }
} catch (Exception ex) { } catch (Exception ex) {
if (res != null) Log.e(TAG, "Cannot execute command", ex);
res.append("\n" + ex); if (result != null)
result.append("\n").append(ex);
} finally { } finally {
destroy(); if (pipe != null) {
Exec.close(pipe);
}
if (pid[0] != -1) {
Exec.hangupProcessGroup(pid[0]);
}
} }
} }
} }
...@@ -181,8 +152,6 @@ public class Utils { ...@@ -181,8 +152,6 @@ public class Utils {
public final static String ALTERNATIVE_ROOT = "/system/xbin/su"; 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 DEFAULT_IPTABLES = "/data/data/com.github.shadowsocks/iptables";
public final static String ALTERNATIVE_IPTABLES = "/system/bin/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; public final static int TIME_OUT = -99;
private static boolean initialized = false; private static boolean initialized = false;
...@@ -281,7 +250,7 @@ public class Utils { ...@@ -281,7 +250,7 @@ public class Utils {
public static boolean getHasRedirectSupport() { public static boolean getHasRedirectSupport() {
if (hasRedirectSupport == -1) if (hasRedirectSupport == -1)
initHasRedirectSupported(); initHasRedirectSupported();
return hasRedirectSupport == 1 ? true : false; return hasRedirectSupport == 1;
} }
public static String getIptables() { public static String getIptables() {
...@@ -338,7 +307,7 @@ public class Utils { ...@@ -338,7 +307,7 @@ public class Utils {
public static boolean isRoot() { public static boolean isRoot() {
if (isRoot != -1) if (isRoot != -1)
return isRoot == 1 ? true : false; return isRoot == 1;
// switch between binaries // switch between binaries
if (new File(DEFAULT_ROOT).exists()) { if (new File(DEFAULT_ROOT).exists()) {
...@@ -352,7 +321,7 @@ public class Utils { ...@@ -352,7 +321,7 @@ public class Utils {
String lines = null; String lines = null;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
String command = "ls /\n" + "exit\n"; String command = "ls /\n exit\n";
int exitcode = runScript(command, sb, 10 * 1000, true); int exitcode = runScript(command, sb, 10 * 1000, true);
...@@ -366,7 +335,7 @@ public class Utils { ...@@ -366,7 +335,7 @@ public class Utils {
isRoot = 1; isRoot = 1;
} }
return isRoot == 1 ? true : false; return isRoot == 1;
} }
public static boolean runCommand(String command) { public static boolean runCommand(String command) {
...@@ -392,8 +361,10 @@ public class Utils { ...@@ -392,8 +361,10 @@ public class Utils {
public static boolean runRootCommand(String command, int timeout) { public static boolean runRootCommand(String command, int timeout) {
if (!isRoot()) if (!isRoot()) {
Log.e(TAG, "Cannot get ROOT permission: " + root_shell);
return false; return false;
}
Log.d(TAG, command); Log.d(TAG, command);
...@@ -402,10 +373,9 @@ public class Utils { ...@@ -402,10 +373,9 @@ public class Utils {
return true; return true;
} }
private synchronized static int runScript(String script, StringBuilder res, private synchronized static int runScript(String script, StringBuilder result,
long timeout, boolean asroot) { long timeout, boolean asroot) {
final File file = new File(SCRIPT_FILE); final ScriptRunner runner = new ScriptRunner(script, result, asroot);
final ScriptRunner runner = new ScriptRunner(file, script, res, asroot);
runner.start(); runner.start();
try { try {
if (timeout > 0) { if (timeout > 0) {
......
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