diff --git a/moonraker/plugins/shell_command.py b/moonraker/plugins/shell_command.py index c50a308..d1916b2 100644 --- a/moonraker/plugins/shell_command.py +++ b/moonraker/plugins/shell_command.py @@ -19,6 +19,7 @@ class ShellCommand: cmd = os.path.expanduser(cmd) self.command = shlex.split(cmd) self.partial_output = b"" + self.cancelled = False def _process_output(self, fd, events): if events & IOLoop.ERROR: @@ -40,7 +41,14 @@ class ShellCommand: except Exception: logging.exception("Error writing command output") + def cancel(self): + self.cancelled = True + async def run(self, timeout=2., verbose=True): + fd = None + if timeout is None: + # Never timeout + timeout = 9999999999999999. if not timeout or self.output_cb is None: # Fire and forget commands cannot be verbose as we can't # clean up after the process terminates @@ -58,7 +66,7 @@ class ShellCommand: fd, self._process_output, IOLoop.READ | IOLoop.ERROR) elif not timeout: # fire and forget, return from execution - return + return True sleeptime = 0 complete = False while sleeptime < timeout: @@ -67,6 +75,8 @@ class ShellCommand: if proc.poll() is not None: complete = True break + if self.cancelled: + break if not complete: proc.terminate() if verbose: @@ -75,10 +85,13 @@ class ShellCommand: self.partial_output = b"" if complete: msg = f"Command ({self.name}) finished" + elif self.cancelled: + msg = f"Command ({self.name}) cancelled" else: msg = f"Command ({self.name}) timed out" logging.info(msg) self.io_loop.remove_handler(fd) + return complete class ShellCommandFactory: def build_shell_command(self, cmd, callback):