//! system/services/log-flush — a one-shot that copies the kernel's in-memory //! diagnostic log to a file on the mounted USB FAT volume, so the boot log //! survives to be read on another machine. On a headless or real board there is //! no host capturing serial, so without this the log is lost at power-off; this //! is the on-disk equivalent of QEMU's `-serial file:`. //! //! It reads the whole kernel log back through `klog_read` (the RAM sink in //! system/kernel/log.zig) and writes it to /mnt/usb/DANOS.LOG. The name is 8.3 //! (FAT short-name rule: base <= 8, extension <= 3) and lives at the mount root //! (there is no mkdir on the FAT path yet). init spawns this once the boot //! services are up; init itself repeats the flush at shutdown for a fuller log. //! //! If no USB volume is mounted — no stick, or the initial-ramdisk sweep that //! spawns every bundled binary bare with no VFS — it waits briefly, then exits //! silently, deranging no other test's output. const std = @import("std"); const runtime = @import("runtime"); const fs = runtime.fs; const log_path = "/mnt/usb/DANOS.LOG"; /// Copy the whole kernel log to the open file, looping klog_read -> write until /// the log is exhausted. Returns the number of bytes written. fn drainKernelLog(file: *fs.File) usize { var chunk: [4096]u8 = undefined; var offset: usize = 0; while (true) { const got = runtime.system.klogRead(offset, &chunk); if (got == 0) break; // reached the end of the accumulated log if (file.writeAll(chunk[0..got]) == null) break; // storage went away offset += got; } return offset; } pub fn main() void { // Wait for the fat server to mount /mnt/usb (it must bring up the whole USB // storage chain first, so it races us at boot). Bounded: if the mount never // appears — no volume, or the no-VFS ramdisk sweep — give up silently. var ready = false; var tries: u32 = 0; while (tries < 1400) : (tries += 1) { if (fs.openDirectory("/mnt/usb")) |directory| { var dir = directory; dir.close(); ready = true; break; } runtime.system.sleep(50); } if (!ready) return; // /mnt/usb never became available — nothing to persist to // Truncate on open: each flush replaces the file, so a shorter log on a later // boot of the same stick leaves no stale tail from a previous, longer one. var file = fs.open(log_path, .{ .create = true, .truncate = true }) orelse return; const written = drainKernelLog(&file); file.close(); var line: [96]u8 = undefined; _ = runtime.system.write(std.fmt.bufPrint(&line, "log-flush: wrote {d} bytes to {s}\n", .{ written, log_path }) catch return); } pub const panic = runtime.panic; comptime { _ = &runtime.start._start; // pull the runtime entry shim into the image }