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  }