# HG changeset patch # User jfp <jf.pieronne@laposte.net> # Date 1683634910 -7200 # Tue May 09 14:21:50 2023 +0200 # Node ID d46a90f35a22ff36f562e1a7a7d7bbbb342b8cc7 # Parent 8fe8267c9b59bdf3de5ede2b55dcc8c00b83752c Add optional password for supervisorctl, return a reply for each command diff --git a/supervisorctl.py b/supervisorctl.py --- a/supervisorctl.py +++ b/supervisorctl.py @@ -1,10 +1,12 @@ +import argparse import cmd import json -import sys -from typing import Tuple +from typing import Tuple, Any from ovms import cmbdef, iodef, mbxqio, psldef, starlet +supervisorctl_pwd = '' + def pstat2str(s: int) -> str: dstate = { @@ -36,6 +38,15 @@ self.mcmd_r = mcmd_r cmd.Cmd.__init__(self, completekey, stdin, stdout) + def write_cmd(self, cmd: dict) -> Any: + global supervisorctl_pwd + cmd['pwd'] = supervisorctl_pwd + jscmd = json.dumps(cmd) + self.mcmd.write(jscmd.encode('ascii'), iodef.IO_M_READERCHECK) + return json.loads( + self.mcmd_r.read(4096, iodef.IO_M_WRITERCHECK) # type: ignore + ) + def do_EOF(self, line): """To quit, type ^D or use the quit command""" return True @@ -50,21 +61,36 @@ if len(lst) != 0: print('*** invalid number of arguments') return - jscmd = json.dumps({'cmd': 'shutdown'}) - self.mcmd.write(jscmd.encode('ascii'), iodef.IO_M_READERCHECK) + res = self.write_cmd({'cmd': 'shutdown'}) + print(res) def do_status(self, arg): """Display status of programs""" lst = arg.split() - jscmd = json.dumps({'cmd': 'status', 'programs': lst}) - self.mcmd.write(jscmd.encode('ascii'), iodef.IO_M_READERCHECK) - res: bytes - res = self.mcmd_r.read(4096, iodef.IO_M_WRITERCHECK) # type: ignore - jscmd = json.loads(res) - for pinfo in jscmd['reply']: + jscmd = self.write_cmd({'cmd': 'status', 'programs': lst}) + if 'error' in jscmd: + print(jscmd['error']) + return + for pinfo in jscmd['result']: pinfo[3] = pstat2str(pinfo[3]) print(pinfo) + def do_start(self, arg): + lst = arg.split() + if len(lst) < 1: + print('*** invalid number of arguments') + return + res = self.write_cmd({'cmd': 'start', 'programs': lst}) + print(res) + + def do_stop(self, arg): + lst = arg.split() + if len(lst) < 1: + print('*** invalid number of arguments') + return + res = self.write_cmd({'cmd': 'stop', 'programs': lst}) + print(res) + def help_status(self): print('status <name>\t\tGet status for a single process') print('status <gname>:*\tGet status for all ' 'processes in a group') @@ -73,28 +99,12 @@ ) print('status\t\t\tGet all process status info') - def do_start(self, arg): - lst = arg.split() - if len(lst) < 1: - print('*** invalid number of arguments') - return - jscmd = json.dumps({'cmd': 'start', 'programs': lst}) - self.mcmd.write(jscmd.encode('ascii'), iodef.IO_M_READERCHECK) - def help_start(self): print('start <name>\t\tStart a process') print('start <gname>:*\t\tStart all processes in a group') print('start <name> <name>\tStart multiple processes or groups') print('start all\t\tStart all processes') - def do_stop(self, arg): - lst = arg.split() - if len(lst) < 1: - print('*** invalid number of arguments') - return - jscmd = json.dumps({'cmd': 'stop', 'programs': lst}) - self.mcmd.write(jscmd.encode('ascii'), iodef.IO_M_READERCHECK) - def help_stop(self): print('stop <name>\t\tStop a process') print('stop <gname>:*\t\tStop all processes in a group') @@ -126,14 +136,28 @@ def main(): + global supervisorctl_pwd + parser = argparse.ArgumentParser(description='supervisorctl') + parser.add_argument( + '-p', + '--password', + required=False, + # type=str, + default='', + help='password for supervisord', + ) + # args = parser.parse_args() + # print('-->', args) + args, unknownargs = parser.parse_known_args() + supervisorctl_pwd = args.password chan, chan_r = mbx_init() with ( mbxqio.MBXQIO(channel=chan) as mcmd, mbxqio.MBXQIO(channel=chan_r) as mcmd_r, ): c = Controller(mcmd, mcmd_r) - if len(sys.argv) > 1: - c.onecmd(' '.join(sys.argv[1:])) + if len(unknownargs) > 0: + c.onecmd(' '.join(unknownargs)) else: c.cmdloop() diff --git a/supervisord.conf_template b/supervisord.conf_template --- a/supervisord.conf_template +++ b/supervisord.conf_template @@ -1,3 +1,5 @@ +[openvms] +password=xxxxxx [program:firstpgm] command=dev:[dir]PGM1.COM process_name=PGM1D diff --git a/supervisord.py b/supervisord.py --- a/supervisord.py +++ b/supervisord.py @@ -1,6 +1,7 @@ # # Required privileges: SYSNAM, PRMMBX, IMPERSONATE, WORLD # +import argparse import configparser import ctypes import datetime @@ -38,6 +39,7 @@ current_tick: int = 0 timer_queue: queue.PriorityQueue['PrioritizedItem'] = queue.PriorityQueue() +supervisorctl_pwd = '' class TimerItemType(IntEnum): UNDEF = 0 @@ -420,11 +422,29 @@ return astctxt +def send_cmd_reply(fcmd_r: mbxqio.MBXQIO, cmdreply: dict): + jscmd = json.dumps(cmdreply) + try: + fcmd_r.write( + jscmd.encode('ascii'), + iodef.IO_M_READERCHECK | iodef.IO_M_NOW, + ) + except OSError: + pass + def dispatch_cmd(res: bytes, fcmd_r: mbxqio.MBXQIO): + global supervisorctl_pwd jscmd = json.loads(res) - logging.info(f'Receive cmd: {jscmd}') + pwd = jscmd['pwd'] + jscmd['pwd'] = '*****' + if pwd != supervisorctl_pwd: + logging.info('Invalid password') + send_cmd_reply(fcmd_r, {'error': 'Invalid password'}) + + return match jscmd['cmd'].lower(): case 'shutdown': + send_cmd_reply(fcmd_r, {'result': None}) exit(1) case 'start': pgmnames = set([name.upper() for name in jscmd['programs']]) @@ -441,6 +461,7 @@ pgm.process.state = ProcessStates.STOPPED pgm.remain_startretries = pgm.startretries + 1 pgm.create_process(False) + send_cmd_reply(fcmd_r, {'result': None}) case 'stop': pgmnames = set([name.upper() for name in jscmd['programs']]) if 'ALL' in pgmnames: @@ -450,6 +471,7 @@ for pgm in Program.programs.values(): if pgm.name in pgmnames: pgm.kill() + send_cmd_reply(fcmd_r, {'result': None}) case 'status': pgmnames = set([name.upper() for name in jscmd['programs']]) lst = [] @@ -466,21 +488,14 @@ ) if pgm.process.state in ( ProcessStates.BACKOFF, - ProcessStates.STOPPED, + ProcessStates.STOPPED, ProcessStates.EXITED, ProcessStates.FATAL, ): lst[-1].append(str(pgm.process.end_time)[:19]) lst[-1].append(pgm.process.finalsts) - jscmd = json.dumps({'reply': lst}) - try: - fcmd_r.write( - jscmd.encode('ascii'), - iodef.IO_M_READERCHECK | iodef.IO_M_NOW, - ) - except OSError: - pass + send_cmd_reply(fcmd_r, {'result': lst}) def dispatch_ast(astparam: AstParam, fcmd_r: mbxqio.MBXQIO): @@ -579,7 +594,7 @@ def main(): - global logger + global logger, supervisorctl_pwd logging_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' logging.basicConfig( @@ -603,8 +618,22 @@ | prvdef.PRV_M_WORLD, ) + parser = argparse.ArgumentParser(description='supervisord') + parser.add_argument( + '-c', + '--configuration', + type=argparse.FileType('r', encoding='latin1'), + default='./supervisord.conf', + help='Configuration file path (default ./supervisord.conf)', + ) + args = parser.parse_args() + config = configparser.ConfigParser() - config.read('supervisord.conf') + config.read_file(args.configuration) + args.configuration.close() + + supervisorctl_pwd = config['openvms'].get('password', '') + for sn in config.sections(): if sn.startswith('program:'): name = sn.split(':')[-1].upper()