def is_bash_type(bash: pexpect.spawn, cmd: str) -> bool:
    typecmd = "type %s &>/dev/null && echo -n 0 || echo -n 1" % cmd
    bash.sendline(typecmd)
    bash.expect_exact(typecmd + "\r\n")
    result = bash.expect_exact(["0", "1"]) == 0
    bash.expect_exact(PS1)
    return result
Exemple #2
0
 def _run_expect(self, child: pexpect.spawn) -> Optional[str]:
     if self._database.has_password():
         child.expect('Enter password to unlock {}: '.format(
             self._database.get_path()))
         child.sendline(self._database.get_password())
     child.expect(pexpect.EOF)
     return child.before
Exemple #3
0
def run_cheribsd_command_or_die(qemu: pexpect.spawn, cmd: str, timeout=600):
    qemu.sendline(
        test_command +
        " ;if test $? -eq 0; then echo 'COMMAND' 'SUCCESSFUL'; else echo 'COMMAND' 'FAILED'; fi"
    )
    i = qemu.expect([
        pexpect.TIMEOUT, "COMMAND SUCCESSFUL", "COMMAND FAILED", PANIC,
        CHERI_TRAP, STOPPED
    ],
                    timeout=timeout)
    testtime = datetime.datetime.now() - run_tests_starttime
    if i == 0:  # Timeout
        return failure("timeout after",
                       testtime,
                       "waiting for tests: ",
                       str(qemu),
                       exit=False)
    elif i == 1:
        success("===> Tests completed!")
        success("Running tests took ", testtime)
        return True
    else:
        return failure("error after ",
                       testtime,
                       "while running tests : ",
                       str(qemu),
                       exit=False)
def assert_bash_exec(
        bash: pexpect.spawn, cmd: str, want_output: bool = False) -> str:

    # Send command
    bash.sendline(cmd)
    bash.expect_exact(cmd)

    # Find prompt, output is before it
    bash.expect_exact("\r\n" + PS1)
    output = bash.before

    # Retrieve exit status
    echo = "echo $?"
    bash.sendline(echo)
    got = bash.expect([
        r"^%s\r\n(\d+)\r\n%s" % (re.escape(echo), re.escape(PS1)),
        PS1,
        pexpect.EOF,
        pexpect.TIMEOUT,
    ])
    status = bash.match.group(1) if got == 0 else "unknown"

    assert status == "0", \
        'Error running "%s": exit status=%s, output="%s"' % \
        (cmd, status, output)
    if output:
        assert want_output, \
            'Unexpected output from "%s": exit status=%s, output="%s"' % \
            (cmd, status, output)
    else:
        assert not want_output, \
            'Expected output from "%s": exit status=%s, output="%s"' % \
            (cmd, status, output)

    return output
Exemple #5
0
def is_bash_type(bash: pexpect.spawn, cmd: str) -> bool:
    typecmd = "type %s &>/dev/null && echo -n 0 || echo -n 1" % cmd
    bash.sendline(typecmd)
    bash.expect_exact(typecmd + "\r\n")
    result = bash.expect_exact(["0", "1"]) == 0
    bash.expect_exact(PS1)
    return result
Exemple #6
0
def run_cheribsd_command(qemu: pexpect.spawn,
                         cmd: str,
                         expected_output=None,
                         error_output=None,
                         timeout=60):
    qemu.sendline(cmd)
    if expected_output:
        qemu.expect(expected_output)
    results = [
        pexpect.TIMEOUT, PROMPT, "/bin/sh: [\\w\\d_-]+: not found", CHERI_TRAP
    ]
    if error_output:
        results.append(error_output)
    i = qemu.expect(results, timeout=timeout)
    if i == 0:
        failure("timeout running ", cmd)
    elif i == 2:
        failure("Command not found!")
    elif i == 3:
        # wait up to 20 seconds for a prompt to ensure the dump output has been printed
        qemu.expect([pexpect.TIMEOUT, PROMPT], timeout=20)
        qemu.flush()
        failure("Got CHERI TRAP!")
    elif i == 4:
        # wait up to 5 seconds for a prompt to ensure the full output has been printed
        qemu.expect([pexpect.TIMEOUT, PROMPT], timeout=5)
        qemu.flush()
        failure("Matched error output ", error_output)
