using System.Text.Json; using System.Text.Json.Serialization; using Fleet.Core; using Fleet.Hosting; using MoShiro.Measurement; using MoShiro.Protocol; namespace MoShiro.Agent; /// /// Native supervisor bridge: writes only fixed wake-up flags and a health beacon in tmpfs. /// Never downloads, installs, verifies releases, or executes commands. The root-owned host /// updater independently fetches and verifies its locally configured release feed. /// public sealed class OpenWrtUpdater { private readonly ConfigStore _runtime; private readonly string _statusPath; private readonly TimeProvider _time; private readonly INodeLog _log; public OpenWrtUpdater(string runtimeDirectory, string statusPath, TimeProvider time, INodeLog log) { _runtime = new ConfigStore(runtimeDirectory); _statusPath = statusPath; _time = time; _log = log; } public static OpenWrtUpdater? FromEnvironment(TimeProvider time, INodeLog log) => Environment.GetEnvironmentVariable(RestartUpdater.EnvironmentVariable) == "openwrt" ? new OpenWrtUpdater("/var/run/moshiro-agent", "/var/run/moshiro-update/status.json", time, log) : null; public void Offer() => Signal("check.request"); public void RequestApply() => Signal("apply.request"); private void Signal(string fileName) { try { _runtime.Write(fileName, "check"u8.ToArray()); } catch (Exception ex) when (ex is IOException or UnauthorizedAccessException) { _log.Warn("could not signal the native updater", ("err", ex.Message)); } } /// Called by the Fleet heartbeat: evidence that this version reached Backend. public UpdateStatus? Status() { try { var health = new OpenWrtHealth(BuildInfo.Version, Environment.ProcessId, _time.GetUtcNow().ToUnixTimeSeconds()); _runtime.Write("health.json", JsonSerializer.SerializeToUtf8Bytes(health, AgentConfigContext.Default.OpenWrtHealth)); if (!File.Exists(_statusPath) || new FileInfo(_statusPath).Length > 8192) { return null; } return JsonSerializer.Deserialize(File.ReadAllBytes(_statusPath), MoShiroJsonContext.Default.UpdateStatus); } catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or JsonException) { _log.Warn("could not read native update status", ("err", ex.Message)); return null; } } } public sealed record OpenWrtHealth( [property: JsonPropertyName("version")] string Version, [property: JsonPropertyName("pid")] int Pid, [property: JsonPropertyName("at")] long At);