package process import ( "fmt" "io" "os" "os/exec" "time" ) type Process struct { Label string ExecPath string Args []string Pid int StartTime time.Time EndTime time.Time Cmd *exec.Cmd `json:"-"` ExitState *os.ProcessState `json:"-"` InputFile io.Reader `json:"-"` OutputFile io.WriteCloser `json:"-"` WaitCh chan struct{} `json:"-"` } // execPath: command name // args: args to command. (should not include name) func StartProcess(label string, execPath string, args []string, inFile io.Reader, outFile io.WriteCloser) (*Process, error) { cmd := exec.Command(execPath, args...) cmd.Stdout = outFile cmd.Stderr = outFile cmd.Stdin = inFile if err := cmd.Start(); err != nil { return nil, err } proc := &Process{ Label: label, ExecPath: execPath, Args: args, Pid: cmd.Process.Pid, StartTime: time.Now(), Cmd: cmd, ExitState: nil, InputFile: inFile, 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.ExitState = proc.Cmd.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 (proc *Process) StopProcess() 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) } }