Esempio n. 1
0
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()
Esempio n. 2
0
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()