You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

203 lines
12 KiB

# BSP 통합 가이드 — Web Configurator v1.11.16
디바이스 이미지(Yocto/OE 등)에 포함하기 위한 **커밋 히스토리 없는 클린 스냅샷**입니다.
런타임 앱·systemd 유닛·샘플·문서와 optional nginx sample 설정만 들어 있고, 개발/내부 자료와 VCS 이력은 없습니다.
---
## 1. 런타임 구조
- **Python 3 애플리케이션, stdlib 전용** — pip·virtualenv·외부 패키지 없음.
- 실행 진입점: `python3 /opt/web-configurator/src/server.py`
- 리슨 포트: **9090** (유닛의 `Environment=PORT=` 로 변경 가능)
- 앱은 기본적으로 `:9090`에서 직접 서비스됩니다. nginx `:80` reverse proxy는 optional이며, `deploy/nginx.conf`는 BSP가 필요할 때 쓰는 sample 설정입니다.
- 영속 상태 — **패키지에 없음**(런타임/다른 컴포넌트가 생성):
- DB: `/home/root/db/dynamic_data.db` (`DB_PATH`)
- 로그: `/opt/log/dpworldapp` (`LOG_DIR`)
> **nginx optional**: `scripts/deploy.ps1`는 nginx 설정을 설치하거나 수정하지 않습니다. BSP에서 `deploy/nginx.conf`를 사용할 때만 이미지 레이아웃에 맞게 설치하고, Python 앱 단독 프록시라면 `proxy_pass`를 `http://127.0.0.1:9090/` 로 맞추세요.
---
## 2. 설치 경로 (`do_install`)
| 소스 (이 패키지) | 디바이스 설치 위치 |
|---|---|
| `src/` | `/opt/web-configurator/src/` |
| `deploy/web-configurator.service` | `${systemd_system_unitdir}/` |
| `deploy/dpworld-*.service` | `${systemd_system_unitdir}/` (제품이 사용하는 유닛만) |
| `deploy/*.service.d/` | `${systemd_system_unitdir}/<unit>.service.d/` |
| `deploy/dpworld-*.sh` | 각 유닛의 `ExecStart=` 가 가리키는 경로 |
| `deploy/dpworld-network-apply-ondemand.conf` | 해당 유닛이 참조하는 경로 |
| `deploy/nginx.conf` | Optional nginx sample site config. BSP/firmware가 nginx를 별도로 소유하면 생략 가능 |
권장 전체 기능 설치 목록:
- 항상 설치: `src/`, `deploy/web-configurator.service`
- 네트워크 apply 사용 시 (**권장 = 직접 교체**): `deploy/dpworld-network-apply-hardened.sh` 의 내용을 **`/usr/bin/dpworld-network-apply.sh` 원본 이름으로 설치**(펌웨어 base 스크립트 교체)하고 `deploy/dpworld-network-apply-ondemand.conf` 를 함께 설치. firmware-native base 유닛(`dpworld-network-apply.service`/`dpworld-network-seed.service`)이 그 경로를 호출하므로 자동 적용됨. **이 방식에서는 `.service.d` 드롭인을 설치하지 않습니다** (드롭인은 라이브 후적용 fallback 전용 — §7, `docs/firmware-boot-hardening.md`)
- Wi-Fi AP 사용 시: `deploy/dpworld-ap-apply.service`, `deploy/dpworld-ap-seed.service`, `deploy/dpworld-hostapd-ap0.service`, `deploy/dpworld-udhcpd-ap0.service`, `deploy/dpworld-ap-apply.sh`를 함께 설치
- 복구 유닛 사용 시: `deploy/dpworld-net-recover.service` 설치
- `*.sh``0755` 실행 권한으로 `/usr/bin/`에 설치
쓰기 가능 런타임 디렉토리 생성 (recipe `do_install` / tmpfiles.d / 유닛의 `ExecStartPre` 중 택1):
```
/opt/web-configurator /opt/log/dpworldapp /opt/fw_staging /opt/fw_upload
/opt/config_backups /home/root/db /home/root/network
```
---
## 3. 런타임 의존성 (`RDEPENDS`)
**stdlib 전용** — 앱이 import하는 python3 표준 모듈만 이미지에 포함하면 됩니다. (외부 패키지 없음)
OE의 모듈형 python3 기준 예시:
```
python3-core python3-sqlite3 python3-json python3-netserver python3-netclient
python3-compression python3-crypt python3-logging python3-datetime python3-threading
python3-shell python3-io python3-mime python3-stringold python3-ctypes
```
실제 import: `sqlite3, http(.server), socketserver, socket, urllib, json, gzip, tarfile,
zipfile, hashlib, subprocess, shlex, logging, datetime, ipaddress, mimetypes, struct,
tempfile, shutil, re, io, os, sys, time, copy, traceback` — 사용하는 OE python3 분할 패키지에 맞춰 확인하세요.
추가로 유닛이 런타임에 쓰는 시스템 패키지: nginx reverse proxy를 이미지에서 제공할 때만 `nginx`.
네트워크/AP 유닛 사용 시 `wpa-supplicant`, `hostapd`, `udhcpd`/`busybox`, `iproute2` 등.
---
## 4. systemd
```
SYSTEMD_SERVICE:${PN} = "web-configurator.service"
SYSTEMD_AUTO_ENABLE = "enable"
```
제품에서 필요한 `dpworld-*` 유닛(network-apply, Wi-Fi AP, recovery)을 추가하세요. 전체 기능 이미지에서는 보통 다음처럼 함께 등록합니다:
```
SYSTEMD_SERVICE:${PN} = "web-configurator.service \
dpworld-ap-seed.service \
dpworld-hostapd-ap0.service \
dpworld-udhcpd-ap0.service"
```
`dpworld-ap-apply.service``dpworld-net-recover.service`는 웹 설정기/watchdog이 on-demand로 `systemctl start` 하므로 `[Install]` 없이 설치만 하면 됩니다. `dpworld-network-apply.service`/`dpworld-network-seed.service`는 firmware-native base 유닛이며 `/usr/bin/dpworld-network-apply.sh` 를 호출합니다 — 본 패키지는 **그 base 스크립트를 하드닝본으로 교체**합니다(권장, §7). (펌웨어 원본을 교체할 수 없는 라이브 디바이스에서는 `.service.d` override 드롭인이 fallback.)
---
## 5. 샘플 레시피 — `web-configurator_1.11.16.bb`
```bitbake
SUMMARY = "DP World Smart Solutions — Web Configurator"
LICENSE = "CLOSED"
# 방법 A — 내부 git 서버의 태그 릴리스를 fetch (배포물이 repo 루트에 평탄 배치됨):
SRC_URI = "git://<internal-host>/<path>/NewWebConfigurator.git;branch=<branch>;protocol=ssh"
SRCREV = "<v1.11.16 sha 또는 tag>"
S = "${WORKDIR}/git"
# 방법 B — git archive 로 만든 스냅샷 tarball 사용:
# (git -C <repo> archive v1.11.16 | gzip > web-configurator-v1.11.16.tar.gz)
# SRC_URI = "file://web-configurator-v1.11.16.tar.gz"
# S = "${WORKDIR}"
inherit systemd
RDEPENDS:${PN} += "python3-core python3-sqlite3 python3-json python3-netserver \
python3-netclient python3-compression python3-crypt python3-logging \
python3-datetime python3-threading python3-shell python3-io \
python3-mime"
# Optional: add nginx only if this recipe installs/enables the nginx reverse proxy.
# RDEPENDS:${PN} += "nginx"
SYSTEMD_SERVICE:${PN} = "web-configurator.service"
SYSTEMD_AUTO_ENABLE = "enable"
do_install() {
# 애플리케이션
install -d ${D}/opt/web-configurator
cp -r ${S}/src ${D}/opt/web-configurator/
# systemd 유닛
install -d ${D}${systemd_system_unitdir}
install -m 0644 ${S}/deploy/web-configurator.service ${D}${systemd_system_unitdir}/
install -m 0644 ${S}/deploy/dpworld-ap-apply.service ${D}${systemd_system_unitdir}/
install -m 0644 ${S}/deploy/dpworld-ap-seed.service ${D}${systemd_system_unitdir}/
install -m 0644 ${S}/deploy/dpworld-hostapd-ap0.service ${D}${systemd_system_unitdir}/
install -m 0644 ${S}/deploy/dpworld-udhcpd-ap0.service ${D}${systemd_system_unitdir}/
install -m 0644 ${S}/deploy/dpworld-net-recover.service ${D}${systemd_system_unitdir}/
# 유닛 ExecStart 대상 스크립트
install -d ${D}${bindir}
install -m 0755 ${S}/deploy/dpworld-ap-apply.sh ${D}${bindir}/dpworld-ap-apply.sh
# 네트워크-적용 하드닝 (권장 = 직접 교체):
# 펌웨어 base 스크립트 /usr/bin/dpworld-network-apply.sh 를 하드닝본으로 교체한다.
# firmware-native 한 dpworld-network-apply.service / dpworld-network-seed.service 가
# 이 경로를 호출하므로 apply·--boot 모두 자동으로 하드닝 동작을 탄다. 드롭인은 설치하지 않는다.
# (파일 소유권은 BSP에서 조정 — firmware recipe의 base 파일을 이 패키지가 덮어쓰도록
# bbappend 또는 recipe 우선순위로 정리. 자세히 docs/firmware-boot-hardening.md)
install -m 0755 ${S}/deploy/dpworld-network-apply-hardened.sh \
${D}${bindir}/dpworld-network-apply.sh
install -m 0644 ${S}/deploy/dpworld-network-apply-ondemand.conf \
${D}${systemd_system_unitdir}/dpworld-network-apply-ondemand.conf
# Fallback (라이브 후적용 전용 — 베이킹 시 설치하지 않음):
# 펌웨어 원본을 교체할 수 없는 경우에만, 하드닝본을 원래 -hardened 이름으로 설치하고
# deploy/dpworld-network-{apply,seed}.service.d/20-hardened.conf 드롭인 2개로
# base 유닛의 ExecStart 를 override 한다.
# Optional nginx site config.
# scripts/deploy.ps1 does not use this file. Skip this block if firmware/BSP
# owns nginx, or if the device will access the app directly on :9090.
if [ -f ${S}/deploy/nginx.conf ]; then
install -d ${D}${sysconfdir}/nginx
install -m 0644 ${S}/deploy/nginx.conf ${D}${sysconfdir}/nginx/web-configurator.conf
fi
# 쓰기 가능 런타임 디렉토리
install -d ${D}/opt/log/dpworldapp ${D}/opt/fw_staging ${D}/opt/fw_upload \
${D}/opt/config_backups ${D}/home/root/db ${D}/home/root/network
}
FILES:${PN} += "/opt/web-configurator /opt/log ${systemd_system_unitdir} \
/opt/fw_staging /opt/fw_upload /opt/config_backups \
/home/root/db /home/root/network ${bindir}/dpworld-*.sh \
${sysconfdir}/nginx"
```
---
## 6. 부팅 후 동작 확인 (smoke test)
```sh
systemctl status web-configurator # active (running)
curl -fsS http://127.0.0.1:9090/ | head # 앱 응답
curl -fsS http://127.0.0.1/ | head # optional: nginx 경유 사용 시
```
---
## 7. firmware-native 전제 + 네트워크-적용 하드닝 통합
### firmware가 제공하는 것 (이 패키지에 **미포함**)
- **base 네트워크-적용 유닛** — `dpworld-network-apply.service`, `dpworld-network-seed.service`. 이 유닛들은 `/usr/bin/dpworld-network-apply.sh` 를 호출합니다(no-arg / `--boot`).
- **dpworldapp**(텔레메트리 바이너리), (전환기의 레거시 `app-runner`) — firmware-native.
- **nginx** — 제품 이미지가 `:80` reverse proxy를 제공할 때만. 앱을 `:9090`으로 직접 접근하면 불필요.
### 네트워크-적용 하드닝본 통합 — ① 직접 교체(권장) vs ② override(fallback)
펌웨어 base `/usr/bin/dpworld-network-apply.sh` 에는 WiFi country 변경 시 `modprobe -r wlan`
라이브 재로드 경로가 있어 QCA6490에서 PMU 워치독 재부팅 루프를 유발한 이력이 있습니다.
`deploy/dpworld-network-apply-hardened.sh` 가 이를 제거한 **완전 대체본**입니다.
| | ① 직접 교체 (BSP 베이킹, **권장**) | ② override (라이브 후적용, fallback) |
|---|---|---|
| 대상 | 펌웨어 이미지를 빌드하는 협력사 | 이미 구워진 디바이스(우리 `deploy.ps1`) |
| 방법 | 하드닝본 내용을 **`/usr/bin/dpworld-network-apply.sh` 원본 이름**으로 설치(원본 교체) | 하드닝본을 `…-hardened.sh` 로 두고 `.service.d/20-hardened.conf` 드롭인 2개로 ExecStart 교체 |
| 드롭인 | **설치 안 함** | 설치함 |
| 효과 | base 유닛이 그대로 하드닝 스크립트를 실행 | base 유닛의 ExecStart가 하드닝본으로 바뀜 |
절차 상세: `docs/firmware-boot-hardening.md` "펌웨어 베이킹 (BSP) — 원본 교체 방법".
### 이 패키지가 소유·제공하는 스크립트 (모두 포함됨)
| 스크립트 | 설치 위치(권장 ①) | 호출 주체 |
|---|---|---|
| `deploy/dpworld-ap-apply.sh` | `/usr/bin/dpworld-ap-apply.sh` | `dpworld-ap-apply.service`, `dpworld-ap-seed.service` |
| `deploy/dpworld-network-apply-hardened.sh` | `/usr/bin/dpworld-network-apply.sh` (원본 교체) | firmware-native `dpworld-network-apply.service` / `dpworld-network-seed.service` |
> fallback(②) 사용 시에는 `…-hardened.sh` 이름으로 설치하고 `.service.d` 드롭인 2개(`deploy/dpworld-network-apply.service.d/`, `deploy/dpworld-network-seed.service.d/`)를 함께 둡니다.
### 웹 설정기 앱이 런타임에 호출하는 유닛
`server.py` 는 다음을 `systemctl start` 로 호출합니다:
`dpworld-network-apply.service`(firmware base 유닛 — 교체된 하드닝 스크립트 실행), `dpworld-ap-apply.service`, `dpworld-hostapd-ap0.service`, `dpworld-udhcpd-ap0.service`, `dpworld-net-recover.service`.
→ base 네트워크-적용 **유닛**(firmware-native)을 제외하면 모두 본 패키지에 포함됩니다.