pranshuparmar--witr
36b3af2e3d
PR Check / Code Quality: Format (push) Failing after 1s
PR Check / Code Quality: Lint (darwin) (push) Failing after 0s
PR Check / Code Quality: Lint (freebsd) (push) Failing after 1s
PR Check / Code Quality: Lint (windows) (push) Failing after 1s
PR Check / Code Quality: Lint (linux) (push) Failing after 1s
PR Check / Security: Vulnerability Scan (push) Failing after 0s
Update Documentation / update-docs (push) Failing after 2s
PR Check / Code Quality: Vendor (push) Failing after 1s
PR Check / Code Quality: Coverage (push) Failing after 0s
PR Check / Tests: Unit (macos-latest) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04-arm) (push) Has been cancelled
PR Check / Tests: Unit (windows-latest) (push) Has been cancelled
107 行
2.5 KiB
Go
107 行
2.5 KiB
Go
//go:build darwin
|
|
|
|
package proc
|
|
|
|
import (
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/pranshuparmar/witr/pkg/model"
|
|
)
|
|
|
|
// ListAllOpenFiles uses `lsof` to enumerate open files across all processes.
|
|
// Kernel-internal entries (sockets, pipes, kqueue, etc.) are filtered out so
|
|
// the result roughly resembles "files a user would recognize on disk". This
|
|
// can take a noticeable beat on busy systems.
|
|
func ListAllOpenFiles() []*model.LockedFile {
|
|
// lsof may exit non-zero when it can't read one of the processes it
|
|
// scans (permission denied, process exiting mid-scan, etc.) while still
|
|
// emitting valid rows on stdout for the rest; salvage stdout when present.
|
|
out, err := exec.Command("lsof", "-l", "-n", "-P").Output()
|
|
if err != nil && len(out) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var files []*model.LockedFile
|
|
for _, line := range strings.Split(string(out), "\n") {
|
|
fields := strings.Fields(line)
|
|
// COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
|
|
if len(fields) < 9 {
|
|
continue
|
|
}
|
|
fdType := fields[4]
|
|
if !lsofTypeIsFile(fdType) {
|
|
continue
|
|
}
|
|
pid, err := strconv.Atoi(fields[1])
|
|
if err != nil || pid <= 0 {
|
|
continue
|
|
}
|
|
path := strings.Join(fields[8:], " ")
|
|
if !isInterestingDarwinPath(path) {
|
|
continue
|
|
}
|
|
files = append(files, &model.LockedFile{
|
|
PID: pid,
|
|
Process: fields[0],
|
|
Path: path,
|
|
Type: "OPEN",
|
|
Mode: lsofFDMode(fields[3]),
|
|
})
|
|
}
|
|
return files
|
|
}
|
|
|
|
// lsofTypeIsFile keeps regular files and directories; drops sockets, pipes,
|
|
// kqueue, character/block devices, and other kernel-internal handle types.
|
|
func lsofTypeIsFile(t string) bool {
|
|
switch t {
|
|
case "REG", "DIR":
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// isInterestingDarwinPath drops paths that are almost never useful when a
|
|
// user is asking "who has this file open?".
|
|
func isInterestingDarwinPath(p string) bool {
|
|
if p == "" || p == "/dev/null" {
|
|
return false
|
|
}
|
|
if strings.HasPrefix(p, "/dev/tty") || strings.HasPrefix(p, "/dev/ttys") {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// lsofFDMode strips the lock indicators from an lsof FD column and returns
|
|
// R/W/RW based on the access mode character. Returns "" if unrecognized.
|
|
func lsofFDMode(fd string) string {
|
|
if fd == "" {
|
|
return ""
|
|
}
|
|
// Strip trailing lock indicators (W,R,r,w,u,U).
|
|
end := len(fd)
|
|
for end > 0 {
|
|
switch fd[end-1] {
|
|
case 'W', 'R', 'r', 'w', 'u', 'U', 'N', ' ':
|
|
end--
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
if end == 0 {
|
|
return ""
|
|
}
|
|
switch fd[end-1] {
|
|
case 'r':
|
|
return "R"
|
|
case 'w':
|
|
return "W"
|
|
case 'u':
|
|
return "RW"
|
|
}
|
|
return ""
|
|
}
|