210 lines
8.6 KiB
Shell
210 lines
8.6 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)
|
|
[[ ",$expected," == *",$code,"* ]] && { 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/ 200,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() {
|
|
/usr/bin/grub-editenv /boot/grub/grubenv unset recordfail
|
|
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
|