openfiles

package
v1.0.13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package openfiles answers the one question every reclaimer that deletes from a shared directory must ask first: is a live process using this path?

Three reclaimers ask it — the temp sweep, source-cache eviction and worktree removal — and each had its own lsof parser, which went wrong in the same three ways:

  • PATH SPELLING. lsof reports the path the kernel resolved, so on macOS a file under os.TempDir() (/var/folders/…) is reported as /private/var/folders/…. A string comparison against the unresolved form matched 2 of 266 open files under $TMPDIR on one Mac: the in-use check was off and nothing said so. Snapshot compares canonical paths on BOTH sides.
  • PARTIAL SNAPSHOTS. A timed-out or killed lsof has already written part of its listing. Treating that prefix as the whole machine reports every process it never reached as idle. Take accepts only a listing lsof finished: exit 0, or lsof's routine exit 1 for an inaccessible process.
  • NO TIMEOUT. One caller ran lsof unbounded, so a wedged lsof hung `forge env up`. Take always runs under Timeout.

The /proc source (openfiles_proc.go) avoids the same three by construction: it feeds FromPaths, scans every process before answering or fails, and runs under Timeout.

Every failure is an error, and every caller treats an error as "everything is in use". Without the snapshot, age is the only evidence left, and age cannot tell abandoned from merely quiet.

Index

Constants

View Source
const SourceEnv = "FORGE_OPENFILES_SOURCE"

SourceEnv names the environment variable that, set to "lsof", makes Take use lsof on Linux instead of /proc: an escape hatch for a host whose procfs is restricted in a way lsof is not (and the seam the tests use to drive a fake lsof).

View Source
const Timeout = 2 * time.Minute

Timeout bounds one snapshot. lsof takes seconds on a busy developer machine (~6s measured across ~74k records); minutes means it is wedged on a hung mount, and an answer that never arrives is no better than a wrong one.

Variables

This section is empty.

Functions

This section is empty.

Types

type Snapshot

type Snapshot struct {
	// contains filtered or unexported fields
}

Snapshot is the set of paths live processes held at one instant: open files, working directories, mapped executables. The zero value holds nothing.

func FromPaths

func FromPaths(paths []string) Snapshot

FromPaths builds a snapshot from paths already known to be open. Take uses it for lsof's output; tests use it to state exactly what is open.

func Take

func Take(ctx context.Context) (Snapshot, error)

Take records one machine-wide snapshot: from /proc on Linux, with lsof on other Unixes, lazily on Windows.

Linux reads /proc rather than running lsof because lsof is itself only a reader of /proc there. Going to the source needs no binary (minimal images, containers and sandboxes ship none, and every caller fails closed without a snapshot, so a missing lsof meant nothing was ever reclaimed), no subprocess whose output can be cut short, and no parsing of a second format. macOS has no /proc, so lsof stays the source there.

One snapshot rather than a probe per candidate: a per-path `lsof +D` would race — a path could be opened between its own probe and its removal.

func (Snapshot) Holds

func (s Snapshot) Holds(path string) bool

Holds reports whether a live process holds path itself or anything under it. path is compared in both its given and its canonical spelling, so it matches however the snapshot's paths and the caller's path were reached.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL