package process import ( "bytes" "fmt" "io" "io/ioutil" "os" "os/exec" "time" . "github.com/tendermint/tendermint/common" ) type Process struct { Label string ExecPath string Args []string Pid int StartTime time.Time EndTime time.Time OutputPath string Cmd *exec.Cmd `json:"-"` ExitState *os.ProcessState `json:"-"` OutputFile *AutoFile `json:"-"` WaitCh chan struct{} `json:"-"` } const ( ProcessModeStd = iota ProcessModeDaemon ) // execPath: command name // args: args to command. (should not include name) func Create(mode int, label string, execPath string, args []string, input string, outPath string) (*Process, error) { outFile, err := OpenAutoFile(outPath) if err != nil { return nil, err } cmd := exec.Command(execPath, args...) switch mode { case ProcessModeStd: cmd.Stdout = io.MultiWriter(os.Stdout, outFile) cmd.Stderr = io.MultiWriter(os.Stderr, outFile) cmd.Stdin = nil case ProcessModeDaemon: cmd.Stdout = outFile cmd.Stderr = outFile cmd.Stdin = nil } if input != "" { cmd.Stdin = bytes.NewReader([]byte(input)) } if err := cmd.Start(); err != nil { return nil, err } proc := &Process{ Label: label, ExecPath: execPath, Args: args, Pid: cmd.Process.Pid, StartTime: time.Now(), OutputPath: outPath, Cmd: cmd, ExitState: nil, OutputFile: outFile, WaitCh: make(chan struct{}), } go func() { err := proc.Cmd.Wait() if err != nil { fmt.Printf("Process exit: %v\n", err) if exitError, ok := err.(*exec.ExitError); ok { proc.ExitState = exitError.ProcessState } } proc.EndTime = time.Now() // TODO make this goroutine-safe err = proc.OutputFile.Close() if err != nil { fmt.Printf("Error closing output file for %v: %v\n", proc.Label, err) } close(proc.WaitCh) }() return proc, nil } func ReadOutput(proc *Process) string { output, err := ioutil.ReadFile(proc.OutputPath) if err != nil { return fmt.Sprintf("ERROR READING OUTPUT: %v", err) } return string(output) } func Stop(proc *Process, kill bool) error { defer proc.OutputFile.Close() if kill { fmt.Printf("Killing process %v\n", proc.Cmd.Process) return proc.Cmd.Process.Kill() } else { fmt.Printf("Stopping process %v\n", proc.Cmd.Process) return proc.Cmd.Process.Signal(os.Interrupt) } }