@@ -0,0 +1,160 @@
package main
import (
"fmt"
"io"
"io/ioutil"
"net"
"os"
"strings"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
type SSHCommand struct {
Path string
Env []string
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
}
type SSHClient struct {
Config * ssh.ClientConfig
Host string
Port int
}
func (client * SSHClient ) RunCommand (cmd * SSHCommand ) error {
var (
session * ssh.Session
err error
)
if session , err = client .newSession (); err != nil {
return err
}
if err = client .prepareCommand (session , cmd ); err != nil {
session .Close ()
return err
}
err = session .Run (cmd .Path )
return err
}
func (client * SSHClient ) prepareCommand (session * ssh.Session , cmd * SSHCommand ) error {
for _ , env := range cmd .Env {
variable := strings .Split (env , "=" )
if len (variable ) != 2 {
continue
}
if err := session .Setenv (variable [0 ], variable [1 ]); err != nil {
return err
}
}
if cmd .Stdin != nil {
stdin , err := session .StdinPipe ()
if err != nil {
return fmt .Errorf ("Unable to setup stdin for session: %v" , err )
}
go io .Copy (stdin , cmd .Stdin )
}
if cmd .Stdout != nil {
stdout , err := session .StdoutPipe ()
if err != nil {
return fmt .Errorf ("Unable to setup stdout for session: %v" , err )
}
go io .Copy (cmd .Stdout , stdout )
}
if cmd .Stderr != nil {
stderr , err := session .StderrPipe ()
if err != nil {
return fmt .Errorf ("Unable to setup stderr for session: %v" , err )
}
go io .Copy (cmd .Stderr , stderr )
}
return nil
}
func (client * SSHClient ) newSession () (* ssh.Session , error ) {
connection , err := ssh .Dial ("tcp" , fmt .Sprintf ("%s:%d" , client .Host , client .Port ), client .Config )
if err != nil {
return nil , fmt .Errorf ("Failed to dial: %s" , err )
}
session , err := connection .NewSession ()
if err != nil {
return nil , fmt .Errorf ("Failed to create session: %s" , err )
}
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
}
func PublicKeyFile (file string ) ssh.AuthMethod {
buffer , err := ioutil .ReadFile (file )
if err != nil {
return nil
}
key , err := ssh .ParsePrivateKey (buffer )
if err != nil {
return nil
}
return ssh .PublicKeys (key )
}
func SSHAgent () ssh.AuthMethod {
if sshAgent , err := net .Dial ("unix" , os .Getenv ("SSH_AUTH_SOCK" )); err == nil {
return ssh .PublicKeysCallback (agent .NewClient (sshAgent ).Signers )
}
return nil
}
func main () {
// ssh.Password("your_password")
sshConfig := & ssh.ClientConfig {
User : "jsmith" ,
Auth : []ssh.AuthMethod {
SSHAgent (),
},
}
client := & SSHClient {
Config : sshConfig ,
Host : "example.com" ,
Port : 22 ,
}
cmd := & SSHCommand {
Path : "ls -l $LC_DIR" ,
Env : []string {"LC_DIR=/" },
Stdin : os .Stdin ,
Stdout : os .Stdout ,
Stderr : os .Stderr ,
}
fmt .Printf ("Running command: %s\n " , cmd .Path )
if err := client .RunCommand (cmd ); err != nil {
fmt .Fprintf (os .Stderr , "command run error: %s\n " , err )
os .Exit (1 )
}
}