Files
obsidian-vault/family/how-to/zont-modbus-bridge-udev-race-protection.md

5.0 KiB
Raw Permalink Blame History

status, tags, title, updated
status tags title updated
implemented
family
homeautomation
zont
modbus
Защита modbus-bridge от гонки с udev (ZONT датчики) 2026-08-25

Защита modbus-bridge/mbusd от гонки с udev (ZONT датчики)

Статус: внедрено (2026-08-25, шаги 13). Датчики в ZONT снова отвечают, скрипт и init script на месте.

Проблема

ZONT (192.168.0.10) — Modbus master на RS-485 шине ttyZONT. 485-датчики: Dining=1, Kids=2, Bedroom=3. Контейнер modbus-bridge сниффит их и подставляет виртуальные slaves 101/102/103 → ZONT опрашивает как внешние датчики. Если modbus-bridge не слушает шину → ZONT показывает датчики «недоступными».

Первопричина (2026-08-25): гонка загрузки TrueNAS — docker стартовал раньше udev, /dev/ttyZONT и /dev/ttyVent ещё не существовали → контейнеры modbus-bridge (Exit 128) и mbusd (Exit 255) упали на старте: error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory (RestartCount=0). Фикс был: ручной docker start modbus-bridge mbusd.

Почему контейнеры сами НЕ поднялись (RestartCount=0)

restart: unless-stopped/always ретраит в ДВУХ случаях:

  1. Процесс контейнера стартовал и завершился с ненулевым кодом → демон ретраит с backoff.
  2. Перезапуск docker daemon → демон пробует поднять unless-stopped/always контейнеры.

Наш случай — ТРЕТИЙ, особый: контейнер вообще не стартовал — ошибка на этапе монтирования устройства.

  • Нет процесса, который «завершился бы» → restart policy не на что применятьRestartCount=0, демон не ретраит.
  • После падения (13:24) демон не перезапускался → второго триггера не было. Вывод: отказ на этапе mount устройства НЕ перезапускается restart policy. Авто-подъём гарантирован только скриптом, ждущим устройства.

Ключевое ограничение (задержка в entrypoint НЕ помогает)

У обоих контейнеров устройство проброшено через devices: /dev/ttyZONT:/dev/ttyUSB0 / devices: /dev/ttyVent:/dev/ttyUSB0. Docker при start монтирует устройство ДО запуска процесса — если <src> отсутствует на момент старта, контейнер вообще не стартует, и sleep внутри entrypoint/CMD не выполняется. Поэтому решение — на уровне init скрипта.

Внедрённое решение

1. Скрипт /mnt/RED_2TB/system/start-modbus.sh (root, -rw-r--r--)

Ожидает появления tty-алиасов от udev (до ~50с), затем запускает контейнеры:

#!/bin/bash
# Ждём tty-алиасы от udev (макс ~25с), затем стартуем modbus контейнеры.
for i in $(seq 1 50); do
  [ -e /dev/ttyZONT ] && [ -e /dev/ttyVent ] && break
  sleep 1
done
sleep 2
docker start modbus-bridge mbusd 2>/dev/null

2. Init script (TrueNAS, id=3)

  • when: POSTINIT, type: COMMAND, timeout: 30, enabled: true
  • command: bash /mnt/RED_2TB/system/start-modbus.sh
  • comment: Start modbus-bridge/mbusd after udev tty aliases

Порядок POSTINIT скриптов

id command comment enabled
1 cp .../99-tty-alias.rules /etc/udev/rules.d/ && udevadm control --reload-rules && udevadm trigger Map ttyUSB
2 bash /mnt/RED_2TB/system/tunnel.sh &
3 bash /mnt/RED_2TB/system/start-modbus.sh Start modbus-bridge/mbusd after udev tty aliases

Проверка

  • /dev/ttyZONTttyUSB1 (CH340), /dev/ttyVentttyUSB0 — симлинки появляются после POSTINIT id=1.
  • После reboot: docker ps — modbus-bridge и mbusd Up; датчики в ZONT отвечают.
  • Просмотр init scripts: midclt call initshutdownscript.query
  • Откат: midclt call initshutdownscript.delete <ID> + удалить скрипт с диска.

Связанные заметки