Skip to content
Open
Show file tree
Hide file tree
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
35 changes: 35 additions & 0 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ func Key(prvFile string, passphrase string) (Auth, error) {
}, nil
}

func RawKey(privateKey string, passphrase string) (Auth, error) {
signer, err := GetSignerForRawKey([]byte(privateKey), passphrase)
if err != nil {
return nil, err
}

return Auth{
ssh.PublicKeys(signer),
}, nil
}

// HasAgent checks if ssh agent exists.
func HasAgent() bool {
return os.Getenv("SSH_AUTH_SOCK") != ""
Expand Down Expand Up @@ -78,3 +89,27 @@ func GetSigner(prvFile string, passphrase string) (ssh.Signer, error) {

return signer, err
}

// GetSigner returns ssh signer from private key file.
func GetSignerForRawKey(privateKey []byte, passphrase string) (ssh.Signer, error) {

var (
err error
signer ssh.Signer
)

if err != nil {

return nil, err

} else if passphrase != "" {

signer, err = ssh.ParsePrivateKeyWithPassphrase(privateKey, []byte(passphrase))

} else {

signer, err = ssh.ParsePrivateKey(privateKey)
}

return signer, err
}
34 changes: 34 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
package goph

import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"time"
Expand Down Expand Up @@ -109,6 +111,36 @@ func (c Client) Run(cmd string) ([]byte, error) {
return sess.CombinedOutput(cmd)
}

// Run starts a new SSH session and runs the cmd, it returns CombinedOutput and err if any.
func (c Client) Script(script string) (*Cmd, error) {

var (
err error
sess *ssh.Session
)

if sess, err = c.NewSession(); err != nil {
return nil, err
}

return &Cmd{
Path: "",
Args: []string{},
Session: sess,
Context: context.Background(),
script: bytes.NewBufferString(script + "\n"),
_type: rawScript,
}, nil
}

func (c Client) ScriptFile(filePath string) (*Cmd, error) {
file, err := ioutil.ReadFile(filePath)
if err != nil {
return nil, err
}
return c.Script(string(file))
}

// Run starts a new SSH session with context and runs the cmd. It returns CombinedOutput and err if any.
func (c Client) RunContext(ctx context.Context, name string) ([]byte, error) {
cmd, err := c.CommandContext(ctx, name)
Expand Down Expand Up @@ -136,6 +168,8 @@ func (c Client) Command(name string, args ...string) (*Cmd, error) {
Args: args,
Session: sess,
Context: context.Background(),
_type: cmdLine,
script: bytes.NewBufferString(name + "\n"),
}, nil
}

Expand Down
107 changes: 106 additions & 1 deletion cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,22 @@
package goph

import (
"bytes"
"context"
"fmt"
"io"
"strings"

"github.com/pkg/errors"
"golang.org/x/crypto/ssh"
"strings"
)

type remoteScriptType byte

const (
cmdLine remoteScriptType = iota
rawScript
scriptFile
)

// Cmd it's like os/exec.Cmd but for ssh session.
Expand All @@ -28,6 +39,21 @@ type Cmd struct {

// Context for cancellation
Context context.Context

_type remoteScriptType

script *bytes.Buffer

// scriptFile string

stdout io.Writer
stderr io.Writer
}

func appendStringToBuffer(str string, buf bytes.Buffer) *bytes.Buffer {
parentStr := buf.String()
parentStr = fmt.Sprintf("%v \n %v", str, parentStr)
return bytes.NewBufferString(parentStr)
}

// CombinedOutput runs cmd on the remote host and returns its combined stdout and stderr.
Expand All @@ -52,6 +78,82 @@ func (c *Cmd) Output() ([]byte, error) {
})
}

// Output runs cmd on the remote host and returns its stdout.
func (c *Cmd) ScriptOutput() ([]byte, error) {
if err := c.init(); err != nil {
return nil, errors.Wrap(err, "cmd init")
}
var (
stdout bytes.Buffer
stderr bytes.Buffer
)
c.stdout = &stdout
c.stderr = &stderr
return c.runWithContext(func() ([]byte, error) {
err := c.run()
if err != nil {
return stderr.Bytes(), err
}
return stdout.Bytes(), err
})
}

func (c *Cmd) run() error {
if c._type == cmdLine {
return c.runCmds()
} else if c._type == rawScript {
return c.runScript()
} else if c._type == scriptFile {
return nil
// return c.runScriptFile()
} else {
return errors.New("Not supported RemoteScript type")
}
}

func (c *Cmd) runCmd(cmd string) error {
c.Session.Stdout = c.stdout
c.Session.Stderr = c.stderr

if err := c.Session.Run(cmd); err != nil {
return err
}
return nil
}

func (c *Cmd) runCmds() error {
for {
statment, err := c.script.ReadString('\n')
if err == io.EOF {
break
}
if err != nil {
return err
}

if err := c.runCmd(statment); err != nil {
return err
}
}

return nil
}

func (c *Cmd) runScript() error {
envs := strings.Join(c.Env, "\n")
c.script = appendStringToBuffer(fmt.Sprintf("%v \n", envs), *c.script)
c.Session.Stdin = c.script
c.Session.Stdout = c.stdout
c.Session.Stderr = c.stderr
if err := c.Session.Shell(); err != nil {
return err
}
if err := c.Session.Wait(); err != nil {
return err
}
return nil
}

// Run runs cmd on the remote host.
func (c *Cmd) Run() error {
if err := c.init(); err != nil {
Expand Down Expand Up @@ -81,6 +183,9 @@ func (c *Cmd) String() string {
// Init inits and sets session env vars.
func (c *Cmd) init() (err error) {

if c._type == rawScript {
return
}
// Set session env vars
var env []string
for _, value := range c.Env {
Expand Down
2 changes: 1 addition & 1 deletion examples/goph/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (
"strings"
"time"

"github.com/melbahja/goph"
"github.com/pkg/sftp"
"github.com/tareksalem/goph"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/terminal"
)
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
module github.com/melbahja/goph
module github.com/tareksalem/goph

go 1.13

require (
github.com/pkg/errors v0.9.1
github.com/pkg/sftp v1.13.5
golang.org/x/crypto v0.6.0
golang.org/x/term v0.7.0
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,14 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
Expand Down
7 changes: 4 additions & 3 deletions goph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import (
"net"
"testing"

"github.com/melbahja/goph"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/term"

"github.com/tareksalem/goph"
)

var privateBytes = []byte(`
Expand Down Expand Up @@ -183,7 +184,7 @@ func newServer(port string) {
}
}(requests)

term := terminal.NewTerminal(channel, "> ")
term := term.NewTerminal(channel, "> ")

go func() {
defer channel.Close()
Expand Down