176 lines
3.2 KiB
Go
176 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"next-terminal/server/log"
|
|
"os"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
"golang.org/x/crypto/ssh/terminal"
|
|
)
|
|
|
|
type SSHTerminal struct {
|
|
Session *ssh.Session
|
|
exitMsg string
|
|
stdout io.Reader
|
|
stdin io.Writer
|
|
stderr io.Reader
|
|
}
|
|
|
|
func main() {
|
|
sshConfig := &ssh.ClientConfig{
|
|
User: "root",
|
|
Auth: []ssh.AuthMethod{
|
|
ssh.Password("root"),
|
|
},
|
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
|
}
|
|
|
|
client, err := ssh.Dial("tcp", "172.16.101.32:22", sshConfig)
|
|
if err != nil {
|
|
log.Error(err)
|
|
}
|
|
defer client.Close()
|
|
|
|
err = New(client)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
}
|
|
|
|
func (t *SSHTerminal) updateTerminalSize() {
|
|
|
|
go func() {
|
|
// SIGWINCH is sent to the process when the window size of the terminal has
|
|
// changed.
|
|
sigwinchCh := make(chan os.Signal, 1)
|
|
//signal.Notify(sigwinchCh, syscall.SIN)
|
|
|
|
fd := int(os.Stdin.Fd())
|
|
termWidth, termHeight, err := terminal.GetSize(fd)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
|
|
for {
|
|
select {
|
|
// The client updated the size of the local PTY. This change needs to occur
|
|
// on the server side PTY as well.
|
|
case sigwinch := <-sigwinchCh:
|
|
if sigwinch == nil {
|
|
return
|
|
}
|
|
currTermWidth, currTermHeight, err := terminal.GetSize(fd)
|
|
|
|
// Terminal size has not changed, don't do anything.
|
|
if currTermHeight == termHeight && currTermWidth == termWidth {
|
|
continue
|
|
}
|
|
|
|
err = t.Session.WindowChange(currTermHeight, currTermWidth)
|
|
if err != nil {
|
|
fmt.Printf("Unable to send window-change reqest: %s.", err)
|
|
continue
|
|
}
|
|
|
|
termWidth, termHeight = currTermWidth, currTermHeight
|
|
|
|
}
|
|
}
|
|
}()
|
|
|
|
}
|
|
|
|
func (t *SSHTerminal) interactiveSession() error {
|
|
|
|
defer func() {
|
|
if t.exitMsg == "" {
|
|
log.Info(os.Stdout, "the connection was closed on the remote side on ", time.Now().Format(time.RFC822))
|
|
} else {
|
|
log.Info(os.Stdout, t.exitMsg)
|
|
}
|
|
}()
|
|
|
|
fd := int(os.Stdin.Fd())
|
|
state, err := terminal.MakeRaw(fd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer terminal.Restore(fd, state)
|
|
|
|
termWidth, termHeight, err := terminal.GetSize(fd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
termType := os.Getenv("TERM")
|
|
if termType == "" {
|
|
termType = "xterm-256color"
|
|
}
|
|
|
|
err = t.Session.RequestPty(termType, termHeight, termWidth, ssh.TerminalModes{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
t.updateTerminalSize()
|
|
|
|
t.stdin, err = t.Session.StdinPipe()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
t.stdout, err = t.Session.StdoutPipe()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
t.stderr, err = t.Session.StderrPipe()
|
|
|
|
go io.Copy(os.Stderr, t.stderr)
|
|
go io.Copy(os.Stdout, t.stdout)
|
|
go func() {
|
|
buf := make([]byte, 128)
|
|
for {
|
|
n, err := os.Stdin.Read(buf)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
if n > 0 {
|
|
_, err = t.stdin.Write(buf[:n])
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
t.exitMsg = err.Error()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
err = t.Session.Shell()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = t.Session.Wait()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func New(client *ssh.Client) error {
|
|
|
|
session, err := client.NewSession()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer session.Close()
|
|
|
|
s := SSHTerminal{
|
|
Session: session,
|
|
}
|
|
|
|
return s.interactiveSession()
|
|
}
|