j
This commit is contained in:
@@ -44,6 +44,8 @@ import sys
|
||||
import json
|
||||
import argparse
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any
|
||||
from datetime import datetime
|
||||
@@ -81,6 +83,46 @@ except ImportError:
|
||||
# ============================================================================
|
||||
|
||||
|
||||
def monitor_parent(parent_pid: int):
|
||||
"""Monitor the parent process and shut down if it dies."""
|
||||
if parent_pid <= 1:
|
||||
return
|
||||
|
||||
logger.info(f"Monitoring parent process {parent_pid}")
|
||||
|
||||
# On Windows, we might need a different approach if os.kill(pid, 0) is unreliable
|
||||
is_windows = sys.platform == "win32"
|
||||
|
||||
while True:
|
||||
try:
|
||||
if is_windows:
|
||||
# OpenProcess with PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
|
||||
# This is safer than os.kill on Windows for existence checks
|
||||
import ctypes
|
||||
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
|
||||
handle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, parent_pid)
|
||||
if handle:
|
||||
exit_code = ctypes.c_ulong()
|
||||
ctypes.windll.kernel32.GetExitCodeProcess(handle, ctypes.byref(exit_code))
|
||||
ctypes.windll.kernel32.CloseHandle(handle)
|
||||
# STILL_ACTIVE is 259
|
||||
if exit_code.value != 259:
|
||||
logger.info(f"Parent process {parent_pid} finished with code {exit_code.value}. Shutting down...")
|
||||
os._exit(0)
|
||||
else:
|
||||
# On Windows, sometimes we lose access to the handle if the parent is transitioning
|
||||
# or if it was started from a shell that already closed.
|
||||
# We'll ignore handle failures for now unless we want to be very strict.
|
||||
pass
|
||||
else:
|
||||
os.kill(parent_pid, 0)
|
||||
except Exception as e:
|
||||
# Parent is dead or inaccessible
|
||||
logger.info(f"Parent process {parent_pid} no longer accessible: {e}. Shutting down server...")
|
||||
os._exit(0)
|
||||
time.sleep(5) # Increase check interval to be less aggressive
|
||||
|
||||
|
||||
def get_local_ip() -> Optional[str]:
|
||||
"""Get the local IP address that would be used for external connections."""
|
||||
import socket
|
||||
@@ -594,9 +636,25 @@ def main():
|
||||
help="API key for authentication (optional)"
|
||||
)
|
||||
parser.add_argument("--debug", action="store_true", help="Enable debug mode")
|
||||
parser.add_argument(
|
||||
"--monitor",
|
||||
action="store_true",
|
||||
help="Shut down if parent process dies"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Start monitor thread if requested
|
||||
if args.monitor:
|
||||
ppid = os.getppid()
|
||||
if ppid > 1:
|
||||
monitor_thread = threading.Thread(
|
||||
target=monitor_parent,
|
||||
args=(ppid, ),
|
||||
daemon=True
|
||||
)
|
||||
monitor_thread.start()
|
||||
|
||||
global STORAGE_PATH, API_KEY
|
||||
STORAGE_PATH = Path(args.storage_path).resolve()
|
||||
API_KEY = args.api_key
|
||||
|
||||
Reference in New Issue
Block a user