Exemple #7
0
def debug_kernel_panic(qemu: pexpect.spawn):
    # wait up to 10 seconds for a db prompt
    i = qemu.expect([pexpect.TIMEOUT, "db> "], timeout=10)
    if i == 1:
        qemu.sendline("bt")
    # wait for the backtrace
    qemu.expect([pexpect.TIMEOUT, "db> "], timeout=30)
    failure("GOT KERNEL PANIC!", exit=False)
Exemple #8
0
def assert_bash_exec(
    bash: pexpect.spawn,
    cmd: str,
    want_output: Optional[bool] = False,
    want_newline=True,
) -> str:
    """
    :param want_output: if None, don't care if got output or not
    """

    # Send command
    bash.sendline(cmd)
    bash.expect_exact(cmd)

    # Find prompt, output is before it
    bash.expect_exact("%s%s" % ("\r\n" if want_newline else "", PS1))
    output = bash.before

    # Retrieve exit status
    echo = "echo $?"
    bash.sendline(echo)
    got = bash.expect(
        [
            r"^%s\r\n(\d+)\r\n%s" % (re.escape(echo), re.escape(PS1)),
            PS1,
            pexpect.EOF,
            pexpect.TIMEOUT,
        ]
    )
    status = bash.match.group(1) if got == 0 else "unknown"

    assert status == "0", 'Error running "%s": exit status=%s, output="%s"' % (
        cmd,
        status,
        output,
    )
    if want_output is not None:
        if output:
            assert (
                want_output
            ), 'Unexpected output from "%s": exit status=%s, output="%s"' % (
                cmd,
                status,
                output,
            )
        else:
            assert (
                not want_output
            ), 'Expected output from "%s": exit status=%s, output="%s"' % (
                cmd,
                status,
                output,
            )

    return output
Exemple #9
0
 def _run_expect(self, child: pexpect.spawn) -> Optional[str]:
     if self._database.has_password():
         child.expect('Enter password to unlock {}: '.format(
             self._database.get_path()))
         child.sendline(self._database.get_password())
     if self._database_from.has_password():
         child.expect('Enter password to unlock {}: '.format(
             self._database_from.get_path()))
         child.sendline(self._database_from.get_password())
     child.expect('Database was not modified by merge operation.')
     return child.before
Exemple #10
0
def run_noop_test(qemu: pexpect.spawn, args: argparse.Namespace):
    import boot_cheribsd
    boot_cheribsd.success("Booted successfully")
    boot_cheribsd.run_cheribsd_command(qemu, "mount_smbfs --help", cheri_trap_fatal=False)
    boot_cheribsd.run_cheribsd_command(qemu, "/libexec/ld-cheri-elf.so.1 --help")
    poweroff_start = datetime.datetime.now()
    qemu.sendline("poweroff")
    i = qemu.expect(["Uptime:", pexpect.TIMEOUT, pexpect.EOF] + boot_cheribsd.FATAL_ERROR_MESSAGES, timeout=20)
    if i != 0:
        boot_cheribsd.failure("Poweroff " + ("timed out" if i == 1 else "failed"))
        return False
    i = qemu.expect([pexpect.TIMEOUT, pexpect.EOF], timeout=20)
    if i == 0:
        boot_cheribsd.failure("QEMU didn't exit after shutdown!")
        return False
    boot_cheribsd.success("Poweroff took: ", datetime.datetime.now() - poweroff_start)
    return True
Exemple #11
0
def wait_for(child: pexpect.spawn, pattern: str, answer: str):
    countdown = 1024
    while True:
        hit = child.expect([pattern, pexpect.TIMEOUT, pexpect.EOF], timeout=15)
        if hit == 0:
            if answer and len(answer):
                child.sendline(answer)
            return 0
        if hit == 1:
            print('timeout on pattern : ', pattern)
            sigterm(signal.SIGTERM, None)
            return 1
        if hit == 2:
            countdown -= 1
            if countdown == 0:
                print('too much EOF on pattern : ', pattern)
                return 2
