Public Access
ci: capture native recovery startup context before platform probe
This commit is contained in:
@@ -117,6 +117,8 @@ static class NativeDiagnostic
|
||||
AssertContainer(File.ReadAllText(Path.Combine(output, "container-created.stdout.log")), token);
|
||||
await Command("docker", ["start", id], output, "docker-start", deadline.Token);
|
||||
Console.WriteLine("The owned unprivileged TCG guest is starting. Success requires native macOS 14+/x86_64 and a writable 64-GiB disk; no installer will run.");
|
||||
var recoveryStarted = Stopwatch.StartNew();
|
||||
var heartbeat = Stopwatch.StartNew();
|
||||
while (true)
|
||||
{
|
||||
deadline.Token.ThrowIfCancellationRequested();
|
||||
@@ -132,6 +134,11 @@ static class NativeDiagnostic
|
||||
}
|
||||
var running = await Command("docker", ["inspect", "--format", "{{.State.Running}}", id], output, "container-running", deadline.Token);
|
||||
if (running.Output.Trim() != "true") throw new InvalidOperationException("Guest container exited before a native readiness result.");
|
||||
if (heartbeat.Elapsed >= TimeSpan.FromSeconds(60))
|
||||
{
|
||||
Console.WriteLine($"[native-diagnostic] phase=recovery; elapsed={recoveryStarted.Elapsed.TotalMinutes:F1} minutes; container=running; readiness=pending");
|
||||
heartbeat.Restart();
|
||||
}
|
||||
await Task.Delay(TimeSpan.FromSeconds(20), deadline.Token);
|
||||
}
|
||||
}
|
||||
@@ -144,7 +151,7 @@ static class NativeDiagnostic
|
||||
{
|
||||
Console.CancelKeyPress -= cancelHandler;
|
||||
using var captureDeadline = new CancellationTokenSource(TimeSpan.FromSeconds(45));
|
||||
try { await CaptureGuest(state.ContainerName, output, captureDeadline.Token); } catch (Exception exception) { Console.Error.WriteLine("Final evidence capture: " + exception.Message); }
|
||||
try { await CaptureGuest(state.ContainerId ?? state.ContainerName, output, captureDeadline.Token, true, state.Token); } catch (Exception exception) { Console.Error.WriteLine("Final evidence capture: " + exception.Message); }
|
||||
try { PrintGuestProof(output, state.Token); } catch (Exception exception) { Console.Error.WriteLine("Native proof output: " + exception.Message); }
|
||||
var clean = await Cleanup(output);
|
||||
if (!clean) { outcome = "failed"; error = (error ?? "") + " Owned-resource cleanup failed; inspect cleanup evidence."; }
|
||||
@@ -229,8 +236,9 @@ static class NativeDiagnostic
|
||||
throw new InvalidOperationException("Created container exceeds the owned/unprivileged diagnostic boundary.");
|
||||
}
|
||||
|
||||
static async Task CaptureGuest(string id, string output, CancellationToken cancellation)
|
||||
static async Task CaptureGuest(string id, string output, CancellationToken cancellation, bool final = false, string? token = null)
|
||||
{
|
||||
if (final && token is not null) await CaptureMonitor(id, output, token, cancellation);
|
||||
var logs = await Command("docker", ["logs", "--tail", "3000", id], output, "container", cancellation, requireSuccess: false);
|
||||
foreach (var file in new[] { ("proof.log", "guest-proof.log"), ("result.json", "guest-result.json") })
|
||||
{
|
||||
@@ -240,6 +248,41 @@ static class NativeDiagnostic
|
||||
await Command("docker", ["exec", id, "sh", "-c", "printf '[qemu]\n'; qemu-system-x86_64 --version | head -n 1; printf '[Recovery hash]\n'; test ! -f /storage/14/setup.dmg || sha256sum /storage/14/setup.dmg; printf '[resources]\n'; df -Pk /storage; cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/cpu.max 2>/dev/null || true"], output, "guest-container-resources", cancellation, requireSuccess: false);
|
||||
}
|
||||
|
||||
static async Task CaptureMonitor(string id, string output, string token, CancellationToken cancellation)
|
||||
{
|
||||
using var deadline = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
|
||||
deadline.CancelAfter(TimeSpan.FromSeconds(10));
|
||||
int? monitorExit = null, copyExit = null;
|
||||
string? error = null;
|
||||
try
|
||||
{
|
||||
if (!System.Text.RegularExpressions.Regex.IsMatch(id, "^[0-9a-f]{64}$") || !System.Text.RegularExpressions.Regex.IsMatch(token, "^[0-9a-f]{32}$")) throw new InvalidOperationException("No saved owned container identity for the optional monitor capture.");
|
||||
var inspection = await Command("docker", ["inspect", id], output, "capture-monitor-container", deadline.Token);
|
||||
AssertContainer(inspection.Output, token);
|
||||
using (var document = JsonDocument.Parse(inspection.Output))
|
||||
if (document.RootElement[0].GetProperty("Id").GetString() != id || !document.RootElement[0].GetProperty("State").GetProperty("Running").GetBoolean()) throw new InvalidOperationException("Owned guest container is no longer running for the optional monitor capture.");
|
||||
var screen = "/dev/shm/native-diagnostic-screen-" + token + ".ppm";
|
||||
var monitor = await Command("docker", ["exec", id, "sh", "-c", """
|
||||
test -S /run/shm/monitor.sock || exit 1
|
||||
rm -f -- "$1" || exit 1
|
||||
printf 'info status\nscreendump %s\n' "$1" | /usr/bin/timeout -s KILL 5 /usr/bin/nc.openbsd -q 1 -w 2 -U /run/shm/monitor.sock
|
||||
monitor_exit=$?
|
||||
printf '\n[monitor-exit] %s\n' "$monitor_exit"
|
||||
[ "$monitor_exit" -eq 0 ] || exit "$monitor_exit"
|
||||
bytes=$(stat -c%s "$1") || exit 1
|
||||
[ "$bytes" -gt 0 ] && [ "$bytes" -le 8388608 ] || exit 1
|
||||
printf '[screen-bytes] %s\n' "$bytes"
|
||||
""", "native-monitor", screen], output, "capture-monitor", deadline.Token, requireSuccess: false);
|
||||
monitorExit = monitor.ExitCode;
|
||||
if (monitorExit != 0) throw new InvalidOperationException("Optional monitor status/screenshot command exited " + monitorExit + ".");
|
||||
var copy = await Command("docker", ["cp", id + ":" + screen, Path.Combine(output, "guest-screen-" + token + ".ppm")], output, "capture-monitor-screen", deadline.Token, requireSuccess: false);
|
||||
copyExit = copy.ExitCode;
|
||||
if (copyExit != 0) throw new InvalidOperationException("Optional monitor screenshot copy exited " + copyExit + ".");
|
||||
}
|
||||
catch (Exception exception) { error = exception.Message; Console.Error.WriteLine("Optional final monitor capture: " + error); }
|
||||
finally { Save(Path.Combine(output, "monitor-capture.json"), new { token, monitorExit, copyExit, success = error is null, error, capturedUtc = DateTimeOffset.UtcNow }); }
|
||||
}
|
||||
|
||||
static async Task<bool> Cleanup(string output)
|
||||
{
|
||||
var path = Path.Combine(output, "owned-resources.json");
|
||||
|
||||
Reference in New Issue
Block a user