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
119 行
2.6 KiB
Go
119 行
2.6 KiB
Go
//go:build freebsd
|
|
|
|
package proc
|
|
|
|
import (
|
|
"os/exec"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/pranshuparmar/witr/pkg/model"
|
|
)
|
|
|
|
// GetFileContext returns file descriptor and lock info for a process
|
|
func GetFileContext(pid int) *model.FileContext {
|
|
ctx := &model.FileContext{}
|
|
|
|
// Run fstat once and reuse output for both open file count and lock detection
|
|
fstatOutput, _ := exec.Command("fstat", "-p", strconv.Itoa(pid)).Output()
|
|
|
|
openFiles, fileLimit := getOpenFileCount(fstatOutput, pid)
|
|
ctx.OpenFiles = openFiles
|
|
ctx.FileLimit = fileLimit
|
|
|
|
ctx.LockedFiles = getLockedFiles(fstatOutput)
|
|
|
|
// Only return if we have meaningful data to show
|
|
// Show if: high file usage (>50% of limit) or has locks
|
|
if len(ctx.LockedFiles) > 0 {
|
|
return ctx
|
|
}
|
|
|
|
if ctx.FileLimit > 0 && ctx.OpenFiles > 0 {
|
|
usagePercent := float64(ctx.OpenFiles) / float64(ctx.FileLimit) * 100
|
|
if usagePercent > 50 {
|
|
return ctx
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getOpenFileCount(fstatOut []byte, pid int) (int, int) {
|
|
if len(fstatOut) == 0 {
|
|
return 0, 0
|
|
}
|
|
|
|
openFiles := 0
|
|
for line := range strings.Lines(string(fstatOut)) {
|
|
if strings.TrimSpace(line) != "" {
|
|
openFiles++
|
|
}
|
|
}
|
|
if openFiles > 0 {
|
|
openFiles-- // Subtract header line
|
|
}
|
|
|
|
// Get file limit using sysctl or limits
|
|
fileLimit := getFileLimit(pid)
|
|
|
|
return openFiles, fileLimit
|
|
}
|
|
|
|
// getFileLimit returns the file descriptor limit for a process
|
|
func getFileLimit(pid int) int {
|
|
// Try procstat to get limits
|
|
out, err := exec.Command("procstat", "-l", strconv.Itoa(pid)).Output()
|
|
if err == nil {
|
|
// Parse procstat -l output for openfiles limit
|
|
for line := range strings.Lines(string(out)) {
|
|
if strings.Contains(line, "openfiles") {
|
|
fields := strings.Fields(line)
|
|
if len(fields) >= 3 {
|
|
limit, err := strconv.Atoi(fields[2])
|
|
if err == nil {
|
|
return limit
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback: get system-wide limit
|
|
out, err = exec.Command("sysctl", "-n", "kern.maxfilesperproc").Output()
|
|
if err == nil {
|
|
limit, err := strconv.Atoi(strings.TrimSpace(string(out)))
|
|
if err == nil {
|
|
return limit
|
|
}
|
|
}
|
|
|
|
// Default FreeBSD limit
|
|
return 1024
|
|
}
|
|
|
|
func getLockedFiles(fstatOut []byte) []string {
|
|
var locked []string
|
|
if len(fstatOut) == 0 {
|
|
return locked
|
|
}
|
|
|
|
for line := range strings.Lines(string(fstatOut)) {
|
|
// Look for lock indicators in the output
|
|
if strings.Contains(line, ".lock") ||
|
|
strings.Contains(line, ".pid") ||
|
|
strings.Contains(line, "/lock") {
|
|
fields := strings.Fields(line)
|
|
if len(fields) >= 8 {
|
|
fileName := fields[len(fields)-1]
|
|
if !slices.Contains(locked, fileName) {
|
|
locked = append(locked, fileName)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return locked
|
|
}
|