/// <summary> /// Dump the input until we see a particular string in the returning text. /// </summary> /// <param name="s"></param> /// <param name="refreshTimeout">If we see something back from the host, reset the timeout counter</param> /// <param name="p"></param> /// <param name="failNow">Function that returns true if we should throw out right away</param> private void DumpTillFind(ShellStream s, string matchText, Action<string> ongo = null, bool dontdumplineofmatch = true, int secondsTimeout = 60*60, bool refreshTimeout = false, Func<bool> failNow = null ) { var lb = new LineBuffer(ongo); if (dontdumplineofmatch) { lb.Suppress(matchText); } var timeout = DateTime.Now + TimeSpan.FromSeconds(secondsTimeout); bool gotmatch = false; while (timeout > DateTime.Now) { s.Expect(TimeSpan.FromMilliseconds(100), new ExpectAction(matchText, l => { lb.Add(l); gotmatch = true; })); gotmatch = gotmatch || lb.Match(matchText); if (gotmatch) break; var data = s.Read(); if (data != null && data.Length > 0 && refreshTimeout) { timeout = DateTime.Now + TimeSpan.FromSeconds(secondsTimeout); } lb.Add(data); if (failNow != null && failNow()) { throw new SSHCommandInterruptedException("Calling routine requested termination of command"); } } if (!gotmatch) { throw new TimeoutException(string.Format("Waiting for '{0}' back from host and it was not seen inside of {1} seconds.", matchText, secondsTimeout)); } lb.DumpRest(); }
public void MatchOnCharByChar() { var l = new LineBuffer(); l.Add("b"); l.Add("a"); Assert.IsFalse(l.Match("bash")); l.Add("s"); l.Add("h"); Assert.IsTrue(l.Match("bash")); }
/// <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}'"); } }
public void TestEmptyLineWidth() { var l = new LineBuffer(null); Assert.IsFalse(l.Match("bogus")); }
public void SimpleMatchWithExtra() { var l = new LineBuffer(); l.Add("bogusbashdude"); Assert.IsTrue(l.Match("bash")); }
public void SimpleMatch() { var l = new LineBuffer(); l.Add("bash"); Assert.IsTrue(l.Match("bash")); }
public void MissMatchOnNewLine() { var l = new LineBuffer(); l.Add("bash" + CrLf + "dude"); Assert.IsFalse(l.Match("bash")); }