kport 2.1.2__tar.gz → 3.1.1__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.
kport-3.1.1/LICENSE ADDED
@@ -0,0 +1,30 @@
1
+ GNU AFFERO GENERAL PUBLIC LICENSE
2
+ Version 3, 19 November 2007
3
+
4
+ Copyright (C) 2025 kport contributors
5
+
6
+ This program is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Affero General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ This program is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Affero General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Affero General Public License
17
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
18
+
19
+ --------------------------------------------------------------------------
20
+
21
+ NOTE: This is the AGPL-3.0 license, which is similar to GPL-3.0 but with
22
+ an additional requirement: If you run a modified version of this program
23
+ on a server and let users interact with it remotely through a network,
24
+ you must make the complete source code of your modified version available
25
+ to those users.
26
+
27
+ This protects against the "SaaS loophole" where companies could use the
28
+ software as a service without sharing their modifications.
29
+
30
+ Full license text: https://www.gnu.org/licenses/agpl-3.0.txt
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kport
3
- Version: 2.1.2
3
+ Version: 3.1.1
4
4
  Summary: A cross-platform command-line tool to inspect and kill processes using specific ports
5
5
  Home-page: https://github.com/farman20ali/port-killer
6
6
  Author: Farman Ali
@@ -13,7 +13,7 @@ Classifier: Intended Audience :: Developers
13
13
  Classifier: Intended Audience :: System Administrators
14
14
  Classifier: Topic :: System :: Networking
15
15
  Classifier: Topic :: System :: Systems Administration
16
- Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3
17
17
  Classifier: Programming Language :: Python :: 3
18
18
  Classifier: Programming Language :: Python :: 3.6
19
19
  Classifier: Programming Language :: Python :: 3.7
@@ -28,6 +28,7 @@ Classifier: Operating System :: MacOS
28
28
  Requires-Python: >=3.6
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
+ Requires-Dist: psutil>=5.9.0
31
32
  Dynamic: author
32
33
  Dynamic: author-email
33
34
  Dynamic: classifier
@@ -37,6 +38,7 @@ Dynamic: home-page
37
38
  Dynamic: keywords
38
39
  Dynamic: license-file
39
40
  Dynamic: project-url
41
+ Dynamic: requires-dist
40
42
  Dynamic: requires-python
41
43
  Dynamic: summary
42
44
 
@@ -44,7 +46,7 @@ Dynamic: summary
44
46
 
