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 monitor application to EVE #4410

Merged
merged 11 commits into from
Nov 28, 2024
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
11 changes: 8 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -252,16 +252,19 @@ QEMU_OPTS_TPM=$(QEMU_OPTS_TPM_$(TPM:%=Y)_$(ZARCH))
QEMU_OPTS_amd64=-smbios type=1,serial=$(QEMU_EVE_SERIAL)
QEMU_OPTS_arm64=-smbios type=1,serial=$(QEMU_EVE_SERIAL) -drive file=fat:rw:$(dir $(DEVICETREE_DTB)),label=QEMU_DTB,format=vvfat
QEMU_OPTS_riscv64=-kernel $(UBOOT_IMG)/u-boot.bin -device virtio-blk,drive=uefi-disk
QEMU_OPTS_COMMON= -m $(QEMU_MEMORY) -smp 4 -display none $(QEMU_OPTS_BIOS) \
QEMU_OPTS_NO_DISPLAY=-display none
QEMU_OPTS_VGA_DISPLAY=-vga std
QEMU_OPTS_COMMON= -m $(QEMU_MEMORY) -smp 4 $(QEMU_OPTS_BIOS) \
-serial mon:stdio \
-global ICH9-LPC.noreboot=false -watchdog-action reset \
-global ICH9-LPC.noreboot=false -watchdog-action reset \
-rtc base=utc,clock=rt \
-netdev user,id=eth0,net=$(QEMU_OPTS_NET1),dhcpstart=$(QEMU_OPTS_NET1_FIRST_IP),hostfwd=tcp::$(SSH_PORT)-:22$(QEMU_TFTP_OPTS) -device virtio-net-pci,netdev=eth0,romfile="" \
-netdev user,id=eth1,net=$(QEMU_OPTS_NET2),dhcpstart=$(QEMU_OPTS_NET2_FIRST_IP) -device virtio-net-pci,netdev=eth1,romfile="" \
-device nec-usb-xhci,id=xhci \
-qmp unix:$(CURDIR)/qmp.sock,server,wait=off
QEMU_OPTS_CONF_PART=$(shell [ -d "$(CONF_PART)" ] && echo '-drive file=fat:rw:$(CONF_PART),format=raw')
QEMU_OPTS=$(QEMU_OPTS_COMMON) $(QEMU_ACCEL) $(QEMU_OPTS_$(ZARCH)) $(QEMU_OPTS_CONF_PART) $(QEMU_OPTS_TPM)
QEMU_OPTS=$(QEMU_OPTS_NO_DISPLAY) $(QEMU_OPTS_COMMON) $(QEMU_ACCEL) $(QEMU_OPTS_$(ZARCH)) $(QEMU_OPTS_CONF_PART) $(QEMU_OPTS_TPM)
QEMU_OPTS_GUI=$(QEMU_OPTS_VGA_DISPLAY) $(QEMU_OPTS_COMMON) $(QEMU_ACCEL) $(QEMU_OPTS_$(ZARCH)) $(QEMU_OPTS_CONF_PART) $(QEMU_OPTS_TPM)
# -device virtio-blk-device,drive=image -drive if=none,id=image,file=X
# -device virtio-net-device,netdev=user0 -netdev user,id=user0,hostfwd=tcp::1234-:22

Expand Down Expand Up @@ -566,6 +569,8 @@ run-installer-net: $(BIOS_IMG) $(IPXE_IMG) $(DEVICETREE_DTB) $(SWTPM) GETTY
# run MUST NOT change the current dir; it depends on the output being correct from a previous build
run-live run: $(BIOS_IMG) $(DEVICETREE_DTB) $(SWTPM) GETTY
$(QEMU_SYSTEM) $(QEMU_OPTS) -drive file=$(CURRENT_IMG),format=$(IMG_FORMAT),id=uefi-disk
run-live-gui run: $(BIOS_IMG) $(DEVICETREE_DTB) $(SWTPM) GETTY
$(QEMU_SYSTEM) $(QEMU_OPTS_GUI) -drive file=$(CURRENT_IMG),format=$(IMG_FORMAT),id=uefi-disk

run-target: $(BIOS_IMG) $(DEVICETREE_DTB) $(SWTPM) GETTY
$(QEMU_SYSTEM) $(QEMU_OPTS) -drive file=$(TARGET_IMG),format=$(IMG_FORMAT)
Expand Down
41 changes: 41 additions & 0 deletions docs/LOCAL-TUI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# TUI (Text User Interface) for the local operator

EVE has a user-friendly TUI (Text User Interface) that can be used to interact with the system.
The implementation is consists of two parts