Exemple #12
0
def assert_bash_exec(bash: pexpect.spawn,
                     cmd: str,
                     want_output: bool = False) -> str:

    # Send command
    bash.sendline(cmd)
    bash.expect_exact(cmd)

    # Find prompt, output is before it
    bash.expect_exact("\r\n" + PS1)
    output = bash.before

    # Retrieve exit status
    echo = "echo $?"
    bash.sendline(echo)
    got = bash.expect([
        r"^%s\r\n(\d+)\r\n%s" % (re.escape(echo), re.escape(PS1)),
        PS1,
        pexpect.EOF,
        pexpect.TIMEOUT,
    ])
    status = bash.match.group(1) if got == 0 else "unknown"

    assert status == "0", 'Error running "%s": exit status=%s, output="%s"' % (
        cmd,
        status,
        output,
    )
    if output:
        assert want_output, (
            'Unexpected output from "%s": exit status=%s, output="%s"' %
            (cmd, status, output))
    else:
        assert not want_output, (
            'Expected output from "%s": exit status=%s, output="%s"' %
            (cmd, status, output))

    return output
Exemple #13
0
def setup_ssh(qemu: pexpect.spawn, pubkey: Path):
    run_cheribsd_command(qemu, "mkdir -p /root/.ssh")
    contents = pubkey.read_text(encoding="utf-8").strip()
    run_cheribsd_command(
        qemu,
        "echo " + shlex.quote(contents) + " >> /root/.ssh/authorized_keys")
    run_cheribsd_command(qemu, "chmod 600 /root/.ssh/authorized_keys")
    run_cheribsd_command(
        qemu,
        "echo 'PermitRootLogin without-password' >> /etc/ssh/sshd_config")
    # TODO: check for bluehive images without /sbin/service
    run_cheribsd_command(qemu,
                         "cat /root/.ssh/authorized_keys",
                         expected_output="ssh-")
    run_cheribsd_command(qemu, "grep -n PermitRootLogin /etc/ssh/sshd_config")
    qemu.sendline("service sshd restart")
    i = qemu.expect([pexpect.TIMEOUT, "service: not found", "Starting sshd."],
                    timeout=120)
    if i == 0:
        failure("Timed out setting up SSH keys")
    qemu.expect(PROMPT)
    time.sleep(2)  # sleep for two seconds to avoid a rejection
    success("===> SSH authorized_keys set up")
Exemple #14
0
def worker_run_task_in_subprocess(sp: pexpect.spawn, *args, **kwargs):
    # TODO check the buffer size rules for stdin and stdout and directly
    # stream bytes instead of saving them to the file system. See 'encoding='
    # argument in pexpect.spawn constructor

    # Serialize args and kwargs to a file
    path_to_args = os.path.join(tempfile.gettempdir(), f'{uuid.uuid4()}.pkl')
    with open(path_to_args, 'wb') as args_file:
        cloudpickle.dump([args, kwargs], args_file)

    # Prepare result and error files
    path_to_result = os.path.join(tempfile.gettempdir(), f'{uuid.uuid4()}.pkl')
    path_to_error = os.path.join(tempfile.gettempdir(), f'{uuid.uuid4()}.pkl')

    # Communicate to the subprocess
    sp.expect('Ready for next task.')  # Printed in the subprocess loop
    sp.sendline(path_to_args)
    sp.sendline(path_to_result)
    sp.sendline(path_to_error)

    # Wait for the subprocess to complete
    sp.expect('Task complete.')  # Printed in the subprocess loop

    # Check for an exception to be reraised
    error: SerializableException = None
    try:
        with open(path_to_error, 'rb') as error_file:
            error: SerializableException = cloudpickle.load(error_file)
    except FileNotFoundError:
        pass

    # Check for a result to be returned
    result = None
    try:
        with open(path_to_result, 'rb') as result_file:
            result = cloudpickle.load(result_file)
    except FileNotFoundError as e:
        pass

    # Remove the args, result and error files
    try:
        os.remove(path_to_args)
    except (FileNotFoundError, NameError):
        pass
    try:
        os.remove(path_to_result)
    except (FileNotFoundError, NameError):
        pass
    try:
        os.remove(path_to_error)
    except (FileNotFoundError, NameError):
        pass

    # Finish up (reraise error or return result)
    if error is not None:
        error.reraise()
    return result
