duanchanguo7603 2018-01-16 02:25
浏览 547

Golang正确处理SSH会话的标准输出

I am writing a Go program to connect to a Cisco wireless controller to run multiple configuration commands on connected access points. I am able to connect to and send commands to the controller via Go's SSH package golang.com/x/crypto/ssh.

I need to a) only print the output of each command I send, not the controller's prompt or login prompts, and b) figure out how to send successive commands after a command whose output includes --More-- or (q)uit prompts.

Right now I have the os.Stdout assigned to the SSH session.Stdout, which is why I'm printing more than just the output of the commands I send. How would I manually control the output of session.Stdout? I know the session.Stdout is an io.Writer, but I don't know how to control what it writes to os.Stdout. Here is my code along with its output:

package main

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

func SendCommand(in io.WriteCloser, cmd string) error {
    if _, err := in.Write([]byte(cmd + "
")); err != nil {
        return err
    }

    return nil
}

func main() {
    // Prompt for Username
    fmt.Print("Username: ")
    r := bufio.NewReader(os.Stdin)
    username, err := r.ReadString('
')
    if err != nil {
        log.Fatal(err)
    }

    // Prompt for password
    fmt.Print("Password: ")
    password, err := terminal.ReadPassword(int(os.Stdin.Fd()))
    if err != nil {
        log.Fatal(err)
    }

    // Setup configuration for SSH client
    config := &ssh.ClientConfig{
        Timeout: time.Second * 5,
        User:    username,
        Auth: []ssh.AuthMethod{
            ssh.Password(string(password)),
        },
        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
    }

    // Connect to the client
    client, err := ssh.Dial("tcp", "host:22", config)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    // Create a session
    session, err := client.NewSession()
    if err != nil {
        log.Fatal(err)
    }
    defer session.Close()

    // Setup StdinPipe to send commands
    stdin, err := session.StdinPipe()
    if err != nil {
        log.Fatal(err)
    }
    defer stdin.Close()

    // Route session Stdout/Stderr to system Stdout/Stderr
    session.Stdout = os.Stdout
    session.Stderr = os.Stderr

    // Start a shell
    if err := session.Shell(); err != nil {
        log.Fatal(err)
    }

    // Send username
    if _, err := stdin.Write([]byte(username)); err != nil {
        log.Fatal(err)
    }
    // Send password
    SendCommand(stdin, string(password))

    // Run configuration commands
    SendCommand(stdin, "show ap summary")
    SendCommand(stdin, "logout")
    SendCommand(stdin, "N")

    session.Wait()
}

Sample run:

$ go run main.go 
Username: username
Password: 

(Cisco Controller) 
User: username
Password:****************
(Cisco Controller) >show ap summary

Number of APs.................................... 1

Global AP User Name.............................. its-wap
Global AP Dot1x User Name........................ Not Configured

AP Name             Slots  AP Model              Ethernet MAC       Location          Country     IP Address       Clients   DSE Location  
------------------  -----  --------------------  -----------------  ----------------  ----------  ---------------  --------  --------------
csc-ap3500   2     AIR-CAP3502I-A-K9     00:00:00:00:00:00  csc-TESTLAB-ap35  US          10.10.110.10       0       [0 ,0 ,0 ]

(Cisco Controller) >logout
The system has unsaved changes.
Would you like to save them now? (y/N) N

My next problem comes when the output of a command has one or more --More-- or (q)uit prompts and I try to send a successive command. For example, here is the output of my code when running show sysinfo followed by logout:

$ go run main.go 
Username: username
Password: 

(Cisco Controller) 
User: username
Password:****************
(Cisco Controller) >show sysinfo

Manufacturer's Name.............................. Cisco Systems Inc.
Product Name..................................... Cisco Controller
Product Version.................................. 8.2.161.0
Bootloader Version............................... 1.0.20
Field Recovery Image Version..................... 7.6.101.1
Firmware Version................................. PIC 16.0


Build Type....................................... DATA + WPS

--More-- or (q)uit

Configured Country............................... US  - United States
Operating Environment............................ Commercial (0 to 40 C)
Internal Temp Alarm Limits....................... 0 to 65 C
Internal Temperature............................. +23 C
External Temperature............................. +28 C
Fan Status....................................... 3900 rpm
# the "l" is missing in "logout"
(Cisco Controller) >ogout

Incorrect usage.  Use the '?' or <TAB> key to list commands.

(Cisco Controller) >N

Incorrect usage.  Use the '?' or <TAB> key to list commands.
# the program hangs here
(Cisco Controller) > 

TL;DR How to manually control the output of an io.Writer and how to correctly handle sending commands in succession?

Any help is appreciated. Thanks in advance.

  • 写回答

0条回答 默认 最新

    报告相同问题?

    悬赏问题

    • ¥100 有人会搭建GPT-J-6B框架吗?有偿
    • ¥15 求差集那个函数有问题,有无佬可以解决
    • ¥15 【提问】基于Invest的水源涵养
    • ¥20 微信网友居然可以通过vx号找到我绑的手机号
    • ¥15 寻一个支付宝扫码远程授权登录的软件助手app
    • ¥15 解riccati方程组
    • ¥15 display:none;样式在嵌套结构中的已设置了display样式的元素上不起作用?
    • ¥15 使用rabbitMQ 消息队列作为url源进行多线程爬取时,总有几个url没有处理的问题。
    • ¥15 Ubuntu在安装序列比对软件STAR时出现报错如何解决
    • ¥50 树莓派安卓APK系统签名