nothing will be simple with reaper + os/exec in the same process
This commit is contained in:
		| @ -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 | ||||
							
								
								
									
										44
									
								
								cmd/dkl-system-init/reaper_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								cmd/dkl-system-init/reaper_test.go
									
									
									
									
									
										Normal 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() | ||||
| } | ||||
		Reference in New Issue
	
	Block a user