mirror of
https://github.com/siderolabs/talos.git
synced 2025-08-18 04:27:06 +02:00
Most of the fixes were automatically applied. Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
158 lines
2.6 KiB
Go
158 lines
2.6 KiB
Go
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package reaper
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"sync"
|
|
"syscall"
|
|
)
|
|
|
|
type zombieHunter struct {
|
|
mu sync.Mutex
|
|
|
|
running bool
|
|
listeners map[chan<- ProcessInfo]struct{}
|
|
ready chan struct{}
|
|
shutdown chan struct{}
|
|
}
|
|
|
|
func (zh *zombieHunter) Run() {
|
|
zh.mu.Lock()
|
|
defer zh.mu.Unlock()
|
|
|
|
if zh.running {
|
|
panic("zombie hunter is already running")
|
|
}
|
|
|
|
zh.running = true
|
|
|
|
zh.ready = make(chan struct{})
|
|
zh.shutdown = make(chan struct{})
|
|
zh.listeners = make(map[chan<- ProcessInfo]struct{})
|
|
|
|
go zh.run()
|
|
|
|
<-zh.ready
|
|
}
|
|
|
|
func (zh *zombieHunter) Shutdown() {
|
|
zh.mu.Lock()
|
|
running := zh.running
|
|
zh.mu.Unlock()
|
|
|
|
if !running {
|
|
return
|
|
}
|
|
|
|
zh.shutdown <- struct{}{}
|
|
<-zh.shutdown
|
|
}
|
|
|
|
func (zh *zombieHunter) Notify(ch chan<- ProcessInfo) bool {
|
|
zh.mu.Lock()
|
|
defer zh.mu.Unlock()
|
|
|
|
if !zh.running {
|
|
return false
|
|
}
|
|
|
|
zh.listeners[ch] = struct{}{}
|
|
|
|
return true
|
|
}
|
|
|
|
func (zh *zombieHunter) Stop(ch chan<- ProcessInfo) {
|
|
zh.mu.Lock()
|
|
defer zh.mu.Unlock()
|
|
|
|
delete(zh.listeners, ch)
|
|
}
|
|
|
|
func (zh *zombieHunter) run() {
|
|
sigCh := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigCh, syscall.SIGCHLD)
|
|
defer signal.Stop(sigCh)
|
|
|
|
zh.ready <- struct{}{}
|
|
|
|
for {
|
|
// wait for SIGCHLD
|
|
select {
|
|
case <-sigCh:
|
|
case <-zh.shutdown:
|
|
zh.mu.Lock()
|
|
zh.running = false
|
|
zh.mu.Unlock()
|
|
|
|
zh.shutdown <- struct{}{}
|
|
|
|
return
|
|
}
|
|
|
|
// reap all the zombies
|
|
zh.reapLoop()
|
|
}
|
|
}
|
|
|
|
// reapLoop processes all the known zombies at the moment.
|
|
func (zh *zombieHunter) reapLoop() {
|
|
for {
|
|
var (
|
|
wstatus syscall.WaitStatus
|
|
pid int
|
|
err error
|
|
)
|
|
|
|
for {
|
|
// retry EINTR on wait4()
|
|
pid, err = syscall.Wait4(-1, &wstatus, syscall.WNOHANG, nil)
|
|
if err != syscall.EINTR {
|
|
break
|
|
}
|
|
}
|
|
|
|
if err == syscall.ECHILD || pid == 0 {
|
|
// no more zombies
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
log.Printf("zombie reaper error in wait4: %s", err)
|
|
|
|
return
|
|
}
|
|
|
|
zh.send(pid, wstatus)
|
|
}
|
|
}
|
|
|
|
// send notification about reaped zombie to all listeners.
|
|
func (zh *zombieHunter) send(pid int, wstatus syscall.WaitStatus) {
|
|
zh.mu.Lock()
|
|
|
|
listeners := make([]chan<- ProcessInfo, 0, len(zh.listeners))
|
|
for ch := range zh.listeners {
|
|
listeners = append(listeners, ch)
|
|
}
|
|
|
|
zh.mu.Unlock()
|
|
|
|
notification := ProcessInfo{
|
|
Pid: pid,
|
|
Status: wstatus,
|
|
}
|
|
|
|
for _, listener := range listeners {
|
|
select {
|
|
case listener <- notification:
|
|
default: // drop notifications if listener is not keeping up
|
|
}
|
|
}
|
|
}
|