# BSP 통합 가이드 — Web Configurator v1.12.1 디바이스 이미지(Yocto/OE 등)에 포함하기 위한 **커밋 히스토리 없는 클린 스냅샷**입니다. 런타임 앱·systemd 유닛·샘플·문서와 optional nginx sample 설정만 들어 있고, 개발/내부 자료와 VCS 이력은 없습니다. --- ## 1. 런타임 구조 - **Python 3 애플리케이션, stdlib 전용** — pip·virtualenv·외부 패키지 없음. - 실행 진입점: `python3 /usr/lib/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/` | `${libdir}/web-configurator/src/` (`${libdir}` = `/usr/lib`) | | `deploy/web-configurator.service` | `${systemd_system_unitdir}/` | | `deploy/dpworld-*.service` | `${systemd_system_unitdir}/` (제품이 사용하는 유닛만) | | `deploy/*.service.d/` | `${systemd_system_unitdir}/.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/`에 설치 > 앱 코드 디렉토리 `/usr/lib/web-configurator` 는 `do_install` 이 이미지에 굽는 **read-only program code** 이므로 아래 쓰기 디렉토리 목록에 포함하지 않습니다. 쓰기 가능 런타임 디렉토리 생성 (recipe `do_install` / tmpfiles.d / 유닛의 `ExecStartPre` 중 택1): ``` /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.12.1.bb` ```bitbake SUMMARY = "DP World Smart Solutions — Web Configurator" LICENSE = "CLOSED" # 방법 A — 내부 git 서버의 태그 릴리스를 fetch (배포물이 repo 루트에 평탄 배치됨): SRC_URI = "git:////NewWebConfigurator.git;branch=;protocol=ssh" SRCREV = "" S = "${WORKDIR}/git" # 방법 B — git archive 로 만든 스냅샷 tarball 사용: # (git -C archive v1.12.1 | gzip > web-configurator-v1.12.1.tar.gz) # SRC_URI = "file://web-configurator-v1.12.1.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() { # 애플리케이션 (read-only program code → ${libdir} = /usr/lib) install -d ${D}${libdir}/web-configurator cp -r ${S}/src ${D}${libdir}/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} += "${libdir}/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)을 제외하면 모두 본 패키지에 포함됩니다.