예제 #1
0
        /**
         * <p>
         * Runs commands using the supplied shell, and returns the output, or null
         * in case of errors.
         * </p>
         * <p>
         * Note that due to compatibility with older Android versions, wantSTDERR is
         * not implemented using redirectErrorStream, but rather appended to the
         * output. STDOUT and STDERR are thus not guaranteed to be in the correct
         * order in the output.
         * </p>
         * <p>
         * Note as well that this code will intentionally crash when run in debug
         * mode from the main thread of the application. You should always execute
         * shell commands from a background thread.
         * </p>
         * <p>
         * When in debug mode, the code will also excessively log the commands
         * passed to and the output returned from the shell.
         * </p>
         * <p>
         * Though this function uses background threads to gobble STDOUT and STDERR
         * so a deadlock does not occur if the shell produces massive output, the
         * output is still stored in a List&lt;String&gt;, and as such doing
         * something like <em>'ls -lR /'</em> will probably have you run out of
         * memory.
         * </p>
         *
         * @param shell The shell to use for executing the commands
         * @param commands The commands to execute
         * @param environment List of all environment variables (in 'key=value'
         *            format) or null for defaults
         * @param wantSTDERR Return STDERR in the output ?
         * @return Output of the commands, or null in case of an error
         */
        public static List<string> run(string shell, string[] commands, string[] environment, bool wantSTDERR)
        {
            string shellUpper = shell.ToUpperInvariant (); //.ToUpperCase(Locale.ENGLISH);

            if (Debug.getSanityChecksEnabledEffective () && Debug.onMainThread ()) {
                // check if we're running in the main thread, and if so, crash if
                // we're in debug mode, to let the developer know attention is
                // needed here.

                Debug.log (ShellOnMainThreadException.EXCEPTION_COMMAND);
            // vhe				throw new ShellOnMainThreadException (ShellOnMainThreadException.EXCEPTION_COMMAND);
            }
            Debug.logCommand (string.Format ("[{0}] START", shellUpper));

            var res = Java.Util.Collections.SynchronizedList (new System.Collections.ArrayList ());

            try {
                // Combine passed environment with system environment
                if (environment != null) {
                    var newEnvironment = new Dictionary<string, string> (global::Java.Lang.JavaSystem.Getenv ());
                    //int split;
                    var split = new char[]{ '=' };
                    foreach (string entry in environment) {
                        var splat = entry.Split (split, 1, StringSplitOptions.RemoveEmptyEntries);
                        if (splat != null && splat.Length == 2) {
                            newEnvironment [splat [0]] = splat [1];
                        }
                    }
            //				int i = 0;
            //					environment = new String[newEnvironment.size()];
            //					foreach (Map.Entry<String, String> entry in newEnvironment.entrySet()) {
            //						environment[i] = entry.getKey() + "=" + entry.getValue();
            //						i++;
            //					}
                    environment = newEnvironment.Select (x => string.Format ("{0}={1}", x.Key, x.Value)).ToArray ();

                }

                // setup our process, retrieve STDIN stream, and STDOUT/STDERR
                // gobblers
                Process process = Runtime.GetRuntime ().Exec (shell, environment);
                var STDIN = new Java.IO.DataOutputStream (process.OutputStream);
                StreamGobbler STDOUT = new StreamGobbler (shellUpper + "-", process.InputStream, res);
                StreamGobbler STDERR = new StreamGobbler (shellUpper + "*", process.ErrorStream, wantSTDERR ? res : null);

                // start gobbling and write our commands to the shell
                STDOUT.Start ();
                STDERR.Start ();
                try {
                    foreach (string write in commands) {
                        Debug.logCommand (string.Format ("[{0}+] {1}", shellUpper, write));
                        STDIN.Write ((write + "\n").getBytes ("UTF-8"));
                        STDIN.Flush ();
                    }
                    STDIN.Write ("exit\n".getBytes ("UTF-8"));
                    STDIN.Flush ();
                } catch (System.Exception e) {
                    if (e.ToString ().Contains ("EPIPE")) {
                        // method most horrid to catch broken pipe, in which case we
                        // do nothing. the command is not a shell, the shell closed
                        // STDIN, the script already contained the exit command, etc.
                        // these cases we want the output instead of returning null
                    } else {
                        // other issues we don't know how to handle, leads to
                        // returning null
                        throw;
                    }
                }

                // wait for our process to finish, while we gobble away in the
                // background
                process.WaitFor ();

                // make sure our threads are done gobbling, our streams are closed,
                // and the process is destroyed - while the latter two shouldn't be
                // needed in theory, and may even produce warnings, in "normal" Java
                // they are required for guaranteed cleanup of resources, so lets be
                // safe and do this on Android as well
                try {
                    STDIN.Close ();
                } catch (System.Exception e) {
                    // might be closed already
                }
                STDOUT.Join ();
                STDERR.Join ();
                process.Destroy ();

                // in case of su, 255 usually indicates access denied
                if (SU.isSU (shell) && (process.ExitValue() != 255)) {  // vhe
                    res = null;
                }
            } catch (Java.IO.IOException e) {
                // shell probably not found
                res = null;
            } catch (InterruptedException e) {
                // this should really be re-thrown
                res = null;
            }

            Debug.logCommand (string.Format ("[{0}%] END", shell.ToUpperInvariant ()));
            var result = new List<string> ();
            foreach (var r in res)
                result.Add (r.ToString ());
            return result;
        }
