66 lines
2.3 KiB
Rust
Raw Normal View History

use anyhow::Result;
use crate::Router;
/// Thin wrappers around `uci` CLI commands over SSH.
impl Router {
/// `uci get <key>` — returns trimmed value.
pub fn uci_get(&self, key: &str) -> Result<String> {
let out = self.run_ok(&format!("uci get {}", key))?;
Ok(out.trim().to_string())
}
/// `uci set <key>=<value>`
pub fn uci_set(&self, key: &str, value: &str) -> Result<()> {
self.run_ok(&format!("uci set {}={}", key, shell_quote(value)))?;
Ok(())
}
/// `uci add <config> <type>` — returns the new section name.
pub fn uci_add(&self, config: &str, section_type: &str) -> Result<String> {
let out = self.run_ok(&format!("uci add {} {}", config, section_type))?;
Ok(out.trim().to_string())
}
/// `uci add_list <key>=<value>`
pub fn uci_add_list(&self, key: &str, value: &str) -> Result<()> {
self.run_ok(&format!("uci add_list {}={}", key, shell_quote(value)))?;
Ok(())
}
/// `uci delete <key>`
pub fn uci_delete(&self, key: &str) -> Result<()> {
self.run_ok(&format!("uci delete {}", key))?;
Ok(())
}
/// `uci commit [<config>]`
pub fn uci_commit(&self, config: Option<&str>) -> Result<()> {
match config {
Some(c) => self.run_ok(&format!("uci commit {}", c))?,
None => self.run_ok("uci commit")?,
};
Ok(())
}
/// Batch: apply a list of `(key, value)` pairs then commit the config.
pub fn uci_apply(&self, config: &str, pairs: &[(&str, &str)]) -> Result<()> {
fix(openwrt): fix TollGate provisioning pipeline, add reconfigure UI Several compounding bugs were blocking end-to-end TollGate provisioning on OpenWrt 25.x (apk-native) routers: - install_ipk's non-ar fallback assumed a flat tarball, but some .ipks are a gzip tar of the three classic ipk members one level deep; it was dumping debian-binary/data.tar.gz/control.tar.gz straight into / instead of unpacking the real payload. - Manually-extracted packages never ran their pending /etc/uci-defaults/* scripts (that only happens through opkg/apk's own postinst bookkeeping), so nothing ever created /etc/config/tollgate. - uci_apply() never ensured the target config file existed first — `uci set` fails outright on a config namespace nothing has created yet, which is true for a package-defined one like "tollgate" (unlike wireless/ network/dhcp, which ship by default). - The installed-check and restart_services looked for a binary/init script named after the opkg package ("tollgate-module-basic-go"/"tollgate"), but the real on-disk names are tollgate-wrt — so status always reported "not installed" and service restarts silently no-op'd. - provision_ssid used `uci add`, creating a new wifi-iface section (and therefore a new duplicate broadcast SSID) on every provision call instead of updating one in place. Also adds a TollGateConfig.enabled field so the enable/disable state is actually applied to the running service and the SSID's own broadcast (stop + disable at boot, or start + enable), not just written to UCI. On the frontend, the OpenWrt Gateway page's TollGate panel was read-only once installed — add an edit form (price, step size, min steps, mint URL, enabled toggle) that reuses the same idempotent provision-tollgate call.
2026-07-01 11:59:43 +00:00
// `uci set config.section=type` fails with "Entry not found" if
// /etc/config/<config> doesn't exist yet — true for any config file
// shipped by the base system (wireless, network, dhcp, ...) but not
// for a package-defined namespace like "tollgate" that nothing has
// created a default for. `touch` is a no-op if it already exists.
self.run_ok(&format!("touch /etc/config/{}", config))?;
for (key, value) in pairs {
self.uci_set(key, value)?;
}
self.uci_commit(Some(config))?;
Ok(())
}
}
/// Wrap a value in single quotes, escaping any embedded single quotes.
fn shell_quote(s: &str) -> String {
format!("'{}'", s.replace('\'', r"'\''"))
}