github.com/wangyougui/gf/v2@v2.6.5/os/gproc/gproc_process.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/wangyougui/gf. 6 7 package gproc 8 9 import ( 10 "context" 11 "fmt" 12 "os" 13 "os/exec" 14 "runtime" 15 "strings" 16 17 "go.opentelemetry.io/otel" 18 "go.opentelemetry.io/otel/propagation" 19 "go.opentelemetry.io/otel/trace" 20 21 "github.com/wangyougui/gf/v2" 22 "github.com/wangyougui/gf/v2/errors/gcode" 23 "github.com/wangyougui/gf/v2/errors/gerror" 24 "github.com/wangyougui/gf/v2/internal/intlog" 25 "github.com/wangyougui/gf/v2/net/gtrace" 26 "github.com/wangyougui/gf/v2/os/genv" 27 "github.com/wangyougui/gf/v2/text/gstr" 28 ) 29 30 // Process is the struct for a single process. 31 type Process struct { 32 exec.Cmd 33 Manager *Manager 34 PPid int 35 } 36 37 // NewProcess creates and returns a new Process. 38 func NewProcess(path string, args []string, environment ...[]string) *Process { 39 env := os.Environ() 40 if len(environment) > 0 { 41 env = append(env, environment[0]...) 42 } 43 process := &Process{ 44 Manager: nil, 45 PPid: os.Getpid(), 46 Cmd: exec.Cmd{ 47 Args: []string{path}, 48 Path: path, 49 Stdin: os.Stdin, 50 Stdout: os.Stdout, 51 Stderr: os.Stderr, 52 Env: env, 53 ExtraFiles: make([]*os.File, 0), 54 }, 55 } 56 process.Dir, _ = os.Getwd() 57 if len(args) > 0 { 58 // Exclude of current binary path. 59 start := 0 60 if strings.EqualFold(path, args[0]) { 61 start = 1 62 } 63 process.Args = append(process.Args, args[start:]...) 64 } 65 return process 66 } 67 68 // NewProcessCmd creates and returns a process with given command and optional environment variable array. 69 func NewProcessCmd(cmd string, environment ...[]string) *Process { 70 return NewProcess(getShell(), append([]string{getShellOption()}, parseCommand(cmd)...), environment...) 71 } 72 73 // Start starts executing the process in non-blocking way. 74 // It returns the pid if success, or else it returns an error. 75 func (p *Process) Start(ctx context.Context) (int, error) { 76 if p.Process != nil { 77 return p.Pid(), nil 78 } 79 // OpenTelemetry for command. 80 var ( 81 span trace.Span 82 tr = otel.GetTracerProvider().Tracer( 83 tracingInstrumentName, 84 trace.WithInstrumentationVersion(gf.VERSION), 85 ) 86 ) 87 ctx, span = tr.Start( 88 otel.GetTextMapPropagator().Extract( 89 ctx, 90 propagation.MapCarrier(genv.Map()), 91 ), 92 gstr.Join(os.Args, " "), 93 trace.WithSpanKind(trace.SpanKindInternal), 94 ) 95 defer span.End() 96 span.SetAttributes(gtrace.CommonLabels()...) 97 98 // OpenTelemetry propagation. 99 tracingEnv := tracingEnvFromCtx(ctx) 100 if len(tracingEnv) > 0 { 101 p.Env = append(p.Env, tracingEnv...) 102 } 103 p.Env = append(p.Env, fmt.Sprintf("%s=%d", envKeyPPid, p.PPid)) 104 p.Env = genv.Filter(p.Env) 105 106 if err := p.Cmd.Start(); err == nil { 107 if p.Manager != nil { 108 p.Manager.processes.Set(p.Process.Pid, p) 109 } 110 return p.Process.Pid, nil 111 } else { 112 return 0, err 113 } 114 } 115 116 // Run executes the process in blocking way. 117 func (p *Process) Run(ctx context.Context) error { 118 if _, err := p.Start(ctx); err == nil { 119 return p.Wait() 120 } else { 121 return err 122 } 123 } 124 125 // Pid retrieves and returns the PID for the process. 126 func (p *Process) Pid() int { 127 if p.Process != nil { 128 return p.Process.Pid 129 } 130 return 0 131 } 132 133 // Send sends custom data to the process. 134 func (p *Process) Send(data []byte) error { 135 if p.Process != nil { 136 return Send(p.Process.Pid, data) 137 } 138 return gerror.NewCode(gcode.CodeInvalidParameter, "invalid process") 139 } 140 141 // Release releases any resources associated with the Process p, 142 // rendering it unusable in the future. 143 // Release only needs to be called if Wait is not. 144 func (p *Process) Release() error { 145 return p.Process.Release() 146 } 147 148 // Kill causes the Process to exit immediately. 149 func (p *Process) Kill() (err error) { 150 err = p.Process.Kill() 151 if err != nil { 152 err = gerror.Wrapf(err, `kill process failed for pid "%d"`, p.Process.Pid) 153 return err 154 } 155 if p.Manager != nil { 156 p.Manager.processes.Remove(p.Pid()) 157 } 158 if runtime.GOOS != "windows" { 159 if err = p.Process.Release(); err != nil { 160 intlog.Errorf(context.TODO(), `%+v`, err) 161 } 162 } 163 // It ignores this error, just log it. 164 _, err = p.Process.Wait() 165 intlog.Errorf(context.TODO(), `%+v`, err) 166 return nil 167 } 168 169 // Signal sends a signal to the Process. 170 // Sending Interrupt on Windows is not implemented. 171 func (p *Process) Signal(sig os.Signal) error { 172 return p.Process.Signal(sig) 173 }