1. Client application responsible for rendering the TUI, sending user input to the server, and handling asynchronous server notification. The client is written in Rust and hosted at [https://github.com/lf-edge/eve-monitor-rs](https://github.com/lf-edge/eve-monitor-rs). Corresponding Dockerfile and LinuxKit build files are located at `pkg/monitor`
2. Server part is implemented inside [pkg/pillar/cmd/monitor](../pkg/pillar/cmd/monitor/)

The client communicates with the server over UNIX socket

## TTY and serial console

The UI is rendered on a local TTY (/dev/tty2) only i.e. on a physical monitor attached to the system. Neither Serial Console nor SSH connection has access to TUI. It is done to ensure the physical presence of the operator.

## /dev/ttyX vs /dev/console

There are two distinguishable console devices in Linux `/dev/console` and `/dev/ttyX`. The later points to a particular virtual terminal device and the former points to *currently active* TTY device. The user can switch between virtual terminals by using `Alt+Fx` or `Alt+<,>` keys. When the current TTY is set `/dev/console` tracks this change and always points to to the current terminal

Monitor application is spawned on a `/dev/tty2` using a `openvt` utility while the rest of the applications are spawned on the default kernel console defined by `console=` parameters on the kernel command line. When the application is in focus (`/dev/tty2` is an active console) writing to `/dev/console` which points to the same device corrupts TUI thus it cannot be used by other services in the system to produce the output. At least when `/dev/tty2` is a current console.

From the other hand `/dev/tty` (no digit at the end!) device always points to a TTY *in the context of running process*. This device can be used instead of `/dev/console` by other services for the output.

## Limitations of linux terminal

Rust application can be built and run on Linux host for testing and development purposes. When running on a host its terminal is used for rendering. Host terminals ( e.g. `TERM=xterm`) are very different in capabilities compared to the built in Linux terminal which is used for `/dev/ttyX` (`TERM=linux`) devices. The major differences important for monitor application are

* Number of supported colors.

`linux` terminal can use only 8 colors for foreground and 8 colors for background colors. In contrast host terminals can easily display 256 colors and more

* Limited number of pseudo-graphics glyphs.

These limitations can be relaxed by using a custom font with 256 glyphs compared to the standard one that uses 512 glyphs. In this case an extra bit can be used to render 16 colors. Besides, extra pseudo-graphics glyphs can be added instead of unused characters to display e.g. rounded boxes.

As of now a standard font is used so the look of the application on the host and on EVE is different

* Key handling.

By default `linux` terminal cannot properly handle many key combinations e.g. `PgDwn`, `Ctrl+left, Ctrl + right`, etc. A custom key map must be set to properly handle required combinations. It is done in [pkg/monitor/run-monitor.sh](../pkg/monitor/run-monitor.sh) by calling `loadkeys` utility

As of now only `Ctrl + [left|right|up|down]` are properly handled.
4 changes: 4 additions & 0 deletions images/rootfs.yml.in
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ onboot:
- name: measure-config
image: MEASURE_CONFIG_TAG
services:
- name: monitor
image: MONITOR_TAG
cgroupsPath: /eve/services/monitor
oomScoreAdj: -999
- name: newlogd
image: NEWLOGD_TAG
cgroupsPath: /eve/services/newlogd
Expand Down
78 changes: 78 additions & 0 deletions pkg/monitor/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Copyright (c) 2024 Zededa, Inc.
# SPDX-License-Identifier: Apache-2.0

ARG MONITOR_RS_VERSION=v0.1.1
ARG RUST_VERSION=lfedge/eve-rust:1.80.1
FROM --platform=$BUILDPLATFORM ${RUST_VERSION} AS toolchain-base
ARG TARGETARCH

FROM toolchain-base AS target-amd64
ENV CARGO_BUILD_TARGET="x86_64-unknown-linux-musl"

FROM toolchain-base AS target-arm64
ENV CARGO_BUILD_TARGET="aarch64-unknown-linux-musl"

FROM toolchain-base AS target-riscv64
ENV CARGO_BUILD_TARGET="riscv64gc-unknown-linux-gnu"

FROM target-$TARGETARCH AS toolchain
ARG MONITOR_RS_VERSION
RUN echo "Cargo target: $CARGO_BUILD_TARGET"

FROM toolchain AS planer
ADD --keep-git-dir=true https://github.com/lf-edge/eve-monitor-rs.git#${MONITOR_RS_VERSION} /app

Check failure on line 23 in pkg/monitor/Dockerfile

View workflow job for this annotation

GitHub Actions / yetus

hadolint: invalid flag: --keep-git-dir

WORKDIR /app
# create a recipe
RUN cargo chef prepare --recipe-path recipe.json

FROM toolchain AS cacher
# copy the recipe
WORKDIR /app
COPY --from=planer /app/recipe.json recipe.json
# build the dependencies
RUN cargo chef cook --release --recipe-path recipe.json

# building the final image
FROM toolchain AS builder
ADD --keep-git-dir=true https://github.com/lf-edge/eve-monitor-rs.git#${MONITOR_RS_VERSION} /app
WORKDIR /app

# copy prebuilt dependencies
# and the cargo directory with crates.io index
COPY --from=cacher /app/target /app/target
COPY --from=cacher $CARGO_HOME $CARGO_HOME
RUN echo "Cargo target: $CARGO_BUILD_TARGET"

RUN cargo build --release
RUN cargo sbom > sbom.spdx.json
RUN cp /app/target/$CARGO_BUILD_TARGET/release/monitor /app/target/


FROM lfedge/eve-alpine:591df01e581889c3027514c8a91feaca1c8ad49f AS runtime
ENV PKGS="kbd pciutils usbutils"
RUN eve-alpine-deploy.sh

FROM scratch
COPY --from=runtime /out/usr/bin/openvt /usr/bin/openvt
COPY --from=runtime /out/usr/bin/loadkeys /usr/bin/loadkeys
COPY --from=runtime /out/bin/dmesg /usr/bin/dmesg
COPY --from=runtime /out/usr/bin/lsusb /usr/bin/lsusb
COPY --from=runtime /out/usr/bin/lspci /usr/bin/lspci
COPY --from=runtime /out/usr/share/keymaps/xkb/us.map.gz /usr/share/keymaps/xkb/us.map.gz
COPY --from=runtime /out/lib /lib
COPY --from=runtime /out/usr/lib /usr/lib

# copy busybox and install all symbolic links but just for debugging
COPY --from=runtime /out/bin/busybox /bin/busybox
# using an 'exec' form of RUN, shell form of 'RUN'
# command expects /bin/sh which doesn't yet exist
RUN [ "/bin/busybox", "--install", "-s", "/bin" ]
COPY --from=runtime /out/usr/bin/du /usr/bin/du

COPY --from=builder /app/target/monitor /sbin/monitor
COPY --from=builder /app/sbom.spdx.json /sbin/sbom.spdx.json
COPY run-monitor.sh /sbin/run-monitor.sh
COPY monitor-wrapper.sh /sbin/monitor-wrapper.sh

CMD ["/sbin/run-monitor.sh"]
57 changes: 57 additions & 0 deletions pkg/monitor/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright (c) 2024 Zededa, Inc.
# SPDX-License-Identifier: Apache-2.0

org: lfedge
image: eve-monitor
network: yes
config:
pid: host
binds:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same question here, is all these access needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shjala probably not.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you go through them, pick the ones you really need and add a comment above each one that is needed stating why it is needed? it makes creating apparmor profile much easier.

same for this.

# for tty/console devices
- /dev:/dev
# for temporary files and UNIX socket
- /run:/run
# to save logs
- /persist:/persist:rshared,rbind

devices:
- path: "/dev/tty"
type: c
major: 5
minor: 0
mode: 0666
- path: "/dev/console"
type: c
major: 5
minor: 1
mode: 0666
- path: "/dev/tty0"
type: c
major: 4
minor: 0
mode: 0666
- path: "/dev/ttyS0"
type: c
major: 4
minor: 64
mode: 0666
- path: "/dev/ttyAMA0"
type: c
major: 204
minor: 64
mode: 0666
# we run the monitor on tty2
- path: "/dev/tty2"
type: c
major: 4
minor: 2
mode: 0666
# direct access to the kernel log
- path: "/dev/kmsg"
type: c
major: 1
minor: 11
mode: 0660
capabilities:
- all
rootfsPropagation: shared
9 changes: 9 additions & 0 deletions pkg/monitor/monitor-wrapper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh

# Copyright (c) 2024 Zededa, Inc.
# SPDX-License-Identifier: Apache-2.0

/sbin/monitor
# wait for key press so user can see the panic info
# shellcheck disable=SC3045,SC2162
read -r -p "Press any key to continue... " -n1 -s
27 changes: 27 additions & 0 deletions pkg/monitor/run-monitor.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/sh

# Copyright (c) 2024 Zededa, Inc.
# SPDX-License-Identifier: Apache-2.0

echo "Running EVE monitor"
export RUST_BACKTRACE=1

# leave only panic on console
dmesg -n 1

# setup keymap
loadkeys -s us - <<EOF
control keycode 103 = F103
control keycode 108 = F108
control keycode 106 = F106
control keycode 105 = F105
string F103 = "\033[1;5A"
string F108 = "\033[1;5B"
string F106 = "\033[1;5C"
string F105 = "\033[1;5D"
EOF

# run monitor on 2nd console in infinite loop
while true; do
openvt -c 2 -s -f -w -- /sbin/monitor-wrapper.sh
done
2 changes: 1 addition & 1 deletion pkg/pillar/agentlog/agentlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func RebootReason(reason string, bootReason types.BootReason, agentName string,
}

// Printing the reboot reason to the console
filename = "/dev/console"
Copy link
Contributor

@rene rene Nov 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some scripts from pkg/pillar/scripts also write to /dev/console, doesn't make sense to change them as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rene I checked those and they do not affect monitor app. They actually should write to console so we see the output on /dev/ttyS* if connected. Monitor is started after they finish so it should be ok. I still need to modify getty to properly handle console devices anyways

filename = "/dev/tty"

operation := "Rebooting"
if bootReason == types.BootReasonPoweroffCmd {
Expand Down
Loading
Loading