165 lines
7.6 KiB
Python
Executable File
165 lines
7.6 KiB
Python
Executable File
#!/usr/bin/python3
|
||
|
||
import os
|
||
import sys
|
||
import signal
|
||
import shutil
|
||
import logging
|
||
import tempfile
|
||
import configparser
|
||
|
||
from subprocess import Popen, PIPE
|
||
|
||
### функция перехвата сигнала от системы
|
||
def receiveSignal(signalNumber, frame):
|
||
print(f"I'm killed - {os.getpid()}")
|
||
raise KeyboardInterrupt()
|
||
|
||
### функция для преобразования элемента в список
|
||
def ini_item(value: str=""):
|
||
tmp=value.split(',')
|
||
if len(tmp) > 1:
|
||
return tmp
|
||
return value
|
||
|
||
### функция вычисления номера порта
|
||
def set_port(value: list, c: int):
|
||
if len(value) > 1:
|
||
try:
|
||
factor=int(value[2])
|
||
except Exception:
|
||
factor=1
|
||
port=int(value[0])+1*factor*c
|
||
if int(value[1])>int(value[0]) and port<=int(value[1]):
|
||
return port
|
||
elif c<int(value[1]):
|
||
return port
|
||
else:
|
||
print(f"I'm killed - {os.getpid()}")
|
||
logging.error("You have exceeded the server port limit")
|
||
raise KeyboardInterrupt()
|
||
return value
|
||
|
||
|
||
### функция запуска сервера
|
||
def daemon_server(count:int, game:str, dif:int, config:str):
|
||
try:
|
||
server_id=f"#{str(count+1)}" # id сервера (используем для именования папки и добавляем к имени сервера)
|
||
# конфигурационный файл серверов
|
||
c_cfg = configparser.ConfigParser()
|
||
c_cfg.optionxform = str # для правельного учета регистра
|
||
c_cfg.read(config)
|
||
cfg_settings={}
|
||
# пробегаем основные параметры
|
||
for key, value in c_cfg.items("SETTINGS"):
|
||
cfg_settings[key]=ini_item(value)
|
||
|
||
# создаем объект класса logging
|
||
logging.basicConfig(filename=cfg_settings['log'], encoding='utf-8', level=logging.INFO,format='%(asctime)s.%(msecs)03d %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S',)
|
||
|
||
# путь к папке с конфигурацией сервера
|
||
c_dir_ini=os.path.join(cfg_settings['dir_server_data'], f"KFGame/Config/{server_id}")
|
||
try:
|
||
# удаляем старый ln
|
||
shutil.rmtree(c_dir_ini, True)
|
||
os.remove(c_dir_ini)
|
||
except FileNotFoundError:
|
||
None
|
||
# создаём временную дерриктори под конфиг
|
||
tmp_dir=tempfile.TemporaryDirectory()
|
||
# изменяем владельца
|
||
shutil.chown(tmp_dir.name, cfg_settings['sys_user'], cfg_settings['sys_group'])
|
||
# создаем ln в папку с сервером
|
||
os.symlink(tmp_dir.name, c_dir_ini)
|
||
|
||
# пробегаем дополнительные параметры конфигурации сервера
|
||
for key1 in c_cfg.sections():
|
||
if key1 != "SETTINGS":
|
||
# путь к файлу inic_dir_ini
|
||
c_file_ini=f"{c_dir_ini}/{key1}"
|
||
# если в теге есть параметры то анализируем
|
||
c_key=c_cfg.items(key1)
|
||
if len(c_key) > 0:
|
||
c_ini = configparser.ConfigParser(strict=False)
|
||
c_ini.optionxform = str # для правельного учета регистра
|
||
# пробегаем парметры деррективы
|
||
for key2, value in c_key:
|
||
# разделяем ключ и значение
|
||
c_param=ini_item(key2)
|
||
# изменяем параметр
|
||
if not c_param[0] in c_ini:
|
||
c_ini[c_param[0]] = {}
|
||
# дописываем индекс в название сервера
|
||
if c_param[1] == 'ServerName' or c_param[1] == 'ClanMotto':
|
||
value+=f" {server_id}"
|
||
# обработка мараметра динамической конфигурации
|
||
if c_param[1] == 'bUsedForTakeover':
|
||
if int(value) > 0 and count < int(value):
|
||
value="FALSE"
|
||
else:
|
||
value="TRUE"
|
||
c_ini[c_param[0]][c_param[1]] = value
|
||
# пишем изменения в файл
|
||
with open(c_file_ini, "w") as f:
|
||
c_ini.write(f)
|
||
c_ini.clear()
|
||
|
||
# запускаем бинарник сервера
|
||
command=f"{cfg_settings['bin_server']} kf-bioticslab?Game={game}?Difficulty={dif} -Port={set_port(cfg_settings['port_game'], count)} -QueryPort={set_port(cfg_settings['port_query'], count)} -WebAdminPort={set_port(cfg_settings['port_webadmin'], count)} -ConfigSubDir={server_id}" # команды запуска
|
||
while True:
|
||
with Popen(command, stdout=PIPE, stderr=PIPE, shell=True, user=cfg_settings['sys_user'], group=cfg_settings['sys_group'], ) as process: # запускаем процесс от пользователя steam
|
||
logging.info(f"Start: {command}")
|
||
process.communicate() # ждём, пока работает процеес и есть вывод в терминал
|
||
logging.info(f"Stop: {command}")
|
||
except KeyboardInterrupt:
|
||
None
|
||
except Exception as err:
|
||
logging.info(f"Unexpected termination: {err}")
|
||
# при завершение подчищаем за собой
|
||
tmp_dir.cleanup()
|
||
try:
|
||
shutil.rmtree(c_dir_ini, True)
|
||
os.remove(c_dir_ini)
|
||
except FileNotFoundError:
|
||
None
|
||
# убиваемся
|
||
logging.info(f"Stop: {command}")
|
||
os.killpg(os.getpgid(process.pid), signal.SIGINT)
|
||
|
||
### главная функция
|
||
if __name__ == '__main__':
|
||
sys.stderr = open('/dev/null')
|
||
# ключи асоциативного массива
|
||
c_args_name=['script', 'count', 'game', 'dif', 'config']
|
||
# если 2 аргумента, вызов из run скрипта
|
||
if len(sys.argv) == 2:
|
||
c_args_value=sys.argv[1].split()
|
||
c_args_value.insert(0, sys.argv[0])
|
||
else:
|
||
# если 4 аргумента, вызов из shell
|
||
c_args_value=sys.argv
|
||
# проверяем кол-во передынных параветров вызова
|
||
if len(c_args_value) <= 4:
|
||
print("Incorrect number of arguments")
|
||
raise KeyError()
|
||
# получаем ассоциативный массив аргументов
|
||
c_args=dict(zip(c_args_name, c_args_value))
|
||
# перехватываем сигналы от системы
|
||
signal.signal(signal.SIGHUP, receiveSignal)
|
||
#signal.signal(signal.SIGINT, receiveSignal)
|
||
signal.signal(signal.SIGQUIT, receiveSignal)
|
||
signal.signal(signal.SIGILL, receiveSignal)
|
||
signal.signal(signal.SIGTRAP, receiveSignal)
|
||
signal.signal(signal.SIGABRT, receiveSignal)
|
||
signal.signal(signal.SIGBUS, receiveSignal)
|
||
signal.signal(signal.SIGFPE, receiveSignal)
|
||
#signal.signal(signal.SIGKILL, receiveSignal)
|
||
signal.signal(signal.SIGUSR1, receiveSignal)
|
||
signal.signal(signal.SIGSEGV, receiveSignal)
|
||
signal.signal(signal.SIGUSR2, receiveSignal)
|
||
signal.signal(signal.SIGPIPE, receiveSignal)
|
||
signal.signal(signal.SIGALRM, receiveSignal)
|
||
signal.signal(signal.SIGTERM, receiveSignal)
|
||
# запускаем экземпляр сервера
|
||
daemon_server(int(c_args['count']), c_args['game'], int(c_args['dif']), c_args['config'])
|