From 56d635f051f5171d1e003c40c0922a927c09600d Mon Sep 17 00:00:00 2001 From: Grigo Date: Tue, 10 Nov 2026 10:58:05 +0000 Subject: [PATCH] docs: what OTA changes in the system (docs/ota.md) bt-update runs as root and installs helpers, sudoers, systemd units and config; none of it is rolled back. Documents the order of stage steps, the 'old bt-update runs the OTA' pitfall and how to add a system change. Co-Authored-By: Claude Opus 5.5 --- README.md | 5 +++ docs/ota.md | 63 +++++++++++++++++++++++++++++++ webconfig/scripts/make-package.sh | 4 +- 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 docs/ota.md diff --git a/README.md b/README.md index 1bfda54..e5d4d79 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,11 @@ restarts both services, then runs a health-check: the web `/healthz` must answer switches `current` back to the previous version and restarts. Progress is shown live and is also readable in the app's **Обновление** page. +OTA also changes system files as root (helpers, sudoers, systemd units and +config) and those are **not** rolled back. What it touches, the "old +`bt-update` runs the OTA" pitfall and how to add a new system change: +[docs/ota.md](docs/ota.md). + ## Display / GUI watchdog Targets "backlight on, no picture until power re-plug" (seen on PoE sites: diff --git a/docs/ota.md b/docs/ota.md new file mode 100644 index 0000000..cb120d0 --- /dev/null +++ b/docs/ota.md @@ -0,0 +1,63 @@ +# OTA: что обновление может менять в системе + +Изначально OTA задумывалась как замена только приложения (бинарник, веб, коллектор). На практике `bt-update` выполняется **от root** и уже меняет системные файлы: хелперы, sudoers, юниты systemd, конфиги systemd. Здесь описано, что именно он делает, в каком порядке и как добавить новую системную правку. + +## Как запускается + +1. Веб (пользователь `buttontask`) принимает пакет и вызывает `sudo -n /opt/buttontask/scripts/bt-update stage `. Это разрешено правилом в `/etc/sudoers.d/buttontask`. +2. `bt-update` перезапускает сам себя через `systemd-run` в отдельном временном юните `buttontask-update-`. Поэтому он переживает перезапуск `buttontask-web` посреди обновления. +3. Прогресс пишется в `run/update-status.json`, подробный лог в `run/update.log`. + +## Порядок `stage` + +| Шаг | Что делается | Куда пишет | +|---|---|---| +| 1. распаковка | пакет → `versions/`, проверка `manifest.json` | `/opt/buttontask/versions/` | +| 2. `sync_helpers` | все `webconfig/scripts/bt-*` → root-owned хелперы; sudoers ставится только если прошёл `visudo -c` | `/opt/buttontask/scripts/`, `/etc/sudoers.d/buttontask` | +| 3. коллектор | `buttontask-collector.service`, enable/disable по `collectorEnabled` | `/etc/systemd/system/` | +| 4. bluetooth | drop-in `Restart=always` для `bluetooth.service` | `/etc/systemd/system/bluetooth.service.d/override.conf` | +| 5. `sync_system` | `buttontask.service`, `buttontask-displaywatch.service`, аппаратный вотчдог; `daemon-reload` или `daemon-reexec`, enable/restart displaywatch | `/etc/systemd/system/`, `/etc/systemd/system.conf.d/` | +| 6. переключение | `current` → новая версия, перезапуск сервисов | | +| 7. health-check | `/healthz`, свежий `run/qt.alive`, `buttontask` active (30 с) | | +| 8. откат | при провале `current` → предыдущая версия, перезапуск | | + +Подкоманды для ручного запуска: + +```sh +sudo /opt/buttontask/scripts/bt-update sync-helpers # шаг 2 из текущей версии +sudo /opt/buttontask/scripts/bt-update sync-system # шаг 5 из текущей версии +``` + +## Важные ограничения + +### 1. OTA выполняет **старый** `bt-update` + +Обновление запускает `bt-update`, который уже установлен на устройстве, а не тот, что лежит в пакете. Новый `bt-update` приезжает на шаге 2 и работает только **со следующей** OTA. Значит, системную правку, добавленную в `bt-update`, первая OTA не применит. + +Как это обойти (сделано для вотчдога): веб при каждом старте вызывает `sudo bt-update sync-system` (`sync_system_units()` в `webconfig/system_info.py`). На шаге 6 веб перезапускается уже из новой версии, вызывает новый `bt-update` (его установил шаг 2), и правка применяется в ту же OTA. Поэтому `sync-system` должен быть **идемпотентным**: без изменений он ничего не делает (`install_if_changed` + `cmp`). + +### 2. Системные правки **не откатываются** + +Шаги 2–5 выполняются до health-check, и откат возвращает только симлинк `current`. Юниты, sudoers и конфиги остаются от новой версии. Отсюда правило: **любая системная правка должна работать и со старыми версиями приложения.** Пример: `buttontask.service` не задаёт `WatchdogSec=`, таймаут включает сам бинарник через `WATCHDOG_USEC`. Иначе старые версии без пингов убивались бы каждые 30 секунд. + +### 3. Изменённый юнит не применяется к уже запущенному процессу + +`daemon-reload` обновляет юнит, но переменные окружения (например, `NOTIFY_SOCKET`) процесс получает только при старте. Если юнит поменялся после перезапуска на шаге 6 (это случай из пункта 1), новые настройки заработают со следующего рестарта сервиса или перезагрузки. + +### 4. Чего OTA не умеет + +- **apt/deb-пакеты.** Для них нужен полный deploy-бандл (`winDeployScripts/`, `device-install.sh`). +- **Произвольные одноразовые скрипты-миграции.** Отдельного механизма нет: каждая правка вписывается в `bt-update` руками (см. ниже). +- **Перезагрузку** после установки. Если она нужна, это ручной шаг. + +## Как добавить новую системную правку + +1. Положите файл в пакет, в `webconfig/…` (всё из `webconfig/` попадает в OTA через `make-package.sh`). +2. В `bt-update` в `sync_system` добавьте `install_if_changed ` и нужный `reload`/`reexec`/`enable`. Не добавляйте новый шаг прямо в `cmd_stage`: тогда правка не применится при первой OTA (пункт 1). +3. Проверьте, что правка совместима со старыми версиями приложения (пункт 2). +4. Повторите то же в `winDeployScripts/device-install.sh` для чистой установки. Если добавили новый хелпер `bt-*`, впишите его в списки в `device-install.sh` и `make-adb-bundle.ps1`. +5. Новые типы файлов (`*.conf` и т.п.) пропишите в `.gitattributes` с `eol=lf`. + +## Безопасность + +Пакет OTA фактически исполняется от root: хелперы, sudoers и юниты из него ставятся без подписи. От подмены защищают только пароль веб-конфигуратора и (необязательная) сверка sha256 при загрузке. Доступ к вкладке «Обновление» равен root-доступу к устройству. diff --git a/webconfig/scripts/make-package.sh b/webconfig/scripts/make-package.sh index 06fa539..6885657 100755 --- a/webconfig/scripts/make-package.sh +++ b/webconfig/scripts/make-package.sh @@ -8,7 +8,9 @@ # collector/ (BLE GATT collector, if present in the repo) # manifest.json ({"version": ..., "created": ...}) # and prints the package's sha256 (paste it into the web UI for integrity). -# Note: new systemd units / apt packages still require a full deploy .tgz. +# System files (helpers, sudoers, systemd units/config) are installed by +# bt-update from webconfig/ — see docs/ota.md. apt packages still need a full +# deploy .tgz. # # Usage: # make-package.sh [build-dir] [out-dir]