#!/usr/bin/env python3 """ Memory Sentinel Script This script monitors the system's RAM usage and kills the process with the highest memory consumption if available RAM drops below 5GB. Usage: python memory_sentinel.py The script runs continuously, checking memory usage every second. Script was generated by Claude 3.7 with the following prompt: I'm working on a server and I have one script that sometimes consumes so much memory that the entire server becomes unresponsive. This is a hue problem because then I cannot log in to it anymore. Could you write a sentinel script in python that does the following: Check the total available RAM of the system (disregarding swap) Check the currently used RAM of the system If we have more than 5G left, do nothing Else, find the process with the highest RAM consumption and kill it. Note: Use the SIGKILL command Check every second """ import logging import os import signal import time import psutil # Set up logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s", filename="memory_sentinel.log", filemode="a" ) # Memory threshold in GB MEMORY_THRESHOLD_GB = 5 # Convert to bytes for comparison MEMORY_THRESHOLD_BYTES = MEMORY_THRESHOLD_GB * 1024 * 1024 * 1024 def get_available_ram(): """Get the available RAM in bytes, excluding swap.""" return psutil.virtual_memory().available def get_total_ram(): """Get the total RAM in bytes, excluding swap.""" return psutil.virtual_memory().total def get_used_ram(): """Get the used RAM in bytes.""" return psutil.virtual_memory().used def get_process_with_highest_memory(): """Find the process with the highest memory consumption.""" processes = [] for proc in psutil.process_iter(["pid", "name", "memory_info"]): try: processes.append((proc.info["pid"], proc.info["name"], proc.info["memory_info"].rss)) except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): pass # Sort by memory usage (descending) processes.sort(key=lambda x: x[2], reverse=True) if processes: return processes[0] return None def kill_process(pid): """Kill a process using SIGKILL.""" try: os.kill(pid, signal.SIGKILL) return True except OSError as e: logging.error(f"Failed to kill process {pid}: {e}") return False def format_bytes(bytes_value): """Format bytes to a human-readable string.""" for unit in ["B", "KB", "MB", "GB", "TB"]: if bytes_value < 1024.0: return f"{bytes_value:.2f} {unit}" bytes_value /= 1024.0 return f"{bytes_value:.2f} PB" def main(): """Main function that runs the memory monitoring loop.""" logging.info("Memory Sentinel started") logging.info(f"Memory threshold set to {MEMORY_THRESHOLD_GB}GB") try: while True: available_ram = get_available_ram() total_ram = get_total_ram() used_ram = get_used_ram() logging.debug( f"Total RAM: {format_bytes(total_ram)}, " + f"Used RAM: {format_bytes(used_ram)}, " + f"Available RAM: {format_bytes(available_ram)}" ) if available_ram < MEMORY_THRESHOLD_BYTES: logging.warning( f"Available RAM ({format_bytes(available_ram)}) " + f"below threshold of {MEMORY_THRESHOLD_GB}GB" ) process = get_process_with_highest_memory() if process: pid, name, memory = process logging.warning(f"Killing process {pid} ({name}) " + f"using {format_bytes(memory)}") if kill_process(pid): logging.info(f"Successfully killed process {pid} ({name})") else: logging.error(f"Failed to kill process {pid} ({name})") else: logging.warning("No process found to kill") # Sleep for 1 second time.sleep(1) except KeyboardInterrupt: logging.info("Memory Sentinel stopped by user") except Exception as e: logging.error(f"Unexpected error: {e}") raise if __name__ == "__main__": main()