From f3d96ae2ee79587d83af9fe866e4b3bfc02083db Mon Sep 17 00:00:00 2001 From: ssmithx Date: Sat, 5 Sep 2026 15:14:00 +0000 Subject: [PATCH] fix(openwrt): resolve opkg/apk via $PATH, not a hardcoded /usr/bin path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit opkg_check() and every opkg/apk invocation hardcoded /usr/bin/opkg and /usr/bin/apk. Official OpenWrt images don't all symlink /bin into /usr/bin — the glinet_gl-mt3000 24.10.2 build keeps them as separate real directories with opkg living in /bin — so the check silently missed a perfectly normal install and TollGate provisioning failed with "this router's firmware may not support package management". Switched every call to resolve through the router's own $PATH (command -v / bare opkg / apk) instead. Reproduced and fixed live against archy-x250-pa3, 2026-09-05. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_0176RpCxFNS9ZaSJjL72W9Z5 --- core/archipelago/src/api/rpc/openwrt.rs | 2 +- core/openwrt/src/opkg.rs | 34 ++++++++++++++----------- core/openwrt/src/tollgate/install.rs | 4 +-- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/core/archipelago/src/api/rpc/openwrt.rs b/core/archipelago/src/api/rpc/openwrt.rs index 7ab27e30..32ac1ea6 100644 --- a/core/archipelago/src/api/rpc/openwrt.rs +++ b/core/archipelago/src/api/rpc/openwrt.rs @@ -135,7 +135,7 @@ impl RpcHandler { // not /usr/bin/tollgate-module-basic-go — that's only the opkg/apk // *package* name, never an on-disk filename. let tollgate_installed = router - .run("/usr/bin/opkg list-installed 2>/dev/null | grep -q '^tollgate-module-basic-go ' || \ + .run("opkg list-installed 2>/dev/null | grep -q '^tollgate-module-basic-go ' || \ test -f /usr/bin/tollgate-wrt 2>/dev/null") .map(|(_, code)| code == 0) .unwrap_or(false); diff --git a/core/openwrt/src/opkg.rs b/core/openwrt/src/opkg.rs index d62f36cf..332fee32 100644 --- a/core/openwrt/src/opkg.rs +++ b/core/openwrt/src/opkg.rs @@ -15,25 +15,32 @@ pub enum PkgManager { impl Router { /// Detect which package manager is available. /// - /// - If `/usr/bin/opkg` exists → `PkgManager::Opkg` (nothing to do). - /// - If `/usr/bin/apk` exists → run `apk update` (switching repos to HTTP + /// Looks up `opkg`/`apk` via the router's `$PATH` (`command -v`) rather + /// than a hardcoded `/usr/bin/` — official OpenWrt images don't all + /// symlink `/bin` into `/usr/bin` (e.g. the `glinet_gl-mt3000` 24.10.2 + /// build keeps them as separate real directories with `opkg` living in + /// `/bin`), so a fixed absolute path silently misses a perfectly normal + /// install and reports "no package management" (archy-x250-pa3, 2026-09-05). + /// + /// - If `opkg` is on PATH → `PkgManager::Opkg` (nothing to do). + /// - If `apk` is on PATH → run `apk update` (switching repos to HTTP /// first to work around missing CA bundle on fresh images), then try /// `apk add opkg`. If opkg is in the repos → `Opkg`. If not (OpenWrt /// 25.x) → `ApkNative`. /// - Neither found → error. pub fn opkg_check(&self) -> Result { - let (_, code) = self.run("test -x /usr/bin/opkg")?; + let (_, code) = self.run("command -v opkg >/dev/null 2>&1")?; if code == 0 { return Ok(PkgManager::Opkg); } - let (_, apk_code) = self.run("test -x /usr/bin/apk")?; + let (_, apk_code) = self.run("command -v apk >/dev/null 2>&1")?; if apk_code == 0 { info!("[{}] opkg not found — using apk (OpenWrt 25.x+)", self.host); // Fresh images ship without a CA bundle; switch repos to HTTP so // apk's wget can reach the package index without TLS verification. self.run_ok("sed -i 's|https://|http://|g' /etc/apk/repositories 2>/dev/null || true")?; - let (update_out, update_code) = self.run("/usr/bin/apk update 2>&1")?; + let (update_out, update_code) = self.run("apk update 2>&1")?; if update_code != 0 { anyhow::bail!( "apk update failed (exit {}) — router may have no internet access. \ @@ -43,7 +50,7 @@ impl Router { ); } // Try to install opkg (only available on some 25.x builds). - let (add_out, add_code) = self.run("/usr/bin/apk add opkg 2>&1")?; + let (add_out, add_code) = self.run("apk add opkg 2>&1")?; if add_code == 0 { return Ok(PkgManager::Opkg); } @@ -62,7 +69,7 @@ impl Router { } anyhow::bail!( - "opkg not found at /usr/bin/opkg — this router's firmware may not \ + "Neither opkg nor apk found on this router's $PATH — its firmware may not \ support package management (TollGate requires a standard OpenWrt build)" ); } @@ -70,31 +77,28 @@ impl Router { /// `opkg update` — refresh package lists. pub fn opkg_update(&self) -> Result<()> { info!("[{}] opkg update", self.host); - self.run_ok("/usr/bin/opkg update")?; + self.run_ok("opkg update")?; 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. - let (_, code) = self.run(&format!( - "/usr/bin/opkg list-installed | grep -q '^{} '", - package - ))?; + let (_, code) = self.run(&format!("opkg list-installed | grep -q '^{} '", package))?; if code == 0 { info!("[{}] {} already installed", self.host, package); return Ok(()); } info!("[{}] opkg install {}", self.host, package); - self.run_ok(&format!("/usr/bin/opkg install {}", package))?; + self.run_ok(&format!("opkg install {}", package))?; Ok(()) } /// Remove a package. pub fn opkg_remove(&self, package: &str) -> Result<()> { info!("[{}] opkg remove {}", self.host, package); - self.run_ok(&format!("/usr/bin/opkg remove {}", package))?; + self.run_ok(&format!("opkg remove {}", package))?; Ok(()) } @@ -121,7 +125,7 @@ impl Router { } info!("[{}] apk add {}", self.host, package); - self.run_ok(&format!("/usr/bin/apk add {}", package))?; + self.run_ok(&format!("apk add {}", package))?; Ok(()) } } diff --git a/core/openwrt/src/tollgate/install.rs b/core/openwrt/src/tollgate/install.rs index ff26a09e..e74db978 100644 --- a/core/openwrt/src/tollgate/install.rs +++ b/core/openwrt/src/tollgate/install.rs @@ -35,7 +35,7 @@ pub fn install_tollgate(router: &Router) -> Result<()> { // Package not in any feed — download the .ipk directly. let arch = router - .run_ok("/usr/bin/opkg print-architecture | grep -v all | grep -v noarch | tail -1 | awk '{print $2}'")?; + .run_ok("opkg print-architecture | grep -v all | grep -v noarch | tail -1 | awk '{print $2}'")?; let arch = arch.trim(); let url = ipk_url(arch).ok_or_else(|| { @@ -88,7 +88,7 @@ pub fn install_tollgate_apk_native(router: &Router) -> Result<()> { ". /etc/openwrt_release 2>/dev/null \ && a=\"${DISTRIB_ARCH:-${OPENWRT_ARCH:-}}\" \ && [ -n \"$a\" ] && echo \"$a\" \ - || /usr/bin/apk --print-arch 2>/dev/null \ + || apk --print-arch 2>/dev/null \ || uname -m", )?; // Normalise: uname -m returns bare "mipsel"/"mips"; map to 24kc variant