Files
appsandbox/tools/iso-patch/engine/log.c
T
James Stringer 2135f5cbeb Direct Ubuntu ISO->VHDX build (no subiquity, no cidata)
iso-patch.exe gains a new --ubuntu-to-vhdx mode that produces a fully
installed, bootable Ubuntu VHDX in one shot:
  - vendored xz-embedded + custom ext4 writer + squashfs reader
  - mounts the ISO, partitions GPT (ESP + Linux root), streams
    casper/minimal.squashfs into a freshly-built ext4 rootfs through
    a producer/N-decompress-worker/single-writer pipeline
  - installs signed shim + grub from the ISO's pool .debs onto ESP,
    writes a redirect grub.cfg pointing at the rootfs by UUID
  - plants a defensive one-shot first-boot systemd unit that runs
    grub-install + update-grub + AppSandbox guest setup (agent ELFs,
    kernel modules, systemd units, wsl-mesa, wsl-deps, ldconfig,
    no-sleep dconf, etc.) and reboots

linux_create_thread now spawns iso-patch with --ubuntu-to-vhdx
and a --stage manifest covering the full extras tree (reuses
stage_linux_agent_and_extras to populate a staging dir then walks
it). image_path is cleared post-build so the ISO isn't re-attached
to the runtime VM. ComPorts/com1 named pipe and the do_connect_rdp
window path are dropped (IDD is the only live display path now).

VideoMonitor stays unconditional (vmwp.exe crashes 0xC0000005
without it) — but with no ConnectionOptions/named pipe, matching
master.

JS frontend: "Installing Linux" branch added so the spinner label
no longer says "Installing Windows" for Linux VMs. Progress %
now tracks ingest fraction (n_processed / total_inodes) mapped
into 10..75%, with post-ingest phases at 82..99%.
2026-05-25 22:34:57 -04:00

175 lines
4.7 KiB
C

/* log.c - implementation of the two-sink logger. */
#include "log.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <sys/timeb.h>
#define MAX_PHASE_DEPTH 8
static FILE *g_log_file = NULL;
static const char *g_phase_stack[MAX_PHASE_DEPTH] = { "INIT" };
static int g_phase_depth = 1;
static const char *level_str(log_level_t lvl)
{
switch (lvl) {
case LOG_DEBUG: return "DEBUG";
case LOG_INFO: return "INFO ";
case LOG_WARN: return "WARN ";
case LOG_ERROR: return "ERROR";
default: return "?????";
}
}
static void emit_line_prefix(FILE *fp, log_level_t lvl,
const char *file, int line)
{
struct _timeb tb;
_ftime64_s(&tb);
struct tm lt;
localtime_s(&lt, &tb.time);
const char *base = file;
for (const char *p = file; *p; p++) {
if (*p == '\\' || *p == '/') base = p + 1;
}
fprintf(fp, "[%02d:%02d:%02d.%03u] [%s] [%s] %s:%d ",
lt.tm_hour, lt.tm_min, lt.tm_sec, tb.millitm,
g_phase_stack[g_phase_depth - 1],
level_str(lvl),
base, line);
}
int log_open(const char *path)
{
char auto_path[MAX_PATH];
if (path == NULL) {
time_t now = time(NULL);
struct tm lt;
localtime_s(&lt, &now);
CreateDirectoryA("out", NULL); /* ok if exists */
snprintf(auto_path, sizeof(auto_path),
"out\\pocbuild-%04d%02d%02d-%02d%02d%02d.log",
lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday,
lt.tm_hour, lt.tm_min, lt.tm_sec);
path = auto_path;
}
g_log_file = fopen(path, "wb");
if (!g_log_file) {
fprintf(stderr, "log_open: cannot open %s (errno=%d)\n",
path, errno);
return -1;
}
/* No buffering - we want every line on disk in case we crash. */
setvbuf(g_log_file, NULL, _IONBF, 0);
LOG_I("===== pocbuild log opened: %s =====", path);
return 0;
}
void log_close(void)
{
if (g_log_file) {
LOG_I("===== log closed =====");
fclose(g_log_file);
g_log_file = NULL;
}
}
void log_phase_push(const char *phase)
{
if (g_phase_depth < MAX_PHASE_DEPTH) {
g_phase_stack[g_phase_depth++] = phase;
LOG_I(">>> ENTER phase %s", phase);
}
}
void log_phase_pop(void)
{
if (g_phase_depth > 1) {
const char *p = g_phase_stack[--g_phase_depth];
LOG_I("<<< EXIT phase %s", p);
}
}
void log_emit(log_level_t lvl, const char *file, int line,
const char *fmt, ...)
{
char msg[2048];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
emit_line_prefix(stdout, lvl, file, line);
fputs(msg, stdout);
fputc('\n', stdout);
fflush(stdout);
if (g_log_file) {
emit_line_prefix(g_log_file, lvl, file, line);
fputs(msg, g_log_file);
fputc('\n', g_log_file);
}
}
void log_hex(log_level_t lvl, const char *label, const void *buf, size_t len)
{
const unsigned char *p = (const unsigned char *)buf;
char line[128];
log_emit(lvl, __FILE__, __LINE__, "HEX [%s] %zu bytes:", label, len);
for (size_t off = 0; off < len; off += 16) {
int n = snprintf(line, sizeof(line), " %08zx ", off);
size_t row = (len - off < 16) ? (len - off) : 16;
for (size_t i = 0; i < 16; i++) {
if (i < row) {
n += snprintf(line + n, sizeof(line) - n, "%02x ", p[off + i]);
} else {
n += snprintf(line + n, sizeof(line) - n, " ");
}
if (i == 7) {
n += snprintf(line + n, sizeof(line) - n, " ");
}
}
n += snprintf(line + n, sizeof(line) - n, " |");
for (size_t i = 0; i < row; i++) {
unsigned char c = p[off + i];
n += snprintf(line + n, sizeof(line) - n, "%c",
(c >= 0x20 && c < 0x7f) ? c : '.');
}
snprintf(line + n, sizeof(line) - n, "|");
log_emit(lvl, __FILE__, __LINE__, "%s", line);
}
}
void log_win32_fail(const char *file, int line, const char *call, DWORD err)
{
char buf[512] = { 0 };
FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buf, sizeof(buf) - 1, NULL);
/* Strip trailing whitespace - FormatMessage appends CRLF. */
size_t n = strlen(buf);
while (n > 0 && (buf[n - 1] == '\r' || buf[n - 1] == '\n' ||
buf[n - 1] == ' ' || buf[n - 1] == '\t')) {
buf[--n] = 0;
}
log_emit(LOG_ERROR, file, line,
"WIN32 FAIL: %s err=0x%08lx (%lu) - %s",
call, err, err, buf);
}