Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
com.ccwangluo.accelerator
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
sheteng
com.ccwangluo.accelerator
Commits
185f32aa
Commit
185f32aa
authored
Mar 02, 2014
by
Max Lv
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix the selinux issue in galaxy s4
parent
cb31dd2a
Changes
16
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
2009 additions
and
798 deletions
+2009
-798
src/main/java/com/github/shadowsocks/Exec.java
src/main/java/com/github/shadowsocks/Exec.java
+0
-67
src/main/java/eu/chainfire/libsuperuser/Application.java
src/main/java/eu/chainfire/libsuperuser/Application.java
+79
-0
src/main/java/eu/chainfire/libsuperuser/Debug.java
src/main/java/eu/chainfire/libsuperuser/Debug.java
+241
-0
src/main/java/eu/chainfire/libsuperuser/Shell.java
src/main/java/eu/chainfire/libsuperuser/Shell.java
+1342
-0
src/main/java/eu/chainfire/libsuperuser/ShellNotClosedException.java
...va/eu/chainfire/libsuperuser/ShellNotClosedException.java
+29
-0
src/main/java/eu/chainfire/libsuperuser/ShellOnMainThreadException.java
...eu/chainfire/libsuperuser/ShellOnMainThreadException.java
+32
-0
src/main/java/eu/chainfire/libsuperuser/StreamGobbler.java
src/main/java/eu/chainfire/libsuperuser/StreamGobbler.java
+103
-0
src/main/jni/Android.mk
src/main/jni/Android.mk
+0
-15
src/main/jni/termExec.cpp
src/main/jni/termExec.cpp
+0
-428
src/main/scala/com/github/shadowsocks/Shadowsocks.scala
src/main/scala/com/github/shadowsocks/Shadowsocks.scala
+41
-31
src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
.../scala/com/github/shadowsocks/ShadowsocksNatService.scala
+23
-22
src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala
...la/com/github/shadowsocks/ShadowsocksRunnerActivity.scala
+1
-1
src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
.../scala/com/github/shadowsocks/ShadowsocksVpnService.scala
+16
-14
src/main/scala/com/github/shadowsocks/utils/Console.scala
src/main/scala/com/github/shadowsocks/utils/Console.scala
+77
-0
src/main/scala/com/github/shadowsocks/utils/Constants.scala
src/main/scala/com/github/shadowsocks/utils/Constants.scala
+9
-0
src/main/scala/com/github/shadowsocks/utils/Utils.scala
src/main/scala/com/github/shadowsocks/utils/Utils.scala
+16
-220
No files found.
src/main/java/com/github/shadowsocks/Exec.java
deleted
100755 → 0
View file @
cb31dd2a
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com.github.shadowsocks
;
import
java.io.FileDescriptor
;
/**
* Utility methods for creating and managing a subprocess.
* <p/>
* Note: The native methods access a package-private java.io.FileDescriptor
* field to get and set the raw Linux file descriptor. This might break if the
* implementation of java.io.FileDescriptor is changed.
*/
public
class
Exec
{
static
{
java
.
lang
.
System
.
loadLibrary
(
"exec"
);
}
/** Close a given file descriptor. */
public
static
native
void
close
(
FileDescriptor
fd
);
/**
* Create a subprocess. Differs from java.lang.ProcessBuilder in that a pty
* is used to communicate with the subprocess.
* <p/>
* Callers are responsible for calling Exec.close() on the returned file
* descriptor.
*
* @param rdt Whether redirect stdout and stderr
* @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.
* @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 File descriptor
*/
public
static
native
FileDescriptor
createSubprocess
(
int
rdt
,
String
cmd
,
String
[]
args
,
String
[]
envVars
,
String
scripts
,
int
[]
processId
);
/** Send SIGHUP to a process group. */
public
static
native
void
hangupProcessGroup
(
int
processId
);
/**
* Causes the calling thread to wait for the process associated with the
* receiver to finish executing.
*
* @return The exit value of the Process being waited on
*/
public
static
native
int
waitFor
(
int
processId
);
}
src/main/java/eu/chainfire/libsuperuser/Application.java
0 → 100644
View file @
185f32aa
/*
* Copyright (C) 2012-2013 Jorrit "Chainfire" Jongma
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
eu.chainfire.libsuperuser
;
import
android.content.Context
;
import
android.os.Handler
;
import
android.widget.Toast
;
/**
* Base application class to extend from, solving some issues with
* toasts and AsyncTasks you are likely to run into
*/
public
class
Application
extends
android
.
app
.
Application
{
/**
* Shows a toast message
*
* @param context Any context belonging to this application
* @param message The message to show
*/
public
static
void
toast
(
Context
context
,
String
message
)
{
// this is a static method so it is easier to call,
// as the context checking and casting is done for you
if
(
context
==
null
)
return
;
if
(!(
context
instanceof
Application
))
{
context
=
context
.
getApplicationContext
();
}
if
(
context
instanceof
Application
)
{
final
Context
c
=
context
;
final
String
m
=
message
;
((
Application
)
context
).
runInApplicationThread
(
new
Runnable
()
{
@Override
public
void
run
()
{
Toast
.
makeText
(
c
,
m
,
Toast
.
LENGTH_LONG
).
show
();
}
});
}
}
private
static
Handler
mApplicationHandler
=
new
Handler
();
/**
* Run a runnable in the main application thread
*
* @param r Runnable to run
*/
public
void
runInApplicationThread
(
Runnable
r
)
{
mApplicationHandler
.
post
(
r
);
}
@Override
public
void
onCreate
()
{
super
.
onCreate
();
try
{
// workaround bug in AsyncTask, can show up (for example) when you toast from a service
// this makes sure AsyncTask's internal handler is created from the right (main) thread
Class
.
forName
(
"android.os.AsyncTask"
);
}
catch
(
ClassNotFoundException
e
)
{
}
}
}
src/main/java/eu/chainfire/libsuperuser/Debug.java
0 → 100644
View file @
185f32aa
/*
* Copyright (C) 2012-2013 Jorrit "Chainfire" Jongma
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
eu.chainfire.libsuperuser
;
import
android.os.Looper
;
import
android.util.Log
;
import
com.github.shadowsocks.BuildConfig
;
/**
* Utility class for logging and debug features that (by default) does nothing when not in debug mode
*/
public
class
Debug
{
// ----- DEBUGGING -----
private
static
boolean
debug
=
BuildConfig
.
DEBUG
;
/**
* <p>Enable or disable debug mode</p>
*
* <p>By default, debug mode is enabled for development
* builds and disabled for exported APKs - see
* BuildConfig.DEBUG</p>
*
* @param enabled Enable debug mode ?
*/
public
static
void
setDebug
(
boolean
enable
)
{
debug
=
enable
;
}
/**
* <p>Is debug mode enabled ?</p>
*
* @return Debug mode enabled
*/
public
static
boolean
getDebug
()
{
return
debug
;
}
// ----- LOGGING -----
public
interface
OnLogListener
{
public
void
onLog
(
int
type
,
String
typeIndicator
,
String
message
);
}
public
static
final
String
TAG
=
"libsuperuser"
;
public
static
final
int
LOG_GENERAL
=
0x0001
;
public
static
final
int
LOG_COMMAND
=
0x0002
;
public
static
final
int
LOG_OUTPUT
=
0x0004
;
public
static
final
int
LOG_NONE
=
0x0000
;
public
static
final
int
LOG_ALL
=
0xFFFF
;
private
static
int
logTypes
=
LOG_ALL
;
private
static
OnLogListener
logListener
=
null
;
/**
* <p>Log a message (internal)</p>
*
* <p>Current debug and enabled logtypes decide what gets logged -
* even if a custom callback is registered</p>
*
* @param type Type of message to log
* @param typeIndicator String indicator for message type
* @param message The message to log
*/
private
static
void
logCommon
(
int
type
,
String
typeIndicator
,
String
message
)
{
if
(
debug
&&
((
logTypes
&
type
)
==
type
))
{
if
(
logListener
!=
null
)
{
logListener
.
onLog
(
type
,
typeIndicator
,
message
);
}
else
{
Log
.
d
(
TAG
,
"["
+
TAG
+
"]["
+
typeIndicator
+
"]"
+
(!
message
.
startsWith
(
"["
)
&&
!
message
.
startsWith
(
" "
)
?
" "
:
""
)
+
message
);
}
}
}
/**
* <p>Log a "general" message</p>
*
* <p>These messages are infrequent and mostly occur at startup/shutdown or on error</p>
*
* @param message The message to log
*/
public
static
void
log
(
String
message
)
{
logCommon
(
LOG_GENERAL
,
"G"
,
message
);
}
/**
* <p>Log a "per-command" message</p>
*
* <p>This could produce a lot of output if the client runs many commands in the session</p>
*
* @param message The message to log
*/
public
static
void
logCommand
(
String
message
)
{
logCommon
(
LOG_COMMAND
,
"C"
,
message
);
}
/**
* <p>Log a line of stdout/stderr output</p>
*
* <p>This could produce a lot of output if the shell commands are noisy</p>
*
* @param message The message to log
*/
public
static
void
logOutput
(
String
message
)
{
logCommon
(
LOG_OUTPUT
,
"O"
,
message
);
}
/**
* <p>Enable or disable logging specific types of message</p>
*
* <p>You may | (or) LOG_* constants together. Note that
* debug mode must also be enabled for actual logging to
* occur.</p>
*
* @param type LOG_* constants
* @param enabled Enable or disable
*/
public
static
void
setLogTypeEnabled
(
int
type
,
boolean
enable
)
{
if
(
enable
)
{
logTypes
|=
type
;
}
else
{
logTypes
&=
~
type
;
}
}
/**
* <p>Is logging for specific types of messages enabled ?</p>
*
* <p>You may | (or) LOG_* constants together, to learn if
* <b>all</b> passed message types are enabled for logging. Note
* that debug mode must also be enabled for actual logging
* to occur.</p>
*
* @param type LOG_* constants
*/
public
static
boolean
getLogTypeEnabled
(
int
type
)
{
return
((
logTypes
&
type
)
==
type
);
}
/**
* <p>Is logging for specific types of messages enabled ?</p>
*
* <p>You may | (or) LOG_* constants together, to learn if
* <b>all</b> message types are enabled for logging. Takes
* debug mode into account for the result.</p>
*
* @param type LOG_* constants
*/
public
static
boolean
getLogTypeEnabledEffective
(
int
type
)
{
return
getDebug
()
&&
getLogTypeEnabled
(
type
);
}
/**
* <p>Register a custom log handler</p>
*
* <p>Replaces the log method (write to logcat) with your own
* handler. Whether your handler gets called is still dependent
* on debug mode and message types being enabled for logging.</p>
*
* @param onLogListener Custom log listener or NULL to revert to default
*/
public
static
void
setOnLogListener
(
OnLogListener
onLogListener
)
{
logListener
=
onLogListener
;
}
/**
* <p>Get the currently registered custom log handler</p>
*
* @return Current custom log handler or NULL if none is present
*/
public
static
OnLogListener
getOnLogListener
()
{
return
logListener
;
}
// ----- SANITY CHECKS -----
private
static
boolean
sanityChecks
=
true
;
/**
* <p>Enable or disable sanity checks</p>
*
* <p>Enables or disables the library crashing when su is called
* from the main thread.</p>
*
* @param enabled Enable or disable
*/
public
static
void
setSanityChecksEnabled
(
boolean
enable
)
{
sanityChecks
=
enable
;
}
/**
* <p>Are sanity checks enabled ?</p>
*
* <p>Note that debug mode must also be enabled for actual
* sanity checks to occur.</p>
*
* @return True if enabled
*/
public
static
boolean
getSanityChecksEnabled
()
{
return
sanityChecks
;
}
/**
* <p>Are sanity checks enabled ?</p>
*
* <p>Takes debug mode into account for the result.</p>
*
* @return True if enabled
*/
public
static
boolean
getSanityChecksEnabledEffective
()
{
return
getDebug
()
&&
getSanityChecksEnabled
();
}
/**
* <p>Are we running on the main thread ?</p>
*
* @return Running on main thread ?
*/
public
static
boolean
onMainThread
()
{
return
((
Looper
.
myLooper
()
!=
null
)
&&
(
Looper
.
myLooper
()
==
Looper
.
getMainLooper
()));
}
}
src/main/java/eu/chainfire/libsuperuser/Shell.java
0 → 100644
View file @
185f32aa
This diff is collapsed.
Click to expand it.
src/main/java/eu/chainfire/libsuperuser/ShellNotClosedException.java
0 → 100644
View file @
185f32aa
/*
* Copyright (C) 2012-2013 Jorrit "Chainfire" Jongma
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
eu.chainfire.libsuperuser
;
/**
* Exception class used to notify developer that a shell was not close()d
*/
@SuppressWarnings
(
"serial"
)
public
class
ShellNotClosedException
extends
RuntimeException
{
public
static
final
String
EXCEPTION_NOT_CLOSED
=
"Application did not close() interactive shell"
;
public
ShellNotClosedException
()
{
super
(
EXCEPTION_NOT_CLOSED
);
}
}
src/main/java/eu/chainfire/libsuperuser/ShellOnMainThreadException.java
0 → 100644
View file @
185f32aa
/*
* Copyright (C) 2012-2013 Jorrit "Chainfire" Jongma
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
eu.chainfire.libsuperuser
;
/**
* Exception class used to crash application when shell commands are executed
* from the main thread, and we are in debug mode.
*/
@SuppressWarnings
(
"serial"
)
public
class
ShellOnMainThreadException
extends
RuntimeException
{
public
static
final
String
EXCEPTION_COMMAND
=
"Application attempted to run a shell command from the main thread"
;
public
static
final
String
EXCEPTION_NOT_IDLE
=
"Application attempted to wait for a non-idle shell to close on the main thread"
;
public
static
final
String
EXCEPTION_WAIT_IDLE
=
"Application attempted to wait for a shell to become idle on the main thread"
;
public
ShellOnMainThreadException
(
String
message
)
{
super
(
message
);
}
}
src/main/java/eu/chainfire/libsuperuser/StreamGobbler.java
0 → 100644
View file @
185f32aa
/*
* Copyright (C) 2012-2013 Jorrit "Chainfire" Jongma
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
eu.chainfire.libsuperuser
;
import
java.io.BufferedReader
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.util.List
;
/**
* Thread utility class continuously reading from an InputStream
*/
public
class
StreamGobbler
extends
Thread
{
/**
* Line callback interface
*/
public
interface
OnLineListener
{
/**
* <p>Line callback</p>
*
* <p>This callback should process the line as quickly as possible.
* Delays in this callback may pause the native process or even
* result in a deadlock</p>
*
* @param line String that was gobbled
*/
public
void
onLine
(
String
line
);
}
private
String
shell
=
null
;
private
BufferedReader
reader
=
null
;
private
List
<
String
>
writer
=
null
;
private
OnLineListener
listener
=
null
;
/**
* <p>StreamGobbler constructor</p>
*
* <p>We use this class because shell STDOUT and STDERR should be read as quickly as
* possible to prevent a deadlock from occurring, or Process.waitFor() never
* returning (as the buffer is full, pausing the native process)</p>
*
* @param shell Name of the shell
* @param inputStream InputStream to read from
* @param outputList List<String> to write to, or null
*/
public
StreamGobbler
(
String
shell
,
InputStream
inputStream
,
List
<
String
>
outputList
)
{
this
.
shell
=
shell
;
reader
=
new
BufferedReader
(
new
InputStreamReader
(
inputStream
));
writer
=
outputList
;
}
/**
* <p>StreamGobbler constructor</p>
*
* <p>We use this class because shell STDOUT and STDERR should be read as quickly as
* possible to prevent a deadlock from occurring, or Process.waitFor() never
* returning (as the buffer is full, pausing the native process)</p>
*
* @param shell Name of the shell
* @param inputStream InputStream to read from
* @param onLineListener OnLineListener callback
*/
public
StreamGobbler
(
String
shell
,
InputStream
inputStream
,
OnLineListener
onLineListener
)
{
this
.
shell
=
shell
;
reader
=
new
BufferedReader
(
new
InputStreamReader
(
inputStream
));
listener
=
onLineListener
;
}
@Override
public
void
run
()
{
// keep reading the InputStream until it ends (or an error occurs)
try
{
String
line
=
null
;
while
((
line
=
reader
.
readLine
())
!=
null
)
{
Debug
.
logOutput
(
String
.
format
(
"[%s] %s"
,
shell
,
line
));
if
(
writer
!=
null
)
writer
.
add
(
line
);
if
(
listener
!=
null
)
listener
.
onLine
(
line
);
}
}
catch
(
IOException
e
)
{
}
// make sure our stream is closed and resources will be freed
try
{
reader
.
close
();
}
catch
(
IOException
e
)
{
}
}
}
src/main/jni/Android.mk
View file @
185f32aa
...
...
@@ -130,21 +130,6 @@ LOCAL_LDLIBS := -llog
include
$(BUILD_EXECUTABLE)
########################################################
## termExec
########################################################
include
$(CLEAR_VARS)
LOCAL_MODULE
:=
exec
LOCAL_SRC_FILES
:=
\
termExec.cpp
LOCAL_LDLIBS
:=
-ldl
-llog
include
$(BUILD_SHARED_LIBRARY)
########################################################
## system
########################################################
...
...
src/main/jni/termExec.cpp
deleted
100644 → 0
View file @
cb31dd2a
This diff is collapsed.
Click to expand it.
src/main/scala/com/github/shadowsocks/Shadowsocks.scala
View file @
185f32aa
...
...
@@ -49,7 +49,6 @@ import android.view._
import
android.widget._
import
com.google.analytics.tracking.android.
{
MapBuilder
,
EasyTracker
}
import
de.keyboardsurfer.android.widget.crouton.
{
Crouton
,
Style
,
Configuration
}
import
java.io._
import
java.util.Hashtable
import
net.saik0.android.unifiedpreference.UnifiedPreferenceFragment
import
net.saik0.android.unifiedpreference.UnifiedSherlockPreferenceActivity
...
...
@@ -63,7 +62,7 @@ import com.google.ads.{AdRequest, AdSize, AdView}
import
net.simonvt.menudrawer.MenuDrawer
import
com.github.shadowsocks.database._
import
scala.collection.mutable.
ListBuffer
import
scala.collection.mutable.
{
ArrayBuffer
,
ListBuffer
}
import
com.github.shadowsocks.database.Profile
import
com.nostra13.universalimageloader.core.download.BaseImageDownloader
import
com.github.shadowsocks.preferences.
{
ProfileEditTextPreference
,
PasswordEditTextPreference
,
SummaryEditTextPreference
}
...
...
@@ -72,6 +71,11 @@ import com.github.shadowsocks.database.Item
import
com.github.shadowsocks.database.Category
import
com.google.zxing.integration.android.IntentIntegrator
import
com.github.shadowsocks.aidl.
{
IShadowsocksServiceCallback
,
IShadowsocksService
}
import
java.io._
import
scala.Some
import
com.github.shadowsocks.database.Item
import
com.github.shadowsocks.database.Category
import
com.github.shadowsocks.utils.Console
class
ProfileIconDownloader
(
context
:
Context
,
connectTimeout
:
Int
,
readTimeout
:
Int
)
extends
BaseImageDownloader
(
context
,
connectTimeout
,
readTimeout
)
{
...
...
@@ -118,13 +122,17 @@ object Typefaces {
object
Shadowsocks
{
// Constants
val
TAG
=
"Shadowsocks"
val
REQUEST_CONNECT
=
1
val
PREFS_NAME
=
"Shadowsocks"
val
PROXY_PREFS
=
Array
(
Key
.
profileName
,
Key
.
proxy
,
Key
.
remotePort
,
Key
.
localPort
,
Key
.
sitekey
,
Key
.
encMethod
)
val
FEATRUE_PREFS
=
Array
(
Key
.
isGFWList
,
Key
.
isGlobalProxy
,
Key
.
proxyedApps
,
Key
.
isTrafficStat
,
Key
.
isUdpDns
,
Key
.
isAutoConnect
)
val
TAG
=
"Shadowsocks"
val
REQUEST_CONNECT
=
1
val
EXECUTABLES
=
Array
(
Executable
.
IPTABLES
,
Executable
.
PDNSD
,
Executable
.
REDSOCKS
,
Executable
.
SS_LOCAL
,
Executable
.
SS_TUNNEL
,
Executable
.
TUN2SOCKS
)
// Helper functions
def
updateListPreference
(
pref
:
Preference
,
value
:
String
)
{
...
...
@@ -415,26 +423,28 @@ class Shadowsocks
}
private
def
crash_recovery
()
{
val
sb
=
new
StringBuilder
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`"
).
append
(
"\n"
)
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/ss-local.pid`"
).
append
(
"\n"
)
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/ss-tunnel.pid`"
).
append
(
"\n"
)
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/tun2socks.pid`"
).
append
(
"\n"
)
sb
.
append
(
"killall -9 pdnsd"
).
append
(
"\n"
)
sb
.
append
(
"killall -9 ss-local"
).
append
(
"\n"
)
sb
.
append
(
"killall -9 ss-tunnel"
).
append
(
"\n"
)
sb
.
append
(
"killall -9 tun2socks"
).
append
(
"\n"
)
sb
.
append
(
"rm /data/data/com.github.shadowsocks/pdnsd.conf"
).
append
(
"\n"
)
sb
.
append
(
"rm /data/data/com.github.shadowsocks/pdnsd.cache"
).
append
(
"\n"
)
Utils
.
runCommand
(
sb
.
toString
())
sb
.
clear
()
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`"
).
append
(
"\n"
)
sb
.
append
(
"killall -9 redsocks"
).
append
(
"\n"
)
sb
.
append
(
"rm /data/data/com.github.shadowsocks/redsocks.conf"
).
append
(
"\n"
)
sb
.
append
(
Utils
.
getIptables
).
append
(
" -t nat -F OUTPUT"
).
append
(
"\n"
)
Utils
.
runRootCommand
(
sb
.
toString
())
val
ab
=
new
ArrayBuffer
[
String
]
ab
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`"
)
ab
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/ss-local.pid`"
)
ab
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/ss-tunnel.pid`"
)
ab
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/tun2socks.pid`"
)
ab
.
append
(
"killall -9 pdnsd"
)
ab
.
append
(
"killall -9 ss-local"
)
ab
.
append
(
"killall -9 ss-tunnel"
)
ab
.
append
(
"killall -9 tun2socks"
)
ab
.
append
(
"rm /data/data/com.github.shadowsocks/pdnsd.conf"
)
ab
.
append
(
"rm /data/data/com.github.shadowsocks/pdnsd.cache"
)
Console
.
runCommand
(
ab
.
toArray
)
ab
.
clear
()
ab
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`"
)
ab
.
append
(
"killall -9 redsocks"
)
ab
.
append
(
"rm /data/data/com.github.shadowsocks/redsocks.conf"
)
ab
.
append
(
Utils
.
getIptables
+
" -t nat -F OUTPUT"
)
Console
.
runRootCommand
(
ab
.
toArray
)
}
private
def
getVersionName
:
String
=
{
...
...
@@ -587,7 +597,7 @@ class Shadowsocks
// Bind to the service
spawn
{
val
isRoot
=
Utils
.
get
Root
val
isRoot
=
Console
.
is
Root
handler
.
post
(
new
Runnable
{
override
def
run
()
{
status
.
edit
.
putBoolean
(
Key
.
isRoot
,
isRoot
).
commit
()
...
...
@@ -908,12 +918,12 @@ class Shadowsocks
def
reset
()
{
crash_recovery
()
copyAssets
(
System
.
getABI
)
Utils
.
runCommand
(
"chmod 755 /data/data/com.github.shadowsocks/iptables\n"
+
"chmod 755 /data/data/com.github.shadowsocks/redsocks\n"
+
"chmod 755 /data/data/com.github.shadowsocks/pdnsd\n"
+
"chmod 755 /data/data/com.github.shadowsocks/ss-local\n"
+
"chmod 755 /data/data/com.github.shadowsocks/ss-tunnel\n"
+
"chmod 755 /data/data/com.github.shadowsocks/tun2socks\n"
)
val
ab
=
new
ArrayBuffer
[
String
]
for
(
executable
<-
Shadowsocks
.
EXECUTABLES
)
{
ab
.
append
(
"chmod 755 "
+
Path
.
BASE
+
executable
)
}
Console
.
runCommand
(
ab
.
toArray
)
}
private
def
recovery
()
{
...
...
src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
View file @
185f32aa
...
...
@@ -50,7 +50,6 @@ import android.os._
import
android.support.v4.app.NotificationCompat
import
android.util.Log
import
com.google.analytics.tracking.android.
{
Fields
,
MapBuilder
,
EasyTracker
}
import
java.io._
import
java.lang.reflect.InvocationTargetException
import
java.lang.reflect.Method
import
org.apache.http.conn.util.InetAddressUtils
...
...
@@ -62,6 +61,8 @@ import com.github.shadowsocks.utils._
import
scala.Some
import
android.graphics.Color
import
com.github.shadowsocks.aidl.Config
import
scala.collection.mutable.ArrayBuffer
import
java.io.File
case
class
TrafficStat
(
tx
:
Long
,
rx
:
Long
,
timestamp
:
Long
)
...
...
@@ -107,7 +108,7 @@ class ShadowsocksNatService extends Service with BaseService {
Path
.
BASE
+
"ss-local.pid"
)
.
format
(
config
.
proxy
,
config
.
remotePort
,
config
.
localPort
,
config
.
sitekey
,
config
.
encMethod
)
if
(
BuildConfig
.
DEBUG
)
Log
.
d
(
TAG
,
cmd
)
Utils
.
runCommand
(
cmd
)
System
.
exec
(
cmd
)
}
def
startDnsDaemon
()
{
...
...
@@ -123,8 +124,8 @@ class ShadowsocksNatService extends Service with BaseService {
})
Path
.
BASE
+
"pdnsd -c "
+
Path
.
BASE
+
"pdnsd.conf"
}
Utils
.
runCommand
(
cmd
)
if
(
BuildConfig
.
DEBUG
)
Log
.
d
(
TAG
,
cmd
)
System
.
exec
(
cmd
)
}
def
getVersionName
:
String
=
{
...
...
@@ -146,7 +147,7 @@ class ShadowsocksNatService extends Service with BaseService {
ConfigUtils
.
printToFile
(
new
File
(
Path
.
BASE
+
"redsocks.conf"
))(
p
=>
{
p
.
println
(
conf
)
})
Utils
.
runRootCommand
(
cmd
)
Console
.
runRootCommand
(
cmd
)
}
/** Called when the activity is first created. */
...
...
@@ -267,24 +268,25 @@ class ShadowsocksNatService extends Service with BaseService {
}
def
killProcesses
()
{
Utils
.
runRootCommand
(
Utils
.
getIptables
+
" -t nat -F OUTPUT"
)
Console
.
runRootCommand
(
Utils
.
getIptables
+
" -t nat -F OUTPUT"
)
val
ab
=
new
ArrayBuffer
[
String
]
val
sb
=
new
StringBuilder
ab
.
append
(
"kill -9 `cat "
+
Path
.
BASE
+
"redsocks.pid`"
)
ab
.
append
(
"killall -9 redsocks"
)
sb
++=
"kill -9 `cat "
++=
Path
.
BASE
++=
"redsocks.pid`"
++=
"\n"
sb
++=
"killall -9 redsocks"
++=
"\n"
Utils
.
runRootCommand
(
sb
.
toString
())
Console
.
runRootCommand
(
ab
.
toArray
)
s
b
.
clear
()
a
b
.
clear
()
sb
++=
"kill -9 `cat "
++=
Path
.
BASE
++=
"pdnsd.pid`"
++=
"\n"
sb
++=
"killall -9 pdnsd"
++=
"\n"
sb
++=
"kill -9 `cat "
++=
Path
.
BASE
++=
"ss-local.pid`"
++=
"\n"
sb
++=
"killall -9 ss-local"
++=
"\n"
sb
++=
"kill -9 `cat "
++=
Path
.
BASE
++=
"ss-tunnel.pid`"
++=
"\n"
sb
++=
"killall -9 ss-tunnel"
++=
"\n"
ab
.
append
(
"kill -9 `cat "
+
Path
.
BASE
+
"ss-local.pid`"
)
ab
.
append
(
"killall -9 ss-local"
)
ab
.
append
(
"kill -9 `cat "
+
Path
.
BASE
+
"ss-tunnel.pid`"
)
ab
.
append
(
"killall -9 ss-tunnel"
)
ab
.
append
(
"kill -9 `cat "
+
Path
.
BASE
+
"pdnsd.pid`"
)
ab
.
append
(
"killall -9 pdnsd"
)
Utils
.
runCommand
(
sb
.
toString
()
)
Console
.
runRootCommand
(
ab
.
toArray
)
}
override
def
onStartCommand
(
intent
:
Intent
,
flags
:
Int
,
startId
:
Int
)
:
Int
=
{
...
...
@@ -292,8 +294,7 @@ class ShadowsocksNatService extends Service with BaseService {
}
def
flushDNS
()
{
Utils
.
runRootCommand
(
"ndc resolver flushdefaultif\n"
+
"ndc resolver flushif wlan0\n"
)
Console
.
runRootCommand
(
Array
(
"ndc resolver flushdefaultif"
,
"ndc resolver flushif wlan0"
))
}
def
setupIptables
:
Boolean
=
{
...
...
@@ -354,9 +355,9 @@ class ShadowsocksNatService extends Service with BaseService {
}
}
val
init_rules
:
String
=
init_sb
.
toString
()
Utils
.
runRootCommand
(
init_rules
,
30
*
1000
)
Console
.
runRootCommand
(
init_rules
)
val
redt_rules
:
String
=
http_sb
.
toString
()
Utils
.
runRootCommand
(
redt_rules
)
Console
.
runRootCommand
(
redt_rules
)
true
}
...
...
src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala
View file @
185f32aa
...
...
@@ -73,7 +73,7 @@ class ShadowsocksRunnerActivity extends Activity {
def
isVpnEnabled
:
Boolean
=
{
if
(
vpnEnabled
<
0
)
{
vpnEnabled
=
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
ICE_CREAM_SANDWICH
&&
!
Utils
.
get
Root
)
{
&&
!
Console
.
is
Root
)
{
1
}
else
{
0
...
...
src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
View file @
185f32aa
...
...
@@ -47,7 +47,6 @@ import android.os._
import
android.support.v4.app.NotificationCompat
import
android.util.Log
import
com.google.analytics.tracking.android.
{
Fields
,
MapBuilder
,
EasyTracker
}
import
java.io._
import
android.net.VpnService
import
org.apache.http.conn.util.InetAddressUtils
import
android.os.Message
...
...
@@ -57,6 +56,8 @@ import java.net.InetAddress
import
com.github.shadowsocks.utils._
import
scala.Some
import
com.github.shadowsocks.aidl.
{
IShadowsocksService
,
Config
}
import
scala.collection.mutable.ArrayBuffer
import
java.io.File
class
ShadowsocksVpnService
extends
VpnService
with
BaseService
{
...
...
@@ -96,7 +97,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
})
Path
.
BASE
+
"pdnsd -c "
+
Path
.
BASE
+
"pdnsd.conf"
}
Utils
.
runCommand
(
cmd
)
if
(
BuildConfig
.
DEBUG
)
Log
.
d
(
TAG
,
cmd
)
System
.
exec
(
cmd
)
}
def
getVersionName
:
String
=
{
...
...
@@ -248,18 +250,18 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}
def
killProcesses
()
{
val
sb
=
new
StringBuilder
sb
++=
"kill -9 `cat "
++=
Path
.
BASE
++=
"ss-local.pid`"
++=
"\n"
sb
++=
"killall -9 ss-local"
++=
"\n"
sb
++=
"kill -9 `cat "
++=
Path
.
BASE
++=
"ss-tunnel.pid`"
++=
"\n"
sb
++=
"killall -9 ss-tunnel"
++=
"\n"
sb
++=
"kill -9 `cat "
++=
Path
.
BASE
++=
"tun2socks.pid`"
++=
"\n"
sb
++=
"killall -9 tun2socks"
++=
"\n"
sb
++=
"kill -9 `cat "
++=
Path
.
BASE
++=
"pdnsd.pid`"
++=
"\n"
sb
++=
"killall -9 pdnsd"
++=
"\n"
Utils
.
runCommand
(
sb
.
toString
()
)
val
ab
=
new
ArrayBuffer
[
String
]
ab
.
append
(
"kill -9 `cat "
+
Path
.
BASE
+
"ss-local.pid`"
)
ab
.
append
(
"killall -9 ss-local"
)
ab
.
append
(
"kill -9 `cat "
+
Path
.
BASE
+
"ss-tunnel.pid`"
)
ab
.
append
(
"killall -9 ss-tunnel"
)
ab
.
append
(
"kill -9 `cat "
+
Path
.
BASE
+
"tun2socks.pid`"
)
ab
.
append
(
"killall -9 tun2socks"
)
ab
.
append
(
"kill -9 `cat "
+
Path
.
BASE
+
"pdnsd.pid`"
)
ab
.
append
(
"killall -9 pdnsd"
)
Console
.
runCommand
(
ab
.
toArray
)
}
override
def
onStartCommand
(
intent
:
Intent
,
flags
:
Int
,
startId
:
Int
)
:
Int
=
{
...
...
src/main/scala/com/github/shadowsocks/utils/Console.scala
0 → 100644
View file @
185f32aa
package
com.github.shadowsocks.utils
import
eu.chainfire.libsuperuser.Shell
import
eu.chainfire.libsuperuser.Shell.
{
OnCommandResultListener
,
SU
,
Builder
}
import
java.util
import
android.util.Log
object
Console
{
private
var
shell
:
Shell.Interactive
=
null
private
var
rootShell
:
Shell.Interactive
=
null
private
def
openShell
()
{
if
(
shell
==
null
)
{
val
builder
=
new
Builder
()
shell
=
builder
.
useSH
()
.
setWatchdogTimeout
(
10
)
.
open
(
new
OnCommandResultListener
{
override
def
onCommandResult
(
commandCode
:
Int
,
exitCode
:
Int
,
output
:
util.List
[
String
])
{
if
(
exitCode
<
0
)
shell
=
null
}
})
}
}
private
def
openRootShell
()
{
if
(
rootShell
==
null
)
{
val
builder
=
new
Builder
()
rootShell
=
builder
.
setShell
(
SU
.
shell
(
0
,
"u:r:untrusted_app:s0"
))
.
setWantSTDERR
(
true
)
.
setWatchdogTimeout
(
10
)
.
open
()
}
}
def
runCommand
(
command
:
String
)
{
runCommand
(
Array
(
command
))
}
def
runCommand
(
commands
:
Array
[
String
])
{
if
(
shell
==
null
)
{
openShell
()
}
shell
.
addCommand
(
commands
)
shell
.
waitForIdle
()
}
def
runRootCommand
(
command
:
String
)
:
String
=
runRootCommand
(
Array
(
command
))
def
runRootCommand
(
commands
:
Array
[
String
])
:
String
=
{
if
(!
isRoot
)
{
return
null
}
if
(
rootShell
==
null
)
{
openRootShell
()
}
val
sb
=
new
StringBuilder
rootShell
.
addCommand
(
commands
,
0
,
new
OnCommandResultListener
{
override
def
onCommandResult
(
commandCode
:
Int
,
exitCode
:
Int
,
output
:
util.List
[
String
])
{
if
(
exitCode
<
0
)
{
rootShell
=
null
}
else
{
import
scala.collection.JavaConversions._
output
.
foreach
(
line
=>
sb
.
append
(
line
).
append
(
'\n'
))
}
}
})
if
(
rootShell
.
waitForIdle
())
sb
.
toString
()
else
null
}
def
isRoot
:
Boolean
=
SU
.
available
()
}
src/main/scala/com/github/shadowsocks/utils/Constants.scala
View file @
185f32aa
...
...
@@ -42,6 +42,15 @@ package com.github.shadowsocks.utils
import
android.content.
{
Intent
,
SharedPreferences
}
import
com.github.shadowsocks.aidl.Config
object
Executable
{
val
REDSOCKS
=
"redsocks"
val
PDNSD
=
"pdnsd"
val
SS_LOCAL
=
"ss-local"
val
SS_TUNNEL
=
"ss-tunnel"
val
IPTABLES
=
"iptables"
val
TUN2SOCKS
=
"tun2socks"
}
object
Msg
{
val
CONNECT_FINISH
=
1
val
CONNECT_SUCCESS
=
2
...
...
src/main/scala/com/github/shadowsocks/utils/Utils.scala
View file @
185f32aa
...
...
@@ -134,8 +134,7 @@ object Utils {
private
def
toggleAboveApiLevel17
()
{
// Android 4.2 and above
Utils
.
runRootCommand
(
"ndc resolver flushdefaultif\n"
+
"ndc resolver flushif wlan0\n"
)
Console
.
runRootCommand
(
Array
(
"ndc resolver flushdefaultif"
,
"ndc resolver flushif wlan0"
))
//Utils.runRootCommand("settings put global airplane_mode_on 1\n"
// + "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true\n"
...
...
@@ -207,22 +206,19 @@ object Utils {
def
resolve
(
host
:
String
,
enableIPv6
:
Boolean
)
:
Option
[
String
]
=
{
if
(
enableIPv6
&&
Utils
.
isIPv6Support
)
{
resolve
(
host
,
Type
.
AAAA
)
match
{
case
Some
(
addr
)
=>
{
case
Some
(
addr
)
=>
return
Some
(
addr
)
}
case
None
=>
}
}
resolve
(
host
,
Type
.
A
)
match
{
case
Some
(
addr
)
=>
{
case
Some
(
addr
)
=>
return
Some
(
addr
)
}
case
None
=>
}
resolve
(
host
)
match
{
case
Some
(
addr
)
=>
{
case
Some
(
addr
)
=>
return
Some
(
addr
)
}
case
None
=>
}
None
...
...
@@ -248,9 +244,8 @@ object Utils {
}
}
}
catch
{
case
ex
:
Exception
=>
{
case
ex
:
Exception
=>
Log
.
e
(
TAG
,
"Failed to get interfaces' addresses."
,
ex
)
}
}
None
}
...
...
@@ -276,9 +271,8 @@ object Utils {
}
}
}
catch
{
case
ex
:
Exception
=>
{
case
ex
:
Exception
=>
Log
.
e
(
TAG
,
"Failed to get interfaces' addresses."
,
ex
)
}
}
false
}
...
...
@@ -289,23 +283,20 @@ object Utils {
*/
def
checkIptables
()
{
if
(!
Utils
.
get
Root
)
{
if
(!
Console
.
is
Root
)
{
iptables
=
DEFAULT_IPTABLES
return
}
iptables
=
DEFAULT_IPTABLES
var
lines
:
String
=
null
var
compatible
:
Boolean
=
false
var
version
:
Boolean
=
false
val
sb
=
new
StringBuilder
val
command
=
iptables
+
" --version\n"
+
iptables
+
" -L -t nat -n\n"
+
"exit\n"
val
exitcode
=
runScript
(
command
,
sb
,
10
*
1000
,
asroot
=
true
)
if
(
exitcode
==
TIME_OUT
)
return
val
command
=
Array
(
iptables
+
" --version"
,
iptables
+
" -L -t nat -n"
)
val
lines
=
Console
.
runRootCommand
(
command
)
if
(
lines
==
null
)
return
lines
=
sb
.
toString
()
if
(
lines
.
contains
(
"OUTPUT"
))
{
compatible
=
true
}
...
...
@@ -346,9 +337,8 @@ object Utils {
val
appInfo
:
ApplicationInfo
=
pm
.
getApplicationInfo
(
packages
(
0
),
0
)
return
pm
.
getApplicationIcon
(
appInfo
)
}
catch
{
case
e
:
PackageManager.NameNotFoundException
=>
{
case
e
:
PackageManager.NameNotFoundException
=>
Log
.
e
(
c
.
getPackageName
,
"No package found matching with the uid "
+
uid
)
}
}
}
}
else
{
...
...
@@ -367,25 +357,16 @@ object Utils {
iptables
}
def
getShell
:
String
=
{
if
(
shell
==
null
)
{
shell
=
DEFAULT_SHELL
if
(!
new
File
(
shell
).
exists
)
shell
=
"sh"
}
shell
}
def
initHasRedirectSupported
()
{
if
(!
Utils
.
get
Root
)
return
if
(!
Console
.
is
Root
)
return
hasRedirectSupport
=
1
val
sb
=
new
StringBuilder
val
command
=
Utils
.
getIptables
+
" -t nat -A OUTPUT -p udp --dport 54 -j REDIRECT --to 8154"
val
exitcode
:
Int
=
runScript
(
command
,
sb
,
10
*
1000
,
asroot
=
true
)
val
lines
=
sb
.
toString
()
val
lines
=
Console
.
runRootCommand
(
command
)
Utils
.
runRootCommand
(
command
.
replace
(
"-A"
,
"-D"
))
if
(
exitcode
==
TIME_OUT
)
return
Console
.
runRootCommand
(
command
.
replace
(
"-A"
,
"-D"
))
if
(
lines
==
null
)
return
if
(
lines
.
contains
(
"No chain/target/match"
))
{
hasRedirectSupport
=
0
}
...
...
@@ -394,196 +375,11 @@ object Utils {
def
isInitialized
:
Boolean
=
{
initialized
match
{
case
true
=>
true
case
_
=>
{
case
_
=>
initialized
=
true
false
}
}
}
def
getRoot
:
Boolean
=
{
if
(
isRoot
!=
-
1
)
return
isRoot
==
1
if
(
new
File
(
DEFAULT_ROOT
).
exists
)
{
root_shell
=
DEFAULT_ROOT
}
else
if
(
new
File
(
ALTERNATIVE_ROOT
).
exists
)
{
root_shell
=
ALTERNATIVE_ROOT
}
else
{
root_shell
=
"su"
}
val
sb
=
new
StringBuilder
val
command
:
String
=
"id\n"
val
exitcode
:
Int
=
runScript
(
command
,
sb
,
10
*
1000
,
asroot
=
true
)
if
(
exitcode
==
TIME_OUT
)
{
return
false
}
val
lines
=
sb
.
toString
()
if
(
lines
.
contains
(
"uid=0"
))
{
isRoot
=
1
}
else
{
if
(
rootTries
>=
1
)
isRoot
=
0
rootTries
+=
1
}
isRoot
==
1
}
def
runCommand
(
command
:
String
)
:
Boolean
=
{
runCommand
(
command
,
10
*
1000
)
}
def
runCommand
(
command
:
String
,
timeout
:
Int
)
:
Boolean
=
{
if
(
BuildConfig
.
DEBUG
)
Log
.
d
(
TAG
,
command
)
runScript
(
command
,
null
,
timeout
,
asroot
=
false
)
true
}
def
runRootCommand
(
command
:
String
)
:
Boolean
=
{
runRootCommand
(
command
,
10
*
1000
)
}
def
runRootCommand
(
command
:
String
,
timeout
:
Int
)
:
Boolean
=
{
if
(!
Utils
.
getRoot
)
{
Log
.
e
(
TAG
,
"Cannot get ROOT permission: "
+
root_shell
)
return
false
}
if
(
BuildConfig
.
DEBUG
)
Log
.
d
(
TAG
,
command
)
runScript
(
command
,
null
,
timeout
,
asroot
=
true
)
true
}
def
runScript
(
script
:
String
,
result
:
StringBuilder
,
timeout
:
Long
,
asroot
:
Boolean
)
:
Int
=
{
val
runner
:
Utils.ScriptRunner
=
new
Utils
.
ScriptRunner
(
script
,
result
,
asroot
)
runner
.
start
()
try
{
if
(
timeout
>
0
)
{
runner
.
join
(
timeout
)
}
else
{
runner
.
join
()
}
if
(
runner
.
isAlive
)
{
runner
.
destroy
()
runner
.
join
(
1000
)
return
TIME_OUT
}
}
catch
{
case
ex
:
InterruptedException
=>
{
return
TIME_OUT
}
}
runner
.
exitcode
}
/**
* Internal thread used to execute scripts (as root or not).
* Creates a new script runner.
*
* @param scripts script to run
* @param result result output
* @param asroot if true, executes the script as root
*/
class
ScriptRunner
(
val
scripts
:
String
,
val
result
:
StringBuilder
,
val
asroot
:
Boolean
)
extends
Thread
{
var
exitcode
:
Int
=
-
1
val
pid
:
Array
[
Int
]
=
new
Array
[
Int
](
1
)
var
pipe
:
FileDescriptor
=
null
override
def
destroy
()
{
if
(
pid
(
0
)
!=
-
1
)
{
Exec
.
hangupProcessGroup
(
pid
(
0
))
pid
(
0
)
=
-
1
}
if
(
pipe
!=
null
)
{
Exec
.
close
(
pipe
)
pipe
=
null
}
}
def
createSubprocess
(
processId
:
Array
[
Int
],
cmd
:
String
)
:
FileDescriptor
=
{
val
args
=
parse
(
cmd
)
val
arg0
=
args
(
0
)
Exec
.
createSubprocess
(
if
(
result
!=
null
)
1
else
0
,
arg0
,
args
,
null
,
scripts
+
"\nexit\n"
,
processId
)
}
def
parse
(
cmd
:
String
)
:
Array
[
String
]
=
{
val
PLAIN
=
0
val
INQUOTE
=
2
val
SKIP
=
3
var
state
=
PLAIN
val
result
:
ArrayBuffer
[
String
]
=
new
ArrayBuffer
[
String
]
val
builder
=
new
StringBuilder
()
cmd
foreach
{
ch
=>
{
state
match
{
case
PLAIN
=>
{
ch
match
{
case
c
if
Character
.
isWhitespace
(
c
)
=>
{
result
+=
builder
.
toString
builder
.
clear
()
}
case
'"'
=>
state
=
INQUOTE
case
_
=>
builder
+=
ch
}
}
case
INQUOTE
=>
{
ch
match
{
case
'\\'
=>
state
=
SKIP
case
'"'
=>
state
=
PLAIN
case
_
=>
builder
+=
ch
}
}
case
SKIP
=>
{
builder
+=
ch
state
=
INQUOTE
}
}
}
}
if
(
builder
.
length
>
0
)
{
result
+=
builder
.
toString
}
result
.
toArray
}
override
def
run
()
{
pid
(
0
)
=
-
1
try
{
if
(
this
.
asroot
)
{
pipe
=
createSubprocess
(
pid
,
root_shell
)
}
else
{
pipe
=
createSubprocess
(
pid
,
getShell
)
}
if
(
pid
(
0
)
!=
-
1
)
{
exitcode
=
Exec
.
waitFor
(
pid
(
0
))
}
if
(
result
==
null
||
pipe
==
null
)
return
val
stdout
:
InputStream
=
new
FileInputStream
(
pipe
)
val
buf
=
new
Array
[
Byte
](
8192
)
var
read
:
Int
=
0
while
(
stdout
.
available
>
0
)
{
read
=
stdout
.
read
(
buf
)
result
.
append
(
new
String
(
buf
,
0
,
read
))
}
}
catch
{
case
ex
:
Exception
=>
{
Log
.
e
(
TAG
,
"Cannot execute command"
,
ex
)
if
(
result
!=
null
)
result
.
append
(
"\n"
).
append
(
ex
)
}
}
finally
{
if
(
pipe
!=
null
)
{
Exec
.
close
(
pipe
)
}
if
(
pid
(
0
)
!=
-
1
)
{
Exec
.
hangupProcessGroup
(
pid
(
0
))
}
}
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment