162 lines
3.2 KiB
Bash
Executable File
162 lines
3.2 KiB
Bash
Executable File
#!/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
|
|
}
|