2023-10-17 06:31:13 +02:00
|
|
|
// Copyright 2023, Command Line Inc.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2022-06-29 00:04:08 +02:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2022-07-07 03:59:46 +02:00
|
|
|
"context"
|
2023-09-01 07:03:38 +02:00
|
|
|
"errors"
|
2022-06-29 00:04:08 +02:00
|
|
|
"fmt"
|
2023-09-01 07:03:38 +02:00
|
|
|
"io"
|
2023-09-06 06:21:34 +02:00
|
|
|
"io/fs"
|
2022-06-29 00:04:08 +02:00
|
|
|
"os"
|
2022-08-09 23:23:59 +02:00
|
|
|
"os/exec"
|
2023-09-01 07:03:38 +02:00
|
|
|
"path/filepath"
|
2022-08-11 19:21:11 +02:00
|
|
|
"sort"
|
2022-08-09 23:23:59 +02:00
|
|
|
"strings"
|
2022-06-29 00:04:08 +02:00
|
|
|
"sync"
|
2022-11-27 22:47:18 +01:00
|
|
|
"time"
|
2022-06-29 00:04:08 +02:00
|
|
|
|
2022-08-09 23:23:59 +02:00
|
|
|
"github.com/alessio/shellescape"
|
2023-10-16 22:25:53 +02:00
|
|
|
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
|
|
|
|
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
"github.com/wavetermdev/waveterm/waveshell/pkg/shellapi"
|
2023-10-16 22:25:53 +02:00
|
|
|
"github.com/wavetermdev/waveterm/waveshell/pkg/shexec"
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
"github.com/wavetermdev/waveterm/waveshell/pkg/utilfn"
|
2024-02-16 02:42:43 +01:00
|
|
|
"github.com/wavetermdev/waveterm/waveshell/pkg/wlog"
|
2022-06-29 00:04:08 +02:00
|
|
|
)
|
|
|
|
|
2023-09-01 07:03:38 +02:00
|
|
|
const MaxFileDataPacketSize = 16 * 1024
|
|
|
|
const WriteFileContextTimeout = 30 * time.Second
|
|
|
|
const cleanLoopTime = 5 * time.Second
|
|
|
|
const MaxWriteFileContextData = 100
|
2024-03-21 07:38:05 +01:00
|
|
|
const InboundRpcErrorTimeoutTime = 30 * time.Second
|
2023-09-01 07:03:38 +02:00
|
|
|
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
type shellStateMapKey struct {
|
|
|
|
ShellType string
|
|
|
|
Hash string
|
|
|
|
}
|
|
|
|
|
|
|
|
type ShellStateMap struct {
|
|
|
|
Lock *sync.Mutex
|
|
|
|
StateMap map[shellStateMapKey]*packet.ShellState // shelltype+hash -> state
|
|
|
|
CurrentStateMap map[string]string // shelltype -> hash
|
|
|
|
}
|
|
|
|
|
2022-07-07 02:16:45 +02:00
|
|
|
// TODO create unblockable packet-sender (backed by an array) for clientproc
|
2022-06-29 00:04:08 +02:00
|
|
|
type MServer struct {
|
2023-09-01 07:03:38 +02:00
|
|
|
Lock *sync.Mutex
|
|
|
|
MainInput *packet.PacketParser
|
|
|
|
Sender *packet.PacketSender
|
|
|
|
ClientMap map[base.CommandKey]*shexec.ClientProc
|
|
|
|
Debug bool
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
WriteErrorCh chan bool // closed if there is a I/O write error
|
2023-09-01 07:03:38 +02:00
|
|
|
WriteErrorChOnce *sync.Once
|
|
|
|
Done bool
|
2024-03-21 07:38:05 +01:00
|
|
|
InboundRpcHandlers map[string]RpcHandler
|
|
|
|
InboundRpcErrorSent map[string]time.Time // limits the amount of error messages sent back to the client
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ RpcHandler = (*WriteFileContext)(nil)
|
|
|
|
|
|
|
|
type RpcHandler interface {
|
|
|
|
GetTimeoutTime() time.Time
|
|
|
|
DispatchPacket(reqId string, pk packet.RpcFollowUpPacketType)
|
|
|
|
UnRegisterCallback()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MServer) registerRpcHandler(reqId string, handler RpcHandler) error {
|
|
|
|
if handler == nil {
|
|
|
|
return errors.New("handler is nil")
|
|
|
|
}
|
|
|
|
m.Lock.Lock()
|
|
|
|
defer m.Lock.Unlock()
|
|
|
|
if m.InboundRpcHandlers[reqId] != nil {
|
|
|
|
return errors.New("handler already registered")
|
|
|
|
}
|
|
|
|
delete(m.InboundRpcErrorSent, reqId)
|
|
|
|
m.InboundRpcHandlers[reqId] = handler
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MServer) unregisterRpcHandler(reqId string) {
|
|
|
|
m.Lock.Lock()
|
|
|
|
defer m.Lock.Unlock()
|
|
|
|
handler := m.InboundRpcHandlers[reqId]
|
|
|
|
delete(m.InboundRpcHandlers, reqId)
|
|
|
|
if handler != nil {
|
|
|
|
handler.UnRegisterCallback()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// limits the number of error responses that can be sent
|
|
|
|
func (m *MServer) sendInboundRpcError(reqId string, err error) {
|
|
|
|
m.Lock.Lock()
|
|
|
|
defer m.Lock.Unlock()
|
|
|
|
if _, ok := m.InboundRpcErrorSent[reqId]; ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
m.InboundRpcErrorSent[reqId] = time.Now().Add(InboundRpcErrorTimeoutTime)
|
|
|
|
m.Sender.SendErrorResponse(reqId, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns true if dispatched to a waiting RPC handler
|
|
|
|
func (m *MServer) dispatchRpcFollowUp(pk packet.RpcFollowUpPacketType) bool {
|
|
|
|
if pk == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
reqId := pk.GetAssociatedReqId()
|
|
|
|
m.Lock.Lock()
|
|
|
|
defer m.Lock.Unlock()
|
|
|
|
if rh := m.InboundRpcHandlers[reqId]; rh != nil {
|
|
|
|
rh.DispatchPacket(reqId, pk)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MServer) cleanRpcHandlers() {
|
|
|
|
var staleHandlers []RpcHandler
|
|
|
|
now := time.Now()
|
|
|
|
m.Lock.Lock()
|
|
|
|
for reqId, rh := range m.InboundRpcHandlers {
|
|
|
|
if now.After(rh.GetTimeoutTime()) {
|
|
|
|
staleHandlers = append(staleHandlers, rh)
|
|
|
|
delete(m.InboundRpcHandlers, reqId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for reqId, timeoutTime := range m.InboundRpcErrorSent {
|
|
|
|
if now.After(timeoutTime) {
|
|
|
|
delete(m.InboundRpcErrorSent, reqId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m.Lock.Unlock()
|
|
|
|
// we do this outside of m.Lock just in case there is some lock contention (UnRegisterCallback might be slow)
|
|
|
|
for _, rh := range staleHandlers {
|
|
|
|
rh.UnRegisterCallback()
|
|
|
|
}
|
2023-09-01 07:03:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type WriteFileContext struct {
|
|
|
|
CVar *sync.Cond
|
|
|
|
Data []*packet.FileDataPacketType
|
|
|
|
LastActive time.Time
|
|
|
|
Err error
|
|
|
|
Done bool
|
2022-06-29 00:04:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MServer) Close() {
|
|
|
|
m.Sender.Close()
|
|
|
|
m.Sender.WaitForDone()
|
2023-09-01 07:03:38 +02:00
|
|
|
m.Lock.Lock()
|
|
|
|
defer m.Lock.Unlock()
|
|
|
|
m.Done = true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MServer) checkDone() bool {
|
|
|
|
m.Lock.Lock()
|
|
|
|
defer m.Lock.Unlock()
|
|
|
|
return m.Done
|
|
|
|
}
|
|
|
|
|
2024-03-21 07:38:05 +01:00
|
|
|
func (wfc *WriteFileContext) GetTimeoutTime() time.Time {
|
|
|
|
return wfc.LastActive.Add(WriteFileContextTimeout)
|
2023-09-01 07:03:38 +02:00
|
|
|
}
|
|
|
|
|
2024-03-21 07:38:05 +01:00
|
|
|
func (wfc *WriteFileContext) DispatchPacket(reqId string, pkArg packet.RpcFollowUpPacketType) {
|
|
|
|
dataPk, ok := pkArg.(*packet.FileDataPacketType)
|
|
|
|
if !ok {
|
2023-09-01 07:03:38 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
wfc.CVar.L.Lock()
|
|
|
|
defer wfc.CVar.L.Unlock()
|
|
|
|
if wfc.Done || wfc.Err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if len(wfc.Data) > MaxWriteFileContextData {
|
|
|
|
wfc.Err = errors.New("write-file buffer length exceeded")
|
|
|
|
wfc.Data = nil
|
|
|
|
wfc.CVar.Broadcast()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
wfc.LastActive = time.Now()
|
2024-03-21 07:38:05 +01:00
|
|
|
wfc.Data = append(wfc.Data, dataPk)
|
2023-09-01 07:03:38 +02:00
|
|
|
wfc.CVar.Signal()
|
|
|
|
}
|
|
|
|
|
2024-03-21 07:38:05 +01:00
|
|
|
func (wfc *WriteFileContext) UnRegisterCallback() {
|
2023-09-01 07:03:38 +02:00
|
|
|
wfc.CVar.L.Lock()
|
|
|
|
defer wfc.CVar.L.Unlock()
|
|
|
|
wfc.Done = true
|
|
|
|
wfc.Data = nil
|
|
|
|
wfc.CVar.Broadcast()
|
|
|
|
}
|
|
|
|
|
2022-06-29 04:01:33 +02:00
|
|
|
func (m *MServer) ProcessCommandPacket(pk packet.CommandPacketType) {
|
|
|
|
ck := pk.GetCK()
|
|
|
|
if ck == "" {
|
2022-11-27 22:47:18 +01:00
|
|
|
m.Sender.SendMessageFmt("received '%s' packet without ck", pk.GetType())
|
2022-06-29 04:01:33 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
m.Lock.Lock()
|
2022-07-07 02:16:45 +02:00
|
|
|
cproc := m.ClientMap[ck]
|
2022-06-29 04:01:33 +02:00
|
|
|
m.Lock.Unlock()
|
2022-07-07 02:16:45 +02:00
|
|
|
if cproc == nil {
|
2024-03-14 02:52:41 +01:00
|
|
|
wlog.Logf("no client proc for ck %q, pk=%s", ck, packet.AsString(pk))
|
2022-06-29 04:01:33 +02:00
|
|
|
return
|
|
|
|
}
|
2022-07-07 02:16:45 +02:00
|
|
|
cproc.Input.SendPacket(pk)
|
2022-06-29 23:29:38 +02:00
|
|
|
}
|
|
|
|
|
2022-08-11 19:21:11 +02:00
|
|
|
func runSingleCompGen(cwd string, compType string, prefix string) ([]string, bool, error) {
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
sapi, err := shellapi.MakeShellApi(packet.ShellType_bash)
|
|
|
|
if err != nil {
|
|
|
|
return nil, false, err
|
|
|
|
}
|
2022-08-11 19:21:11 +02:00
|
|
|
if !packet.IsValidCompGenType(compType) {
|
|
|
|
return nil, false, fmt.Errorf("invalid compgen type '%s'", compType)
|
2022-08-09 23:23:59 +02:00
|
|
|
}
|
2022-08-11 19:21:11 +02:00
|
|
|
compGenCmdStr := fmt.Sprintf("cd %s; compgen -A %s -- %s | sort | uniq | head -n %d", shellescape.Quote(cwd), shellescape.Quote(compType), shellescape.Quote(prefix), packet.MaxCompGenValues+1)
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
ecmd := exec.Command(sapi.GetLocalShellPath(), "-c", compGenCmdStr)
|
2022-08-09 23:23:59 +02:00
|
|
|
outputBytes, err := ecmd.Output()
|
|
|
|
if err != nil {
|
2022-08-11 19:21:11 +02:00
|
|
|
return nil, false, fmt.Errorf("compgen error: %w", err)
|
2022-08-09 23:23:59 +02:00
|
|
|
}
|
|
|
|
outputStr := string(outputBytes)
|
|
|
|
parts := strings.Split(outputStr, "\n")
|
|
|
|
if len(parts) > 0 && parts[len(parts)-1] == "" {
|
|
|
|
parts = parts[0 : len(parts)-1]
|
|
|
|
}
|
2022-08-11 01:07:41 +02:00
|
|
|
hasMore := false
|
|
|
|
if len(parts) > packet.MaxCompGenValues {
|
|
|
|
hasMore = true
|
|
|
|
parts = parts[0:packet.MaxCompGenValues]
|
|
|
|
}
|
2022-08-11 19:21:11 +02:00
|
|
|
return parts, hasMore, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func appendSlashes(comps []string) {
|
|
|
|
for idx, comp := range comps {
|
|
|
|
comps[idx] = comp + "/"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func strArrToMap(strs []string) map[string]bool {
|
|
|
|
rtn := make(map[string]bool)
|
|
|
|
for _, s := range strs {
|
|
|
|
rtn[s] = true
|
|
|
|
}
|
|
|
|
return rtn
|
|
|
|
}
|
|
|
|
|
2022-11-23 19:51:51 +01:00
|
|
|
func (m *MServer) runMixedCompGen(compPk *packet.CompGenPacketType) {
|
2022-08-11 19:21:11 +02:00
|
|
|
// get directories and files, unique them and put slashes on directories for completion
|
|
|
|
reqId := compPk.GetReqId()
|
|
|
|
compDirs, hasMoreDirs, err := runSingleCompGen(compPk.Cwd, "directory", compPk.Prefix)
|
|
|
|
if err != nil {
|
|
|
|
m.Sender.SendErrorResponse(reqId, err)
|
|
|
|
return
|
|
|
|
}
|
2022-11-23 19:51:51 +01:00
|
|
|
compFiles, hasMoreFiles, err := runSingleCompGen(compPk.Cwd, compPk.CompType, compPk.Prefix)
|
2022-08-11 19:21:11 +02:00
|
|
|
if err != nil {
|
|
|
|
m.Sender.SendErrorResponse(reqId, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
dirMap := strArrToMap(compDirs)
|
|
|
|
// seed comps with dirs (but append slashes)
|
|
|
|
comps := compDirs
|
|
|
|
appendSlashes(comps)
|
|
|
|
// add files that are not directories (look up in dirMap)
|
|
|
|
for _, file := range compFiles {
|
|
|
|
if dirMap[file] {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
comps = append(comps, file)
|
|
|
|
}
|
|
|
|
sort.Strings(comps) // resort
|
|
|
|
m.Sender.SendResponse(reqId, map[string]interface{}{"comps": comps, "hasmore": (hasMoreFiles || hasMoreDirs)})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MServer) runCompGen(compPk *packet.CompGenPacketType) {
|
|
|
|
reqId := compPk.GetReqId()
|
2022-11-23 19:51:51 +01:00
|
|
|
if compPk.CompType == "file" || compPk.CompType == "command" {
|
|
|
|
m.runMixedCompGen(compPk)
|
2022-08-11 19:21:11 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
comps, hasMore, err := runSingleCompGen(compPk.Cwd, compPk.CompType, compPk.Prefix)
|
|
|
|
if err != nil {
|
|
|
|
m.Sender.SendErrorResponse(reqId, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if compPk.CompType == "directory" {
|
|
|
|
appendSlashes(comps)
|
|
|
|
}
|
|
|
|
m.Sender.SendResponse(reqId, map[string]interface{}{"comps": comps, "hasmore": hasMore})
|
2022-08-09 23:23:59 +02:00
|
|
|
}
|
|
|
|
|
2024-03-21 07:38:05 +01:00
|
|
|
type ReinitRpcHandler struct {
|
|
|
|
ReqId string
|
|
|
|
TimeoutTime time.Time
|
|
|
|
StdinDataCh chan []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rh *ReinitRpcHandler) GetTimeoutTime() time.Time {
|
|
|
|
return rh.TimeoutTime
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rh *ReinitRpcHandler) DispatchPacket(reqId string, pkArg packet.RpcFollowUpPacketType) {
|
|
|
|
rpcInput, ok := pkArg.(*packet.RpcInputPacketType)
|
|
|
|
if !ok {
|
|
|
|
wlog.Logf("reinit rpc handler: invalid packet type: %T", pkArg)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// nonblocking send
|
|
|
|
select {
|
|
|
|
case rh.StdinDataCh <- rpcInput.Data:
|
|
|
|
default:
|
|
|
|
wlog.Logf("reinit rpc handler: stdin data channel full, dropping data")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rh *ReinitRpcHandler) UnRegisterCallback() {
|
|
|
|
close(rh.StdinDataCh)
|
|
|
|
}
|
|
|
|
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
func (m *MServer) reinit(reqId string, shellType string) {
|
2024-03-21 07:38:05 +01:00
|
|
|
stdinDataCh := make(chan []byte, 10)
|
|
|
|
rh := &ReinitRpcHandler{
|
|
|
|
ReqId: reqId,
|
|
|
|
TimeoutTime: time.Now().Add(30 * time.Second),
|
|
|
|
StdinDataCh: stdinDataCh,
|
|
|
|
}
|
|
|
|
m.registerRpcHandler(reqId, rh)
|
|
|
|
defer m.unregisterRpcHandler(reqId)
|
|
|
|
ssPk, err := m.MakeShellStatePacket(reqId, shellType, stdinDataCh)
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
if err != nil {
|
2024-03-21 07:38:05 +01:00
|
|
|
m.Sender.SendErrorResponse(reqId, fmt.Errorf("error initializing shell: %w", err))
|
2022-11-27 22:47:18 +01:00
|
|
|
return
|
|
|
|
}
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
ssPk.RespId = reqId
|
|
|
|
m.Sender.SendPacket(ssPk)
|
2022-10-28 06:59:17 +02:00
|
|
|
}
|
|
|
|
|
2024-03-21 07:38:05 +01:00
|
|
|
func (m *MServer) MakeShellStatePacket(reqId string, shellType string, stdinDataCh chan []byte) (*packet.ShellStatePacketType, error) {
|
2024-03-20 00:38:38 +01:00
|
|
|
sapi, err := shellapi.MakeShellApi(shellType)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
rtnCh := make(chan shellapi.ShellStateOutput, 1)
|
reinit updates (#500)
* working on re-init when you create a tab. some refactoring of existing reinit to make the messaging clearer. auto-connect, etc.
* working to remove the 'default' shell states out of MShellProc. each tab should have its own state that gets set on open.
* refactor newtab settings into individual components (and move to a new file)
* more refactoring of tab settings -- use same control in settings and newtab
* have screensettings use the same newtab settings components
* use same conn dropdown, fix classes, update some of the confirm messages to be less confusing (replace screen with tab)
* force a cr on a new tab to initialize state in a new line. poc right now, need to add to new workspace workflow as well
* small fixups
* remove nohist from GetRawStr, make const
* update hover behavior for tabs
* fix interaction between change remote dropdown, cmdinput, error handling, and selecting a remote
* only switch screen remote if the activemainview is session (new remote flow). don't switch it if we're on the connections page which is confusing. also make it interactive
* fix wording on tos modal
* allow empty workspaces. also allow the last workspace to be deleted. (prep for new startup sequence where we initialize the first session after tos modal)
* add some dead code that might come in use later (when we change how we show connection in cmdinput)
* working a cople different angles. new settings tab-pulldown (likely orphaned). and then allowing null activeScreen and null activeSession in workspaceview (show appropriate messages, and give buttons to create new tabs/workspaces). prep for new startup flow
* don't call initActiveShells anymore. also call ensureWorkspace() on TOS close
* trying to use new pulldown screen settings
* experiment with an escape keybinding
* working on tab settings close triggers
* close tab settings on tab switch
* small updates to tos popup, reorder, update button text/size, small wording updates
* when deleting a screen, send SIGHUP to all running commands
* not sure how this happened, lineid should not be passed to setLineFocus
* remove context timeouts for ReInit (it is now interactive, so it gets canceled like a normal command -- via ^C, and should not timeout on its own)
* deal with screen/session tombstones updates (ignore to quite warning)
* remove defaultfestate from remote
* fix issue with removing default ris
* remove dead code
* open the settings pulldown for new screens
* update prompt to show when the shell is still initializing (or if it failed)
* switch buttons to use wave button class, update messages, and add warning for no shell state
* all an override of rptr for dyncmds. needed for the 'connect' command (we need to set the rptr to the *new* connection rather than the old one)
* remove old commented out code
2024-03-27 08:22:57 +01:00
|
|
|
ctx, cancelFn := context.WithCancel(context.Background())
|
|
|
|
defer cancelFn()
|
|
|
|
go sapi.GetShellState(ctx, rtnCh, stdinDataCh)
|
2024-03-20 00:38:38 +01:00
|
|
|
for ssOutput := range rtnCh {
|
|
|
|
if ssOutput.Error != "" {
|
|
|
|
return nil, errors.New(ssOutput.Error)
|
|
|
|
}
|
|
|
|
if ssOutput.ShellState != nil {
|
|
|
|
rtn := packet.MakeShellStatePacket()
|
|
|
|
rtn.State = ssOutput.ShellState
|
|
|
|
rtn.Stats = ssOutput.Stats
|
|
|
|
return rtn, nil
|
|
|
|
}
|
|
|
|
if ssOutput.Output != nil {
|
|
|
|
dataPk := packet.MakeFileDataPacket(reqId)
|
|
|
|
dataPk.Data = ssOutput.Output
|
|
|
|
m.Sender.SendPacket(dataPk)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2023-09-06 06:21:34 +02:00
|
|
|
func makeTemp(path string, mode fs.FileMode) (*os.File, error) {
|
|
|
|
dirName := filepath.Dir(path)
|
|
|
|
baseName := filepath.Base(path)
|
|
|
|
baseTempName := baseName + ".tmp."
|
|
|
|
writeFd, err := os.CreateTemp(dirName, baseTempName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
err = writeFd.Chmod(mode)
|
|
|
|
if err != nil {
|
|
|
|
writeFd.Close()
|
|
|
|
os.Remove(writeFd.Name())
|
|
|
|
return nil, fmt.Errorf("error setting tempfile permissions: %w", err)
|
|
|
|
}
|
|
|
|
return writeFd, nil
|
|
|
|
}
|
|
|
|
|
2023-09-07 06:45:15 +02:00
|
|
|
func checkFileWritable(path string) error {
|
|
|
|
finfo, err := os.Stat(path) // ok to follow symlinks
|
|
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
|
|
dirName := filepath.Dir(path)
|
|
|
|
dirInfo, err := os.Stat(dirName)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("file does not exist, error trying to stat parent directory: %w", err)
|
|
|
|
}
|
|
|
|
if !dirInfo.IsDir() {
|
|
|
|
return fmt.Errorf("file does not exist, parent path [%s] is not a directory", dirName)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
} else {
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("cannot stat: %w", err)
|
|
|
|
}
|
|
|
|
if finfo.IsDir() {
|
|
|
|
return fmt.Errorf("invalid path, cannot write a directory")
|
|
|
|
}
|
|
|
|
if (finfo.Mode() & fs.ModeSymlink) != 0 {
|
|
|
|
return fmt.Errorf("writefile does not support symlinks") // note this shouldn't happen because we're using Stat (not Lstat)
|
|
|
|
}
|
|
|
|
if (finfo.Mode() & (fs.ModeNamedPipe | fs.ModeSocket | fs.ModeDevice)) != 0 {
|
|
|
|
return fmt.Errorf("writefile does not support special files (named pipes, sockets, devices): mode=%v", finfo.Mode())
|
|
|
|
}
|
|
|
|
writePerm := (finfo.Mode().Perm() & 0o222)
|
|
|
|
if writePerm == 0 {
|
|
|
|
return fmt.Errorf("file is not writable, perms: %v", finfo.Mode().Perm())
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func copyFile(dstName string, srcName string) error {
|
|
|
|
srcFd, err := os.Open(srcName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer srcFd.Close()
|
2023-09-07 06:57:41 +02:00
|
|
|
dstFd, err := os.OpenFile(dstName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o666) // use 666 because OpenFile respects umask
|
2023-09-07 06:45:15 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// we don't defer dstFd.Close() so we can return an error if dstFd.Close() returns an error
|
|
|
|
_, err = io.Copy(dstFd, srcFd)
|
|
|
|
if err != nil {
|
|
|
|
dstFd.Close()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return dstFd.Close()
|
|
|
|
}
|
|
|
|
|
2023-09-01 07:03:38 +02:00
|
|
|
func (m *MServer) writeFile(pk *packet.WriteFilePacketType, wfc *WriteFileContext) {
|
2024-03-21 07:38:05 +01:00
|
|
|
defer m.unregisterRpcHandler(pk.ReqId)
|
2023-09-01 07:03:38 +02:00
|
|
|
if pk.Path == "" {
|
|
|
|
resp := packet.MakeWriteFileReadyPacket(pk.ReqId)
|
|
|
|
resp.Error = "invalid write-file request, no path specified"
|
|
|
|
m.Sender.SendPacket(resp)
|
|
|
|
return
|
|
|
|
}
|
2023-09-07 06:45:15 +02:00
|
|
|
err := checkFileWritable(pk.Path)
|
2023-09-01 07:03:38 +02:00
|
|
|
if err != nil {
|
|
|
|
resp := packet.MakeWriteFileReadyPacket(pk.ReqId)
|
|
|
|
resp.Error = err.Error()
|
|
|
|
m.Sender.SendPacket(resp)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var writeFd *os.File
|
|
|
|
if pk.UseTemp {
|
2024-05-02 23:16:00 +02:00
|
|
|
writeFd, err = os.CreateTemp("", "waveshell.writefile.*") // "" means make this file in standard TempDir
|
2023-09-01 07:03:38 +02:00
|
|
|
if err != nil {
|
|
|
|
resp := packet.MakeWriteFileReadyPacket(pk.ReqId)
|
2023-09-07 06:45:15 +02:00
|
|
|
resp.Error = fmt.Sprintf("cannot create temp file: %v", err)
|
2023-09-01 07:03:38 +02:00
|
|
|
m.Sender.SendPacket(resp)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
} else {
|
2023-09-07 06:57:41 +02:00
|
|
|
writeFd, err = os.OpenFile(pk.Path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o666) // use 666 because OpenFile respects umask
|
2023-09-01 07:03:38 +02:00
|
|
|
if err != nil {
|
|
|
|
resp := packet.MakeWriteFileReadyPacket(pk.ReqId)
|
|
|
|
resp.Error = fmt.Sprintf("write-file could not open file: %v", err)
|
|
|
|
m.Sender.SendPacket(resp)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ok, so now writeFd is valid, send the "ready" response
|
|
|
|
resp := packet.MakeWriteFileReadyPacket(pk.ReqId)
|
|
|
|
m.Sender.SendPacket(resp)
|
|
|
|
|
|
|
|
// now we wait for data (cond var)
|
|
|
|
// this Unlock() runs first (because it is a later defer) so we can still run wfc.setDone() safely
|
|
|
|
wfc.CVar.L.Lock()
|
|
|
|
defer wfc.CVar.L.Unlock()
|
|
|
|
var doneErr error
|
|
|
|
for {
|
|
|
|
if wfc.Done {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if wfc.Err != nil {
|
|
|
|
doneErr = wfc.Err
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if len(wfc.Data) == 0 {
|
|
|
|
wfc.CVar.Wait()
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
dataPk := wfc.Data[0]
|
|
|
|
wfc.Data = wfc.Data[1:]
|
|
|
|
if dataPk.Error != "" {
|
|
|
|
doneErr = fmt.Errorf("error received from client: %v", errors.New(dataPk.Error))
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if len(dataPk.Data) > 0 {
|
|
|
|
_, err := writeFd.Write(dataPk.Data)
|
|
|
|
if err != nil {
|
|
|
|
doneErr = fmt.Errorf("error writing data to file: %v", err)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if dataPk.Eof {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
closeErr := writeFd.Close()
|
|
|
|
if doneErr == nil && closeErr != nil {
|
|
|
|
doneErr = fmt.Errorf("error closing file: %v", closeErr)
|
|
|
|
}
|
|
|
|
if pk.UseTemp {
|
|
|
|
if doneErr != nil {
|
|
|
|
os.Remove(writeFd.Name())
|
|
|
|
} else {
|
2023-09-07 06:45:15 +02:00
|
|
|
// copy file between writeFd.Name() and pk.Path
|
|
|
|
copyErr := copyFile(pk.Path, writeFd.Name())
|
|
|
|
if err != nil {
|
|
|
|
doneErr = fmt.Errorf("error writing file: %v", copyErr)
|
2023-09-01 07:03:38 +02:00
|
|
|
}
|
2023-09-07 06:45:15 +02:00
|
|
|
os.Remove(writeFd.Name())
|
2023-09-01 07:03:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
donePk := packet.MakeWriteFileDonePacket(pk.ReqId)
|
|
|
|
if doneErr != nil {
|
|
|
|
donePk.Error = doneErr.Error()
|
|
|
|
}
|
|
|
|
m.Sender.SendPacket(donePk)
|
|
|
|
}
|
|
|
|
|
2023-09-07 06:45:15 +02:00
|
|
|
func (m *MServer) returnStreamFileNewFileResponse(pk *packet.StreamFilePacketType) {
|
|
|
|
// ok, file doesn't exist, so try to check the directory at least to see if we can write a file here
|
|
|
|
resp := packet.MakeStreamFileResponse(pk.ReqId)
|
|
|
|
defer func() {
|
|
|
|
if resp.Error == "" {
|
|
|
|
resp.Done = true
|
|
|
|
}
|
|
|
|
m.Sender.SendPacket(resp)
|
|
|
|
}()
|
|
|
|
dirName := filepath.Dir(pk.Path)
|
|
|
|
dirInfo, err := os.Stat(dirName)
|
|
|
|
if err != nil {
|
|
|
|
resp.Error = fmt.Sprintf("file does not exist, error trying to stat parent directory: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !dirInfo.IsDir() {
|
|
|
|
resp.Error = fmt.Sprintf("file does not exist, parent path [%s] is not a directory", dirName)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
resp.Info = &packet.FileInfo{
|
|
|
|
Name: pk.Path,
|
|
|
|
Size: 0,
|
|
|
|
ModTs: 0,
|
|
|
|
IsDir: false,
|
|
|
|
Perm: int(dirInfo.Mode().Perm()),
|
|
|
|
NotFound: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-01 07:03:38 +02:00
|
|
|
func (m *MServer) streamFile(pk *packet.StreamFilePacketType) {
|
|
|
|
resp := packet.MakeStreamFileResponse(pk.ReqId)
|
|
|
|
finfo, err := os.Stat(pk.Path)
|
2023-09-07 06:45:15 +02:00
|
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
|
|
// special return
|
|
|
|
m.returnStreamFileNewFileResponse(pk)
|
|
|
|
return
|
|
|
|
}
|
2023-09-01 07:03:38 +02:00
|
|
|
if err != nil {
|
|
|
|
resp.Error = fmt.Sprintf("cannot stat file %q: %v", pk.Path, err)
|
|
|
|
m.Sender.SendPacket(resp)
|
|
|
|
return
|
|
|
|
}
|
2024-04-05 19:52:04 +02:00
|
|
|
mimeType := utilfn.DetectMimeType(pk.Path)
|
2023-09-01 07:03:38 +02:00
|
|
|
resp.Info = &packet.FileInfo{
|
2024-04-05 19:52:04 +02:00
|
|
|
Name: pk.Path,
|
|
|
|
Size: finfo.Size(),
|
|
|
|
ModTs: finfo.ModTime().UnixMilli(),
|
|
|
|
IsDir: finfo.IsDir(),
|
|
|
|
MimeType: mimeType,
|
|
|
|
Perm: int(finfo.Mode().Perm()),
|
2023-09-01 07:03:38 +02:00
|
|
|
}
|
|
|
|
if pk.StatOnly {
|
|
|
|
resp.Done = true
|
|
|
|
m.Sender.SendPacket(resp)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// like the http Range header. range header is end inclusive. for us, endByte is non-inclusive (so we add 1)
|
|
|
|
var startByte, endByte int64
|
|
|
|
if len(pk.ByteRange) == 0 {
|
|
|
|
endByte = finfo.Size()
|
|
|
|
} else if len(pk.ByteRange) == 1 && pk.ByteRange[0] >= 0 {
|
|
|
|
startByte = pk.ByteRange[0]
|
|
|
|
endByte = finfo.Size()
|
|
|
|
} else if len(pk.ByteRange) == 1 && pk.ByteRange[0] < 0 {
|
|
|
|
startByte = finfo.Size() + pk.ByteRange[0] // "+" since ByteRange[0] is less than 0
|
|
|
|
endByte = finfo.Size()
|
|
|
|
} else if len(pk.ByteRange) == 2 {
|
|
|
|
startByte = pk.ByteRange[0]
|
|
|
|
endByte = pk.ByteRange[1] + 1
|
|
|
|
} else {
|
|
|
|
resp.Error = fmt.Sprintf("invalid byte range (%d entries)", len(pk.ByteRange))
|
|
|
|
m.Sender.SendPacket(resp)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if startByte < 0 {
|
|
|
|
startByte = 0
|
|
|
|
}
|
|
|
|
if endByte > finfo.Size() {
|
|
|
|
endByte = finfo.Size()
|
|
|
|
}
|
|
|
|
if startByte >= endByte {
|
|
|
|
resp.Done = true
|
|
|
|
m.Sender.SendPacket(resp)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fd, err := os.Open(pk.Path)
|
|
|
|
if err != nil {
|
|
|
|
resp.Error = fmt.Sprintf("opening file: %v", err)
|
|
|
|
m.Sender.SendPacket(resp)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer fd.Close()
|
|
|
|
m.Sender.SendPacket(resp)
|
|
|
|
var buffer [MaxFileDataPacketSize]byte
|
|
|
|
var sentDone bool
|
|
|
|
first := true
|
|
|
|
for ; startByte < endByte; startByte += MaxFileDataPacketSize {
|
|
|
|
if !first {
|
|
|
|
// throttle packet sending @ 1000 packets/s, or 16M/s
|
|
|
|
time.Sleep(1 * time.Millisecond)
|
|
|
|
}
|
|
|
|
first = false
|
|
|
|
readLen := int64Min(MaxFileDataPacketSize, endByte-startByte)
|
|
|
|
bufSlice := buffer[0:readLen]
|
|
|
|
nr, err := fd.ReadAt(bufSlice, startByte)
|
|
|
|
dataPk := packet.MakeFileDataPacket(pk.ReqId)
|
|
|
|
dataPk.Data = make([]byte, nr)
|
|
|
|
copy(dataPk.Data, bufSlice)
|
|
|
|
if err == io.EOF {
|
|
|
|
dataPk.Eof = true
|
|
|
|
} else if err != nil {
|
|
|
|
dataPk.Error = err.Error()
|
|
|
|
}
|
|
|
|
m.Sender.SendPacket(dataPk)
|
|
|
|
if dataPk.GetResponseDone() {
|
|
|
|
sentDone = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !sentDone {
|
|
|
|
dataPk := packet.MakeFileDataPacket(pk.ReqId)
|
|
|
|
dataPk.Eof = true
|
|
|
|
m.Sender.SendPacket(dataPk)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func int64Min(v1 int64, v2 int64) int64 {
|
|
|
|
if v1 < v2 {
|
|
|
|
return v1
|
|
|
|
}
|
|
|
|
return v2
|
|
|
|
}
|
|
|
|
|
2022-08-09 23:23:59 +02:00
|
|
|
func (m *MServer) ProcessRpcPacket(pk packet.RpcPacketType) {
|
|
|
|
reqId := pk.GetReqId()
|
|
|
|
if cdPk, ok := pk.(*packet.CdPacketType); ok {
|
|
|
|
err := os.Chdir(cdPk.Dir)
|
|
|
|
if err != nil {
|
|
|
|
m.Sender.SendErrorResponse(reqId, fmt.Errorf("cannot change directory: %w", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
m.Sender.SendResponse(reqId, true)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if compPk, ok := pk.(*packet.CompGenPacketType); ok {
|
|
|
|
go m.runCompGen(compPk)
|
|
|
|
return
|
|
|
|
}
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
if reinitPk, ok := pk.(*packet.ReInitPacketType); ok {
|
|
|
|
go m.reinit(reqId, reinitPk.ShellType)
|
2022-10-28 06:59:17 +02:00
|
|
|
return
|
|
|
|
}
|
2023-09-01 07:03:38 +02:00
|
|
|
if streamPk, ok := pk.(*packet.StreamFilePacketType); ok {
|
|
|
|
go m.streamFile(streamPk)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if writePk, ok := pk.(*packet.WriteFilePacketType); ok {
|
2024-03-21 07:38:05 +01:00
|
|
|
wfc := &WriteFileContext{
|
|
|
|
CVar: sync.NewCond(&sync.Mutex{}),
|
|
|
|
LastActive: time.Now(),
|
|
|
|
}
|
|
|
|
err := m.registerRpcHandler(writePk.ReqId, wfc)
|
|
|
|
if err != nil {
|
|
|
|
m.Sender.SendErrorResponse(reqId, fmt.Errorf("error registering write-file handler: %w", err))
|
|
|
|
return
|
|
|
|
}
|
2023-09-01 07:03:38 +02:00
|
|
|
go m.writeFile(writePk, wfc)
|
|
|
|
return
|
|
|
|
}
|
2022-08-09 23:23:59 +02:00
|
|
|
m.Sender.SendErrorResponse(reqId, fmt.Errorf("invalid rpc type '%s'", pk.GetType()))
|
|
|
|
}
|
|
|
|
|
2024-03-29 00:56:39 +01:00
|
|
|
func (m *MServer) clientPacketCallback(shellType string, pk packet.PacketType, runPk *packet.RunPacketType) {
|
2022-11-27 22:47:18 +01:00
|
|
|
if pk.GetType() != packet.CmdDonePacketStr {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
donePk := pk.(*packet.CmdDonePacketType)
|
|
|
|
if donePk.FinalState == nil {
|
|
|
|
return
|
|
|
|
}
|
2024-03-29 00:56:39 +01:00
|
|
|
initialState := runPk.State
|
|
|
|
if initialState == nil {
|
2022-11-27 22:47:18 +01:00
|
|
|
return
|
|
|
|
}
|
2024-03-29 00:56:39 +01:00
|
|
|
initialStateHash := initialState.GetHashVal(false)
|
|
|
|
sapi, err := shellapi.MakeShellApi(initialState.GetShellType())
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2024-03-29 00:56:39 +01:00
|
|
|
diff, err := sapi.MakeShellStateDiff(initialState, initialStateHash, donePk.FinalState)
|
2022-11-27 22:47:18 +01:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
donePk.FinalState = nil
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
donePk.FinalStateDiff = diff
|
2024-03-29 00:56:39 +01:00
|
|
|
donePk.FinalStateBasePtr = runPk.StatePtr
|
2022-11-27 22:47:18 +01:00
|
|
|
}
|
|
|
|
|
2022-06-29 02:20:01 +02:00
|
|
|
func (m *MServer) runCommand(runPacket *packet.RunPacketType) {
|
2022-06-29 04:01:33 +02:00
|
|
|
if err := runPacket.CK.Validate("packet"); err != nil {
|
2022-07-06 09:21:44 +02:00
|
|
|
m.Sender.SendErrorResponse(runPacket.ReqId, fmt.Errorf("server run packets require valid ck: %s", err))
|
2022-06-29 04:01:33 +02:00
|
|
|
return
|
|
|
|
}
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
if runPacket.ShellType == "" {
|
|
|
|
m.Sender.SendErrorResponse(runPacket.ReqId, fmt.Errorf("server run packets require shell type"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if runPacket.State == nil {
|
|
|
|
m.Sender.SendErrorResponse(runPacket.ReqId, fmt.Errorf("server run packets require state"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
_, _, err := packet.ParseShellStateVersion(runPacket.State.Version)
|
|
|
|
if err != nil {
|
|
|
|
m.Sender.SendErrorResponse(runPacket.ReqId, fmt.Errorf("invalid shellstate version: %w", err))
|
|
|
|
return
|
|
|
|
}
|
2024-04-05 00:08:45 +02:00
|
|
|
if runPacket.Command == "wave:testerror" {
|
|
|
|
m.Sender.SendErrorResponse(runPacket.ReqId, fmt.Errorf("test error"))
|
|
|
|
return
|
|
|
|
}
|
2024-05-02 23:16:00 +02:00
|
|
|
ecmd, err := shexec.MakeWaveshellSingleCmd()
|
2022-07-07 03:59:46 +02:00
|
|
|
if err != nil {
|
|
|
|
m.Sender.SendErrorResponse(runPacket.ReqId, fmt.Errorf("server run packets require valid ck: %s", err))
|
|
|
|
return
|
|
|
|
}
|
2024-02-29 20:37:03 +01:00
|
|
|
cproc, err := shexec.MakeClientProc(context.Background(), shexec.CmdWrap{Cmd: ecmd})
|
2022-07-07 02:16:45 +02:00
|
|
|
if err != nil {
|
2024-05-02 23:16:00 +02:00
|
|
|
m.Sender.SendErrorResponse(runPacket.ReqId, fmt.Errorf("starting waveshell client: %s", err))
|
2022-07-07 02:16:45 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
m.Lock.Lock()
|
|
|
|
m.ClientMap[runPacket.CK] = cproc
|
|
|
|
m.Lock.Unlock()
|
2022-06-29 02:20:01 +02:00
|
|
|
go func() {
|
2022-07-07 02:16:45 +02:00
|
|
|
defer func() {
|
2022-11-27 22:47:18 +01:00
|
|
|
r := recover()
|
|
|
|
finalPk := packet.MakeCmdFinalPacket(runPacket.CK)
|
|
|
|
finalPk.Ts = time.Now().UnixMilli()
|
|
|
|
if r != nil {
|
|
|
|
finalPk.Error = fmt.Sprintf("%s", r)
|
|
|
|
}
|
|
|
|
m.Sender.SendPacket(finalPk)
|
2022-07-07 02:16:45 +02:00
|
|
|
m.Lock.Lock()
|
|
|
|
delete(m.ClientMap, runPacket.CK)
|
|
|
|
m.Lock.Unlock()
|
|
|
|
cproc.Close()
|
|
|
|
}()
|
2022-07-07 03:59:46 +02:00
|
|
|
shexec.SendRunPacketAndRunData(context.Background(), cproc.Input, runPacket)
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
cproc.ProxySingleOutput(runPacket.CK, m.Sender, func(pk packet.PacketType) {
|
2024-03-29 00:56:39 +01:00
|
|
|
m.clientPacketCallback(runPacket.ShellType, pk, runPacket)
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
})
|
2022-06-29 02:20:01 +02:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2022-11-27 22:47:18 +01:00
|
|
|
func (m *MServer) packetSenderErrorHandler(sender *packet.PacketSender, pk packet.PacketType, err error) {
|
|
|
|
if serr, ok := err.(*packet.SendError); ok && serr.IsMarshalError {
|
|
|
|
msg := packet.MakeMessagePacket(err.Error())
|
|
|
|
if cpk, ok := pk.(packet.CommandPacketType); ok {
|
|
|
|
msg.CK = cpk.GetCK()
|
|
|
|
}
|
|
|
|
sender.SendPacket(msg)
|
2022-12-06 00:38:44 +01:00
|
|
|
return
|
|
|
|
} else {
|
|
|
|
// I/O error: close the WriteErrorCh to signal that we are dead (cannot continue if we can't write output)
|
|
|
|
m.WriteErrorChOnce.Do(func() {
|
|
|
|
close(m.WriteErrorCh)
|
|
|
|
})
|
2022-11-27 22:47:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-06 00:38:44 +01:00
|
|
|
func (server *MServer) runReadLoop() {
|
2022-06-29 07:05:47 +02:00
|
|
|
builder := packet.MakeRunPacketBuilder()
|
2022-06-29 00:04:08 +02:00
|
|
|
for pk := range server.MainInput.MainCh {
|
2022-06-29 04:01:33 +02:00
|
|
|
if server.Debug {
|
2024-03-21 07:38:05 +01:00
|
|
|
wlog.Logf("runReadLoop got packet %s\n", packet.AsString(pk))
|
2022-06-29 04:01:33 +02:00
|
|
|
}
|
2022-06-29 07:05:47 +02:00
|
|
|
ok, runPacket := builder.ProcessPacket(pk)
|
|
|
|
if ok {
|
|
|
|
if runPacket != nil {
|
|
|
|
server.runCommand(runPacket)
|
|
|
|
continue
|
|
|
|
}
|
2022-06-29 02:20:01 +02:00
|
|
|
continue
|
|
|
|
}
|
2024-03-21 07:38:05 +01:00
|
|
|
if cmdPk, ok := pk.(packet.CommandPacketType); ok && cmdPk.GetCK() != "" {
|
2022-06-29 04:01:33 +02:00
|
|
|
server.ProcessCommandPacket(cmdPk)
|
2022-06-29 00:04:08 +02:00
|
|
|
continue
|
|
|
|
}
|
2024-03-21 07:38:05 +01:00
|
|
|
if rpcPk, ok := pk.(packet.RpcPacketType); ok && rpcPk.GetReqId() != "" {
|
2022-08-09 23:23:59 +02:00
|
|
|
server.ProcessRpcPacket(rpcPk)
|
|
|
|
continue
|
|
|
|
}
|
2024-03-21 07:38:05 +01:00
|
|
|
if rpcFollowUp, ok := pk.(packet.RpcFollowUpPacketType); ok && rpcFollowUp.GetAssociatedReqId() != "" {
|
|
|
|
ok := server.dispatchRpcFollowUp(rpcFollowUp)
|
|
|
|
if !ok {
|
|
|
|
server.sendInboundRpcError(rpcFollowUp.GetAssociatedReqId(), fmt.Errorf("no handler for rpc follow-up packet"))
|
|
|
|
}
|
2023-09-01 07:03:38 +02:00
|
|
|
continue
|
|
|
|
}
|
2024-05-02 23:16:00 +02:00
|
|
|
server.Sender.SendMessageFmt("invalid packet '%s' sent to waveshell server", packet.AsString(pk))
|
2022-06-29 00:04:08 +02:00
|
|
|
continue
|
|
|
|
}
|
2022-12-06 00:38:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func RunServer() (int, error) {
|
|
|
|
debug := false
|
|
|
|
if len(os.Args) >= 3 && os.Args[2] == "--debug" {
|
|
|
|
debug = true
|
|
|
|
}
|
|
|
|
server := &MServer{
|
2023-09-01 07:03:38 +02:00
|
|
|
Lock: &sync.Mutex{},
|
|
|
|
ClientMap: make(map[base.CommandKey]*shexec.ClientProc),
|
|
|
|
Debug: debug,
|
|
|
|
WriteErrorCh: make(chan bool),
|
|
|
|
WriteErrorChOnce: &sync.Once{},
|
2024-03-21 07:38:05 +01:00
|
|
|
InboundRpcHandlers: make(map[string]RpcHandler),
|
|
|
|
InboundRpcErrorSent: make(map[string]time.Time),
|
2022-12-06 00:38:44 +01:00
|
|
|
}
|
2024-02-16 02:42:43 +01:00
|
|
|
if debug {
|
|
|
|
packet.GlobalDebug = true
|
|
|
|
}
|
|
|
|
server.MainInput = packet.MakePacketParser(os.Stdin, nil)
|
|
|
|
server.Sender = packet.MakePacketSender(os.Stdout, server.packetSenderErrorHandler)
|
|
|
|
defer server.Close()
|
|
|
|
wlog.LogConsumer = server.Sender.SendLogPacket
|
2023-09-01 07:03:38 +02:00
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
if server.checkDone() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
time.Sleep(cleanLoopTime)
|
2024-03-21 07:38:05 +01:00
|
|
|
server.cleanRpcHandlers()
|
2023-09-01 07:03:38 +02:00
|
|
|
}
|
|
|
|
}()
|
2022-12-06 00:38:44 +01:00
|
|
|
var err error
|
|
|
|
initPacket, err := shexec.MakeServerInitPacket()
|
|
|
|
if err != nil {
|
|
|
|
return 1, err
|
|
|
|
}
|
|
|
|
server.Sender.SendPacket(initPacket)
|
|
|
|
ticker := time.NewTicker(1 * time.Minute)
|
|
|
|
go func() {
|
|
|
|
for range ticker.C {
|
|
|
|
server.Sender.SendPacket(packet.MakePingPacket())
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
defer ticker.Stop()
|
|
|
|
readLoopDoneCh := make(chan bool)
|
|
|
|
go func() {
|
|
|
|
defer close(readLoopDoneCh)
|
|
|
|
server.runReadLoop()
|
|
|
|
}()
|
|
|
|
select {
|
|
|
|
case <-readLoopDoneCh:
|
|
|
|
break
|
|
|
|
|
|
|
|
case <-server.WriteErrorCh:
|
|
|
|
break
|
|
|
|
}
|
2022-06-29 00:04:08 +02:00
|
|
|
return 0, nil
|
|
|
|
}
|
zsh support (#227)
adds zsh support to waveterm. big change, lots going on here. lots of other improvements and bug fixes added while debugging and building out the feature.
Commits:
* refactor shexec parser.go into new package shellenv. separate out bash specific parsing from generic functions
* checkpoint
* work on refactoring shexec. created two new packages shellapi (for bash/zsh specific stuff), and shellutil (shared between shellapi and shexec)
* more refactoring
* create shellapi interface to abstract bash specific functionality
* more refactoring, move bash shell state parsing to shellapi
* move makeRcFile to shellapi. remove all of the 'client' options CLI options from waveshell
* get shellType passed through to server/single paths for waveshell
* add a local shelltype detector
* mock out a zshapi
* move shelltype through more of the code
* get a command to run via zsh
* zsh can now switch directories. poc, needs cleanup
* working on ShellState encoding differences between zsh/bash. Working on parsing zsh decls. move utilfn package into waveshell (shouldn't have been in wavesrv)
* switch to use []byte for vardecl serialization + diffs
* progress on zsh environment. still have issues reconciling init environment with trap environment
* fix typeset argument parsing
* parse promptvars, more zsh specific ignores
* fix bug with promptvar not getting set (wrong check in FeState func)
* add sdk (issue #188) to list of rtnstate commands
* more zsh compatibility -- working with a larger ohmyzsh environment. ignore more variables, handle exit trap better. unique path/fpath. add a processtype variable to base.
* must return a value
* zsh alias parsing/restoring. diff changes (and rtnstate changes). introduces linediff v1.
* force zmodload of zsh/parameter
* starting work on zsh functions
* need a v1 of mapdiff as well (to handle null chars)
* pack/unpack of ints was wrong (one used int and one use uint). turned out we only ever encoded '0' so it worked. that also means it is safe to change unpack to unpackUInt
* reworking for binary encoding of aliases and functions (because of zsh allows any character, including nulls, in names and values)
* fixes, working on functions, issue with line endings
* zsh functions. lots of ugliness here around dealing with line dicipline and cooked stty. new runcommand function to grab output from a non-tty fd. note that we still to run the actual command in a stty to get the proper output.
* write uuid tempdir, cleanup with tmprcfilename code
* hack in some simple zsh function declaration finding code for rtnstate. create function diff for rtnstate that supports zsh
* make sure key order is constant so shell hashes are consistent
* fix problems with state diffs to support new zsh formats. add diff/apply code to shellapi (moved from shellenv), that is now specific to zsh or bash
* add log packet and new shellstate packets
* switch to shellstate map that's also keyed by shelltype
* add shelltype to remoteinstance
* remove shell argument from waveshell
* added new shelltype statemap to remote.go (msh), deal with fallout
* move shellstate out of init packet, and move to an explicit reinit call. try to initialize all of the active shell states
* change dont always store init state (only store on demand). initialize shell states on demand (if not already initialized). allow reset to change shells
* add shellpref field to remote table. use to drive the default shell choice for new tabs
* show shelltag on cmdinput, pass through ri and remote (defaultshellstate)
* bump mshell version to v0.4
* better version validation for shellstate. also relax compatibility requirements for diffing states (shelltype + major version need to match)
* better error handling, check shellstate compatibility during run (on waveshell server)
* add extra separator for bash shellstate processing to deal with spurious output from rc files
* special migration for v30 -- flag invalid bash shell states and show special button in UI to fix
* format
* remove zsh-decls (unused)
* remove test code
* remove debug print
* fix typo
2024-01-17 01:11:04 +01:00
|
|
|
|
|
|
|
func MakeShellStateMap() *ShellStateMap {
|
|
|
|
return &ShellStateMap{
|
|
|
|
Lock: &sync.Mutex{},
|
|
|
|
StateMap: make(map[shellStateMapKey]*packet.ShellState),
|
|
|
|
CurrentStateMap: make(map[string]string),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sm *ShellStateMap) GetCurrentState(shellType string) (string, *packet.ShellState) {
|
|
|
|
sm.Lock.Lock()
|
|
|
|
defer sm.Lock.Unlock()
|
|
|
|
hval := sm.CurrentStateMap[shellType]
|
|
|
|
return hval, sm.StateMap[shellStateMapKey{ShellType: shellType, Hash: hval}]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sm *ShellStateMap) SetCurrentState(shellType string, state *packet.ShellState) error {
|
|
|
|
if state == nil {
|
|
|
|
return fmt.Errorf("cannot set nil state")
|
|
|
|
}
|
|
|
|
if shellType != state.GetShellType() {
|
|
|
|
return fmt.Errorf("shell type mismatch: %s != %s", shellType, state.GetShellType())
|
|
|
|
}
|
|
|
|
sm.Lock.Lock()
|
|
|
|
defer sm.Lock.Unlock()
|
|
|
|
hval, _ := state.EncodeAndHash()
|
|
|
|
key := shellStateMapKey{ShellType: shellType, Hash: hval}
|
|
|
|
sm.StateMap[key] = state
|
|
|
|
sm.CurrentStateMap[shellType] = hval
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sm *ShellStateMap) GetStateByHash(shellType string, hash string) *packet.ShellState {
|
|
|
|
sm.Lock.Lock()
|
|
|
|
defer sm.Lock.Unlock()
|
|
|
|
return sm.StateMap[shellStateMapKey{ShellType: shellType, Hash: hash}]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sm *ShellStateMap) Clear() {
|
|
|
|
sm.Lock.Lock()
|
|
|
|
defer sm.Lock.Unlock()
|
|
|
|
sm.StateMap = make(map[shellStateMapKey]*packet.ShellState)
|
|
|
|
sm.CurrentStateMap = make(map[string]string)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sm *ShellStateMap) GetShells() []string {
|
|
|
|
sm.Lock.Lock()
|
|
|
|
defer sm.Lock.Unlock()
|
|
|
|
return utilfn.GetMapKeys(sm.CurrentStateMap)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sm *ShellStateMap) HasShell(shellType string) bool {
|
|
|
|
sm.Lock.Lock()
|
|
|
|
defer sm.Lock.Unlock()
|
|
|
|
_, found := sm.CurrentStateMap[shellType]
|
|
|
|
return found
|
|
|
|
}
|