github.com/sandwich-go/boost@v1.3.29/xproc/process.go (about) 1 package xproc 2 3 import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "runtime" 8 "strings" 9 10 "github.com/sandwich-go/boost/internal/log" 11 "github.com/sandwich-go/boost/xos" 12 "github.com/sandwich-go/boost/xslice" 13 ) 14 15 const envKeyPPid = "GO_PROC_PPID" 16 17 type Process struct { 18 cc *ProcessOptions 19 exec.Cmd 20 Manager *Manager 21 PPid int 22 } 23 24 // NewProcess creates and returns a new Process. 25 func NewProcessWithOptions(path string, cc *ProcessOptions) *Process { 26 process := &Process{ 27 cc: cc, 28 Manager: nil, 29 PPid: os.Getpid(), 30 Cmd: exec.Cmd{ 31 Args: []string{path}, 32 Path: path, 33 Stdin: cc.Stdin, 34 Stdout: cc.Stdout, 35 Stderr: cc.Stderr, 36 Env: append(os.Environ(), cc.Env...), 37 Dir: cc.WorkingDir, 38 ExtraFiles: make([]*os.File, 0), // not support on windows 39 }, 40 } 41 if len(cc.Args) > 0 { 42 start := 0 43 if strings.EqualFold(path, cc.Args[0]) { 44 start = 1 45 } 46 process.Args = append(process.Args, cc.Args[start:]...) 47 } 48 return process 49 } 50 51 // NewProcess creates and returns a new Process. 52 func NewProcess(path string, opt ...ProcessOption) *Process { 53 return NewProcessWithOptions(path, NewProcessOptions(opt...)) 54 } 55 56 // NewProcessShellCmdWithOptions creates and returns a process with given command and optional environment variable array. 57 func NewProcessShellCmdWithOptions(cmd string, cc *ProcessOptions) *Process { 58 argsLen := len(cc.Args) 59 cc.Args = xslice.StringsSetAdd(parseCommand(cmd), cc.Args...) 60 if argsLen == 0 { 61 cc.Args = xslice.StringsSetAdd([]string{xos.GetShellOption()}, cc.Args...) 62 } 63 return NewProcessWithOptions(xos.GetShell(), cc) 64 } 65 66 // Start starts executing the process in non-blocking way. 67 // It returns the pid if success, or else it returns an error. 68 func (p *Process) Start() (int, error) { 69 if p.Process != nil { 70 return p.Pid(), nil 71 } 72 p.Env = append(p.Env, fmt.Sprintf("%s=%d", envKeyPPid, p.PPid)) 73 if err := p.Cmd.Start(); err == nil { 74 if p.Manager != nil { 75 p.Manager.processes.Store(p.Process.Pid, p) 76 } 77 return p.Process.Pid, nil 78 } else { 79 return 0, err 80 } 81 } 82 83 // Run executes the process in blocking way. 84 func (p *Process) Run() error { 85 if _, err := p.Start(); err == nil { 86 return p.Wait() 87 } else { 88 return err 89 } 90 } 91 92 func (p *Process) Pid() int { 93 if p.Process != nil { 94 return p.Process.Pid 95 } 96 return 0 97 } 98 99 func (p *Process) Release() error { 100 return p.Process.Release() 101 } 102 103 // Kill causes the Process to exit immediately. 104 func (p *Process) Kill() (err error) { 105 if err = p.Process.Kill(); err != nil { 106 return fmt.Errorf("Kill got err:%w", err) 107 } 108 if p.Manager != nil { 109 p.Manager.processes.Delete(p.Pid()) 110 } 111 if runtime.GOOS != "windows" { 112 if err = p.Process.Release(); err != nil { 113 return fmt.Errorf("Release got err:%w", err) 114 } 115 } 116 // ignores this error, just log it. 117 _, err = p.Process.Wait() 118 if err != nil { 119 log.Error(fmt.Sprintf("Wait got err:%s", err.Error())) 120 } 121 return nil 122 } 123 124 // Signal sends a signal to the Process. 125 // Sending Interrupt on Windows is not implemented. 126 func (p *Process) Signal(sig os.Signal) error { 127 return p.Process.Signal(sig) 128 }