125 lines
2.5 KiB
Go
125 lines
2.5 KiB
Go
|
package sshcommand
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"runtime"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// Command contains settings to build a ssh command
|
||
|
type Command struct {
|
||
|
Host string
|
||
|
User string
|
||
|
Port int
|
||
|
SSHOptions []string
|
||
|
Gateway *Command
|
||
|
Command []string
|
||
|
Debug bool
|
||
|
NoEscapeCommand bool
|
||
|
SkipHostKeyChecking bool
|
||
|
Quiet bool
|
||
|
AllocateTTY bool
|
||
|
EnableSSHKeyForwarding bool
|
||
|
|
||
|
isGateway bool
|
||
|
}
|
||
|
|
||
|
// New returns a minimal Command
|
||
|
func New(host string) *Command {
|
||
|
return &Command{
|
||
|
Host: host,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (c *Command) applyDefaults() {
|
||
|
if strings.Contains(c.Host, "@") {
|
||
|
parts := strings.Split(c.Host, "@")
|
||
|
c.User = parts[0]
|
||
|
c.Host = parts[1]
|
||
|
}
|
||
|
|
||
|
if c.Port == 0 {
|
||
|
c.Port = 22
|
||
|
}
|
||
|
|
||
|
if c.isGateway {
|
||
|
c.SSHOptions = []string{"-W", "%h:%p"}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Slice returns an execve compatible slice of arguments
|
||
|
func (c *Command) Slice() []string {
|
||
|
c.applyDefaults()
|
||
|
|
||
|
slice := []string{}
|
||
|
|
||
|
slice = append(slice, "ssh")
|
||
|
|
||
|
if c.EnableSSHKeyForwarding {
|
||
|
slice = append(slice, "-A")
|
||
|
}
|
||
|
|
||
|
if c.Quiet {
|
||
|
slice = append(slice, "-q")
|
||
|
}
|
||
|
|
||
|
if c.SkipHostKeyChecking {
|
||
|
slice = append(slice, "-o", "UserKnownHostsFile=/dev/null", "-o", "StrictHostKeyChecking=no")
|
||
|
}
|
||
|
|
||
|
if len(c.SSHOptions) > 0 {
|
||
|
slice = append(slice, c.SSHOptions...)
|
||
|
}
|
||
|
|
||
|
if c.Gateway != nil {
|
||
|
c.Gateway.isGateway = true
|
||
|
slice = append(slice, "-o", "ProxyCommand="+c.Gateway.String())
|
||
|
}
|
||
|
|
||
|
if c.User != "" {
|
||
|
slice = append(slice, "-l", c.User)
|
||
|
}
|
||
|
|
||
|
slice = append(slice, c.Host)
|
||
|
|
||
|
if c.AllocateTTY {
|
||
|
slice = append(slice, "-t", "-t")
|
||
|
}
|
||
|
|
||
|
slice = append(slice, "-p", fmt.Sprintf("%d", c.Port))
|
||
|
if len(c.Command) > 0 {
|
||
|
slice = append(slice, "--", "/bin/sh", "-e")
|
||
|
if c.Debug {
|
||
|
slice = append(slice, "-x")
|
||
|
}
|
||
|
slice = append(slice, "-c")
|
||
|
|
||
|
var escapedCommand []string
|
||
|
if c.NoEscapeCommand {
|
||
|
escapedCommand = c.Command
|
||
|
} else {
|
||
|
escapedCommand = []string{}
|
||
|
for _, part := range c.Command {
|
||
|
escapedCommand = append(escapedCommand, fmt.Sprintf("%q", part))
|
||
|
}
|
||
|
}
|
||
|
slice = append(slice, fmt.Sprintf("%q", strings.Join(escapedCommand, " ")))
|
||
|
}
|
||
|
if runtime.GOOS == "windows" {
|
||
|
slice[len(slice)-1] = slice[len(slice)-1] + " " // Why ?
|
||
|
}
|
||
|
return slice
|
||
|
}
|
||
|
|
||
|
// String returns a copy-pasteable command, useful for debugging
|
||
|
func (c *Command) String() string {
|
||
|
slice := c.Slice()
|
||
|
for i := range slice {
|
||
|
quoted := fmt.Sprintf("%q", slice[i])
|
||
|
if strings.Contains(slice[i], " ") || len(quoted) != len(slice[i])+2 {
|
||
|
slice[i] = quoted
|
||
|
}
|
||
|
}
|
||
|
return strings.Join(slice, " ")
|
||
|
}
|