feat: initial qemu-ga based netvm command to control guest
This commit is contained in:
parent
b64d76b60d
commit
18fa25b87e
|
@ -62,13 +62,14 @@ FROM stagex/user-yq@sha256:47a39bfdeffff4344f41d60aa81671c7fd30c3e5e6d21ced21a05
|
||||||
FROM stagex/user-edk2@sha256:db24be51d35117d264dccfc44f0ca331f59d738083170cd9bb86b49a5c06abff AS user-edk2
|
FROM stagex/user-edk2@sha256:db24be51d35117d264dccfc44f0ca331f59d738083170cd9bb86b49a5c06abff AS user-edk2
|
||||||
FROM stagex/core-ca-certificates@sha256:d6fca6c0080e8e5360cd85fc1c4bd3eab71ce626f40602e38488bfd61fd3e89d AS core-ca-certificates
|
FROM stagex/core-ca-certificates@sha256:d6fca6c0080e8e5360cd85fc1c4bd3eab71ce626f40602e38488bfd61fd3e89d AS core-ca-certificates
|
||||||
FROM stagex/user-linux-guest-net@sha256:994b6fe49dd4331b32b0854055bff31b06db5eabdeafb32b2c0d55465b7ccf45 AS user-linux-guest-net
|
FROM stagex/user-linux-guest-net@sha256:994b6fe49dd4331b32b0854055bff31b06db5eabdeafb32b2c0d55465b7ccf45 AS user-linux-guest-net
|
||||||
FROM stagex/user-linux-airgap@sha256:c8575c92aa63544ee92a820a97034fcc203abf2671c0e7e21d0c4e20daef8827 AS user-linux-airgap
|
FROM stagex/user-linux-airgap@sha256:4f163e5f1f09f87d8f0fcf060193345f0b4dd8dbff2a2b2aec1cd4c254a628ca AS user-linux-airgap
|
||||||
FROM stagex/user-libimobiledevice-glue@sha256:3ce674285cbc04b694b7e400703868fcaac65401f2f2ca2aa2b720b3e0efee3c AS user-libimobiledevice-glue
|
FROM stagex/user-libimobiledevice-glue@sha256:3ce674285cbc04b694b7e400703868fcaac65401f2f2ca2aa2b720b3e0efee3c AS user-libimobiledevice-glue
|
||||||
FROM stagex/user-libimobiledevice@sha256:fcda68bdc397213fa76bd893472a304b093522aaac28e36f458275b93bb1af34 AS user-libimobiledevice
|
FROM stagex/user-libimobiledevice@sha256:fcda68bdc397213fa76bd893472a304b093522aaac28e36f458275b93bb1af34 AS user-libimobiledevice
|
||||||
FROM stagex/user-libplist@sha256:2d776cb4eca3689a8bd6ac755a23f492850bf6c7b0c72e3525db6135e4d6e0bc AS user-libplist
|
FROM stagex/user-libplist@sha256:2d776cb4eca3689a8bd6ac755a23f492850bf6c7b0c72e3525db6135e4d6e0bc AS user-libplist
|
||||||
FROM stagex/user-libusb@sha256:53d499555164f12d9e87118a6d44e1d07f0b1cc9081a29eb66975662be818a00 AS user-libusb
|
FROM stagex/user-libusb@sha256:53d499555164f12d9e87118a6d44e1d07f0b1cc9081a29eb66975662be818a00 AS user-libusb
|
||||||
FROM stagex/user-libusbmuxd@sha256:1e97f0a2ede0ee5fac9b056d0395e12b77c9f0bf550f9d0c20734ce0617eb51f AS user-libusbmuxd
|
FROM stagex/user-libusbmuxd@sha256:1e97f0a2ede0ee5fac9b056d0395e12b77c9f0bf550f9d0c20734ce0617eb51f AS user-libusbmuxd
|
||||||
FROM stagex/user-usbmuxd@sha256:90f687d2368328b76141badc382a21873a5b44d4ddccf851c017caf1e78af418 AS user-usbmuxd
|
FROM stagex/user-usbmuxd@sha256:90f687d2368328b76141badc382a21873a5b44d4ddccf851c017caf1e78af418 AS user-usbmuxd
|
||||||
|
FROM stagex/user-socat@sha256:990a70ae13462d8ba0a925fe959dd83070cbecdb3f91ff145caca5232171f3b8 AS user-socat
|
||||||
|
|
||||||
FROM scratch AS base
|
FROM scratch AS base
|
||||||
ARG VERSION development
|
ARG VERSION development
|
||||||
|
@ -124,7 +125,11 @@ COPY --from=user-libimobiledevice . initramfs
|
||||||
COPY --from=user-libplist . initramfs
|
COPY --from=user-libplist . initramfs
|
||||||
COPY --from=user-libusb . initramfs
|
COPY --from=user-libusb . initramfs
|
||||||
COPY --from=user-libusbmuxd . initramfs
|
COPY --from=user-libusbmuxd . initramfs
|
||||||
|
COPY --from=core-gcc /usr/lib/. initramfs/usr/lib/
|
||||||
COPY --from=user-usbmuxd . initramfs
|
COPY --from=user-usbmuxd . initramfs
|
||||||
|
COPY --from=user-glib . initramfs
|
||||||
|
COPY --from=user-numactl . initramfs
|
||||||
|
COPY --from=user-qemu /usr/bin/qemu-ga initramfs/usr/bin/
|
||||||
|
|
||||||
COPY src/guest/rootfs/ initramfs
|
COPY src/guest/rootfs/ initramfs
|
||||||
RUN <<-EOF
|
RUN <<-EOF
|
||||||
|
@ -234,6 +239,7 @@ COPY --from=user-libslirp . initramfs
|
||||||
COPY --from=user-seabios . initramfs
|
COPY --from=user-seabios . initramfs
|
||||||
COPY --from=user-canokey-qemu . initramfs
|
COPY --from=user-canokey-qemu . initramfs
|
||||||
COPY --from=user-qemu . initramfs
|
COPY --from=user-qemu . initramfs
|
||||||
|
COPY --from=user-socat . initramfs
|
||||||
COPY --from=user-libzbar . initramfs
|
COPY --from=user-libzbar . initramfs
|
||||||
COPY --from=user-keyfork . initramfs
|
COPY --from=user-keyfork . initramfs
|
||||||
COPY --from=user-icepick . initramfs
|
COPY --from=user-icepick . initramfs
|
||||||
|
|
3
Makefile
3
Makefile
|
@ -73,6 +73,9 @@ vm: out/dev-shell.digest out/airgap.iso out/sdcard.img
|
||||||
-device intel-iommu,intremap=on \
|
-device intel-iommu,intremap=on \
|
||||||
-netdev user,id=net0 \
|
-netdev user,id=net0 \
|
||||||
-device e1000,netdev=net0 \
|
-device e1000,netdev=net0 \
|
||||||
|
-chardev socket,path=out/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 \
|
||||||
|
|
|
@ -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"
|
||||||
|
cat /proc/cpuinfo | grep QEMU && qemu-ga &
|
||||||
|
;;
|
||||||
|
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
|
|
@ -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"
|
||||||
|
cat /proc/cpuinfo | grep QEMU && qemu-ga &
|
||||||
|
;;
|
||||||
|
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
|
|
@ -0,0 +1,131 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
COMMAND=($@)
|
||||||
|
QGA_SOCKET=/var/run/netvm_qga.sock
|
||||||
|
LOCKFILE=/var/run/netvm.pid
|
||||||
|
|
||||||
|
json() {
|
||||||
|
jq -ncM "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_execute() {
|
||||||
|
local COMMAND ARGS
|
||||||
|
COMMAND="$1"
|
||||||
|
ARGS="${2-}"
|
||||||
|
|
||||||
|
json --arg cmd "$COMMAND" --argjson args "$ARGS" '{"execute": $cmd, "arguments": $args}' >&$FD_SOCKET_OUT
|
||||||
|
|
||||||
|
local LINE
|
||||||
|
read -t 5 -r -u $FD_SOCKET_IN LINE
|
||||||
|
|
||||||
|
local ERROR=$(jq -r '.error.desc // empty' <<< "$LINE")
|
||||||
|
if [[ -n "$ERROR" ]]; then
|
||||||
|
echo "$ERROR" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
GA_RETURN=$(jq -cM .return <<< "$LINE")
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_ga() {
|
||||||
|
local COMMAND ARGS
|
||||||
|
COMMAND="$1"
|
||||||
|
ARGS="$2"
|
||||||
|
|
||||||
|
coproc FDS (
|
||||||
|
exec socat - UNIX-CONNECT:"${QGA_SOCKET}"
|
||||||
|
)
|
||||||
|
|
||||||
|
FD_SOCKET_IN=${FDS[0]}
|
||||||
|
FD_SOCKET_OUT=${FDS[1]}
|
||||||
|
|
||||||
|
local PID=$$
|
||||||
|
qemu_execute guest-sync "$(json --argjson pid "$PID" '{"id": $pid}')"
|
||||||
|
[[ "$(jq -re . <<< "$GA_RETURN")" = "$$" ]] || (echo "guest-sync mismatch" >&2 && return 1)
|
||||||
|
|
||||||
|
qemu_execute "$COMMAND" "$ARGS"
|
||||||
|
echo "$GA_RETURN" 2>&1
|
||||||
|
|
||||||
|
local RETURN
|
||||||
|
kill -INT "$FDS_PID" 2>/dev/null
|
||||||
|
wait "$FDS_PID" || RETURN=$?
|
||||||
|
if [[ $RETURN != 130 ]]; then
|
||||||
|
return $RETURN
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function start(){
|
||||||
|
[ ! -f "${LOCKFILE}" ] || { echo "Error: Netvm already running"; exit 1; }
|
||||||
|
echo "Starting netvm";
|
||||||
|
qemu-system-x86_64 -m 512M \
|
||||||
|
--machine q35 \
|
||||||
|
-nographic \
|
||||||
|
-serial none \
|
||||||
|
-monitor none \
|
||||||
|
-net none \
|
||||||
|
-cdrom /guest.img \
|
||||||
|
-boot order=d \
|
||||||
|
-chardev socket,path=/var/run/netvm_qga.sock,server=on,wait=off,id=qga0 \
|
||||||
|
-device virtio-serial \
|
||||||
|
-device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0 &
|
||||||
|
echo $! > "${LOCKFILE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop(){
|
||||||
|
pkill -F "${LOCKFILE}" && rm "${LOCKFILE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function status(){
|
||||||
|
qemu_ga guest-get-host-name "{}" | jq -r '."host-name"'
|
||||||
|
pid=$(qemu_ga guest-exec '{"path": "uptime", "capture-output": true}' | jq -r '.pid')
|
||||||
|
out=$(qemu_ga guest-exec-status "$(jq -n --argjson pid "$pid" '{pid: $pid }')" \
|
||||||
|
| jq -r '."out-data"' \
|
||||||
|
| base64 -d \
|
||||||
|
)
|
||||||
|
echo $out
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(){
|
||||||
|
[ -z "${COMMAND[1]}" ] && { echo "Error: missing command"; exit 1; }
|
||||||
|
[ -f "${LOCKFILE}" ] || { echo "Error: Netvm is not running"; exit 1; }
|
||||||
|
[ -S "${QGA_SOCKET}" ] || { echo "Error: Netvm QGA socket is missing"; exit 1; }
|
||||||
|
local cmd="${COMMAND[1]}"
|
||||||
|
local args="${COMMAND[@]:2}"
|
||||||
|
local args_json="[]"
|
||||||
|
if [[ -n "$args" ]]; then
|
||||||
|
args_json=$(printf '%s\n' "$args" | jq -R . | jq -s .)
|
||||||
|
fi
|
||||||
|
local request
|
||||||
|
request=$( \
|
||||||
|
jq -n \
|
||||||
|
--arg path "$cmd" \
|
||||||
|
--argjson args "$args_json" \
|
||||||
|
'{
|
||||||
|
path: $path,
|
||||||
|
arg: $args,
|
||||||
|
"capture-output": true
|
||||||
|
}' \
|
||||||
|
)
|
||||||
|
|
||||||
|
pid=$(qemu_ga guest-exec "$request" | jq -r '.pid')
|
||||||
|
out=$(qemu_ga guest-exec-status "$(jq -n --argjson pid "$pid" '{pid: $pid }')" \
|
||||||
|
| jq -r '."out-data"' \
|
||||||
|
)
|
||||||
|
sleep 1
|
||||||
|
echo $out
|
||||||
|
echo $out | base64 -d
|
||||||
|
}
|
||||||
|
|
||||||
|
function help(){
|
||||||
|
echo "Valid operations: start, stop, run, help"
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${COMMAND[0]}" in
|
||||||
|
status) status ;;
|
||||||
|
start) start ;;
|
||||||
|
stop) stop ;;
|
||||||
|
run) run ;;
|
||||||
|
help) help ;;
|
||||||
|
*) help ;;
|
||||||
|
esac
|
Loading…
Reference in New Issue