MCPcopy
hub / github.com/keploy/keploy / InterruptProcessTree

Function InterruptProcessTree

utils/utils.go:833–947  ·  view source on GitHub ↗

InterruptProcessTree interrupts an entire process tree using the given signal

(logger *zap.Logger, ppid int, sig syscall.Signal)

Source from the content-addressed store, hash-verified

831
832// InterruptProcessTree interrupts an entire process tree using the given signal
833func InterruptProcessTree(logger *zap.Logger, ppid int, sig syscall.Signal) error {
834 // Windows: /proc and process group semantics don't exist. Prefer console ctrl events, fallback to taskkill.
835 if runtime.GOOS == "windows" {
836 logger.Debug("Interrupting process tree on windows", zap.Int("pid", ppid), zap.String("signal", sig.String()))
837
838 // List ALL descendant processes (recursively) before attempting to kill
839 listAllCmd := exec.Command("powershell", "-Command",
840 fmt.Sprintf(`$parent = %d
841$processes = Get-CimInstance Win32_Process
842function Get-ChildProcesses($parentId) {
843 $children = $processes | Where-Object {$_.ParentProcessId -eq $parentId}
844 foreach ($child in $children) {
845 $child
846 Get-ChildProcesses $child.ProcessId
847 }
848}
849Get-ChildProcesses $parent | Select-Object ProcessId, ParentProcessId, Name, CommandLine | Format-List`, ppid))
850 if output, err := listAllCmd.CombinedOutput(); err == nil {
851 logger.Debug("ALL descendant processes before kill", zap.Int("parent_pid", ppid), zap.String("output", string(output)))
852 } else {
853 logger.Debug("Failed to list descendant processes", zap.Int("pid", ppid), zap.Error(err), zap.String("output", string(output)))
854 }
855
856 // Prefer a console Ctrl+Break event so the app can exit cleanly.
857 if sig == syscall.SIGINT {
858 if err := SendSignal(logger, -ppid, sig); err == nil {
859 if err := waitForProcessExit(ppid, 10*time.Second, logger); err != nil {
860 logger.Error("error waiting for process to exit", zap.Int("pid", ppid), zap.Error(err))
861 }
862 if running, err := isProcessRunning(ppid); err == nil && !running {
863 return nil
864 }
865 } else {
866 logger.Debug("failed to send console ctrl event", zap.Int("pid", ppid), zap.Error(err))
867 }
868 }
869
870 // Try graceful taskkill as a fallback (without /F).
871 cmd := exec.Command("taskkill", "/PID", strconv.Itoa(ppid), "/T")
872 if output, err := cmd.CombinedOutput(); err != nil {
873 logger.Debug("taskkill graceful failed", zap.Int("pid", ppid), zap.Error(err), zap.String("output", string(output)))
874 } else {
875 logger.Debug("taskkill graceful succeeded", zap.Int("pid", ppid), zap.String("output", string(output)))
876 }
877
878 // Force kill as the last resort if the process tree is still alive.
879 if running, err := isProcessRunning(ppid); err != nil {
880 logger.Debug("failed to check process state after graceful taskkill", zap.Int("pid", ppid), zap.Error(err))
881 } else if running {
882 forcedCmd := exec.Command("taskkill", "/PID", strconv.Itoa(ppid), "/T", "/F")
883 if output, err := forcedCmd.CombinedOutput(); err != nil {
884 logger.Error("taskkill forced failed", zap.Int("pid", ppid), zap.Error(err), zap.String("output", string(output)))
885 } else {
886 logger.Debug("taskkill forced succeeded", zap.Int("pid", ppid), zap.String("output", string(output)))
887 }
888 }
889
890 // Check for remaining descendant processes after kill

Callers 2

runMethod · 0.92
executeScriptMethod · 0.92

Calls 8

waitForProcessExitFunction · 0.85
isProcessRunningFunction · 0.85
findChildPIDsFunction · 0.85
uniqueProcessGroupsFunction · 0.85
SendSignalFunction · 0.70
DebugMethod · 0.65
StringMethod · 0.45
ErrorMethod · 0.45

Tested by

no test coverage detected