diff --git a/screenless/mgmt.py b/screenless/mgmt.py index 2feddf9..033bbd7 100644 --- a/screenless/mgmt.py +++ b/screenless/mgmt.py @@ -9,51 +9,85 @@ import configparser import importlib import multiprocessing import os +import signal import time -def mgmt(): + +class Management: """ - Primary management function. Main loop that starts and runs all bureaus. - Some day this will do nice supervisory things like restart crashed buros. + Management is the executive of the Screenless Office. It can start, stop + and reconfigure the subordinate bureaus of the office. """ - basepath = os.path.expanduser("~/.screenless") - if not os.path.exists(basepath): - os.mkdir(basepath) - os.chdir(basepath) - - config = configparser.ConfigParser() - procs = {} + def __init__(self): + basepath = os.path.expanduser("~/.screenless") + if not os.path.exists(basepath): + os.mkdir(basepath) + os.chdir(basepath) + self.procs = {} + self.org_chart = [] + self._load_config() - try: - config.read("mgmt.ini") - org_chart = config["mgmt"]["bureaus"].split() - except KeyError: - config["mgmt"] = {"bureaus": - "ihr typing publicrelations photography jokes"} - with open("mgmt.ini", "w") as configfile: - config.write(configfile) - print("created new mgmt.ini config file. please modify this to suit.") - org_chart = config["mgmt"]["bureaus"].split() + def _load_config(self): + config = configparser.ConfigParser() + try: + config.read("mgmt.ini") + self.org_chart = config["mgmt"]["bureaus"].split() + except KeyError: + config["mgmt"] = {"bureaus": + "ihr typing publicrelations photography jokes"} + with open("mgmt.ini", "w") as configfile: + config.write(configfile) + print("Created new mgmt.ini config file. Please modify to suit.") + self.org_chart = config["mgmt"]["bureaus"].split() - print("org chart:", org_chart) + def _start_bureaus(self): + """ + Initialized and start all child bureaus + """ + for buro in self.org_chart: + # TODO: this may need some sanity checking for crash/reload + lib = importlib.import_module("bureau." + buro) + proc = multiprocessing.Process(target=lib.main) + self.procs[buro] = proc + proc.start() - for buro in org_chart: - lib = importlib.import_module("bureau." + buro) - proc = multiprocessing.Process(target=lib.main) - procs[buro] = proc - proc.start() + def _stop_bureaus(self): + """ + Terminates all running bureau children + """ + for buro in self.org_chart: + proc = self.procs[buro] + proc.terminate() - while True: - for buro in org_chart: - proc = procs[buro] - if not proc.is_alive(): - print("bureau", buro, "has crashed! Call the consultants!") - #TODO this should probably restart in some sensible way - time.sleep(1) + def reload(self, signum=None, frame=None): + """ + stop all bureaus, reload config, restart all bureaus. + Note, this may cause a loss of state in some cases. + """ + self._stop_bureaus() + self._load_config() + self._start_bureaus() + def run(self): + """ + main loop for Management, will restart crashed bureaus + and reload everything when sent a SIGHUP + """ + signal.signal(signal.SIGHUP, self.reload) + self._start_bureaus() + while True: + for buro in self.org_chart: + proc = self.procs[buro] + if not proc.is_alive(): + print("bureau", buro, "has crashed! Call the consultants!") + print("restarting...") + #TODO this should probably only retry a few times + proc.start() + time.sleep(1) if __name__ == "__main__": - mgmt() + mgmt = Management() + mgmt.run()