Skip to content

Recipes

Scope: the everyday set, each one a question in the words it's actually asked, the command, and the captured answer it produces. Every recipe was run in the lab; the chapter numbers point at the full captures.

"Something is on this port and my service can't start"

$ lsof -iTCP:8734 -sTCP:LISTEN
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
python3 22428 root    3u  IPv4 857701      0t0  TCP localhost:8734 (LISTEN)

Chapter 5. The pid is the negotiation: stop it, reconfigure it, or pick another port — but identify it first with ps (the Watching Your System guide) before killing anything; a listener is often a service someone else is using. Add -iUDP:8734 if the squatter might be a UDP socket — the two protocols have separate namespaces and both can hold "the same" port number.

"The disk is full but du can't find it"

$ lsof +L1
COMMAND   PID USER   FD   TYPE DEVICE  SIZE/OFF NLINK    NODE NAME
python3 22406 root    3r   REG   0,86 209715200     0 7484178 /home/hatch/workspace/lsof-lab/tree/big/big.bin (deleted)

Chapter 4, the centerpiece as a one-liner. Deleted-but-open files, sizes in the SIZE/OFF column, biggest first by eye. The fix is the holder closing or restarting — and the proof afterwards is df giving the space back, not lsof going quiet (though in the lab it did both).

"This directory won't delete / unmount — it's busy"

Two different holds, two questions. Who is standing in it (cwd) or holding the directory itself:

$ lsof /home/hatch/workspace/lsof-lab/tree/sub
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME
sleep   22375 root  cwd    DIR   0,86       16 7484148 /home/hatch/workspace/lsof-lab/tree/sub

And who holds anything under it:

$ lsof +D /home/hatch/workspace/lsof-lab/tree

(three rows in chapter 3 — the tail, the holder, the sleeper). Chapter 3 has the cost warning: aim +D at the smallest tree that covers the question. A forgotten shell sitting in a directory with its cwd is the usual culprit, and the fix is cd out, not a kill.

"What is this daemon actually holding?"

$ lsof -p 22347

Chapter 2's table: files, sockets, cwd, executable, libraries — the process's whole open world in one listing. Skim discipline: the mem rows are almost never the story; the numbered descriptors are. Cut them with a -d exclusion list (each item negated with ^, the chapter 6 grammar), captured against a planted holder:

$ lsof -a -p 25152 -d ^mem,^cwd,^rtd,^txt
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME
python3 25152 root    0r   CHR    1,3      0t0       4 /dev/null
python3 25152 root    1w   CHR    1,3      0t0       4 /dev/null
python3 25152 root    2w   REG   0,86       70 7490068 /home/hatch/workspace/lsof-lab/transcripts/s12-slicing-noise.txt
python3 25152 root    3r   REG   0,86        9 7484200 /home/hatch/workspace/lsof-lab/tree/script.txt
python3 25152 root    4u   REG   0,86       10 7484150 /home/hatch/workspace/lsof-lab/tree/both.txt
[exit 0]

What survives is exactly what the process opened for itself: the standard streams and its two planted files.

"Which process writes this log?"

$ lsof /home/hatch/workspace/lsof-lab/tree/notes.log
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME
tail    22329 root    3r   REG   0,86       23 7484136 /home/hatch/workspace/lsof-lab/tree/notes.log

Chapter 1's move, with the FD letter doing the talking: 3r is a reader (here a tail -f watching the log). A writer wears w or u — in chapter 2's planted set, 4w on write.txt. If two processes hold the log, one reading and one writing, the letters separate them at a glance.

"A connection to that host — whose is it?"

$ lsof -i :8734

Chapter 5's four-row table is the pattern: listener, the accepted server-side socket, and the client's row, the NAMEs mirrored (8734->59048, 59048->8734). On a real machine, lsof -i @dbhost or a grep of lsof -i output for the far address finds the local end; the pid column names the owner.

"Show me just the interesting part, repeatedly"

$ lsof -r 2 -i :8734

Chapter 7: snapshots every 2 seconds with ======= between them — for the connection that exists only while the cron job runs, or the holder that appears at the moment of failure. Bound it with timeout when sampling for a record.