gf/os/gproc/gproc_process.go

140 lines
3.4 KiB
Go
Raw Normal View History

2021-01-17 21:46:25 +08:00
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
2018-05-08 18:41:29 +08:00
//
// 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.
2018-05-08 18:41:29 +08:00
package gproc
2018-05-08 18:41:29 +08:00
import (
"context"
2019-06-19 09:06:52 +08:00
"fmt"
"os"
"os/exec"
2020-01-10 22:32:07 +08:00
"runtime"
2019-06-19 09:06:52 +08:00
"strings"
2021-11-15 20:49:02 +08:00
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/intlog"
2018-05-08 18:41:29 +08:00
)
2019-12-14 17:01:27 +08:00
// Process is the struct for a single process.
type Process struct {
2019-06-19 09:06:52 +08:00
exec.Cmd
2019-12-14 17:01:27 +08:00
Manager *Manager
PPid int
}
2019-12-14 17:01:27 +08:00
// NewProcess creates and returns a new Process.
2019-06-19 09:06:52 +08:00
func NewProcess(path string, args []string, environment ...[]string) *Process {
2020-01-19 20:42:21 +08:00
env := os.Environ()
2019-06-19 09:06:52 +08:00
if len(environment) > 0 {
env = append(env, environment[0]...)
2019-06-19 09:06:52 +08:00
}
process := &Process{
2019-06-19 09:06:52 +08:00
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()
2019-06-19 09:06:52 +08:00
if len(args) > 0 {
// Exclude of current binary path.
2019-06-19 09:06:52 +08:00
start := 0
if strings.EqualFold(path, args[0]) {
start = 1
}
process.Args = append(process.Args, args[start:]...)
2019-06-19 09:06:52 +08:00
}
return process
}
// NewProcessCmd creates and returns a process with given command and optional environment variable array.
func NewProcessCmd(cmd string, environment ...[]string) *Process {
2019-12-14 17:01:27 +08:00
return NewProcess(getShell(), append([]string{getShellOption()}, parseCommand(cmd)...), environment...)
}
2019-12-14 17:01:27 +08:00
// 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() (int, error) {
2019-06-19 09:06:52 +08:00
if p.Process != nil {
return p.Pid(), nil
}
p.Env = append(p.Env, fmt.Sprintf("%s=%d", envKeyPPid, p.PPid))
2019-06-19 09:06:52 +08:00
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
}
2018-05-08 18:41:29 +08:00
}
2019-12-14 17:01:27 +08:00
// Run executes the process in blocking way.
func (p *Process) Run() error {
2019-06-19 09:06:52 +08:00
if _, err := p.Start(); err == nil {
return p.Wait()
} else {
return err
}
2018-05-08 18:41:29 +08:00
}
// Pid retrieves and returns the PID for the process.
2018-05-08 18:41:29 +08:00
func (p *Process) Pid() int {
2019-06-19 09:06:52 +08:00
if p.Process != nil {
return p.Process.Pid
}
return 0
2018-05-08 18:41:29 +08:00
}
// Send sends custom data to the process.
func (p *Process) Send(data []byte) error {
2019-06-19 09:06:52 +08:00
if p.Process != nil {
return Send(p.Process.Pid, data)
}
return gerror.NewCode(gcode.CodeInvalidParameter, "invalid process")
}
2018-05-08 18:41:29 +08:00
// 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 {
2019-06-19 09:06:52 +08:00
return p.Process.Release()
2018-05-08 18:41:29 +08:00
}
// 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)
2019-06-19 09:06:52 +08:00
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
2018-05-08 18:41:29 +08:00
}
// Signal sends a signal to the Process.
// Sending Interrupt on Windows is not implemented.
func (p *Process) Signal(sig os.Signal) error {
2019-06-19 09:06:52 +08:00
return p.Process.Signal(sig)
}