2017-04-14 19:26:32 +02:00
|
|
|
package sshbytestream
|
|
|
|
|
2017-04-15 15:43:50 +02:00
|
|
|
import (
|
2017-04-26 20:25:53 +02:00
|
|
|
"fmt"
|
2017-05-14 12:27:15 +02:00
|
|
|
"github.com/zrepl/zrepl/util"
|
2017-04-15 15:43:50 +02:00
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
)
|
|
|
|
|
2017-04-30 23:35:08 +02:00
|
|
|
type Error struct {
|
|
|
|
Stderr []byte
|
|
|
|
WaitErr error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e Error) Error() string {
|
|
|
|
return fmt.Sprintf("ssh command failed with error: %v. stderr:\n%s\n", e.WaitErr, e.Stderr)
|
|
|
|
}
|
|
|
|
|
2017-04-29 20:09:09 +02:00
|
|
|
type SSHTransport struct {
|
2017-04-30 16:11:33 +02:00
|
|
|
Host string
|
|
|
|
User string
|
|
|
|
Port uint16
|
|
|
|
IdentityFile string
|
|
|
|
SSHCommand string
|
|
|
|
Options []string
|
2017-04-29 20:09:09 +02:00
|
|
|
}
|
|
|
|
|
2017-04-15 15:43:50 +02:00
|
|
|
var SSHCommand string = "ssh"
|
|
|
|
|
|
|
|
func Incoming() (wc io.ReadWriteCloser, err error) {
|
2017-04-14 19:26:32 +02:00
|
|
|
// derivce ReadWriteCloser from stdin & stdout
|
2017-04-15 15:43:50 +02:00
|
|
|
return IncomingReadWriteCloser{}, nil
|
2017-04-14 19:26:32 +02:00
|
|
|
}
|
|
|
|
|
2017-04-15 15:43:50 +02:00
|
|
|
type IncomingReadWriteCloser struct{}
|
|
|
|
|
|
|
|
func (f IncomingReadWriteCloser) Read(p []byte) (n int, err error) {
|
|
|
|
return os.Stdin.Read(p)
|
2017-04-14 19:26:32 +02:00
|
|
|
}
|
|
|
|
|
2017-04-15 15:43:50 +02:00
|
|
|
func (f IncomingReadWriteCloser) Write(p []byte) (n int, err error) {
|
|
|
|
return os.Stdout.Write(p)
|
|
|
|
}
|
2017-04-14 19:26:32 +02:00
|
|
|
|
2017-04-26 20:25:53 +02:00
|
|
|
func (f IncomingReadWriteCloser) Close() (err error) {
|
2017-04-30 23:35:08 +02:00
|
|
|
if err = os.Stdin.Close(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err = os.Stdout.Close(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
return
|
2017-04-14 19:26:32 +02:00
|
|
|
}
|
|
|
|
|
2017-05-14 12:27:15 +02:00
|
|
|
func Outgoing(remote SSHTransport) (c *util.IOCommand, err error) {
|
2017-04-15 15:43:50 +02:00
|
|
|
|
2017-04-30 16:11:33 +02:00
|
|
|
sshArgs := make([]string, 0, 2*len(remote.Options)+4)
|
2017-04-15 15:43:50 +02:00
|
|
|
sshArgs = append(sshArgs,
|
|
|
|
"-p", fmt.Sprintf("%d", remote.Port),
|
2017-04-30 16:11:33 +02:00
|
|
|
"-q",
|
|
|
|
"-i", remote.IdentityFile,
|
2017-04-15 15:43:50 +02:00
|
|
|
"-o", "BatchMode=yes",
|
|
|
|
)
|
2017-04-26 20:25:53 +02:00
|
|
|
for _, option := range remote.Options {
|
2017-04-15 15:43:50 +02:00
|
|
|
sshArgs = append(sshArgs, "-o", option)
|
|
|
|
}
|
|
|
|
sshArgs = append(sshArgs, fmt.Sprintf("%s@%s", remote.User, remote.Host))
|
|
|
|
|
2017-04-30 16:11:33 +02:00
|
|
|
var sshCommand = SSHCommand
|
|
|
|
if len(remote.SSHCommand) > 0 {
|
|
|
|
sshCommand = SSHCommand
|
|
|
|
}
|
|
|
|
|
2017-05-14 12:27:15 +02:00
|
|
|
if c, err = util.NewIOCommand(sshCommand, sshArgs, util.IOCommandStderrBufSize); err != nil {
|
2017-04-15 15:43:50 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-05-14 12:27:15 +02:00
|
|
|
// Clear environment of cmd, ssh shall not rely on SSH_AUTH_SOCK, etc.
|
|
|
|
c.Cmd.Env = []string{}
|
2017-04-14 19:26:32 +02:00
|
|
|
|
2017-05-14 12:27:15 +02:00
|
|
|
err = c.Start()
|
2017-04-30 23:35:08 +02:00
|
|
|
return
|
2017-04-14 19:26:32 +02:00
|
|
|
}
|