45
47
  [![Version](https://img.shields.io/badge/version-1.0.0-blue.svg)](https://github.com/farman20ali/port-killer)
46
48
  [![Python](https://img.shields.io/badge/python-3.6+-blue.svg)](https://www.python.org/downloads/)
47
- [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
49
+ [![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](LICENSE)
48
50
  [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)](https://github.com/farman20ali/port-killer)
49
51
 
50
52
  A simple, powerful command-line tool to inspect and kill processes using specific ports on Windows, Linux, and macOS.
@@ -155,8 +157,20 @@ On Linux, some ports may appear as `LISTEN` but the owning PID/process name is n
155
157
  If you see `local-unknown` in `inspect` / `explain`, try:
156
158
 
157
159
  ```bash
158
- sudo kport inspect 6379
159
- sudo kport explain 6379
160
+ sudo -E kport inspect 6379
161
+ sudo -E kport explain 6379
162
+ ```
163
+
164
+ If you installed with `pip install --user kport`, `sudo` may not find `kport` because root's `PATH` doesn't include your user scripts directory.
165
+
166
+ Alternatives:
167
+
168
+ ```bash
169
+ # Option 1: keep your PATH when using sudo
170
+ sudo -E "$HOME/.local/bin/kport" inspect 6379
171
+
172
+ # Option 2: run the module via the system python (when working from repo)
173
+ sudo -E python3 kport.py inspect 6379
160
174
  ```
161
175
 
162
176
  ### Config file support (Phase 2)
@@ -458,6 +472,33 @@ kport -i 80
458
472
  kport -l
459
473
  ```
460
474
 
475
+ ## 📚 Documentation
476
+
477
+ - **[Installation Guide](INSTALL.md)** - Detailed installation instructions
478
+ - **[Quick Start](QUICKSTART.md)** - Get started quickly
479
+ - **[Publishing Guide](PUBLISH.md)** - How to publish kport
480
+ - **[Release Guide](RELEASE_GUIDE.md)** - Creating releases (manual & automated)
481
+ - **[Debian Release](DEB_RELEASE.md)** - Debian packaging and APT distribution
482
+ - **[Contributing](CONTRIBUTING.md)** - How to contribute
483
+
484
+ ## 🚀 For Maintainers
485
+
486
+ ### Creating a Release
487
+
488
+ Automated release (recommended):
489
+
490
+ ```bash
491
+ python3 release.py
492
+ ```
493
+
494
+ This script handles:
495
+ - Git tagging
496
+ - PyPI package building
497
+ - Debian package building
498
+ - GitHub release creation
499
+
500
+ See [RELEASE_GUIDE.md](RELEASE_GUIDE.md) for manual release steps and troubleshooting.
501
+
461
502
  ## 🤝 Contributing
462
503
 
463
504
  Contributions are welcome! Please feel free to submit a Pull Request.
@@ -470,7 +511,15 @@ Contributions are welcome! Please feel free to submit a Pull Request.
470
511
 
471
512
  ## 📝 License
472
513
 
473
- This project is licensed under the MIT License - see the LICENSE file for details.
514
+ This project is licensed under the GNU Affero General Public License v3.0 - see the [LICENSE](LICENSE) file for details.
515
+
516
+ **What this means:**
517
+ - ✅ Free to use, modify, and distribute
518
+ - ✅ Must share source code of any modifications
519
+ - ✅ **Network use = distribution**: If you run a modified version as a service, you must share the source code
520
+ - ❌ Cannot use in proprietary SaaS without sharing modifications
521
+
522
+ For commercial licensing or if AGPL doesn't fit your use case, contact: farman20ali@gmail.com
474
523
 
475
524
  ## ⚠️ Important Notes
476
525
 
@@ -490,6 +539,38 @@ sudo kport -k 80
490
539
 
491
540
  On Windows, run your terminal as Administrator.
492
541
 
542
+ ### Stubborn processes that won't die
543
+
544
+ Some processes (especially Java applications) may not respond to graceful termination. Use the `--force` flag which automatically uses a multi-tier kill strategy (SIGTERM → SIGKILL → fuser):
545
+
546
+ ```bash
547
+ kport -k 8081 --force
548
+ ```
549
+
550
+ On Linux, `kport` will automatically use `fuser -k` as a fallback when standard kill methods fail. This is extremely effective for stubborn Java/Node/Python processes:
551
+
552
+ ```bash
553
+ # Install fuser for best results (Ubuntu/Debian)
554
+ sudo apt-get install psmisc
555
+
556
+ # Install fuser (RHEL/CentOS/Fedora)
557
+ sudo yum install psmisc
558
+
559
+ # Then kport will automatically use it when needed
560
+ kport -k 8081 --force
561
+ ```
562
+
563
+ **What happens:**
564
+ 1. First tries SIGTERM (graceful shutdown)
565
+ 2. Then tries SIGKILL after timeout
566
+ 3. Finally uses `fuser -k 8081/tcp` if process still lives (Linux only)
567
+
568
+ **Manual alternative:** You can also kill a port directly with fuser:
569
+ ```bash
570
+ # Kill all processes using port 8081 (requires sudo)
571
+ sudo fuser -k 8081/tcp
572
+ ```
573
+
493
574
  ### Port not found
494
575
 
495
576
  Make sure the port number is correct and that a process is actually using it. Use `kport -l` to see all active ports.
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Version](https://img.shields.io/badge/version-1.0.0-blue.svg)](https://github.com/farman20ali/port-killer)
4
4
  [![Python](https://img.shields.io/badge/python-3.6+-blue.svg)](https://www.python.org/downloads/)
5
- [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
5
+ [![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](LICENSE)
6
6
  [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)](https://github.com/farman20ali/port-killer)
7
7
 
8
8
  A simple, powerful command-line tool to inspect and kill processes using specific ports on Windows, Linux, and macOS.
@@ -113,8 +113,20 @@ On Linux, some ports may appear as `LISTEN` but the owning PID/process name is n
113
113
  If you see `local-unknown` in `inspect` / `explain`, try:
114
114
 
115
115
  ```bash
116
- sudo kport inspect 6379
117
- sudo kport explain 6379
116
+ sudo -E kport inspect 6379
117
+ sudo -E kport explain 6379
118
+ ```
119
+
120
+ If you installed with `pip install --user kport`, `sudo` may not find `kport` because root's `PATH` doesn't include your user scripts directory.
121
+
122
+ Alternatives:
123
+
124
+ ```bash
125
+ # Option 1: keep your PATH when using sudo
126
+ sudo -E "$HOME/.local/bin/kport" inspect 6379
127
+
128
+ # Option 2: run the module via the system python (when working from repo)
129
+ sudo -E python3 kport.py inspect 6379
118
130
  ```
119
131
 
120
132
  ### Config file support (Phase 2)
@@ -416,6 +428,33 @@ kport -i 80
416
428
  kport -l
417
429
  ```
418
430
 
431
+ ## 📚 Documentation
432
+
433
+ - **[Installation Guide](INSTALL.md)** - Detailed installation instructions
434
+ - **[Quick Start](QUICKSTART.md)** - Get started quickly
435
+ - **[Publishing Guide](PUBLISH.md)** - How to publish kport
436
+ - **[Release Guide](RELEASE_GUIDE.md)** - Creating releases (manual & automated)
437
+ - **[Debian Release](DEB_RELEASE.md)** - Debian packaging and APT distribution
438
+ - **[Contributing](CONTRIBUTING.md)** - How to contribute
439
+
440
+ ## 🚀 For Maintainers
441
+
442
+ ### Creating a Release
443
+
444
+ Automated release (recommended):
445
+
446
+ ```bash
447
+ python3 release.py
448
+ ```
449
+
450
+ This script handles:
451
+ - Git tagging
452
+ - PyPI package building
453
+ - Debian package building
454
+ - GitHub release creation
455
+
456
+ See [RELEASE_GUIDE.md](RELEASE_GUIDE.md) for manual release steps and troubleshooting.
457
+
419
458
  ## 🤝 Contributing
420
459
 
421
460
  Contributions are welcome! Please feel free to submit a Pull Request.
@@ -428,7 +467,15 @@ Contributions are welcome! Please feel free to submit a Pull Request.
428
467
 
429
468
  ## 📝 License
430
469
 
431
- This project is licensed under the MIT License - see the LICENSE file for details.
470
+ This project is licensed under the GNU Affero General Public License v3.0 - see the [LICENSE](LICENSE) file for details.
471
+
472
+ **What this means:**
473
+ - ✅ Free to use, modify, and distribute
474
+ - ✅ Must share source code of any modifications
475
+ - ✅ **Network use = distribution**: If you run a modified version as a service, you must share the source code
476
+ - ❌ Cannot use in proprietary SaaS without sharing modifications
477
+
478
+ For commercial licensing or if AGPL doesn't fit your use case, contact: farman20ali@gmail.com
432
479
 
433
480
  ## ⚠️ Important Notes
434
481
 
@@ -448,6 +495,38 @@ sudo kport -k 80
448
495
 
449
496
  On Windows, run your terminal as Administrator.
450
497
 
498
+ ### Stubborn processes that won't die
499
+
500
+ Some processes (especially Java applications) may not respond to graceful termination. Use the `--force` flag which automatically uses a multi-tier kill strategy (SIGTERM → SIGKILL → fuser):
501
+
502
+ ```bash
503
+ kport -k 8081 --force
504
+ ```
505
+
506
+ On Linux, `kport` will automatically use `fuser -k` as a fallback when standard kill methods fail. This is extremely effective for stubborn Java/Node/Python processes:
507
+
508
+ ```bash
509
+ # Install fuser for best results (Ubuntu/Debian)
510
+ sudo apt-get install psmisc
511
+
512
+ # Install fuser (RHEL/CentOS/Fedora)
513
+ sudo yum install psmisc
514
+
515
+ # Then kport will automatically use it when needed
516
+ kport -k 8081 --force
517
+ ```
518
+
519
+ **What happens:**
520
+ 1. First tries SIGTERM (graceful shutdown)
521
+ 2. Then tries SIGKILL after timeout
522
+ 3. Finally uses `fuser -k 8081/tcp` if process still lives (Linux only)
523
+
524
+ **Manual alternative:** You can also kill a port directly with fuser:
525
+ ```bash
526
+ # Kill all processes using port 8081 (requires sudo)
527
+ sudo fuser -k 8081/tcp
528
+ ```
529
+
451
530
  ### Port not found
452
531
 
453
532
  Make sure the port number is correct and that a process is actually using it. Use `kport -l` to see all active ports.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kport
3
- Version: 2.1.2
3
+ Version: 3.1.1
4
4
  Summary: A cross-platform command-line tool to inspect and kill processes using specific ports
5
5
  Home-page: https://github.com/farman20ali/port-killer
6
6
  Author: Farman Ali
@@ -13,7 +13,7 @@ Classifier: Intended Audience :: Developers
13
13
  Classifier: Intended Audience :: System Administrators
14
14
  Classifier: Topic :: System :: Networking
15
15
  Classifier: Topic :: System :: Systems Administration
16
- Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3
17
17
  Classifier: Programming Language :: Python :: 3
18
18
  Classifier: Programming Language :: Python :: 3.6
19
19
  Classifier: Programming Language :: Python :: 3.7
@@ -28,6 +28,7 @@ Classifier: Operating System :: MacOS
28
28
  Requires-Python: >=3.6
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
+ Requires-Dist: psutil>=5.9.0
31
32
  Dynamic: author
32
33
  Dynamic: author-email
33
34
  Dynamic: classifier
@@ -37,6 +38,7 @@ Dynamic: home-page
37
38
  Dynamic: keywords
38
39
  Dynamic: license-file
39
40
  Dynamic: project-url
41
+ Dynamic: requires-dist
40
42
  Dynamic: requires-python
41
43
  Dynamic: summary
42
44
 
@@ -44,7 +46,7 @@ Dynamic: summary
44
46
 
45
47
  [![Version](https://img.shields.io/badge/version-1.0.0-blue.svg)](https://github.com/farman20ali/port-killer)
46
48
  [![Python](https://img.shields.io/badge/python-3.6+-blue.svg)](https://www.python.org/downloads/)
47
- [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
49
+ [![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](LICENSE)
48
50
  [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)](https://github.com/farman20ali/port-killer)
49
51
 
50
52
  A simple, powerful command-line tool to inspect and kill processes using specific ports on Windows, Linux, and macOS.
@@ -155,8 +157,20 @@ On Linux, some ports may appear as `LISTEN` but the owning PID/process name is n
155
157
  If you see `local-unknown` in `inspect` / `explain`, try:
156
158
 
157
159
  ```bash
158
- sudo kport inspect 6379
159
- sudo kport explain 6379
160
+ sudo -E kport inspect 6379
161
+ sudo -E kport explain 6379
162
+ ```
163
+
164
+ If you installed with `pip install --user kport`, `sudo` may not find `kport` because root's `PATH` doesn't include your user scripts directory.
165
+
166
+ Alternatives:
167
+
168
+ ```bash
169
+ # Option 1: keep your PATH when using sudo
170
+ sudo -E "$HOME/.local/bin/kport" inspect 6379
171
+
172
+ # Option 2: run the module via the system python (when working from repo)
173
+ sudo -E python3 kport.py inspect 6379
160
174
  ```
161
175
 
162
176
  ### Config file support (Phase 2)
@@ -458,6 +472,33 @@ kport -i 80
458
472
  kport -l
459
473
  ```
460
474
 
475
+ ## 📚 Documentation
476
+
477
+ - **[Installation Guide](INSTALL.md)** - Detailed installation instructions
478
+ - **[Quick Start](QUICKSTART.md)** - Get started quickly
479
+ - **[Publishing Guide](PUBLISH.md)** - How to publish kport
480
+ - **[Release Guide](RELEASE_GUIDE.md)** - Creating releases (manual & automated)
481
+ - **[Debian Release](DEB_RELEASE.md)** - Debian packaging and APT distribution
482
+ - **[Contributing](CONTRIBUTING.md)** - How to contribute
483
+
484
+ ## 🚀 For Maintainers
485
+
486
+ ### Creating a Release
487
+
488
+ Automated release (recommended):
489
+
490
+ ```bash
491
+ python3 release.py
492
+ ```
493
+
494
+ This script handles:
495
+ - Git tagging
496
+ - PyPI package building
497
+ - Debian package building
498
+ - GitHub release creation
499
+
500
+ See [RELEASE_GUIDE.md](RELEASE_GUIDE.md) for manual release steps and troubleshooting.
501
+
461
502
  ## 🤝 Contributing
462
503
 
463
504
  Contributions are welcome! Please feel free to submit a Pull Request.
@@ -470,7 +511,15 @@ Contributions are welcome! Please feel free to submit a Pull Request.
470
511
 
471
512
  ## 📝 License
472
513
 
473
- This project is licensed under the MIT License - see the LICENSE file for details.
514
+ This project is licensed under the GNU Affero General Public License v3.0 - see the [LICENSE](LICENSE) file for details.
515
+
516
+ **What this means:**
517
+ - ✅ Free to use, modify, and distribute
518
+ - ✅ Must share source code of any modifications
519
+ - ✅ **Network use = distribution**: If you run a modified version as a service, you must share the source code
520
+ - ❌ Cannot use in proprietary SaaS without sharing modifications
521
+
522
+ For commercial licensing or if AGPL doesn't fit your use case, contact: farman20ali@gmail.com
474
523
 
475
524
  ## ⚠️ Important Notes
476
525
 
@@ -490,6 +539,38 @@ sudo kport -k 80
490
539
 
491
540
  On Windows, run your terminal as Administrator.
492
541
 
542
+ ### Stubborn processes that won't die
543
+
544
+ Some processes (especially Java applications) may not respond to graceful termination. Use the `--force` flag which automatically uses a multi-tier kill strategy (SIGTERM → SIGKILL → fuser):
545
+
546
+ ```bash
547
+ kport -k 8081 --force
548
+ ```
549
+
550
+ On Linux, `kport` will automatically use `fuser -k` as a fallback when standard kill methods fail. This is extremely effective for stubborn Java/Node/Python processes:
551
+
552
+ ```bash
553
+ # Install fuser for best results (Ubuntu/Debian)
554
+ sudo apt-get install psmisc
555
+
556
+ # Install fuser (RHEL/CentOS/Fedora)
557
+ sudo yum install psmisc
558
+
559
+ # Then kport will automatically use it when needed
560
+ kport -k 8081 --force
561
+ ```
562
+
563
+ **What happens:**
564
+ 1. First tries SIGTERM (graceful shutdown)
565
+ 2. Then tries SIGKILL after timeout
566
+ 3. Finally uses `fuser -k 8081/tcp` if process still lives (Linux only)
567
+
568
+ **Manual alternative:** You can also kill a port directly with fuser:
569
+ ```bash
570
+ # Kill all processes using port 8081 (requires sudo)
571
+ sudo fuser -k 8081/tcp
572
+ ```
573
+
493
574
  ### Port not found
494
575
 
495
576
  Make sure the port number is correct and that a process is actually using it. Use `kport -l` to see all active ports.
@@ -6,4 +6,5 @@ kport.egg-info/PKG-INFO
6
6
  kport.egg-info/SOURCES.txt
7
7
  kport.egg-info/dependency_links.txt
8
8
  kport.egg-info/entry_points.txt
9
+ kport.egg-info/requires.txt
9
10
  kport.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ psutil>=5.9.0
@@ -381,6 +381,32 @@ class BaseInspector:
381
381
  """
382
382
  raise NotImplementedError()
383
383
 
384
+ def kill_port(self, port: int, graceful_timeout: float = 3.0, force: bool = False, dry_run: bool = False) -> Tuple[bool, str]:
385
+ """
386
+ Kill all processes using a specific port.
387
+ Default implementation finds PIDs and kills them one by one.
388
+ Can be overridden for more efficient port-based killing (e.g., fuser on Linux).
389
+ """
390
+ pids = self.find_pids_on_port(port)
391
+ if not pids:
392
+ return False, "No process found on port"
393
+
394
+ killed_count = 0
395
+ errors = []
396
+ for pid in pids:
397
+ ok, msg = self.kill_pid(pid, graceful_timeout, force, dry_run)
398
+ if ok:
399
+ killed_count += 1
400
+ else:
401
+ errors.append(f"PID {pid}: {msg}")
402
+
403
+ if killed_count == len(pids):
404
+ return True, f"Killed {killed_count} process(es)"
405
+ elif killed_count > 0:
406
+ return False, f"Killed {killed_count}/{len(pids)} process(es). Errors: {'; '.join(errors)}"
407
+ else:
408
+ return False, f"Failed to kill any process. Errors: {'; '.join(errors)}"
409
+
384
410
  # psutil-based inspector (best behavior cross-platform)
385
411
  class PsutilInspector(BaseInspector):
386
412
  def list_listening(self) -> List[PortBinding]:
@@ -551,6 +577,38 @@ class FallbackInspector(BaseInspector):
551
577
  except Exception:
552
578
  return None
553
579
 
580
+ def _kill_port_with_fuser(self, port: int, proto: str = "tcp", dry_run: bool = False) -> Tuple[bool, str]:
581
+ """Kill all processes using a port with fuser (Linux utility).
582
+
583
+ This is often more reliable than kill -9 for stubborn processes.
584
+ """
585
+ if dry_run:
586
+ return True, f"Dry-run: would fuser -k {port}/{proto}"
587
+
588
+ if not check_dependency("fuser"):
589
+ return False, "fuser not available"
590
+
591
+ try:
592
+ # fuser -k sends SIGKILL by default to all processes using the port
593
+ proc = subprocess.run(
594
+ ["fuser", "-k", f"{port}/{proto}"],
595
+ capture_output=True,
596
+ text=True,
597
+ timeout=5
598
+ )
599
+ # fuser returns 0 on success, 1 if no process found
600
+ if proc.returncode == 0:
601
+ return True, f"Killed process(es) using port {port} with fuser"
602
+ elif proc.returncode == 1:
603
+ return False, "No process found on port"
604
+ else:
605
+ stderr = (proc.stderr or "").strip()
606
+ return False, f"fuser failed: {stderr}"
607
+ except subprocess.TimeoutExpired:
608
+ return False, "fuser command timed out"
609
+ except Exception as e:
610
+ return False, f"Error running fuser: {e}"
611
+
554
612
  def list_listening(self) -> List[PortBinding]:
555
613
  if self.system == "Windows":
556
614
  return self._windows_listening()
@@ -907,7 +965,7 @@ class FallbackInspector(BaseInspector):
907
965
  return False, f"Error taskkill: {e}"
908
966
  return False, "Still running; taskkill gentle failed"
909
967
  else:
910
- # Unix: try SIGTERM then SIGKILL
968
+ # Unix: try SIGTERM then SIGKILL, with multiple attempts
911
969
  try:
912
970
  os.kill(pid, signal.SIGTERM)
913
971
  except PermissionError:
@@ -932,19 +990,46 @@ class FallbackInspector(BaseInspector):
932
990
  return False, "Permission denied"
933
991
  if not force:
934
992
  return False, "Still alive after graceful timeout"
935
- # force kill
993
+ # force kill with SIGKILL
936
994
  try:
937
995
  os.kill(pid, signal.SIGKILL)
938
- return True, "Killed (SIGKILL)"
996
+ # Wait briefly to confirm kill
997
+ time.sleep(0.5)
998
+ try:
999
+ os.kill(pid, 0)
1000
+ # Process still exists, might need more aggressive approach
1001
+ except ProcessLookupError:
1002
+ return True, "Killed (SIGKILL)"
1003
+ except PermissionError:
1004
+ return False, "Permission denied"
939
1005
  except PermissionError:
940
1006
  return False, "Permission denied on SIGKILL"
941
1007
  except ProcessLookupError:
942
- return True, "Process disappeared after SIGKILL"
1008
+ return True, "Process disappeared"
943
1009
  except Exception as e:
944
1010
  return False, f"Error SIGKILL: {e}"
1011
+
1012
+ # If still running, return status indicating process is stubborn
1013
+ return False, "Process did not terminate after SIGKILL"
945
1014
  except Exception as e:
946
1015
  return False, f"Unexpected error: {e}"
947
1016
 
1017
+ def kill_port(self, port: int, graceful_timeout: float = 3.0, force: bool = False, dry_run: bool = False) -> Tuple[bool, str]:
1018
+ """
1019
+ Kill all processes using a specific port.
1020
+ On Linux, tries fuser first as it's more reliable, then falls back to default implementation.
1021
+ """
1022
+ if self.system != "Windows":
1023
+ # Try fuser first on Unix-like systems when force=True or as fallback
1024
+ if force and check_dependency("fuser"):
1025
+ ok, msg = self._kill_port_with_fuser(port, "tcp", dry_run)
1026
+ if ok:
1027
+ return ok, msg
1028
+ # If fuser fails, fall through to default implementation
1029
+
1030
+ # Use default implementation: find PIDs and kill them
1031
+ return super().kill_port(port, graceful_timeout, force, dry_run)
1032
+
948
1033
  # Factory
949
1034
  def get_inspector() -> BaseInspector:
950
1035
  if USING_PSUTIL:
@@ -1291,6 +1376,23 @@ def handle_product_command(args: argparse.Namespace, inspector: BaseInspector) -
1291
1376
  out_killed.append({"pid": pid, "msg": msg})
1292
1377
  else:
1293
1378
  out_failed.append({"pid": pid, "msg": msg})
1379
+
1380
+ # If any processes failed to kill and fuser is available on Linux, try as last resort
1381
+ if out_failed and isinstance(inspector, FallbackInspector) and inspector.system != "Windows" and check_dependency("fuser"):
1382
+ if not args.json:
1383
+ print(colorize(f"\n⚠ Some processes couldn't be killed. Trying fuser as fallback...", Colors.YELLOW))
1384
+ fuser_ok, fuser_msg = inspector._kill_port_with_fuser(args.port, "tcp", dry_run=args.dry_run)
1385
+ if fuser_ok:
1386
+ # Re-check if processes are gone
1387
+ remaining_pids = inspector.find_pids_on_port(args.port)
1388
+ if not remaining_pids:
1389
+ # Success! Mark all failed as killed
1390
+ for f in out_failed:
1391
+ out_killed.append({"pid": f["pid"], "msg": f"{f['msg']} → killed with fuser"})
1392
+ out_failed = []
1393
+ if not args.json:
1394
+ print(colorize(f"✓ {fuser_msg}", Colors.GREEN))
1395
+
1294
1396
  if args.json:
1295
1397
  print(json.dumps({"port": args.port, "killed": out_killed, "failed": out_failed}, indent=2))
1296
1398
  else:
@@ -1400,9 +1502,9 @@ Examples:
1400
1502
  parser.add_argument("-kr", "--kill-range", type=str, metavar="RANGE", help="Kill processes on port range (e.g., 3000-3010)")
1401
1503
  parser.add_argument("-l", "--list", action="store_true", help="List all listening ports and their processes")
1402
1504
  parser.add_argument("--exact", action="store_true", help="Use exact match for process name lookups")
1403
- parser.add_argument("--force", action="store_true", help="Force kill immediately if needed (after graceful timeout)")
1505
+ parser.add_argument("--force", action="store_true", help="Force kill immediately if needed (uses SIGKILL/fuser on Linux for stubborn processes)")
1404
1506
  parser.add_argument("--graceful-timeout", type=float, default=3.0, help="Seconds to wait for graceful termination before forcing (default 3.0)")
1405
- parser.add_argument("-v", "--version", action="version", version="kport 3.0.0 (upgraded)")
1507
+ parser.add_argument("-v", "--version", action="version", version="kport 3.1.1")
1406
1508
 
1407
1509
  # PRODUCT.md subcommands
1408
1510
  sub = parser.add_subparsers(dest="command")
@@ -8,7 +8,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
8
8
 
9
9
  setup(
10
10
  name="kport",
11
- version="2.1.2",
11
+ version="3.1.1",
12
12
  author="Farman Ali",
13
13
  author_email="farman20ali@gmail.com",
14
14
  description="A cross-platform command-line tool to inspect and kill processes using specific ports",
@@ -22,7 +22,7 @@ setup(
22
22
  "Intended Audience :: System Administrators",
23
23
  "Topic :: System :: Networking",
24
24
  "Topic :: System :: Systems Administration",
25
- "License :: OSI Approved :: MIT License",
25
+ "License :: OSI Approved :: GNU Affero General Public License v3",
26
26
  "Programming Language :: Python :: 3",
27
27
  "Programming Language :: Python :: 3.6",
28
28
  "Programming Language :: Python :: 3.7",
@@ -36,6 +36,9 @@ setup(
36
36
  "Operating System :: MacOS",
37
37
  ],
38
38
  python_requires=">=3.6",
39
+ install_requires=[
40
+ "psutil>=5.9.0",
41
+ ],
39
42
  entry_points={
40
43
  "console_scripts": [
41
44
  "kport=kport:main",
kport-2.1.2/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 kport
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
File without changes