waveterm/wavesrv/pkg/cmdrunner/resolver.go

529 lines
14 KiB
Go
Raw Normal View History

2023-10-17 06:31:13 +02:00
// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
2022-08-27 01:24:07 +02:00
package cmdrunner
import (
"context"
"fmt"
"log"
2022-08-27 01:24:07 +02:00
"regexp"
"strconv"
"strings"
"github.com/google/uuid"
2023-10-16 22:30:10 +02:00
"github.com/wavetermdev/waveterm/wavesrv/pkg/remote"
"github.com/wavetermdev/waveterm/wavesrv/pkg/scpacket"
"github.com/wavetermdev/waveterm/wavesrv/pkg/sstore"
2022-08-27 01:24:07 +02:00
)
const (
R_Session = 1
R_Screen = 2
R_Remote = 8
R_RemoteConnected = 16
2022-08-27 01:24:07 +02:00
)
const (
ConnectedRemote = "connected"
LocalRemote = "local"
)
2022-08-27 01:24:07 +02:00
type resolvedIds struct {
SessionId string
ScreenId string
Remote *ResolvedRemote
}
type ResolvedRemote struct {
DisplayName string
RemotePtr sstore.RemotePtrType
MShell *remote.MShellProc
RState remote.RemoteRuntimeState
2022-10-04 04:04:48 +02:00
RemoteCopy *sstore.RemoteType
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
ShellType string
StatePtr *sstore.ShellStatePtr
FeState map[string]string
2022-08-27 01:24:07 +02:00
}
type ResolveItem = sstore.ResolveItem
func itemNames(items []ResolveItem) []string {
if len(items) == 0 {
return nil
}
rtn := make([]string, len(items))
for idx, item := range items {
rtn[idx] = item.Name
}
return rtn
}
func sessionsToResolveItems(sessions []*sstore.SessionType) []ResolveItem {
if len(sessions) == 0 {
return nil
}
rtn := make([]ResolveItem, len(sessions))
for idx, session := range sessions {
2022-12-25 22:03:11 +01:00
rtn[idx] = ResolveItem{Name: session.Name, Id: session.SessionId, Hidden: session.Archived}
}
return rtn
}
2022-08-27 02:29:32 +02:00
func screensToResolveItems(screens []*sstore.ScreenType) []ResolveItem {
if len(screens) == 0 {
return nil
}
rtn := make([]ResolveItem, len(screens))
for idx, screen := range screens {
2022-12-25 22:03:11 +01:00
rtn[idx] = ResolveItem{Name: screen.Name, Id: screen.ScreenId, Hidden: screen.Archived}
2022-08-27 02:29:32 +02:00
}
return rtn
}
// 1-indexed
func boundInt(ival int, maxVal int, wrap bool) int {
if maxVal == 0 {
return 0
}
if ival < 1 {
if wrap {
return maxVal
} else {
return 1
}
2022-08-27 01:24:07 +02:00
}
if ival > maxVal {
if wrap {
return 1
} else {
return maxVal
}
}
return ival
}
type posArgType struct {
Pos int
IsWrap bool
IsRelative bool
StartAnchor bool
EndAnchor bool
}
func parsePosArg(posStr string) *posArgType {
2022-08-27 01:24:07 +02:00
if !positionRe.MatchString(posStr) {
return nil
2022-08-27 01:24:07 +02:00
}
if posStr == "+" {
return &posArgType{Pos: 1, IsWrap: true, IsRelative: true}
} else if posStr == "-" {
return &posArgType{Pos: -1, IsWrap: true, IsRelative: true}
} else if posStr == "S" {
return &posArgType{Pos: 0, IsRelative: true, StartAnchor: true}
} else if posStr == "E" {
return &posArgType{Pos: 0, IsRelative: true, EndAnchor: true}
}
if strings.HasPrefix(posStr, "S+") {
pos, _ := strconv.Atoi(posStr[2:])
return &posArgType{Pos: pos, IsRelative: true, StartAnchor: true}
}
if strings.HasPrefix(posStr, "E-") {
pos, _ := strconv.Atoi(posStr[1:])
return &posArgType{Pos: pos, IsRelative: true, EndAnchor: true}
}
if strings.HasPrefix(posStr, "+") || strings.HasPrefix(posStr, "-") {
pos, _ := strconv.Atoi(posStr)
return &posArgType{Pos: pos, IsRelative: true}
}
pos, _ := strconv.Atoi(posStr)
return &posArgType{Pos: pos}
}
2022-12-24 00:56:29 +01:00
func resolveByPosition(isNumeric bool, allItems []ResolveItem, curId string, posStr string) *ResolveItem {
items := make([]ResolveItem, 0, len(allItems))
for _, item := range allItems {
if !item.Hidden {
items = append(items, item)
}
}
if len(items) == 0 {
return nil
2022-08-27 01:24:07 +02:00
}
posArg := parsePosArg(posStr)
if posArg == nil {
return nil
2022-08-27 01:24:07 +02:00
}
var finalPos int
if posArg.IsRelative {
var curIdx int
if posArg.StartAnchor {
curIdx = 1
} else if posArg.EndAnchor {
curIdx = len(items)
} else {
curIdx = 1 // if no match, curIdx will be first item
for idx, item := range items {
if item.Id == curId {
curIdx = idx + 1
break
}
}
2022-08-27 01:24:07 +02:00
}
finalPos = curIdx + posArg.Pos
finalPos = boundInt(finalPos, len(items), posArg.IsWrap)
return &items[finalPos-1]
} else if isNumeric {
// these resolve items have a "Num" set that should be used to look up non-relative positions
2022-12-24 00:56:29 +01:00
// use allItems for numeric resolve
for _, item := range allItems {
if item.Num == posArg.Pos {
return &item
}
}
return nil
} else {
// non-numeric means position is just the index
finalPos = posArg.Pos
if finalPos <= 0 || finalPos > len(items) {
return nil
}
return &items[finalPos-1]
2022-08-27 01:24:07 +02:00
}
}
func resolveRemoteArg(remoteArg string) (*sstore.RemotePtrType, error) {
rrUser, rrRemote, rrName, err := parseFullRemoteRef(remoteArg)
if err != nil {
return nil, err
}
if rrUser != "" {
return nil, fmt.Errorf("remoteusers not supported")
}
2022-09-14 22:01:52 +02:00
msh := remote.GetRemoteByArg(rrRemote)
if msh == nil {
return nil, nil
}
rcopy := msh.GetRemoteCopy()
return &sstore.RemotePtrType{RemoteId: rcopy.RemoteId, Name: rrName}, nil
}
func resolveUiIds(ctx context.Context, pk *scpacket.FeCommandPacketType, rtype int) (resolvedIds, error) {
2022-08-27 01:24:07 +02:00
rtn := resolvedIds{}
uictx := pk.UIContext
if uictx != nil {
rtn.SessionId = uictx.SessionId
rtn.ScreenId = uictx.ScreenId
2022-08-27 01:24:07 +02:00
}
if pk.Kwargs["session"] != "" {
sessionId, err := resolveSessionArg(pk.Kwargs["session"])
if err != nil {
return rtn, err
}
if sessionId != "" {
rtn.SessionId = sessionId
}
}
if pk.Kwargs["screen"] != "" {
screenId, err := resolveScreenArg(rtn.SessionId, pk.Kwargs["screen"])
if err != nil {
return rtn, err
}
if screenId != "" {
rtn.ScreenId = screenId
}
}
var rptr *sstore.RemotePtrType
var err error
if pk.Kwargs["remote"] != "" {
rptr, err = resolveRemoteArg(pk.Kwargs["remote"])
if err != nil {
return rtn, err
}
if rptr == nil {
return rtn, fmt.Errorf("invalid remote argument %q passed, remote not found", pk.Kwargs["remote"])
}
} else if uictx.Remote != nil {
rptr = uictx.Remote
}
if rptr != nil {
err = rptr.Validate()
if err != nil {
return rtn, fmt.Errorf("invalid resolved remote: %v", err)
}
rr, err := ResolveRemoteFromPtr(ctx, rptr, rtn.SessionId, rtn.ScreenId)
if err != nil {
return rtn, err
}
rtn.Remote = rr
}
if rtype&R_Session > 0 && rtn.SessionId == "" {
return rtn, fmt.Errorf("no session")
2022-08-27 01:24:07 +02:00
}
if rtype&R_Screen > 0 && rtn.ScreenId == "" {
return rtn, fmt.Errorf("no screen")
2022-08-27 01:24:07 +02:00
}
if (rtype&R_Remote > 0 || rtype&R_RemoteConnected > 0) && rtn.Remote == nil {
return rtn, fmt.Errorf("no remote")
}
if rtype&R_RemoteConnected > 0 {
if !rtn.Remote.RState.IsConnected() {
2022-12-28 22:56:19 +01:00
err = rtn.Remote.MShell.TryAutoConnect()
if err != nil {
return rtn, fmt.Errorf("error trying to auto-connect remote [%s]: %w", rtn.Remote.DisplayName, err)
2022-12-28 22:56:19 +01:00
}
rrNew, err := ResolveRemoteFromPtr(ctx, rptr, rtn.SessionId, rtn.ScreenId)
2022-12-29 01:59:54 +01:00
if err != nil {
return rtn, err
}
rtn.Remote = rrNew
2022-12-28 22:56:19 +01:00
}
if !rtn.Remote.RState.IsConnected() {
return rtn, fmt.Errorf("remote [%s] is not connected", rtn.Remote.DisplayName)
2022-08-27 01:24:07 +02:00
}
if rtn.Remote.StatePtr == nil || rtn.Remote.FeState == nil {
return rtn, fmt.Errorf("remote [%s] state is not available", rtn.Remote.DisplayName)
2022-08-27 01:24:07 +02:00
}
}
return rtn, nil
}
2022-08-27 02:29:32 +02:00
func resolveSessionScreen(ctx context.Context, sessionId string, screenArg string, curScreenArg string) (*ResolveItem, error) {
2023-03-13 20:10:23 +01:00
screens, err := sstore.GetSessionScreens(ctx, sessionId)
2022-08-27 01:24:07 +02:00
if err != nil {
return nil, fmt.Errorf("could not retreive screens for session=%s: %v", sessionId, err)
2022-08-27 01:24:07 +02:00
}
2022-08-27 02:29:32 +02:00
ritems := screensToResolveItems(screens)
return genericResolve(screenArg, curScreenArg, ritems, false, "screen")
2022-08-27 01:24:07 +02:00
}
2022-12-27 01:09:21 +01:00
func resolveSession(ctx context.Context, sessionArg string, curSessionArg string) (*ResolveItem, error) {
bareSessions, err := sstore.GetBareSessions(ctx)
if err != nil {
return nil, err
}
ritems := sessionsToResolveItems(bareSessions)
ritem, err := genericResolve(sessionArg, curSessionArg, ritems, false, "session")
if err != nil {
return nil, err
}
return ritem, nil
}
2023-03-15 00:37:22 +01:00
func resolveLine(ctx context.Context, sessionId string, screenId string, lineArg string, curLineArg string) (*ResolveItem, error) {
2023-03-21 03:20:57 +01:00
lines, err := sstore.GetLineResolveItems(ctx, screenId)
if err != nil {
return nil, fmt.Errorf("could not get lines: %v", err)
}
return genericResolve(lineArg, curLineArg, lines, true, "line")
}
2022-08-27 01:24:07 +02:00
func getSessionIds(sarr []*sstore.SessionType) []string {
rtn := make([]string, len(sarr))
for idx, s := range sarr {
rtn[idx] = s.SessionId
}
return rtn
}
var partialUUIDRe = regexp.MustCompile("^[0-9a-f]{8}$")
func isPartialUUID(s string) bool {
return partialUUIDRe.MatchString(s)
}
func isUUID(s string) bool {
_, err := uuid.Parse(s)
return err == nil
}
func getResolveItemById(id string, items []ResolveItem) *ResolveItem {
if id == "" {
return nil
}
for _, item := range items {
if item.Id == id {
return &item
}
}
return nil
}
func genericResolve(arg string, curArg string, items []ResolveItem, isNumeric bool, typeStr string) (*ResolveItem, error) {
if len(items) == 0 || arg == "" {
return nil, nil
}
var curId string
if curArg != "" {
curItem, _ := genericResolve(curArg, "", items, isNumeric, typeStr)
if curItem != nil {
curId = curItem.Id
2022-08-27 01:24:07 +02:00
}
}
rtnItem := resolveByPosition(isNumeric, items, curId, arg)
if rtnItem != nil {
return rtnItem, nil
2022-08-27 01:24:07 +02:00
}
isUuid := isUUID(arg)
tryPuid := isPartialUUID(arg)
var prefixMatches []ResolveItem
for _, item := range items {
if (isUuid && item.Id == arg) || (tryPuid && strings.HasPrefix(item.Id, arg)) {
return &item, nil
2022-08-27 01:24:07 +02:00
}
2022-12-26 21:38:47 +01:00
if item.Name != "" {
if item.Name == arg {
return &item, nil
}
2022-12-26 21:38:47 +01:00
if !item.Hidden && strings.HasPrefix(item.Name, arg) {
prefixMatches = append(prefixMatches, item)
}
2022-08-27 01:24:07 +02:00
}
}
if len(prefixMatches) == 1 {
return &prefixMatches[0], nil
2022-08-27 01:24:07 +02:00
}
if len(prefixMatches) > 1 {
return nil, fmt.Errorf("could not resolve %s '%s', ambiguious prefix matched multiple %ss: %s", typeStr, arg, typeStr, formatStrs(itemNames(prefixMatches), "and", true))
2022-08-27 01:24:07 +02:00
}
return nil, fmt.Errorf("could not resolve %s '%s' (name/id/pos not found)", typeStr, arg)
2022-08-27 01:24:07 +02:00
}
func resolveSessionId(pk *scpacket.FeCommandPacketType) (string, error) {
sessionId := pk.Kwargs["session"]
if sessionId == "" {
return "", nil
}
if _, err := uuid.Parse(sessionId); err != nil {
return "", fmt.Errorf("invalid sessionid '%s'", sessionId)
}
return sessionId, nil
}
func resolveSessionArg(sessionArg string) (string, error) {
if sessionArg == "" {
return "", nil
}
if _, err := uuid.Parse(sessionArg); err != nil {
return "", fmt.Errorf("invalid session arg specified (must be sessionid) '%s'", sessionArg)
}
return sessionArg, nil
}
func resolveScreenArg(sessionId string, screenArg string) (string, error) {
if screenArg == "" {
return "", nil
}
if _, err := uuid.Parse(screenArg); err != nil {
2022-12-24 00:56:29 +01:00
return "", fmt.Errorf("invalid screen arg specified (must be screenid) '%s'", screenArg)
}
return screenArg, nil
}
2022-08-27 01:24:07 +02:00
func resolveScreenId(ctx context.Context, pk *scpacket.FeCommandPacketType, sessionId string) (string, error) {
screenArg := pk.Kwargs["screen"]
if screenArg == "" {
return "", nil
}
if _, err := uuid.Parse(screenArg); err == nil {
return screenArg, nil
}
if sessionId == "" {
return "", fmt.Errorf("cannot resolve screen without session")
}
2022-08-27 02:29:32 +02:00
ritem, err := resolveSessionScreen(ctx, sessionId, screenArg, "")
if err != nil {
return "", err
}
return ritem.Id, nil
2022-08-27 01:24:07 +02:00
}
// returns (remoteuserref, remoteref, name, error)
func parseFullRemoteRef(fullRemoteRef string) (string, string, string, error) {
if strings.HasPrefix(fullRemoteRef, "[") && strings.HasSuffix(fullRemoteRef, "]") {
fullRemoteRef = fullRemoteRef[1 : len(fullRemoteRef)-1]
}
Integrate SSH Library with Waveshell Installation/Auto-update (#322) * refactor launch code to integrate install easier The previous set up of launch was difficult to navigate. This makes it much clearer which will make the auto install flow easier to manage. * feat: integrate auto install into new ssh setup This change makes it possible to auto install using the ssh library instead of making a call to the ssh cli command. This will auto install if the installed waveshell version is incorrect or cannot be found. * chore: clean up some lints for sshclient There was a context that didn't have it's cancel function deferred and an error that wasn't being handle. They're fixed now. * fix: disconnect client if requested or launch fail A recent commit made it so a client remained part of the MShellProc after being disconnected. This is undesireable since a manual disconnection indicates that the user will need to enter their credentials again if required. Similarly, if the launch fails with an error, the expectation is that credentials will have to be entered again. * fix: use legacy timer for the time being The legacy timer frustrates me because it adds a lot of state to the MShellProc struct that is complicated to manage. But it currently works, so I will be keeping it for the time being. * fix: change separator between remoteref and name With the inclusion of the port number in the canonical id, the : separator between the remoteref and remote name causes problems if the port is parsed instead. This changes it to a # in order to avoid this conflict. * fix: check for null when closing extra files It is possible for the list of extra files to contain null files. This change ensures the null files will not be erroneously closed. * fix: change connecting method to show port once With port added to the canonicalname, it no longer makes sense to append the port afterward. * feat: use user input modal for sudo connection The sudo connection used to have a unique way of entering a password. This change provides an alternative method using the user input modal that the other connection methods use. It does not work perfectly with this revision, but the basic building blocks are in place. It needs a few timer updates to be complete. * fix: remove old timer to prevent conflicts with it With this change the old timer is no longer needed. It is not fully removed yet, but it is disabled so as to not get in the way. Additionally, error handling has been slightly improved. There is still a bug where an incorrect password prints a new password prompt after the error message. That needs to be fixed in the future.
2024-02-29 20:37:03 +01:00
fields := strings.Split(fullRemoteRef, "#")
2022-08-27 01:24:07 +02:00
if len(fields) > 3 {
return "", "", "", fmt.Errorf("invalid remote format '%s'", fullRemoteRef)
}
if len(fields) == 1 {
return "", fields[0], "", nil
}
if len(fields) == 2 {
if strings.HasPrefix(fields[0], "@") {
return fields[0], fields[1], "", nil
}
return "", fields[0], fields[1], nil
}
return fields[0], fields[1], fields[2], nil
}
func ResolveRemoteFromPtr(ctx context.Context, rptr *sstore.RemotePtrType, sessionId string, screenId string) (*ResolvedRemote, error) {
if rptr == nil || rptr.RemoteId == "" {
return nil, nil
}
msh := remote.GetRemoteById(rptr.RemoteId)
if msh == nil {
return nil, fmt.Errorf("invalid remote '%s', not found", rptr.RemoteId)
}
rstate := msh.GetRemoteRuntimeState()
2022-10-04 04:04:48 +02:00
rcopy := msh.GetRemoteCopy()
displayName := rstate.GetDisplayName(rptr)
rtn := &ResolvedRemote{
DisplayName: displayName,
RemotePtr: *rptr,
RState: rstate,
MShell: msh,
2022-10-04 04:04:48 +02:00
RemoteCopy: &rcopy,
StatePtr: nil,
FeState: 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
ShellType: "",
}
2023-03-15 00:37:22 +01:00
if sessionId != "" && screenId != "" {
ri, err := sstore.GetRemoteInstance(ctx, sessionId, screenId, *rptr)
if err != nil {
log.Printf("ERROR resolving remote state '%s': %v\n", displayName, err)
// continue with state set to nil
} else {
if ri == 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
rtn.ShellType = msh.GetShellPref()
rtn.StatePtr = msh.GetDefaultStatePtr(rtn.ShellType)
rtn.FeState = msh.GetDefaultFeState(rtn.ShellType)
} else {
rtn.StatePtr = &sstore.ShellStatePtr{BaseHash: ri.StateBaseHash, DiffHashArr: ri.StateDiffHashArr}
rtn.FeState = ri.FeState
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
rtn.ShellType = ri.ShellType
}
}
}
return rtn, nil
}
2022-08-27 01:24:07 +02:00
// returns (remoteDisplayName, remoteptr, state, rstate, err)
2023-03-15 00:37:22 +01:00
func resolveRemote(ctx context.Context, fullRemoteRef string, sessionId string, screenId string) (string, *sstore.RemotePtrType, *remote.RemoteRuntimeState, error) {
2022-08-27 01:24:07 +02:00
if fullRemoteRef == "" {
2022-11-28 09:13:00 +01:00
return "", nil, nil, nil
2022-08-27 01:24:07 +02:00
}
userRef, remoteRef, remoteName, err := parseFullRemoteRef(fullRemoteRef)
if err != nil {
2022-11-28 09:13:00 +01:00
return "", nil, nil, err
2022-08-27 01:24:07 +02:00
}
if userRef != "" {
2022-11-28 09:13:00 +01:00
return "", nil, nil, fmt.Errorf("invalid remote '%s', cannot resolve remote userid '%s'", fullRemoteRef, userRef)
2022-08-27 01:24:07 +02:00
}
rstate := remote.ResolveRemoteRef(remoteRef)
if rstate == nil {
2022-11-28 09:13:00 +01:00
return "", nil, nil, fmt.Errorf("cannot resolve remote '%s': not found", fullRemoteRef)
2022-08-27 01:24:07 +02:00
}
rptr := sstore.RemotePtrType{RemoteId: rstate.RemoteId, Name: remoteName}
rname := rstate.RemoteCanonicalName
if rstate.RemoteAlias != "" {
rname = rstate.RemoteAlias
}
if rptr.Name != "" {
rname = fmt.Sprintf("%s:%s", rname, rptr.Name)
}
2022-11-28 09:13:00 +01:00
return rname, &rptr, rstate, nil
2022-08-27 01:24:07 +02:00
}