package starainrt import ( "context" "errors" "io" "os" "os/exec" "syscall" "time" ) type StarShell struct { outfile *io.ReadCloser infile *io.WriteCloser errfile *io.ReadCloser cmd *exec.Cmd running bool stopSign context.Context stopFunc context.CancelFunc stdout []byte errout []byte runerr error exitcode int } func NewStarShell(command string, args ...string) (*StarShell, error) { shell := new(StarShell) shell.stopSign, shell.stopFunc = context.WithCancel(context.Background()) cmd := exec.CommandContext(shell.stopSign, command, args...) shell.cmd = cmd infile, err := shell.cmd.StdinPipe() if err != nil { return shell, err } shell.infile = &infile errfile, err := shell.cmd.StderrPipe() if err != nil { return shell, err } shell.errfile = &errfile outfile, err := shell.cmd.StdoutPipe() if err != nil { return shell, err } shell.outfile = &outfile shell.runerr = nil shell.exitcode = -999 return shell, nil } func (starcli *StarShell) Start() error { if err := starcli.cmd.Start(); err != nil { return err } starcli.running = true go func() { err := starcli.cmd.Wait() if err != nil { starcli.runerr = err } starcli.exitcode = starcli.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() starcli.running = false }() go starcli.queryResult() go starcli.queryErrResult() return nil } func (starcli *StarShell) queryResult() error { for starcli.running { out := make([]byte, 65535) n, err := (*starcli.outfile).Read(out) if n != 0 { for i := 0; i < n; i++ { starcli.stdout = append(starcli.stdout, out[i]) } } if err != nil { if err == io.EOF { break } else { starcli.runerr = err return err } } time.Sleep(time.Microsecond * 100) } return nil } func (starcli *StarShell) queryErrResult() error { for starcli.running { out := make([]byte, 65535) n, err := (*starcli.errfile).Read(out) if n != 0 { for i := 0; i < n; i++ { starcli.errout = append(starcli.errout, out[i]) } } if err != nil { if err == io.EOF { break } else { starcli.runerr = err return err } } time.Sleep(time.Microsecond * 100) } return nil } func (starcli *StarShell) GetResult() (string, error) { np := len(starcli.stdout) res1 := string(starcli.stdout[0:np]) starcli.stdout = starcli.stdout[np:] np = len(starcli.errout) res2 := string(starcli.errout[0:np]) starcli.errout = starcli.errout[np:] if len(res2) == 0 && starcli.runerr != nil { res2 = starcli.runerr.Error() } if len(res2) == 0 { return res1, nil } return res1, errors.New(res2) } func (starcli *StarShell) Exec(cmd string, wait int) (string, error) { (*starcli.infile).Write([]byte(cmd + "\n")) time.Sleep(time.Millisecond * time.Duration(wait)) return starcli.GetResult() } func (starcli *StarShell) WriteCmd(cmdstr string) { (*starcli.infile).Write([]byte(cmdstr + "\n")) return } func (starcli *StarShell) ExitCode() int { return starcli.exitcode } func (starcli *StarShell) Kill() { starcli.stopFunc() starcli.running = false } func (starcli *StarShell) IsRunning() bool { return starcli.running } func (starcli *StarShell) GetPid() int { return starcli.cmd.Process.Pid } func (starcli *StarShell) Signal(sig os.Signal) error { return starcli.cmd.Process.Signal(sig) }