88 lines
1.8 KiB
Go
88 lines
1.8 KiB
Go
//go:build !windows
|
|
|
|
package sshd
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
|
|
"github.com/creack/pty"
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
const (
|
|
ptySupported = true
|
|
runSupported = true
|
|
)
|
|
|
|
// run starts the user's shell (or, for exec, runs cmd through it) and returns its exit code.
|
|
// With a pty request it gets a terminal, otherwise plain pipes.
|
|
func run(ch ssh.Channel, cmd string, p *ptyReq, win <-chan [2]uint32) int {
|
|
shell := os.Getenv("SHELL")
|
|
if shell == "" {
|
|
shell = "/bin/sh"
|
|
}
|
|
var c *exec.Cmd
|
|
if cmd == "" {
|
|
c = exec.Command(shell, "-l")
|
|
} else {
|
|
c = exec.Command(shell, "-c", cmd)
|
|
}
|
|
c.Env = os.Environ()
|
|
if p != nil {
|
|
term := p.term
|
|
if term == "" {
|
|
term = "xterm"
|
|
}
|
|
c.Env = append(c.Env, "TERM="+term)
|
|
return runPty(ch, c, p, win)
|
|
}
|
|
in, err := c.StdinPipe()
|
|
if err != nil {
|
|
fmt.Fprintf(ch.Stderr(), "bamboo: %v\r\n", err)
|
|
return 255
|
|
}
|
|
go func() { io.Copy(in, ch); in.Close() }()
|
|
c.Stdout = ch
|
|
c.Stderr = ch.Stderr()
|
|
return exitCode(c.Run(), c)
|
|
}
|
|
|
|
func runPty(ch ssh.Channel, c *exec.Cmd, p *ptyReq, win <-chan [2]uint32) int {
|
|
f, err := pty.StartWithSize(c, &pty.Winsize{Cols: uint16(p.cols), Rows: uint16(p.rows)})
|
|
if err != nil {
|
|
fmt.Fprintf(ch.Stderr(), "bamboo: %v\r\n", err)
|
|
return 255
|
|
}
|
|
defer f.Close()
|
|
done := make(chan struct{})
|
|
defer close(done)
|
|
go func() {
|
|
for {
|
|
select {
|
|
case w := <-win:
|
|
pty.Setsize(f, &pty.Winsize{Cols: uint16(w[0]), Rows: uint16(w[1])})
|
|
case <-done:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
go func() { io.Copy(f, ch) }()
|
|
io.Copy(ch, f) // ends with an error when the shell exits and the terminal closes
|
|
return exitCode(c.Wait(), c)
|
|
}
|
|
|
|
func exitCode(err error, c *exec.Cmd) int {
|
|
if err == nil {
|
|
return 0
|
|
}
|
|
var ee *exec.ExitError
|
|
if errors.As(err, &ee) && ee.ExitCode() >= 0 {
|
|
return ee.ExitCode()
|
|
}
|
|
return 255
|
|
}
|