예제 #2
0
        /**
         * <p>
         * Runs commands using the supplied shell, and returns the output, or null
         * in case of errors.
         * </p>
         * <p>
         * Note that due to compatibility with older Android versions, wantSTDERR is
         * not implemented using redirectErrorStream, but rather appended to the
         * output. STDOUT and STDERR are thus not guaranteed to be in the correct
         * order in the output.
         * </p>
         * <p>
         * Note as well that this code will intentionally crash when run in debug
         * mode from the main thread of the application. You should always execute
         * shell commands from a background thread.
         * </p>
         * <p>
         * When in debug mode, the code will also excessively log the commands
         * passed to and the output returned from the shell.
         * </p>
         * <p>
         * Though this function uses background threads to gobble STDOUT and STDERR
         * so a deadlock does not occur if the shell produces massive output, the
         * output is still stored in a List&lt;String&gt;, and as such doing
         * something like <em>'ls -lR /'</em> will probably have you run out of
         * memory.
         * </p>
         *
         * @param shell The shell to use for executing the commands
         * @param commands The commands to execute
         * @param environment List of all environment variables (in 'key=value'
         *            format) or null for defaults
         * @param wantSTDERR Return STDERR in the output ?
         * @return Output of the commands, or null in case of an error
         */
        public static List <string> run(string shell, string[] commands, string[] environment, bool wantSTDERR)
        {
            string shellUpper = shell.ToUpperInvariant();              //.ToUpperCase(Locale.ENGLISH);

            if (Debug.getSanityChecksEnabledEffective() && Debug.onMainThread())
            {
                // check if we're running in the main thread, and if so, crash if
                // we're in debug mode, to let the developer know attention is
                // needed here.

                Debug.log(ShellOnMainThreadException.EXCEPTION_COMMAND);
                throw new ShellOnMainThreadException(ShellOnMainThreadException.EXCEPTION_COMMAND);
            }
            Debug.logCommand(string.Format("[{0}] START", shellUpper));

            var res = Java.Util.Collections.SynchronizedList(new System.Collections.ArrayList());

            try {
                // Combine passed environment with system environment
                if (environment != null)
                {
                    var newEnvironment = new Dictionary <string, string> (global::Java.Lang.JavaSystem.Getenv());
                    //int split;
                    var split = new char[] { '=' };
                    foreach (string entry in environment)
                    {
                        var splat = entry.Split(split, 1, StringSplitOptions.RemoveEmptyEntries);
                        if (splat != null && splat.Length == 2)
                        {
                            newEnvironment [splat [0]] = splat [1];
                        }
                    }
                    int i = 0;
//					environment = new String[newEnvironment.size()];
//					foreach (Map.Entry<String, String> entry in newEnvironment.entrySet()) {
//						environment[i] = entry.getKey() + "=" + entry.getValue();
//						i++;
//					}
                    environment = newEnvironment.Select(x => string.Format("{0}={1}", x.Key, x.Value)).ToArray();
                }

                // setup our process, retrieve STDIN stream, and STDOUT/STDERR
                // gobblers
                Process       process = Runtime.GetRuntime().Exec(shell, environment);
                var           STDIN   = new Java.IO.DataOutputStream(process.OutputStream);
                StreamGobbler STDOUT  = new StreamGobbler(shellUpper + "-", process.InputStream, res);
                StreamGobbler STDERR  = new StreamGobbler(shellUpper + "*", process.ErrorStream, wantSTDERR ? res : null);

                // start gobbling and write our commands to the shell
                STDOUT.Start();
                STDERR.Start();
                try {
                    foreach (string write in commands)
                    {
                        Debug.logCommand(string.Format("[{0}+] {1}", shellUpper, write));
                        STDIN.Write((write + "\n").getBytes("UTF-8"));
                        STDIN.Flush();
                    }
                    STDIN.Write("exit\n".getBytes("UTF-8"));
                    STDIN.Flush();
                } catch (System.Exception e) {
                    if (e.ToString().Contains("EPIPE"))
                    {
                        // method most horrid to catch broken pipe, in which case we
                        // do nothing. the command is not a shell, the shell closed
                        // STDIN, the script already contained the exit command, etc.
                        // these cases we want the output instead of returning null
                    }
                    else
                    {
                        // other issues we don't know how to handle, leads to
                        // returning null
                        throw;
                    }
                }

                // wait for our process to finish, while we gobble away in the
                // background
                process.WaitFor();

                // make sure our threads are done gobbling, our streams are closed,
                // and the process is destroyed - while the latter two shouldn't be
                // needed in theory, and may even produce warnings, in "normal" Java
                // they are required for guaranteed cleanup of resources, so lets be
                // safe and do this on Android as well
                try {
                    STDIN.Close();
                } catch (System.Exception e) {
                    // might be closed already
                }
                STDOUT.Join();
                STDERR.Join();
                process.Destroy();

                // in case of su, 255 usually indicates access denied
                if (SU.isSU(shell) && (process.ExitValue() == 255))
                {
                    res = null;
                }
            } catch (Java.IO.IOException e) {
                // shell probably not found
                res = null;
            } catch (InterruptedException e) {
                // this should really be re-thrown
                res = null;
            }

            Debug.logCommand(string.Format("[{0}%] END", shell.ToUpperInvariant()));
            var result = new List <string> ();

            foreach (var r in res)
            {
                result.Add(r.ToString());
            }
            return(result);
        }