Golang - Stdout filtering an active SSH session

I am writing an SSH client in Go that connects to a switch and launches a list of configuration commands. So far, I can successfully connect to the switch, run the necessary commands and print the output of the session. The problem occurs when the switch expects input \n, space, or "q" when the output of the command is too long. For instance:

switch#show int status

Port      Name               Status       Vlan       Duplex  Speed Type
Gi1/0/1                      notconnect   100          auto   auto 10/100/1000BaseTX
Gi1/0/2                      connected    915        a-full  a-100 10/100/1000BaseTX
Gi1/0/3                      notconnect   100          auto   auto 10/100/1000BaseTX
Gi1/0/4                      notconnect   100          auto   auto 10/100/1000BaseTX
Gi1/0/5                      notconnect   230          auto   auto 10/100/1000BaseTX
...
Po1       sw-sww-100-sww-0-0 connected    trunk      a-full    10G
 --More-- # Program hangs here; expecting a new line, space, or 'q'

The request --More--is not actually printed on the screen, so just check if the current line Stdout --More--and send \n, space or q works .

Gif of <code> - More - </code> the hint disappears.

, Stdout , , , - . , , .

:

  • ?
  • , "q" ?

. :

package main

import (
    "bufio"
    "fmt"
    "golang.org/x/crypto/ssh"
    "io"
    "log"
    "os"
    "time"
)

type Device struct {
    Config  *ssh.ClientConfig
    Client  *ssh.Client
    Session *ssh.Session
    Stdin   io.WriteCloser
    Stdout  io.Reader
    Stderr  io.Reader
}

func (d *Device) Connect() error {
    client, err := ssh.Dial("tcp", os.Args[1]+":22", d.Config)
    if err != nil {
        return err
    }
    session, err := client.NewSession()
    if err != nil {
        return err
    }
    sshIn, err := session.StdinPipe()
    if err != nil {
        return err
    }
    sshOut, err := session.StdoutPipe()
    if err != nil {
        return err
    }
    sshErr, err := session.StderrPipe()
    if err != nil {
        return err
    }
    d.Client = client
    d.Session = session
    d.Stdin = sshIn
    d.Stdout = sshOut
    d.Stderr = sshErr
    return nil
}

func (d *Device) SendCommand(cmd string) error {
    if _, err := io.WriteString(d.Stdin, cmd+"\r\n"); err != nil {
        return err
    }
    return nil
}

func (d *Device) SendConfigSet(cmds []string) error {
    for _, cmd := range cmds {
        if _, err := io.WriteString(d.Stdin, cmd+"\r\n"); err != nil {
            return err
        }
        time.Sleep(time.Second)
    }
    return nil
}

func (d *Device) PrintOutput() {
    r := bufio.NewReader(d.Stdout)
    for {
        text, err := r.ReadString('\n')
        fmt.Printf("%s", text)
        if err == io.EOF {
            break
        }
    }
}

func (d *Device) PrintErr() {
    r := bufio.NewReader(d.Stderr)
    for {
        text, err := r.ReadString('\n')
        fmt.Printf("%s", text)
        if err == io.EOF {
            break
        }
    }
}

func main() {
    sshConf := ssh.Config{}
    sshConf.Ciphers = append(sshConf.Ciphers, "aes128-cbc", "3des-cbc", "blowfish-cbc", "arcfour")
    config := &ssh.ClientConfig{
        Config: sshConf,
        User:   "mwalto7",
        Auth: []ssh.AuthMethod{
            ssh.Password("Lion$Tiger$Bear$"),
        },
        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        Timeout:         time.Second * 5,
    }

    sw := &Device{Config: config}

    fmt.Println("Connecting to ", os.Args[1])
    if err := sw.Connect(); err != nil {
        log.Fatal(err)
    }
    defer sw.Client.Close()
    defer sw.Session.Close()
    defer sw.Stdin.Close()

    if err := sw.Session.Shell(); err != nil {
        log.Fatal(err)
    }

    commands := []string{"show int status", "exit"}
    if err := sw.SendConfigSet(commands); err != nil {
        log.Fatal(err)
    }

    sw.Session.Wait()

    sw.PrintOutput()
    sw.PrintErr()
}
+4
1

, terminal length 0 , . —More—, 0, .

0

Source: https://habr.com/ru/post/1692782/


All Articles