github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/os/process.go (about) 1 package os 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "os/exec" 8 "time" 9 ) 10 11 type Process struct { 12 Label string 13 WorkDir string 14 ExecPath string 15 Args []string 16 Pid int 17 StartTime time.Time 18 EndTime time.Time 19 Cmd *exec.Cmd `json:"-"` 20 ExitState *os.ProcessState `json:"-"` 21 Stdin io.Reader `json:"-"` 22 Stdout io.WriteCloser `json:"-"` 23 Stderr io.WriteCloser `json:"-"` 24 WaitCh chan struct{} `json:"-"` 25 } 26 27 // execPath: command name 28 // args: args to command. (should not include name) 29 func StartProcess(label string, dir string, execPath string, args []string, stdin io.Reader, stdout, stderr io.WriteCloser) (*Process, error) { 30 cmd := exec.Command(execPath, args...) 31 cmd.Dir = dir 32 cmd.Stdout = stdout 33 cmd.Stderr = stderr 34 cmd.Stdin = stdin 35 if err := cmd.Start(); err != nil { 36 return nil, err 37 } 38 proc := &Process{ 39 Label: label, 40 WorkDir: dir, 41 ExecPath: execPath, 42 Args: args, 43 Pid: cmd.Process.Pid, 44 StartTime: time.Now(), 45 Cmd: cmd, 46 ExitState: nil, 47 Stdin: stdin, 48 Stdout: stdout, 49 Stderr: stderr, 50 WaitCh: make(chan struct{}), 51 } 52 go func() { 53 err := proc.Cmd.Wait() 54 if err != nil { 55 // fmt.Printf("Process exit: %v\n", err) 56 if exitError, ok := err.(*exec.ExitError); ok { 57 proc.ExitState = exitError.ProcessState 58 } 59 } 60 proc.ExitState = proc.Cmd.ProcessState 61 proc.EndTime = time.Now() // TODO make this goroutine-safe 62 err = proc.Stdout.Close() 63 if err != nil { 64 fmt.Printf("Error closing stdout for %v: %v\n", proc.Label, err) 65 } 66 if proc.Stderr != proc.Stdout { 67 err = proc.Stderr.Close() 68 if err != nil { 69 fmt.Printf("Error closing stderr for %v: %v\n", proc.Label, err) 70 } 71 } 72 close(proc.WaitCh) 73 }() 74 return proc, nil 75 } 76 77 func (proc *Process) StopProcess(kill bool) error { 78 defer func() { 79 proc.Stdout.Close() 80 if proc.Stderr != proc.Stdout { 81 proc.Stderr.Close() 82 } 83 }() 84 if kill { 85 // fmt.Printf("Killing process %v\n", proc.Cmd.Process) 86 return proc.Cmd.Process.Kill() 87 } else { 88 // fmt.Printf("Stopping process %v\n", proc.Cmd.Process) 89 return proc.Cmd.Process.Signal(os.Interrupt) 90 } 91 }