portctrl is a CLI tool for inspecting and resolving local TCP port conflicts safely.
It helps you find what is listening, inspect a single port, and free a port.
This project is a work in progress. It is usable for core workflows, and more features are planned.
Current platform support:
- macOS
- Ubuntu / Linux
Planned:
- Windows support
- C++ compiler with C++20 support (
clang++org++) makelsofinPATH
Check with:
command -v lsofIf this prints a path (example: /usr/sbin/lsof), you are set.
xcode-select --install
command -v lsofsudo apt update
sudo apt install -y build-essential lsof
command -v lsofFrom the repository root:
make clean && make
./bin/portctrlRun unit tests:
make test-unitRun integration tests:
make test-integrationRun all tests:
make testInstall globally (default target: /usr/local/bin):
make installDepending on your system permissions, you may need:
sudo make installInstall to a user-local prefix:
make install PREFIX=$HOME/.localUninstall:
make uninstallIf you installed with custom variables, use the same values for uninstall:
make uninstall PREFIX=$HOME/.local
make uninstall PREFIX=/usr/local DESTDIR=/path/to/stageShows which process(es) are listening on one TCP port.
./bin/portctrl who 3000Output is shown in a table for easier scanning.
Lists listening TCP endpoints.
./bin/portctrl listOutput is shown in a table for easier scanning. Note: one PID can appear on multiple rows if one process listens on multiple ports/endpoints.
Safely frees a busy port.
Behavior:
- default is dry-run (no signal sent)
--applysends a graceful signal (SIGTERMby default) to all listener PIDs on that port--signal INTswitches graceful signal toSIGINT- if still busy,
--force(with--apply) can escalate toSIGKILLfor remaining listener PIDs - in non-interactive mode, destructive actions require
--yes - listener sets in
freeare displayed in tables
Examples:
# Dry-run (safe preview)
./bin/portctrl free 3000
# Graceful stop
./bin/portctrl free 3000 --apply
# Graceful stop with SIGINT
./bin/portctrl free 3000 --apply --signal INT
# Escalate to SIGKILL if graceful stop fails
./bin/portctrl free 3000 --apply --force
# Non-interactive usage
./bin/portctrl free 3000 --apply --yes- Valid ports are numeric
1to65535. --forceis only valid with--apply.- Invalid command options return an error and usage output.
command not found: lsof- Install
lsofand verify withcommand -v lsof.
- Install
./bin/portctrl: no such file or directory- Build first with
make clean && make.
- Build first with
kill(...) failed: Operation not permitted- Target process is likely protected or owned by another user.
- Better project-aware suggestions (prefer changing app port when safer)
- Improved conflict workflows
- History/audit features
- Test coverage and packaging improvements
- Windows support
Contributions are welcome. If you are interested, feel free to open an issue or submit a pull request.
MIT