grim/hgkeeper

access: Implement stubs
access-control
2019-05-04, Wagner Riffel
208e17128bb9
access: Implement stubs
package ssh
import (
"fmt"
"io"
"os/exec"
"path/filepath"
"strings"
"sync"
"github.com/alecthomas/kong"
"github.com/kballard/go-shellquote"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
"bitbucket.org/rw_grim/hgkeeper/hg"
)
type cli struct {
Hg struct {
Repo string `kong:"flag,short='R'"`
Serve struct {
Stdio bool `kong:"flag,name='stdio'"`
} `kong:"cmd"`
Init struct {
Repo string `kong:"arg"`
} `kong:"cmd"`
} `kong:"cmd"`
}
type Command struct {
path string
cmd *exec.Cmd
}
func NewCommand(path string, cmd *exec.Cmd) *Command {
return &Command{
path: path,
cmd: cmd,
}
}
func (c *Command) run(conn ssh.Channel, serverConn *ssh.ServerConn, req *ssh.Request) error {
teardown := func() {
conn.Close()
if err := c.cmd.Wait(); err != nil {
log.Warnf(
"%s command %q failed: %v",
serverConn.RemoteAddr(),
strings.Join(c.cmd.Args, " "),
err,
)
req.Reply(false, nil)
return
}
log.Debugf(
"%s command %s finished",
serverConn.RemoteAddr(),
strings.Join(c.cmd.Args, " "),
)
req.Reply(true, nil)
}
stdinWriter, err := c.cmd.StdinPipe()
if err != nil {
return err
}
stdoutReader, err := c.cmd.StdoutPipe()
if err != nil {
return err
}
stderrReader, err := c.cmd.StderrPipe()
if err != nil {
return err
}
var once sync.Once
// now wire up stdin/stdout/stderr
go func() {
io.Copy(stdinWriter, conn)
once.Do(teardown)
}()
go func() {
io.Copy(conn, stdoutReader)
once.Do(teardown)
}()
go func() {
io.Copy(conn.Stderr(), stderrReader)
once.Do(teardown)
}()
if err := c.cmd.Start(); err != nil {
req.Reply(false, nil)
once.Do(teardown)
return err
}
return nil
}
func parseCommand(cmd string) (cli, string, error) {
values := cli{}
args, err := shellquote.Split(cmd)
if err != nil {
return values, "", err
}
parser := kong.Must(&values)
ctx, err := parser.Parse(args)
if err != nil {
return values, "", err
}
return values, ctx.Command(), nil
}
func findCommand(cmd, reposPath string) (*Command, error) {
values, pcmd, err := parseCommand(cmd)
if err != nil {
return nil, err
}
switch pcmd {
case "hg serve":
return NewCommand(
values.Hg.Repo,
hg.Serve(filepath.Join(reposPath, values.Hg.Repo)),
), nil
case "hg init <repo>":
return NewCommand(
values.Hg.Init.Repo,
hg.Init(filepath.Join(reposPath, values.Hg.Init.Repo)),
), nil
default:
return nil, fmt.Errorf("unknown command %s", cmd)
}
}