Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an askfirst initializer for dealing with "askfirst" TTYs #98

Merged
merged 3 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Documentation/modules/machine_board.rst
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,6 @@ and login.

.. autoclass:: tbot.machine.board.LinuxBootLogin
:members:

.. autoclass:: tbot.machine.board.AskfirstInitializer
:members:
2 changes: 2 additions & 0 deletions selftest/testmachines.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ def _init_pre_connect(self) -> Iterator:
echo "Pretending to boot Linux..."
echo ""
echo "[0.0000] Welcome to the simulation."
read -p "Please press Enter to activate this console. GARBLE GARBLE"
read -p "login: "
read -s -p "password: "
echo ""
Expand Down Expand Up @@ -309,6 +310,7 @@ class MockhwBoardUBoot(

class MockhwBoardLinux(
board.LinuxUbootConnector,
board.AskfirstInitializer,
board.LinuxBootLogin,
linux.Bash,
tbot.role.Role,
Expand Down
2 changes: 2 additions & 0 deletions tbot/machine/board/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
from .uboot import UBootShell, UBootAutobootIntercept # isort: skip
from .board import PowerControl, Board, BoardMachineBase, Connector # isort: skip
from .linux import LinuxUbootConnector, LinuxBootLogin # isort: skip
from .linux import AskfirstInitializer # isort: skip
from ..linux.special import Then, AndThen, OrElse, Raw # isort: skip

__all__ = (
"AndThen",
"AskfirstInitializer",
"Board",
"BoardMachineBase",
"Connector",
Expand Down
70 changes: 68 additions & 2 deletions tbot/machine/board/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ class StandaloneLinux(
.. versionadded:: 0.10.0
"""

_boot_start: float
# Used for timeout tracking
_boot_start: typing.Optional[float] = None

bootlog: str
"""Log of kernel-messages which were output during boot."""
Expand Down Expand Up @@ -129,6 +130,8 @@ def password(self) -> typing.Optional[str]:
def _timeout_remaining(self) -> typing.Optional[float]:
if self.boot_timeout is None:
return None
if self._boot_start is None:
self._boot_start = time.monotonic()
remaining = self.boot_timeout - (time.monotonic() - self._boot_start)
if remaining <= 0:
raise TimeoutError
Expand All @@ -141,7 +144,8 @@ def _init_machine(self) -> typing.Iterator:
ev = cx.enter_context(self._linux_boot_event())
cx.enter_context(self.ch.with_stream(ev))

self._boot_start = time.monotonic()
if self._boot_start is None:
self._boot_start = time.monotonic()

self.ch.read_until_prompt(
prompt=self.login_prompt, timeout=self.boot_timeout
Expand Down Expand Up @@ -197,6 +201,68 @@ def _init_machine(self) -> typing.Iterator:
yield None


class AskfirstInitializer(machine.Initializer, LinuxBoot):
"""
Initializer to deal with ``askfirst`` TTYs.

On some boards, the console is configured with ``askfirst`` which means that
the getty for logging in is only spawned after an initial ENTER is sent.
This initializer takes care of that by first waiting for the ``askfirst``
prompt and then sending ENTER.

The ``askfirst`` prompt can be customized with the ``askfirst_prompt`` attribute.

**Example**:

.. code-block:: python

from tbot.machine import board, linux

class StandaloneLinux(
board.Connector,
board.AskfirstInitializer,
board.LinuxBootLogin,
linux.Bash,
):
# board.LinuxBootLogin config:
username = "root"
password = "hunter2"

.. versionadded:: UNRELEASED
"""

askfirst_prompt = "Please press Enter to activate this console."
"""
Prompt that indicates the board is waiting for ``askfirst`` confirmation.
"""

# For proper integration with LinuxBootLogin
boot_timeout: typing.Optional[float] = None
_boot_start: typing.Optional[float] = None
bootlog: str

@contextlib.contextmanager
def _init_machine(self) -> typing.Iterator:
# This ExitStack holds the boot event until we successfully reached the
# askfirst prompt. Then it releases the boot event such that further
# initializers like LinuxBootLogin can continue using it.
with contextlib.ExitStack() as cx:
ev = cx.enter_context(self._linux_boot_event())

with self.ch.with_stream(ev):
if self._boot_start is None:
self._boot_start = time.monotonic()

# Using expect() instead of read_until_prompt() so we are not
# confused by garbage following the prompt.
self.ch.expect(self.askfirst_prompt, timeout=self.boot_timeout)
self.ch.sendline("")

cx.pop_all()

yield None


Self = typing.TypeVar("Self", bound="LinuxUbootConnector")


Expand Down