Skip to content

Commit e44dbbc

Browse files
fix: firewall shutdown on ctrl+c signal
1 parent df47bff commit e44dbbc

2 files changed

Lines changed: 52 additions & 20 deletions

File tree

main.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@
1010
import signal
1111
import traceback
1212
from pathlib import Path
13+
import threading
14+
import time
1315

1416
# Add src directory to Python path for absolute imports
1517
current_dir = Path(__file__).parent
1618
src_dir = current_dir / 'src'
1719
sys.path.insert(0, str(src_dir))
20+
_SHUTDOWN_INITIATED = False
21+
_SHUTDOWN_LOCK = threading.Lock()
1822

1923
try:
2024
from firewall.core import SimpleFirewall
@@ -148,15 +152,34 @@ def main():
148152

149153
# Setup signal handlers for graceful shutdown
150154
def signal_handler(signum, frame):
151-
print(f"\n{Fore.YELLOW}Signal {signum} received, shutting down...{Style.RESET_ALL}")
152-
firewall.stop()
153-
sys.exit(0)
154-
155-
signal.signal(signal.SIGINT, signal_handler)
156-
signal.signal(signal.SIGTERM, signal_handler)
157-
158-
# Start the firewall
159-
firewall.start()
155+
"""Enhanced signal handler with timeout and state tracking"""
156+
global _SHUTDOWN_INITIATED
157+
158+
with _SHUTDOWN_LOCK:
159+
if _SHUTDOWN_INITIATED:
160+
print(f"\n{Fore.RED}Forced shutdown initiated...{Style.RESET_ALL}")
161+
os._exit(1)
162+
163+
_SHUTDOWN_INITIATED = True
164+
165+
print(f"\n{Fore.YELLOW}Signal {signum} received, initiating graceful shutdown...{Style.RESET_ALL}")
166+
167+
def force_shutdown():
168+
time.sleep(5)
169+
print(f"\n{Fore.RED}Graceful shutdown timeout, forcing exit...{Style.RESET_ALL}")
170+
os._exit(1)
171+
172+
force_thread = threading.Thread(target=force_shutdown, daemon=True)
173+
force_thread.start()
174+
175+
try:
176+
if 'firewall' in locals():
177+
firewall.stop()
178+
print(f"{Fore.GREEN}Firewall shutdown completed successfully.{Style.RESET_ALL}")
179+
sys.exit(0)
180+
except Exception as e:
181+
print(f"{Fore.RED}Error during shutdown: {e}{Style.RESET_ALL}")
182+
os._exit(1)
160183

161184
except KeyboardInterrupt:
162185
print(f"\n{Fore.YELLOW}Interrupted by user{Style.RESET_ALL}")

src/firewall/core.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import time
55
import signal
66
import sys
7-
from scapy.all import sniff
7+
from scapy.all import sniff,AsyncSniffer
88
from colorama import Fore, Style
99
from typing import Optional
1010

@@ -41,6 +41,9 @@ def __init__(self, interface: str = None, config_file: str = None):
4141
# Control flags
4242
self.running = False
4343
self._threads = []
44+
45+
self._sniffer = None
46+
self._stop_event = threading.Event()
4447

4548
self.logger.info("Simple Firewall initialized successfully")
4649

@@ -167,26 +170,32 @@ def start(self):
167170
self.stop()
168171

169172
def stop(self):
170-
"""Stop the firewall and cleanup"""
173+
"""Stop the firewall and cleanup - ENHANCED VERSION"""
174+
if not self.running:
175+
return
176+
171177
print(f"\n{Fore.YELLOW}Stopping firewall...{Style.RESET_ALL}")
172178
self.running = False
179+
self._stop_event.set()
180+
181+
if self._sniffer and self._sniffer.running:
182+
self._sniffer.stop()
183+
184+
timeout = 3.0
185+
start_time = time.time()
173186

174-
# Wait for threads to finish (with timeout)
175187
for thread in self._threads:
176188
if thread.is_alive():
177-
thread.join(timeout=2.0)
189+
remaining_time = timeout - (time.time() - start_time)
190+
if remaining_time > 0:
191+
thread.join(timeout=remaining_time)
178192

179-
# Display final stats
180-
self._display_stats()
181-
182-
# Cleanup firewall rules
183-
print(f"{Fore.YELLOW}Cleaning up firewall rules...{Style.RESET_ALL}")
184193
cleaned_ips = self.blocker.cleanup_all_blocks()
185194
if cleaned_ips:
186195
self.logger.info(f"Cleaned up blocks for {len(cleaned_ips)} IPs")
187196

188-
print(f"{Fore.GREEN}Firewall stopped.{Style.RESET_ALL}")
189-
self.logger.info("Simple Firewall stopped")
197+
self._display_stats()
198+
print(f"{Fore.GREEN}Firewall stopped successfully.{Style.RESET_ALL}")
190199

191200
def get_status(self) -> dict:
192201
"""Get current firewall status"""

0 commit comments

Comments
 (0)