diff --git a/db/migrations/000001_init.up.sql b/db/migrations/000001_init.up.sql index b428e2cb..630af038 100644 --- a/db/migrations/000001_init.up.sql +++ b/db/migrations/000001_init.up.sql @@ -98,6 +98,7 @@ CREATE TABLE cmd ( donepk json NOT NULL, runout json NOT NULL, usedrows int NOT NULL, + prompt text NOT NULL, PRIMARY KEY (sessionid, cmdid) ); diff --git a/db/schema.sql b/db/schema.sql index eedef254..9f4a2d58 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -62,6 +62,7 @@ CREATE TABLE line ( linetype varchar(10) NOT NULL, text text NOT NULL, cmdid varchar(36) NOT NULL, + ephemeral boolean NOT NULL, PRIMARY KEY (sessionid, windowid, lineid) ); CREATE TABLE remote ( @@ -91,6 +92,7 @@ CREATE TABLE cmd ( donepk json NOT NULL, runout json NOT NULL, usedrows int NOT NULL, + prompt text NOT NULL, PRIMARY KEY (sessionid, cmdid) ); CREATE TABLE history ( diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index a84c068c..b0308d17 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -9,6 +9,7 @@ import ( "os" "os/exec" "path" + "strconv" "strings" "sync" "syscall" @@ -151,6 +152,14 @@ func unquoteDQBashString(str string) (string, bool) { return string(rtn), true } +func makeShortHost(host string) string { + dotIdx := strings.Index(host, ".") + if dotIdx == -1 { + return host + } + return host[0:dotIdx] +} + func (proc *MShellProc) GetRemoteState() RemoteState { proc.Lock.Lock() defer proc.Lock.Unlock() @@ -169,6 +178,7 @@ func (proc *MShellProc) GetRemoteState() RemoteState { vars := make(map[string]string) vars["user"] = proc.Remote.RemoteUser vars["host"] = proc.Remote.RemoteHost + vars["shorthost"] = makeShortHost(proc.Remote.RemoteHost) if proc.Remote.RemoteSudo { vars["sudo"] = "1" } @@ -186,6 +196,7 @@ func (proc *MShellProc) GetRemoteState() RemoteState { vars["home"] = proc.ServerProc.InitPk.HomeDir vars["remoteuser"] = proc.ServerProc.InitPk.User vars["remotehost"] = proc.ServerProc.InitPk.HostName + vars["remoteshorthost"] = makeShortHost(proc.ServerProc.InitPk.HostName) if proc.Remote.SSHOpts == nil || proc.Remote.SSHOpts.SSHHost == "" { vars["local"] = "1" } @@ -590,3 +601,104 @@ func (runner *MShellProc) ProcessPackets() { fmt.Printf("MSH> %s\n", packet.AsString(pk)) } } + +func EvalPromptEsc(escCode string, vars map[string]string, state sstore.RemoteState) string { + if escCode == "d" { + now := time.Now() + return now.Format("Mon Jan 02") + } + if strings.HasPrefix(escCode, "x{") && strings.HasSuffix(escCode, "}") { + varName := escCode[2 : len(escCode)-1] + return vars[varName] + } + if strings.HasPrefix(escCode, "y{") && strings.HasSuffix(escCode, "}") { + varName := escCode[2 : len(escCode)-1] + varMap := shexec.ParseEnv0(state.Env0) + return varMap[varName] + } + if escCode == "h" { + return vars["remoteshorthost"] + } + if escCode == "H" { + return vars["remotehost"] + } + if escCode == "j" { + return "0" + } + if escCode == "l" { + return "(l)" + } + if escCode == "s" { + return "mshell" + } + if escCode == "t" { + now := time.Now() + return now.Format("15:04:05") + } + if escCode == "T" { + now := time.Now() + return now.Format("03:04:05") + } + if escCode == "@" { + now := time.Now() + return now.Format("03:04:05PM") + } + if escCode == "u" { + return vars["remoteuser"] + } + if escCode == "v" { + return "0.1" + } + if escCode == "V" { + return "0" + } + if escCode == "w" { + return state.Cwd + } + if escCode == "W" { + return path.Base(state.Cwd) + } + if escCode == "!" { + return "(!)" + } + if escCode == "#" { + return "(#)" + } + if escCode == "$" { + if vars["remoteuser"] == "root" { + return "#" + } else { + return "$" + } + } + if len(escCode) == 3 { + // \nnn escape + ival, err := strconv.ParseInt(escCode, 8, 32) + if err != nil { + return escCode + } + return string([]byte{byte(ival)}) + } + if escCode == "e" { + return "\033" + } + if escCode == "n" { + return "\n" + } + if escCode == "r" { + return "\r" + } + if escCode == "a" { + return "\007" + } + if escCode == "\\" { + return "\\" + } + if escCode == "[" { + return "" + } + if escCode == "]" { + return "" + } + return "(" + escCode + ")" +} diff --git a/pkg/sstore/dbops.go b/pkg/sstore/dbops.go index a0de382d..2de2b72d 100644 --- a/pkg/sstore/dbops.go +++ b/pkg/sstore/dbops.go @@ -404,8 +404,8 @@ func InsertLine(ctx context.Context, line *LineType, cmd *CmdType) error { if cmd != nil { cmdMap := cmd.ToMap() query = ` -INSERT INTO cmd ( sessionid, cmdid, remoteid, cmdstr, remotestate, termopts, status, startpk, donepk, runout, usedrows) - VALUES (:sessionid,:cmdid,:remoteid,:cmdstr,:remotestate,:termopts,:status,:startpk,:donepk,:runout,:usedrows) +INSERT INTO cmd ( sessionid, cmdid, remoteid, cmdstr, remotestate, termopts, status, startpk, donepk, runout, usedrows, prompt) + VALUES (:sessionid,:cmdid,:remoteid,:cmdstr,:remotestate,:termopts,:status,:startpk,:donepk,:runout,:usedrows,:prompt) ` tx.NamedExecWrap(query, cmdMap) } diff --git a/pkg/sstore/sstore.go b/pkg/sstore/sstore.go index f82d38d2..b07243ac 100644 --- a/pkg/sstore/sstore.go +++ b/pkg/sstore/sstore.go @@ -283,6 +283,7 @@ type LineType struct { } type SSHOpts struct { + Local bool `json:"local"` SSHHost string `json:"sshhost"` SSHOptsStr string `json:"sshopts"` SSHIdentity string `json:"sshidentity"` @@ -290,7 +291,8 @@ type SSHOpts struct { } type RemoteOptsType struct { - Color string `json:"color"` + Color string `json:"color"` + Prompt string `json:"prompt"` } func (opts *RemoteOptsType) Scan(val interface{}) error { @@ -339,6 +341,7 @@ type CmdType struct { DonePk *packet.CmdDonePacketType `json:"donepk"` UsedRows int64 `json:"usedrows"` RunOut []packet.PacketType `json:"runout"` + Prompt string `json:"prompt"` Remove bool `json:"remove"` } @@ -394,6 +397,7 @@ func (cmd *CmdType) ToMap() map[string]interface{} { rtn["donepk"] = quickJson(cmd.DonePk) rtn["runout"] = quickJson(cmd.RunOut) rtn["usedrows"] = cmd.UsedRows + rtn["prompt"] = cmd.Prompt return rtn } @@ -413,6 +417,7 @@ func CmdFromMap(m map[string]interface{}) *CmdType { quickSetJson(&cmd.DonePk, m, "donepk") quickSetJson(&cmd.RunOut, m, "runout") quickSetInt64(&cmd.UsedRows, m, "usedrows") + quickSetStr(&cmd.Prompt, m, "prompt") return &cmd } @@ -489,6 +494,7 @@ func EnsureLocalRemote(ctx context.Context) error { RemoteUser: user.Username, RemoteHost: hostName, ConnectMode: ConnectModeStartup, + SSHOpts: &SSHOpts{Local: true}, } err = InsertRemote(ctx, localRemote) if err != nil {