Blocking snu.lge.com in /etc/hosts never stopped the boot popup on a CX, and the reason only showed up on the TV itself: /usr/sbin/update is a systemd unit (webos-mbd.target) that runs its version check against https://snu.lge.com/CheckSWAutoUpdate.laf 15-20 seconds before the Homebrew Channel gets as far as running its init.d hooks. The check therefore succeeds on every boot and the alert is already on screen before any hosts entry exists. Homebrew Channel's own "block system updates" toggle loses the same race. Nothing running that late can win it: every systemd unit path is a read-only overlay except tmpfs /run, and no persistent setting gates the check - automaticUpdate, support/softwareUpdateEnable, hotelMode/swUpdateEnable and .UpdateIsInprogress were each measured by restarting the daemon and counting its requests. So dismiss the popup instead. The boot hook recovers the alert id from the updater's own log (_gAlertWindowId), which is the only way to reach an alert that opened before we could subscribe - com.webos.notification never reports it to a late subscriber and closeAllAlerts rejects every source id it accepts. A companion alert-watch.sh then stays subscribed for the rest of the session. Also drop two things that were never true. There is no staged firmware image driving the popup (the staging dir is empty at boot; the size the daemon reports is in-memory only), and there is no update service to stop - /etc/init is dead upstart leftovers on a systemd TV, so the old stopServices layer printed "stopped update" while doing nothing. - rename the hook to 00-lgupdateblocker so run-parts runs it first, removing the legacy file on apply - add support/ and hotelMode/ to the scanned settings categories - stop matching "ota" inside screenRotation, which would have switched screen rotation off - kill the watcher by process group, and make its TERM trap exit - a trap that only returns resumes the script, which then re-subscribes Verified on an LG OLED55CX8LB (webOS 5, 04.60.65): after a reboot the boot log records "dismissed update popup com.webos.service.update-1788650782451", matching the id the updater logged that boot. Raised 01:26:22, closed 01:26:37 - so it is visible for ~15s and then goes away on its own. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ae8229588e
commit
ef8b6b96c4
@@ -13,32 +13,63 @@ only ever probes for things and acts on what it actually finds on your TV.
|
||||
|
||||

