From dfd6a516400b59aebe425b18cc481e23e8eb1575 Mon Sep 17 00:00:00 2001 From: Martin Magr Date: Mon, 9 Aug 2021 22:45:04 +0200 Subject: [PATCH] Add system.GetOpenedFiles Enables programatically find out about opened files from a given process. --- system/proc.go | 32 ++++++++++++++++++++++++++------ tests/system_test.go | 29 +++++++++++++++++++++++------ 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/system/proc.go b/system/proc.go index 271410a..0d05f82 100644 --- a/system/proc.go +++ b/system/proc.go @@ -11,18 +11,22 @@ import ( // Modifiable constants var ( - ProcLimitColumns = 4 - ProcLimitPathFmt = "/proc/%d/limits" - splitRex = regexp.MustCompile(" +") + ProcLimitColumns = 4 + ProcLimitPathFmt = "/proc/%d/limits" + ProcOpenedFilesFmt = "/proc/%d/fd" + splitRex = regexp.MustCompile(" +") ) -// GetProcLimits returns limits for the given process. Use -1 for actual process. -func GetProcLimits(PID int) (map[string]map[string]interface{}, error) { +func getProcPath(pathFmt string, PID int) string { if PID == -1 { PID = os.Getpid() } + return fmt.Sprintf(pathFmt, PID) +} - data, err := ioutil.ReadFile(fmt.Sprintf(ProcLimitPathFmt, PID)) +// GetProcLimits returns limits for the given process. Use -1 for actual process. +func GetProcLimits(PID int) (map[string]map[string]interface{}, error) { + data, err := ioutil.ReadFile(getProcPath(ProcLimitPathFmt, PID)) if err != nil { return nil, err } @@ -56,3 +60,19 @@ func GetProcLimits(PID int) (map[string]map[string]interface{}, error) { return out, nil } + +// GetOpenedFiles returns count of opened files by the given process. Use -1 for actual process. +func GetOpenedFiles(PID int) (int, error) { + dir, err := os.Open(getProcPath(ProcOpenedFilesFmt, PID)) + if err != nil { + return -1, err + } + + files, err := dir.Readdir(-1) + dir.Close() + if err != nil { + return -1, err + } + + return len(files), nil +} diff --git a/tests/system_test.go b/tests/system_test.go index fd687a9..4d85c18 100644 --- a/tests/system_test.go +++ b/tests/system_test.go @@ -1,6 +1,7 @@ package tests import ( + "fmt" "io/ioutil" "os" "path" @@ -34,13 +35,13 @@ func TestProcLimits(t *testing.T) { require.NoError(t, err) defer os.RemoveAll(tmpdir) - // save test content - file, err := os.Create(path.Join(tmpdir, "0_limits")) - require.NoError(t, err) - file.WriteString(procLimitData) - require.NoError(t, file.Close()) - t.Run("Test parsed limit file", func(t *testing.T) { + // save test content + file, err := os.Create(path.Join(tmpdir, "0_limits")) + require.NoError(t, err) + file.WriteString(procLimitData) + require.NoError(t, file.Close()) + system.ProcLimitPathFmt = path.Join(tmpdir, "%d_limits") expected := map[string]map[string]interface{}{ @@ -131,4 +132,20 @@ func TestProcLimits(t *testing.T) { assert.Equal(t, expected, parsed, "Did not parse correctly") }) + t.Run("Test opened files from process", func(t *testing.T) { + fdir := path.Join(tmpdir, "0") + err := os.Mkdir(fdir, 0755) + require.NoError(t, err) + + for i := 0; i < 10; i++ { + file, err := os.Create(path.Join(fdir, fmt.Sprintf("socket%d", i))) + require.NoError(t, err) + require.NoError(t, file.Close()) + } + + system.ProcOpenedFilesFmt = path.Join(tmpdir, "%d") + fcount, err := system.GetOpenedFiles(0) + require.NoError(t, err) + assert.Equal(t, 10, fcount) + }) }