Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 82 additions & 13 deletions easyssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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
}

Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -151,7 +220,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 {
Expand Down Expand Up @@ -188,7 +257,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/%s", destDir, targetFile)); err != nil {
return err
}

Expand Down