Updates from Gitea, step 3: install from Gitea, and IRC steps aside for it

A release downloads straight into the inactive slot through the existing
install path: the signature is checked after 160 bytes, before anything
is written, the hash at the end. Tried on the device against the real
server: a download cut short, a flipped byte in the signature and one in
the image are each refused with the running firmware untouched; the real
v0.10.0 installed, restarted, and confirmed itself on Probation.

A TLS connection to Gitea peaks at about 52 KB of heap whether or not the
certificate is verified. With IRC connected (66 KB free) a check left 3 KB
and a download 836 bytes. A check, list or install a person asks for now
makes IRC step aside (holdForUpdate) and come back after: the lowest free
heap during a full download with IRC connected is 38 KB. The daily check
never interrupts IRC; with IRC up it waits. A TLS connection starts with
80 KB free (it was 55).

Debug Builds get test knobs: update probe <host>, update damage cut|flip,
update pretend <version>.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-06 15:51:10 +02:00
co-authored by Claude Sonnet 5.5
parent 5754ae5b57
commit 510ce42a99
10 changed files with 120 additions and 10 deletions
+19 -1
View File
@@ -2,6 +2,7 @@
#include <freertos/FreeRTOS.h>
#include <functional>
#include <string>
#include "update_check.h"
@@ -53,12 +54,22 @@ class UpdateService : public Service {
// it would take its Debug Console away) unless `force`, which only the Debug Console passes.
std::string requestInstall(const std::string& tag, bool force = false);
bool giteaBusy() const { return request_ != Request::None || serving_; }
// Asked to make room for a TLS connection (R1, Q172): IRC steps aside while the Update Service
// talks to Gitea. Set by the main loop; `true` to step aside, `false` to come back.
std::function<void(bool)> holdMemory;
// The daily check (Q165) only runs once the main loop says it may: not in Safe Mode.
void enableDailyCheck() { dailyCheck_ = true; }
// The version that counts as running for comparisons: a Debug Build can pretend to be older.
std::string runningVersion() const { return fakeVersion_.empty() ? versionString() : fakeVersion_; }
void pretendVersion(const std::string& v) { fakeVersion_ = v; }
std::string failedVersion() const; // a release that rolled back here, "" if none
#ifdef RORO_DEBUG
// Debug Builds only, to try the refusals on the real network path: one HTTPS GET to any host
// (is its certificate accepted?), and a download cut short or with one byte flipped.
void requestProbe(const std::string& host, const std::string& path) { probeHost_ = host; probePath_ = path; probeResult_ = "..."; request_ = Request::Probe; }
std::string probeResult() const { return probeResult_; }
void damageNextDownload(long cutAfter, long flipAt) { damageCut_ = cutAfter; damageFlip_ = flipAt; }
#endif
// The main loop calls this once it has drawn a frame (part of Probation).
void firstFrameDrawn() { firstFrame_ = true; }
@@ -66,9 +77,10 @@ class UpdateService : public Service {
bool rebootPending() const { return phase_ == Phase::Installed; }
private:
enum class Request : uint8_t { None, Check, BackgroundCheck, List, Install };
enum class Request : uint8_t { None, Check, BackgroundCheck, List, Install, Probe };
void serve(); // on this task: one request
bool makeRoom(); // true when a TLS connection can start; may have asked holdMemory
void installFromGitea(const release::Release& release);
void scheduleDailyCheck(uint32_t nowMs); // from tick()
static void taskEntry(void* self);
@@ -92,9 +104,15 @@ class UpdateService : public Service {
GiteaReleases gitea_;
volatile Request request_ = Request::None;
volatile bool serving_ = false;
bool held_ = false; // IRC was asked to step aside, on this task
std::string installTag_, fakeVersion_, announced_;
bool dailyCheck_ = false;
uint32_t nextCheckMs_ = 90000; // not before the device has settled
#ifdef RORO_DEBUG
std::string probeHost_, probePath_;
volatile long damageCut_ = -1, damageFlip_ = -1;
std::string probeResult_;
#endif
};
} // namespace roro