You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
5.3 KiB
239 lines
5.3 KiB
3 years ago
|
package main
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"os"
|
||
|
"os/exec"
|
||
|
"strconv"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
var deadPids []int
|
||
|
|
||
|
func main() {
|
||
|
|
||
|
deadPids = make([]int, 5)
|
||
|
|
||
|
if len(os.Args) > 1 && os.Args[1] == "child" {
|
||
|
go func() {
|
||
|
i := 0
|
||
|
for {
|
||
|
time.Sleep(time.Millisecond * 500)
|
||
|
fmt.Printf("I am the Child!! i=%d\n", i)
|
||
|
i++
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
sigs := getSignalChannelOSIndependent()
|
||
|
done := make(chan bool, 1)
|
||
|
|
||
|
go func() {
|
||
|
sig := <-sigs
|
||
|
fmt.Printf("child process recieved signal: %s\n", sig)
|
||
|
waitSeconds := 10
|
||
|
if len(os.Args) > 2 {
|
||
|
waitSeconds64, err := strconv.ParseInt(os.Args[2], 10, 64)
|
||
|
if err != nil {
|
||
|
fmt.Printf("cant parse %s (fight_seconds) as integer, defaulting to 10 seconds\n", os.Args[2])
|
||
|
} else {
|
||
|
waitSeconds = int(waitSeconds64)
|
||
|
}
|
||
|
}
|
||
|
fmt.Printf("I AM NOT GOING DOWN WITHOUT A FIGHT! Sleeping for %d seconds before exiting...\n", waitSeconds)
|
||
|
time.Sleep(time.Second * time.Duration(waitSeconds))
|
||
|
done <- true
|
||
|
}()
|
||
|
|
||
|
fmt.Println("child process is running!")
|
||
|
<-done
|
||
|
fmt.Println("child process is exiting")
|
||
|
|
||
|
} else {
|
||
|
|
||
|
executableLocation, err := os.Executable()
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
log.Println(executableLocation)
|
||
|
|
||
|
args := []string{"child"}
|
||
|
if len(os.Args) > 1 {
|
||
|
args = append(args, os.Args[1])
|
||
|
}
|
||
|
command := exec.Command(executableLocation, args...)
|
||
|
|
||
|
command.SysProcAttr = processAttributesOSIndependent
|
||
|
|
||
|
stdoutReader, err := command.StdoutPipe()
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
stdoutScanner := bufio.NewScanner(stdoutReader)
|
||
|
stdoutScanner.Split(bufio.ScanLines)
|
||
|
go func() {
|
||
|
for stdoutScanner.Scan() {
|
||
|
log.Println(stdoutScanner.Text())
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
stderrReader, err := command.StderrPipe()
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
stderrScanner := bufio.NewScanner(stderrReader)
|
||
|
stderrScanner.Split(bufio.ScanLines)
|
||
|
go func() {
|
||
|
for stderrScanner.Scan() {
|
||
|
log.Println(stderrScanner.Text())
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
err = command.Start()
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
go (func() {
|
||
|
err := command.Wait()
|
||
|
|
||
|
if err != nil {
|
||
|
log.Printf(
|
||
|
"command.Wait() returned '%s' for child process '%s %s'\n",
|
||
|
err, "test", "child",
|
||
|
)
|
||
|
}
|
||
|
log.Printf(
|
||
|
"child process '%s %s' ended with exit code %d",
|
||
|
"test", "child", command.ProcessState.ExitCode(),
|
||
|
)
|
||
|
|
||
|
deadPids = append(deadPids[1:], getPidFromCommand(command))
|
||
|
})()
|
||
|
|
||
|
time.Sleep(time.Second)
|
||
|
|
||
|
log.Println()
|
||
|
debugStatus(command)
|
||
|
printStatus(command)
|
||
|
log.Println()
|
||
|
|
||
|
time.Sleep(time.Second)
|
||
|
|
||
|
err = terminateProcessOSIndependent(getPidFromCommand(command), os.Interrupt)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
log.Printf("parent waiting for child to actually die after calling terminateProcessOSIndependent...\n")
|
||
|
for i := 0; i < 5; i++ {
|
||
|
time.Sleep(time.Millisecond * 500)
|
||
|
|
||
|
if isProcessDead(command) {
|
||
|
log.Printf("the child process is dead from the parents POV\n")
|
||
|
break
|
||
|
}
|
||
|
|
||
|
log.Printf(".\n")
|
||
|
}
|
||
|
|
||
|
log.Println()
|
||
|
debugStatus(command)
|
||
|
printStatus(command)
|
||
|
log.Println()
|
||
|
|
||
|
if !isProcessDead(command) {
|
||
|
log.Printf("parent process is fed up, now calling killProcessOSIndependent\n")
|
||
|
|
||
|
err = killProcessOSIndependent(command.Process)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
log.Println()
|
||
|
debugStatus(command)
|
||
|
printStatus(command)
|
||
|
log.Println()
|
||
|
|
||
|
time.Sleep(time.Second)
|
||
|
|
||
|
log.Println()
|
||
|
debugStatus(command)
|
||
|
printStatus(command)
|
||
|
log.Println()
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
func printStatus(command *exec.Cmd) {
|
||
|
|
||
|
currentPid := getPidFromCommand(command)
|
||
|
if currentPid > 1 {
|
||
|
if isProcessDead(command) {
|
||
|
log.Printf("Child process (%d) is no longer running.\n", currentPid)
|
||
|
} else {
|
||
|
log.Printf("Child process (%d) is ALIVE!!!\n", currentPid)
|
||
|
}
|
||
|
} else {
|
||
|
log.Printf("Child process (%d) has a null process id, it must not be started yet.\n", currentPid)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
func isProcessDead(command *exec.Cmd) bool {
|
||
|
if command == nil || command.Process == nil || command.Process.Pid == 0 || command.Process.Pid == -1 {
|
||
|
return true
|
||
|
}
|
||
|
// for _, deadPid := range deadPids {
|
||
|
// if deadPid == pid {
|
||
|
// return true
|
||
|
// }
|
||
|
// }
|
||
|
return command.ProcessState != nil && (command.ProcessState.Exited() || command.ProcessState.ExitCode() == -1)
|
||
|
}
|
||
|
|
||
|
func debugStatus(command *exec.Cmd) {
|
||
|
hasProcess := command.Process != nil
|
||
|
processPid := "nil"
|
||
|
hasProcessState := command.ProcessState != nil
|
||
|
processStateExited := "nil"
|
||
|
processStateExitCode := "nil"
|
||
|
processStatePid := "nil"
|
||
|
osProcessExists := false
|
||
|
if hasProcess {
|
||
|
processPid = fmt.Sprintf("%d", command.Process.Pid)
|
||
|
}
|
||
|
if hasProcessState {
|
||
|
processStateExited = fmt.Sprintf("%t", command.ProcessState.Exited())
|
||
|
processStateExitCode = fmt.Sprintf("%d", command.ProcessState.ExitCode())
|
||
|
processStatePid = fmt.Sprintf("%d", command.ProcessState.Pid())
|
||
|
}
|
||
|
|
||
|
currentPid := getPidFromCommand(command)
|
||
|
if currentPid > 1 {
|
||
|
osProcess, osProcessErr := os.FindProcess(currentPid)
|
||
|
osProcessExists = osProcessErr == nil && osProcess.Pid == currentPid
|
||
|
}
|
||
|
|
||
|
log.Printf(`golang sez: hasProcess: %t, processPid: %s, osProcessExists: %t,
|
||
|
hasProcessState: %t, processStatePid: %s, processStateExited: %s, processStateExitCode: %s`,
|
||
|
hasProcess, processPid, osProcessExists,
|
||
|
hasProcessState, processStatePid, processStateExited, processStateExitCode)
|
||
|
|
||
|
}
|
||
|
|
||
|
func getPidFromCommand(command *exec.Cmd) int {
|
||
|
pid := -1
|
||
|
if command.Process != nil {
|
||
|
pid = command.Process.Pid
|
||
|
}
|
||
|
if pid < 1 && command.ProcessState != nil {
|
||
|
pid = command.ProcessState.Pid()
|
||
|
}
|
||
|
|
||
|
return pid
|
||
|
}
|