Files
gf/os/gproc/gproc.go

235 lines
6.0 KiB
Go
Raw Normal View History

2021-01-17 21:46:25 +08:00
// 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 implements management and communication for processes.
package gproc
import (
2019-06-19 09:06:52 +08:00
"bytes"
"io"
"os"
"runtime"
"time"
2021-11-15 20:49:02 +08:00
"github.com/gogf/gf/v2/os/genv"
2021-10-11 21:41:56 +08:00
"github.com/gogf/gf/v2/os/gfile"
2021-11-15 20:49:02 +08:00
"github.com/gogf/gf/v2/text/gstr"
2021-10-11 21:41:56 +08:00
"github.com/gogf/gf/v2/util/gconv"
)
const (
envKeyPPid = "GPROC_PPID"
)
2019-02-20 16:07:11 +08:00
var (
processPid = os.Getpid() // processPid is the pid of current process.
processStartTime = time.Now() // processStartTime is the start time of current process.
2019-02-20 16:07:11 +08:00
)
// Pid returns the pid of current process.
func Pid() int {
return processPid
}
2019-12-14 17:01:27 +08:00
// PPid returns the custom parent pid if exists, or else it returns the system parent pid.
func PPid() int {
2019-06-19 09:06:52 +08:00
if !IsChild() {
return Pid()
}
ppidValue := os.Getenv(envKeyPPid)
2019-06-19 09:06:52 +08:00
if ppidValue != "" && ppidValue != "0" {
return gconv.Int(ppidValue)
}
return PPidOS()
}
2019-12-14 17:01:27 +08:00
// PPidOS returns the system parent pid of current process.
// Note that the difference between PPidOS and PPid function is that the PPidOS returns
// the system ppid, but the PPid functions may return the custom pid by gproc if the custom
// ppid exists.
func PPidOS() int {
2019-06-19 09:06:52 +08:00
return os.Getppid()
}
2019-12-14 17:01:27 +08:00
// IsChild checks and returns whether current process is a child process.
// A child process is forked by another gproc process.
func IsChild() bool {
ppidValue := os.Getenv(envKeyPPid)
2019-06-19 09:06:52 +08:00
return ppidValue != "" && ppidValue != "0"
}
2019-12-14 17:01:27 +08:00
// SetPPid sets custom parent pid for current process.
func SetPPid(ppid int) error {
2019-06-19 09:06:52 +08:00
if ppid > 0 {
return os.Setenv(envKeyPPid, gconv.String(ppid))
2019-06-19 09:06:52 +08:00
} else {
return os.Unsetenv(envKeyPPid)
2019-06-19 09:06:52 +08:00
}
}
2019-12-14 17:01:27 +08:00
// StartTime returns the start time of current process.
func StartTime() time.Time {
2019-06-19 09:06:52 +08:00
return processStartTime
}
2019-12-14 17:01:27 +08:00
// Uptime returns the duration which current process has been running
func Uptime() time.Duration {
return time.Now().Sub(processStartTime)
}
// Shell executes command `cmd` synchronously with given input pipe `in` and output pipe `out`.
// The command `cmd` reads the input parameters from input pipe `in`, and writes its output automatically
// to output pipe `out`.
2018-08-24 17:11:50 +08:00
func Shell(cmd string, out io.Writer, in io.Reader) error {
p := NewProcess(
getShell(),
append([]string{getShellOption()}, parseCommand(cmd)...),
)
2019-06-19 09:06:52 +08:00
p.Stdin = in
p.Stdout = out
return p.Run()
2018-08-24 17:11:50 +08:00
}
// ShellRun executes given command `cmd` synchronously and outputs the command result to the stdout.
2018-08-24 17:11:50 +08:00
func ShellRun(cmd string) error {
p := NewProcess(
getShell(),
append([]string{getShellOption()}, parseCommand(cmd)...),
)
2019-06-19 09:06:52 +08:00
return p.Run()
2018-08-24 17:11:50 +08:00
}
// ShellExec executes given command `cmd` synchronously and returns the command result.
2021-12-14 21:01:36 +08:00
func ShellExec(cmd string, environment ...[]string) (result string, err error) {
var (
buf = bytes.NewBuffer(nil)
p = NewProcess(
getShell(),
append([]string{getShellOption()}, parseCommand(cmd)...),
environment...,
)
)
2019-06-19 09:06:52 +08:00
p.Stdout = buf
p.Stderr = buf
2021-12-14 21:01:36 +08:00
err = p.Run()
result = buf.String()
return
2018-08-24 17:11:50 +08:00
}
// parseCommand parses command `cmd` into slice arguments.
2019-12-14 17:01:27 +08:00
//
// Note that it just parses the `cmd` for "cmd.exe" binary in windows, but it is not necessary
// parsing the `cmd` for other systems using "bash"/"sh" binary.
2019-12-14 17:01:27 +08:00
func parseCommand(cmd string) (args []string) {
if runtime.GOOS != "windows" {
return []string{cmd}
}
// Just for "cmd.exe" in windows.
var argStr string
var firstChar, prevChar, lastChar1, lastChar2 byte
array := gstr.SplitAndTrim(cmd, " ")
for _, v := range array {
if len(argStr) > 0 {
argStr += " "
}
firstChar = v[0]
lastChar1 = v[len(v)-1]
lastChar2 = 0
if len(v) > 1 {
lastChar2 = v[len(v)-2]
}
if prevChar == 0 && (firstChar == '"' || firstChar == '\'') {
// It should remove the first quote char.
argStr += v[1:]
prevChar = firstChar
} else if prevChar != 0 && lastChar2 != '\\' && lastChar1 == prevChar {
// It should remove the last quote char.
argStr += v[:len(v)-1]
args = append(args, argStr)
argStr = ""
prevChar = 0
} else if len(argStr) > 0 {
argStr += v
} else {
args = append(args, v)
2019-06-19 09:06:52 +08:00
}
}
2019-12-14 17:01:27 +08:00
return
}
// getShell returns the shell command depending on current working operating system.
2019-12-14 17:01:27 +08:00
// It returns "cmd.exe" for windows, and "bash" or "sh" for others.
2018-08-24 17:11:50 +08:00
func getShell() string {
2019-06-19 09:06:52 +08:00
switch runtime.GOOS {
case "windows":
return SearchBinary("cmd.exe")
2019-06-19 09:06:52 +08:00
default:
// Check the default binary storage path.
if gfile.Exists("/bin/bash") {
return "/bin/bash"
}
if gfile.Exists("/bin/sh") {
return "/bin/sh"
}
// Else search the env PATH.
path := SearchBinary("bash")
2019-06-19 09:06:52 +08:00
if path == "" {
path = SearchBinary("sh")
2019-06-19 09:06:52 +08:00
}
return path
}
2018-08-24 17:11:50 +08:00
}
// getShellOption returns the shell option depending on current working operating system.
2019-12-14 17:01:27 +08:00
// It returns "/c" for windows, and "-c" for others.
2018-08-24 17:11:50 +08:00
func getShellOption() string {
2019-06-19 09:06:52 +08:00
switch runtime.GOOS {
case "windows":
return "/c"
default:
return "-c"
}
2018-08-24 17:11:50 +08:00
}
// SearchBinary searches the binary `file` in current working folder and PATH environment.
func SearchBinary(file string) string {
// Check if it is absolute path of exists at current working directory.
2019-06-19 09:06:52 +08:00
if gfile.Exists(file) {
return file
}
2020-01-18 22:21:37 +08:00
return SearchBinaryPath(file)
}
// SearchBinaryPath searches the binary `file` in PATH environment.
2020-01-18 22:21:37 +08:00
func SearchBinaryPath(file string) string {
2019-06-19 09:06:52 +08:00
array := ([]string)(nil)
switch runtime.GOOS {
case "windows":
envPath := genv.Get("PATH", genv.Get("Path")).String()
2020-01-09 23:05:03 +08:00
if gstr.Contains(envPath, ";") {
array = gstr.SplitAndTrim(envPath, ";")
} else if gstr.Contains(envPath, ":") {
array = gstr.SplitAndTrim(envPath, ":")
}
2019-06-19 09:06:52 +08:00
if gfile.Ext(file) != ".exe" {
file += ".exe"
}
2019-06-19 09:06:52 +08:00
default:
array = gstr.SplitAndTrim(genv.Get("PATH").String(), ":")
2019-06-19 09:06:52 +08:00
}
if len(array) > 0 {
path := ""
2019-06-19 09:06:52 +08:00
for _, v := range array {
path = v + gfile.Separator + file
2020-01-09 23:05:03 +08:00
if gfile.Exists(path) && gfile.IsFile(path) {
2019-06-19 09:06:52 +08:00
return path
}
}
}
return ""
2018-08-24 17:11:50 +08:00
}