230 lines
8.1 KiB
Python
230 lines
8.1 KiB
Python
#!/usr/bin/env python3
|
||
# server_manager.py - Скрипт для управления сервером в фоновом режиме
|
||
|
||
import os
|
||
import sys
|
||
import signal
|
||
import subprocess
|
||
import time
|
||
import atexit
|
||
from pathlib import Path
|
||
|
||
# Конфигурация
|
||
PID_FILE = "server.pid"
|
||
LOG_FILE = "server.log"
|
||
ERROR_LOG = "server_error.log"
|
||
PYTHON_CMD = sys.executable
|
||
SERVER_SCRIPT = "server.py"
|
||
|
||
|
||
class ServerManager:
|
||
def __init__(self):
|
||
self.pid_file = Path(PID_FILE)
|
||
self.log_file = Path(LOG_FILE)
|
||
self.error_log = Path(ERROR_LOG)
|
||
|
||
def is_running(self):
|
||
"""Проверка, запущен ли сервер"""
|
||
if not self.pid_file.exists():
|
||
return False
|
||
|
||
try:
|
||
with open(self.pid_file, 'r') as f:
|
||
pid = int(f.read().strip())
|
||
|
||
# Проверяем, существует ли процесс
|
||
os.kill(pid, 0)
|
||
return True
|
||
except (OSError, ValueError, ProcessLookupError):
|
||
# PID файл существует, но процесс не найден
|
||
self.pid_file.unlink(missing_ok=True)
|
||
return False
|
||
|
||
def get_pid(self):
|
||
"""Получить PID сервера"""
|
||
if not self.pid_file.exists():
|
||
return None
|
||
|
||
try:
|
||
with open(self.pid_file, 'r') as f:
|
||
return int(f.read().strip())
|
||
except:
|
||
return None
|
||
|
||
def start(self):
|
||
"""Запуск сервера в фоновом режиме"""
|
||
if self.is_running():
|
||
print(f"❌ Сервер уже запущен (PID: {self.get_pid()})")
|
||
return False
|
||
|
||
print("🚀 Запуск сервера в фоновом режиме...")
|
||
|
||
try:
|
||
# Открываем файлы для логов
|
||
log_fd = open(self.log_file, 'a')
|
||
err_fd = open(self.error_log, 'a')
|
||
|
||
# Запускаем процесс
|
||
process = subprocess.Popen(
|
||
[PYTHON_CMD, SERVER_SCRIPT],
|
||
stdout=log_fd,
|
||
stderr=err_fd,
|
||
stdin=subprocess.DEVNULL,
|
||
start_new_session=True, # Отдельная сессия для фонового процесса
|
||
cwd=os.path.dirname(os.path.abspath(__file__))
|
||
)
|
||
|
||
# Сохраняем PID
|
||
with open(self.pid_file, 'w') as f:
|
||
f.write(str(process.pid))
|
||
|
||
# Небольшая задержка для проверки запуска
|
||
time.sleep(2)
|
||
|
||
if self.is_running():
|
||
print(f"✅ Сервер успешно запущен (PID: {process.pid})")
|
||
print(f"📝 Логи: {self.log_file}")
|
||
print(f"❌ Логи ошибок: {self.error_log}")
|
||
return True
|
||
else:
|
||
print("❌ Сервер не запустился")
|
||
return False
|
||
|
||
except Exception as e:
|
||
print(f"❌ Ошибка при запуске: {e}")
|
||
return False
|
||
|
||
def stop(self):
|
||
"""Остановка сервера"""
|
||
if not self.is_running():
|
||
print("❌ Сервер не запущен")
|
||
return False
|
||
|
||
pid = self.get_pid()
|
||
print(f"🛑 Остановка сервера (PID: {pid})...")
|
||
|
||
try:
|
||
# Отправляем SIGTERM
|
||
os.kill(pid, signal.SIGTERM)
|
||
|
||
# Ждем завершения
|
||
timeout = 10
|
||
for _ in range(timeout):
|
||
time.sleep(1)
|
||
if not self.is_running():
|
||
print("✅ Сервер остановлен")
|
||
self.pid_file.unlink(missing_ok=True)
|
||
return True
|
||
|
||
# Если не остановился, принудительно завершаем
|
||
print("⚠️ Сервер не отвечает, принудительное завершение...")
|
||
os.kill(pid, signal.SIGKILL)
|
||
time.sleep(1)
|
||
|
||
if not self.is_running():
|
||
print("✅ Сервер принудительно остановлен")
|
||
self.pid_file.unlink(missing_ok=True)
|
||
return True
|
||
else:
|
||
print("❌ Не удалось остановить сервер")
|
||
return False
|
||
|
||
except ProcessLookupError:
|
||
print("⚠️ Процесс не найден, удаляем PID файл")
|
||
self.pid_file.unlink(missing_ok=True)
|
||
return True
|
||
except Exception as e:
|
||
print(f"❌ Ошибка при остановке: {e}")
|
||
return False
|
||
|
||
def restart(self):
|
||
"""Перезапуск сервера"""
|
||
print("🔄 Перезапуск сервера...")
|
||
if self.is_running():
|
||
self.stop()
|
||
time.sleep(2)
|
||
return self.start()
|
||
|
||
def status(self):
|
||
"""Статус сервера"""
|
||
if self.is_running():
|
||
pid = self.get_pid()
|
||
print(f"✅ Сервер запущен (PID: {pid})")
|
||
print(f"📝 Логи: {self.log_file} ({self.log_file.stat().st_size} bytes)")
|
||
print(
|
||
f"❌ Логи ошибок: {self.error_log} ({self.error_log.stat().st_size if self.error_log.exists() else 0} bytes)")
|
||
|
||
# Показываем последние строки лога
|
||
if self.log_file.exists():
|
||
print("\n📋 Последние строки лога:")
|
||
with open(self.log_file, 'r') as f:
|
||
lines = f.readlines()
|
||
for line in lines[-5:]:
|
||
print(f" {line.strip()}")
|
||
return True
|
||
else:
|
||
print("❌ Сервер не запущен")
|
||
return False
|
||
|
||
def logs(self, lines=20):
|
||
"""Показать последние строки лога"""
|
||
if not self.log_file.exists():
|
||
print("📝 Лог файл не найден")
|
||
return
|
||
|
||
print(f"📋 Последние {lines} строк лога:")
|
||
print("-" * 50)
|
||
with open(self.log_file, 'r') as f:
|
||
all_lines = f.readlines()
|
||
for line in all_lines[-lines:]:
|
||
print(line.strip())
|
||
|
||
def errors(self, lines=20):
|
||
"""Показать последние строки лога ошибок"""
|
||
if not self.error_log.exists():
|
||
print("❌ Лог ошибок не найден")
|
||
return
|
||
|
||
print(f"⚠️ Последние {lines} строк лога ошибок:")
|
||
print("-" * 50)
|
||
with open(self.error_log, 'r') as f:
|
||
all_lines = f.readlines()
|
||
for line in all_lines[-lines:]:
|
||
print(line.strip())
|
||
|
||
|
||
def main():
|
||
manager = ServerManager()
|
||
|
||
if len(sys.argv) < 2:
|
||
print("Использование:")
|
||
print(" python server_manager.py start - запустить сервер")
|
||
print(" python server_manager.py stop - остановить сервер")
|
||
print(" python server_manager.py restart - перезапустить сервер")
|
||
print(" python server_manager.py status - статус сервера")
|
||
print(" python server_manager.py logs - показать логи")
|
||
print(" python server_manager.py errors - показать ошибки")
|
||
return
|
||
|
||
command = sys.argv[1].lower()
|
||
|
||
if command == 'start':
|
||
manager.start()
|
||
elif command == 'stop':
|
||
manager.stop()
|
||
elif command == 'restart':
|
||
manager.restart()
|
||
elif command == 'status':
|
||
manager.status()
|
||
elif command == 'logs':
|
||
lines = int(sys.argv[2]) if len(sys.argv) > 2 else 20
|
||
manager.logs(lines)
|
||
elif command == 'errors':
|
||
lines = int(sys.argv[2]) if len(sys.argv) > 2 else 20
|
||
manager.errors(lines)
|
||
else:
|
||
print(f"❌ Неизвестная команда: {command}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |