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  }