diff --git a/cmd/dkl-system-init/dumb-init-bridge.go b/cmd/dkl-system-init/reaper.go similarity index 77% rename from cmd/dkl-system-init/dumb-init-bridge.go rename to cmd/dkl-system-init/reaper.go index 1a7651e..42850bb 100644 --- a/cmd/dkl-system-init/dumb-init-bridge.go +++ b/cmd/dkl-system-init/reaper.go @@ -1,36 +1,41 @@ package main import ( + "fmt" "os" "os/signal" + "sync" "syscall" "golang.org/x/sys/unix" "novit.nc/direktil/pkg/log" ) +var reapLock = sync.RWMutex{} + func handleChildren() { + sigchld := make(chan os.Signal, 2048) + signal.Notify(sigchld, syscall.SIGCHLD) + // set us as a sub-reaper if err := unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0); err != nil { initLog.Taintf(log.Error, "reaper: failed to set myself a child sub-reaper: %v", err) } - sigchld := make(chan os.Signal, 2048) - signal.Notify(sigchld, syscall.SIGCHLD) - for range sigchld { reapChildren() } } func reapChildren() { + reapLock.Lock() + defer reapLock.Unlock() for { pid, err := syscall.Wait4(-1, nil, syscall.WNOHANG, nil) - if err != nil { - if err == unix.ECHILD { - break - } + if err != nil && err != syscall.ECHILD { initLog.Taintf(log.Warning, "reaper: wait4 failed: %v", err) + fmt.Printf("reaper: wait4 failed: %v\n", err) + break } if pid <= 0 { break diff --git a/cmd/dkl-system-init/reaper_test.go b/cmd/dkl-system-init/reaper_test.go new file mode 100644 index 0000000..c83e54d --- /dev/null +++ b/cmd/dkl-system-init/reaper_test.go @@ -0,0 +1,44 @@ +package main + +import ( + "os" + "os/exec" + "sync" + "testing" +) + +func TestReap(t *testing.T) { + truePath, err := exec.LookPath("true") + if err != nil { + t.Log("true binary not found, ignoring this test.") + return + } + + go handleChildren() + + count := 1000 + + wg := &sync.WaitGroup{} + wg.Add(count) + for i := 0; i < count; i++ { + i := i + go func() { + cmd := exec.Command(truePath) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + if err := cmd.Run(); err != nil { + t.Errorf("[%d] %v", i, err) + } + wg.Done() + }() + } + + wg.Wait() + + cmd := exec.Command("sh", "-c", "ps aux |grep Z") + cmd.Stdout = os.Stdout + cmd.Run() + + t.Fail() +}