public static Task <string> ExpectOnceAsync(this ShellStream Self, Regex Expect, CancellationToken Token = default) { Token.ThrowIfCancellationRequested(); var Task = new TaskCompletionSource <string>(); IDisposable Disposable = null; IAsyncResult result = null; Disposable = Token.Register(async() => { try { if (result != null) { var _result = result; result = null; var expect = await System.Threading.Tasks.Task.Run(() => Self.EndExpect(_result)); } } catch { } Task.TrySetCanceled(Token); }); result = Self.BeginExpect(async ar => { try { if (result != null) { result = null; var expect = await System.Threading.Tasks.Task.Run(() => Self.EndExpect(ar)); Task.TrySetResult(expect); } } catch (Exception e) { Task.TrySetException(e); } finally { Disposable?.Dispose(); Disposable = null; } }, new ExpectAction(Expect, async v => { try { if (result != null) { var _result = result; result = null; var expect = await System.Threading.Tasks.Task.Run(() => Self.EndExpect(_result)); Task.TrySetResult(expect); } } catch (Exception e) { Task.TrySetException(e); } finally { Disposable?.Dispose(); Disposable = null; } })); return(Task.Task); }
public void BeginExpectTest1() { Session session = null; // TODO: Initialize to an appropriate value string terminalName = string.Empty; // TODO: Initialize to an appropriate value uint columns = 0; // TODO: Initialize to an appropriate value uint rows = 0; // TODO: Initialize to an appropriate value uint width = 0; // TODO: Initialize to an appropriate value uint height = 0; // TODO: Initialize to an appropriate value int maxLines = 0; // TODO: Initialize to an appropriate value IDictionary<TerminalModes, uint> terminalModeValues = null; // TODO: Initialize to an appropriate value ShellStream target = new ShellStream(session, terminalName, columns, rows, width, height, maxLines, terminalModeValues); // TODO: Initialize to an appropriate value ExpectAction[] expectActions = null; // TODO: Initialize to an appropriate value IAsyncResult expected = null; // TODO: Initialize to an appropriate value IAsyncResult actual; actual = target.BeginExpect(expectActions); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); }
public override bool ExecuteAsUser(string command, string arguments, out ProcessExecuteStatus process_status, out string process_output, out string process_error, string user, SecureString password, Action <string> OutputDataReceived = null, Action <string> OutputErrorReceived = null, [CallerMemberName] string memberName = "", [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0) { CallerInformation caller = new CallerInformation(memberName, fileName, lineNumber); if (!this.IsConnected) { throw new InvalidOperationException("The SSH session is not connected."); } process_status = ProcessExecuteStatus.Unknown; process_output = ""; process_error = ""; string c; if (password == null) { c = string.Format("-n -u {0} -s {1} {2}", user, command, arguments); return(this.Execute("sudo", c, out process_status, out process_output, out process_error)); } StringBuilder shell_data = new StringBuilder(); ShellStream stream = this.SshClient.CreateShellStream("dumb", 0, 0, 800, 600, 1024, new Dictionary <TerminalModes, uint> { { TerminalModes.ECHO, 0 } }); stream.DataReceived += (s, d) => shell_data.Append(Encoding.UTF8.GetString(d.Data)); c = string.Format("PAGER=cat su -c \"echo CMD_START && {0} {1} && echo CMD_SUCCESS || echo CMD_ERROR\" {2} || echo CMD_ERROR", command, arguments, user); byte[] b = Encoding.UTF8.GetBytes(c + this.LineTerminator); Stopwatch cs = new Stopwatch(); cs.Start(); IAsyncResult wr = stream.BeginWrite(b, 0, b.Length, new AsyncCallback(SshStreamWriteAsyncCallback), new KeyValuePair <string, ShellStream>(c, stream)); stream.EndWrite(wr); bool got_password_prompt = false; ExpectAction[] got_password_prompt_action = { new ExpectAction("Password:"******"Unexpected response from server attempting to execute {0}: {1}", c, shell_data); return(false); } stream.EndWrite(wr); bool cmd_success = false; string cmd_output = string.Empty; ExpectAction[] cmd_actions = { new ExpectAction("CMD_ERROR", (o) => { cmd_output = o.Replace("CMD_ERROR", string.Empty); cmd_success = false; }), new ExpectAction("CMD_SUCCESS", (o) => { cmd_output = o.Replace("CMD_SUCCESS", string.Empty).Replace("CMD_START", string.Empty); cmd_success = true; }), }; er = stream.BeginExpect(new TimeSpan(0, 0, 5), new AsyncCallback(SshExpectAsyncCallback), new KeyValuePair <string, Stopwatch>(c, cs), cmd_actions); stream.EndExpect(er); if (!cmd_success) { process_status = ProcessExecuteStatus.Error; Debug(caller, "Execute {0} {1} returned non-zero exit code. Output: {2}.", command, arguments, cmd_output); return(false); } else { Debug(caller, "Execute {0} {1} returned zero exit code. Output: {2}.", command, arguments, cmd_output); process_status = ProcessExecuteStatus.Completed; process_output = cmd_output.Trim('\r', '\n'); return(true); } }
/// <summary> /// Dump the input until we see a particular string in the returning text. /// </summary> /// <param name="s">The ssh stream to run against</param> /// <param name="matchText">The text to match - command is done and we return when we see it</param> /// <param name="failNow">Function that returns true if we should throw out right away</param> /// <param name="ongo">When we see a complete line, call this function. Defaults to null</param> /// <param name="secondsTimeout">How long should there be before we have a timeout.</param> /// <param name="refreshTimeout">If we see something back from the host, reset the timeout counter</param> /// <param name="crlfExpectedAtEnd">If the crlf is expected, then eat it when we see it. A command line prompt, for example, will not have this.</param> /// <param name="seeAndRespond">Dictionary of strings to look for and to respond to with further input.</param> private static async Task DumpTillFind(ShellStream s, SshClient client, string matchText, Action <string> ongo = null, int secondsTimeout = 60 *60, bool refreshTimeout = false, Func <bool> failNow = null, bool crlfExpectedAtEnd = false, Dictionary <string, string> seeAndRespond = null ) { /// <summary> /// How long to go before checking for update data from the stream. The actual timeout accuracy /// when refreshtimeout is set will depend on this. /// </summary> TimeSpan TimeoutGranularity = TimeSpan.FromSeconds(5); // Send back text we see if there is someone watching. // Don't send back the matched text. var lb = new LineBuffer(); if (ongo != null) { lb.AddAction(l => { if (!l.Contains(matchText)) { ongo(l); } }); } // Set when we see the text we are trying to match. bool gotmatch = false; // The Shell Stream works by looking for strings, and when it matches, performing a call out and returning. // The most obvious string to wait for is our matched string. var expectedMatchText = matchText + (crlfExpectedAtEnd ? LineBuffer.CrLf : ""); var matchText_action = new ExpectAction(expectedMatchText, l => { Trace.WriteLine($"DumpTillFill: Found expected Text: '{SummarizeText(l)}'"); lb.Add(l); gotmatch = true; }); Trace.WriteLine($"DumpTillFind: searching for text: {matchText} (with crlf: {crlfExpectedAtEnd})"); // Next is the end-of-line action. var matchEOL_action = new ExpectAction(new Regex("^.*" + LineBuffer.CrLf + "$"), l => { if (!gotmatch) { lb.Add(l); } }); // Finally, there are some see-and-then-send actions that might be present. When we see a match, send back some text. // This is an easy way to deal with a conversation (like a password request). var seeAndRespond_actions = seeAndRespond == null ? Enumerable.Empty <ExpectAction>() : seeAndRespond .Select(sr => new ExpectAction(sr.Key, whatMatched => { Trace.WriteLine($"DumpTillFill: Found seeAndRespond: {whatMatched}"); lb.Add(whatMatched); s.WriteLine(sr.Value); })); if (seeAndRespond != null) { foreach (var item in seeAndRespond) { Trace.WriteLine($"DumpTillFind: -> also looking for '{item.Key}' and will respond with '{item.Value}'"); } } // All the actions together var expect_actions = new[] { matchText_action, matchEOL_action } .Concat(seeAndRespond_actions) .ToArray(); // When is time up? If we are supposed to refresh the timeout then everytime the data changes in the buffer, we will want // to update the timeout. var timesUp = DateTime.Now + TimeSpan.FromSeconds(secondsTimeout); var streamDataLength = new ValueHasChanged <long>(() => s.Length); streamDataLength.Evaluate(); while (timesUp > DateTime.Now) { // Make sure the connection hasn't dropped. We won't get an exception later on if that hasn't happened. if (!client.IsConnected) { throw new SSHConnectionDroppedException($"Connection to {client.ConnectionInfo.Host} has been dropped."); } // Wait for some sort of interesting text to come back. var howLongToWait = MinTime(timesUp - DateTime.Now, TimeoutGranularity); var seenText = await Task.Factory.FromAsync( s.BeginExpect(howLongToWait, null, null, expect_actions), s.EndExpect); if (gotmatch) { break; } // Reset the timeout if data has come in and we are doing that. if (refreshTimeout && streamDataLength.HasChanged) { timesUp = DateTime.Now + TimeSpan.FromSeconds(secondsTimeout); } // Check to see if we should fail if (failNow != null && failNow()) { throw new SSHCommandInterruptedException("Calling routine requested termination of command"); } } // Done. IF there is anything that doesn't include a complete line in there, then dump it. lb.DumpRest(); if (!gotmatch) { var tmpString = lb.ToString(); Debug.WriteLine($"Waiting for '{matchText}' back from host and it was not seen inside of {secondsTimeout} seconds. Remaining in buffer: '{tmpString}'"); throw new TimeoutException($"Waiting for '{matchText}' back from host and it was not seen inside of {secondsTimeout} seconds. Remaining in buffer: '{tmpString}'"); } }