Exemple #15
0
    def connect(self, connection=None):
        """
        See :meth:`BaseShell.connect` for more information.
        """
        connection = connection or self._default_connection or '0'

        if connection in self._connections and self.is_connected(connection):
            raise AlreadyConnectedError(connection)

        # Create a child process
        spawn = Spawn(
            self._get_connect_command().strip(),
            **self._spawn_args
        )
        self._connections[connection] = spawn

        try:
            # If connection is via user
            if self._user is not None:
                spawn.expect(
                    [self._user_match], timeout=self._timeout
                )
                spawn.sendline(self._user)

            # If connection is via password
            if self._password is not None:
                spawn.expect(
                    [self._password_match], timeout=self._timeout
                )
                spawn.sendline(self._password)

            # Setup shell before using it
            self._setup_shell(connection)

            # Execute initial command if required
            if self._initial_command is not None:
                spawn.expect(
                    self._prompt, timeout=self._timeout
                )
                spawn.sendline(self._initial_command)

            # Wait for command response to match the prompt
            spawn.expect(
                self._prompt, timeout=self._timeout
            )

        except:
            # Always remove bad connections if it failed
            del self._connections[connection]
            raise

        # Set connection as default connection if required
        if self.default_connection is None:
            self.default_connection = connection
Exemple #16
0
    def connect(self, connection=None):
        """
        See :meth:`BaseShell.connect` for more information.
        """
        connection = connection or self._default_connection or '0'

        if connection in self._connections and self.is_connected(connection):
            raise AlreadyConnectedError(connection)

        # Create a child process
        spawn = Spawn(self._get_connect_command().strip(), **self._spawn_args)
        self._connections[connection] = spawn

        try:
            # If connection is via user
            if self._user is not None:
                spawn.expect([self._user_match], timeout=self._timeout)
                spawn.sendline(self._user)

            # If connection is via password
            if self._password is not None:
                spawn.expect([self._password_match], timeout=self._timeout)
                spawn.sendline(self._password)

            # Setup shell before using it
            self._setup_shell(connection)

            # Execute initial command if required
            if self._initial_command is not None:
                spawn.expect(self._prompt, timeout=self._timeout)
                spawn.sendline(self._initial_command)

            # Wait for command response to match the prompt
            spawn.expect(self._prompt, timeout=self._timeout)

        except:
            # Always remove bad connections if it failed
            del self._connections[connection]
            raise

        # Set connection as default connection if required
        if self.default_connection is None:
            self.default_connection = connection
Exemple #17
0
def send_cmd(child: pexpect.spawn, cmd):
    prompt = ['# ', '>>> ', '> ', '\$ ']
    child.sendline(cmd)
    child.expect(prompt)
    print(child.before.decode())
Exemple #18
0
 def spawnAction(self, sp: spawn):
     print("sendline {}".format(self))
     yield sp.sendline(self)
