nothing will be simple with reaper + os/exec in the same process

This commit is contained in:
Mikaël Cluseau 2018-07-24 20:51:28 +11:00
parent ab9a10fbe3
commit 10e72f18ae
2 changed files with 56 additions and 7 deletions

View File

@ -1,36 +1,41 @@
package main package main
import ( import (
"fmt"
"os" "os"
"os/signal" "os/signal"
"sync"
"syscall" "syscall"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"novit.nc/direktil/pkg/log" "novit.nc/direktil/pkg/log"
) )
var reapLock = sync.RWMutex{}
func handleChildren() { func handleChildren() {
sigchld := make(chan os.Signal, 2048)
signal.Notify(sigchld, syscall.SIGCHLD)
// set us as a sub-reaper // set us as a sub-reaper
if err := unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0); err != nil { 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) 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 { for range sigchld {
reapChildren() reapChildren()
} }
} }
func reapChildren() { func reapChildren() {
reapLock.Lock()
defer reapLock.Unlock()
for { for {
pid, err := syscall.Wait4(-1, nil, syscall.WNOHANG, nil) pid, err := syscall.Wait4(-1, nil, syscall.WNOHANG, nil)
if err != nil { if err != nil && err != syscall.ECHILD {
if err == unix.ECHILD {
break
}
initLog.Taintf(log.Warning, "reaper: wait4 failed: %v", err) initLog.Taintf(log.Warning, "reaper: wait4 failed: %v", err)
fmt.Printf("reaper: wait4 failed: %v\n", err)
break
} }
if pid <= 0 { if pid <= 0 {
break break

View File

@ -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()
}