podman
1//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
2
3package machine4
5import (6"errors"7"fmt"8"syscall"9"time"10
11psutil "github.com/shirou/gopsutil/v3/process"12"github.com/sirupsen/logrus"13"golang.org/x/sys/unix"14)
15
16const (17loops = 818sleepTime = time.Millisecond * 119)
20
21// backoffForProcess checks if the process still exists, for something like
22// sigterm. If the process still exists after loops and sleep time are exhausted,
23// an error is returned
24func backoffForProcess(p *psutil.Process) error {25sleepInterval := sleepTime26for i := 0; i < loops; i++ {27running, err := p.IsRunning()28if err != nil {29// It is possible that while in our loop, the PID vaporize triggering30// an input/output error (#21845)31if errors.Is(err, unix.EIO) {32return nil33}34return fmt.Errorf("checking if process running: %w", err)35}36if !running {37return nil38}39
40time.Sleep(sleepInterval)41// double the time42sleepInterval += sleepInterval43}44return fmt.Errorf("process %d has not ended", p.Pid)45}
46
47// / waitOnProcess takes a pid and sends a sigterm to it. it then waits for the
48// process to not exist. if the sigterm does not end the process after an interval,
49// then sigkill is sent. it also waits for the process to exit after the sigkill too.
50func waitOnProcess(processID int) error {51logrus.Infof("Going to stop gvproxy (PID %d)", processID)52
53p, err := psutil.NewProcess(int32(processID))54if err != nil {55return fmt.Errorf("looking up PID %d: %w", processID, err)56}57
58running, err := p.IsRunning()59if err != nil {60return fmt.Errorf("checking if gvproxy is running: %w", err)61}62if !running {63return nil64}65
66if err := p.Kill(); err != nil {67if errors.Is(err, syscall.ESRCH) {68logrus.Debugf("Gvproxy already dead, exiting cleanly")69return nil70}71return err72}73return backoffForProcess(p)74}
75