0

I posted a similar question here for reading from a telnet session.

I am trying to read data from an SSH session in golang. I wrote the following functions to try to accomplish this.

I was running into an issue where I was trying to read from stdout and it was empty and it caused my program to lock. To try to work around this I wrote BufferSocketData, it checks the channel ReadDataFromSocket is supposed to append to and if it has data it adds it to the buffer. If after 1 second it still hasn't received any data it stops the read.

This isn't working correctly though and I'm unsure why. Only the first read gets new data subsequent reads return an empty string even if there is data in the buffer.

In my previous question I was able to use SetReadDeadline to limit the amount of time reading from the socket, is there something similar I can use with an SSH session? or do I need to use a different strategy all together?

/*
ReadDataFromSocket - Attempts to read any data in the socket.
*/
func ReadDataFromSocket(sock io.Reader, c chan string) {
    var recvData = make([]byte, 1024)
    var numBytes, _ = sock.Read(recvData)
    c <- string(recvData[:numBytes])
}

/*
BufferSocketData - Read information from the socket and store it in the buffer.
*/
func (s *SSHLib) BufferSocketData(inp chan string, out chan string) {
    var data string
    var timeout int64 = 1000 // 1 second timeout.
    var start = utils.GetTimestamp()

    for utils.GetTimestamp()-start < timeout {
        select {
        case data = <-inp:
        default:
        }
        if data != "" {
            break
        }
    }
    out <- data
}

/*
GetData - Start goroutines to get and buffer data.
*/
func (s *SSHLib) GetData() {
    var sockCh = make(chan string)
    var buffCh = make(chan string)

    go ReadDataFromSocket(s.Stdout, sockCh)
    go s.BufferSocketData(sockCh, buffCh)

    var data = <-buffCh

    if data != "" {
        s.Buffer += data
    }
}

Please let me know if you need any other information.

4
  • Is GetData called multiple times? Commented Jun 26, 2018 at 17:15
  • Yes, I have a function that checks for a regex match in the buffer, if no match is found and the timeout hasn't occurred it calls GetData again. Commented Jun 26, 2018 at 17:21
  • Every call to GetData starts a goroutine to read the connection and those goroutines can be left hanging when GetData returns. These goroutines will eventual read data, but the data will be ignored. Describe the higher level problem you are trying to solve. Are you call regex match on s.Buffer until match is found or timeout? Commented Jun 26, 2018 at 18:04
  • The main problem is that I never get any new data after the initial call to GetData and I'm guessing it has something to do with my go routines and use of channels. Yes I check s.Buffer to see if it matches some regex or timeout if no match is found in a reasonable amount of time. Commented Jun 26, 2018 at 18:21

1 Answer 1

1

Start a single reader goroutine for the session. This goroutine reads from the session and sends data to a channel.

In the main goroutine, select in a loop with cases for data received and timeout. Process each case as appropriate.

type SSHLib struct {
    Stdout io.Reader
    Buffer string
    Data   chan string  // <-- Add this member
}

// New creates a new SSHLib. This example shows the code 
// relevant to reading stdout only. 
func New() *SSHLib {
    s := &SSHLib{Data: make(chan string)}
    go s.Reader()
    return s
}

// Reader reads data from stdout in a loop.
func (s *SSHLib) Reader() {
    var data = make([]byte, 1024)
    for {
        n, err := s.Stdout.Read(data)
        if err != nil {
            // Handle error
            return
        }
        s.Data <- string(data[:n])
    }
}

// GetData receives data until regexp match or timeout.
func (s *SSHLib) GetData() {
    t := time.NewTimer(time.Second)
    defer t.Stop()
    for {
        select {
        case d := <-s.Data:
            s.Buffer += d
            // Check for regexp match in S.Buffer
        case <-t.C:
            // Handle timeout
            return
        }
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

in this example, what will cleanup the goroutine started with go s.Reader() if it blocks on n, err := s.Stdout.Read(data)?
@RomanDodin The call to s.Stdout.Read returns immediately with an error when the application closes the underlying network connection. How that happens is outside the scope of the question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.