sihwang-webapp 좀비 방지 wrapper 수정 + 재검증
sihwang-webapp wrapper 수정 + 재검증 결과
판정: PASS (이전 PARTIAL → PASS 전환)
이전 검증에서 PARTIAL 판정한 wrapper 스크립트를 직접 수정하고 end-to-end 재현 테스트 완료. /tmp/sihwang_restart_validation.log에 모든 단계 성공 기록.
수정 내용 (diff)
파일: ~/.openclaw/workspace/scripts/maintenance/sihwang_webapp_restart.sh
변경 전 (단일 시도, 실패 시 로그만):
log "launchctl unload 시작: ${PLIST}"
launchctl unload "${PLIST}" || log "unload 경고: ..."
...
log "launchctl load 시작: ${PLIST}"
launchctl load "${PLIST}"
변경 후 (unload → bootout 폴백, load → bootstrap 폴백 함수 분리):
unload_service() {
log "launchctl unload 시도: ${PLIST}"
if launchctl unload "${PLIST}" 2>/dev/null; then
log "launchctl unload 성공"
return 0
fi
log "launchctl unload 실패 — launchctl bootout 폴백 시도"
local gui_target="gui/$(id -u)/${LABEL}"
if launchctl bootout "${gui_target}" 2>/dev/null; then
log "launchctl bootout 성공: ${gui_target}"
return 0
fi
log "unload 경고: unload/bootout 둘 다 상태 반환 없음 (이미 내려갔을 수 있음)"
return 0
}
load_service() {
log "launchctl load 시도: ${PLIST}"
if launchctl load "${PLIST}" 2>/dev/null; then
log "launchctl load 성공"
return 0
fi
log "launchctl load 실패 — launchctl bootstrap 폴백 시도"
local gui_domain="gui/$(id -u)"
if launchctl bootstrap "${gui_domain}" "${PLIST}" 2>/dev/null; then
log "launchctl bootstrap 성공: ${gui_domain}"
return 0
fi
log "load/bootstrap 양쪽 실패"
return 1
}
main() {
...
unload_service
sleep "${SLEEP_AFTER_UNLOAD}"
kill_port_listeners
load_service
verify_health
}
핵심 개선:
1. launchctl unload 실패(I/O error 5 등) 시 launchctl bootout gui/<uid>/<label> 폴백
2. launchctl load 실패 시 launchctl bootstrap gui/<uid> <plist> 폴백 (대칭 보강)
3. 함수 분리로 로직 가시성 향상
4. set -euo pipefail, while read PID 수집, kill -0 체크 등 기존 안전장치 유지
5. mapfile 미사용 재확인 (bash 3.2 호환)
재검증 실행 결과
실행 시점
2026-04-10 18:49:38
/tmp/sihwang_restart_validation.log 전체
=== BEFORE 상태 ===
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
Python 51021 ron 4u IPv4 0x294c5c698283f0a8 0t0 TCP *:8080 (LISTEN)
"LastExitStatus" = 15;
"PID" = 51021;
=== wrapper 실행 ===
[2026-04-10 18:49:38] launchctl unload 시도: /Users/ron/Library/LaunchAgents/com.openclaw.sihwang-webapp.plist
[2026-04-10 18:49:38] launchctl unload 성공
[2026-04-10 18:49:39] 포트 8080 점유 PID 없음
[2026-04-10 18:49:39] launchctl load 시도: /Users/ron/Library/LaunchAgents/com.openclaw.sihwang-webapp.plist
[2026-04-10 18:49:39] launchctl load 성공
[2026-04-10 18:49:41] 기동 확인 성공: http://localhost:8080/
exit=0
=== AFTER 상태 ===
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
Python 24947 ron 4u IPv4 0x21307c10a63111ca 0t0 TCP *:8080 (LISTEN)
"LastExitStatus" = 0;
"PID" = 24947;
curl HTTP 200
검증 요약
| 단계 | 결과 |
|---|---|
| BEFORE PID | 51021 (LastExitStatus=15) |
| unload 시도 | ✓ 성공 (이번엔 I/O error 없음 — bootout 폴백 불필요) |
| 포트 점유 정리 | ✓ unload 후 자연 정리, kill 불필요 |
| load 시도 | ✓ 성공 |
| health check | ✓ 18:49:41 성공 (3초 이내) |
| AFTER PID | 24947 (교체 확인) |
| LastExitStatus | 0 (이전 15 → 정상 종료로 전환) |
| curl HTTP | 200 |
| 스크립트 exit | 0 |
테스트 시 확인한 부수 사항
curl: (7) Failed to connect에러 2회 — wrapper 내부verify_health의 초기 재시도 실패(정상). 3회차부터 성공.bash -n문법 체크: OK- 이전에 있던
Unload failed: 5: Input/output error재현 안 됨 — 이번엔unload첫 시도에서 성공. bootout 폴백은 대기 중으로서 유효 (향후 I/O error 재발 시 동작 예정).
폴백 경로 검증
bootout 분기가 실제로 동작하는지 확인하려면 launchctl이 I/O error 상태여야 하는데 재현 어려움. 대안:
- bash -n 문법 체크 ✓
- 논리적 플로우 리뷰 ✓ (unload 실패 → bootout → 그래도 실패 → 경고 로그 후 continue)
- 함수 분리로 각 경로 독립 테스트 가능
기존 #60/#69 작업 복귀
이 작업은 검증 연장선. 완료 후 메인 세션의 원래 작업으로 복귀:
- #60: 이미 완료 (dry-run 검증 완료, 해리 승인 대기)
- #69: /graphify 404 — Next.js 프로세스 없음 확인. 별도 라우팅 복구 설계 필요 (Flask send_from_directory 추가 or standalone 서버 기동 + LaunchAgent 등록)
결론
- 이전 PARTIAL → 현재 PASS
- 스크립트 문법 ✓, 로직 안전성 ✓, end-to-end 재현 증거 ✓
/tmp/sihwang_restart_validation.log에 모든 단계 성공 기록 완료- 프로덕션 배포 가능 상태