diff --git a/implementations/main.cpp b/implementations/main.cpp index 0b72bff..f8df4b6 100644 --- a/implementations/main.cpp +++ b/implementations/main.cpp @@ -56,6 +56,7 @@ bool g_listeners = false; bool g_auth = false; int g_frames = 40; int g_frameGapMs = 500; +std::string g_logDir = "/var/log/fingerprintd"; std::uint32_t g_gid = 0; std::string g_taPath = "/lib/firmware/focal64.mbn"; std::string g_cfgPath = "/lib/firmware/fingerprintd.json"; @@ -66,6 +67,30 @@ qcomtee_object* g_root = QCOMTEE_OBJECT_NULL; // around it so the supplicant thread can be stopped while blocked in the // kernel waiting for QTEE. // +// Every run writes its own timestamped transcript. Not a convenience: a run +// whose result nobody recorded is a run that has to be repeated on a human's +// finger. And a SINGLE shared log path is worse than none -- the next run, +// including a quick control, destroys the interesting one, which is how the +// first successful authentication in this project was very nearly lost. +// +// Done at the file-descriptor level rather than by wrapping a stream, because +// std::println writes to stdout through C stdio: an ostream wrapper would +// capture nothing. Routing fd 1 through tee catches every line including the +// ones libqcomtee prints. +bool StartTranscript(const std::string& dir) { + std::error_code ec; + std::filesystem::create_directories(dir, ec); + auto now = std::chrono::system_clock::now(); + std::string path = std::format("{}/{:%Y%m%d-%H%M%S}.log", dir, + std::chrono::floor(now)); + FILE* t = ::popen(std::format("tee {}", path).c_str(), "w"); + if (!t) return false; + ::dup2(::fileno(t), 1); + ::setvbuf(stdout, nullptr, _IOLBF, 0); + std::println("transcript: {}", path); + return true; +} + // tee_call_t's second parameter is `unsigned long` on glibc and `int` on musl // (qcomtee_object.h keys it off __GLIBC__), so the signature has to match or // the function pointer will not convert. The native build is glibc and the @@ -1136,12 +1161,15 @@ int main(int argc, char** argv) { if (a == "--rpmb-write") g_rpmbWrite = true; if (a == "--auth") { g_auth = true; g_listeners = true; } if (a.starts_with("--frames=")) g_frames = std::stoi(std::string(a.substr(9))); + if (a.starts_with("--log-dir=")) g_logDir = a.substr(10); if (a.starts_with("--sfs-root=")) g_sfsRoot = a.substr(11); if (a.starts_with("--gid=")) g_gid = static_cast( std::stoul(std::string(a.substr(6)))); } - if (probe) + if (probe) { + StartTranscript(g_logDir); return Probe(); + } std::println(std::cerr, "fingerprintd {}: no runtime yet. --probe-tee reaches QTEE; "