123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
- //
- // This Source Code Form is subject to the terms of the MIT License.
- // If a copy of the MIT was not distributed with this file,
- // You can obtain one at https://github.com/gogf/gf.
- package gproc
- import (
- "context"
- "fmt"
- "os"
- "os/exec"
- "runtime"
- "strings"
- "go.opentelemetry.io/otel"
- "go.opentelemetry.io/otel/propagation"
- "go.opentelemetry.io/otel/trace"
- "github.com/gogf/gf/v2"
- "github.com/gogf/gf/v2/errors/gcode"
- "github.com/gogf/gf/v2/errors/gerror"
- "github.com/gogf/gf/v2/internal/intlog"
- "github.com/gogf/gf/v2/net/gtrace"
- "github.com/gogf/gf/v2/os/genv"
- "github.com/gogf/gf/v2/text/gstr"
- )
- // Process is the struct for a single process.
- type Process struct {
- exec.Cmd
- Manager *Manager
- PPid int
- }
- // NewProcess creates and returns a new Process.
- func NewProcess(path string, args []string, environment ...[]string) *Process {
- env := os.Environ()
- if len(environment) > 0 {
- env = append(env, environment[0]...)
- }
- process := &Process{
- Manager: nil,
- PPid: os.Getpid(),
- Cmd: exec.Cmd{
- Args: []string{path},
- Path: path,
- Stdin: os.Stdin,
- Stdout: os.Stdout,
- Stderr: os.Stderr,
- Env: env,
- ExtraFiles: make([]*os.File, 0),
- },
- }
- process.Dir, _ = os.Getwd()
- if len(args) > 0 {
- // Exclude of current binary path.
- start := 0
- if strings.EqualFold(path, args[0]) {
- start = 1
- }
- process.Args = append(process.Args, args[start:]...)
- }
- return process
- }
- // NewProcessCmd creates and returns a process with given command and optional environment variable array.
- func NewProcessCmd(cmd string, environment ...[]string) *Process {
- return NewProcess(getShell(), append([]string{getShellOption()}, parseCommand(cmd)...), environment...)
- }
- // Start starts executing the process in non-blocking way.
- // It returns the pid if success, or else it returns an error.
- func (p *Process) Start(ctx context.Context) (int, error) {
- if p.Process != nil {
- return p.Pid(), nil
- }
- // OpenTelemetry for command.
- var (
- span trace.Span
- tr = otel.GetTracerProvider().Tracer(
- tracingInstrumentName,
- trace.WithInstrumentationVersion(gf.VERSION),
- )
- )
- ctx, span = tr.Start(
- otel.GetTextMapPropagator().Extract(
- ctx,
- propagation.MapCarrier(genv.Map()),
- ),
- gstr.Join(os.Args, " "),
- trace.WithSpanKind(trace.SpanKindInternal),
- )
- defer span.End()
- span.SetAttributes(gtrace.CommonLabels()...)
- // OpenTelemetry propagation.
- tracingEnv := tracingEnvFromCtx(ctx)
- if len(tracingEnv) > 0 {
- p.Env = append(p.Env, tracingEnv...)
- }
- p.Env = append(p.Env, fmt.Sprintf("%s=%d", envKeyPPid, p.PPid))
- p.Env = genv.Filter(p.Env)
- if err := p.Cmd.Start(); err == nil {
- if p.Manager != nil {
- p.Manager.processes.Set(p.Process.Pid, p)
- }
- return p.Process.Pid, nil
- } else {
- return 0, err
- }
- }
- // Run executes the process in blocking way.
- func (p *Process) Run(ctx context.Context) error {
- if _, err := p.Start(ctx); err == nil {
- return p.Wait()
- } else {
- return err
- }
- }
- // Pid retrieves and returns the PID for the process.
- func (p *Process) Pid() int {
- if p.Process != nil {
- return p.Process.Pid
- }
- return 0
- }
- // Send sends custom data to the process.
- func (p *Process) Send(data []byte) error {
- if p.Process != nil {
- return Send(p.Process.Pid, data)
- }
- return gerror.NewCode(gcode.CodeInvalidParameter, "invalid process")
- }
- // Release releases any resources associated with the Process p,
- // rendering it unusable in the future.
- // Release only needs to be called if Wait is not.
- func (p *Process) Release() error {
- return p.Process.Release()
- }
- // Kill causes the Process to exit immediately.
- func (p *Process) Kill() (err error) {
- err = p.Process.Kill()
- if err != nil {
- err = gerror.Wrapf(err, `kill process failed for pid "%d"`, p.Process.Pid)
- return err
- }
- if p.Manager != nil {
- p.Manager.processes.Remove(p.Pid())
- }
- if runtime.GOOS != "windows" {
- if err = p.Process.Release(); err != nil {
- intlog.Errorf(context.TODO(), `%+v`, err)
- }
- }
- // It ignores this error, just log it.
- _, err = p.Process.Wait()
- intlog.Errorf(context.TODO(), `%+v`, err)
- return nil
- }
- // Signal sends a signal to the Process.
- // Sending Interrupt on Windows is not implemented.
- func (p *Process) Signal(sig os.Signal) error {
- return p.Process.Signal(sig)
- }
|