try to guard accidental region load command

This commit is contained in:
liquidraver
2026-08-28 10:59:42 +02:00
parent bc208ade26
commit 2a519e11d1
7 changed files with 42 additions and 10 deletions
+2 -2
View File
@@ -96,7 +96,7 @@ Regions control which flood packets the repeater forwards. The region tree is hi
| Command | Description |
|---------|-------------|
| `region` | Export the current region map (indented text tree) |
| `region load` | Enter interactive region load mode. Paste indented region lines; send a blank line to commit |
| `region load` | Enter interactive region load mode. Paste indented region lines; send a blank line to commit. Any unindented command (e.g. `reboot`) aborts the load without committing, and then runs |
| `region save` | Save the current region map to persistent storage |
| `region def <token> [...]` | Cursor-walk bulk region builder — define a hierarchy in one line (see below) |
| `region put <name> [<parent>]` | Create a region; default parent is the wildcard root. Flood is **allowed** by default (use `region denyf` to deny) |
@@ -346,5 +346,5 @@ Changes are persisted immediately unless noted. Some require a reboot.
- **USB-only commands** — `get acl`, `get prv.key`, `set freq`, `log` (dump), `stats-packets`, `stats-radio`, `stats-core`, `erase` — are blocked when the command arrives over the mesh (remote admin). These are the only ones gated on `sender_timestamp == 0`; `get public.key` and `set prv.key` are **not** among them.
- **Adaptive contention window** — `txdelay`, `rxdelay`, and `direct.txdelay` are accepted and stored for Arduino prefs compatibility but have no effect. Use `get txdelay` to inspect the current adaptive state and `set backoff.multiplier` to tune reactive backoff.
- **Region load mode** — after `region load`, every line received is parsed as a region entry until a blank line is sent. The loaded map is only committed to the live region tree at that point; use `region save` to persist it.
- **Region load mode** — after `region load`, every line received is parsed as a region entry until a blank line is sent. The loaded map is only committed to the live region tree at that point; use `region save` to persist it. Region rows must be indented by at least one space, so an **unindented line that starts with a name character aborts the mode and is executed as a normal command** — the escape hatch if a `region load` is started by accident or a client dies mid-transfer. An abort discards the partial map, leaving the live region tree untouched. The exported wildcard header line `*` stays unindented and is ignored as before, so pasting the output of `region` still loads cleanly.
- **Reboot delay** — `start dfu`, `start ota` (nRF52 BLE-DFU path only), `reboot`, `clkreboot` and `erase` defer the reset by **2 seconds** so the reply can be transmitted over LoRa first. On a companion the handler then keeps deferring in 20 ms steps until the BLE/USB transport has drained, up to a further 3 s grace. On ESP32 `start ota` starts a WiFi AP + HTTP server and does **not** reboot.
+1 -1
View File
@@ -1351,7 +1351,7 @@ void RepeaterMesh::resetDutyCycleTimeoutRestarts() {
void RepeaterMesh::handleCommand(uint32_t sender_timestamp, char* command, char* reply) {
if (region_load_active) {
handleRegionLoadLine(command, reply);
handleRegionLoadLine(sender_timestamp, command, reply);
return;
}
+1 -1
View File
@@ -152,7 +152,7 @@ class RepeaterMesh : public mesh::Mesh, public CommonCLICallbacks {
/* Region-definition CLI (defined in app/RepeaterRegionCLI.cpp).
* handleRegionLoadLine: a continuation line during `region load`.
* handleRegionCommand: a `region ...` command. */
void handleRegionLoadLine(char* command, char* reply);
void handleRegionLoadLine(uint32_t sender_timestamp, char* command, char* reply);
void handleRegionCommand(char* command, char* reply);
protected:
+17 -1
View File
@@ -57,7 +57,7 @@ static bool processRegionDefSegment(RegionMap* map, char* tok, RegionEntry** cur
}
/* ---------------------------------------- */
void RepeaterMesh::handleRegionLoadLine(char* command, char* reply) {
void RepeaterMesh::handleRegionLoadLine(uint32_t sender_timestamp, char* command, char* reply) {
if (StrHelper::isBlank(command)) {
region_map = temp_map;
region_load_active = false;
@@ -67,6 +67,22 @@ void RepeaterMesh::handleRegionLoadLine(char* command, char* reply) {
while (*np == ' ') np++;
int indent = np - command;
/* An unindented, name-like line is a typed command, not a region row:
* real rows are indent >= 1 (load_stack[0] is the wildcard), and the
* one unindented line a client legitimately sends is the exported
* wildcard header "*", whose '*' is not a name char. Without this,
* `region load` is only escapable by a blank line -- which the USB
* reader discards (main_repeater.cpp) and a dead remote-admin client
* never sends, stranding the CLI until a reboot. Abort without
* committing temp_map and run the command. Must come BEFORE the
* name-terminator write below, which would truncate `set foo 1` to
* `set`. */
if (indent == 0 && RegionMap::is_name_char((uint8_t)*np)) {
region_load_active = false;
handleCommand(sender_timestamp, command, reply);
return;
}
char* ep = np;
while (RegionMap::is_name_char(*ep)) ep++;
if (*ep) { *ep++ = 0; }
+2 -2
View File
@@ -1008,11 +1008,11 @@ void RoomServerMesh::resetDutyCycleTimeoutRestarts() {
}
/* Region-def CLI (handleRegionLoadLine / handleRegionCommand) and its static
* parser helpers live in app/RepeaterRegionCLI.cpp. */
* parser helpers live in app/RoomServerRegionCLI.cpp. */
void RoomServerMesh::handleCommand(uint32_t sender_timestamp, char* command, char* reply) {
if (region_load_active) {
handleRegionLoadLine(command, reply);
handleRegionLoadLine(sender_timestamp, command, reply);
return;
}
+2 -2
View File
@@ -120,10 +120,10 @@ class RoomServerMesh : public mesh::Mesh, public CommonCLICallbacks {
void sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint32_t delay_millis, uint8_t path_hash_size);
void sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size);
/* Region-definition CLI (defined in app/RepeaterRegionCLI.cpp).
/* Region-definition CLI (defined in app/RoomServerRegionCLI.cpp).
* handleRegionLoadLine: a continuation line during `region load`.
* handleRegionCommand: a `region ...` command. */
void handleRegionLoadLine(char* command, char* reply);
void handleRegionLoadLine(uint32_t sender_timestamp, char* command, char* reply);
void handleRegionCommand(char* command, char* reply);
protected:
+17 -1
View File
@@ -57,7 +57,7 @@ static bool processRegionDefSegment(RegionMap* map, char* tok, RegionEntry** cur
}
/* ---------------------------------------- */
void RoomServerMesh::handleRegionLoadLine(char* command, char* reply) {
void RoomServerMesh::handleRegionLoadLine(uint32_t sender_timestamp, char* command, char* reply) {
if (StrHelper::isBlank(command)) {
region_map = temp_map;
region_load_active = false;
@@ -67,6 +67,22 @@ void RoomServerMesh::handleRegionLoadLine(char* command, char* reply) {
while (*np == ' ') np++;
int indent = np - command;
/* An unindented, name-like line is a typed command, not a region row:
* real rows are indent >= 1 (load_stack[0] is the wildcard), and the
* one unindented line a client legitimately sends is the exported
* wildcard header "*", whose '*' is not a name char. Without this,
* `region load` is only escapable by a blank line -- which the USB
* reader discards (main_repeater.cpp) and a dead remote-admin client
* never sends, stranding the CLI until a reboot. Abort without
* committing temp_map and run the command. Must come BEFORE the
* name-terminator write below, which would truncate `set foo 1` to
* `set`. */
if (indent == 0 && RegionMap::is_name_char((uint8_t)*np)) {
region_load_active = false;
handleCommand(sender_timestamp, command, reply);
return;
}
char* ep = np;
while (RegionMap::is_name_char(*ep)) ep++;
if (*ep) { *ep++ = 0; }