Make pid handling more stable, also internally
This commit is contained in:
parent
c6eca6bf03
commit
c0fe8a92ee
5 changed files with 30 additions and 34 deletions
|
|
@ -4,6 +4,7 @@ Portal. This module sets up the Client-side communication.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
from evennia.server.portal import amp
|
from evennia.server.portal import amp
|
||||||
from twisted.internet import protocol
|
from twisted.internet import protocol
|
||||||
from evennia.utils import logger
|
from evennia.utils import logger
|
||||||
|
|
@ -105,7 +106,8 @@ class AMPServerClientProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
super(AMPServerClientProtocol, self).connectionMade()
|
super(AMPServerClientProtocol, self).connectionMade()
|
||||||
# first thing we do is to request the Portal to sync all sessions
|
# 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)
|
# 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):
|
def data_to_portal(self, command, sessid, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -679,16 +679,14 @@ def query_status(callback=None):
|
||||||
callback(response)
|
callback(response)
|
||||||
else:
|
else:
|
||||||
pstatus, sstatus, ppid, spid, pinfo, sinfo = _parse_status(response)
|
pstatus, sstatus, ppid, spid, pinfo, sinfo = _parse_status(response)
|
||||||
# note - the server reports its pid, but this is likely to be a
|
print("Evennia Portal: {}{}\n Server: {}{}".format(
|
||||||
# thread and can't be relied on, so we use the pid file instead
|
wmap[pstatus], " (pid {})".format(get_pid(PORTAL_PIDFILE, ppid)) if pstatus else "",
|
||||||
print("Portal: {} (pid {})\nServer: {} (pid {})".format(
|
wmap[sstatus], " (pid {})".format(get_pid(SERVER_PIDFILE, spid)) if sstatus else ""))
|
||||||
wmap[pstatus], get_pid(PORTAL_PIDFILE),
|
|
||||||
wmap[sstatus], get_pid(SERVER_PIDFILE)))
|
|
||||||
_reactor_stop()
|
_reactor_stop()
|
||||||
|
|
||||||
def _errback(fail):
|
def _errback(fail):
|
||||||
pstatus, sstatus = False, False
|
pstatus, sstatus = False, False
|
||||||
print("Portal: {}\nServer: {}".format(wmap[pstatus], wmap[sstatus]))
|
print("Portal: {}\nServer: {}".format(wmap[pstatus], wmap[sstatus]))
|
||||||
_reactor_stop()
|
_reactor_stop()
|
||||||
|
|
||||||
send_instruction(PSTATUS, None, _callback, _errback)
|
send_instruction(PSTATUS, None, _callback, _errback)
|
||||||
|
|
@ -1355,22 +1353,23 @@ def getenv():
|
||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
def get_pid(pidfile):
|
def get_pid(pidfile, default=None):
|
||||||
"""
|
"""
|
||||||
Get the PID (Process ID) by trying to access an PID file.
|
Get the PID (Process ID) by trying to access an PID file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
pidfile (str): The path of the pid file.
|
pidfile (str): The path of the pid file.
|
||||||
|
default (int, optional): What to return if file does not exist.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
pid (str or None): The process id.
|
pid (str): The process id or `default`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if os.path.exists(pidfile):
|
if os.path.exists(pidfile):
|
||||||
with open(pidfile, 'r') as f:
|
with open(pidfile, 'r') as f:
|
||||||
pid = f.read()
|
pid = f.read()
|
||||||
return pid
|
return pid
|
||||||
return None
|
return default
|
||||||
|
|
||||||
|
|
||||||
def del_pid(pidfile):
|
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.
|
# We must catch and ignore the interrupt sent.
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
# Linux can send the SIGINT signal directly
|
# Linux/Unix can send the SIGINT signal directly
|
||||||
# to the specified PID.
|
# to the specified PID.
|
||||||
os.kill(int(pid), killsignal)
|
os.kill(int(pid), killsignal)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,6 @@ class AMPServerFactory(protocol.ServerFactory):
|
||||||
self.protocol = AMPServerProtocol
|
self.protocol = AMPServerProtocol
|
||||||
self.broadcasts = []
|
self.broadcasts = []
|
||||||
self.server_connection = None
|
self.server_connection = None
|
||||||
self.server_info_dict = None
|
|
||||||
self.launcher_connection = None
|
self.launcher_connection = None
|
||||||
self.disconnect_callbacks = {}
|
self.disconnect_callbacks = {}
|
||||||
self.server_connect_callbacks = []
|
self.server_connect_callbacks = []
|
||||||
|
|
@ -89,7 +88,7 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
super(AMPServerProtocol, self).connectionLost(reason)
|
super(AMPServerProtocol, self).connectionLost(reason)
|
||||||
if self.factory.server_connection == self:
|
if self.factory.server_connection == self:
|
||||||
self.factory.server_connection = None
|
self.factory.server_connection = None
|
||||||
self.factory.server_info_dict = None
|
self.factory.portal.server_info_dict = {}
|
||||||
if self.factory.launcher_connection == self:
|
if self.factory.launcher_connection == self:
|
||||||
self.factory.launcher_connection = None
|
self.factory.launcher_connection = None
|
||||||
|
|
||||||
|
|
@ -112,7 +111,7 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
server_connected = bool(self.factory.server_connection and
|
server_connected = bool(self.factory.server_connection and
|
||||||
self.factory.server_connection.transport.connected)
|
self.factory.server_connection.transport.connected)
|
||||||
portal_info_dict = self.factory.portal.get_info_dict()
|
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
|
server_pid = self.factory.portal.server_process_id
|
||||||
portal_pid = os.getpid()
|
portal_pid = os.getpid()
|
||||||
return (True, server_connected, portal_pid, server_pid, portal_info_dict, server_info_dict)
|
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
|
# start the Server
|
||||||
|
process = None
|
||||||
with open(settings.SERVER_LOG_FILE, 'a') as logfile:
|
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:
|
try:
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
# Windows requires special care
|
# Windows requires special care
|
||||||
|
|
@ -159,24 +162,20 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
process = Popen(server_twistd_cmd, env=getenv(), bufsize=-1,
|
process = Popen(server_twistd_cmd, env=getenv(), bufsize=-1,
|
||||||
stdout=logfile, stderr=STDOUT,
|
stdout=logfile, stderr=STDOUT,
|
||||||
creationflags=create_no_window)
|
creationflags=create_no_window)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
process = Popen(server_twistd_cmd, env=getenv(), bufsize=-1,
|
process = Popen(server_twistd_cmd, env=getenv(), bufsize=-1,
|
||||||
stdout=logfile, stderr=STDOUT)
|
stdout=logfile, stderr=STDOUT)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.factory.portal.server_process_id = None
|
|
||||||
logger.log_trace()
|
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
|
self.factory.portal.server_twistd_cmd = server_twistd_cmd
|
||||||
logfile.flush()
|
logfile.flush()
|
||||||
|
if process:
|
||||||
|
# avoid zombie-process
|
||||||
|
process.wait()
|
||||||
return process.pid
|
return process.pid
|
||||||
|
return 0
|
||||||
|
|
||||||
def wait_for_disconnect(self, callback, *args, **kwargs):
|
def wait_for_disconnect(self, callback, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -414,7 +413,8 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
|
|
||||||
elif operation == amp.PSYNC: # portal sync
|
elif operation == amp.PSYNC: # portal sync
|
||||||
# Server has (re-)connected and wants the session data from portal
|
# 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
|
# this defaults to 'shutdown' or whatever value set in server_stop
|
||||||
server_restart_mode = self.factory.portal.server_restart_mode
|
server_restart_mode = self.factory.portal.server_restart_mode
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,6 @@ except Exception:
|
||||||
PORTAL_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(settings.PORTAL_SERVICES_PLUGIN_MODULES)]
|
PORTAL_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(settings.PORTAL_SERVICES_PLUGIN_MODULES)]
|
||||||
LOCKDOWN_MODE = settings.LOCKDOWN_MODE
|
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
|
# Evennia Portal settings
|
||||||
# -------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
@ -113,8 +108,10 @@ class Portal(object):
|
||||||
self.sessions = PORTAL_SESSIONS
|
self.sessions = PORTAL_SESSIONS
|
||||||
self.sessions.portal = self
|
self.sessions.portal = self
|
||||||
self.process_id = os.getpid()
|
self.process_id = os.getpid()
|
||||||
|
|
||||||
self.server_process_id = None
|
self.server_process_id = None
|
||||||
self.server_restart_mode = "shutdown"
|
self.server_restart_mode = "shutdown"
|
||||||
|
self.server_info_dict = {}
|
||||||
|
|
||||||
# set a callback if the server is killed abruptly,
|
# set a callback if the server is killed abruptly,
|
||||||
# by Ctrl-C, reboot etc.
|
# by Ctrl-C, reboot etc.
|
||||||
|
|
@ -163,9 +160,7 @@ class Portal(object):
|
||||||
return
|
return
|
||||||
self.sessions.disconnect_all()
|
self.sessions.disconnect_all()
|
||||||
self.set_restart_mode(restart)
|
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:
|
if not _reactor_stopping:
|
||||||
# shutting down the reactor will trigger another signal. We set
|
# shutting down the reactor will trigger another signal. We set
|
||||||
# a flag to avoid loops.
|
# a flag to avoid loops.
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
SERVER_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(settings.SERVER_SERVICES_PLUGIN_MODULES)]
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
# Evennia Server settings
|
# Evennia Server settings
|
||||||
#------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
SERVERNAME = settings.SERVERNAME
|
SERVERNAME = settings.SERVERNAME
|
||||||
VERSION = get_evennia_version()
|
VERSION = get_evennia_version()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue