2026-06-28 18:42:20 +00:00
|
|
|
use anyhow::Result;
|
|
|
|
|
use tracing::info;
|
|
|
|
|
|
|
|
|
|
use crate::Router;
|
|
|
|
|
|
|
|
|
|
impl Router {
|
2026-06-29 14:02:53 +00:00
|
|
|
/// Verify opkg is available. On OpenWrt 25.x+ which ships apk instead of
|
|
|
|
|
/// opkg, bootstraps opkg via `apk add opkg` before returning.
|
2026-06-29 13:17:20 +00:00
|
|
|
pub fn opkg_check(&self) -> Result<()> {
|
|
|
|
|
let (_, code) = self.run("test -x /usr/bin/opkg")?;
|
2026-06-29 14:02:53 +00:00
|
|
|
if code == 0 {
|
|
|
|
|
return Ok(());
|
2026-06-29 13:17:20 +00:00
|
|
|
}
|
2026-06-29 14:02:53 +00:00
|
|
|
// OpenWrt 25.x switched to apk as default — install opkg through it.
|
|
|
|
|
let (_, apk_code) = self.run("test -x /usr/bin/apk")?;
|
|
|
|
|
if apk_code == 0 {
|
|
|
|
|
info!("[{}] opkg not found, bootstrapping via apk", self.host);
|
2026-06-29 14:27:13 +00:00
|
|
|
// Capture stderr so apk errors are visible in server logs.
|
|
|
|
|
let (update_out, update_code) = self.run("/usr/bin/apk update 2>&1")?;
|
|
|
|
|
if update_code != 0 {
|
|
|
|
|
anyhow::bail!(
|
|
|
|
|
"apk update failed (exit {}) — router may have no internet access. \
|
|
|
|
|
Ensure WAN/internet is working on the router before provisioning.\n{}",
|
|
|
|
|
update_code,
|
|
|
|
|
update_out.trim()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
self.run_ok("/usr/bin/apk add opkg 2>&1")?;
|
2026-06-29 14:02:53 +00:00
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
anyhow::bail!(
|
|
|
|
|
"opkg not found at /usr/bin/opkg — this router's firmware may not \
|
|
|
|
|
support package management (TollGate requires a standard OpenWrt build)"
|
|
|
|
|
);
|
2026-06-29 13:17:20 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-28 18:42:20 +00:00
|
|
|
/// `opkg update` — refresh package lists.
|
|
|
|
|
pub fn opkg_update(&self) -> Result<()> {
|
|
|
|
|
info!("[{}] opkg update", self.host);
|
2026-06-29 13:17:20 +00:00
|
|
|
self.run_ok("/usr/bin/opkg update")?;
|
2026-06-28 18:42:20 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Install a package, skipping if already installed.
|
|
|
|
|
pub fn opkg_install(&self, package: &str) -> Result<()> {
|
|
|
|
|
// Check if already installed to avoid unnecessary network traffic.
|
2026-06-29 13:17:20 +00:00
|
|
|
let (_, code) = self.run(&format!("/usr/bin/opkg list-installed | grep -q '^{} '", package))?;
|
2026-06-28 18:42:20 +00:00
|
|
|
if code == 0 {
|
|
|
|
|
info!("[{}] {} already installed", self.host, package);
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info!("[{}] opkg install {}", self.host, package);
|
2026-06-29 13:17:20 +00:00
|
|
|
self.run_ok(&format!("/usr/bin/opkg install {}", package))?;
|
2026-06-28 18:42:20 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Remove a package.
|
|
|
|
|
pub fn opkg_remove(&self, package: &str) -> Result<()> {
|
|
|
|
|
info!("[{}] opkg remove {}", self.host, package);
|
2026-06-29 13:17:20 +00:00
|
|
|
self.run_ok(&format!("/usr/bin/opkg remove {}", package))?;
|
2026-06-28 18:42:20 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|