71 lines
1.8 KiB
Go
71 lines
1.8 KiB
Go
package pid1
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"os/signal"
|
|
"syscall"
|
|
)
|
|
|
|
// ReRun converts the current process into a bare-bones init, runs the current
|
|
// commandline as a child process, and waits for it to complete. The new child
|
|
// process shares stdin/stdout/stderr with the parent. When the child exits,
|
|
// this will return the same value as exec.Command.Run(). If there is an error
|
|
// in reaping children that this can not handle, it will panic.
|
|
func ReRun() error {
|
|
bin, err := os.Readlink("/proc/self/exe")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd := exec.Command(bin, os.Args[1:]...)
|
|
cmd.Stdin = os.Stdin
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Start(); err != nil {
|
|
return err
|
|
}
|
|
runInit(cmd.Process.Pid)
|
|
return nil
|
|
}
|
|
|
|
// runInit runs a bare-bones init process. This will return when firstborn
|
|
// exits. In case of truly unknown errors it will panic.
|
|
func runInit(firstborn int) {
|
|
sigs := make(chan os.Signal, 8)
|
|
signal.Notify(sigs)
|
|
for sig := range sigs {
|
|
if sig != syscall.SIGCHLD {
|
|
// Pass it on to the real process.
|
|
syscall.Kill(firstborn, sig.(syscall.Signal))
|
|
}
|
|
// Always try to reap a child - empirically, sometimes this gets missed.
|
|
if sigchld(firstborn) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// sigchld handles a SIGCHLD. This will return true when firstborn exits. In
|
|
// case of truly unknown errors it will panic.
|
|
func sigchld(firstborn int) bool {
|
|
// Loop to handle multiple child processes.
|
|
for {
|
|
var status syscall.WaitStatus
|
|
pid, err := syscall.Wait4(-1, &status, syscall.WNOHANG, nil)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("failed to wait4(): %v\n", err))
|
|
}
|
|
|
|
if pid == firstborn {
|
|
return true
|
|
}
|
|
if pid <= 0 {
|
|
// No more children to reap.
|
|
break
|
|
}
|
|
// Must have found one, see if there are more.
|
|
}
|
|
return false
|
|
}
|