2024-07-16 03:00:10 +02:00
|
|
|
package shellexec
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
2024-09-05 09:21:08 +02:00
|
|
|
"os"
|
2024-07-16 03:00:10 +02:00
|
|
|
"os/exec"
|
2024-10-25 01:07:18 +02:00
|
|
|
"runtime"
|
|
|
|
"syscall"
|
2024-09-05 09:21:08 +02:00
|
|
|
"time"
|
2024-07-16 03:00:10 +02:00
|
|
|
|
2024-08-10 03:49:35 +02:00
|
|
|
"github.com/creack/pty"
|
2024-10-24 07:43:17 +02:00
|
|
|
"github.com/wavetermdev/waveterm/pkg/wsl"
|
2024-07-16 03:00:10 +02:00
|
|
|
"golang.org/x/crypto/ssh"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ConnInterface interface {
|
|
|
|
Kill()
|
2024-09-05 09:21:08 +02:00
|
|
|
KillGraceful(time.Duration)
|
2024-07-16 03:00:10 +02:00
|
|
|
Wait() error
|
|
|
|
Start() error
|
|
|
|
StdinPipe() (io.WriteCloser, error)
|
|
|
|
StdoutPipe() (io.ReadCloser, error)
|
|
|
|
StderrPipe() (io.ReadCloser, error)
|
2024-07-19 01:56:00 +02:00
|
|
|
SetSize(w int, h int) error
|
2024-08-10 03:49:35 +02:00
|
|
|
pty.Pty
|
2024-07-16 03:00:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type CmdWrap struct {
|
|
|
|
Cmd *exec.Cmd
|
2024-08-10 03:49:35 +02:00
|
|
|
pty.Pty
|
2024-07-16 03:00:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (cw CmdWrap) Kill() {
|
|
|
|
cw.Cmd.Process.Kill()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cw CmdWrap) Wait() error {
|
|
|
|
return cw.Cmd.Wait()
|
|
|
|
}
|
|
|
|
|
2024-09-05 09:21:08 +02:00
|
|
|
func (cw CmdWrap) KillGraceful(timeout time.Duration) {
|
|
|
|
if cw.Cmd.Process == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if cw.Cmd.ProcessState != nil && cw.Cmd.ProcessState.Exited() {
|
|
|
|
return
|
|
|
|
}
|
2024-10-25 01:07:18 +02:00
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
cw.Cmd.Process.Signal(os.Interrupt)
|
|
|
|
} else {
|
|
|
|
cw.Cmd.Process.Signal(syscall.SIGTERM)
|
|
|
|
}
|
2024-09-05 09:21:08 +02:00
|
|
|
go func() {
|
|
|
|
time.Sleep(timeout)
|
|
|
|
if cw.Cmd.ProcessState == nil || !cw.Cmd.ProcessState.Exited() {
|
|
|
|
cw.Cmd.Process.Kill() // force kill if it is already not exited
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2024-07-16 03:00:10 +02:00
|
|
|
func (cw CmdWrap) Start() error {
|
|
|
|
defer func() {
|
|
|
|
for _, extraFile := range cw.Cmd.ExtraFiles {
|
|
|
|
if extraFile != nil {
|
|
|
|
extraFile.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return cw.Cmd.Start()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cw CmdWrap) StdinPipe() (io.WriteCloser, error) {
|
|
|
|
return cw.Cmd.StdinPipe()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cw CmdWrap) StdoutPipe() (io.ReadCloser, error) {
|
|
|
|
return cw.Cmd.StdoutPipe()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cw CmdWrap) StderrPipe() (io.ReadCloser, error) {
|
|
|
|
return cw.Cmd.StderrPipe()
|
|
|
|
}
|
|
|
|
|
2024-07-19 01:56:00 +02:00
|
|
|
func (cw CmdWrap) SetSize(w int, h int) error {
|
2024-08-10 03:49:35 +02:00
|
|
|
err := pty.Setsize(cw.Pty, &pty.Winsize{Rows: uint16(w), Cols: uint16(h)})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-07-19 01:56:00 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-07-16 03:00:10 +02:00
|
|
|
type SessionWrap struct {
|
|
|
|
Session *ssh.Session
|
|
|
|
StartCmd string
|
2024-08-10 03:49:35 +02:00
|
|
|
Tty pty.Tty
|
|
|
|
pty.Pty
|
2024-07-16 03:00:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sw SessionWrap) Kill() {
|
|
|
|
sw.Tty.Close()
|
|
|
|
sw.Session.Close()
|
|
|
|
}
|
|
|
|
|
2024-09-05 09:21:08 +02:00
|
|
|
func (sw SessionWrap) KillGraceful(timeout time.Duration) {
|
|
|
|
sw.Kill()
|
|
|
|
}
|
|
|
|
|
2024-07-16 03:00:10 +02:00
|
|
|
func (sw SessionWrap) Wait() error {
|
|
|
|
return sw.Session.Wait()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sw SessionWrap) Start() error {
|
|
|
|
return sw.Session.Start(sw.StartCmd)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sw SessionWrap) StdinPipe() (io.WriteCloser, error) {
|
|
|
|
return sw.Session.StdinPipe()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sw SessionWrap) StdoutPipe() (io.ReadCloser, error) {
|
|
|
|
stdoutReader, err := sw.Session.StdoutPipe()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return io.NopCloser(stdoutReader), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sw SessionWrap) StderrPipe() (io.ReadCloser, error) {
|
|
|
|
stderrReader, err := sw.Session.StderrPipe()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return io.NopCloser(stderrReader), nil
|
|
|
|
}
|
2024-07-19 01:56:00 +02:00
|
|
|
|
|
|
|
func (sw SessionWrap) SetSize(h int, w int) error {
|
|
|
|
return sw.Session.WindowChange(h, w)
|
|
|
|
}
|
2024-10-24 07:43:17 +02:00
|
|
|
|
|
|
|
type WslCmdWrap struct {
|
|
|
|
*wsl.WslCmd
|
|
|
|
Tty pty.Tty
|
|
|
|
pty.Pty
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wcw WslCmdWrap) Kill() {
|
|
|
|
wcw.Tty.Close()
|
|
|
|
wcw.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wcw WslCmdWrap) KillGraceful(timeout time.Duration) {
|
|
|
|
process := wcw.WslCmd.GetProcess()
|
|
|
|
if process == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
processState := wcw.WslCmd.GetProcessState()
|
|
|
|
if processState != nil && processState.Exited() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
process.Signal(os.Interrupt)
|
|
|
|
go func() {
|
|
|
|
time.Sleep(timeout)
|
|
|
|
process := wcw.WslCmd.GetProcess()
|
|
|
|
processState := wcw.WslCmd.GetProcessState()
|
|
|
|
if processState == nil || !processState.Exited() {
|
|
|
|
process.Kill() // force kill if it is already not exited
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SetSize does nothing for WslCmdWrap as there
|
|
|
|
* is no pty to manage.
|
|
|
|
**/
|
|
|
|
func (wcw WslCmdWrap) SetSize(w int, h int) error {
|
|
|
|
return nil
|
|
|
|
}
|