docker/docker

View on GitHub
pkg/fileutils/fileutils_linux.go

Summary

Maintainability
A
35 mins
Test Coverage
package fileutils

import (
    "context"
    "fmt"
    "io"
    "os"

    "github.com/containerd/containerd/tracing"
    "github.com/containerd/log"
    "golang.org/x/sys/unix"
)

// GetTotalUsedFds Returns the number of used File Descriptors by
// reading it via /proc filesystem.
func GetTotalUsedFds(ctx context.Context) int {
    ctx, span := tracing.StartSpan(ctx, "GetTotalUsedFds")
    defer span.End()

    name := fmt.Sprintf("/proc/%d/fd", os.Getpid())

    // Fast-path for Linux 6.2 (since [f1f1f2569901ec5b9d425f2e91c09a0e320768f3]).
    // From the [Linux docs]:
    //
    // "The number of open files for the process is stored in 'size' member of
    // stat() output for /proc/<pid>/fd for fast access."
    //
    // [Linux docs]: https://docs.kernel.org/filesystems/proc.html#proc-pid-fd-list-of-symlinks-to-open-files:
    // [f1f1f2569901ec5b9d425f2e91c09a0e320768f3]: https://github.com/torvalds/linux/commit/f1f1f2569901ec5b9d425f2e91c09a0e320768f3
    var stat unix.Stat_t
    if err := unix.Stat(name, &stat); err == nil && stat.Size > 0 {
        return int(stat.Size)
    }

    f, err := os.Open(name)
    if err != nil {
        log.G(ctx).WithError(err).Error("Error listing file descriptors")
        return -1
    }
    defer f.Close()

    var fdCount int
    for {
        select {
        case <-ctx.Done():
            log.G(ctx).WithError(ctx.Err()).Error("Context cancelled while counting file descriptors")
            return -1
        default:
        }

        names, err := f.Readdirnames(100)
        fdCount += len(names)
        if err == io.EOF {
            break
        } else if err != nil {
            log.G(ctx).WithError(err).Error("Error listing file descriptors")
            return -1
        }
    }
    // Note that the slow path has 1 more file-descriptor, due to the open
    // file-handle for /proc/<pid>/fd during the calculation.
    return fdCount
}