checkport 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checkport-2.0.0/PKG-INFO +66 -0
- checkport-2.0.0/README.md +43 -0
- checkport-2.0.0/checkport.egg-info/PKG-INFO +66 -0
- checkport-2.0.0/checkport.egg-info/SOURCES.txt +9 -0
- checkport-2.0.0/checkport.egg-info/dependency_links.txt +1 -0
- checkport-2.0.0/checkport.egg-info/entry_points.txt +2 -0
- checkport-2.0.0/checkport.egg-info/top_level.txt +1 -0
- checkport-2.0.0/checkport.py +608 -0
- checkport-2.0.0/pyproject.toml +32 -0
- checkport-2.0.0/setup.cfg +4 -0
- checkport-2.0.0/setup.py +17 -0
checkport-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: checkport
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Port management tool for checking and killing processes on specific ports
|
|
5
|
+
Home-page: https://github.com/watcher1337/checkport
|
|
6
|
+
Author: watcher1337
|
|
7
|
+
Author-email: watcher1337 <watcher1337@example.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: requires-python
|
|
23
|
+
|
|
24
|
+
# checkport
|
|
25
|
+
|
|
26
|
+
[](https://github.com/watcher1337/checkport/releases/latest)
|
|
27
|
+
[](https://www.python.org/)
|
|
28
|
+
[](https://github.com/watcher1337/checkport/releases)
|
|
29
|
+
|
|
30
|
+
**A powerful port management tool for checking and killing processes running on specific ports. Cross-platform and easy to use.**
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 📋 Table of Contents
|
|
35
|
+
|
|
36
|
+
- [Features](#-features)
|
|
37
|
+
- [Quick Install](#-quick-install)
|
|
38
|
+
- [Usage](#-usage)
|
|
39
|
+
- [Examples](#-examples)
|
|
40
|
+
- [Building from Source](#-building-from-source)
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## ✨ Features
|
|
45
|
+
|
|
46
|
+
- 🔍 **Port Checking** – Check if a port is in use and get process information
|
|
47
|
+
- 🔪 **Process Killing** – Kill processes running on specific ports with graceful termination
|
|
48
|
+
- 📋 **List Ports** – Display all listening ports with process details
|
|
49
|
+
- 🔎 **Port Scanning** – Scan a range of ports to find open ones
|
|
50
|
+
- 🖥️ **Cross-platform** – Windows, Linux, macOS (x64 & ARM64)
|
|
51
|
+
- 🎨 **Colored Output** – Beautiful terminal output with status indicators
|
|
52
|
+
- 🚀 **Fast** – Concurrent scanning for quick results
|
|
53
|
+
- 💪 **Force Kill** – Force kill processes when graceful termination fails
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 🚀 Quick Install
|
|
58
|
+
|
|
59
|
+
### Using pip / pipx
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Install using pip
|
|
63
|
+
pip install checkport
|
|
64
|
+
|
|
65
|
+
# Install using pipx (recommended for CLI tools)
|
|
66
|
+
pipx install checkport
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# checkport
|
|
2
|
+
|
|
3
|
+
[](https://github.com/watcher1337/checkport/releases/latest)
|
|
4
|
+
[](https://www.python.org/)
|
|
5
|
+
[](https://github.com/watcher1337/checkport/releases)
|
|
6
|
+
|
|
7
|
+
**A powerful port management tool for checking and killing processes running on specific ports. Cross-platform and easy to use.**
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 📋 Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Features](#-features)
|
|
14
|
+
- [Quick Install](#-quick-install)
|
|
15
|
+
- [Usage](#-usage)
|
|
16
|
+
- [Examples](#-examples)
|
|
17
|
+
- [Building from Source](#-building-from-source)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## ✨ Features
|
|
22
|
+
|
|
23
|
+
- 🔍 **Port Checking** – Check if a port is in use and get process information
|
|
24
|
+
- 🔪 **Process Killing** – Kill processes running on specific ports with graceful termination
|
|
25
|
+
- 📋 **List Ports** – Display all listening ports with process details
|
|
26
|
+
- 🔎 **Port Scanning** – Scan a range of ports to find open ones
|
|
27
|
+
- 🖥️ **Cross-platform** – Windows, Linux, macOS (x64 & ARM64)
|
|
28
|
+
- 🎨 **Colored Output** – Beautiful terminal output with status indicators
|
|
29
|
+
- 🚀 **Fast** – Concurrent scanning for quick results
|
|
30
|
+
- 💪 **Force Kill** – Force kill processes when graceful termination fails
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 🚀 Quick Install
|
|
35
|
+
|
|
36
|
+
### Using pip / pipx
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Install using pip
|
|
40
|
+
pip install checkport
|
|
41
|
+
|
|
42
|
+
# Install using pipx (recommended for CLI tools)
|
|
43
|
+
pipx install checkport
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: checkport
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Port management tool for checking and killing processes on specific ports
|
|
5
|
+
Home-page: https://github.com/watcher1337/checkport
|
|
6
|
+
Author: watcher1337
|
|
7
|
+
Author-email: watcher1337 <watcher1337@example.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: requires-python
|
|
23
|
+
|
|
24
|
+
# checkport
|
|
25
|
+
|
|
26
|
+
[](https://github.com/watcher1337/checkport/releases/latest)
|
|
27
|
+
[](https://www.python.org/)
|
|
28
|
+
[](https://github.com/watcher1337/checkport/releases)
|
|
29
|
+
|
|
30
|
+
**A powerful port management tool for checking and killing processes running on specific ports. Cross-platform and easy to use.**
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 📋 Table of Contents
|
|
35
|
+
|
|
36
|
+
- [Features](#-features)
|
|
37
|
+
- [Quick Install](#-quick-install)
|
|
38
|
+
- [Usage](#-usage)
|
|
39
|
+
- [Examples](#-examples)
|
|
40
|
+
- [Building from Source](#-building-from-source)
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## ✨ Features
|
|
45
|
+
|
|
46
|
+
- 🔍 **Port Checking** – Check if a port is in use and get process information
|
|
47
|
+
- 🔪 **Process Killing** – Kill processes running on specific ports with graceful termination
|
|
48
|
+
- 📋 **List Ports** – Display all listening ports with process details
|
|
49
|
+
- 🔎 **Port Scanning** – Scan a range of ports to find open ones
|
|
50
|
+
- 🖥️ **Cross-platform** – Windows, Linux, macOS (x64 & ARM64)
|
|
51
|
+
- 🎨 **Colored Output** – Beautiful terminal output with status indicators
|
|
52
|
+
- 🚀 **Fast** – Concurrent scanning for quick results
|
|
53
|
+
- 💪 **Force Kill** – Force kill processes when graceful termination fails
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 🚀 Quick Install
|
|
58
|
+
|
|
59
|
+
### Using pip / pipx
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Install using pip
|
|
63
|
+
pip install checkport
|
|
64
|
+
|
|
65
|
+
# Install using pipx (recommended for CLI tools)
|
|
66
|
+
pipx install checkport
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
checkport
|
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
PORT MANAGER v2.0
|
|
4
|
+
A tool to check and kill processes running on specific ports
|
|
5
|
+
Enhanced with fixtime-style architecture and features
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
import platform
|
|
11
|
+
import subprocess
|
|
12
|
+
import argparse
|
|
13
|
+
import socket
|
|
14
|
+
import threading
|
|
15
|
+
import time
|
|
16
|
+
import re
|
|
17
|
+
import warnings
|
|
18
|
+
from typing import Optional, Tuple, List, Dict, Any
|
|
19
|
+
from datetime import datetime
|
|
20
|
+
import concurrent.futures
|
|
21
|
+
|
|
22
|
+
# ============================================================
|
|
23
|
+
# Terminal Colors
|
|
24
|
+
# ============================================================
|
|
25
|
+
|
|
26
|
+
class Color:
|
|
27
|
+
RESET = "\033[0m"
|
|
28
|
+
BOLD = "\033[1m"
|
|
29
|
+
DIM = "\033[2m"
|
|
30
|
+
BLACK = "\033[30m"
|
|
31
|
+
RED = "\033[91m"
|
|
32
|
+
GREEN = "\033[92m"
|
|
33
|
+
YELLOW = "\033[93m"
|
|
34
|
+
BLUE = "\033[94m"
|
|
35
|
+
MAGENTA = "\033[95m"
|
|
36
|
+
CYAN = "\033[96m"
|
|
37
|
+
WHITE = "\033[97m"
|
|
38
|
+
|
|
39
|
+
def supports_color():
|
|
40
|
+
"""Return True if terminal likely supports ANSI colors."""
|
|
41
|
+
if os.getenv("NO_COLOR") is not None:
|
|
42
|
+
return False
|
|
43
|
+
if not hasattr(sys.stdout, "isatty"):
|
|
44
|
+
return False
|
|
45
|
+
if not sys.stdout.isatty():
|
|
46
|
+
return False
|
|
47
|
+
if platform.system().lower() == "windows":
|
|
48
|
+
return (os.getenv("ANSICON") is not None or
|
|
49
|
+
os.getenv("WT_SESSION") is not None or
|
|
50
|
+
os.getenv("TERM_PROGRAM") == "vscode" or
|
|
51
|
+
os.getenv("TERM") is not None)
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
USE_COLOR = supports_color()
|
|
55
|
+
|
|
56
|
+
def color(text, code):
|
|
57
|
+
if not USE_COLOR:
|
|
58
|
+
return str(text)
|
|
59
|
+
return f"{code}{text}{Color.RESET}"
|
|
60
|
+
|
|
61
|
+
def bold(text): return color(text, Color.BOLD)
|
|
62
|
+
def red(text): return color(text, Color.RED)
|
|
63
|
+
def green(text): return color(text, Color.GREEN)
|
|
64
|
+
def yellow(text): return color(text, Color.YELLOW)
|
|
65
|
+
def blue(text): return color(text, Color.BLUE)
|
|
66
|
+
def cyan(text): return color(text, Color.CYAN)
|
|
67
|
+
def magenta(text): return color(text, Color.MAGENTA)
|
|
68
|
+
def dim(text): return color(text, Color.DIM)
|
|
69
|
+
|
|
70
|
+
def colorize_message(message):
|
|
71
|
+
message = str(message)
|
|
72
|
+
prefixes = (("[✓]", Color.GREEN), ("[✗]", Color.RED), ("[!]", Color.YELLOW))
|
|
73
|
+
for prefix, prefix_color in prefixes:
|
|
74
|
+
if message.startswith(prefix):
|
|
75
|
+
colored_prefix = color(prefix, prefix_color)
|
|
76
|
+
return f"{colored_prefix}{message[len(prefix):]}"
|
|
77
|
+
return message
|
|
78
|
+
|
|
79
|
+
def cprint(*values, **kwargs):
|
|
80
|
+
"""Replacement for print() that colors known status prefixes."""
|
|
81
|
+
if not values:
|
|
82
|
+
print(**kwargs)
|
|
83
|
+
return
|
|
84
|
+
values = list(values)
|
|
85
|
+
if isinstance(values[0], str):
|
|
86
|
+
values[0] = colorize_message(values[0])
|
|
87
|
+
print(*values, **kwargs)
|
|
88
|
+
|
|
89
|
+
def print_value(label, value, indent=4):
|
|
90
|
+
"""Print a label/value pair with cyan label and bold value."""
|
|
91
|
+
spaces = " " * indent
|
|
92
|
+
print(f"{spaces}{cyan(f'{label:<16}')}{bold(value)}")
|
|
93
|
+
|
|
94
|
+
def print_separator(char="=", length=60):
|
|
95
|
+
print(dim(char * length))
|
|
96
|
+
|
|
97
|
+
# ============================================================
|
|
98
|
+
# Platform detection
|
|
99
|
+
# ============================================================
|
|
100
|
+
|
|
101
|
+
def detect_os():
|
|
102
|
+
"""Detect OS and architecture."""
|
|
103
|
+
system = platform.system().lower()
|
|
104
|
+
machine = platform.machine().lower()
|
|
105
|
+
|
|
106
|
+
if system == "windows":
|
|
107
|
+
return "windows"
|
|
108
|
+
elif system == "darwin":
|
|
109
|
+
if machine in ["arm64", "aarch64"]:
|
|
110
|
+
return "darwin_arm"
|
|
111
|
+
return "darwin"
|
|
112
|
+
else:
|
|
113
|
+
if machine in ["arm64", "aarch64", "armv7l", "armv8l"]:
|
|
114
|
+
return "linux_arm"
|
|
115
|
+
return "linux"
|
|
116
|
+
|
|
117
|
+
def is_admin():
|
|
118
|
+
"""Return True if running with admin/root privileges."""
|
|
119
|
+
if OS_NAME == "windows":
|
|
120
|
+
try:
|
|
121
|
+
import ctypes
|
|
122
|
+
return ctypes.windll.shell32.IsUserAnAdmin() != 0
|
|
123
|
+
except Exception:
|
|
124
|
+
return False
|
|
125
|
+
else:
|
|
126
|
+
try:
|
|
127
|
+
return os.geteuid() == 0
|
|
128
|
+
except AttributeError:
|
|
129
|
+
return os.system("sudo -n true") == 0
|
|
130
|
+
|
|
131
|
+
OS_NAME = detect_os()
|
|
132
|
+
IS_ADMIN = is_admin()
|
|
133
|
+
|
|
134
|
+
# ============================================================
|
|
135
|
+
# Configuration
|
|
136
|
+
# ============================================================
|
|
137
|
+
|
|
138
|
+
MAX_WORKERS = 4
|
|
139
|
+
print_lock = threading.Lock()
|
|
140
|
+
|
|
141
|
+
# ============================================================
|
|
142
|
+
# Arguments
|
|
143
|
+
# ============================================================
|
|
144
|
+
|
|
145
|
+
parser = argparse.ArgumentParser(
|
|
146
|
+
description="Port management tool for checking and killing processes on specific ports",
|
|
147
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
148
|
+
usage="checkport [OPTIONS]",
|
|
149
|
+
add_help=False,
|
|
150
|
+
epilog="""
|
|
151
|
+
Examples:
|
|
152
|
+
checkport -c 8080 # Check if port 8080 is in use
|
|
153
|
+
checkport -k 8080 # Kill process on port 8080
|
|
154
|
+
checkport -c 80 -v # Check port 80 with verbose output
|
|
155
|
+
checkport -k 443 --force # Force kill process on port 443
|
|
156
|
+
checkport --list # List all listening ports
|
|
157
|
+
checkport --scan 8000-8100 # Scan port range
|
|
158
|
+
""",
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
parser.add_argument("-h", action="help", default=argparse.SUPPRESS,
|
|
162
|
+
help="show this help message")
|
|
163
|
+
parser.add_argument("-c", "--check", type=int, metavar="PORT",
|
|
164
|
+
help="Check if a port is open and show process info")
|
|
165
|
+
parser.add_argument("-k", "--kill", type=int, metavar="PORT",
|
|
166
|
+
help="Kill the process running on the specified port")
|
|
167
|
+
parser.add_argument("-v", "--verbose", action="store_true",
|
|
168
|
+
help="Enable verbose output")
|
|
169
|
+
parser.add_argument("--force", action="store_true",
|
|
170
|
+
help="Force kill process (SIGKILL immediately)")
|
|
171
|
+
parser.add_argument("--list", action="store_true",
|
|
172
|
+
help="List all listening ports")
|
|
173
|
+
parser.add_argument("--scan", type=str, metavar="RANGE",
|
|
174
|
+
help="Scan port range (e.g., 8000-8100)")
|
|
175
|
+
parser.add_argument("--no-color", action="store_true",
|
|
176
|
+
help="Disable colored output")
|
|
177
|
+
|
|
178
|
+
args = parser.parse_args()
|
|
179
|
+
|
|
180
|
+
if args.no_color:
|
|
181
|
+
USE_COLOR = False
|
|
182
|
+
|
|
183
|
+
# ============================================================
|
|
184
|
+
# Logging
|
|
185
|
+
# ============================================================
|
|
186
|
+
|
|
187
|
+
def log(msg, force=False):
|
|
188
|
+
if args.verbose or force:
|
|
189
|
+
with print_lock:
|
|
190
|
+
cprint(msg)
|
|
191
|
+
|
|
192
|
+
# ============================================================
|
|
193
|
+
# Core Port Management
|
|
194
|
+
# ============================================================
|
|
195
|
+
|
|
196
|
+
class PortManager:
|
|
197
|
+
def __init__(self):
|
|
198
|
+
self.system = OS_NAME
|
|
199
|
+
self.is_admin = IS_ADMIN
|
|
200
|
+
|
|
201
|
+
def find_process_on_port(self, port: int) -> Optional[Tuple[str, str, str]]:
|
|
202
|
+
"""
|
|
203
|
+
Find process using a specific port.
|
|
204
|
+
Returns (pid, command, user) if found, None otherwise.
|
|
205
|
+
"""
|
|
206
|
+
log(f"[*] Looking for process on port {port}")
|
|
207
|
+
|
|
208
|
+
# Try lsof first (most reliable)
|
|
209
|
+
try:
|
|
210
|
+
result = subprocess.run(
|
|
211
|
+
['lsof', '-i', f':{port}', '-t'],
|
|
212
|
+
capture_output=True,
|
|
213
|
+
text=True,
|
|
214
|
+
timeout=5
|
|
215
|
+
)
|
|
216
|
+
if result.stdout.strip():
|
|
217
|
+
pid = result.stdout.strip().split('\n')[0]
|
|
218
|
+
# Get process info
|
|
219
|
+
ps_result = subprocess.run(
|
|
220
|
+
['ps', '-p', pid, '-o', 'comm=', '-o', 'user='],
|
|
221
|
+
capture_output=True,
|
|
222
|
+
text=True,
|
|
223
|
+
timeout=5
|
|
224
|
+
)
|
|
225
|
+
if ps_result.stdout.strip():
|
|
226
|
+
parts = ps_result.stdout.strip().split()
|
|
227
|
+
if len(parts) >= 2:
|
|
228
|
+
command = parts[0]
|
|
229
|
+
user = parts[1]
|
|
230
|
+
log(f"[+] Found process {pid} ({command}) on port {port}")
|
|
231
|
+
return (pid, command, user)
|
|
232
|
+
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
|
|
233
|
+
pass
|
|
234
|
+
|
|
235
|
+
# Fallback to netstat/ss
|
|
236
|
+
try:
|
|
237
|
+
if self.system == 'linux' or self.system == 'linux_arm':
|
|
238
|
+
cmd = ['ss', '-tlnp', f'sport = :{port}']
|
|
239
|
+
elif self.system.startswith('darwin'):
|
|
240
|
+
cmd = ['netstat', '-anv', '-p', 'tcp']
|
|
241
|
+
else: # windows
|
|
242
|
+
cmd = ['netstat', '-ano', '-p', 'TCP']
|
|
243
|
+
|
|
244
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
|
|
245
|
+
|
|
246
|
+
if self.system.startswith('linux'):
|
|
247
|
+
# Parse ss output
|
|
248
|
+
for line in result.stdout.split('\n'):
|
|
249
|
+
if f':{port}' in line and 'LISTEN' in line:
|
|
250
|
+
match = re.search(r'pid=(\d+)', line)
|
|
251
|
+
if match:
|
|
252
|
+
pid = match.group(1)
|
|
253
|
+
ps_result = subprocess.run(
|
|
254
|
+
['ps', '-p', pid, '-o', 'comm=', '-o', 'user='],
|
|
255
|
+
capture_output=True,
|
|
256
|
+
text=True,
|
|
257
|
+
timeout=5
|
|
258
|
+
)
|
|
259
|
+
if ps_result.stdout.strip():
|
|
260
|
+
parts = ps_result.stdout.strip().split()
|
|
261
|
+
if len(parts) >= 2:
|
|
262
|
+
command = parts[0]
|
|
263
|
+
user = parts[1]
|
|
264
|
+
log(f"[+] Found process {pid} ({command}) on port {port}")
|
|
265
|
+
return (pid, command, user)
|
|
266
|
+
elif self.system.startswith('darwin'):
|
|
267
|
+
# Parse netstat output (macOS)
|
|
268
|
+
for line in result.stdout.split('\n'):
|
|
269
|
+
if f'.{port}' in line and 'LISTEN' in line:
|
|
270
|
+
parts = line.split()
|
|
271
|
+
if len(parts) >= 9:
|
|
272
|
+
pid = parts[8] if parts[8].isdigit() else None
|
|
273
|
+
if pid:
|
|
274
|
+
ps_result = subprocess.run(
|
|
275
|
+
['ps', '-p', pid, '-o', 'comm=', '-o', 'user='],
|
|
276
|
+
capture_output=True,
|
|
277
|
+
text=True,
|
|
278
|
+
timeout=5
|
|
279
|
+
)
|
|
280
|
+
if ps_result.stdout.strip():
|
|
281
|
+
parts = ps_result.stdout.strip().split()
|
|
282
|
+
if len(parts) >= 2:
|
|
283
|
+
command = parts[0]
|
|
284
|
+
user = parts[1]
|
|
285
|
+
log(f"[+] Found process {pid} ({command}) on port {port}")
|
|
286
|
+
return (pid, command, user)
|
|
287
|
+
else: # Windows
|
|
288
|
+
for line in result.stdout.split('\n'):
|
|
289
|
+
if f':{port}' in line and 'LISTENING' in line:
|
|
290
|
+
parts = line.split()
|
|
291
|
+
if len(parts) >= 5:
|
|
292
|
+
pid = parts[4] if parts[4].isdigit() else None
|
|
293
|
+
if pid:
|
|
294
|
+
try:
|
|
295
|
+
ps_result = subprocess.run(
|
|
296
|
+
['tasklist', '/FI', f'PID eq {pid}'],
|
|
297
|
+
capture_output=True,
|
|
298
|
+
text=True,
|
|
299
|
+
timeout=5
|
|
300
|
+
)
|
|
301
|
+
if ps_result.stdout:
|
|
302
|
+
lines = ps_result.stdout.split('\n')
|
|
303
|
+
if len(lines) > 3:
|
|
304
|
+
proc_info = lines[3].split()
|
|
305
|
+
if len(proc_info) >= 2:
|
|
306
|
+
command = proc_info[0]
|
|
307
|
+
user = "N/A"
|
|
308
|
+
log(f"[+] Found process {pid} ({command}) on port {port}")
|
|
309
|
+
return (pid, command, user)
|
|
310
|
+
except Exception:
|
|
311
|
+
pass
|
|
312
|
+
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
|
|
313
|
+
pass
|
|
314
|
+
|
|
315
|
+
# Last fallback: try fuser
|
|
316
|
+
try:
|
|
317
|
+
result = subprocess.run(
|
|
318
|
+
['fuser', f'{port}/tcp'],
|
|
319
|
+
capture_output=True,
|
|
320
|
+
text=True,
|
|
321
|
+
timeout=5
|
|
322
|
+
)
|
|
323
|
+
if result.stdout.strip():
|
|
324
|
+
pid = result.stdout.strip().split()[0]
|
|
325
|
+
ps_result = subprocess.run(
|
|
326
|
+
['ps', '-p', pid, '-o', 'comm=', '-o', 'user='],
|
|
327
|
+
capture_output=True,
|
|
328
|
+
text=True,
|
|
329
|
+
timeout=5
|
|
330
|
+
)
|
|
331
|
+
if ps_result.stdout.strip():
|
|
332
|
+
parts = ps_result.stdout.strip().split()
|
|
333
|
+
if len(parts) >= 2:
|
|
334
|
+
command = parts[0]
|
|
335
|
+
user = parts[1]
|
|
336
|
+
log(f"[+] Found process {pid} ({command}) on port {port}")
|
|
337
|
+
return (pid, command, user)
|
|
338
|
+
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
|
|
339
|
+
pass
|
|
340
|
+
|
|
341
|
+
log(f"[-] No process found on port {port}")
|
|
342
|
+
return None
|
|
343
|
+
|
|
344
|
+
def check_port(self, port: int) -> bool:
|
|
345
|
+
"""
|
|
346
|
+
Check if port is in use and display process info.
|
|
347
|
+
Returns True if port is in use, False otherwise.
|
|
348
|
+
"""
|
|
349
|
+
log(f"[*] Checking port {port}")
|
|
350
|
+
|
|
351
|
+
result = self.find_process_on_port(port)
|
|
352
|
+
|
|
353
|
+
print()
|
|
354
|
+
print_separator()
|
|
355
|
+
if result:
|
|
356
|
+
pid, command, user = result
|
|
357
|
+
cprint(f"[✓] Port {port} is IN USE")
|
|
358
|
+
print_separator()
|
|
359
|
+
print_value("COMMAND", command)
|
|
360
|
+
print_value("PID", pid)
|
|
361
|
+
print_value("USER", user)
|
|
362
|
+
print_separator()
|
|
363
|
+
|
|
364
|
+
# Show detailed lsof output if available
|
|
365
|
+
if self.system.startswith('darwin') or self.system.startswith('linux'):
|
|
366
|
+
try:
|
|
367
|
+
lsof_result = subprocess.run(
|
|
368
|
+
['lsof', '-i', f':{port}'],
|
|
369
|
+
capture_output=True,
|
|
370
|
+
text=True,
|
|
371
|
+
timeout=5
|
|
372
|
+
)
|
|
373
|
+
if lsof_result.stdout:
|
|
374
|
+
lines = lsof_result.stdout.split('\n')
|
|
375
|
+
if len(lines) > 1:
|
|
376
|
+
print("\nDetailed info:")
|
|
377
|
+
print(lines[0]) # Header
|
|
378
|
+
if len(lines) > 2:
|
|
379
|
+
print(lines[1]) # First data line
|
|
380
|
+
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
|
|
381
|
+
pass
|
|
382
|
+
return True
|
|
383
|
+
else:
|
|
384
|
+
cprint(f"[✓] Port {port} is FREE")
|
|
385
|
+
print_separator()
|
|
386
|
+
return False
|
|
387
|
+
|
|
388
|
+
def kill_process_on_port(self, port: int) -> bool:
|
|
389
|
+
"""
|
|
390
|
+
Kill process running on specified port.
|
|
391
|
+
Returns True if successful, False otherwise.
|
|
392
|
+
"""
|
|
393
|
+
# Check if port is privileged
|
|
394
|
+
if port < 1024 and not self.is_admin:
|
|
395
|
+
cprint(f"[!] Port {port} is a privileged port (below 1024)")
|
|
396
|
+
cprint("[!] You may need to run with sudo for proper operation")
|
|
397
|
+
|
|
398
|
+
result = self.find_process_on_port(port)
|
|
399
|
+
|
|
400
|
+
if not result:
|
|
401
|
+
cprint(f"[✓] No process found on port {port}")
|
|
402
|
+
return False
|
|
403
|
+
|
|
404
|
+
pid, command, user = result
|
|
405
|
+
cprint(f"[*] Found process {pid} ({command}) on port {port}")
|
|
406
|
+
|
|
407
|
+
try:
|
|
408
|
+
if args.force:
|
|
409
|
+
# Force kill immediately
|
|
410
|
+
if self.system == "windows":
|
|
411
|
+
subprocess.run(['taskkill', '/F', '/PID', pid], check=True)
|
|
412
|
+
else:
|
|
413
|
+
os.kill(int(pid), 9) # SIGKILL
|
|
414
|
+
cprint(f"[✓] Successfully force killed process {pid} on port {port}")
|
|
415
|
+
return True
|
|
416
|
+
|
|
417
|
+
# Try gentle kill first (SIGTERM)
|
|
418
|
+
if self.system == "windows":
|
|
419
|
+
subprocess.run(['taskkill', '/PID', pid], check=True)
|
|
420
|
+
else:
|
|
421
|
+
os.kill(int(pid), 15) # SIGTERM
|
|
422
|
+
|
|
423
|
+
# Wait a moment and check if process is still running
|
|
424
|
+
time.sleep(0.5)
|
|
425
|
+
|
|
426
|
+
# Check if process still exists
|
|
427
|
+
if self.system == "windows":
|
|
428
|
+
try:
|
|
429
|
+
subprocess.run(['tasklist', '/FI', f'PID eq {pid}'],
|
|
430
|
+
capture_output=True, check=True)
|
|
431
|
+
except Exception:
|
|
432
|
+
cprint(f"[✓] Successfully killed process {pid} on port {port}")
|
|
433
|
+
return True
|
|
434
|
+
else:
|
|
435
|
+
try:
|
|
436
|
+
os.kill(int(pid), 0) # Check if process exists
|
|
437
|
+
# Process still exists, try SIGKILL
|
|
438
|
+
cprint("[!] Process didn't terminate gracefully, forcing kill...")
|
|
439
|
+
os.kill(int(pid), 9) # SIGKILL
|
|
440
|
+
cprint(f"[✓] Successfully killed process {pid} on port {port}")
|
|
441
|
+
return True
|
|
442
|
+
except OSError:
|
|
443
|
+
# Process is gone
|
|
444
|
+
cprint(f"[✓] Successfully killed process {pid} on port {port}")
|
|
445
|
+
return True
|
|
446
|
+
|
|
447
|
+
except PermissionError:
|
|
448
|
+
cprint("[✗] Permission denied. Try running with sudo")
|
|
449
|
+
return False
|
|
450
|
+
except ProcessLookupError:
|
|
451
|
+
cprint(f"[✓] Process {pid} was already terminated")
|
|
452
|
+
return True
|
|
453
|
+
except Exception as e:
|
|
454
|
+
cprint(f"[✗] Error killing process: {e}")
|
|
455
|
+
return False
|
|
456
|
+
|
|
457
|
+
def list_listening_ports(self) -> List[Dict[str, Any]]:
|
|
458
|
+
"""List all listening ports with process info."""
|
|
459
|
+
ports = []
|
|
460
|
+
|
|
461
|
+
try:
|
|
462
|
+
if self.system.startswith('linux'):
|
|
463
|
+
cmd = ['ss', '-tlnp']
|
|
464
|
+
elif self.system.startswith('darwin'):
|
|
465
|
+
cmd = ['netstat', '-anv', '-p', 'tcp']
|
|
466
|
+
else: # windows
|
|
467
|
+
cmd = ['netstat', '-ano', '-p', 'TCP']
|
|
468
|
+
|
|
469
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
|
470
|
+
|
|
471
|
+
for line in result.stdout.split('\n'):
|
|
472
|
+
if 'LISTEN' in line:
|
|
473
|
+
port_match = re.search(r':(\d+)\s', line)
|
|
474
|
+
if port_match:
|
|
475
|
+
port = int(port_match.group(1))
|
|
476
|
+
process_info = self.find_process_on_port(port)
|
|
477
|
+
ports.append({
|
|
478
|
+
'port': port,
|
|
479
|
+
'pid': process_info[0] if process_info else 'N/A',
|
|
480
|
+
'command': process_info[1] if process_info else 'N/A',
|
|
481
|
+
'user': process_info[2] if process_info else 'N/A'
|
|
482
|
+
})
|
|
483
|
+
except Exception as e:
|
|
484
|
+
log(f"[-] Error listing ports: {e}")
|
|
485
|
+
|
|
486
|
+
return ports
|
|
487
|
+
|
|
488
|
+
def scan_port_range(self, start: int, end: int) -> List[int]:
|
|
489
|
+
"""Scan a range of ports and return open ones."""
|
|
490
|
+
open_ports = []
|
|
491
|
+
|
|
492
|
+
cprint(f"[*] Scanning ports {start}-{end}...")
|
|
493
|
+
|
|
494
|
+
def scan_single_port(port):
|
|
495
|
+
try:
|
|
496
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
497
|
+
sock.settimeout(0.5)
|
|
498
|
+
result = sock.connect_ex(('127.0.0.1', port))
|
|
499
|
+
sock.close()
|
|
500
|
+
if result == 0:
|
|
501
|
+
return port
|
|
502
|
+
except Exception:
|
|
503
|
+
pass
|
|
504
|
+
return None
|
|
505
|
+
|
|
506
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
|
|
507
|
+
futures = {executor.submit(scan_single_port, port): port
|
|
508
|
+
for port in range(start, end + 1)}
|
|
509
|
+
|
|
510
|
+
for future in concurrent.futures.as_completed(futures):
|
|
511
|
+
result = future.result()
|
|
512
|
+
if result:
|
|
513
|
+
open_ports.append(result)
|
|
514
|
+
process_info = self.find_process_on_port(result)
|
|
515
|
+
if process_info:
|
|
516
|
+
cprint(f"[+] Port {result} is open (PID: {process_info[0]}, {process_info[1]})")
|
|
517
|
+
else:
|
|
518
|
+
cprint(f"[+] Port {result} is open")
|
|
519
|
+
|
|
520
|
+
return open_ports
|
|
521
|
+
|
|
522
|
+
# ============================================================
|
|
523
|
+
# Main
|
|
524
|
+
# ============================================================
|
|
525
|
+
|
|
526
|
+
def main():
|
|
527
|
+
"""Main entry point for the checkport tool."""
|
|
528
|
+
# Display system info in verbose mode
|
|
529
|
+
if args.verbose:
|
|
530
|
+
cprint(f"[*] OS: {OS_NAME}")
|
|
531
|
+
cprint(f"[*] Architecture: {platform.machine()}")
|
|
532
|
+
cprint(f"[*] Admin privileges: {IS_ADMIN}")
|
|
533
|
+
|
|
534
|
+
pm = PortManager()
|
|
535
|
+
|
|
536
|
+
# Handle list operation
|
|
537
|
+
if args.list:
|
|
538
|
+
cprint("\n[🔄] Listing all listening ports...")
|
|
539
|
+
print_separator()
|
|
540
|
+
ports = pm.list_listening_ports()
|
|
541
|
+
if ports:
|
|
542
|
+
cprint(f"[+] Found {len(ports)} listening ports:")
|
|
543
|
+
print_separator()
|
|
544
|
+
print(f"{'PORT':<8} {'PID':<8} {'USER':<12} COMMAND")
|
|
545
|
+
print_separator()
|
|
546
|
+
for p in sorted(ports, key=lambda x: x['port']):
|
|
547
|
+
print(f"{p['port']:<8} {p['pid']:<8} {p['user']:<12} {p['command']}")
|
|
548
|
+
print_separator()
|
|
549
|
+
else:
|
|
550
|
+
cprint("[!] No listening ports found")
|
|
551
|
+
return
|
|
552
|
+
|
|
553
|
+
# Handle scan operation
|
|
554
|
+
if args.scan:
|
|
555
|
+
try:
|
|
556
|
+
if '-' in args.scan:
|
|
557
|
+
start, end = map(int, args.scan.split('-'))
|
|
558
|
+
if start > end:
|
|
559
|
+
start, end = end, start
|
|
560
|
+
if start < 1 or end > 65535:
|
|
561
|
+
cprint("[✗] Port range must be between 1 and 65535")
|
|
562
|
+
sys.exit(1)
|
|
563
|
+
open_ports = pm.scan_port_range(start, end)
|
|
564
|
+
if open_ports:
|
|
565
|
+
cprint(f"\n[+] Found {len(open_ports)} open ports: {open_ports}")
|
|
566
|
+
else:
|
|
567
|
+
cprint("\n[!] No open ports found in range")
|
|
568
|
+
else:
|
|
569
|
+
cprint("[✗] Invalid port range format. Use start-end (e.g., 8000-8100)")
|
|
570
|
+
sys.exit(1)
|
|
571
|
+
except ValueError:
|
|
572
|
+
cprint("[✗] Invalid port range. Use numeric values (e.g., 8000-8100)")
|
|
573
|
+
sys.exit(1)
|
|
574
|
+
return
|
|
575
|
+
|
|
576
|
+
# Handle check operation
|
|
577
|
+
if args.check:
|
|
578
|
+
port = args.check
|
|
579
|
+
if not (1 <= port <= 65535):
|
|
580
|
+
cprint(f"[✗] Invalid port number: {port}. Must be between 1 and 65535")
|
|
581
|
+
sys.exit(1)
|
|
582
|
+
in_use = pm.check_port(port)
|
|
583
|
+
sys.exit(0 if in_use else 1)
|
|
584
|
+
|
|
585
|
+
# Handle kill operation
|
|
586
|
+
if args.kill:
|
|
587
|
+
port = args.kill
|
|
588
|
+
if not (1 <= port <= 65535):
|
|
589
|
+
cprint(f"[✗] Invalid port number: {port}. Must be between 1 and 65535")
|
|
590
|
+
sys.exit(1)
|
|
591
|
+
success = pm.kill_process_on_port(port)
|
|
592
|
+
sys.exit(0 if success else 1)
|
|
593
|
+
|
|
594
|
+
# No arguments provided
|
|
595
|
+
parser.print_help()
|
|
596
|
+
|
|
597
|
+
if __name__ == "__main__":
|
|
598
|
+
try:
|
|
599
|
+
main()
|
|
600
|
+
except KeyboardInterrupt:
|
|
601
|
+
cprint("\n[!] Operation cancelled by user")
|
|
602
|
+
sys.exit(1)
|
|
603
|
+
except Exception as e:
|
|
604
|
+
cprint(f"\n[✗] Unexpected error: {e}")
|
|
605
|
+
if args.verbose:
|
|
606
|
+
import traceback
|
|
607
|
+
traceback.print_exc()
|
|
608
|
+
sys.exit(1)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "checkport"
|
|
3
|
+
version = "2.0.0"
|
|
4
|
+
description = "Port management tool for checking and killing processes on specific ports"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.8"
|
|
7
|
+
authors = [
|
|
8
|
+
{name = "watcher1337", email = "watcher1337@example.com"}
|
|
9
|
+
]
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"Programming Language :: Python :: 3.8",
|
|
14
|
+
"Programming Language :: Python :: 3.9",
|
|
15
|
+
"Programming Language :: Python :: 3.10",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
]
|
|
22
|
+
dependencies = []
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
checkport = "checkport:main"
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["setuptools>=61.0"]
|
|
29
|
+
build-backend = "setuptools.build_meta"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools]
|
|
32
|
+
py-modules = ["checkport"]
|
checkport-2.0.0/setup.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from setuptools import setup
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="checkport",
|
|
5
|
+
version="2.0.0",
|
|
6
|
+
py_modules=["checkport"],
|
|
7
|
+
install_requires=[],
|
|
8
|
+
entry_points={
|
|
9
|
+
"console_scripts": [
|
|
10
|
+
"checkport=checkport:main",
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
author="watcher1337",
|
|
14
|
+
description="Port management tool for checking and killing processes on specific ports",
|
|
15
|
+
url="https://github.com/watcher1337/checkport",
|
|
16
|
+
python_requires=">=3.8",
|
|
17
|
+
)
|