Make pid handling more stable, also internally

This commit is contained in:
Griatch 2018-01-27 00:33:09 +01:00
parent c6eca6bf03
commit c0fe8a92ee
5 changed files with 30 additions and 34 deletions

View file

@ -4,6 +4,7 @@ Portal. This module sets up the Client-side communication.
"""
import os
from evennia.server.portal import amp
from twisted.internet import protocol
from evennia.utils import logger
@ -105,7 +106,8 @@ class AMPServerClientProtocol(amp.AMPMultiConnectionProtocol):
super(AMPServerClientProtocol, self).connectionMade()
# first thing we do is to request the Portal to sync all sessions
# back with the Server side. We also need the startup mode (reload, reset, shutdown)
self.send_AdminServer2Portal(amp.DUMMYSESSION, operation=amp.PSYNC, info_dict=info_dict)
self.send_AdminServer2Portal(
amp.DUMMYSESSION, operation=amp.PSYNC, spid=os.getpid(), info_dict=info_dict)
def data_to_portal(self, command, sessid, **kwargs):
"""

View file

@ -679,16 +679,14 @@ def query_status(callback=None):
callback(response)
else:
pstatus, sstatus, ppid, spid, pinfo, sinfo = _parse_status(response)
# note - the server reports its pid, but this is likely to be a
# thread and can't be relied on, so we use the pid file instead
print("Portal: {} (pid {})\nServer: {} (pid {})".format(
wmap[pstatus], get_pid(PORTAL_PIDFILE),
wmap[sstatus], get_pid(SERVER_PIDFILE)))
print("Evennia Portal: {}{}\n Server: {}{}".format(
wmap[pstatus], " (pid {})".format(get_pid(PORTAL_PIDFILE, ppid)) if pstatus else "",
wmap[sstatus], " (pid {})".format(get_pid(SERVER_PIDFILE, spid)) if sstatus else ""))
_reactor_stop()
def _errback(fail):
pstatus, sstatus = False, False
print("Portal: {}\nServer: {}".format(wmap[pstatus], wmap[sstatus]))
print("Portal: {}\nServer: {}".format(wmap[pstatus], wmap[sstatus]))
_reactor_stop()
send_instruction(PSTATUS, None, _callback, _errback)
@ -1355,22 +1353,23 @@ def getenv():
return env
def get_pid(pidfile):
def get_pid(pidfile, default=None):
"""
Get the PID (Process ID) by trying to access an PID file.
Args:
pidfile (str): The path of the pid file.
default (int, optional): What to return if file does not exist.
Returns:
pid (str or None): The process id.
pid (str): The process id or `default`.
"""
if os.path.exists(pidfile):
with open(pidfile, 'r') as f:
pid = f.read()
return pid
return None
return default
def del_pid(pidfile):
@ -1418,7 +1417,7 @@ def kill(pidfile, component='Server', callback=None, errback=None, killsignal=SI
# We must catch and ignore the interrupt sent.
pass
else:
# Linux can send the SIGINT signal directly
# Linux/Unix can send the SIGINT signal directly
# to the specified PID.
os.kill(int(pid), killsignal)

View file

@ -54,7 +54,6 @@ class AMPServerFactory(protocol.ServerFactory):
self.protocol = AMPServerProtocol
self.broadcasts = []
self.server_connection = None
self.server_info_dict = None
self.launcher_connection = None
self.disconnect_callbacks = {}
self.server_connect_callbacks = []
@ -89,7 +88,7 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
super(AMPServerProtocol, self).connectionLost(reason)
if self.factory.server_connection == self:
self.factory.server_connection = None
self.factory.server_info_dict = None
self.factory.portal.server_info_dict = {}
if self.factory.launcher_connection == self:
self.factory.launcher_connection = None
@ -112,7 +111,7 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
server_connected = bool(self.factory.server_connection and
self.factory.server_connection.transport.connected)
portal_info_dict = self.factory.portal.get_info_dict()
server_info_dict = self.factory.server_info_dict
server_info_dict = self.factory.portal.server_info_dict
server_pid = self.factory.portal.server_process_id
portal_pid = os.getpid()
return (True, server_connected, portal_pid, server_pid, portal_info_dict, server_info_dict)
@ -151,7 +150,11 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
"""
# start the Server
process = None
with open(settings.SERVER_LOG_FILE, 'a') as logfile:
# we link stdout to a file in order to catch
# eventual errors happening before the Server has
# opened its logger.
try:
if os.name == 'nt':
# Windows requires special care
@ -159,24 +162,20 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
process = Popen(server_twistd_cmd, env=getenv(), bufsize=-1,
stdout=logfile, stderr=STDOUT,
creationflags=create_no_window)
else:
process = Popen(server_twistd_cmd, env=getenv(), bufsize=-1,
stdout=logfile, stderr=STDOUT)
except Exception:
self.factory.portal.server_process_id = None
logger.log_trace()
logfile.flush()
return 0
# there is a short window before the server logger is up where we must
# catch the stdout of the Server or eventual tracebacks will be lost.
# with process.stdout as out:
# logger.log_server(out.readlines())
# store the pid and launch argument for future reference
self.factory.portal.server_process_id = process.pid
self.factory.portal.server_twistd_cmd = server_twistd_cmd
logfile.flush()
if process:
# avoid zombie-process
process.wait()
return process.pid
return 0
def wait_for_disconnect(self, callback, *args, **kwargs):
"""
@ -414,7 +413,8 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
elif operation == amp.PSYNC: # portal sync
# Server has (re-)connected and wants the session data from portal
self.factory.server_info_dict = kwargs.get("info_dict", {})
self.factory.portal.server_info_dict = kwargs.get("info_dict", {})
self.factory.portal.server_process_id = kwargs.get("spid", None)
# this defaults to 'shutdown' or whatever value set in server_stop
server_restart_mode = self.factory.portal.server_restart_mode

View file

@ -39,11 +39,6 @@ except Exception:
PORTAL_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(settings.PORTAL_SERVICES_PLUGIN_MODULES)]
LOCKDOWN_MODE = settings.LOCKDOWN_MODE
PORTAL_PIDFILE = ""
if os.name == 'nt':
# For Windows we need to handle pid files manually.
PORTAL_PIDFILE = os.path.join(settings.GAME_DIR, "server", 'portal.pid')
# -------------------------------------------------------------
# Evennia Portal settings
# -------------------------------------------------------------
@ -113,8 +108,10 @@ class Portal(object):
self.sessions = PORTAL_SESSIONS
self.sessions.portal = self
self.process_id = os.getpid()
self.server_process_id = None
self.server_restart_mode = "shutdown"
self.server_info_dict = {}
# set a callback if the server is killed abruptly,
# by Ctrl-C, reboot etc.
@ -163,9 +160,7 @@ class Portal(object):
return
self.sessions.disconnect_all()
self.set_restart_mode(restart)
if os.name == 'nt' and os.path.exists(PORTAL_PIDFILE):
# for Windows we need to remove pid files manually
os.remove(PORTAL_PIDFILE)
if not _reactor_stopping:
# shutting down the reactor will trigger another signal. We set
# a flag to avoid loops.

View file

@ -51,9 +51,9 @@ SERVER_STARTSTOP_MODULE = mod_import(settings.AT_SERVER_STARTSTOP_MODULE)
SERVER_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(settings.SERVER_SERVICES_PLUGIN_MODULES)]
#------------------------------------------------------------
# ------------------------------------------------------------
# Evennia Server settings
#------------------------------------------------------------
# ------------------------------------------------------------
SERVERNAME = settings.SERVERNAME
VERSION = get_evennia_version()