forked from rancher-sandbox/rancher-desktop
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rdctl: factory-reset: Terminate extension processes
This attempts to terminate extension process (as in, processes where the executable are in the extension directory) when we run `rdctl factory-reset`. This is needed because we don't gracefully shut down Rancher Desktop when we do a factory reset. Signed-off-by: Mark Yen <mark.yen@suse.com>
- Loading branch information
Showing
7 changed files
with
217 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package process | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"slices" | ||
"strings" | ||
|
||
"github.com/sirupsen/logrus" | ||
"golang.org/x/sys/unix" | ||
) | ||
|
||
const ( | ||
CTL_KERN = "kern" | ||
KERN_PROCARGS = 38 | ||
) | ||
|
||
// TerminateProcessInDirectory terminates all processes where the executable | ||
// resides within the given directory, as gracefully as possible. | ||
func TerminateProcessInDirectory(ctx context.Context, directory string) error { | ||
procs, err := unix.SysctlKinfoProcSlice("kern.proc.all") | ||
if err != nil { | ||
return fmt.Errorf("failed to list processes: %w", err) | ||
} | ||
for _, proc := range procs { | ||
pid := int(proc.Proc.P_pid) | ||
buf, err := unix.SysctlRaw(CTL_KERN, KERN_PROCARGS, pid) | ||
if err != nil { | ||
if !errors.Is(err, unix.EINVAL) { | ||
logrus.Infof("Failed to get command line of pid %d: %s", pid, err) | ||
} | ||
continue | ||
} | ||
// The buffer starts with a null-terminated executable path, plus | ||
// command line arguments and things. | ||
index := slices.Index(buf, 0) | ||
if index < 0 { | ||
// If we have unexpected data, don't fall over. | ||
continue | ||
} | ||
procPath := string(buf[:index]) | ||
relPath, err := filepath.Rel(directory, procPath) | ||
if err != nil || strings.HasPrefix(relPath, "../") { | ||
continue | ||
} | ||
process, err := os.FindProcess(pid) | ||
if err != nil { | ||
continue | ||
} | ||
err = process.Signal(unix.SIGTERM) | ||
if err == nil { | ||
logrus.Infof("Terminated process %d (%s)", pid, procPath) | ||
} else if !errors.Is(err, unix.EINVAL) { | ||
logrus.Infof("Ignoring failure to terminate pid %d (%s): %s", pid, procPath, err) | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package process | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/sirupsen/logrus" | ||
"golang.org/x/sys/unix" | ||
) | ||
|
||
// TerminateProcessInDirectory terminates all processes where the executable | ||
// resides within the given directory, as gracefully as possible. | ||
func TerminateProcessInDirectory(ctx context.Context, directory string) error { | ||
// Check /proc/<pid>/exe to see if they're the correct file. | ||
pidfds, err := os.ReadDir("/proc") | ||
if err != nil { | ||
return fmt.Errorf("error listing processes: %w", err) | ||
} | ||
for _, pidfd := range pidfds { | ||
if !pidfd.IsDir() { | ||
continue | ||
} | ||
pid, err := strconv.Atoi(pidfd.Name()) | ||
if err != nil { | ||
continue | ||
} | ||
procPath, err := os.Readlink(filepath.Join("/proc", pidfd.Name(), "exe")) | ||
if err != nil { | ||
continue | ||
} | ||
relPath, err := filepath.Rel(directory, procPath) | ||
if err != nil || strings.HasPrefix(relPath, "../") { | ||
continue | ||
} | ||
proc, err := os.FindProcess(pid) | ||
if err != nil { | ||
continue | ||
} | ||
err = proc.Signal(unix.SIGTERM) | ||
if err == nil { | ||
logrus.Infof("Terminated process %d", pid) | ||
} else if !errors.Is(err, unix.EINVAL) { | ||
logrus.Infof("Ignoring failure to terminate pid %d: %s", pid, err) | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package process | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"path/filepath" | ||
"strings" | ||
"unsafe" | ||
|
||
"github.com/sirupsen/logrus" | ||
"golang.org/x/sys/windows" | ||
) | ||
|
||
// TerminateProcessInDirectory terminates all processes where the executable | ||
// resides within the given directory, as gracefully as possible. | ||
func TerminateProcessInDirectory(ctx context.Context, directory string) error { | ||
pids := make([]uint32, 4096) | ||
// Try EnumProcesses until the number of pids returned is less than the | ||
// buffer size. | ||
for { | ||
var bytesReturned uint32 | ||
err := windows.EnumProcesses(pids, &bytesReturned) | ||
if err != nil || len(pids) < 1 { | ||
return fmt.Errorf("failed to enumerate processes: %w", err) | ||
} | ||
pidsReturned := uintptr(bytesReturned) / unsafe.Sizeof(pids[0]) | ||
if pidsReturned < uintptr(len(pids)) { | ||
// Remember to truncate the pids to only the valid set. | ||
pids = pids[:pidsReturned] | ||
break | ||
} | ||
pids = make([]uint32, len(pids)*2) | ||
} | ||
|
||
for _, pid := range pids { | ||
// Do each iteration in a function so defer statements run faster. | ||
err := (func() error { | ||
hProc, err := windows.OpenProcess( | ||
windows.PROCESS_QUERY_LIMITED_INFORMATION|windows.PROCESS_TERMINATE, | ||
false, | ||
pid) | ||
if err != nil { | ||
logrus.Infof("Ignoring error opening process %d: %s", pid, err) | ||
return nil | ||
} | ||
defer func() { | ||
_ = windows.CloseHandle(hProc) | ||
}() | ||
|
||
nameBuf := make([]uint16, 1024) | ||
for { | ||
bufSize := uint32(len(nameBuf)) | ||
err = windows.QueryFullProcessImageName(hProc, 0, &nameBuf[0], &bufSize) | ||
if err != nil { | ||
return fmt.Errorf("error getting process %d executable: %w", pid, err) | ||
} | ||
if int(bufSize) < len(nameBuf) { | ||
break | ||
} | ||
nameBuf = make([]uint16, len(nameBuf)*2) | ||
} | ||
executablePath := windows.UTF16ToString(nameBuf) | ||
|
||
relPath, err := filepath.Rel(directory, executablePath) | ||
if err != nil || strings.HasPrefix(relPath, "../") { | ||
return nil | ||
} | ||
|
||
if err = windows.TerminateProcess(hProc, 0); err != nil { | ||
return fmt.Errorf("failed to terminate pid %d (%s): %w", pid, executablePath, err) | ||
} | ||
|
||
return nil | ||
})() | ||
if err != nil { | ||
logrus.Errorf("%s", err) | ||
} | ||
} | ||
|
||
return nil | ||
} |