From 8636bb70c3ff37279af4926d0decec3d3a31918c Mon Sep 17 00:00:00 2001 From: bitbyte08 Date: Thu, 4 Jun 2026 09:33:38 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20OTA=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EC=8B=9C=20ETXTBSY=20(Text=20file=20busy)=20?= =?UTF-8?q?=ED=9A=8C=ED=94=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 다운로드 임시 경로를 /tmp(별도 fs)에서 INSTALL_DIR(..new)로 이동해 같은 파일시스템 내 mv가 rename(2)로 동작하도록 변경. rename(2)은 실행 중 ELF에 대해서도 atomic 치환이 보장되므로 Restart=always 서비스에서 stop과 systemd 재시작 사이의 레이스로 'cp: Text file busy' 가 나던 문제 해결. - TMP_BINARY: /tmp/... → ${INSTALL_DIR}/.${BINARY_NAME}.new - cp + mv .old 패턴 → mv -f (atomic rename) 한 번으로 교체 - 서비스명 상수화 (SERVICE_NAME) - mv 실패 시 명시적 에러 분기 추가 scripts/update.sh(cluster), scripts/entertainment-update.sh 동일 패치. --- scripts/entertainment-update.sh | 23 +++++++++++++++-------- scripts/update.sh | 26 +++++++++++++++++--------- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/scripts/entertainment-update.sh b/scripts/entertainment-update.sh index 5f6db24..a06d06d 100755 --- a/scripts/entertainment-update.sh +++ b/scripts/entertainment-update.sh @@ -6,11 +6,13 @@ set -euo pipefail INSTALL_DIR="/opt/entertainment" BINARY_NAME="entertainment" +SERVICE_NAME="entertainment-kiosk.service" ASSET_NAME="entertainment-arm64" GITHUB_REPO="BitByte08/Cockpit.EntertainmentClusterUnit" DOWNLOAD_BASE="https://github.com/${GITHUB_REPO}/releases/latest/download" VERSION_FILE="${INSTALL_DIR}/VERSION" -TMP_BINARY="/tmp/entertainment-update" +# 같은 파일시스템에 받아야 rename(2)로 atomic 교체 가능 (ETXTBSY 회피) +TMP_BINARY="${INSTALL_DIR}/.${BINARY_NAME}.new" TMP_VERSION="/tmp/entertainment-latest-version" LOG_TAG="entertainment-update" @@ -54,6 +56,8 @@ fi log "업데이트 발견: v${CURRENT_VERSION} → v${LATEST_VERSION}" log "다운로드 중: ${DOWNLOAD_BASE}/${ASSET_NAME}" +mkdir -p "$INSTALL_DIR" +rm -f "$TMP_BINARY" if ! curl -fL --max-time 120 --progress-bar -H "User-Agent: entertainment-update" \ "${DOWNLOAD_BASE}/${ASSET_NAME}" -o "$TMP_BINARY"; then log "다운로드 실패"; rm -f "$TMP_BINARY"; exit 0 @@ -64,17 +68,20 @@ if [[ "$MAGIC" != "7f454c46" ]]; then log "유효한 ELF 바이너리가 아님"; rm -f "$TMP_BINARY"; exit 0 fi -systemctl stop entertainment-kiosk.service 2>/dev/null || true +chmod 755 "$TMP_BINARY" +chown root:root "$TMP_BINARY" 2>/dev/null || true + +systemctl stop "$SERVICE_NAME" 2>/dev/null || true + +# 같은 파일시스템(${INSTALL_DIR}) 내 mv → rename(2) → 실행 중 바이너리도 atomic 교체 +if ! mv -f "$TMP_BINARY" "${INSTALL_DIR}/${BINARY_NAME}"; then + log "교체 실패"; rm -f "$TMP_BINARY"; exit 1 +fi -chmod +x "$TMP_BINARY" -mv "${INSTALL_DIR}/${BINARY_NAME}" "${INSTALL_DIR}/${BINARY_NAME}.old" 2>/dev/null || true -cp "$TMP_BINARY" "${INSTALL_DIR}/${BINARY_NAME}" -rm -f "${INSTALL_DIR}/${BINARY_NAME}.old" -rm -f "$TMP_BINARY" echo "$LATEST_VERSION" > "$VERSION_FILE" if ! systemctl is-system-running 2>/dev/null | grep -q 'booting'; then - systemctl start entertainment-kiosk.service 2>/dev/null || true + systemctl start "$SERVICE_NAME" 2>/dev/null || true fi log "업데이트 완료: v${LATEST_VERSION}" diff --git a/scripts/update.sh b/scripts/update.sh index 9084315..d48901f 100644 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -6,11 +6,13 @@ set -euo pipefail INSTALL_DIR="/opt/cluster" BINARY_NAME="cluster" +SERVICE_NAME="cluster-kiosk.service" ASSET_NAME="cluster-arm64" GITHUB_REPO="BitByte08/Cockpit.EntertainmentClusterUnit" DOWNLOAD_BASE="https://github.com/${GITHUB_REPO}/releases/latest/download" VERSION_FILE="${INSTALL_DIR}/VERSION" -TMP_BINARY="/tmp/cluster-update" +# 같은 파일시스템에 받아야 rename(2)로 atomic 교체 가능 (ETXTBSY 회피) +TMP_BINARY="${INSTALL_DIR}/.${BINARY_NAME}.new" TMP_VERSION="/tmp/cluster-latest-version" LOG_TAG="cluster-update" @@ -61,6 +63,8 @@ log "업데이트 발견: v${CURRENT_VERSION} → v${LATEST_VERSION}" # ── 바이너리 다운로드 (API 없이) ─────────────────────────────────────────────── log "다운로드 중: ${DOWNLOAD_BASE}/${ASSET_NAME}" +mkdir -p "$INSTALL_DIR" +rm -f "$TMP_BINARY" if ! curl -fL --max-time 120 --progress-bar -H "User-Agent: cluster-update" \ "${DOWNLOAD_BASE}/${ASSET_NAME}" -o "$TMP_BINARY"; then log "다운로드 실패" @@ -76,18 +80,22 @@ if [[ "$MAGIC" != "7f454c46" ]]; then exit 0 fi -# ── 교체 (mv로 우회, 실행 중 덮어쓰기 회피) ────────────────────────────────── -systemctl stop cluster-kiosk.service 2>/dev/null || true +chmod 755 "$TMP_BINARY" +chown root:root "$TMP_BINARY" 2>/dev/null || true + +systemctl stop "$SERVICE_NAME" 2>/dev/null || true + +# 같은 파일시스템(${INSTALL_DIR}) 내 mv → rename(2) → 실행 중 바이너리도 atomic 교체 +if ! mv -f "$TMP_BINARY" "${INSTALL_DIR}/${BINARY_NAME}"; then + log "교체 실패" + rm -f "$TMP_BINARY" + exit 1 +fi -chmod +x "$TMP_BINARY" -mv "${INSTALL_DIR}/${BINARY_NAME}" "${INSTALL_DIR}/${BINARY_NAME}.old" 2>/dev/null || true -cp "$TMP_BINARY" "${INSTALL_DIR}/${BINARY_NAME}" -rm -f "${INSTALL_DIR}/${BINARY_NAME}.old" -rm -f "$TMP_BINARY" echo "$LATEST_VERSION" > "$VERSION_FILE" if ! systemctl is-system-running 2>/dev/null | grep -q 'booting'; then - systemctl start cluster-kiosk.service 2>/dev/null || true + systemctl start "$SERVICE_NAME" 2>/dev/null || true fi log "업데이트 완료: v${LATEST_VERSION}"