""" PID менеджер для управления PID файлом процесса """ import os import sys from pathlib import Path from typing import Optional from loguru import logger class PIDManager: """Менеджер для управления PID файлом процесса""" def __init__(self, service_name: str = "anon_bot", pid_dir: str = "/tmp"): self.service_name = service_name self.pid_dir = Path(pid_dir) self.pid_file_path = self.pid_dir / f"{service_name}.pid" self.pid: Optional[int] = None def create_pid_file(self) -> bool: """Создать PID файл""" try: # Создаем директорию для PID файлов, если она не существует self.pid_dir.mkdir(parents=True, exist_ok=True) # Проверяем, не запущен ли уже процесс if self.pid_file_path.exists(): try: with open(self.pid_file_path, 'r') as f: existing_pid = int(f.read().strip()) # Проверяем, жив ли процесс с этим PID if self._is_process_running(existing_pid): logger.error(f"Процесс {self.service_name} уже запущен с PID {existing_pid}") return False else: logger.warning(f"Найден устаревший PID файл для {existing_pid}, удаляем его") self.pid_file_path.unlink() except (ValueError, OSError) as e: logger.warning(f"Не удалось прочитать существующий PID файл: {e}, удаляем его") self.pid_file_path.unlink() # Получаем PID текущего процесса self.pid = os.getpid() # Создаем PID файл with open(self.pid_file_path, 'w') as f: f.write(str(self.pid)) logger.info(f"PID файл создан: {self.pid_file_path} (PID: {self.pid})") return True except Exception as e: logger.error(f"Не удалось создать PID файл: {e}") return False def cleanup_pid_file(self) -> None: """Очистить PID файл""" try: if self.pid_file_path.exists(): # Проверяем, что PID файл принадлежит нашему процессу with open(self.pid_file_path, 'r') as f: file_pid = int(f.read().strip()) if file_pid == self.pid: self.pid_file_path.unlink() logger.info(f"PID файл удален: {self.pid_file_path}") else: logger.warning(f"PID файл содержит другой PID ({file_pid}), не удаляем") except Exception as e: logger.error(f"Ошибка при удалении PID файла: {e}") def get_pid(self) -> Optional[int]: """Получить PID процесса""" return self.pid def get_pid_file_path(self) -> Path: """Получить путь к PID файлу""" return self.pid_file_path def _is_process_running(self, pid: int) -> bool: """Проверить, запущен ли процесс с указанным PID""" try: # В Unix-системах отправляем сигнал 0 для проверки существования процесса os.kill(pid, 0) return True except (OSError, ProcessLookupError): return False # Глобальный экземпляр PID менеджера _pid_manager: Optional[PIDManager] = None def get_pid_manager(service_name: str = "anon_bot", pid_dir: str = "/tmp") -> PIDManager: """Получить экземпляр PID менеджера""" global _pid_manager if _pid_manager is None: _pid_manager = PIDManager(service_name, pid_dir) return _pid_manager def create_pid_file(service_name: str = "anon_bot", pid_dir: str = "/tmp") -> bool: """Создать PID файл""" pid_manager = get_pid_manager(service_name, pid_dir) return pid_manager.create_pid_file() def cleanup_pid_file() -> None: """Очистить PID файл""" if _pid_manager: _pid_manager.cleanup_pid_file()