/// <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> private void RunPscp( PscpResult result, string args, string argsToLog, Func <string, bool> inlineOutHandler, Func <string, bool> inlineErrHandler, Action <string[]> successOutHandler) { if (!File.Exists(Options.PscpLocation)) { result.SetError(string.Format(LocalizedText.PscpClient_RunPscp_Pscp_missing, Options.PscpLocation), null); } else if (Session.Username == null) { result.SetError(LocalizedText.PscpClient_RunPscp_UserName_is_null, null); } else if (Session.Host == null) { result.SetError(LocalizedText.PscpClient_RunPscp_Host_is_null, null); } else if (Session.Port < 0) { result.SetError(string.Format(LocalizedText.PscpClient_RunPscp_Invalid_port, Session.Port), null); } else { Process proc = NewProcess(Options.PscpLocation, args); Timer timeoutTimer = null; AsyncStreamReader outReader = null; AsyncStreamReader errReader = null; try { // Start pscp Log.InfoFormat("Starting process: file={0}, args={1}", Options.PscpLocation, argsToLog); proc.Start(); // Timeout when no output is received timeoutTimer = new Timer( (x) => { // timeout SafeKill(proc); result.SetErrorFormat(LocalizedText.PscpClient_RunPscp_Process_timed_out, argsToLog); }, null, 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 == PuttyInteractiveAuth || 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 : Options.TimeoutMs, Timeout.Infinite); return(keepReading); }); errReader = new AsyncStreamReader( "ERR", proc.StandardError, strErr => { bool keepReading = true; bool completed = false; if (strErr != null && strErr.Contains(PuttyNoKey)) { 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 : 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(PuttyUnableToOpen)) { // bad path int idx = outputStr.IndexOf(PuttyUnableToOpen, StringComparison.Ordinal); result.SetErrorFormat(outputStr.Substring(idx)); } else if (proc.ExitCode == 0) { // successful operation successOutHandler?.Invoke(output); } else { // some kind of error if (result.StatusCode != ResultStatusCode.Success) { Log.Debug("Skipping output check since proactively killed process."); } else if (output.Contains(PuttyArgumentsHelpHeader)) { result.SetErrorFormat("Invalid arguments sent to pscp, args={0}, output={1}", args, output); } else if (err.Contains(PuttyHostDoesNotExist)) { result.SetErrorFormat("Host does not exist. {0}:{1}", Session.Host, 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); } } }
/// <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> private 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); } } }
/// <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); } } }