def invoke(myprog: drgn.Program, first_input: Iterable[drgn.Object], line: str) -> Iterable[drgn.Object]: """ This function intends to integrate directly with the SDB REPL, such that the REPL will pass in the user-specified line, and this function is responsible for converting that string into the appropriate pipeline of Command objects, and executing it. """ target.set_prog(myprog) # # Build the pipeline by constructing each of the commands we want to # use and building a list of them. If a shell pipeline is constructed # at the end save it shell_cmd. # shell_cmd = None pipeline = [] for cmd, cmd_type in parser.tokenize(line): if cmd_type == parser.ExpressionType.CMD: name, *args = cmd if name not in get_registered_commands(): raise CommandNotFoundError(name) try: pipeline.append(get_registered_commands()[name](args, name)) except SystemExit as cmd_exit: # # The passed in arguments to each command will be parsed in # the command object's constructor. We use "argparse" to do # the argument parsing, and when that detects an error, it # will throw this exception. Rather than exiting the entire # SDB session, we only abort this specific pipeline by raising # a CommandArgumentsError. # raise CommandArgumentsError(name) from cmd_exit else: assert cmd_type == parser.ExpressionType.SHELL_CMD shell_cmd = cmd # # If we have a !, redirect stdout to a shell process. This avoids # having to have a custom printing function that we pass around and # use everywhere. We'll fix stdout to point back to the normal stdout # at the end. # if shell_cmd is not None: shell_proc = subprocess.Popen(shell_cmd, shell=True, stdin=subprocess.PIPE, encoding="utf-8") old_stdout = sys.stdout # # The type ignore below is due to the following false positive: # https://github.com/python/typeshed/issues/1229 # sys.stdout = shell_proc.stdin # type: ignore[assignment] try: if pipeline: pipeline[0].isfirst = True pipeline[-1].islast = True yield from execute_pipeline(first_input, pipeline) if shell_cmd is not None: shell_proc.stdin.flush() shell_proc.stdin.close() finally: if shell_cmd is not None: sys.stdout = old_stdout shell_proc.wait()
def invoke(myprog: drgn.Program, first_input: Iterable[drgn.Object], line: str) -> Iterable[drgn.Object]: """ This function intends to integrate directly with the SDB REPL, such that the REPL will pass in the user-specified line, and this function is responsible for converting that string into the appropriate pipeline of Command objects, and executing it. """ # pylint: disable=too-many-locals # pylint: disable=too-many-branches # pylint: disable=too-many-statements target.set_prog(myprog) shell_cmd = None # Parse the argument string. Each pipeline stage is delimited by # a pipe character "|". If there is a "!" character detected, then # pipe all the remaining outout into a subshell. lexer = shlex.shlex(line, posix=False, punctuation_chars="|!") lexer.wordchars += "();<>&[]" all_tokens = list(lexer) pipe_stages = [] tokens: List[str] = [] for num, token in enumerate(all_tokens): if token == "|": pipe_stages.append(" ".join(tokens)) tokens = [] elif token == "!": pipe_stages.append(" ".join(tokens)) if any(t == "!" for t in all_tokens[num + 1:]): print("Multiple ! not supported") return shell_cmd = " ".join(all_tokens[num + 1:]) break else: tokens.append(token) else: # We didn't find a !, so all remaining tokens are part of # the last pipe pipe_stages.append(" ".join(tokens)) # Build the pipeline by constructing each of the commands we want to # use and building a list of them. pipeline = [] for stage in pipe_stages: (name, _, args) = stage.strip().partition(" ") if name not in get_registered_commands(): raise CommandNotFoundError(name) try: pipeline.append(get_registered_commands()[name](args, name)) except SystemExit: # The passed in arguments to each command will be parsed in # the command object's constructor. We use "argparse" to do # the argument parsing, and when that detects an error, it # will throw this exception. Rather than exiting the entire # SDB session, we only abort this specific pipeline by raising # a CommandArgumentsError. raise CommandArgumentsError(name) pipeline[-1].islast = True # If we have a !, redirect stdout to a shell process. This avoids # having to have a custom printing function that we pass around and # use everywhere. We'll fix stdout to point back to the normal stdout # at the end. if shell_cmd is not None: shell_proc = subprocess.Popen(shell_cmd, shell=True, stdin=subprocess.PIPE, encoding="utf-8") old_stdout = sys.stdout # # The type ignore below is due to the following false positive: # https://github.com/python/typeshed/issues/1229 # sys.stdout = shell_proc.stdin # type: ignore[assignment] try: yield from execute_pipeline(first_input, pipeline) if shell_cmd is not None: shell_proc.stdin.flush() shell_proc.stdin.close() finally: if shell_cmd is not None: sys.stdout = old_stdout shell_proc.wait()