mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-11-14 04:07:56 +08:00
142 lines
5.5 KiB
Bash
142 lines
5.5 KiB
Bash
#!/bin/sh
|
|
|
|
PATH=/usr/sbin:/usr/bin:/sbin:/bin
|
|
export PATH
|
|
umask 077
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
JOBS=/tmp/honk-jobs
|
|
MAINT=/usr/share/honk/maintenance.sh
|
|
|
|
write_status() {
|
|
status_state=$1
|
|
status_phase=$2
|
|
status_ok=$3
|
|
status_message=$4
|
|
status_detail=$5
|
|
# jshn 的布尔只认 1/0,传 "true" 会被写进状态文件成 false。
|
|
case "$status_ok" in true|1) status_ok=1 ;; *) status_ok=0 ;; esac
|
|
status_tmp=$job_dir/status.new
|
|
{
|
|
json_init
|
|
json_add_string state "$status_state"
|
|
json_add_string phase "$status_phase"
|
|
json_add_boolean ok "$status_ok"
|
|
json_add_string message "$status_message"
|
|
# 失败原因进面板,不然只有一句错误码。
|
|
[ -n "$status_detail" ] && json_add_string detail "$status_detail"
|
|
json_dump
|
|
} > "$status_tmp" || return 1
|
|
chmod 0600 "$status_tmp" || return 1
|
|
mv -f "$status_tmp" "$job_dir/status" || return 1
|
|
}
|
|
|
|
job_id=$1
|
|
operation=$2
|
|
case "$job_id" in
|
|
*[!0-9a-f]*|'') exit 2 ;;
|
|
esac
|
|
[ "${#job_id}" -eq 32 ] || exit 2
|
|
case "$operation" in backup|restore|reset|update_check|update_apply) ;; *) exit 2 ;; esac
|
|
|
|
job_dir=$JOBS/$job_id
|
|
# 早退也要留下状态,否则面板只能看到 worker 掉了,问不出是哪一项不过。
|
|
[ -d "$JOBS" ] && [ ! -L "$JOBS" ] && [ -d "$job_dir" ] && [ ! -L "$job_dir" ] || exit 2
|
|
[ "$(stat -c '%u:%a' "$JOBS" 2>/dev/null)" = 0:700 ] && [ "$(stat -c '%u:%a' "$job_dir" 2>/dev/null)" = 0:700 ] || exit 2
|
|
if [ "$(id -u)" != 0 ]; then
|
|
write_status failed complete false job_runner_not_root "uid=$(id -u)"
|
|
logger -t honk-maintenance "job runner is not root (uid=$(id -u))" 2>/dev/null || :
|
|
exit 2
|
|
fi
|
|
write_status running preparing true preparing || exit 1
|
|
: > "$job_dir/output" || exit 1
|
|
chmod 0600 "$job_dir/output" || exit 1
|
|
# stderr 单独存:混进 output 会让结果 JSON 解析失败,报成“操作失败”。
|
|
: > "$job_dir/stderr" || exit 1
|
|
chmod 0600 "$job_dir/stderr" || exit 1
|
|
if [ "$operation" = update_apply ]; then
|
|
/usr/share/honk/update.sh apply > "$job_dir/output" 2> "$job_dir/stderr" &
|
|
elif [ "$operation" = update_check ]; then
|
|
/usr/share/honk/update.sh check > "$job_dir/output" 2> "$job_dir/stderr" &
|
|
else
|
|
"$MAINT" "$operation" > "$job_dir/output" 2> "$job_dir/stderr" &
|
|
fi
|
|
maint_pid=$!
|
|
logger -t honk-maintenance "$operation started" 2>/dev/null || :
|
|
# Total watchdog: never poll forever if maintenance.sh hangs.
|
|
_poll_deadline=$(($(date +%s) + 1800))
|
|
while kill -0 "$maint_pid" 2>/dev/null; do
|
|
if [ "$(date +%s)" -ge "$_poll_deadline" ]; then
|
|
logger -t honk-maintenance "$operation timed out after 1800s, killing $maint_pid" 2>/dev/null || :
|
|
kill -9 "$maint_pid" 2>/dev/null || :
|
|
break
|
|
fi
|
|
journal_phase=$(sed -n 's/^phase=//p' /etc/.honk-maintenance/journal 2>/dev/null | sed -n '1p')
|
|
if [ "$operation" = update_apply ]; then
|
|
update_phase=$(cat /etc/.honk-update/phase 2>/dev/null)
|
|
case "$update_phase" in
|
|
downloading) status_phase=downloading ;;
|
|
verifying) status_phase=validating ;;
|
|
stopping) status_phase=stopping ;;
|
|
installing) status_phase=applying ;;
|
|
starting) status_phase=starting ;;
|
|
*) status_phase=preparing ;;
|
|
esac
|
|
elif [ "$operation" = update_check ]; then
|
|
status_phase=checking_feed
|
|
else case "$journal_phase" in
|
|
prepared) status_phase=preparing ;;
|
|
stopped) status_phase=stopping ;;
|
|
old_moved|new_installed) status_phase=applying ;;
|
|
uci_applied) status_phase=validating ;;
|
|
service_started) status_phase=starting ;;
|
|
rollback_old_start) status_phase=rolling_back ;;
|
|
committed) status_phase=committing ;;
|
|
*) status_phase=preparing ;;
|
|
esac; fi
|
|
write_status running "$status_phase" true "$status_phase" || :
|
|
sleep 1
|
|
done
|
|
wait "$maint_pid"
|
|
operation_rc=$?
|
|
# json_load 空串会返回 0,而且失败时 jshn 还留着上一次状态对象的 message,
|
|
# 所以空输出必须显式判掉,否则面板报的是随机残留值(曾经出现「preparing」)。
|
|
result_json=$(cat "$job_dir/output" 2>/dev/null)
|
|
json_cleanup 2>/dev/null || :
|
|
if [ -z "$result_json" ] || ! json_load "$result_json" 2>/dev/null; then
|
|
# 脚本没吐出结果时 stderr 是唯一线索,进状态给面板看。
|
|
invalid_reason=$(sed -n '$p' "$job_dir/stderr" 2>/dev/null | tr -d '\r' | cut -c1-200)
|
|
logger -t honk-maintenance "$operation produced no result: ${invalid_reason:-<no stderr>}" 2>/dev/null || :
|
|
if [ -e /etc/.honk-maintenance/journal ] || [ -L /etc/.honk-maintenance/journal ] || [ -d /run/honk.lock ]; then
|
|
write_status rollback_required recovery false maintenance_recovery_required "$invalid_reason"
|
|
else
|
|
write_status failed complete false maintenance_no_result "$invalid_reason"
|
|
fi
|
|
exit 1
|
|
fi
|
|
json_get_var operation_ok ok
|
|
json_get_var operation_message message
|
|
if [ "$operation_rc" -eq 0 ] && [ "$operation_ok" = 1 ]; then
|
|
if [ "$operation" = update_check ]; then
|
|
json_add_string state succeeded
|
|
json_add_string phase complete
|
|
json_add_string job_id "$job_id"
|
|
json_dump > "$job_dir/status.new" && chmod 0600 "$job_dir/status.new" && mv -f "$job_dir/status.new" "$job_dir/status" || exit 1
|
|
logger -t honk-maintenance "$operation completed" 2>/dev/null || :
|
|
exit 0
|
|
fi
|
|
write_status succeeded complete true "$operation_message"
|
|
logger -t honk-maintenance "$operation completed" 2>/dev/null || :
|
|
exit 0
|
|
fi
|
|
case "$operation_message" in
|
|
*rollback_failed*|*recovery_required*|*snapshot_retained*)
|
|
write_status rollback_required recovery false "$operation_message"
|
|
logger -t honk-maintenance "$operation requires recovery" 2>/dev/null || :
|
|
exit 1
|
|
;;
|
|
esac
|
|
write_status failed complete false "${operation_message:-operation_failed}"
|
|
logger -t honk-maintenance "$operation failed" 2>/dev/null || :
|
|
exit 1
|