118 lines
4.7 KiB
Python
118 lines
4.7 KiB
Python
"""
|
||
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()
|