mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
big update to handle cmd returnstate (still need to process new state with donepacket)
This commit is contained in:
@@ -182,6 +182,36 @@ func HandleGetWindow(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HandleRtnState(w http.ResponseWriter, r *http.Request) {
|
||||||
|
qvals := r.URL.Query()
|
||||||
|
sessionId := qvals.Get("sessionid")
|
||||||
|
cmdId := qvals.Get("cmdid")
|
||||||
|
if sessionId == "" || cmdId == "" {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
w.Write([]byte(fmt.Sprintf("must specify sessionid and cmdid")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := uuid.Parse(sessionId); err != nil {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
w.Write([]byte(fmt.Sprintf("invalid sessionid: %v", err)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := uuid.Parse(cmdId); err != nil {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
w.Write([]byte(fmt.Sprintf("invalid cmdid: %v", err)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := cmdrunner.GetRtnStateDiff(r.Context(), sessionId, cmdId)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
w.Write([]byte(fmt.Sprintf("cannot get rtnstate diff: %v", err)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func HandleRemotePty(w http.ResponseWriter, r *http.Request) {
|
func HandleRemotePty(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
|
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
|
||||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
@@ -393,6 +423,7 @@ func main() {
|
|||||||
gr := mux.NewRouter()
|
gr := mux.NewRouter()
|
||||||
gr.HandleFunc("/api/ptyout", HandleGetPtyOut)
|
gr.HandleFunc("/api/ptyout", HandleGetPtyOut)
|
||||||
gr.HandleFunc("/api/remote-pty", HandleRemotePty)
|
gr.HandleFunc("/api/remote-pty", HandleRemotePty)
|
||||||
|
gr.HandleFunc("/api/rtnstate", HandleRtnState)
|
||||||
gr.HandleFunc("/api/get-window", HandleGetWindow)
|
gr.HandleFunc("/api/get-window", HandleGetWindow)
|
||||||
gr.HandleFunc("/api/run-command", HandleRunCommand).Methods("GET", "POST", "OPTIONS")
|
gr.HandleFunc("/api/run-command", HandleRunCommand).Methods("GET", "POST", "OPTIONS")
|
||||||
gr.HandleFunc("/api/get-client-data", HandleGetClientData)
|
gr.HandleFunc("/api/get-client-data", HandleGetClientData)
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ CREATE TABLE line (
|
|||||||
text text NOT NULL,
|
text text NOT NULL,
|
||||||
cmdid varchar(36) NOT NULL,
|
cmdid varchar(36) NOT NULL,
|
||||||
ephemeral boolean NOT NULL,
|
ephemeral boolean NOT NULL,
|
||||||
|
contentheight int NOT NULL,
|
||||||
PRIMARY KEY (sessionid, windowid, lineid)
|
PRIMARY KEY (sessionid, windowid, lineid)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ CREATE TABLE cmd (
|
|||||||
startpk json NOT NULL,
|
startpk json NOT NULL,
|
||||||
donepk json NOT NULL,
|
donepk json NOT NULL,
|
||||||
runout json NOT NULL,
|
runout json NOT NULL,
|
||||||
usedrows int NOT NULL,
|
rtnstate bool NOT NULL,
|
||||||
PRIMARY KEY (sessionid, cmdid)
|
PRIMARY KEY (sessionid, cmdid)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
+33
-133
@@ -39,7 +39,7 @@ var ColorNames = []string{"black", "red", "green", "yellow", "blue", "magenta",
|
|||||||
var RemoteColorNames = []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"}
|
var RemoteColorNames = []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"}
|
||||||
var RemoteSetArgs = []string{"alias", "connectmode", "key", "password", "autoinstall", "color"}
|
var RemoteSetArgs = []string{"alias", "connectmode", "key", "password", "autoinstall", "color"}
|
||||||
|
|
||||||
var WindowCmds = []string{"run", "comment", "cd", "cr", "setenv", "unset", "clear", "sw", "alias", "unalias", "function", "source", "reset"}
|
var WindowCmds = []string{"run", "comment", "cd", "cr", "clear", "sw", "alias", "unalias", "function", "reset"}
|
||||||
var NoHistCmds = []string{"compgen", "line", "history"}
|
var NoHistCmds = []string{"compgen", "line", "history"}
|
||||||
var GlobalCmds = []string{"session", "screen", "remote"}
|
var GlobalCmds = []string{"session", "screen", "remote"}
|
||||||
|
|
||||||
@@ -72,11 +72,9 @@ func init() {
|
|||||||
registerCmdFn("run", RunCommand)
|
registerCmdFn("run", RunCommand)
|
||||||
registerCmdFn("eval", EvalCommand)
|
registerCmdFn("eval", EvalCommand)
|
||||||
registerCmdFn("comment", CommentCommand)
|
registerCmdFn("comment", CommentCommand)
|
||||||
registerCmdFn("cd", CdCommand)
|
// registerCmdFn("cd", CdCommand)
|
||||||
registerCmdFn("cr", CrCommand)
|
registerCmdFn("cr", CrCommand)
|
||||||
registerCmdFn("compgen", CompGenCommand)
|
registerCmdFn("compgen", CompGenCommand)
|
||||||
registerCmdFn("setenv", SetEnvCommand)
|
|
||||||
registerCmdFn("unset", UnSetCommand)
|
|
||||||
registerCmdFn("clear", ClearCommand)
|
registerCmdFn("clear", ClearCommand)
|
||||||
registerCmdFn("reset", ResetCommand)
|
registerCmdFn("reset", ResetCommand)
|
||||||
|
|
||||||
@@ -114,8 +112,6 @@ func init() {
|
|||||||
registerCmdFn("line:show", LineShowCommand)
|
registerCmdFn("line:show", LineShowCommand)
|
||||||
|
|
||||||
registerCmdFn("history", HistoryCommand)
|
registerCmdFn("history", HistoryCommand)
|
||||||
|
|
||||||
registerCmdFn("source", SourceCommand)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getValidCommands() []string {
|
func getValidCommands() []string {
|
||||||
@@ -239,40 +235,6 @@ func getUITermOpts(uiContext *scpacket.UIContextType) *packet.TermOpts {
|
|||||||
return termOpts
|
return termOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
func SourceCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
|
||||||
ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_RemoteConnected)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("/source error: %w", err)
|
|
||||||
}
|
|
||||||
if len(pk.Args) != 1 {
|
|
||||||
return nil, fmt.Errorf("/source takes one argument (the file to source)")
|
|
||||||
}
|
|
||||||
cmdId := scbase.GenSCUUID()
|
|
||||||
runPacket := packet.MakeRunPacket()
|
|
||||||
runPacket.ReqId = uuid.New().String()
|
|
||||||
runPacket.CK = base.MakeCommandKey(ids.SessionId, cmdId)
|
|
||||||
runPacket.State = ids.Remote.RemoteState
|
|
||||||
runPacket.StateComplete = true
|
|
||||||
runPacket.UsePty = true
|
|
||||||
runPacket.TermOpts = getUITermOpts(pk.UIContext)
|
|
||||||
runPacket.Command = strings.TrimSpace(fmt.Sprintf("source %s", shellescape.Quote(pk.Args[0])))
|
|
||||||
runPacket.ReturnState = true
|
|
||||||
cmd, callback, err := remote.RunCommand(ctx, cmdId, ids.Remote.RemotePtr, ids.Remote.RemoteState, runPacket)
|
|
||||||
if callback != nil {
|
|
||||||
defer callback()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
update, err := addLineForCmd(ctx, "/source", true, ids, cmd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
update.Interactive = pk.Interactive
|
|
||||||
sstore.MainBus.SendUpdate(ids.SessionId, update)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
||||||
ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_RemoteConnected)
|
ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_RemoteConnected)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -280,6 +242,7 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U
|
|||||||
}
|
}
|
||||||
cmdId := scbase.GenSCUUID()
|
cmdId := scbase.GenSCUUID()
|
||||||
cmdStr := firstArg(pk)
|
cmdStr := firstArg(pk)
|
||||||
|
isRtnStateCmd := IsReturnStateCommand(cmdStr)
|
||||||
runPacket := packet.MakeRunPacket()
|
runPacket := packet.MakeRunPacket()
|
||||||
runPacket.ReqId = uuid.New().String()
|
runPacket.ReqId = uuid.New().String()
|
||||||
runPacket.CK = base.MakeCommandKey(ids.SessionId, cmdId)
|
runPacket.CK = base.MakeCommandKey(ids.SessionId, cmdId)
|
||||||
@@ -288,6 +251,7 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U
|
|||||||
runPacket.UsePty = true
|
runPacket.UsePty = true
|
||||||
runPacket.TermOpts = getUITermOpts(pk.UIContext)
|
runPacket.TermOpts = getUITermOpts(pk.UIContext)
|
||||||
runPacket.Command = strings.TrimSpace(cmdStr)
|
runPacket.Command = strings.TrimSpace(cmdStr)
|
||||||
|
runPacket.ReturnState = resolveBool(pk.Kwargs["rtnstate"], isRtnStateCmd)
|
||||||
cmd, callback, err := remote.RunCommand(ctx, cmdId, ids.Remote.RemotePtr, ids.Remote.RemoteState, runPacket)
|
cmd, callback, err := remote.RunCommand(ctx, cmdId, ids.Remote.RemotePtr, ids.Remote.RemoteState, runPacket)
|
||||||
if callback != nil {
|
if callback != nil {
|
||||||
defer callback()
|
defer callback()
|
||||||
@@ -522,43 +486,6 @@ func SwSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore
|
|||||||
return sstore.ModelUpdate{ScreenWindows: []*sstore.ScreenWindowType{sw}}, nil
|
return sstore.ModelUpdate{ScreenWindows: []*sstore.ScreenWindowType{sw}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
|
||||||
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_RemoteConnected)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot unset: %v", err)
|
|
||||||
}
|
|
||||||
declMap := shexec.DeclMapFromState(ids.Remote.RemoteState)
|
|
||||||
unsetVars := make(map[string]bool)
|
|
||||||
for _, argStr := range pk.Args {
|
|
||||||
eqIdx := strings.Index(argStr, "=")
|
|
||||||
if eqIdx != -1 {
|
|
||||||
return nil, fmt.Errorf("invalid argument to setenv, '%s' (cannot contain equal sign)", argStr)
|
|
||||||
}
|
|
||||||
delete(declMap, argStr)
|
|
||||||
unsetVars[argStr] = true
|
|
||||||
}
|
|
||||||
if len(unsetVars) == 0 {
|
|
||||||
return nil, fmt.Errorf("no variables provided to unset")
|
|
||||||
}
|
|
||||||
state := *ids.Remote.RemoteState
|
|
||||||
state.ShellVars = shexec.SerializeDeclMap(declMap)
|
|
||||||
remoteInst, err := sstore.UpdateRemoteState(ctx, ids.SessionId, ids.WindowId, ids.Remote.RemotePtr, state)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var cmdOutput bytes.Buffer
|
|
||||||
displayStateUpdate(&cmdOutput, *ids.Remote.RemoteState, remoteInst.State)
|
|
||||||
cmd, err := makeStaticCmd(ctx, "unset", ids, pk.GetRawStr(), cmdOutput.Bytes())
|
|
||||||
update, err := addLineForCmd(ctx, "/unset", false, ids, cmd)
|
|
||||||
if err != nil {
|
|
||||||
// TODO tricky error since the command was a success, but we can't show the output
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
update.Interactive = pk.Interactive
|
|
||||||
update.Sessions = sstore.MakeSessionsUpdateForRemote(ids.SessionId, remoteInst)
|
|
||||||
return update, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func RemoteInstallCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
func RemoteInstallCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
||||||
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_Remote)
|
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_Remote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -939,56 +866,6 @@ func RemoteCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstor
|
|||||||
return nil, fmt.Errorf("/remote requires a subcommand: %s", formatStrs([]string{"show"}, "or", false))
|
return nil, fmt.Errorf("/remote requires a subcommand: %s", formatStrs([]string{"show"}, "or", false))
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetEnvCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
|
||||||
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_RemoteConnected)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot setenv: %v", err)
|
|
||||||
}
|
|
||||||
declMap := shexec.DeclMapFromState(ids.Remote.RemoteState)
|
|
||||||
if len(pk.Args) == 0 {
|
|
||||||
var infoLines []string
|
|
||||||
for _, decl := range declMap {
|
|
||||||
line := fmt.Sprintf("%s=%s", decl.Name, shellescape.Quote(decl.Value))
|
|
||||||
infoLines = append(infoLines, line)
|
|
||||||
}
|
|
||||||
update := sstore.ModelUpdate{
|
|
||||||
Info: &sstore.InfoMsgType{
|
|
||||||
InfoTitle: fmt.Sprintf("environment for remote [%s]", ids.Remote.DisplayName),
|
|
||||||
InfoLines: infoLines,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return update, nil
|
|
||||||
}
|
|
||||||
setVars := make(map[string]bool)
|
|
||||||
for _, argStr := range pk.Args {
|
|
||||||
eqIdx := strings.Index(argStr, "=")
|
|
||||||
if eqIdx == -1 {
|
|
||||||
return nil, fmt.Errorf("invalid argument to setenv, '%s' (no equal sign)", argStr)
|
|
||||||
}
|
|
||||||
envName := argStr[:eqIdx]
|
|
||||||
envVal := argStr[eqIdx+1:]
|
|
||||||
declMap[envName] = &shexec.DeclareDeclType{Args: "x", Name: envName, Value: envVal}
|
|
||||||
setVars[envName] = true
|
|
||||||
}
|
|
||||||
state := *ids.Remote.RemoteState
|
|
||||||
state.ShellVars = shexec.SerializeDeclMap(declMap)
|
|
||||||
remoteInst, err := sstore.UpdateRemoteState(ctx, ids.SessionId, ids.WindowId, ids.Remote.RemotePtr, state)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var cmdOutput bytes.Buffer
|
|
||||||
displayStateUpdate(&cmdOutput, *ids.Remote.RemoteState, remoteInst.State)
|
|
||||||
cmd, err := makeStaticCmd(ctx, "setenv", ids, pk.GetRawStr(), cmdOutput.Bytes())
|
|
||||||
update, err := addLineForCmd(ctx, "/setenv", false, ids, cmd)
|
|
||||||
if err != nil {
|
|
||||||
// TODO tricky error since the command was a success, but we can't show the output
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
update.Interactive = pk.Interactive
|
|
||||||
update.Sessions = sstore.MakeSessionsUpdateForRemote(ids.SessionId, remoteInst)
|
|
||||||
return update, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func CrCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
func CrCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
||||||
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window)
|
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1814,21 +1691,25 @@ func formatTextTable(totalCols int, data [][]string, colMeta []ColMeta) []string
|
|||||||
|
|
||||||
func displayStateUpdate(buf *bytes.Buffer, oldState packet.ShellState, newState packet.ShellState) {
|
func displayStateUpdate(buf *bytes.Buffer, oldState packet.ShellState, newState packet.ShellState) {
|
||||||
if newState.Cwd != oldState.Cwd {
|
if newState.Cwd != oldState.Cwd {
|
||||||
buf.WriteString(fmt.Sprintf("cwd %s\r\n", newState.Cwd))
|
buf.WriteString(fmt.Sprintf("cwd %s\n", newState.Cwd))
|
||||||
}
|
}
|
||||||
if !bytes.Equal(newState.ShellVars, oldState.ShellVars) {
|
if !bytes.Equal(newState.ShellVars, oldState.ShellVars) {
|
||||||
newEnvMap := shexec.DeclMapFromState(&newState)
|
newEnvMap := shexec.DeclMapFromState(&newState)
|
||||||
oldEnvMap := shexec.DeclMapFromState(&oldState)
|
oldEnvMap := shexec.DeclMapFromState(&oldState)
|
||||||
for key, newVal := range newEnvMap {
|
for key, newVal := range newEnvMap {
|
||||||
oldVal, found := oldEnvMap[key]
|
oldVal, found := oldEnvMap[key]
|
||||||
if !found || oldVal.Value != newVal.Value {
|
if !found || ((oldVal.Value != newVal.Value) || (oldVal.IsExport() != newVal.IsExport())) {
|
||||||
buf.WriteString(fmt.Sprintf("%s=%s\r\n", key, shellescape.Quote(newVal.Value)))
|
var exportStr string
|
||||||
|
if newVal.IsExport() {
|
||||||
|
exportStr = "export "
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf("%s%s=%s\n", exportStr, key, ShellQuote(newVal.Value, false, 50)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for key, _ := range oldEnvMap {
|
for key, _ := range oldEnvMap {
|
||||||
_, found := newEnvMap[key]
|
_, found := newEnvMap[key]
|
||||||
if !found {
|
if !found {
|
||||||
buf.WriteString(fmt.Sprintf("unset %s\r\n", key))
|
buf.WriteString(fmt.Sprintf("unset %s\n", key))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1844,7 +1725,7 @@ func displayStateUpdate(buf *bytes.Buffer, oldState packet.ShellState, newState
|
|||||||
for aliasName, _ := range oldAliasMap {
|
for aliasName, _ := range oldAliasMap {
|
||||||
_, found := newAliasMap[aliasName]
|
_, found := newAliasMap[aliasName]
|
||||||
if !found {
|
if !found {
|
||||||
buf.WriteString(fmt.Sprintf("unalias %s\r\n", shellescape.Quote(aliasName)))
|
buf.WriteString(fmt.Sprintf("unalias %s\n", shellescape.Quote(aliasName)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1860,8 +1741,27 @@ func displayStateUpdate(buf *bytes.Buffer, oldState packet.ShellState, newState
|
|||||||
for funcName, _ := range oldFuncMap {
|
for funcName, _ := range oldFuncMap {
|
||||||
_, found := newFuncMap[funcName]
|
_, found := newFuncMap[funcName]
|
||||||
if !found {
|
if !found {
|
||||||
buf.WriteString(fmt.Sprintf("unset -f %s\r\n", shellescape.Quote(funcName)))
|
buf.WriteString(fmt.Sprintf("unset -f %s\n", shellescape.Quote(funcName)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetRtnStateDiff(ctx context.Context, sessionId string, cmdId string) ([]byte, error) {
|
||||||
|
cmd, err := sstore.GetCmdById(ctx, sessionId, cmdId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if cmd == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if !cmd.RtnState {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if cmd.DonePk == nil || cmd.DonePk.FinalState == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var outputBytes bytes.Buffer
|
||||||
|
displayStateUpdate(&outputBytes, cmd.RemoteState, *cmd.DonePk.FinalState)
|
||||||
|
return outputBytes.Bytes(), nil
|
||||||
|
}
|
||||||
|
|||||||
+53
-13
@@ -59,14 +59,8 @@ type BareMetaCmdDecl struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var BareMetaCmds = []BareMetaCmdDecl{
|
var BareMetaCmds = []BareMetaCmdDecl{
|
||||||
BareMetaCmdDecl{"cd", "cd"},
|
|
||||||
BareMetaCmdDecl{"cr", "cr"},
|
BareMetaCmdDecl{"cr", "cr"},
|
||||||
BareMetaCmdDecl{"setenv", "setenv"},
|
|
||||||
BareMetaCmdDecl{"export", "setenv"},
|
|
||||||
BareMetaCmdDecl{"unset", "unset"},
|
|
||||||
BareMetaCmdDecl{"clear", "clear"},
|
BareMetaCmdDecl{"clear", "clear"},
|
||||||
BareMetaCmdDecl{".", "source"},
|
|
||||||
BareMetaCmdDecl{"source", "source"},
|
|
||||||
BareMetaCmdDecl{"reset", "reset"},
|
BareMetaCmdDecl{"reset", "reset"},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +117,7 @@ func onlyRawArgs(metaCmd string, metaSubCmd string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// minimum maxlen=6
|
// minimum maxlen=6
|
||||||
func ForceQuote(val string, maxLen int) string {
|
func ShellQuote(val string, forceQuote bool, maxLen int) string {
|
||||||
if maxLen < 6 {
|
if maxLen < 6 {
|
||||||
maxLen = 6
|
maxLen = 6
|
||||||
}
|
}
|
||||||
@@ -134,10 +128,17 @@ func ForceQuote(val string, maxLen int) string {
|
|||||||
}
|
}
|
||||||
return rtn
|
return rtn
|
||||||
}
|
}
|
||||||
if len(rtn) > maxLen-2 {
|
if forceQuote {
|
||||||
return "\"" + rtn[0:maxLen-5] + "...\""
|
if len(rtn) > maxLen-2 {
|
||||||
|
return "\"" + rtn[0:maxLen-5] + "...\""
|
||||||
|
}
|
||||||
|
return "\"" + rtn + "\""
|
||||||
|
} else {
|
||||||
|
if len(rtn) > maxLen {
|
||||||
|
return rtn[0:maxLen-3] + "..."
|
||||||
|
}
|
||||||
|
return rtn
|
||||||
}
|
}
|
||||||
return "\"" + rtn + "\""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setBracketArgs(argMap map[string]string, bracketStr string) error {
|
func setBracketArgs(argMap map[string]string, bracketStr string) error {
|
||||||
@@ -163,7 +164,7 @@ func setBracketArgs(argMap map[string]string, bracketStr string) error {
|
|||||||
varVal = litStr[eqIdx+1:]
|
varVal = litStr[eqIdx+1:]
|
||||||
}
|
}
|
||||||
if !shexec.IsValidBashIdentifier(varName) {
|
if !shexec.IsValidBashIdentifier(varName) {
|
||||||
wordErr = fmt.Errorf("invalid identifier %s in bracket args", ForceQuote(varName, 20))
|
wordErr = fmt.Errorf("invalid identifier %s in bracket args", ShellQuote(varName, true, 20))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if varVal == "" {
|
if varVal == "" {
|
||||||
@@ -181,6 +182,35 @@ func setBracketArgs(argMap map[string]string, bracketStr string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// detects: export, declare, ., source, X=1, unset
|
||||||
|
func IsReturnStateCommand(cmdStr string) bool {
|
||||||
|
cmdReader := strings.NewReader(cmdStr)
|
||||||
|
parser := syntax.NewParser(syntax.Variant(syntax.LangBash))
|
||||||
|
file, err := parser.Parse(cmdReader, "cmd")
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, stmt := range file.Stmts {
|
||||||
|
if callExpr, ok := stmt.Cmd.(*syntax.CallExpr); ok {
|
||||||
|
if len(callExpr.Assigns) > 0 && len(callExpr.Args) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(callExpr.Args) > 0 && len(callExpr.Args[0].Parts) > 0 {
|
||||||
|
lit, ok := callExpr.Args[0].Parts[0].(*syntax.Lit)
|
||||||
|
if ok {
|
||||||
|
if lit.Value == "." || lit.Value == "source" || lit.Value == "unset" || lit.Value == "cd" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else if _, ok := stmt.Cmd.(*syntax.DeclClause); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func EvalBracketArgs(origCmdStr string) (map[string]string, string, error) {
|
func EvalBracketArgs(origCmdStr string) (map[string]string, string, error) {
|
||||||
rtn := make(map[string]string)
|
rtn := make(map[string]string)
|
||||||
if strings.HasPrefix(origCmdStr, " ") {
|
if strings.HasPrefix(origCmdStr, " ") {
|
||||||
@@ -210,7 +240,11 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType)
|
|||||||
if strings.TrimSpace(origPk.Args[0]) == "" {
|
if strings.TrimSpace(origPk.Args[0]) == "" {
|
||||||
return nil, fmt.Errorf("empty command")
|
return nil, fmt.Errorf("empty command")
|
||||||
}
|
}
|
||||||
metaCmd, metaSubCmd, commandArgs := parseMetaCmd(origPk.Args[0])
|
bracketArgs, cmdStr, err := EvalBracketArgs(origPk.Args[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
metaCmd, metaSubCmd, commandArgs := parseMetaCmd(cmdStr)
|
||||||
rtnPk := scpacket.MakeFeCommandPacket()
|
rtnPk := scpacket.MakeFeCommandPacket()
|
||||||
rtnPk.MetaCmd = metaCmd
|
rtnPk.MetaCmd = metaCmd
|
||||||
rtnPk.MetaSubCmd = metaSubCmd
|
rtnPk.MetaSubCmd = metaSubCmd
|
||||||
@@ -220,6 +254,9 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType)
|
|||||||
for key, val := range origPk.Kwargs {
|
for key, val := range origPk.Kwargs {
|
||||||
rtnPk.Kwargs[key] = val
|
rtnPk.Kwargs[key] = val
|
||||||
}
|
}
|
||||||
|
for key, val := range bracketArgs {
|
||||||
|
rtnPk.Kwargs[key] = val
|
||||||
|
}
|
||||||
if onlyRawArgs(metaCmd, metaSubCmd) {
|
if onlyRawArgs(metaCmd, metaSubCmd) {
|
||||||
// don't evaluate arguments for /run or /comment
|
// don't evaluate arguments for /run or /comment
|
||||||
rtnPk.Args = []string{commandArgs}
|
rtnPk.Args = []string{commandArgs}
|
||||||
@@ -228,7 +265,7 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType)
|
|||||||
commandReader := strings.NewReader(commandArgs)
|
commandReader := strings.NewReader(commandArgs)
|
||||||
parser := syntax.NewParser(syntax.Variant(syntax.LangBash))
|
parser := syntax.NewParser(syntax.Variant(syntax.LangBash))
|
||||||
var words []*syntax.Word
|
var words []*syntax.Word
|
||||||
err := parser.Words(commandReader, func(w *syntax.Word) bool {
|
err = parser.Words(commandReader, func(w *syntax.Word) bool {
|
||||||
words = append(words, w)
|
words = append(words, w)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@@ -338,6 +375,9 @@ func ParseFuncs(funcs string) (map[string]string, error) {
|
|||||||
fmt.Printf("stmt-err: %v\n", err)
|
fmt.Printf("stmt-err: %v\n", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(funcName, "_scripthaus_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if funcName != "" {
|
if funcName != "" {
|
||||||
rtn[funcName] = funcVal
|
rtn[funcName] = funcVal
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1106,6 +1106,7 @@ func RunCommand(ctx context.Context, cmdId string, remotePtr sstore.RemotePtrTyp
|
|||||||
StartPk: startPk,
|
StartPk: startPk,
|
||||||
DonePk: nil,
|
DonePk: nil,
|
||||||
RunOut: nil,
|
RunOut: nil,
|
||||||
|
RtnState: runPacket.ReturnState,
|
||||||
}
|
}
|
||||||
err = sstore.CreateCmdPtyFile(ctx, cmd.SessionId, cmd.CmdId, cmd.TermOpts.MaxPtySize)
|
err = sstore.CreateCmdPtyFile(ctx, cmd.SessionId, cmd.CmdId, cmd.TermOpts.MaxPtySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+4
-4
@@ -623,8 +623,8 @@ func InsertLine(ctx context.Context, line *LineType, cmd *CmdType) error {
|
|||||||
query = `SELECT nextlinenum FROM window WHERE sessionid = ? AND windowid = ?`
|
query = `SELECT nextlinenum FROM window WHERE sessionid = ? AND windowid = ?`
|
||||||
nextLineNum := tx.GetInt(query, line.SessionId, line.WindowId)
|
nextLineNum := tx.GetInt(query, line.SessionId, line.WindowId)
|
||||||
line.LineNum = int64(nextLineNum)
|
line.LineNum = int64(nextLineNum)
|
||||||
query = `INSERT INTO line ( sessionid, windowid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, text, cmdid, ephemeral)
|
query = `INSERT INTO line ( sessionid, windowid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, text, cmdid, ephemeral, contentheight)
|
||||||
VALUES (:sessionid,:windowid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:text,:cmdid,:ephemeral)`
|
VALUES (:sessionid,:windowid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:text,:cmdid,:ephemeral,:contentheight)`
|
||||||
tx.NamedExecWrap(query, line)
|
tx.NamedExecWrap(query, line)
|
||||||
query = `UPDATE window SET nextlinenum = ? WHERE sessionid = ? AND windowid = ?`
|
query = `UPDATE window SET nextlinenum = ? WHERE sessionid = ? AND windowid = ?`
|
||||||
tx.ExecWrap(query, nextLineNum+1, line.SessionId, line.WindowId)
|
tx.ExecWrap(query, nextLineNum+1, line.SessionId, line.WindowId)
|
||||||
@@ -632,8 +632,8 @@ func InsertLine(ctx context.Context, line *LineType, cmd *CmdType) error {
|
|||||||
cmd.OrigTermOpts = cmd.TermOpts
|
cmd.OrigTermOpts = cmd.TermOpts
|
||||||
cmdMap := cmd.ToMap()
|
cmdMap := cmd.ToMap()
|
||||||
query = `
|
query = `
|
||||||
INSERT INTO cmd ( sessionid, cmdid, remoteownerid, remoteid, remotename, cmdstr, remotestate, termopts, origtermopts, status, startpk, donepk, runout, usedrows)
|
INSERT INTO cmd ( sessionid, cmdid, remoteownerid, remoteid, remotename, cmdstr, remotestate, termopts, origtermopts, status, startpk, donepk, rtnstate, runout)
|
||||||
VALUES (:sessionid,:cmdid,:remoteownerid,:remoteid,:remotename,:cmdstr,:remotestate,:termopts,:origtermopts,:status,:startpk,:donepk,:runout,:usedrows)
|
VALUES (:sessionid,:cmdid,:remoteownerid,:remoteid,:remotename,:cmdstr,:remotestate,:termopts,:origtermopts,:status,:startpk,:donepk,:rtnstate,:runout)
|
||||||
`
|
`
|
||||||
tx.NamedExecWrap(query, cmdMap)
|
tx.NamedExecWrap(query, cmdMap)
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-20
@@ -28,6 +28,7 @@ import (
|
|||||||
|
|
||||||
const LineTypeCmd = "cmd"
|
const LineTypeCmd = "cmd"
|
||||||
const LineTypeText = "text"
|
const LineTypeText = "text"
|
||||||
|
const LineNoHeight = -1
|
||||||
const DBFileName = "sh2.db"
|
const DBFileName = "sh2.db"
|
||||||
|
|
||||||
const DefaultSessionName = "default"
|
const DefaultSessionName = "default"
|
||||||
@@ -496,19 +497,20 @@ func RIFromMap(m map[string]interface{}) *RemoteInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LineType struct {
|
type LineType struct {
|
||||||
SessionId string `json:"sessionid"`
|
SessionId string `json:"sessionid"`
|
||||||
WindowId string `json:"windowid"`
|
WindowId string `json:"windowid"`
|
||||||
UserId string `json:"userid"`
|
UserId string `json:"userid"`
|
||||||
LineId string `json:"lineid"`
|
LineId string `json:"lineid"`
|
||||||
Ts int64 `json:"ts"`
|
Ts int64 `json:"ts"`
|
||||||
LineNum int64 `json:"linenum"`
|
LineNum int64 `json:"linenum"`
|
||||||
LineNumTemp bool `json:"linenumtemp,omitempty"`
|
LineNumTemp bool `json:"linenumtemp,omitempty"`
|
||||||
LineLocal bool `json:"linelocal"`
|
LineLocal bool `json:"linelocal"`
|
||||||
LineType string `json:"linetype"`
|
LineType string `json:"linetype"`
|
||||||
Text string `json:"text,omitempty"`
|
Text string `json:"text,omitempty"`
|
||||||
CmdId string `json:"cmdid,omitempty"`
|
CmdId string `json:"cmdid,omitempty"`
|
||||||
Ephemeral bool `json:"ephemeral,omitempty"`
|
Ephemeral bool `json:"ephemeral,omitempty"`
|
||||||
Remove bool `json:"remove,omitempty"`
|
Remove bool `json:"remove,omitempty"`
|
||||||
|
ContentHeight int64 `json:"contentheight,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResolveItem struct {
|
type ResolveItem struct {
|
||||||
@@ -575,11 +577,11 @@ type CmdType struct {
|
|||||||
TermOpts TermOpts `json:"termopts"`
|
TermOpts TermOpts `json:"termopts"`
|
||||||
OrigTermOpts TermOpts `json:"origtermopts"`
|
OrigTermOpts TermOpts `json:"origtermopts"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
StartPk *packet.CmdStartPacketType `json:"startpk"`
|
StartPk *packet.CmdStartPacketType `json:"startpk,omitempty"`
|
||||||
DonePk *packet.CmdDonePacketType `json:"donepk"`
|
DonePk *packet.CmdDonePacketType `json:"donepk,omitempty"`
|
||||||
UsedRows int64 `json:"usedrows"`
|
RunOut []packet.PacketType `json:"runout,omitempty"`
|
||||||
RunOut []packet.PacketType `json:"runout"`
|
RtnState bool `json:"rtnstate,omitempty"`
|
||||||
Remove bool `json:"remove"`
|
Remove bool `json:"remove,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RemoteType) ToMap() map[string]interface{} {
|
func (r *RemoteType) ToMap() map[string]interface{} {
|
||||||
@@ -644,7 +646,7 @@ func (cmd *CmdType) ToMap() map[string]interface{} {
|
|||||||
rtn["startpk"] = quickJson(cmd.StartPk)
|
rtn["startpk"] = quickJson(cmd.StartPk)
|
||||||
rtn["donepk"] = quickJson(cmd.DonePk)
|
rtn["donepk"] = quickJson(cmd.DonePk)
|
||||||
rtn["runout"] = quickJson(cmd.RunOut)
|
rtn["runout"] = quickJson(cmd.RunOut)
|
||||||
rtn["usedrows"] = cmd.UsedRows
|
rtn["rtnstate"] = cmd.RtnState
|
||||||
return rtn
|
return rtn
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,7 +668,7 @@ func CmdFromMap(m map[string]interface{}) *CmdType {
|
|||||||
quickSetJson(&cmd.StartPk, m, "startpk")
|
quickSetJson(&cmd.StartPk, m, "startpk")
|
||||||
quickSetJson(&cmd.DonePk, m, "donepk")
|
quickSetJson(&cmd.DonePk, m, "donepk")
|
||||||
quickSetJson(&cmd.RunOut, m, "runout")
|
quickSetJson(&cmd.RunOut, m, "runout")
|
||||||
quickSetInt64(&cmd.UsedRows, m, "usedrows")
|
quickSetBool(&cmd.RtnState, m, "rtnstate")
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -680,6 +682,7 @@ func makeNewLineCmd(sessionId string, windowId string, userId string, cmdId stri
|
|||||||
rtn.LineLocal = true
|
rtn.LineLocal = true
|
||||||
rtn.LineType = LineTypeCmd
|
rtn.LineType = LineTypeCmd
|
||||||
rtn.CmdId = cmdId
|
rtn.CmdId = cmdId
|
||||||
|
rtn.ContentHeight = LineNoHeight
|
||||||
return rtn
|
return rtn
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,6 +696,7 @@ func makeNewLineText(sessionId string, windowId string, userId string, text stri
|
|||||||
rtn.LineLocal = true
|
rtn.LineLocal = true
|
||||||
rtn.LineType = LineTypeText
|
rtn.LineType = LineTypeText
|
||||||
rtn.Text = text
|
rtn.Text = text
|
||||||
|
rtn.ContentHeight = LineNoHeight
|
||||||
return rtn
|
return rtn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user