From b084f81887b9eafe146eb916558aed669cafb340 Mon Sep 17 00:00:00 2001 From: beccaria Date: Sat, 20 Feb 2016 21:49:03 +0100 Subject: [PATCH 1/3] add support for remote scp location --- easyssh.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/easyssh.go b/easyssh.go index b3002d5..db1d587 100644 --- a/easyssh.go +++ b/easyssh.go @@ -151,7 +151,7 @@ func (ssh_conf *MakeConfig) Run(command string) (outStr string, err error) { } // Scp uploads sourceFile to remote machine like native scp console app. -func (ssh_conf *MakeConfig) Scp(sourceFile string) error { +func (ssh_conf *MakeConfig) Scp(sourceFile string, destDir string) error { session, err := ssh_conf.connect() if err != nil { @@ -188,7 +188,7 @@ func (ssh_conf *MakeConfig) Scp(sourceFile string) error { } }() - if err := session.Run(fmt.Sprintf("scp -t %s", targetFile)); err != nil { + if err := session.Run(fmt.Sprintf("scp -t %s", destDir + "/" + targetFile)); err != nil { return err } From 6546ccec6dabf98079ed2a5697d7f745f88283ba Mon Sep 17 00:00:00 2001 From: beccaria Date: Sat, 20 Feb 2016 21:52:54 +0100 Subject: [PATCH 2/3] go fmt --- easyssh.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easyssh.go b/easyssh.go index db1d587..114a0a4 100644 --- a/easyssh.go +++ b/easyssh.go @@ -188,7 +188,7 @@ func (ssh_conf *MakeConfig) Scp(sourceFile string, destDir string) error { } }() - if err := session.Run(fmt.Sprintf("scp -t %s", destDir + "/" + targetFile)); err != nil { + if err := session.Run(fmt.Sprintf("scp -t %s", destDir+"/"+targetFile)); err != nil { return err } From 6f25495505c67f681b4ac968c5e9b8b789860699 Mon Sep 17 00:00:00 2001 From: beccaria Date: Sun, 21 Feb 2016 13:32:34 +0100 Subject: [PATCH 3/3] add support for TTY and Shell --- easyssh.go | 93 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 12 deletions(-) diff --git a/easyssh.go b/easyssh.go index 114a0a4..0d0ad45 100644 --- a/easyssh.go +++ b/easyssh.go @@ -7,15 +7,15 @@ package easyssh import ( "bufio" "fmt" + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" "io" "io/ioutil" "net" "os" "os/user" "path/filepath" - - "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/agent" + "sync" ) // Contains main authority information. @@ -26,11 +26,14 @@ import ( // Note: easyssh looking for private key in user's home directory (ex. /home/john + Key). // Then ensure your Key begins from '/' (ex. /.ssh/id_rsa) type MakeConfig struct { - User string - Server string - Key string - Port string - Password string + User string + Server string + Key string + Port string + Password string + Tty bool + Shell string + Supassword string } // returns ssh.Signer from user you running app home path + cutted key path. @@ -89,6 +92,19 @@ func (ssh_conf *MakeConfig) connect() (*ssh.Session, error) { return nil, err } + if ssh_conf.Tty { + modes := ssh.TerminalModes{ + ssh.ECHO: 0, // disable echoing + ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud + ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud + } + + if err := session.RequestPty("xterm", 80, 40, modes); err != nil { + session.Close() + return nil, fmt.Errorf("request for pseudo terminal failed: %s", err) + } + } + return session, nil } @@ -102,21 +118,36 @@ func (ssh_conf *MakeConfig) Stream(command string) (output chan string, done cha return output, done, err } // connect to both outputs (they are of type io.Reader) + // session.Shell() outReader, err := session.StdoutPipe() if err != nil { return output, done, err } + inReader, err := session.StdinPipe() + if err != nil { + return output, done, err + } errReader, err := session.StderrPipe() if err != nil { return output, done, err } + // combine outputs, create a line-by-line scanner + outputChan := make(chan string) + done = make(chan bool) outputReader := io.MultiReader(outReader, errReader) - err = session.Start(command) + if len(ssh_conf.Shell) == 0 { + err = session.Start(command) + } else { + err = session.Start(ssh_conf.Shell) + inM, outM := MuxShell(ssh_conf, inReader, outReader) + <-outM //ignore the shell output + inM <- command + fmt.Printf("\noutput: %s\n", <-outM) + inM <- "exit" + } scanner := bufio.NewScanner(outputReader) // continuously send the command's output over the channel - outputChan := make(chan string) - done = make(chan bool) go func(scanner *bufio.Scanner, out chan string, done chan bool) { defer close(outputChan) defer close(done) @@ -130,6 +161,44 @@ func (ssh_conf *MakeConfig) Stream(command string) (output chan string, done cha return outputChan, done, err } +func MuxShell(ssh_conf *MakeConfig, w io.Writer, r io.Reader) (chan<- string, <-chan string) { + in := make(chan string) + out := make(chan string) + var wg sync.WaitGroup + wg.Add(1) //for the shell itself + go func() { + for cmd := range in { + wg.Add(1) + w.Write([]byte(cmd + "\n")) + wg.Wait() + } + }() + go func() { + var ( + buf [65 * 1024]byte + t int + ) + for { + n, err := r.Read(buf[t:]) + if err != nil { + close(in) + close(out) + return + } + t += n + if buf[t-2] == ':' { //Password: + w.Write([]byte(fmt.Sprintf("%s\n", ssh_conf.Supassword))) + } + if buf[t-2] == '$' { //assuming the $PS1 == 'sh-4.3$ ' + out <- string(buf[:t]) + t = 0 + wg.Done() + } + } + }() + return in, out +} + // Runs command on remote machine and returns its stdout as a string func (ssh_conf *MakeConfig) Run(command string) (outStr string, err error) { outChan, doneChan, err := ssh_conf.Stream(command) @@ -188,7 +257,7 @@ func (ssh_conf *MakeConfig) Scp(sourceFile string, destDir string) error { } }() - if err := session.Run(fmt.Sprintf("scp -t %s", destDir+"/"+targetFile)); err != nil { + if err := session.Run(fmt.Sprintf("scp -t %s/%s", destDir, targetFile)); err != nil { return err }