Exemple #19
0
def runtests(
    qemu: pexpect.spawn,
    test_archives: list,
    test_command: str,
    smb_dir: typing.Optional[Path],
    ssh_keyfile: typing.Optional[str],
    ssh_port: typing.Optional[int],
    timeout: int,
    test_function: "typing.Callable[[pexpect.spawn, ...], bool]" = None
) -> bool:
    setup_tests_starttime = datetime.datetime.now()
    # disable coredumps, otherwise we get no space left on device errors
    run_cheribsd_command(qemu, "sysctl kern.coredump=0")
    # create tmpfs on opt
    run_cheribsd_command(
        qemu, "mkdir -p /opt && mount -t tmpfs -o size=500m tmpfs /opt")
    # ensure that /usr/local exists and if not create it as a tmpfs (happens in the minimal image)
    run_cheribsd_command(
        qemu,
        "mkdir -p /usr/local && mount -t tmpfs -o size=300m tmpfs /usr/local")
    run_cheribsd_command(qemu, "df -h", expected_output="/opt")
    info("\nWill transfer the following archives: ", test_archives)
    # strip the .pub from the key file
    for archive in test_archives:
        if smb_dir:
            run_host_command(["tar", "xJf", str(archive), "-C", str(smb_dir)])
        else:
            # Extract to temporary directory and scp over
            with tempfile.TemporaryDirectory(dir=os.getcwd(),
                                             prefix="test_files_") as tmp:
                run_host_command(["tar", "xJf", str(archive), "-C", tmp])
                private_key = str(Path(ssh_keyfile).with_suffix(""))
                scp_cmd = [
                    "scp", "-B", "-r", "-P",
                    str(ssh_port), "-o", "StrictHostKeyChecking=no", "-o",
                    "UserKnownHostsFile=/dev/null", "-i",
                    shlex.quote(private_key), ".", "root@localhost:/"
                ]
                # use script for a fake tty to get progress output from scp
                if sys.platform.startswith("linux"):
                    scp_cmd = [
                        "script", "--quiet", "--return", "--command",
                        " ".join(scp_cmd), "/dev/null"
                    ]
                run_host_command(["ls", "-la"], cwd=tmp)
                run_host_command(scp_cmd, cwd=tmp)
    if test_archives:
        time.sleep(5)  # wait 5 seconds to make sure the disks have synced
    # See how much space we have after running scp
    run_cheribsd_command(qemu, "df -h", expected_output="/opt")
    success("Preparing test enviroment took ",
            datetime.datetime.now() - setup_tests_starttime)

    run_tests_starttime = datetime.datetime.now()
    # Run the tests (allowing custom test functions)
    if test_function:
        return test_function(qemu, ssh_keyfile=ssh_keyfile, ssh_port=ssh_port)

    qemu.sendline(
        test_command +
        " ;if test $? -eq 0; then echo 'TESTS' 'COMPLETED'; else echo 'TESTS' 'FAILED'; fi"
    )
    i = qemu.expect(
        [pexpect.TIMEOUT, "TESTS COMPLETED", "TESTS FAILED", PANIC, STOPPED],
        timeout=timeout)
    testtime = datetime.datetime.now() - run_tests_starttime
    if i == 0:  # Timeout
        return failure("timeout after",
                       testtime,
                       "waiting for tests: ",
                       str(qemu),
                       exit=False)
    elif i == 1:
        success("===> Tests completed!")
        success("Running tests took ", testtime)
        run_cheribsd_command(
            qemu, "df -h",
            expected_output="/opt")  # see how much space we have now
        return True
    else:
        return failure("error after ",
                       testtime,
                       "while running tests : ",
                       str(qemu),
                       exit=False)
Exemple #20
0
    def connect(self, connection=None):
        """
        See :meth:`BaseShell.connect` for more information.
        """
        connection = connection or self._default_connection or '0'

        if connection in self._connections and self.is_connected(connection):
            raise AlreadyConnectedError(connection)

        # Inject framework logger to the spawn object
        spawn_args = {
            'logfile': get_logger(
                OrderedDict([
                    ('node_identifier', self._node_identifier),
                    ('shell_name', self._shell_name),
                    ('connection', connection)
                ]),
                category='pexpect'
            ),
        }

        # Create a child process
        spawn_args.update(self._spawn_args)

        spawn = Spawn(
            self._get_connect_command().strip(),
            **spawn_args
        )

        # Add a connection logger
        # Note: self._node and self._name were added to this shell in the
        #       node's call to its _register_shell method.
        spawn._connection_logger = get_logger(
            OrderedDict([
                ('node_identifier', self._node_identifier),
                ('shell_name', self._shell_name),
                ('connection', connection)
            ]),
            category='connection'
        )

        self._connections[connection] = spawn

        try:
            # If connection is via user
            if self._user is not None:
                spawn.expect(
                    [self._user_match], timeout=self._timeout
                )
                spawn.sendline(self._user)

            # If connection is via password
            if self._password is not None:
                spawn.expect(
                    [self._password_match], timeout=self._timeout
                )
                spawn.sendline(self._password)

            # Setup shell before using it
            self._setup_shell(connection)

            # Execute initial command if required
            if self._initial_command is not None:
                spawn.expect(
                    self._prompt, timeout=self._timeout
                )
                spawn.sendline(self._initial_command)

            # Wait for command response to match the prompt
            spawn.expect(
                self._prompt, timeout=self._timeout
            )

        except:
            # Always remove bad connections if it failed
            del self._connections[connection]
            raise

        # Set connection as default connection if required
        if self.default_connection is None:
            self.default_connection = connection