Fixed orphaned processes and added logging

This commit is contained in:
Malasaur 2025-12-16 23:57:35 +01:00
parent 17fc911290
commit cd2eb8de9d
No known key found for this signature in database
6 changed files with 177 additions and 46 deletions

View file

@ -2,11 +2,13 @@ import shlex
from collections import deque
from pathlib import Path
from subprocess import PIPE, Popen
from threading import Lock
from time import sleep
from typing import Literal
from classes import ProcessStatus, ServerStatus
from config import Config
from logs import logger
from mcrcon import MCRcon
from mcstatus import JavaServer
@ -18,38 +20,65 @@ class ProcessController:
self.last_status: Literal[ProcessStatus.STOPPED, ProcessStatus.CRASHED] = (
ProcessStatus.STOPPED
)
self._lock = Lock()
def start(self) -> None:
"Start the process."
if self.status() == ProcessStatus.RUNNING:
return
logger.debug("ProcessController.start()")
with self._lock:
if self.status() == ProcessStatus.RUNNING:
if self.process:
logger.debug(
"ProcessController.start() - Process was already running with PID: %s",
self.process.pid,
)
return
self.process = Popen(
self.start_command,
stdout=PIPE,
stderr=PIPE,
)
self.process = Popen(
self.start_command,
stdout=PIPE,
stderr=PIPE,
)
logger.info(
"ProcessController.start() - Started process with PID: %s",
self.process.pid,
)
def status(self) -> ProcessStatus:
"Check the process' status."
logger.debug("ProcessController.status()")
if not self.process:
logger.debug("ProcessController.status() => %s", self.last_status)
return self.last_status
match self.process.poll():
case None:
logger.debug("ProcessController.status() => ProcessStatus.RUNNING")
return ProcessStatus.RUNNING
case 0:
logger.debug("ProcessController.status() => ProcessStatus.STOPPED")
self.last_status = ProcessStatus.STOPPED
return ProcessStatus.STOPPED
case _:
logger.debug("ProcessController.status() => ProcessStatus.CRASHED")
self.last_status = ProcessStatus.CRASHED
return ProcessStatus.CRASHED
def kill(self) -> None:
"Kill the process."
if self.process:
self.process.terminate()
self.process = None
self.last_status = ProcessStatus.STOPPED
logger.debug("ProcessController.kill()")
with self._lock:
if self.process:
pid = self.process.pid
self.process.kill()
code = self.process.wait()
self.process = None
logger.info(
"ProcessController.kill() - Process with PID %s killed with return code: %s",
pid,
code,
)
self.last_status = ProcessStatus.STOPPED
class ServerController:
@ -65,9 +94,11 @@ class ServerController:
)
def status(self) -> ServerStatus:
logger.debug("ServerController.status()")
try:
status = self.server.status()
except Exception:
logger.debug("ServerController.status() - Server is offline")
return {"online": False}
players = []
@ -75,6 +106,7 @@ class ServerController:
for player in status.players.sample:
players.append(player.name)
logger.debug("ServerController.status() - Server is online")
return {
"online": True,
"icon": status.icon,
@ -87,12 +119,18 @@ class ServerController:
}
def command(self, command: str) -> str:
logger.debug('ServerController.command(command="%s")', command)
try:
self.rcon.connect()
output = self.rcon.command(command)
self.rcon.disconnect()
logger.info('ServerController.command(command="%s") => %s', command, output)
return output
except Exception:
logger.exception(
'ServerController.command(command="%s") - Command execution failed',
command,
)
return ""
@ -101,19 +139,28 @@ class MaintainanceController:
self.mnt_file = Path(Config.MAINTAINANCE_FILE)
def set(self, reason: str):
logger.debug('MaintainanceController.set(reason="%s")', reason)
self.mnt_file.write_text(reason)
def is_set(self) -> bool:
logger.debug("MaintainanceController.is_set() => %s", self.mnt_file.is_file())
return self.mnt_file.is_file()
def get(self) -> str:
if self.is_set():
logger.debug(
"MaintainanceController.get() => %s", self.mnt_file.read_text()
)
return self.mnt_file.read_text()
logger.debug("MaintainanceController.get() => ")
return ""
def unset(self):
logger.debug("MaintainanceController.unset()")
if self.is_set():
self.mnt_file.unlink()
logger.debug("MaintainanceController.unset() - Maintainance file was unset")
class LogsController:
@ -121,16 +168,21 @@ class LogsController:
self.log_file = Path(Config.LOG_FILE)
def stream(self):
logger.debug("LogsController.stream()")
i = 0
with self.log_file.open() as f:
f.seek(0, 2)
while True:
line = f.readline()
if line:
logger.debug("LogsController.stream() - Yielding line %s", i)
i += 1
yield line
else:
sleep(0.1)
def tail(self, back: int = 10):
logger.debug("LogsController.tail(back=%s)", back)
with self.log_file.open() as f:
for line in deque(f, maxlen=back):
yield line