hololake-system-architecture/product-source/hololake-platform/guanghu-os/scripts/guanghu-master-init.sh

209 lines
8.5 KiB
Shell

#!/bin/bash
set -Eeuo pipefail
# Guanghu OS resident supervisor for JD-FD-PRIMARY. The Linux kernel remains
# the hardware-compatibility substrate; Ubuntu's init/systemd is not started.
readonly NODE_ID=JD-FD-PRIMARY
readonly INSTANCE_ID=f3d4b730-7f02-452f-975b-7091a4800431
readonly ROOT_UUID=9e4550a0-452b-4f28-b5a5-d5364aa450f6
readonly LINUX_RESCUE_ENTRY=gnulinux-simple-9e4550a0-452b-4f28-b5a5-d5364aa450f6
readonly RELEASE_ID=guanghu-master-20260816.1
readonly STATE_ROOT=/run/guanghu/master
readonly RECEIPT_ROOT=/guanghu/recovery/JD-FD-PRIMARY-master-20260816
readonly HLCC=/opt/guanghu/architecture-releases/3d11ac75bea8cf08b5f223fed86ab3cd999ad2fd/server-tools/hololake-code-channel/jd-candidate/hlcc-bootstrap.py
readonly APP_HUB=/opt/guanghu/architecture-releases/333cd222c53d7d162218543cd167bdda4f8efb22/server-tools/jd-app-hub/server.js
readonly AI_DISCOVERY=/opt/guanghu/ai-discovery/server.js
declare -a CHILDREN=()
RECOVERY_ARMED=0
log() {
printf '%s %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$*" | tee -a "${STATE_ROOT}/supervisor.log"
}
json_state() {
local stage=$1 result=$2
local tmp=${STATE_ROOT}/state.json.tmp.$$
printf '%s\n' "{\"schema\":\"guanghu.master-runtime/v1\",\"node_id\":\"${NODE_ID}\",\"instance_id\":\"${INSTANCE_ID}\",\"release_id\":\"${RELEASE_ID}\",\"boot_id\":\"$(cat /proc/sys/kernel/random/boot_id)\",\"control\":\"GUANGHU_OS_MASTER\",\"pid1\":\"GUANGHU_SUPERVISOR\",\"linux_kernel_role\":\"HARDWARE_COMPATIBILITY_SUBSTRATE\",\"full_linux_userspace\":\"DORMANT\",\"linux_repository_bridge\":\"BOUNDED_SUBCONTROL\",\"linux_rescue\":\"${LINUX_RESCUE_ENTRY}\",\"stage\":\"${stage}\",\"result\":\"${result}\"}" >"${tmp}"
chmod 0600 "${tmp}"
mv "${tmp}" "${STATE_ROOT}/state.json"
}
arm_linux_rescue() {
(( RECOVERY_ARMED == 0 )) || return 0
RECOVERY_ARMED=1
/usr/bin/grub-editenv /boot/grub/grubenv set next_entry="${LINUX_RESCUE_ENTRY}" || true
mkdir -p "${RECEIPT_ROOT}"
cp "${STATE_ROOT}/state.json" "${RECEIPT_ROOT}/FAILED-BOOT-STATE.json" 2>/dev/null || true
sync
}
fatal() {
local message=$1
log "FAIL_0 ${message}"
json_state FAILED "${message//\"/}"
arm_linux_rescue
/bin/systemctl --force --force reboot || /bin/bash -c 'echo b >/proc/sysrq-trigger'
while :; do sleep 60; done
}
require_file() {
[[ -f $1 && ! -L $1 ]] || fatal "required_file_unavailable:$1"
}
listen_ready() {
local port=$1
timeout 2 bash -c "</dev/tcp/127.0.0.1/${port}" >/dev/null 2>&1
}
wait_http() {
local name=$1 url=$2 expected=${3:-200} attempts=${4:-90}
local code
for ((i=0; i<attempts; i++)); do
code=$(/usr/bin/curl -sS -o /dev/null -w '%{http_code}' --max-time 2 "${url}" 2>/dev/null || true)
[[ $code == "$expected" ]] && { log "READY ${name} http=${code}"; return 0; }
sleep 1
done
fatal "service_not_ready:${name}:last_http_${code:-none}"
}
start_root() {
local name=$1; shift
log "START ${name}"
"$@" >>"${STATE_ROOT}/${name}.log" 2>&1 &
CHILDREN+=("$!:$name")
}
start_guanghu() {
local name=$1; shift
log "START ${name} owner=guanghu"
/usr/bin/setpriv --reuid=998 --regid=998 --init-groups --inh-caps=-all --reset-env \
/usr/bin/env HOME=/var/lib/guanghu USER=guanghu LOGNAME=guanghu \
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin "$@" \
>>"${STATE_ROOT}/${name}.log" 2>&1 &
CHILDREN+=("$!:$name")
}
verify_identity() {
local observed cmdline root_source
observed=$(tr A-F a-f </sys/class/dmi/id/product_uuid | tr -d '\r\n')
[[ $observed == "$INSTANCE_ID" ]] || fatal "instance_identity_mismatch"
cmdline=$(</proc/cmdline)
[[ " $cmdline " == *' guanghu.master=1 '* ]] || fatal "master_marker_missing"
[[ " $cmdline " == *" root=UUID=${ROOT_UUID} "* ]] || fatal "root_binding_missing"
root_source=$(findmnt -n -o SOURCE /)
[[ $root_source == /dev/vda1 || $root_source == UUID="$ROOT_UUID" ]] || fatal "root_device_mismatch:${root_source}"
grep -Fq "$LINUX_RESCUE_ENTRY" /boot/grub/grub.cfg || fatal "linux_rescue_missing"
[[ $$ -eq 1 ]] || fatal "supervisor_is_not_pid1"
}
prepare_runtime() {
mount -o remount,rw /
mkdir -p "$STATE_ROOT" "$RECEIPT_ROOT" /run/sshd /run/systemd/resolve
chmod 0700 "$STATE_ROOT" "$RECEIPT_ROOT"
chmod 1777 /tmp
mountpoint -q /proc || mount -t proc proc /proc
mountpoint -q /sys || mount -t sysfs sysfs /sys
mountpoint -q /run || mount -t tmpfs -o mode=0755,nosuid,nodev tmpfs /run
printf 'nameserver 183.60.83.19\nnameserver 223.5.5.5\noptions timeout:2 attempts:2\n' >/run/systemd/resolve/stub-resolv.conf
hostname jd-fd-primary
/usr/sbin/ip link set lo up
/usr/sbin/ip link set eth0 up
/usr/sbin/ip addr flush dev eth0
/usr/sbin/ip addr add 172.16.0.6/16 dev eth0
/usr/sbin/ip route replace default via 172.16.0.1 dev eth0
}
start_bridge() {
require_file "$HLCC"
require_file "$APP_HUB"
require_file "$AI_DISCOVERY"
start_root sshd /usr/sbin/sshd -D -e \
-o UsePAM=no -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no \
-o PermitRootLogin=prohibit-password -o AllowTcpForwarding=yes \
-o GatewayPorts=no -o X11Forwarding=no -o PrintMotd=no
sleep 1
listen_ready 22 || fatal "sshd_not_listening"
start_guanghu repository-bridge /usr/bin/python3 "$HLCC"
wait_http repository-bridge http://127.0.0.1:3341/health 200 120
wait_http repository-service http://127.0.0.1:3340/api/healthz 200 30
start_guanghu app-hub /usr/bin/env NODE_ENV=production HUB_HOST=127.0.0.1 \
HUB_PORT=8088 GUANGHU_NODE_ID="$NODE_ID" CODE_CHANNEL_HOST=127.0.0.1 \
CODE_CHANNEL_PORT=3340 CODE_RATE_LIMIT_MAX=600 CODE_BROWSE_RATE_LIMIT_MAX=120 \
/usr/bin/node "$APP_HUB"
wait_http app-hub http://127.0.0.1:8088/api/status 200 30
wait_http code-projection http://127.0.0.1:8088/code/ 303 30
start_guanghu navigation-bridge /usr/bin/env GUANGHU_AI_HOST=127.0.0.1 \
GUANGHU_AI_PORT=3922 GUANGHU_REPOSITORY_MAP=/opt/guanghu/ai-discovery/repository-route-map.json \
GUANGHU_NODE_MAP=/opt/guanghu/ai-discovery/server-node-map.json \
GUANGHU_SUBJECT_REGISTRY=/opt/guanghu/ai-discovery/fifth-domain-subject-registry.json \
GUANGHU_SUBJECT_ALIAS_MAP=/opt/guanghu/ai-discovery/subject-id-alias-map.json \
GUANGHU_NAVIGATION_MAP=/opt/guanghu/ai-discovery/ai-machine-navigation-map.json \
GUANGHU_NAVIGATION_ANCHOR=/opt/guanghu/ai-discovery/public-navigation-anchor.json \
GUANGHU_LIGHTHOUSE_PATHS=/opt/guanghu/ai-discovery/lighthouse-path-registry.json \
GUANGHU_HOST_SKILLS=/opt/guanghu/ai-discovery/host-skill-navigation-map.json \
GUANGHU_IDENTITY_AUTHORITY=/opt/guanghu/ai-discovery/guanghu-identity-authority-map.json \
GUANGHU_TCS_MOTHER_BRAIN=/opt/guanghu/ai-discovery/tcs-mother-brain-runtime-map.json \
GUANGHU_REPOSITORY_GIT_DIR=/var/lib/guanghu/personas/guanghu/hlcc-v16.0.1/data/repositories/bingshuo/guanghu-ice-heart.git \
/usr/bin/node "$AI_DISCOVERY"
wait_http navigation-bridge http://127.0.0.1:3922/v1/anchor 200 30
}
runtime_watch() {
json_state READY PASS_100
cp "$STATE_ROOT/state.json" "$RECEIPT_ROOT/CURRENT-PHYSICAL-STATE.json"
sha256sum "$RECEIPT_ROOT/CURRENT-PHYSICAL-STATE.json" >"$RECEIPT_ROOT/CURRENT-PHYSICAL-STATE.json.sha256"
log 'GUANGHU_OS_MASTER_READY linux_userspace=DORMANT repository_bridge=READY linux_rescue=PRESERVED'
sync
while :; do
sleep 10
for item in "${CHILDREN[@]}"; do
pid=${item%%:*}; name=${item#*:}
kill -0 "$pid" 2>/dev/null || fatal "runtime_process_exited:${name}"
done
listen_ready 22 || fatal "runtime_sshd_lost"
listen_ready 3340 || fatal "runtime_repository_lost"
listen_ready 8088 || fatal "runtime_projection_lost"
listen_ready 3922 || fatal "runtime_navigation_lost"
done
}
main() {
mkdir -p "$STATE_ROOT"
json_state STARTING PENDING
verify_identity
prepare_runtime
json_state STARTING_IDENTITY_VERIFIED PENDING
start_bridge
runtime_watch
}
preflight() {
[[ $(tr A-F a-f </sys/class/dmi/id/product_uuid | tr -d '\r\n') == "$INSTANCE_ID" ]]
[[ $(findmnt -n -o SOURCE /) == /dev/vda1 ]]
grep -Fq "$LINUX_RESCUE_ENTRY" /boot/grub/grub.cfg
getent passwd guanghu | grep -q '^guanghu:x:998:998:'
for path in "$HLCC" "$APP_HUB" "$AI_DISCOVERY" /usr/sbin/sshd \
/usr/bin/node /usr/bin/python3 /usr/bin/setpriv /usr/bin/grub-editenv; do
[[ -e $path && ! -L $path || $path == /usr/bin/python3 ]]
done
printf 'GUANGHU_MASTER_PREFLIGHT_OK node=%s release=%s linux_rescue=%s\n' \
"$NODE_ID" "$RELEASE_ID" "$LINUX_RESCUE_ENTRY"
}
case ${1:-run} in
preflight) preflight ;;
run)
if [[ $$ -ne 1 ]]; then
echo 'GUANGHU_MASTER_REFUSED: run mode requires PID 1' >&2
exit 1
fi
main
;;
*) echo 'usage: guanghu-master-init.sh [run|preflight]' >&2; exit 64 ;;
esac