package master
|
|
import (
|
"analysis/logo"
|
"os"
|
"os/signal"
|
"syscall"
|
)
|
|
// ExitInfo info
|
type ExitInfo struct {
|
Pid int
|
ExitCode int
|
}
|
|
// Config config
|
type Config struct {
|
Pid int
|
Options int
|
DisablePid1Check bool
|
}
|
|
// Handle death of child (SIGCHLD) messages. Pushes the signal onto the
|
// notifications channel if there is a waiter.
|
func sigChildHandler(notifications chan os.Signal) {
|
sigs := make(chan os.Signal, 3)
|
signal.Notify(sigs, syscall.SIGCHLD)
|
signal.Ignore(syscall.SIGPIPE)
|
|
for {
|
sig := <-sigs
|
select {
|
case notifications <- sig: /* published it. */
|
default:
|
/*
|
* Notifications channel full - drop it to the
|
* floor. This ensures we don't fill up the SIGCHLD
|
* queue. The reaper just waits for any child
|
* process (pid=-1), so we ain't loosing it!! ;^)
|
*/
|
}
|
}
|
|
} /* End of function sigChildHandler. */
|
|
// Be a good parent - clean up behind the children.
|
func reapChildren(config Config, info chan<- ExitInfo) {
|
notifications := make(chan os.Signal, 1)
|
|
go sigChildHandler(notifications)
|
|
pid := config.Pid
|
opts := config.Options
|
|
for {
|
sig := <-notifications
|
logo.Infof(" - Received signal %v\n", sig)
|
for {
|
var wstatus syscall.WaitStatus
|
|
/*
|
* Reap 'em, so that zombies don't accumulate.
|
* Plants vs. Zombies!!
|
*/
|
pid, err := syscall.Wait4(pid, &wstatus, opts, nil)
|
info <- ExitInfo{pid, wstatus.ExitStatus()}
|
for syscall.EINTR == err {
|
pid, err = syscall.Wait4(pid, &wstatus, opts, nil)
|
info <- ExitInfo{pid, wstatus.ExitStatus()}
|
}
|
|
if syscall.ECHILD == err {
|
break
|
}
|
|
logo.Infof(" - Grim reaper cleanup: pid=%d, wstatus=%+v\n",
|
pid, wstatus)
|
|
}
|
}
|
|
} /* End of function reapChildren. */
|
|
/*
|
* ======================================================================
|
* Section: Exported functions
|
* ======================================================================
|
*/
|
|
// Normal entry point for the reaper code. Start reaping children in the
|
// background inside a goroutine.
|
|
// Reap reap
|
func Reap(info chan<- ExitInfo) {
|
/*
|
* Only reap processes if we are taking over init's duties aka
|
* we are running as pid 1 inside a docker container. The default
|
* is to reap all processes.
|
*/
|
Start(Config{
|
Pid: -1,
|
Options: 0,
|
DisablePid1Check: true,
|
}, info)
|
|
} /* End of [exported] function Reap. */
|
|
// Entry point for invoking the reaper code with a specific configuration.
|
// The config allows you to bypass the pid 1 checks, so handle with care.
|
// The child processes are reaped in the background inside a goroutine.
|
|
// Start start
|
func Start(config Config, info chan<- ExitInfo) {
|
/*
|
* Start the Reaper with configuration options. This allows you to
|
* reap processes even if the current pid isn't running as pid 1.
|
* So ... use with caution!!
|
*
|
* In most cases, you are better off just using Reap() as that
|
* checks if we are running as Pid 1.
|
*/
|
if !config.DisablePid1Check {
|
mypid := os.Getpid()
|
if 1 != mypid {
|
logo.Errorln(" - Grim reaper disabled, pid not 1\n")
|
return
|
}
|
}
|
|
/*
|
* Ok, so either pid 1 checks are disabled or we are the grandma
|
* of 'em all, either way we get to play the grim reaper.
|
* You will be missed, Terry Pratchett!! RIP
|
*/
|
go reapChildren(config, info)
|
|
} /* End of [exported] function Start. */
|