mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
Windows Pty (#206)
Add Windows Pty support, so the terminal works properly on windows machines
This commit is contained in:
parent
4498ca8e9d
commit
c192fe2663
10
Taskfile.yml
10
Taskfile.yml
@ -8,6 +8,8 @@ vars:
|
|||||||
BIN_DIR: "bin"
|
BIN_DIR: "bin"
|
||||||
VERSION:
|
VERSION:
|
||||||
sh: node version.cjs
|
sh: node version.cjs
|
||||||
|
RM: '{{if eq OS "windows"}}powershell Remove-Item{{else}}rm{{end}}'
|
||||||
|
DATE: '{{if eq OS "windows"}}powershell date -UFormat{{else}}date{{end}}'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
electron:dev:
|
electron:dev:
|
||||||
@ -50,7 +52,7 @@ tasks:
|
|||||||
status:
|
status:
|
||||||
- exit {{if eq OS "darwin"}}1{{else}}0{{end}}
|
- exit {{if eq OS "darwin"}}1{{else}}0{{end}}
|
||||||
cmds:
|
cmds:
|
||||||
- cmd: rm dist/bin/wavesrv*
|
- cmd: '{{.RM}} "dist/bin/wavesrv*"'
|
||||||
ignore_error: true
|
ignore_error: true
|
||||||
- task: build:server:internal
|
- task: build:server:internal
|
||||||
vars:
|
vars:
|
||||||
@ -64,7 +66,7 @@ tasks:
|
|||||||
status:
|
status:
|
||||||
- exit {{if eq OS "darwin"}}0{{else}}1{{end}}
|
- exit {{if eq OS "darwin"}}0{{else}}1{{end}}
|
||||||
cmds:
|
cmds:
|
||||||
- cmd: rm dist/bin/wavesrv*
|
- cmd: '{{.RM}} "dist/bin/wavesrv*"'
|
||||||
ignore_error: true
|
ignore_error: true
|
||||||
- task: build:server:internal
|
- task: build:server:internal
|
||||||
vars:
|
vars:
|
||||||
@ -77,7 +79,7 @@ tasks:
|
|||||||
vars:
|
vars:
|
||||||
- GOARCH
|
- GOARCH
|
||||||
cmds:
|
cmds:
|
||||||
- CGO_ENABLED=1 GOARCH={{.GOARCH}} go build -tags "osusergo,netgo,sqlite_omit_load_extension" -ldflags "{{.GO_LDFLAGS}} -X main.BuildTime=$(date +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wavesrv.{{.GOARCH}}{{exeExt}} cmd/server/main-server.go
|
- CGO_ENABLED=1 GOARCH={{.GOARCH}} go build -tags "osusergo,netgo,sqlite_omit_load_extension" -ldflags "{{.GO_LDFLAGS}} -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wavesrv.{{.GOARCH}}{{exeExt}} cmd/server/main-server.go
|
||||||
sources:
|
sources:
|
||||||
- "cmd/server/*.go"
|
- "cmd/server/*.go"
|
||||||
- "pkg/**/*.go"
|
- "pkg/**/*.go"
|
||||||
@ -133,7 +135,7 @@ tasks:
|
|||||||
generates:
|
generates:
|
||||||
- dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.GOARCH}}{{.EXT}}
|
- dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.GOARCH}}{{.EXT}}
|
||||||
cmds:
|
cmds:
|
||||||
- (CGO_ENABLED=0 GOOS={{.GOOS}} GOARCH={{.GOARCH}} go build -ldflags="-s -w -X main.BuildTime=$(date +'%Y%m%d%H%M')" -o dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.GOARCH}}{{.EXT}} cmd/wsh/main-wsh.go)
|
- (CGO_ENABLED=0 GOOS={{.GOOS}} GOARCH={{.GOARCH}} go build -ldflags="-s -w -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M')" -o dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.GOARCH}}{{.EXT}} cmd/wsh/main-wsh.go)
|
||||||
deps:
|
deps:
|
||||||
- generate
|
- generate
|
||||||
- go:mod:tidy
|
- go:mod:tidy
|
||||||
|
@ -226,6 +226,9 @@ func main() {
|
|||||||
if pidStr != "" {
|
if pidStr != "" {
|
||||||
_, err := strconv.Atoi(pidStr)
|
_, err := strconv.Atoi(pidStr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
if BuildTime == "" {
|
||||||
|
BuildTime = "0"
|
||||||
|
}
|
||||||
// use fmt instead of log here to make sure it goes directly to stderr
|
// use fmt instead of log here to make sure it goes directly to stderr
|
||||||
fmt.Fprintf(os.Stderr, "WAVESRV-ESTART ws:%s web:%s version:%s buildtime:%s\n", wsListener.Addr(), webListener.Addr(), WaveVersion, BuildTime)
|
fmt.Fprintf(os.Stderr, "WAVESRV-ESTART ws:%s web:%s version:%s buildtime:%s\n", wsListener.Addr(), webListener.Addr(), WaveVersion, BuildTime)
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ function getWaveSrvPath(): string {
|
|||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
const winBinName = `${wavesrvBinName}.exe`;
|
const winBinName = `${wavesrvBinName}.exe`;
|
||||||
const appPath = path.join(getGoAppBasePath(), "bin", winBinName);
|
const appPath = path.join(getGoAppBasePath(), "bin", winBinName);
|
||||||
return `& "${appPath}"`;
|
return `${appPath}`;
|
||||||
}
|
}
|
||||||
return path.join(getGoAppBasePath(), "bin", wavesrvBinName);
|
return path.join(getGoAppBasePath(), "bin", wavesrvBinName);
|
||||||
}
|
}
|
||||||
|
2
go.mod
2
go.mod
@ -44,3 +44,5 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/kevinburke/ssh_config => github.com/wavetermdev/ssh_config v0.0.0-20240306041034-17e2087ebde2
|
replace github.com/kevinburke/ssh_config => github.com/wavetermdev/ssh_config v0.0.0-20240306041034-17e2087ebde2
|
||||||
|
|
||||||
|
replace github.com/creack/pty => github.com/photostorm/pty v1.1.19-0.20230903182454-31354506054b
|
||||||
|
5
go.sum
5
go.sum
@ -3,8 +3,6 @@ filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4
|
|||||||
github.com/alexflint/go-filemutex v1.3.0 h1:LgE+nTUWnQCyRKbpoceKZsPQbs84LivvgwUymZXdOcM=
|
github.com/alexflint/go-filemutex v1.3.0 h1:LgE+nTUWnQCyRKbpoceKZsPQbs84LivvgwUymZXdOcM=
|
||||||
github.com/alexflint/go-filemutex v1.3.0/go.mod h1:U0+VA/i30mGBlLCrFPGtTe9y6wGQfNAWPBTekHQ+c8A=
|
github.com/alexflint/go-filemutex v1.3.0/go.mod h1:U0+VA/i30mGBlLCrFPGtTe9y6wGQfNAWPBTekHQ+c8A=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
|
||||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -48,6 +46,8 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o
|
|||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/photostorm/pty v1.1.19-0.20230903182454-31354506054b h1:cLGKfKb1uk0hxI0Q8L83UAJPpeJ+gSpn3cCU/tjd3eg=
|
||||||
|
github.com/photostorm/pty v1.1.19-0.20230903182454-31354506054b/go.mod h1:KO+FcPtyLAiRC0hJwreJVvfwc7vnNz77UxBTIGHdPVk=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||||
@ -90,6 +90,7 @@ golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
|||||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20220721230656-c6bc011c0c49/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/creack/pty"
|
|
||||||
"github.com/wavetermdev/thenextwave/pkg/eventbus"
|
"github.com/wavetermdev/thenextwave/pkg/eventbus"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/filestore"
|
"github.com/wavetermdev/thenextwave/pkg/filestore"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/shellexec"
|
"github.com/wavetermdev/thenextwave/pkg/shellexec"
|
||||||
@ -329,7 +328,7 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta waveobj
|
|||||||
shellInputCh := make(chan *BlockInputUnion, 32)
|
shellInputCh := make(chan *BlockInputUnion, 32)
|
||||||
bc.ShellInputCh = shellInputCh
|
bc.ShellInputCh = shellInputCh
|
||||||
messageCh := make(chan []byte, 32)
|
messageCh := make(chan []byte, 32)
|
||||||
ptyBuffer := wshutil.MakePtyBuffer(wshutil.WaveOSCPrefix, bc.ShellProc.Pty, messageCh)
|
ptyBuffer := wshutil.MakePtyBuffer(wshutil.WaveOSCPrefix, bc.ShellProc.Cmd, messageCh)
|
||||||
outputCh := make(chan []byte, 32)
|
outputCh := make(chan []byte, 32)
|
||||||
WshServerFactoryFn(messageCh, outputCh, wshrpc.RpcContext{BlockId: bc.BlockId, TabId: bc.TabId})
|
WshServerFactoryFn(messageCh, outputCh, wshrpc.RpcContext{BlockId: bc.BlockId, TabId: bc.TabId})
|
||||||
go func() {
|
go func() {
|
||||||
@ -368,17 +367,13 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta waveobj
|
|||||||
// handles input from the shellInputCh, sent to pty
|
// handles input from the shellInputCh, sent to pty
|
||||||
for ic := range shellInputCh {
|
for ic := range shellInputCh {
|
||||||
if len(ic.InputData) > 0 {
|
if len(ic.InputData) > 0 {
|
||||||
bc.ShellProc.Pty.Write(ic.InputData)
|
bc.ShellProc.Cmd.Write(ic.InputData)
|
||||||
}
|
}
|
||||||
if ic.TermSize != nil {
|
if ic.TermSize != nil {
|
||||||
log.Printf("SETTERMSIZE: %dx%d\n", ic.TermSize.Rows, ic.TermSize.Cols)
|
log.Printf("SETTERMSIZE: %dx%d\n", ic.TermSize.Rows, ic.TermSize.Cols)
|
||||||
err := pty.Setsize(bc.ShellProc.Pty, &pty.Winsize{Rows: uint16(ic.TermSize.Rows), Cols: uint16(ic.TermSize.Cols)})
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("error setting term size: %v\n", err)
|
|
||||||
}
|
|
||||||
err = bc.ShellProc.Cmd.SetSize(ic.TermSize.Rows, ic.TermSize.Cols)
|
err = bc.ShellProc.Cmd.SetSize(ic.TermSize.Rows, ic.TermSize.Cols)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error setting remote SIGWINCH: %v\n", err)
|
log.Printf("error setting pty size: %v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@ package shellexec
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/creack/pty"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,10 +16,12 @@ type ConnInterface interface {
|
|||||||
StdoutPipe() (io.ReadCloser, error)
|
StdoutPipe() (io.ReadCloser, error)
|
||||||
StderrPipe() (io.ReadCloser, error)
|
StderrPipe() (io.ReadCloser, error)
|
||||||
SetSize(w int, h int) error
|
SetSize(w int, h int) error
|
||||||
|
pty.Pty
|
||||||
}
|
}
|
||||||
|
|
||||||
type CmdWrap struct {
|
type CmdWrap struct {
|
||||||
Cmd *exec.Cmd
|
Cmd *exec.Cmd
|
||||||
|
pty.Pty
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cw CmdWrap) Kill() {
|
func (cw CmdWrap) Kill() {
|
||||||
@ -54,13 +56,18 @@ func (cw CmdWrap) StderrPipe() (io.ReadCloser, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cw CmdWrap) SetSize(w int, h int) error {
|
func (cw CmdWrap) SetSize(w int, h int) error {
|
||||||
|
err := pty.Setsize(cw.Pty, &pty.Winsize{Rows: uint16(w), Cols: uint16(h)})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionWrap struct {
|
type SessionWrap struct {
|
||||||
Session *ssh.Session
|
Session *ssh.Session
|
||||||
StartCmd string
|
StartCmd string
|
||||||
Tty *os.File
|
Tty pty.Tty
|
||||||
|
pty.Pty
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sw SessionWrap) Kill() {
|
func (sw SessionWrap) Kill() {
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -24,7 +25,6 @@ import (
|
|||||||
"github.com/wavetermdev/thenextwave/pkg/remote"
|
"github.com/wavetermdev/thenextwave/pkg/remote"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/util/shellutil"
|
"github.com/wavetermdev/thenextwave/pkg/util/shellutil"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/wavebase"
|
"github.com/wavetermdev/thenextwave/pkg/wavebase"
|
||||||
"golang.org/x/term"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TermSize struct {
|
type TermSize struct {
|
||||||
@ -41,7 +41,6 @@ type CommandOptsType struct {
|
|||||||
|
|
||||||
type ShellProc struct {
|
type ShellProc struct {
|
||||||
Cmd ConnInterface
|
Cmd ConnInterface
|
||||||
Pty *os.File
|
|
||||||
CloseOnce *sync.Once
|
CloseOnce *sync.Once
|
||||||
DoneCh chan any // closed after proc.Wait() returns
|
DoneCh chan any // closed after proc.Wait() returns
|
||||||
WaitErr error // WaitErr is synchronized by DoneCh (written before DoneCh is closed) and CloseOnce
|
WaitErr error // WaitErr is synchronized by DoneCh (written before DoneCh is closed) and CloseOnce
|
||||||
@ -52,7 +51,13 @@ func (sp *ShellProc) Close() {
|
|||||||
go func() {
|
go func() {
|
||||||
waitErr := sp.Cmd.Wait()
|
waitErr := sp.Cmd.Wait()
|
||||||
sp.SetWaitErrorAndSignalDone(waitErr)
|
sp.SetWaitErrorAndSignalDone(waitErr)
|
||||||
sp.Pty.Close()
|
|
||||||
|
// windows cannot handle the pty being
|
||||||
|
// closed twice, so we let the pty
|
||||||
|
// close itself instead
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
sp.Cmd.Close()
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +120,41 @@ func checkCwd(cwd string) error {
|
|||||||
|
|
||||||
var userHostRe = regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9._@\\-]*@)?([a-z0-9][a-z0-9.-]*)(?::([0-9]+))?$`)
|
var userHostRe = regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9._@\\-]*@)?([a-z0-9][a-z0-9.-]*)(?::([0-9]+))?$`)
|
||||||
|
|
||||||
|
type PipePty struct {
|
||||||
|
remoteStdinWrite *os.File
|
||||||
|
remoteStdoutRead *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *PipePty) Fd() uintptr {
|
||||||
|
return pp.remoteStdinWrite.Fd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *PipePty) Name() string {
|
||||||
|
return "pipe-pty"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *PipePty) Read(p []byte) (n int, err error) {
|
||||||
|
return pp.remoteStdoutRead.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *PipePty) Write(p []byte) (n int, err error) {
|
||||||
|
return pp.remoteStdinWrite.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *PipePty) Close() error {
|
||||||
|
err1 := pp.remoteStdinWrite.Close()
|
||||||
|
err2 := pp.remoteStdoutRead.Close()
|
||||||
|
|
||||||
|
if err1 != nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *PipePty) WriteString(s string) (n int, err error) {
|
||||||
|
return pp.Write([]byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
func StartRemoteShellProc(termSize TermSize, cmdStr string, cmdOpts CommandOptsType, remoteName string) (*ShellProc, error) {
|
func StartRemoteShellProc(termSize TermSize, cmdStr string, cmdOpts CommandOptsType, remoteName string) (*ShellProc, error) {
|
||||||
ctx, cancelFunc := context.WithTimeout(context.Background(), 60*time.Second)
|
ctx, cancelFunc := context.WithTimeout(context.Background(), 60*time.Second)
|
||||||
defer cancelFunc()
|
defer cancelFunc()
|
||||||
@ -158,14 +198,21 @@ func StartRemoteShellProc(termSize TermSize, cmdStr string, cmdOpts CommandOptsT
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// todo: connect pty output, etc
|
|
||||||
// redirect to fake pty???
|
|
||||||
|
|
||||||
cmdPty, cmdTty, err := pty.Open()
|
remoteStdinRead, remoteStdinWriteOurs, err := os.Pipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("opening new pty: %w", err)
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteStdoutReadOurs, remoteStdoutWrite, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pipePty := &PipePty{
|
||||||
|
remoteStdinWrite: remoteStdinWriteOurs,
|
||||||
|
remoteStdoutRead: remoteStdoutReadOurs,
|
||||||
}
|
}
|
||||||
term.MakeRaw(int(cmdTty.Fd()))
|
|
||||||
if termSize.Rows == 0 || termSize.Cols == 0 {
|
if termSize.Rows == 0 || termSize.Cols == 0 {
|
||||||
termSize.Rows = shellutil.DefaultTermRows
|
termSize.Rows = shellutil.DefaultTermRows
|
||||||
termSize.Cols = shellutil.DefaultTermCols
|
termSize.Cols = shellutil.DefaultTermCols
|
||||||
@ -173,10 +220,9 @@ func StartRemoteShellProc(termSize TermSize, cmdStr string, cmdOpts CommandOptsT
|
|||||||
if termSize.Rows <= 0 || termSize.Cols <= 0 {
|
if termSize.Rows <= 0 || termSize.Cols <= 0 {
|
||||||
return nil, fmt.Errorf("invalid term size: %v", termSize)
|
return nil, fmt.Errorf("invalid term size: %v", termSize)
|
||||||
}
|
}
|
||||||
pty.Setsize(cmdPty, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)})
|
session.Stdin = remoteStdinRead
|
||||||
session.Stdin = cmdTty
|
session.Stdout = remoteStdoutWrite
|
||||||
session.Stdout = cmdTty
|
session.Stderr = remoteStdoutWrite
|
||||||
session.Stderr = cmdTty
|
|
||||||
for envKey, envVal := range cmdOpts.Env {
|
for envKey, envVal := range cmdOpts.Env {
|
||||||
// note these might fail depending on server settings, but we still try
|
// note these might fail depending on server settings, but we still try
|
||||||
session.Setenv(envKey, envVal)
|
session.Setenv(envKey, envVal)
|
||||||
@ -184,13 +230,13 @@ func StartRemoteShellProc(termSize TermSize, cmdStr string, cmdOpts CommandOptsT
|
|||||||
|
|
||||||
session.RequestPty("xterm-256color", termSize.Rows, termSize.Cols, nil)
|
session.RequestPty("xterm-256color", termSize.Rows, termSize.Cols, nil)
|
||||||
|
|
||||||
sessionWrap := SessionWrap{session, cmdCombined, cmdTty}
|
sessionWrap := SessionWrap{session, cmdCombined, pipePty, pipePty}
|
||||||
err = sessionWrap.Start()
|
err = sessionWrap.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmdPty.Close()
|
pipePty.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &ShellProc{Cmd: sessionWrap, Pty: cmdPty, CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil
|
return &ShellProc{Cmd: sessionWrap, CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isZshShell(shellPath string) bool {
|
func isZshShell(shellPath string) bool {
|
||||||
@ -216,7 +262,7 @@ func StartShellProc(termSize TermSize, cmdStr string, cmdOpts CommandOptsType) (
|
|||||||
// add --rcfile
|
// add --rcfile
|
||||||
// cant set -l or -i with --rcfile
|
// cant set -l or -i with --rcfile
|
||||||
shellOpts = append(shellOpts, "--rcfile", shellutil.GetBashRcFileOverride())
|
shellOpts = append(shellOpts, "--rcfile", shellutil.GetBashRcFileOverride())
|
||||||
} else {
|
} else if runtime.GOOS != "windows" {
|
||||||
if cmdOpts.Login {
|
if cmdOpts.Login {
|
||||||
shellOpts = append(shellOpts, "-l")
|
shellOpts = append(shellOpts, "-l")
|
||||||
}
|
}
|
||||||
@ -252,10 +298,6 @@ func StartShellProc(termSize TermSize, cmdStr string, cmdOpts CommandOptsType) (
|
|||||||
}
|
}
|
||||||
shellutil.UpdateCmdEnv(ecmd, envToAdd)
|
shellutil.UpdateCmdEnv(ecmd, envToAdd)
|
||||||
shellutil.UpdateCmdEnv(ecmd, cmdOpts.Env)
|
shellutil.UpdateCmdEnv(ecmd, cmdOpts.Env)
|
||||||
cmdPty, cmdTty, err := pty.Open()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("opening new pty: %w", err)
|
|
||||||
}
|
|
||||||
if termSize.Rows == 0 || termSize.Cols == 0 {
|
if termSize.Rows == 0 || termSize.Cols == 0 {
|
||||||
termSize.Rows = shellutil.DefaultTermRows
|
termSize.Rows = shellutil.DefaultTermRows
|
||||||
termSize.Cols = shellutil.DefaultTermCols
|
termSize.Cols = shellutil.DefaultTermCols
|
||||||
@ -263,28 +305,17 @@ func StartShellProc(termSize TermSize, cmdStr string, cmdOpts CommandOptsType) (
|
|||||||
if termSize.Rows <= 0 || termSize.Cols <= 0 {
|
if termSize.Rows <= 0 || termSize.Cols <= 0 {
|
||||||
return nil, fmt.Errorf("invalid term size: %v", termSize)
|
return nil, fmt.Errorf("invalid term size: %v", termSize)
|
||||||
}
|
}
|
||||||
pty.Setsize(cmdPty, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)})
|
cmdPty, err := pty.StartWithSize(ecmd, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)})
|
||||||
ecmd.Stdin = cmdTty
|
|
||||||
ecmd.Stdout = cmdTty
|
|
||||||
ecmd.Stderr = cmdTty
|
|
||||||
ecmd.SysProcAttr = &syscall.SysProcAttr{}
|
|
||||||
setSysProcAttrs(ecmd)
|
|
||||||
err = ecmd.Start()
|
|
||||||
cmdTty.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmdPty.Close()
|
cmdPty.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &ShellProc{Cmd: CmdWrap{ecmd}, Pty: cmdPty, CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil
|
return &ShellProc{Cmd: CmdWrap{ecmd, cmdPty}, CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunSimpleCmdInPty(ecmd *exec.Cmd, termSize TermSize) ([]byte, error) {
|
func RunSimpleCmdInPty(ecmd *exec.Cmd, termSize TermSize) ([]byte, error) {
|
||||||
ecmd.Env = os.Environ()
|
ecmd.Env = os.Environ()
|
||||||
shellutil.UpdateCmdEnv(ecmd, shellutil.WaveshellLocalEnvVars(shellutil.DefaultTermType))
|
shellutil.UpdateCmdEnv(ecmd, shellutil.WaveshellLocalEnvVars(shellutil.DefaultTermType))
|
||||||
cmdPty, cmdTty, err := pty.Open()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("opening new pty: %w", err)
|
|
||||||
}
|
|
||||||
if termSize.Rows == 0 || termSize.Cols == 0 {
|
if termSize.Rows == 0 || termSize.Cols == 0 {
|
||||||
termSize.Rows = shellutil.DefaultTermRows
|
termSize.Rows = shellutil.DefaultTermRows
|
||||||
termSize.Cols = shellutil.DefaultTermCols
|
termSize.Cols = shellutil.DefaultTermCols
|
||||||
@ -292,19 +323,14 @@ func RunSimpleCmdInPty(ecmd *exec.Cmd, termSize TermSize) ([]byte, error) {
|
|||||||
if termSize.Rows <= 0 || termSize.Cols <= 0 {
|
if termSize.Rows <= 0 || termSize.Cols <= 0 {
|
||||||
return nil, fmt.Errorf("invalid term size: %v", termSize)
|
return nil, fmt.Errorf("invalid term size: %v", termSize)
|
||||||
}
|
}
|
||||||
pty.Setsize(cmdPty, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)})
|
cmdPty, err := pty.StartWithSize(ecmd, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)})
|
||||||
ecmd.Stdin = cmdTty
|
|
||||||
ecmd.Stdout = cmdTty
|
|
||||||
ecmd.Stderr = cmdTty
|
|
||||||
ecmd.SysProcAttr = &syscall.SysProcAttr{}
|
|
||||||
setSysProcAttrs(ecmd)
|
|
||||||
err = ecmd.Start()
|
|
||||||
cmdTty.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmdPty.Close()
|
cmdPty.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cmdPty.Close()
|
if runtime.GOOS != "windows" {
|
||||||
|
defer cmdPty.Close()
|
||||||
|
}
|
||||||
ioDone := make(chan bool)
|
ioDone := make(chan bool)
|
||||||
var outputBuf bytes.Buffer
|
var outputBuf bytes.Buffer
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -81,6 +81,9 @@ export PATH=$WAVETERM_WSHBINDIR:$PATH
|
|||||||
)
|
)
|
||||||
|
|
||||||
func DetectLocalShellPath() string {
|
func DetectLocalShellPath() string {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return "powershell.exe"
|
||||||
|
}
|
||||||
shellPath := GetMacUserShell()
|
shellPath := GetMacUserShell()
|
||||||
if shellPath == "" {
|
if shellPath == "" {
|
||||||
shellPath = os.Getenv("SHELL")
|
shellPath = os.Getenv("SHELL")
|
||||||
|
Loading…
Reference in New Issue
Block a user