Kay van Dijk
Wie die poort heeft
Om de paar weken start er iets niet omdat de poort bezet is. Een dev-server, een container, Home Assistant, wat ik ook wilde draaien. Ik weet dat het antwoord lsof of ss is. Ik weet ook dat ik de vlaggen verkeerd heb, ze opzoek, en ze weer vergeet. busy-port is het commando dat ik wilde typen: een poortnummer, en het proces dat hem heeft.
Het is een klein Python-programma. 3.10 of nieuwer, alleen de standaardbibliotheek, dus een checkout kan het draaien zonder een stapel afhankelijkheden: python -m busy_port 8080. Geïnstalleerd heet het commando busy-port 8080, of busy-port --port 8080 als ik expliciet wil zijn. Het standaardprotocol is TCP. --proto udp of --proto both als ik de andere nodig heb. DNS op 53 is meestal waarom ik om UDP vraag.
Wat het print
Als er iets luistert, ziet een tekstregel er zo uit: tcp 8080 LISTEN pid=1234 cmd=python. Dat is het voorbeeld uit de README, met een nep-pid, zodat je de vorm ziet. --json print een lijst objecten met port, proto, state, pid en cmd. Is de poort vrij, dan is de tekst "nothing is listening on tcp port 8080". Met --json en een vrije poort is stdout een lege lijst en gaat dezelfde melding naar stderr, zodat een script "leeg resultaat" kan onderscheiden van "het programma klaagde."
Exitcodes zijn expres saai. 0 betekent minstens één listener. 1 betekent dat er niets luistert. 2 betekent foute argumenten, of dat het opzoeken niet kon draaien. Ik wilde geen programma dat 0 afsluit als het niet heeft gekeken.
Waar de getallen vandaan komen
Op Linux leest het /proc/net/tcp, /proc/net/tcp6, /proc/net/udp en /proc/net/udp6. Het koppelt de socket-inode aan een PID door /proc/*/fd te scannen. Dat werkt zonder root voor processen van jezelf. Ontbreken die bestanden, dan valt het terug op ss -lptn en ss -lpun. Ik lees liever proc dan dat ik ss-vlaggen onthoud, en ik val liever terug dan dat ik doe alsof de poort vrij is.
Op macOS draait het lsof. Voor TCP is dat lsof -nP met de poort en -sTCP:LISTEN. Voor UDP is het lsof -nP op die UDP-poort. -nP zodat het niet namen gaat opzoeken.
Windows is de beperkte, en dat zegt de README. Het draait netstat -ano en, als daar een PID uit komt, tasklist. Het verwacht Engelse netstat-uitvoer. Sommige sockets verbergen de procesnaam nog steeds. Ik heb geen slimme parser gebouwd voor elke gelokaliseerde netstat. Op een Nederlandse Windows kan dit pad de tabel niet lezen. Dat zeg ik liever dan dat ik een gok meestuur.
Het verzint geen PID
Is de socket zichtbaar maar de PID niet, dan gebruikt de regel pid=? en cmd=?. Op Linux betekent dat meestal dat een andere gebruiker het proces heeft. Draai het als root als je hun /proc-regels moet lezen. Ik was in de verleiding om "onbekend proces" te printen alsof dat een antwoord was. Dat is het niet. Een vraagteken is de eerlijke uitvoer.
Dat is het hele programma. Geen daemon, geen config, geen account. Het beantwoordt één vraag die ik steeds aan het verkeerde commando stelde. De repo is github.com/kaygaaf/busy-port. Tests zijn pytest, vanuit een checkout met de dev-extra. Wil je het alleen proberen, dan is python -m busy_port en een poortnummer genoeg.
