145 lines
6.0 KiB
Markdown
145 lines
6.0 KiB
Markdown
<p align="center">
|
|
<img src="assets/icon400.png" width="120" alt="LG Update Blocker">
|
|
</p>
|
|
|
|
# LG Update Blocker
|
|
|
|
A webOS homebrew app that gets rid of the *"a new software version is
|
|
available"* popup LG shows on every boot — without touching the firmware
|
|
itself, and fully reversible.
|
|
|
|
Built for a rooted **LG CX (webOS 5)**, but nothing in it is model specific: it
|
|
only ever probes for things and acts on what it actually finds on your TV.
|
|
|
|

|
|
|
|
## Why the popup keeps coming back
|
|
|
|
The Homebrew Channel's own *Block system updates* toggle only writes four
|
|
hostnames into `/etc/hosts` at boot. That is not enough in practice:
|
|
|
|
* 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.
|
|
|
|
This app covers all of that, and re-applies everything on each boot (webOS
|
|
resets `/`, `/tmp` and the mount namespace on every start).
|
|
|
|
## 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 |
|
|
| **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 |
|
|
|
|
Every layer is undone by **Remove protection**, which also restores the
|
|
settings from the backup it made.
|
|
|
|
## Requirements
|
|
|
|
* A rooted webOS TV with the [Homebrew Channel](https://github.com/webosbrew/webos-homebrew-channel)
|
|
installed and its root status showing **ok**.
|
|
* The app's own service needs root. On first launch it asks the Homebrew
|
|
Channel to elevate it (`elevateService`, falling back to running
|
|
`elevate-service` through `exec`), then restarts itself. The header badge
|
|
turns green when it worked.
|
|
|
|
## Install
|
|
|
|
### Through the Homebrew Channel (repository)
|
|
|
|
Add the repository once — *Homebrew Channel → Settings → Add repository*:
|
|
|
|
```
|
|
https://github.com/<owner>/<repo>/releases/latest/download/apps.json
|
|
```
|
|
|
|
The app then shows up in the Homebrew Channel list, and every new release you
|
|
tag is picked up automatically (that URL always points at the newest release).
|
|
|
|
### Manually
|
|
|
|
Download the `.ipk` from the release and:
|
|
|
|
```sh
|
|
ares-install com.rkievits.lgupdateblocker_1.0.0_all.ipk
|
|
ares-launch com.rkievits.lgupdateblocker
|
|
```
|
|
|
|
## Using it
|
|
|
|
Navigate with ▲▼, select with OK, leave with BACK.
|
|
|
|
1. Check the layers you want (the defaults are a good start).
|
|
2. **Apply protection** — the log pane shows exactly what was changed.
|
|
3. Reboot and confirm the popup is gone.
|
|
|
|
Other buttons: **Purge staged update** (wipe a downloaded image right now),
|
|
**Remove protection** (undo everything), **Refresh status**, and
|
|
**Diagnostics** (dumps OS info, mounts, discovered settings, the tail of
|
|
`/etc/hosts` and the boot-hook log — handy for a bug report).
|
|
|
|
## What it touches
|
|
|
|
| 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` |
|
|
| `/mnt/lg/cmn_data/swupdate` | Staged firmware, emptied (and optionally locked) |
|
|
|
|
Nothing is written to system partitions, and no LG binaries are patched or
|
|
replaced.
|
|
|
|
## Troubleshooting
|
|
|
|
* **Badge says "no root"** — open the Homebrew Channel, verify *Root status* is
|
|
`ok`, then relaunch this app. If the Homebrew Channel itself is unelevated,
|
|
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.
|
|
* **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.
|
|
* **Forced *app* updates** (the *"You must update this app to launch it"*
|
|
dialog) are a different mechanism — that is what
|
|
[LG App Update Blocker](https://github.com/dr0dr1dr2dr3/lgappupdateblocker)
|
|
is for. This app deliberately leaves the `lgtvsdp.com` hosts alone, since
|
|
blocking those also breaks the store and several other services.
|
|
|
|
## Building
|
|
|
|
No bundler, no runtime dependencies — `tools/build.js` copies `app/` and
|
|
`service/` into `build/` and substitutes the app id and version.
|
|
|
|
```sh
|
|
npm install # only the ares CLI
|
|
npm run dist # build + package + manifest, output in dist/
|
|
npm run deploy # ares-install the resulting ipk
|
|
```
|
|
|
|
## Releasing
|
|
|
|
`.github/workflows/release.yml` builds on every push. Tag a commit `v<version>`
|
|
(matching `version` in `package.json`) and the workflow additionally publishes a
|
|
release containing:
|
|
|
|
* `com.rkievits.lgupdateblocker_<version>_all.ipk`
|
|
* `com.rkievits.lgupdateblocker.manifest.json` — Homebrew Channel manifest,
|
|
with the absolute ipk URL, its sha256 and size
|
|
* `apps.json` — the one-package repository index used by the repository URL above
|
|
* `description.html` — the long description shown in the Homebrew Channel
|
|
|
|
The app id lives in exactly one place: the `name` field of `package.json`.
|
|
Change it there and everything (appinfo, service name, Luna URIs, manifest)
|
|
follows.
|
|
|
|
## License
|
|
|
|
MIT
|