Esempio n. 1
0
        /// <summary>
        /// Run Pscp synchronously
        /// </summary>
        /// <param name="result">Result object where results </param>
        /// <param name="args">The args to send to pscp</param>
        /// <param name="argsToLog">The args that are logged or can be returned in status messages</param>
        /// <param name="inlineOutHandler">Inline handler for output</param>
        /// <param name="inlineErrHandler">Inline handler for error</param>
        /// <param name="successOutHandler">Handler for output of successful operation</param>
        void RunPscp(
            PscpResult result,
            string args,
            string argsToLog,
            Func<string, bool> inlineOutHandler,
            Func<string, bool> inlineErrHandler,
            Action<string[]> successOutHandler)
        {
            if (!File.Exists(this.Options.PscpLocation))
            {
                result.SetError(string.Format("Pscp missing, path={0}.", this.Options.PscpLocation), null);
            }
            else if (this.Session.Username == null)
            {
                result.SetError("UserName is null", null);
            }
            else if (this.Session.Host == null)
            {
                result.SetError("Host is null", null);
            }
            else if (this.Session.Port < 0)
            {
                result.SetError("Invalid port: " + this.Session.Port, null);
            }
            else
            {

                Process proc = NewProcess(this.Options.PscpLocation, args);
                Timer timeoutTimer = null;
                AsyncStreamReader outReader = null;
                AsyncStreamReader errReader = null;
                try
                {
                    // Start pscp
                    Log.InfoFormat("Starting process: file={0}, args={1}", this.Options.PscpLocation, argsToLog);
                    proc.Start();

                    // Timeout when no output is received
                    timeoutTimer = new Timer(
                        (x) =>
                        {
                            // timeout
                            SafeKill(proc);
                            result.SetErrorFormat("Process timed out, args={0}", argsToLog);
                        },
                        null, this.Options.TimeoutMs, Timeout.Infinite);

                    // Async read output/err.  Inline actions to quick kill the process when pscp prompts user.
                    // NOTE: Using BeginReadOutput/ErrorReadLine doesn't work here.  Calls to read an empty stream
                    //       will block (e.g. "user's password:"******"OUT",
                        proc.StandardOutput,
                        strOut =>
                        {
                            bool keepReading = true;
                            bool completed = false;
                            if (strOut == PUTTY_INTERACTIVE_AUTH || strOut.Contains("assword:"))
                            {
                                result.StatusCode = ResultStatusCode.RetryAuthentication;
                                Log.Debug("Username/Password invalid or not sent");
                                SafeKill(proc);
                                keepReading = false;
                            }
                            else if (inlineOutHandler != null)
                            {
                                completed = inlineOutHandler(strOut);
                            }
                            timeoutTimer.Change(completed ? Timeout.Infinite : this.Options.TimeoutMs, Timeout.Infinite);
                            return keepReading;
                        });
                    errReader = new AsyncStreamReader(
                        "ERR",
                        proc.StandardError,
                        strErr =>
                        {
                            bool keepReading = true;
                            bool completed = false;
                            if (strErr != null && strErr.Contains(PUTTY_NO_KEY))
                            {
                                result.SetError("Host key not cached.  Connect via putty to cache key then try again", null);
                                SafeKill(proc);
                                keepReading = false;
                            }
                            else if (inlineErrHandler != null)
                            {
                                completed = inlineErrHandler(strErr);
                            }
                            timeoutTimer.Change(completed ? Timeout.Infinite : this.Options.TimeoutMs, Timeout.Infinite);
                            return keepReading;
                        });

                    // start process and wait for results
                    Log.DebugFormat("WaitingForExit");
                    proc.WaitForExit();

                    Log.InfoFormat("Process exited, pid={0}, exitCode={1}", proc.Id, proc.ExitCode);
                    timeoutTimer.Change(Timeout.Infinite, Timeout.Infinite);
                    string[] output = outReader.StopAndGetData();
                    string[] err = errReader.StopAndGetData();

                    string outputStr = String.Join("\r\n", output);
                    if (proc.ExitCode == 0 && outputStr.Contains(PUTTY_UNABLE_TO_OPEN))
                    {
                        // bad path
                        int idx = outputStr.IndexOf(PUTTY_UNABLE_TO_OPEN);
                        result.SetErrorFormat(outputStr.Substring(idx));
                    }
                    else if (proc.ExitCode == 0)
                    {
                        // successful operation
                        if (successOutHandler != null)
                        {
                            successOutHandler(output);
                        }
                    }
                    else
                    {
                        // some kind of error
                        if (result.StatusCode != ResultStatusCode.Success)
                        {
                            Log.Debug("Skipping output check since proactively killed process.");
                        }
                        else if (output.Contains(PUTTY_ARGUMENTS_HELP_HEADER))
                        {
                            result.SetErrorFormat("Invalid arguments sent to pscp, args={0}, output={1}", args, output);
                        }
                        else if (err.Contains(PUTTY_HOST_DOES_NOT_EXIST))
                        {
                            result.SetErrorFormat("Host does not exist.  {0}:{1}", this.Session.Host, this.Session.Port);
                        }
                        else
                        {
                            result.SetErrorFormat("Unknown error.  exitCode={0}, out='{1}', err='{2}'", proc.ExitCode, String.Join("|", output), String.Join("|", err));
                        }
                    }
                }
                finally
                {
                    SafeKill(proc);
                    SafeDispose(timeoutTimer, proc, outReader, errReader);
                }

            }
        }