Public Access
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:
@@ -67,15 +67,21 @@ class FileSource : public UpdateSource {
|
||||
// A release being downloaded from Gitea: the same bytes a push or a card would give.
|
||||
class GiteaSource : public UpdateSource {
|
||||
public:
|
||||
explicit GiteaSource(HttpsGet& get) : get_(get) {}
|
||||
int read(uint8_t* buf, size_t len) override { return get_.read(buf, len); }
|
||||
GiteaSource(HttpsGet& get, long cutAfter, long flipAt) : get_(get), cut_(cutAfter), flip_(flipAt) {}
|
||||
int read(uint8_t* buf, size_t len) override {
|
||||
if (cut_ >= 0 && pos_ >= cut_) return -1; // Debug Builds: the connection "breaks" here
|
||||
int n = get_.read(buf, len);
|
||||
if (n > 0 && flip_ >= pos_ && flip_ < pos_ + n) buf[flip_ - pos_] ^= 1; // and a byte "arrives wrong"
|
||||
if (n > 0) pos_ += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
private:
|
||||
HttpsGet& get_;
|
||||
long cut_, flip_, pos_ = 0;
|
||||
};
|
||||
|
||||
constexpr time_t kClockSetAfter = 1700000000; // anything earlier is the clock's default
|
||||
constexpr size_t kCheckFloor = 55 * 1024; // Q86, Q172
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -106,7 +112,7 @@ void UpdateService::start() {
|
||||
store_.putString("ota_pending", "");
|
||||
}
|
||||
|
||||
if (!task_) xTaskCreate(taskEntry, "update", 8192, this, 1, &task_); // TLS to Gitea (#6): measured below
|
||||
if (!task_) xTaskCreate(taskEntry, "update", 7168, this, 1, &task_); // peak 5.4 KB: TLS to Gitea and the signature check (#6)
|
||||
}
|
||||
|
||||
void UpdateService::bootGuard(KeyValueStore& store) {
|
||||
@@ -235,7 +241,8 @@ void UpdateService::scheduleDailyCheck(uint32_t nowMs) {
|
||||
nextCheckMs_ = nowMs + 3600000; // look again in an hour, in case the day has turned
|
||||
return;
|
||||
}
|
||||
if (esp_get_free_heap_size() < kCheckFloor) {
|
||||
// Never at IRC's expense: with IRC connected there isn't the room (Q172), so this waits.
|
||||
if (esp_get_free_heap_size() < HttpsGet::kNeedFree) {
|
||||
nextCheckMs_ = nowMs + 600000;
|
||||
return;
|
||||
}
|
||||
@@ -243,11 +250,31 @@ void UpdateService::scheduleDailyCheck(uint32_t nowMs) {
|
||||
request_ = Request::BackgroundCheck;
|
||||
}
|
||||
|
||||
// What a person asked for (a check, a list, an install) may take IRC offline for a few seconds:
|
||||
// a TLS connection needs about 80 KB and IRC's own holds 40 KB of what there is (R1, Q172).
|
||||
bool UpdateService::makeRoom() {
|
||||
if (esp_get_free_heap_size() >= HttpsGet::kNeedFree) return true;
|
||||
if (!holdMemory) return false;
|
||||
held_ = true;
|
||||
holdMemory(true);
|
||||
for (int i = 0; i < 40 && esp_get_free_heap_size() < HttpsGet::kNeedFree; i++) delay(100); // its TLS session goes
|
||||
return esp_get_free_heap_size() >= HttpsGet::kNeedFree;
|
||||
}
|
||||
|
||||
void UpdateService::serve() {
|
||||
Request r = request_;
|
||||
if (r == Request::None) return;
|
||||
request_ = Request::None;
|
||||
serving_ = true;
|
||||
held_ = false;
|
||||
if (r != Request::BackgroundCheck && r != Request::None) {
|
||||
bool ok = makeRoom();
|
||||
if (!ok) {
|
||||
gitea_.fail("Not enough memory: close a Gemini page");
|
||||
if (r == Request::Install) notify("Update refused: not enough memory", NotificationLevel::Warning);
|
||||
r = Request::None;
|
||||
}
|
||||
}
|
||||
switch (r) {
|
||||
case Request::Check:
|
||||
case Request::BackgroundCheck: {
|
||||
@@ -268,8 +295,19 @@ void UpdateService::serve() {
|
||||
if (gitea_.find(installTag_, release)) installFromGitea(release);
|
||||
break;
|
||||
}
|
||||
#ifdef RORO_DEBUG
|
||||
case Request::Probe: {
|
||||
HttpsGet get(net::User::Updates);
|
||||
std::string why = get.open(probeHost_, probePath_, "*/*");
|
||||
probeResult_ = why.empty() ? "accepted, the server answered 200" : why;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case Request::None: break;
|
||||
}
|
||||
// IRC comes back, unless the device is about to restart into an update.
|
||||
if (held_ && phase_ != Phase::Installed && holdMemory) holdMemory(false);
|
||||
held_ = false;
|
||||
serving_ = false;
|
||||
}
|
||||
|
||||
@@ -287,7 +325,12 @@ void UpdateService::installFromGitea(const release::Release& r) {
|
||||
notify("Update refused: " + why, NotificationLevel::Warning);
|
||||
return;
|
||||
}
|
||||
GiteaSource source(get);
|
||||
#ifdef RORO_DEBUG
|
||||
GiteaSource source(get, damageCut_, damageFlip_);
|
||||
damageCut_ = damageFlip_ = -1;
|
||||
#else
|
||||
GiteaSource source(get, -1, -1);
|
||||
#endif
|
||||
install(source, "Gitea");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user