from threading import Thread import logging, os, pathlib, subprocess, json, sys # import logger logger = logging.getLogger(__name__) class IrisSystemError(Exception): pass class IrisSystemPermissionError(IrisSystemError): reason = "Permission denied" def __init__(self, path): message = ( "Password-less access to %s was refused. Check your /etc/sudoers file." % path.as_uri() ) logger.error(message) super().__init__(message) class IrisSystemThread(Thread): _USE_SUDO = True def __init__(self, action, ioloop, callback): Thread.__init__(self) self.action = action self.callback = callback self.ioloop = ioloop self.script_path = pathlib.Path(__file__).parent / "system.sh" def get_command(self, action=None, *, non_interactive=False): if self._USE_SUDO: if non_interactive: args = [b"sudo -n"] else: args = [b"sudo"] else: args = [] if action is None: action = self.action args = args + [bytes(self.script_path), action.encode()] return args ## # Run the defined action ## def run(self): logger.info("Running system action '" + self.action + "'") try: self.can_run() except IrisSystemError as e: logger.error(e) error = {"message": e.reason, "description": e.message} return {"error": error} command = self.get_command() logger.debug("Running '%s'", os.fsdecode(b" ".join(command))) process = subprocess.Popen(command, stdout=subprocess.PIPE, encoding="utf8") lines = "" while True: line = process.stdout.readline() if process.poll() is not None: break if line: logger.info(line) lines = lines + "\n" + line # This seems to be ignored. Detected as spammy io? # self.ioloop.add_callback(lambda: self.callback(None, None, {'output': line})) if process.returncode == 0: self.ioloop.add_callback( lambda: self.callback({"output": lines}, None, None) ) else: self.ioloop.add_callback( lambda: self.callback(None, {"error": lines}, None) ) ## # Check if we have access to the system script (system.sh) # # @return boolean or exception ## def can_run(self, *args, **kwargs): # Attempt an empty call to our system file command_bytes = b" ".join(self.get_command("check", non_interactive=True)) process = subprocess.Popen( command_bytes, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True ) result, error = process.communicate() exitCode = process.wait() # Some kind of failure, so we can't run any commands this way if exitCode > 0: raise IrisSystemPermissionError(self.script_path) else: return True