-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
gproc.go
234 lines (213 loc) · 5.96 KB
/
gproc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// 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 (
"bytes"
"io"
"os"
"runtime"
"time"
"github.com/gogf/gf/v2/os/genv"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
)
const (
envKeyPPid = "GPROC_PPID"
)
var (
processPid = os.Getpid() // processPid is the pid of current process.
processStartTime = time.Now() // processStartTime is the start time of current process.
)
// Pid returns the pid of current process.
func Pid() int {
return processPid
}
// PPid returns the custom parent pid if exists, or else it returns the system parent pid.
func PPid() int {
if !IsChild() {
return Pid()
}
ppidValue := os.Getenv(envKeyPPid)
if ppidValue != "" && ppidValue != "0" {
return gconv.Int(ppidValue)
}
return PPidOS()
}
// 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 {
return os.Getppid()
}
// 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)
return ppidValue != "" && ppidValue != "0"
}
// SetPPid sets custom parent pid for current process.
func SetPPid(ppid int) error {
if ppid > 0 {
return os.Setenv(envKeyPPid, gconv.String(ppid))
} else {
return os.Unsetenv(envKeyPPid)
}
}
// StartTime returns the start time of current process.
func StartTime() time.Time {
return processStartTime
}
// 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`.
func Shell(cmd string, out io.Writer, in io.Reader) error {
p := NewProcess(
getShell(),
append([]string{getShellOption()}, parseCommand(cmd)...),
)
p.Stdin = in
p.Stdout = out
return p.Run()
}
// ShellRun executes given command `cmd` synchronously and outputs the command result to the stdout.
func ShellRun(cmd string) error {
p := NewProcess(
getShell(),
append([]string{getShellOption()}, parseCommand(cmd)...),
)
return p.Run()
}
// ShellExec executes given command `cmd` synchronously and returns the command result.
func ShellExec(cmd string, environment ...[]string) (result string, err error) {
var (
buf = bytes.NewBuffer(nil)
p = NewProcess(
getShell(),
append([]string{getShellOption()}, parseCommand(cmd)...),
environment...,
)
)
p.Stdout = buf
p.Stderr = buf
err = p.Run()
result = buf.String()
return
}
// parseCommand parses command `cmd` into slice arguments.
//
// 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.
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)
}
}
return
}
// getShell returns the shell command depending on current working operating system.
// It returns "cmd.exe" for windows, and "bash" or "sh" for others.
func getShell() string {
switch runtime.GOOS {
case "windows":
return SearchBinary("cmd.exe")
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")
if path == "" {
path = SearchBinary("sh")
}
return path
}
}
// getShellOption returns the shell option depending on current working operating system.
// It returns "/c" for windows, and "-c" for others.
func getShellOption() string {
switch runtime.GOOS {
case "windows":
return "/c"
default:
return "-c"
}
}
// 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.
if gfile.Exists(file) {
return file
}
return SearchBinaryPath(file)
}
// SearchBinaryPath searches the binary `file` in PATH environment.
func SearchBinaryPath(file string) string {
array := ([]string)(nil)
switch runtime.GOOS {
case "windows":
envPath := genv.Get("PATH", genv.Get("Path")).String()
if gstr.Contains(envPath, ";") {
array = gstr.SplitAndTrim(envPath, ";")
} else if gstr.Contains(envPath, ":") {
array = gstr.SplitAndTrim(envPath, ":")
}
if gfile.Ext(file) != ".exe" {
file += ".exe"
}
default:
array = gstr.SplitAndTrim(genv.Get("PATH").String(), ":")
}
if len(array) > 0 {
path := ""
for _, v := range array {
path = v + gfile.Separator + file
if gfile.Exists(path) && gfile.IsFile(path) {
return path
}
}
}
return ""
}