Android Open Source - Rashr Shell






From Project

Back to project page Rashr.

License

The source code is released under:

GNU General Public License

If you think the Android project Rashr listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2012 Dominik Schrmann <dominik@dominikschuermann.de>
 * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks, Jeremy Lakeman (RootTools)
 */*www  .j a va2s .  c o m*/
 * 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 org.sufficientlysecure.rootcommands;

import android.content.Context;

import org.sufficientlysecure.rootcommands.command.Command;
import org.sufficientlysecure.rootcommands.command.SimpleCommand;
import org.sufficientlysecure.rootcommands.util.FailedExecuteCommand;
import org.sufficientlysecure.rootcommands.util.Log;
import org.sufficientlysecure.rootcommands.util.RootAccessDeniedException;
import org.sufficientlysecure.rootcommands.util.Utils;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class Shell implements Closeable {
    public static final String PREF_NAME = "shell";
    public static final String PREF_LOG = "log_commands";
    public static final String Logs = "commands.txt";
    private static final String LD_LIBRARY_PATH = System.getenv("LD_LIBRARY_PATH");
    private static final String token = "F*D^W@#FGF";
    private static final String TAG = "Shell";
    private final Process shellProcess;
    private final BufferedReader stdOutErr;
    private final DataOutputStream outputStream;
    private final List<Command> commands = new ArrayList<Command>();
    private boolean close = false;
    private Context mContext = null;
    private Runnable inputRunnable = new Runnable() {
        public void run() {
            try {
                writeCommands();
            } catch (IOException e) {
                Log.e(RootCommands.TAG, "IO Exception", e);
            }
        }
    };
    private Runnable outputRunnable = new Runnable() {
        public void run() {
            try {
                readOutput();
            } catch (IOException e) {
                Log.e(RootCommands.TAG, "IOException", e);
            } catch (InterruptedException e) {
                Log.e(RootCommands.TAG, "InterruptedException", e);
            }
        }
    };

    private Shell(Context mContext, String shell, ArrayList<String> customEnv, String baseDirectory)
            throws IOException {

        this.mContext = mContext;
        Log.d(RootCommands.TAG, "Starting shell: " + shell);

        // start shell process!
        shellProcess = Utils.runWithEnv(shell, customEnv, baseDirectory);

        // StdErr is redirected to StdOut, defined in Command.getCommand()
        stdOutErr = new BufferedReader(new InputStreamReader(shellProcess.getInputStream()));
        outputStream = new DataOutputStream(shellProcess.getOutputStream());

        outputStream.write("echo Started\n".getBytes());
        outputStream.flush();

        while (true) {
            String line = stdOutErr.readLine();
            if (line == null)
                throw new RootAccessDeniedException(
                        "stdout line is null! Access was denied or this executeable is not a shell!");
            if ("".equals(line))
                continue;
            if ("Started".equals(line))
                break;

            destroyShellProcess();
            throw new IOException("Unable to start shell, unexpected output \"" + line + "\"");
        }

        new Thread(inputRunnable, "Shell Input").start();
        new Thread(outputRunnable, "Shell Output").start();
    }

    /**
     * Start root shell
     *
     * @param customEnv
     * @param baseDirectory
     * @return
     * @throws IOException
     */
    public static Shell startRootShell(Context mContext, ArrayList<String> customEnv, String baseDirectory)
            throws IOException {
        Log.d(RootCommands.TAG, "Starting Root Shell!");

        // On some versions of Android (ICS) LD_LIBRARY_PATH is unset when using su
        // We need to pass LD_LIBRARY_PATH over su for some commands to work correctly.
        if (customEnv == null) {
            customEnv = new ArrayList<String>();
        }
        customEnv.add("LD_LIBRARY_PATH=" + LD_LIBRARY_PATH);

        Shell shell = new Shell(mContext, Utils.getSuPath(), customEnv, baseDirectory);

        return shell;
    }

    /**
     * Start root shell without custom environment and base directory
     *
     * @return
     * @throws IOException
     */
    public static Shell startRootShell() throws IOException {
        return startRootShell(null, null, null);
    }

    public static Shell startRootShell(Context mContext) throws IOException {
        return startRootShell(mContext, null, null);
    }

    /**
     * Start default sh shell
     *
     * @param customEnv
     * @param baseDirectory
     * @return
     * @throws IOException
     */
    public static Shell startShell(ArrayList<String> customEnv, String baseDirectory)
            throws IOException {
        Log.d(RootCommands.TAG, "Starting Shell!");
        Shell shell = new Shell(null, "sh", customEnv, baseDirectory);
        return shell;
    }

    /**
     * Start default sh shell without custom environment and base directory
     *
     * @return
     * @throws IOException
     */
    public static Shell startShell() throws IOException {
        return startShell(null, null);
    }

    /**
     * Start custom shell defined by shellPath
     *
     * @param shellPath
     * @param customEnv
     * @param baseDirectory
     * @return
     * @throws IOException
     */
    public static Shell startCustomShell(String shellPath, ArrayList<String> customEnv,
                                         String baseDirectory) throws IOException {
        Log.d(RootCommands.TAG, "Starting Custom Shell!");
        Shell shell = new Shell(null, shellPath, customEnv, baseDirectory);

        return shell;
    }

    /**
     * Start custom shell without custom environment and base directory
     *
     * @param shellPath
     * @return
     * @throws IOException
     */
    public static Shell startCustomShell(String shellPath) throws IOException {
        return startCustomShell(shellPath, null, null);
    }

    /**
     * Destroy shell process considering that the process could already be terminated
     */
    private void destroyShellProcess() {
        try {
            // Yes, this really is the way to check if the process is
            // still running.
            shellProcess.exitValue();
        } catch (IllegalThreadStateException e) {
            // Only call destroy() if the process is still running;
            // Calling it for a terminated process will not crash, but
            // (starting with at least ICS/4.0) spam the log with INFO
            // messages ala "Failed to destroy process" and "kill
            // failed: ESRCH (No such process)".
            shellProcess.destroy();
        }

        Log.d(RootCommands.TAG, "Shell destroyed");
    }

    /**
     * Writes queued commands one after another into the opened shell. After an execution a token is
     * written to seperate command output on read
     *
     * @throws IOException
     */
    private void writeCommands() throws IOException {
        try {
            int commandIndex = 0;
            while (true) {
                DataOutputStream out;
                synchronized (commands) {
                    while (!close && commandIndex >= commands.size()) {
                        commands.wait();
                    }
                    out = this.outputStream;
                }
                if (commandIndex < commands.size()) {
                    Command next = commands.get(commandIndex);
                    next.writeCommand(out);
                    String line = "\necho " + token + " " + commandIndex + " $?\n";
                    out.write(line.getBytes());
                    out.flush();
                    commandIndex++;
                } else if (close) {
                    out.write("\nexit 0\n".getBytes());
                    out.flush();
                    out.close();
                    Log.d(RootCommands.TAG, "Closing shell");
                    return;
                }
            }
        } catch (InterruptedException e) {
            Log.e(RootCommands.TAG, "interrupted while writing command", e);
        }
    }

    /**
     * Reads output line by line, seperated by token written after every command
     *
     * @throws IOException
     * @throws InterruptedException
     */
    private void readOutput() throws IOException, InterruptedException {
        Command command = null;

        // index of current command
        int commandIndex = 0;
        while (true) {
            String lineStdOut = stdOutErr.readLine();

            // terminate on EOF
            if (lineStdOut == null)
                break;

            if (command == null) {

                // break on close after last command
                if (commandIndex >= commands.size()) {
                    if (close)
                        break;
                    continue;
                }

                // get current command
                command = commands.get(commandIndex);
            }

            int pos = lineStdOut.indexOf(token);
            if (pos > 0) {
                command.processOutput(lineStdOut.substring(0, pos));
            }
            if (pos >= 0) {
                lineStdOut = lineStdOut.substring(pos);
                String fields[] = lineStdOut.split(" ");
                int id = Integer.parseInt(fields[1]);
                if (id == commandIndex) {
                    command.setExitCode(Integer.parseInt(fields[2]));

                    // go to next command
                    commandIndex++;
                    command = null;
                    continue;
                }
            }
            command.processOutput(lineStdOut);
        }
        Log.d(RootCommands.TAG, "Read all output");
        shellProcess.waitFor();
        destroyShellProcess();

        while (commandIndex < commands.size()) {
            if (command == null) {
                command = commands.get(commandIndex);
            }
            command.terminated("Unexpected Termination!");
            commandIndex++;
            command = null;
        }
    }

    /**
     * Add command to shell queue
     *
     * @param command
     * @return
     * @throws IOException
     */
    public Command add(Command command) throws IOException, FailedExecuteCommand {
        if (close)
            throw new IOException("Unable to add commands to a closed shell");
        synchronized (commands) {
            commands.add(command);
            // set shell on the command object, to know where the command is running on
            command.addedToShell(this, (commands.size() - 1));
            commands.notifyAll();
        }
        return command;
    }

    /**
     * Close shell
     *
     * @throws IOException
     */
    public void close() throws IOException {
        synchronized (commands) {
            this.close = true;
            commands.notifyAll();
        }
    }

    /**
     * Returns number of queued commands
     *
     * @return
     */
    public int getCommandsSize() {
        return commands.size();
    }


    /**
     * My own customized command executing routine with command and output logging
     *
     * @param Command for example "echo 123"
     * @return Output of command for example "123"
     * @throws FailedExecuteCommand
     */


    public String execCommand(String Command) throws FailedExecuteCommand {
        final SimpleCommand command = new SimpleCommand(Command);
        try {
            android.util.Log.i(TAG, Command);
            this.add(command).waitForFinish();
            String output = command.getOutput();
            logCommand(command);
            android.util.Log.i(TAG, output);
            if (command.getExitCode() != 0) {
                throw new Exception("Exit-Code not 0");
            }
            return output;
        } catch (Exception e) {
            logCommand(command);
            android.util.Log.i(TAG, "Failed: " + command.getOutput());
            throw new FailedExecuteCommand(command);
        }
    }

    public String execCommand(String Command, boolean waitForFinish) throws FailedExecuteCommand {
        final SimpleCommand command = new SimpleCommand(Command);
        try {
            android.util.Log.i(TAG, Command);
            if (waitForFinish) {
                this.add(command).waitForFinish();
            } else {
                this.add(command);
            }
            String output = command.getOutput();
            logCommand(command);
            android.util.Log.i(TAG, output);
            if (command.getExitCode() != 0) {
                throw new Exception("Exit-Code not 0");
            }
            return output;
        } catch (Exception e) {
            logCommand(command);
            android.util.Log.i(TAG, "Failed: " + command.getOutput());
            throw new FailedExecuteCommand(command);
        }
    }

    public String execCommand(Command command) throws FailedExecuteCommand {
        final SimpleCommand simpleCommand = new SimpleCommand(command.getCommand());
        try {
            android.util.Log.i(TAG, simpleCommand.getCommand());
            this.add(simpleCommand).waitForFinish();
            String output = simpleCommand.getOutput();
            logCommand(simpleCommand);
            android.util.Log.i(TAG, output);
            if (simpleCommand.getExitCode() != 0) {
                throw new Exception("Exit-Code not 0");
            }
            return output;
        } catch (Exception e) {
            logCommand(simpleCommand);
            android.util.Log.i(TAG, "Failed: " + command.getCommand());
            throw new FailedExecuteCommand(command);
        }
    }

    public String execCommand(Command command, boolean waitForFinish) throws FailedExecuteCommand {
        final SimpleCommand simpleCommand = new SimpleCommand(command.getCommand());
        try {
            if (waitForFinish) {
                this.add(simpleCommand).waitForFinish();
            } else {
                this.add(simpleCommand);
            }
            String output = simpleCommand.getOutput();
            android.util.Log.i(TAG, simpleCommand.getCommand());
            logCommand(simpleCommand);
            if (simpleCommand.getExitCode() != 0) {
                throw new Exception("Exit-Code not 0");
            }
            return output;
        } catch (Exception e) {
            logCommand(simpleCommand);
            throw new FailedExecuteCommand(command);
        }
    }

    private void logCommand(SimpleCommand command) {
        try {
            if (mContext != null) {
                if (mContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).getBoolean(PREF_LOG, false)) {
                    String Log = "";
                    if (command.getExitCode() == 0) {
                        Log += "\nCommand:\n" + command.getCommand();
                    } else {
                        Log += "\nFailed execute:\n" + command.getCommand() + "\n";
                    }
                    if (!command.getOutput().equals("")) {
                        Log += "\n\nOutput:\n" + command.getOutput();
                    }
                    FileOutputStream fo = mContext.openFileOutput(Logs, Context.MODE_APPEND);
                    fo.write(Log.getBytes());
                    fo.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}




Java Source Code List

com.fima.cardsui.StackAdapter.java
com.fima.cardsui.SwipeDismissTouchListener.java
com.fima.cardsui.Utils.java
com.fima.cardsui.objects.AbstractCard.java
com.fima.cardsui.objects.CardFactory.java
com.fima.cardsui.objects.CardModel.java
com.fima.cardsui.objects.CardStack.java
com.fima.cardsui.objects.Card.java
com.fima.cardsui.objects.RecyclableCard.java
com.fima.cardsui.views.CardUI.java
com.fima.cardsui.views.MyCard.java
com.fima.cardsui.views.MyImageCard.java
com.fima.cardsui.views.MyPlayCard.java
com.fima.cardsui.views.QuickReturnListView.java
de.mkrtchyan.recoverytools.BackupRestoreFragment.java
de.mkrtchyan.recoverytools.Constants.java
de.mkrtchyan.recoverytools.Device.java
de.mkrtchyan.recoverytools.FlashAsFragment.java
de.mkrtchyan.recoverytools.FlashFragment.java
de.mkrtchyan.recoverytools.FlashUtil.java
de.mkrtchyan.recoverytools.NavigationDrawerFragment.java
de.mkrtchyan.recoverytools.RashrActivity.java
de.mkrtchyan.recoverytools.ReportDialog.java
de.mkrtchyan.recoverytools.ScriptManagerFragment.java
de.mkrtchyan.recoverytools.SettingsFragment.java
de.mkrtchyan.utils.Common.java
de.mkrtchyan.utils.Downloader.java
de.mkrtchyan.utils.FileChooserDialog.java
de.mkrtchyan.utils.FileListView.java
de.mkrtchyan.utils.Notifyer.java
de.mkrtchyan.utils.SHA1.java
de.mkrtchyan.utils.Unzipper.java
donations.DonationsFragment.java
donations.google.util.Base64DecoderException.java
donations.google.util.Base64.java
donations.google.util.IabException.java
donations.google.util.IabHelper.java
donations.google.util.IabResult.java
donations.google.util.Inventory.java
donations.google.util.Purchase.java
donations.google.util.Security.java
donations.google.util.SkuDetails.java
org.sufficientlysecure.rootcommands.Mount.java
org.sufficientlysecure.rootcommands.Remounter.java
org.sufficientlysecure.rootcommands.RootCommands.java
org.sufficientlysecure.rootcommands.Shell.java
org.sufficientlysecure.rootcommands.SystemCommands.java
org.sufficientlysecure.rootcommands.Toolbox.java
org.sufficientlysecure.rootcommands.command.BinaryCommand.java
org.sufficientlysecure.rootcommands.command.Command.java
org.sufficientlysecure.rootcommands.command.SimpleBinaryCommand.java
org.sufficientlysecure.rootcommands.command.SimpleCommand.java
org.sufficientlysecure.rootcommands.util.BrokenBusyboxException.java
org.sufficientlysecure.rootcommands.util.FailedExecuteCommand.java
org.sufficientlysecure.rootcommands.util.Log.java
org.sufficientlysecure.rootcommands.util.RootAccessDeniedException.java
org.sufficientlysecure.rootcommands.util.UnsupportedArchitectureException.java
org.sufficientlysecure.rootcommands.util.Utils.java