diff --git a/go.mod b/go.mod index e64ed540..e1a2464c 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/scripthaus-dev/sh2-runner +module github.com/scripthaus-dev/mshell go 1.17 diff --git a/main-runner.go b/main-mshell.go similarity index 96% rename from main-runner.go rename to main-mshell.go index 64a54abd..dd551372 100644 --- a/main-runner.go +++ b/main-mshell.go @@ -15,10 +15,10 @@ import ( "time" "github.com/google/uuid" - "github.com/scripthaus-dev/sh2-runner/pkg/base" - "github.com/scripthaus-dev/sh2-runner/pkg/cmdtail" - "github.com/scripthaus-dev/sh2-runner/pkg/packet" - "github.com/scripthaus-dev/sh2-runner/pkg/shexec" + "github.com/scripthaus-dev/mshell/pkg/base" + "github.com/scripthaus-dev/mshell/pkg/cmdtail" + "github.com/scripthaus-dev/mshell/pkg/packet" + "github.com/scripthaus-dev/mshell/pkg/shexec" ) // in single run mode, we don't want the runner to die from signals @@ -155,7 +155,7 @@ func doMain() { packet.SendErrorPacket(os.Stdout, fmt.Sprintf("cannot change directory to $HOME '%s': %v", homeDir, err)) return } - err = base.EnsureRunnerPath() + err = base.EnsureMShellPath() if err != nil { packet.SendErrorPacket(os.Stdout, err.Error()) return diff --git a/pkg/base/base.go b/pkg/base/base.go index 5da63c01..91f2959b 100644 --- a/pkg/base/base.go +++ b/pkg/base/base.go @@ -11,11 +11,14 @@ import ( "fmt" "io/fs" "os" + "os/exec" "path" "path/filepath" ) -const ScRunnerVarName = "SCRIPTHAUS_RUNNER" +const DefaultMShellPath = "mshell" +const MShellPathVarName = "MSHELL_PATH" +const SSHCommandVarName = "SSH_COMMAND" const ScHomeVarName = "SCRIPTHAUS_HOME" const HomeVarName = "HOME" const ScShell = "bash" @@ -125,33 +128,20 @@ func EnsureSessionDir(sessionId string) (string, error) { return sdir, nil } -func GetScRunnerPath() (string, error) { - runnerPath := os.Getenv(ScRunnerVarName) - if runnerPath != "" { - return runnerPath, nil +func GetMShellPath() string { + msPath := os.Getenv(MShellPathVarName) + if msPath != "" { + return msPath } - scHome, err := GetScHomeDir() - if err != nil { - return "", err - } - return path.Join(scHome, RunnerBaseName), nil + return DefaultMShellPath } -func EnsureRunnerPath() error { - runnerPath, err := GetScRunnerPath() +func EnsureMShellPath() error { + msPath := GetMShellPath() + _, err := exec.LookPath(msPath) if err != nil { return err } - info, err := os.Stat(runnerPath) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - return fmt.Errorf("cannot find scripthaus runner at path '%s'", runnerPath) - } - return fmt.Errorf("error stating scripthaus runner at path '%s'", runnerPath) - } - if info.Mode()&0100 == 0 { - return fmt.Errorf("scripthaus runner at path '%s' is not executable mode=%#o", runnerPath, info.Mode()) - } return nil } diff --git a/pkg/cmdtail/cmdtail.go b/pkg/cmdtail/cmdtail.go index 44b44cc5..51cdbfd8 100644 --- a/pkg/cmdtail/cmdtail.go +++ b/pkg/cmdtail/cmdtail.go @@ -16,8 +16,8 @@ import ( "github.com/fsnotify/fsnotify" "github.com/google/uuid" - "github.com/scripthaus-dev/sh2-runner/pkg/base" - "github.com/scripthaus-dev/sh2-runner/pkg/packet" + "github.com/scripthaus-dev/mshell/pkg/base" + "github.com/scripthaus-dev/mshell/pkg/packet" ) const MaxDataBytes = 4096 diff --git a/pkg/packet/combined.go b/pkg/packet/combined.go new file mode 100644 index 00000000..0f15fd93 --- /dev/null +++ b/pkg/packet/combined.go @@ -0,0 +1,34 @@ +package packet + +type CombinedPacket struct { + Type string `json:"type"` + Success bool `json:"success"` + Ts int64 `json:"ts"` + Id string `json:"id,omitempty"` + + SessionId string `json:"sessionid"` + CmdId string `json:"cmdid"` + + PtyPos int64 `json:"ptypos"` + PtyLen int64 `json:"ptylen"` + RunPos int64 `json:"runpos"` + RunLen int64 `json:"runlen"` + + Error string `json:"error"` + NotFound bool `json:"notfound,omitempty"` + Tail bool `json:"tail,omitempty"` + Dir string `json:"dir"` + ChDir string `json:"chdir,omitempty"` + + Data string `json:"data"` + PtyData string `json:"ptydata"` + RunData string `json:"rundata"` + Message string `json:"message"` + Command string `json:"command"` + + ScHomeDir string `json:"schomedir"` + HomeDir string `json:"homedir"` + Env []string `json:"env"` + ExitCode int `json:"exitcode"` + RunnerPid int `json:"runnerpid"` +} diff --git a/pkg/packet/packet.go b/pkg/packet/packet.go index 2e145f3b..863fc418 100644 --- a/pkg/packet/packet.go +++ b/pkg/packet/packet.go @@ -18,22 +18,29 @@ import ( "sync" ) -const RunPacketStr = "run" -const PingPacketStr = "ping" -const DonePacketStr = "done" -const ErrorPacketStr = "error" -const MessagePacketStr = "message" -const CmdStartPacketStr = "cmdstart" -const CmdDonePacketStr = "cmddone" -const ListCmdPacketStr = "lscmd" -const GetCmdPacketStr = "getcmd" -const UntailCmdPacketStr = "untailcmd" -const RunnerInitPacketStr = "runnerinit" -const CdPacketStr = "cd" -const CdResponseStr = "cdresp" -const CmdDataPacketStr = "cmddata" -const RawPacketStr = "raw" -const InputPacketStr = "input" +// remote: runnerinit, run, ping, data, cmdstart, cmddone +// remote(detached): runnerinit, run, cmdstart +// server: runnerinit, run, ping, cmdstart, cmddone, cd, resp, getcmd, untailcmd, cmddata, input, data, [comp] +// all: error, message + +const ( + RunPacketStr = "run" + PingPacketStr = "ping" + RunnerInitPacketStr = "runnerinit" + DataPacketStr = "data" + CmdStartPacketStr = "cmdstart" + CmdDonePacketStr = "cmddone" + ResponsePacketStr = "resp" + DonePacketStr = "done" + ErrorPacketStr = "error" + MessagePacketStr = "message" + GetCmdPacketStr = "getcmd" + UntailCmdPacketStr = "untailcmd" + CdPacketStr = "cd" + CmdDataPacketStr = "cmddata" + RawPacketStr = "raw" + InputPacketStr = "input" +) var TypeStrToFactory map[string]reflect.Type @@ -41,20 +48,20 @@ func init() { TypeStrToFactory = make(map[string]reflect.Type) TypeStrToFactory[RunPacketStr] = reflect.TypeOf(RunPacketType{}) TypeStrToFactory[PingPacketStr] = reflect.TypeOf(PingPacketType{}) + TypeStrToFactory[ResponsePacketStr] = reflect.TypeOf(ResponsePacketType{}) TypeStrToFactory[DonePacketStr] = reflect.TypeOf(DonePacketType{}) TypeStrToFactory[ErrorPacketStr] = reflect.TypeOf(ErrorPacketType{}) TypeStrToFactory[MessagePacketStr] = reflect.TypeOf(MessagePacketType{}) TypeStrToFactory[CmdStartPacketStr] = reflect.TypeOf(CmdStartPacketType{}) TypeStrToFactory[CmdDonePacketStr] = reflect.TypeOf(CmdDonePacketType{}) - TypeStrToFactory[ListCmdPacketStr] = reflect.TypeOf(ListCmdPacketType{}) TypeStrToFactory[GetCmdPacketStr] = reflect.TypeOf(GetCmdPacketType{}) TypeStrToFactory[UntailCmdPacketStr] = reflect.TypeOf(UntailCmdPacketType{}) TypeStrToFactory[RunnerInitPacketStr] = reflect.TypeOf(RunnerInitPacketType{}) TypeStrToFactory[CdPacketStr] = reflect.TypeOf(CdPacketType{}) - TypeStrToFactory[CdResponseStr] = reflect.TypeOf(CdResponseType{}) TypeStrToFactory[CmdDataPacketStr] = reflect.TypeOf(CmdDataPacketType{}) TypeStrToFactory[RawPacketStr] = reflect.TypeOf(RawPacketType{}) TypeStrToFactory[InputPacketStr] = reflect.TypeOf(InputPacketType{}) + TypeStrToFactory[DataPacketStr] = reflect.TypeOf(DataPacketType{}) } func MakePacket(packetType string) (PacketType, error) { @@ -103,6 +110,22 @@ func MakePingPacket() *PingPacketType { return &PingPacketType{Type: PingPacketStr} } +type DataPacketType struct { + Type string `json:"type"` + SessionId string `json:"sessionid"` + CmdId string `json:"cmdid"` + FdNum int `json:"fdnum"` + Data string `json:"data"` +} + +func (*DataPacketType) GetType() string { + return DataPacketStr +} + +func MakeDataPacket(fdNum int, data string) *DataPacketType { + return &DataPacketType{Type: DataPacketStr, FdNum: fdNum, Data: data} +} + // InputData gets written to PTY directly // SigNum gets sent to process via a signal // WinSize, if set, will run TIOCSWINSZ to set size, and then send SIGWINCH @@ -157,19 +180,6 @@ func MakeGetCmdPacket() *GetCmdPacketType { return &GetCmdPacketType{Type: GetCmdPacketStr} } -type ListCmdPacketType struct { - Type string `json:"type"` - SessionId string `json:"sessionid"` -} - -func (*ListCmdPacketType) GetType() string { - return ListCmdPacketStr -} - -func MakeListCmdPacket(sessionId string) *ListCmdPacketType { - return &ListCmdPacketType{Type: ListCmdPacketStr, SessionId: sessionId} -} - type CdPacketType struct { Type string `json:"type"` PacketId string `json:"packetid"` @@ -180,23 +190,32 @@ func (*CdPacketType) GetType() string { return CdPacketStr } +func (p *CdPacketType) GetPacketId() string { + return p.PacketId +} + func MakeCdPacket() *CdPacketType { return &CdPacketType{Type: CdPacketStr} } -type CdResponseType struct { - Type string `json:"type"` - PacketId string `json:"packetid"` - Success bool `json:"success"` - Error string `json:"error"` +type ResponsePacketType struct { + Type string `json:"type"` + PacketId string `json:"packetid"` + Success bool `json:"success"` + Error string `json:"error"` + Data interface{} `json:"data"` } -func (*CdResponseType) GetType() string { - return CdResponseStr +func (*ResponsePacketType) GetType() string { + return ResponsePacketStr } -func MakeCdResponse() *CdResponseType { - return &CdResponseType{Type: CdResponseStr} +func (p *ResponsePacketType) GetPacketId() string { + return p.PacketId +} + +func MakeResponsePacket(packetId string) *ResponsePacketType { + return &ResponsePacketType{Type: ResponsePacketStr, PacketId: packetId} } type RawPacketType struct { @@ -232,10 +251,11 @@ func FmtMessagePacket(fmtStr string, args ...interface{}) *MessagePacketType { type RunnerInitPacketType struct { Type string `json:"type"` - ScHomeDir string `json:"schomedir"` - HomeDir string `json:"homedir"` - Env []string `json:"env"` - User string `json:"user"` + Version string `json:"version"` + ScHomeDir string `json:"schomedir,omitempty"` + HomeDir string `json:"homedir,omitempty"` + Env []string `json:"env,omitempty"` + User string `json:"user,omitempty"` } func (*RunnerInitPacketType) GetType() string { @@ -290,15 +310,26 @@ func MakeCmdStartPacket() *CmdStartPacketType { return &CmdStartPacketType{Type: CmdStartPacketStr} } +type TermSize struct { + Rows int `json:"rows"` + Cols int `json:"cols"` +} + +type RemoteFd struct { + FdNum int `json:"fdnum"` + Read bool `json:"read"` + Write bool `json:"write"` +} + type RunPacketType struct { Type string `json:"type"` SessionId string `json:"sessionid"` CmdId string `json:"cmdid"` - ChDir string `json:"chdir,omitempty"` - Env map[string]string `json:"env,omitempty"` Command string `json:"command"` - Rows int `json:"rows"` - Cols int `json:'cols"` + Cwd string `json:"cwd,omitempty"` + Env map[string]string `json:"env,omitempty"` + TermSize TermSize `json:"termsize"` + Fds []RemoteFd `json:"fds"` } func (*RunPacketType) GetType() string { @@ -335,6 +366,11 @@ type PacketType interface { GetType() string } +type RpcPacketType interface { + GetType() string + GetPacketId() string +} + func ParseJsonPacket(jsonBuf []byte) (PacketType, error) { var bareCmd BarePacketType err := json.Unmarshal(jsonBuf, &bareCmd) diff --git a/pkg/shexec/shexec.go b/pkg/shexec/shexec.go index cd155702..600dd379 100644 --- a/pkg/shexec/shexec.go +++ b/pkg/shexec/shexec.go @@ -17,8 +17,8 @@ import ( "github.com/creack/pty" "github.com/google/uuid" - "github.com/scripthaus-dev/sh2-runner/pkg/base" - "github.com/scripthaus-dev/sh2-runner/pkg/packet" + "github.com/scripthaus-dev/mshell/pkg/base" + "github.com/scripthaus-dev/mshell/pkg/packet" ) const DefaultRows = 25 @@ -79,8 +79,8 @@ func UpdateCmdEnv(cmd *exec.Cmd, envVars map[string]string) { func MakeExecCmd(pk *packet.RunPacketType, cmdTty *os.File) *exec.Cmd { ecmd := exec.Command("bash", "-c", pk.Command) UpdateCmdEnv(ecmd, pk.Env) - if pk.ChDir != "" { - ecmd.Dir = pk.ChDir + if pk.Cwd != "" { + ecmd.Dir = pk.Cwd } ecmd.Stdin = cmdTty ecmd.Stdout = cmdTty @@ -93,11 +93,8 @@ func MakeExecCmd(pk *packet.RunPacketType, cmdTty *os.File) *exec.Cmd { } func MakeRunnerExec(cmdId string) (*exec.Cmd, error) { - runnerPath, err := base.GetScRunnerPath() - if err != nil { - return nil, err - } - ecmd := exec.Command(runnerPath, cmdId) + msPath := base.GetMShellPath() + ecmd := exec.Command(msPath, cmdId) return ecmd, nil } @@ -141,13 +138,13 @@ func ValidateRunPacket(pk *packet.RunPacketType) error { if err != nil { return fmt.Errorf("invalid cmdid '%s' for command", pk.CmdId) } - if pk.ChDir != "" { - dirInfo, err := os.Stat(pk.ChDir) + if pk.Cwd != "" { + dirInfo, err := os.Stat(pk.Cwd) if err != nil { - return fmt.Errorf("invalid cwd '%s' for command: %v", pk.ChDir, err) + return fmt.Errorf("invalid cwd '%s' for command: %v", pk.Cwd, err) } if !dirInfo.IsDir() { - return fmt.Errorf("invalid cwd '%s' for command, not a directory", pk.ChDir) + return fmt.Errorf("invalid cwd '%s' for command, not a directory", pk.Cwd) } } return nil @@ -156,11 +153,11 @@ func ValidateRunPacket(pk *packet.RunPacketType) error { func GetWinsize(p *packet.RunPacketType) *pty.Winsize { rows := DefaultRows cols := DefaultCols - if p.Rows > 0 && p.Rows <= MaxRows { - rows = p.Rows + if p.TermSize.Rows > 0 && p.TermSize.Rows <= MaxRows { + rows = p.TermSize.Rows } - if p.Cols > 0 && p.Cols <= MaxCols { - cols = p.Cols + if p.TermSize.Cols > 0 && p.TermSize.Cols <= MaxCols { + cols = p.TermSize.Cols } return &pty.Winsize{Rows: uint16(rows), Cols: uint16(cols)} }