|
||||
|
||||
## Why the popup keeps coming back
|
||||
## Why blocking the update servers is not enough
|
||||
|
||||
The Homebrew Channel's own *Block system updates* toggle only writes four
|
||||
hostnames into `/etc/hosts` at boot. That is not enough in practice:
|
||||
Blocking `snu.lge.com` in `/etc/hosts` is the obvious fix, and it is what the
|
||||
Homebrew Channel's own *Block system updates* toggle does. On a CX it does not
|
||||
stop the boot popup, and it took a session on the TV to work out why.
|
||||
|
||||
* it misses several update hostnames (`nsu`, `*-ssl`, the CDN aliases),
|
||||
* it does nothing about a firmware image the TV **already downloaded** — a
|
||||
staged update keeps prompting even when the update servers are unreachable,
|
||||
* it leaves the auto-update settings and the update services alone.
|
||||
`/usr/sbin/update` is started by **systemd** (`update.service`, pulled in by
|
||||
`webos-mbd.target`) and runs its version check against
|
||||
`https://snu.lge.com/CheckSWAutoUpdate.laf` roughly **20 seconds before** the
|
||||
Homebrew Channel gets as far as running its `init.d` hooks. So on every boot
|
||||
the check reaches a real LG server, gets `RESULT_CD 900 / Success` back, and
|
||||
the popup is already on screen by the time any hosts entry exists:
|
||||
|
||||
This app covers all of that, and re-applies everything on each boot (webOS
|
||||
resets `/`, `/tmp` and the mount namespace on every start).
|
||||
```text
|
||||
00:20:52 [tNsuMainTask] pRequestUpdateUrl - https://snu.lge.com/CheckSWAutoUpdate.laf
|
||||
00:20:52 [updateTimer] NSU_CreateUpdateAlert - "…Version: 04.64.00…"
|
||||
00:21:11 lg-update-blocker: hosts entries added ← 19 seconds too late
|
||||
```
|
||||
|
||||
Losing that race is not fixable from userspace. Every directory systemd reads
|
||||
units from is a read-only overlay except `/run/systemd/system`, which is tmpfs
|
||||
and created after the updater has already gone. Nor is there a setting that
|
||||
gates the check — `automaticUpdate`, `support/softwareUpdateEnable`,
|
||||
`hotelMode/swUpdateEnable` and the `.UpdateIsInprogress` flag were each tested
|
||||
by restarting the daemon and counting the requests it made, and none of them
|
||||
stop it.
|
||||
|
||||
So this app **closes the popup** instead of trying to prevent it, and blocks
|
||||
the hostnames to stop the download and every later check in the session. Two
|
||||
things also turned out not to be true, and the app no longer pretends
|
||||
otherwise: the popup is not caused by a staged firmware image (the staging
|
||||
directory is empty at boot; the size the daemon reports is in-memory only), and
|
||||
it is not caused by an update *service* that can be stopped — `/etc/init` is
|
||||
dead upstart leftovers on a systemd TV, and `initctl stop update` silently
|
||||
"succeeds" while doing nothing.
|
||||
|
||||
## Protection layers
|
||||
|
||||
| Layer | What it does | Default |
|
||||
| --- | --- | --- |
|
||||
| **Block LG update servers** | Points `snu`/`su`/`nsu.lge.com`, their TLS variants, the `-dev` fallbacks and the CDN aliases at `127.0.0.1` in `/etc/hosts` | on |
|
||||
| **Delete staged firmware** | Wipes the update image staged in `/mnt/lg/cmn_data/swupdate` | on |
|
||||
| **Close the update popup** | Closes the alert the updater raised during boot, then stays subscribed to `com.webos.notification` and closes any later one as it opens | on |
|
||||
| **Delete staged firmware** | Wipes the update image staged in `/mnt/lg/cmn_data/swupdate` if the TV ever does download one | on |
|
||||
| **Lock the staging folder** | Bind-mounts an empty read-only directory over it, so nothing can be staged again | off |
|
||||
| **Turn off auto-update settings** | Reads every `com.webos.settingsservice` category, switches off each update-related key it finds, and remembers the original values | on |
|
||||
| **Stop update services at boot** | Stops the update-related upstart jobs discovered in `/etc/init` (advanced) | off |
|
||||
| **Turn off auto-update settings** | Reads every `com.webos.settingsservice` category, switches off each update-related key it finds, and remembers the original values. On a CX this includes `hotelMode/swUpdateEnable`, which also hides the *Software Update* menu until you revert | on |
|
||||
|
||||
Every layer is undone by **Remove protection**, which also restores the
|
||||
settings from the backup it made.
|
||||
|
||||
Be honest about what this buys you: the popup is closed within a fraction of a
|
||||
second of the Homebrew Channel starting its hooks, but the updater raised it
|
||||
15–20 seconds earlier, so on a cold boot **you will see it appear and then
|
||||
vanish by itself**. It is gone before you can act on it, and you never have to
|
||||
dismiss it. The hook is installed as `00-lgupdateblocker` so `run-parts` runs
|
||||
it before the other homebrew hooks, which is as early as anything unprivileged
|
||||
can go. Measured on a CX: alert raised at `01:26:22`, closed at `01:26:37`.
|
||||
|
||||
## Requirements
|
||||
|
||||
* A rooted webOS TV with the [Homebrew Channel](https://github.com/webosbrew/webos-homebrew-channel)
|
||||
@@ -67,7 +98,7 @@ to the newest release.
|
||||
Download the `.ipk` from the release and:
|
||||
|
||||
```sh
|
||||
ares-install com.rkievits.lgupdateblocker_1.0.0_all.ipk
|
||||
ares-install com.rkievits.lgupdateblocker_<version>_all.ipk
|
||||
ares-launch com.rkievits.lgupdateblocker
|
||||
```
|
||||
|
||||
@@ -89,8 +120,9 @@ Other buttons: **Purge staged update** (wipe a downloaded image right now),
|
||||
| Path | Purpose |
|
||||
| --- | --- |
|
||||
| `/etc/hosts` | Blocked hostnames, between `# >>> lg-update-blocker >>>` markers. Bind-mounted from `/tmp/lgupdateblocker-hosts` because `/` is read-only |
|
||||
| `/var/lib/webosbrew/init.d/lgupdateblocker` | Boot hook, run by the Homebrew Channel startup script; re-applies the enabled layers |
|
||||
| `/var/lib/webosbrew/lgupdateblocker/` | `config.json`, `hosts.txt`, `settings-backup.json`, `boot.log` |
|
||||
| `/var/lib/webosbrew/init.d/00-lgupdateblocker` | Boot hook, run by the Homebrew Channel startup script; re-applies the enabled layers. The `00-` prefix makes `run-parts` run it first |
|
||||
| `/var/lib/webosbrew/lgupdateblocker/` | `config.json`, `hosts.txt`, `settings-backup.json`, `boot.log`, `alert-watch.sh` |
|
||||
| `/tmp/var/log/update.log` | Read only — the updater logs the id of the alert it raised, which is the only way to close a popup that opened before we were subscribed |
|
||||
| `/mnt/lg/cmn_data/swupdate` | Staged firmware, emptied (and optionally locked) |
|
||||
|
||||
Nothing is written to system partitions, and no LG binaries are patched or
|
||||
@@ -103,7 +135,13 @@ replaced.
|
||||
fix that first (its Settings screen has a button for it).
|
||||
* **Popup still appears after a reboot** — run *Diagnostics* and look at the
|
||||
boot-hook log at the bottom. It records, per boot, whether the hosts entries
|
||||
were added, what was purged and which jobs were stopped.
|
||||
were added, what was purged, and the id of the popup it closed. If there is
|
||||
no `dismissed update popup …` line, check `updateLog` in the same dump for
|
||||
`_gAlertWindowId` — if that is missing too, your TV raises the alert by some
|
||||
other route and the diagnostics dump is what to open an issue with.
|
||||
* **The popup appears for ~15 seconds, then disappears on its own** — expected, see
|
||||
[Why blocking the update servers is not enough](#why-blocking-the-update-servers-is-not-enough).
|
||||
Nothing running after the Homebrew Channel can beat the updater to it.
|
||||
* **Belt and braces** — a hosts file only helps if the TV uses DNS. Blocking
|
||||
`snu.lge.com`, `su.lge.com` and `nsu.lge.com` on your router or Pi-hole as
|
||||
well is the one measure that also survives a factory reset.
|
||||
|
||||
Reference in New Issue
Block a user