From 293c3b077d37f4ca9f9690a771e916fdd9b531fc Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 8 Dec 2016 23:24:14 +0100 Subject: [PATCH] Add external-runner option to the launcher to allow for replacing the native evennia runner with an external process manager (like Linux' start-stop-daemon). Note that without a replacement, this option will make evennia un-reloadable. --- evennia/server/evennia_launcher.py | 14 ++++++++++++-- evennia/server/evennia_runner.py | 22 +++++++++++++++++----- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/evennia/server/evennia_launcher.py b/evennia/server/evennia_launcher.py index 65d1db73d3..36aa64f16d 100644 --- a/evennia/server/evennia_launcher.py +++ b/evennia/server/evennia_launcher.py @@ -1084,7 +1084,7 @@ def run_menu(): return -def server_operation(mode, service, interactive, profiler, logserver=False): +def server_operation(mode, service, interactive, profiler, logserver=False, doexit=False): """ Handle argument options given on the command line. @@ -1095,6 +1095,10 @@ def server_operation(mode, service, interactive, profiler, logserver=False): profiler (bool): Run the service under the profiler. logserver (bool, optional): Log Server data to logfile specified by settings.SERVER_LOG_FILE. + doexit (bool, optional): If True, immediately exit the runner after + starting the relevant processes. If the runner exits, Evennia + cannot be reloaded. This is meant to be used with an external + process manager like Linux' start-stop-daemon. """ @@ -1136,6 +1140,8 @@ def server_operation(mode, service, interactive, profiler, logserver=False): cmdstr.append('--logserver') django.core.management.call_command( 'collectstatic', verbosity=1, interactive=False) + if doexit: + cmdstr.append('--doexit') cmdstr.extend([ GAMEDIR, TWISTED_BINARY, SERVER_LOGFILE, PORTAL_LOGFILE, HTTP_LOGFILE]) @@ -1242,6 +1248,10 @@ def main(): '--initsettings', action='store_true', dest="initsettings", default=False, help="Create a new, empty settings file as gamedir/server/conf/settings.py.") + parser.add_argument( + '--external-runner', action='store_true', dest="doexit", + default=False, + help="Handle server restart with an external process manager.") parser.add_argument( "operation", nargs='?', default="noop", help="Operation to perform: 'start', 'stop', 'reload' or 'menu'.") @@ -1315,7 +1325,7 @@ def main(): elif option in ('start', 'reload', 'stop'): # operate the server directly init_game_directory(CURRENT_DIR, check_db=True) - server_operation(option, service, args.interactive, args.profiler, args.logserver) + server_operation(option, service, args.interactive, args.profiler, args.logserver, doexit=args.doexit) elif option != "noop": # pass-through to django manager check_db = False diff --git a/evennia/server/evennia_runner.py b/evennia/server/evennia_runner.py index ce9299f4f5..d963f8dd21 100644 --- a/evennia/server/evennia_runner.py +++ b/evennia/server/evennia_runner.py @@ -75,6 +75,7 @@ PROCESS_IOERROR = \ PROCESS_RESTART = "{component} restarting ..." +PROCESS_DOEXIT = "Deferring to external runner." # Functions @@ -134,7 +135,7 @@ def cycle_logfile(logfile): # Start program management -def start_services(server_argv, portal_argv): +def start_services(server_argv, portal_argv, doexit=False): """ This calls a threaded loop that launces the Portal and Server and then restarts them when they finish. @@ -162,7 +163,7 @@ def start_services(server_argv, portal_argv): if portal_argv: try: - if get_restart_mode(PORTAL_RESTART) == "True": + if not doexit and get_restart_mode(PORTAL_RESTART) == "True": # start portal as interactive, reloadable thread PORTAL = thread.start_new_thread(portal_waiter, (processes, )) else: @@ -175,12 +176,19 @@ def start_services(server_argv, portal_argv): try: if server_argv: - # start server as a reloadable thread - SERVER = thread.start_new_thread(server_waiter, (processes, )) + if doexit: + SERVER = Popen(server_argv, env=getenv()) + else: + # start server as a reloadable thread + SERVER = thread.start_new_thread(server_waiter, (processes, )) except IOError as e: print(PROCESS_IOERROR.format(component="Server", traceback=e)) return + if doexit: + # Exit immediately + return + # Reload loop while True: @@ -234,6 +242,8 @@ def main(): default=False, help='Profile Portal') parser.add_argument('--nologcycle', action='store_false', dest='nologcycle', default=True, help='Do not cycle log files') + parser.add_argument('--doexit', action='store_true', dest='doexit', + default=False, help='Immediately exit after processes have started.') parser.add_argument('gamedir', help="path to game dir") parser.add_argument('twistdbinary', help="path to twistd binary") parser.add_argument('slogfile', help="path to server log file") @@ -327,6 +337,8 @@ def main(): if args.pportal: portal_argv.extend(pportal_argv) print("\nRunning Evennia Portal under cProfile.") + if args.doexit: + print(PROCESS_DOEXIT) # Windows fixes (Windows don't support pidfiles natively) if os.name == 'nt': @@ -336,7 +348,7 @@ def main(): del portal_argv[-2] # Start processes - start_services(server_argv, portal_argv) + start_services(server_argv, portal_argv, doexit=args.doexit) if __name__ == '__main__': main()