diff options
author | 2017-11-24 21:13:55 -0600 | |
---|---|---|
committer | 2017-11-24 21:16:37 -0600 | |
commit | 50a7a12de279574dd15f4db5cf5ea7aa984b7c80 (patch) | |
tree | dd1d5cad145048cb912195902e2a5fd364964a17 /app/src/main/java/com/wireguard/android/backends/RootShell.java | |
parent | cli: move to android_kernel_wireguard (diff) | |
download | wireguard-android-50a7a12de279574dd15f4db5cf5ea7aa984b7c80.tar.xz wireguard-android-50a7a12de279574dd15f4db5cf5ea7aa984b7c80.zip |
VpnService: Move it to a backends package
It should be split into two pieces: configuration file management
(loading/saving/renaming/deleting) and calling into wg-quick via
RootShell. The configuration file management part should then go
back into the main package. This is in preparation for adding
additional backends based on wg(8) and wireguard-go.
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'app/src/main/java/com/wireguard/android/backends/RootShell.java')
-rw-r--r-- | app/src/main/java/com/wireguard/android/backends/RootShell.java | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/app/src/main/java/com/wireguard/android/backends/RootShell.java b/app/src/main/java/com/wireguard/android/backends/RootShell.java new file mode 100644 index 00000000..0b529065 --- /dev/null +++ b/app/src/main/java/com/wireguard/android/backends/RootShell.java @@ -0,0 +1,86 @@ +package com.wireguard.android.backends; + +import android.content.Context; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; + +/** + * Helper class for running commands as root. + */ + +class RootShell { + /** + * Setup commands that are run at the beginning of each root shell. The trap command ensures + * access to the return value of the last command, since su itself always exits with 0. + */ + private static final String SETUP_TEMPLATE = "export TMPDIR=%s\ntrap 'echo $?' EXIT\n"; + private static final String TAG = "RootShell"; + + private final byte[] setupCommands; + private final String shell; + + RootShell(final Context context) { + this(context, "su"); + } + + RootShell(final Context context, final String shell) { + final String tmpdir = context.getCacheDir().getPath(); + setupCommands = String.format(SETUP_TEMPLATE, tmpdir).getBytes(StandardCharsets.UTF_8); + this.shell = shell; + } + + /** + * Run a series of commands in a root shell. These commands are all sent to the same shell + * process, so they can be considered a shell script. + * + * @param output Lines read from stdout and stderr are appended to this list. Pass null if the + * output from the shell is not important. + * @param commands One or more commands to run as root (each element is a separate line). + * @return The exit value of the last command run, or -1 if there was an internal error. + */ + int run(final List<String> output, final String... commands) { + if (commands.length < 1) + throw new IndexOutOfBoundsException("At least one command must be supplied"); + int exitValue = -1; + try { + final ProcessBuilder builder = new ProcessBuilder().redirectErrorStream(true); + final Process process = builder.command(shell).start(); + final OutputStream stdin = process.getOutputStream(); + stdin.write(setupCommands); + for (final String command : commands) + stdin.write(command.concat("\n").getBytes(StandardCharsets.UTF_8)); + stdin.close(); + Log.d(TAG, "Sent " + commands.length + " command(s), now reading output"); + final InputStream stdout = process.getInputStream(); + final BufferedReader stdoutReader = + new BufferedReader(new InputStreamReader(stdout, StandardCharsets.UTF_8)); + String line; + String lastLine = null; + while ((line = stdoutReader.readLine()) != null) { + Log.v(TAG, line); + lastLine = line; + if (output != null) + output.add(line); + } + process.waitFor(); + process.destroy(); + if (lastLine != null) { + // Remove the exit value line from the output + if (output != null) + output.remove(output.size() - 1); + exitValue = Integer.parseInt(lastLine); + } + Log.d(TAG, "Session completed with exit value " + exitValue); + } catch (IOException | InterruptedException | NumberFormatException e) { + Log.w(TAG, "Session failed with exception", e); + } + return exitValue; + } +} |