github.com/gogf/gf/v2@v2.7.4/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/gogf/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/gogf/gf/v2" 22 "github.com/gogf/gf/v2/errors/gcode" 23 "github.com/gogf/gf/v2/errors/gerror" 24 "github.com/gogf/gf/v2/internal/intlog" 25 "github.com/gogf/gf/v2/net/gtrace" 26 "github.com/gogf/gf/v2/os/genv" 27 "github.com/gogf/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 // On Windows, this works and doesn't work on other platforms 107 if runtime.GOOS == "windows" { 108 joinProcessArgs(p) 109 } 110 111 if err := p.Cmd.Start(); err == nil { 112 if p.Manager != nil { 113 p.Manager.processes.Set(p.Process.Pid, p) 114 } 115 return p.Process.Pid, nil 116 } else { 117 return 0, err 118 } 119 } 120 121 // Run executes the process in blocking way. 122 func (p *Process) Run(ctx context.Context) error { 123 if _, err := p.Start(ctx); err == nil { 124 return p.Wait() 125 } else { 126 return err 127 } 128 } 129 130 // Pid retrieves and returns the PID for the process. 131 func (p *Process) Pid() int { 132 if p.Process != nil { 133 return p.Process.Pid 134 } 135 return 0 136 } 137 138 // Send sends custom data to the process. 139 func (p *Process) Send(data []byte) error { 140 if p.Process != nil { 141 return Send(p.Process.Pid, data) 142 } 143 return gerror.NewCode(gcode.CodeInvalidParameter, "invalid process") 144 } 145 146 // Release releases any resources associated with the Process p, 147 // rendering it unusable in the future. 148 // Release only needs to be called if Wait is not. 149 func (p *Process) Release() error { 150 return p.Process.Release() 151 } 152 153 // Kill causes the Process to exit immediately. 154 func (p *Process) Kill() (err error) { 155 err = p.Process.Kill() 156 if err != nil { 157 err = gerror.Wrapf(err, `kill process failed for pid "%d"`, p.Process.Pid) 158 return err 159 } 160 if p.Manager != nil { 161 p.Manager.processes.Remove(p.Pid()) 162 } 163 if runtime.GOOS != "windows" { 164 if err = p.Process.Release(); err != nil { 165 intlog.Errorf(context.TODO(), `%+v`, err) 166 } 167 } 168 // It ignores this error, just log it. 169 _, err = p.Process.Wait() 170 intlog.Errorf(context.TODO(), `%+v`, err) 171 return nil 172 } 173 174 // Signal sends a signal to the Process. 175 // Sending Interrupt on Windows is not implemented. 176 func (p *Process) Signal(sig os.Signal) error { 177 return p.Process.Signal(sig) 178 }