A comprehensive CLI utility to identify and manage processes listening on network ports. Written in Go with rich terminal UI, detailed process information, and zero external dependencies.
- Rich Interactive Display - Colorized, emoji-enhanced UI with detailed process metrics
- Docker Container Detection - Automatically detects and displays Docker containers using ports
- Docker-Specific Actions - Stop or remove containers instead of killing processes
- Multiple Output Modes - Interactive (default), JSON, or information-only modes
- Comprehensive Process Data - Combines
lsofoutput with Linux/procfilesystem for deep insights - Process Management - Kill processes with interactive prompt or direct signal support
- Cross-Platform - Runs on Linux and macOS with platform-specific optimizations
- Zero Dependencies - Uses only Go standard library (plus Unicode width calculation libs)
- Unix/Linux system with
lsofcommand - Go 1.23+ (for building from source)
- On Linux: Enhanced features via
/procfilesystem - On macOS: Basic features via
lsof(graceful degradation without/proc)
If you have Go installed:
go install github.com/bluehoodie/whoseport/cmd/whoseport@latestThis installs the binary to $GOBIN (or $GOPATH/bin, or $HOME/go/bin by default).
Clone the repository and install:
git clone https://github.com/bluehoodie/whoseport.git
cd whoseport
make installOr build locally for testing:
make build
./whoseport 8080whoseport 8080Default: Interactive Mode Shows detailed process information with an interactive prompt to kill the process.
whoseport 8080Information-Only Mode (no prompt)
whoseport -n 8080
whoseport --no-interactive 8080JSON Output (for scripting)
whoseport --json 8080Kill with Interactive Prompt (default behavior)
whoseport 8080
# Prompts: Kill process? (y/N)
# Allows choosing signal: SIGTERM, SIGKILL, SIGINT, etc.Terminate Gracefully (SIGTERM - allows cleanup)
whoseport -t 8080
whoseport --term 8080Force Kill (SIGKILL - immediate termination)
whoseport -k 8080
whoseport --kill 8080| Flag | Shorthand | Description |
|---|---|---|
--kill |
-k |
Force kill the process immediately (SIGKILL) without prompting |
--term |
-t |
Gracefully terminate the process (SIGTERM) without prompting |
--no-interactive |
-n |
Show process info only, no interactive prompt |
--json |
Output in JSON format for scripting |
Note: SIGTERM allows processes to clean up resources and exit gracefully, while SIGKILL forcefully terminates the process immediately without cleanup.
╔════════════════════════════════════════════════════════════════════════════════════════╗
║ 🔍 PORT 8080 ANALYSIS ║
╚════════════════════════════════════════════════════════════════════════════════════════╝
┏━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ⚙️ PROCESS IDENTITY ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━┛
Command: node
Full Command: node server.js --port 8080
Process ID: 🔢 42573
Parent PID: 41234
Parent Process: bash
User: colin
UID / GID: 501 / 20
Child Processes: 3
┏━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ 📦 BINARY INFORMATION ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━┛
Executable Path: /usr/local/bin/node
Binary Size: 47.3 MB
Environment Vars: 42
Working Directory: /home/colin/projects
┏━━━━━━━━━━━━━━━━━━━┓
┃ 📊 PROCESS STATE ┃
┗━━━━━━━━━━━━━━━━━━━┛
State: 🟢 S (Sleeping)
Threads: 12
Started: 2025-11-01 10:23:15
Uptime: ⏱ 2h 34m
CPU Time: 45.23s (2.5%)
┏━━━━━━━━━━━━━━━━━┓
┃ 💾 MEMORY USAGE ┃
┗━━━━━━━━━━━━━━━━━┛
Resident Set (RSS): 124.5 MB
[██████████████████████████████░░]
Virtual Memory: 2.3 GB
[███████████████░░░░░░░░░░░░░░░░░]
┏━━━━━━━━━━━━━━━━━━━┓
┃ 📁 FILE DESCRIPTORS┃
┗━━━━━━━━━━━━━━━━━━━┛
Open FDs: 23 / 1024 (2.2%)
[██░░░░░░░░░░░░░░░░░░░░░░░░░░░░]
┏━━━━━━━━━━━━━┓
┃ 🌐 NETWORK ┃
┗━━━━━━━━━━━━━┛
Protocol: IPV4
Listening On: 🎧 *:8080 (LISTEN)
Node Type: TCP
Total Connections: 5
▸ TCP Connections
┃ 192.168.1.100:45678 → 192.168.1.50:8080 ESTABLISHED
┃ 192.168.1.101:45679 → 192.168.1.50:8080 ESTABLISHED
$ whoseport --json 8080{
"command": "node",
"id": 42573,
"user": "colin",
"fd": "7u",
"type": "IPv4",
"device": "0x5e4b104643390241",
"size_offset": "0t0",
"node": "TCP",
"name": "*:8080 (LISTEN)",
"full_command": "node server.js --port 8080",
"ppid": 41234,
"parent_command": "bash",
"state": "S",
"threads": 12,
"working_dir": "/home/colin/projects",
"memory_rss_kb": 127488,
"memory_vms_kb": 2457600,
"cpu_time_seconds": 45.23,
"start_time": "2025-11-01 10:23:15",
"uptime": "2h 34m",
"open_fds": 23,
"max_fds": 1024,
"uid": 501,
"gid": 20,
"network_connections": 5,
"tcp_connections": [
"192.168.1.100:45678 → 192.168.1.50:8080 ESTABLISHED",
"192.168.1.101:45679 → 192.168.1.50:8080 ESTABLISHED"
],
"exe_path": "/usr/local/bin/node",
"exe_size_bytes": 49643520,
"io_read_bytes": 1048576,
"io_write_bytes": 524288
}When whoseport detects that a port is being used by a Docker container, it automatically switches to a Docker-specific display and action mode.
The tool automatically detects Docker containers in three ways:
- docker-proxy processes - Detects port forwarding proxies created by Docker
- Container cgroup - Checks if a process is running inside a container via
/proc/{pid}/cgroup - Container environment - Examines process environment variables for Docker indicators
When a Docker container is detected, you'll see a specialized display with container-specific information:
╔══════════════════════════════════════════════════════════════════════════════════════════╗
║ 🐳 PORT 8080 → DOCKER CONTAINER ║
╚══════════════════════════════════════════════════════════════════════════════════════════╝
▌ 🐳 CONTAINER IDENTITY
Container Name: 📦 my-web-app
Container ID: abc123def456
Full ID: abc123def456789012345678901234567890123456789012
State: ✅ RUNNING
Running For: ⏱ 2 hours 15 minutes
▌ 📀 IMAGE
Image: nginx:latest
Image ID: sha256:abcd1234
Platform: linux/amd64
Command: ⚙️ nginx -g daemon off;
▌ 🌐 NETWORK & PORTS
Port Mappings:
*:8080:80 👉 TCP (← matched port)
*:8443:443 TCP
IP Address: 🔗 172.17.0.2
Gateway: 172.17.0.1
Networks: bridge
▌ 📊 RESOURCE USAGE
CPU Usage: ⚡ 2.5%
Memory Usage: 💾 45.2MiB / 512MiB (8.8%)
Network I/O: 📡 1.2MB / 856KB
Block I/O: 💿 4.5MB / 0B
PIDs: 12
▌ ⚙️ CONFIGURATION
Restart Policy: always
Mounts: 📂 2 volume(s)
┃ /var/lib/docker/volumes/web-data → /usr/share/nginx/html (volume, rw)
┃ /home/user/config/nginx.conf → /etc/nginx/nginx.conf (bind, ro)
Labels: 🏷 3 label(s)
┃ com.docker.compose.project=myapp
┃ com.docker.compose.service=web
┃ version=1.0.0
▌ 🔧 UNDERLYING PROCESS
Process ID: 12345
Process Command: docker-proxy
Note: The process above is the Docker proxy/container process.
Actions below will affect the container, not just the process.
When interacting with a Docker container, you have different action options:
Interactive Mode (default)
whoseport 8080You'll be prompted with Docker-specific options:
🐳 Container my-web-app (abc123def456) - Select action:
[1] Stop container (docker stop)
[2] Stop and remove container (docker stop + docker rm)
[3] Force remove running container (docker rm -f)
[4] Cancel
Choice [4]:
Direct Actions
-
Stop container (equivalent to
-tflag)whoseport -t 8080 # Stops the Docker container gracefully -
Stop and remove container (equivalent to
-kflag)whoseport -k 8080 # Stops and removes the Docker container
Docker container detection requires:
- Docker daemon running and accessible
dockerCLI command available in PATH- Sufficient permissions to run
dockercommands
If Docker is not available or detection fails, whoseport gracefully falls back to showing regular process information.
Built following SOLID principles with clear separation of concerns:
cmd/whoseport- Main entry point with CLI flag parsinginternal/process- Process retrieval usinglsof(executor, parser, retriever)internal/procfs-/procfilesystem parsing for enhanced data (Linux-specific)internal/docker- Docker container detection, information retrieval, and actionsinternal/model- CoreProcessInfodata structure (40+ fields)internal/display- Output formatters:interactive- Rich UI for regular processesdocker- Docker-specific UI with container detailsjson- Structured JSON output
internal/action- Process actions (killer, prompter)internal/terminal- Terminal theme and color constantsinternal/testutil- Test fixtures and mocks
Interface-driven design enables easy testing and extension.
- Command name, PID, User, File Descriptor
- Protocol type (TCP/UDP), Network type (IPv4/IPv6)
- Port and listening address
- Process Details: Full command line, parent process, state, threads
- Memory: RSS, VMS, memory limits
- CPU: CPU time, usage percentage, uptime
- I/O: Read/write bytes and syscall counts
- Network: Active TCP/UDP connections with addresses
- Files: Open file descriptor count and limits
- Binary: Executable path, size, working directory
- Identity: UID, GID, groups, nice value, priority
# Build for local testing
make build
# Install to $GOBIN
make install
# Run tests
make test
# Clean build artifacts
make cleanComprehensive test coverage following TDD principles:
# Run all tests
go test ./...
# Run with race detector
go test -race ./...
# Run with coverage
go test -coverprofile=coverage.out ./...Build for different platforms:
# Linux (amd64)
GOOS=linux GOARCH=amd64 go build -o whoseport-linux-amd64 ./cmd/whoseport
# macOS (Intel)
GOOS=darwin GOARCH=amd64 go build -o whoseport-darwin-amd64 ./cmd/whoseport
# macOS (Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -o whoseport-darwin-arm64 ./cmd/whoseportGitHub Actions workflow includes:
- Testing on Ubuntu and macOS with Go 1.23 and 1.24
- Race detection for concurrency issues
- Test coverage reporting
- Cross-compilation for multiple platforms
- Linting with
go vet,go fmt, andstaticcheck - Security scanning with
govulncheck
- SOLID principles throughout
- Interface-driven design for testability
- Dependency injection via constructors
- Table-driven tests for comprehensive coverage
- No global state - all dependencies explicitly passed
- Graceful degradation when
/procunavailable
| Platform | lsof |
/proc Enhancement |
Status |
|---|---|---|---|
| Linux | ✅ | ✅ | Fully supported |
| macOS | ✅ | ❌ | Basic support (graceful degradation) |
| BSD | ✅ | ❌ | Untested (should work with basic features) |
| Windows | ❌ | ❌ | Not supported |
Runtime: None (just lsof command)
Build Dependencies:
- Go 1.23+
github.com/mattn/go-runewidth- Unicode width calculationgithub.com/rivo/uniseg- Unicode segmentation
See the LICENSE file for details.
This project stands on the shoulders of many similar utilities. It was created to combine:
- The simplicity of
lsof-based port queries - The richness of
/procfilesystem data - A modern, colorful terminal UI
- Clean, testable Go architecture
Inspired by countless command-line utilities and aliases that solve this same problem.