Compare commits
2 Commits
main
...
feat/test-
Author | SHA1 | Date |
---|---|---|
|
df609f80af | |
|
b396b1648b |
|
@ -31,11 +31,12 @@ FROM stagex/core-libunwind:sx2025.02.0@sha256:99e2574ace4f7dfa3d8bfc93ab69e1fd5f
|
||||||
FROM stagex/user-libusb:sx2025.02.0@sha256:b78ca9194fdb8dfb7b7177d16a156fac21e6c9822a0c35a17841400bc1a27f68 AS user-libusb
|
FROM stagex/user-libusb:sx2025.02.0@sha256:b78ca9194fdb8dfb7b7177d16a156fac21e6c9822a0c35a17841400bc1a27f68 AS user-libusb
|
||||||
FROM stagex/user-libzbar:sx2025.02.0@sha256:44ad89a661bc395d6b49d89a0367846f7bee40b198780777c5c7b1b3c0d49a0e AS user-libzbar
|
FROM stagex/user-libzbar:sx2025.02.0@sha256:44ad89a661bc395d6b49d89a0367846f7bee40b198780777c5c7b1b3c0d49a0e AS user-libzbar
|
||||||
FROM stagex/core-libzstd:sx2025.02.0@sha256:23cd975a27e218c5398efd17e1f8c491d31969ab674d3468dbf8b75ba40611ad AS core-libzstd
|
FROM stagex/core-libzstd:sx2025.02.0@sha256:23cd975a27e218c5398efd17e1f8c491d31969ab674d3468dbf8b75ba40611ad AS core-libzstd
|
||||||
FROM stagex/user-linux-airgap:sx2025.02.0@sha256:a2dbeace3ce085ba487e88b3968fea1ec29ce392f691d28c4b183e1ed9c0df4d AS user-linux-airgap
|
FROM local-airgap AS user-linux-airgap
|
||||||
FROM stagex/user-lzo:sx2025.02.0@sha256:b71c2944073f3fbc1fe543b9e4dfc4f59ec013a763a6209ded77b8f8bd0a33b4 AS user-lzo
|
FROM stagex/user-lzo:sx2025.02.0@sha256:b71c2944073f3fbc1fe543b9e4dfc4f59ec013a763a6209ded77b8f8bd0a33b4 AS user-lzo
|
||||||
FROM stagex/user-mtools:sx2025.02.0@sha256:ea76e5f82f9833274a4438e9706779afd9b1c0b197c984c9d54c9887163ffb42 AS user-mtools
|
FROM stagex/user-mtools:sx2025.02.0@sha256:ea76e5f82f9833274a4438e9706779afd9b1c0b197c984c9d54c9887163ffb42 AS user-mtools
|
||||||
FROM stagex/core-musl:sx2025.02.0@sha256:23d0614f60449015add2369959c89a6ea08e208302773b9a0811ce1195afc3a4 AS core-musl
|
FROM stagex/core-musl:sx2025.02.0@sha256:23d0614f60449015add2369959c89a6ea08e208302773b9a0811ce1195afc3a4 AS core-musl
|
||||||
FROM stagex/user-nettle:sx2025.02.0@sha256:ec81bb00c990ceee3047632216387d350d1e753cc2a150f3d12c27872832c9ff AS user-nettle
|
FROM stagex/user-nettle:sx2025.02.0@sha256:ec81bb00c990ceee3047632216387d350d1e753cc2a150f3d12c27872832c9ff AS user-nettle
|
||||||
|
FROM stagex/user-numactl:latest as user-numactl
|
||||||
FROM stagex/user-npth:sx2025.02.0@sha256:82462e0c12a8d3e3196ea8b3a647e75efd6d1cc0a84b091a0bb844e0c623d9be AS user-npth
|
FROM stagex/user-npth:sx2025.02.0@sha256:82462e0c12a8d3e3196ea8b3a647e75efd6d1cc0a84b091a0bb844e0c623d9be AS user-npth
|
||||||
FROM stagex/user-numactl:sx2025.02.0@sha256:b89612d78567874127522af2c73d5d0a7d5fffbb37bf4b2193affa679d7f367c AS user-numactl
|
FROM stagex/user-numactl:sx2025.02.0@sha256:b89612d78567874127522af2c73d5d0a7d5fffbb37bf4b2193affa679d7f367c AS user-numactl
|
||||||
FROM stagex/user-openpgp-card-tools:sx2025.02.0@sha256:77d9f2d949548c22badbf29ff8e43a3329ef568c77c66ddbde8d9e2e2dfecb1b AS user-openpgp-card-tools
|
FROM stagex/user-openpgp-card-tools:sx2025.02.0@sha256:77d9f2d949548c22badbf29ff8e43a3329ef568c77c66ddbde8d9e2e2dfecb1b AS user-openpgp-card-tools
|
||||||
|
@ -43,7 +44,7 @@ FROM stagex/user-opensc:sx2025.02.0@sha256:985c0ea0d7ca91b0ed3b2f72c736b75f6d8a3
|
||||||
FROM stagex/core-openssl:sx2025.02.0@sha256:b3371fba4b4c61ddd02d97e81d0406d122a552a59f474d23822b099874690af0 AS core-openssl
|
FROM stagex/core-openssl:sx2025.02.0@sha256:b3371fba4b4c61ddd02d97e81d0406d122a552a59f474d23822b099874690af0 AS core-openssl
|
||||||
FROM stagex/user-pcsc-lite:sx2025.02.0@sha256:825708912c41d93dd38230f6f481f5876acb5b2959461504bdaa02a942f8c7b4 AS user-pcsc-lite
|
FROM stagex/user-pcsc-lite:sx2025.02.0@sha256:825708912c41d93dd38230f6f481f5876acb5b2959461504bdaa02a942f8c7b4 AS user-pcsc-lite
|
||||||
FROM stagex/user-pcsc-tools:sx2025.02.0@sha256:dc609b2eb7ba44f877b481633baa86873e99739573f81fe10d5485eb5a1b4f9d AS user-pcsc-tools
|
FROM stagex/user-pcsc-tools:sx2025.02.0@sha256:dc609b2eb7ba44f877b481633baa86873e99739573f81fe10d5485eb5a1b4f9d AS user-pcsc-tools
|
||||||
FROM stagex/user-qemu:sx2025.02.0@sha256:47653f32fb5874d91969a4b206e8f46f26f056dc2adfc88758d57208a6659b03 AS user-qemu
|
FROM stagex/user-qemu:sx2025.02.0@sha256:768024466eb41de11f270c891257814aa6292b44ec2b5da4cff75f0dbcae65c8 AS user-qemu
|
||||||
FROM stagex/user-canokey-qemu:sx2025.02.0@sha256:aba3be44d4b0da2f4ee52fdc2e2cd5b4f6dd05162323015745d2fd194d3074a7 AS user-canokey-qemu
|
FROM stagex/user-canokey-qemu:sx2025.02.0@sha256:aba3be44d4b0da2f4ee52fdc2e2cd5b4f6dd05162323015745d2fd194d3074a7 AS user-canokey-qemu
|
||||||
FROM stagex/user-sdtool:sx2025.02.0@sha256:7543bbfdc39efd94820484ffdc984ec16aac29523d0533c19887d907828e7a9a AS user-sdtool
|
FROM stagex/user-sdtool:sx2025.02.0@sha256:7543bbfdc39efd94820484ffdc984ec16aac29523d0533c19887d907828e7a9a AS user-sdtool
|
||||||
FROM stagex/user-seabios:sx2025.02.0@sha256:03eeb1344ad5f94dccdedbb3379906b272b62e246972e9334011746c79f234cf AS user-seabios
|
FROM stagex/user-seabios:sx2025.02.0@sha256:03eeb1344ad5f94dccdedbb3379906b272b62e246972e9334011746c79f234cf AS user-seabios
|
||||||
|
@ -118,6 +119,7 @@ COPY --from=core-bash . initramfs
|
||||||
COPY --from=user-gpg . initramfs
|
COPY --from=user-gpg . initramfs
|
||||||
COPY --from=user-jq . initramfs
|
COPY --from=user-jq . initramfs
|
||||||
COPY --from=user-yq . initramfs
|
COPY --from=user-yq . initramfs
|
||||||
|
COPY --from=user-glib . initramfs
|
||||||
COPY --from=core-bc . initramfs
|
COPY --from=core-bc . initramfs
|
||||||
COPY --from=user-flashtools . initramfs
|
COPY --from=user-flashtools . initramfs
|
||||||
COPY --from=core-curl . initramfs
|
COPY --from=core-curl . initramfs
|
||||||
|
@ -131,13 +133,16 @@ COPY --from=user-pcsc-tools . initramfs
|
||||||
COPY --from=user-libqrencode . initramfs
|
COPY --from=user-libqrencode . initramfs
|
||||||
COPY --from=core-gmp . initramfs
|
COPY --from=core-gmp . initramfs
|
||||||
COPY --from=core-libunwind . initramfs
|
COPY --from=core-libunwind . initramfs
|
||||||
|
COPY --from=user-numactl . initramfs
|
||||||
COPY --from=user-nettle . initramfs
|
COPY --from=user-nettle . initramfs
|
||||||
COPY --from=user-opensc . initramfs
|
COPY --from=user-opensc . initramfs
|
||||||
COPY --from=user-util-linux . initramfs
|
COPY --from=user-util-linux . initramfs
|
||||||
COPY --from=user-sops . initramfs
|
COPY --from=user-sops . initramfs
|
||||||
COPY --from=core-gcc /usr/lib/libgcc* initramfs/usr/lib/
|
COPY --from=core-gcc /usr/lib/libgcc* initramfs/usr/lib/
|
||||||
|
COPY --from=core-gcc . initramfs
|
||||||
COPY --from=core-sqlite3 . initramfs
|
COPY --from=core-sqlite3 . initramfs
|
||||||
COPY --from=user-sdtool . initramfs
|
COPY --from=user-sdtool . initramfs
|
||||||
|
COPY --from=user-qemu . initramfs
|
||||||
RUN chmod +x initramfs/usr/bin/sdtool
|
RUN chmod +x initramfs/usr/bin/sdtool
|
||||||
COPY --from=user-openpgp-card-tools . initramfs
|
COPY --from=user-openpgp-card-tools . initramfs
|
||||||
COPY --from=user-sequoia-sq . initramfs
|
COPY --from=user-sequoia-sq . initramfs
|
||||||
|
|
3
Makefile
3
Makefile
|
@ -70,6 +70,9 @@ vm: out/dev-shell.digest out/airgap.iso out/sdcard.img
|
||||||
-device sd-card,drive=external \
|
-device sd-card,drive=external \
|
||||||
-drive id=external,if=none,format=raw,file=out/sdcard.img \
|
-drive id=external,if=none,format=raw,file=out/sdcard.img \
|
||||||
-device usb-storage,drive=usbdrive \
|
-device usb-storage,drive=usbdrive \
|
||||||
|
-chardev socket,path=/tmp/qga.sock,server=on,wait=off,id=qga0 \
|
||||||
|
-device virtio-serial \
|
||||||
|
-device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0 \
|
||||||
$(if $(filter $(EFI),true) ,\
|
$(if $(filter $(EFI),true) ,\
|
||||||
-bios /usr/share/ovmf/OVMF.fd \
|
-bios /usr/share/ovmf/OVMF.fd \
|
||||||
-drive id=boot$(,)if=virtio$(,)format=raw$(,)file=out/airgap.iso \
|
-drive id=boot$(,)if=virtio$(,)format=raw$(,)file=out/airgap.iso \
|
||||||
|
|
|
@ -13,7 +13,7 @@ SYSLOGD_ARGS=""
|
||||||
start() {
|
start() {
|
||||||
printf 'Starting %s: ' "$DAEMON"
|
printf 'Starting %s: ' "$DAEMON"
|
||||||
# shellcheck disable=SC2086 # we need the word splitting
|
# shellcheck disable=SC2086 # we need the word splitting
|
||||||
start-stop-daemon -b -m -S -q -p "$PIDFILE" -x "/bin/$DAEMON" \
|
start-stop-daemon -b -m -S -q -p "$PIDFILE" -x "/sbin/$DAEMON" \
|
||||||
-- -n $SYSLOGD_ARGS
|
-- -n $SYSLOGD_ARGS
|
||||||
status=$?
|
status=$?
|
||||||
if [ "$status" -eq 0 ]; then
|
if [ "$status" -eq 0 ]; then
|
||||||
|
|
|
@ -13,7 +13,7 @@ KLOGD_ARGS=""
|
||||||
start() {
|
start() {
|
||||||
printf 'Starting %s: ' "$DAEMON"
|
printf 'Starting %s: ' "$DAEMON"
|
||||||
# shellcheck disable=SC2086 # we need the word splitting
|
# shellcheck disable=SC2086 # we need the word splitting
|
||||||
start-stop-daemon -b -m -S -q -p "$PIDFILE" -x "/bin/$DAEMON" \
|
start-stop-daemon -b -m -S -q -p "$PIDFILE" -x "/sbin/$DAEMON" \
|
||||||
-- -n $KLOGD_ARGS
|
-- -n $KLOGD_ARGS
|
||||||
status=$?
|
status=$?
|
||||||
if [ "$status" -eq 0 ]; then
|
if [ "$status" -eq 0 ]; then
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/bin/sh
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: qemu-ga
|
||||||
|
# Required-Start: $remote_fs $syslog
|
||||||
|
# Required-Stop:
|
||||||
|
# Default-Start: 2 3 4 5
|
||||||
|
# Default-Stop:
|
||||||
|
# Short-Description: Start QEMU Guest Agent
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
echo "Starting QEMU Guest Agent"
|
||||||
|
qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 &
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
echo "Stopping QEMU Guest Agent"
|
||||||
|
killall qemu-ga
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
"$0" stop
|
||||||
|
"$0" start
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {start|stop|restart}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
|
@ -13,7 +13,6 @@
|
||||||
::sysinit:/bin/mount -t sysfs sysfs /sys
|
::sysinit:/bin/mount -t sysfs sysfs /sys
|
||||||
::sysinit:/bin/mount -t proc proc /proc
|
::sysinit:/bin/mount -t proc proc /proc
|
||||||
::sysinit:/bin/mount -o remount,rw /
|
::sysinit:/bin/mount -o remount,rw /
|
||||||
::sysinit:/bin/mkdir /var/log
|
|
||||||
null::sysinit:/bin/ln -sf /proc/self/fd /dev/fd
|
null::sysinit:/bin/ln -sf /proc/self/fd /dev/fd
|
||||||
null::sysinit:/bin/ln -sf /proc/self/fd/0 /dev/stdin
|
null::sysinit:/bin/ln -sf /proc/self/fd/0 /dev/stdin
|
||||||
null::sysinit:/bin/ln -sf /proc/self/fd/1 /dev/stdout
|
null::sysinit:/bin/ln -sf /proc/self/fd/1 /dev/stdout
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
QGA_SOCKET="/tmp/qga.sock"
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
if [ ! -S "$QGA_SOCKET" ]; then
|
||||||
|
echo "QGA socket not found at $QGA_SOCKET"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown() {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
qga_send() {
|
||||||
|
local json_cmd="$1"
|
||||||
|
local tmp_out
|
||||||
|
tmp_out=$(mktemp)
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "$json_cmd"
|
||||||
|
sleep 0.1
|
||||||
|
} | socat - UNIX-CONNECT:"$QGA_SOCKET" > "$tmp_out"
|
||||||
|
|
||||||
|
cat "$tmp_out"
|
||||||
|
rm -f "$tmp_out"
|
||||||
|
}
|
||||||
|
|
||||||
|
qga_guest_exec() {
|
||||||
|
local cmd="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
local args_json="[]"
|
||||||
|
if [[ "$#" -gt 0 ]]; then
|
||||||
|
args_json=$(printf '%s\n' "$@" | jq -R . | jq -s .)
|
||||||
|
fi
|
||||||
|
|
||||||
|
local json
|
||||||
|
json=$(jq -n \
|
||||||
|
--arg path "$cmd" \
|
||||||
|
--argjson args "$args_json" \
|
||||||
|
'{
|
||||||
|
execute: "guest-exec",
|
||||||
|
arguments: {
|
||||||
|
path: $path,
|
||||||
|
arg: $args,
|
||||||
|
"capture-output": true
|
||||||
|
}
|
||||||
|
}')
|
||||||
|
|
||||||
|
local response
|
||||||
|
response=$(qga_send "$json")
|
||||||
|
|
||||||
|
echo "guest-exec JSON sent:" >&2
|
||||||
|
echo "$json" >&2
|
||||||
|
echo "guest-exec response:" >&2
|
||||||
|
echo "$response" >&2
|
||||||
|
|
||||||
|
local first_line
|
||||||
|
first_line=$(echo "$response" | head -n1)
|
||||||
|
|
||||||
|
local pid
|
||||||
|
pid=$(echo "$first_line" | jq -r '.return.pid // empty')
|
||||||
|
|
||||||
|
if [[ ! "$pid" =~ ^[0-9]+$ ]]; then
|
||||||
|
echo "ERROR: Failed to parse valid PID from response." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$pid"
|
||||||
|
}
|
||||||
|
|
||||||
|
qga_guest_exec_status() {
|
||||||
|
local pid="$1"
|
||||||
|
|
||||||
|
local json
|
||||||
|
json=$(jq -n --argjson pid "$pid" \
|
||||||
|
'{execute: "guest-exec-status", arguments: {pid: $pid}}')
|
||||||
|
|
||||||
|
qga_send "$json"
|
||||||
|
}
|
||||||
|
|
||||||
|
qga_decode_output() {
|
||||||
|
local b64_data="$1"
|
||||||
|
echo "$b64_data" | base64 -d
|
||||||
|
}
|
||||||
|
|
||||||
|
qga_run_cmd_and_get_output() {
|
||||||
|
local pid
|
||||||
|
pid=$(qga_guest_exec "$@")
|
||||||
|
if [[ -z "$pid" ]]; then
|
||||||
|
echo "Failed to get PID from guest-exec" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local status_resp
|
||||||
|
local exited="false"
|
||||||
|
local retries=5
|
||||||
|
|
||||||
|
while [[ "$exited" != "true" && $retries -gt 0 ]]; do
|
||||||
|
sleep 0.5
|
||||||
|
status_resp=$(qga_guest_exec_status "$pid")
|
||||||
|
exited=$(echo "$status_resp" | jq -r '.return.exited // empty')
|
||||||
|
retries=$((retries - 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$exited" != "true" ]]; then
|
||||||
|
echo "Command did not finish after retries." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local out_data
|
||||||
|
out_data=$(echo "$status_resp" | jq -r '.return."out-data" // empty')
|
||||||
|
local err_data
|
||||||
|
err_data=$(echo "$status_resp" | jq -r '.return."err-data" // empty')
|
||||||
|
|
||||||
|
if [[ -n "$err_data" ]]; then
|
||||||
|
echo "STDERR:" >&2
|
||||||
|
qga_decode_output "$err_data" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
qga_decode_output "$out_data"
|
||||||
|
}
|
||||||
|
|
||||||
|
qga_read_guest_file() {
|
||||||
|
local file_path="$1"
|
||||||
|
local pid
|
||||||
|
pid=$(qga_guest_exec "/bin/cat" "$file_path")
|
||||||
|
if [[ -z "$pid" ]]; then
|
||||||
|
echo "Failed to get PID to read file" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local status_resp
|
||||||
|
local exited="false"
|
||||||
|
local retries=5
|
||||||
|
|
||||||
|
while [[ "$exited" != "true" && $retries -gt 0 ]]; do
|
||||||
|
sleep 0.5
|
||||||
|
status_resp=$(qga_guest_exec_status "$pid")
|
||||||
|
exited=$(echo "$status_resp" | jq -r '.return.exited // empty')
|
||||||
|
retries=$((retries - 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$exited" != "true" ]]; then
|
||||||
|
echo "Failed to read file or command did not finish execution." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local out_data
|
||||||
|
out_data=$(echo "$status_resp" | jq -r '.return."out-data"')
|
||||||
|
local decoded_data
|
||||||
|
decoded_data=$(qga_decode_output "$out_data")
|
||||||
|
|
||||||
|
if echo "$decoded_data" | base64 --decode &>/dev/null; then
|
||||||
|
echo "$decoded_data" | base64 --decode
|
||||||
|
else
|
||||||
|
echo "$decoded_data"
|
||||||
|
fi
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load 'helpers/qga_helper'
|
||||||
|
|
||||||
|
@test "echo in guest returns hello" {
|
||||||
|
result="$(qga_run_cmd_and_get_output /bin/echo hello)"
|
||||||
|
echo "Guest output: $result"
|
||||||
|
[ "$result" = "hello" ]
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load 'helpers/qga_helper'
|
||||||
|
|
||||||
|
@test "keyfork --version returns version" {
|
||||||
|
result="$(qga_run_cmd_and_get_output /bin/keyfork --version)"
|
||||||
|
echo "Guest output: $result"
|
||||||
|
[[ "$result" == *"keyfork"* ]]
|
||||||
|
}
|
Loading…
Reference in New Issue