From 7f3f33e0bfca796d53f2114981bf5b232fa9fdb4 Mon Sep 17 00:00:00 2001 From: Justin Bollinger Date: Tue, 10 Mar 2026 14:03:30 -0400 Subject: [PATCH] feat: return to main menu on double Ctrl+C within 2 seconds Install a custom SIGINT handler that tracks interrupt timing. A single Ctrl+C raises KeyboardInterrupt as before (kills the current subprocess and continues). A second Ctrl+C within 2 seconds raises DoubleInterrupt, which bypasses all existing per-subprocess handlers and is caught at the main menu loop, printing "[!] Returning to main menu..." and resuming the menu. Preprocessing and cleanup sections also catch DoubleInterrupt to ensure temp file cleanup runs before re-raising. --- hate_crack/main.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/hate_crack/main.py b/hate_crack/main.py index eaac628..356cbd4 100755 --- a/hate_crack/main.py +++ b/hate_crack/main.py @@ -16,8 +16,10 @@ import glob import random import re import readline +import signal import subprocess import shlex +import time import argparse import urllib.request import urllib.error @@ -76,6 +78,23 @@ except ImportError: display_rule_opcodes_summary = None +_DOUBLE_INTERRUPT_WINDOW = 2.0 +_last_interrupt_time: float = 0.0 + + +class DoubleInterrupt(Exception): + """Raised when Ctrl+C is pressed twice within _DOUBLE_INTERRUPT_WINDOW seconds.""" + + +def _sigint_handler(signum: int, frame: Any) -> None: + global _last_interrupt_time + now = time.time() + if now - _last_interrupt_time <= _DOUBLE_INTERRUPT_WINDOW: + raise DoubleInterrupt() + _last_interrupt_time = now + raise KeyboardInterrupt() + + def _has_hate_crack_assets(path): if not path: return False @@ -2490,6 +2509,9 @@ def cleanup(): os.remove(hcatHashFileOrig + ".working") if os.path.exists(hcatHashFileOrig + ".passwords"): os.remove(hcatHashFileOrig + ".passwords") + except DoubleInterrupt: + cleanup() + raise except KeyboardInterrupt: # incase someone mashes the Control+C it will still cleanup cleanup() @@ -3455,6 +3477,8 @@ def main(): global pipalPath, maxruntime, bandrelbasewords global hcatPotfilePath + signal.signal(signal.SIGINT, _sigint_handler) + # Initialize global variables hcatHashFile = None hcatHashType = None @@ -4043,6 +4067,10 @@ def main(): except OSError: pass _preprocessing_temp_files.remove(dedup_path) + except DoubleInterrupt: + print("\nPreprocessing interrupted. Cleaning up temp files...") + _cleanup_preprocessing_temps() + raise except KeyboardInterrupt: print("\nPreprocessing interrupted. Cleaning up temp files...") _cleanup_preprocessing_temps() @@ -4099,6 +4127,8 @@ def main(): options[task]() except KeyError: pass + except DoubleInterrupt: + print("\n[!] Returning to main menu...") except KeyboardInterrupt: quit_hc()