diff --git a/product-source/hololake-platform/guanghu-os/scripts/guanghu-master-init.sh b/product-source/hololake-platform/guanghu-os/scripts/guanghu-master-init.sh new file mode 100644 index 000000000..40c852cca --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/scripts/guanghu-master-init.sh @@ -0,0 +1,209 @@ +#!/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/null 2>&1 +} + +wait_http() { + local name=$1 url=$2 expected=${3:-200} attempts=${4:-90} + local code + for ((i=0; i/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 /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 &2 + exit 1 + fi + main + ;; + *) echo 'usage: guanghu-master-init.sh [run|preflight]' >&2; exit 64 ;; +esac diff --git a/product-source/hololake-platform/guanghu-os/scripts/install-jd-guanghu-master-candidate.sh b/product-source/hololake-platform/guanghu-os/scripts/install-jd-guanghu-master-candidate.sh new file mode 100644 index 000000000..0c84015f9 --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/scripts/install-jd-guanghu-master-candidate.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +[[ $# == 2 ]] || { echo 'usage: install-jd-guanghu-master-candidate.sh ' >&2; exit 64; } + +source_file=$(readlink -f "$1") +source_commit=$2 +readonly node_id=JD-FD-PRIMARY +readonly instance_id=f3d4b730-7f02-452f-975b-7091a4800431 +readonly root_uuid=9e4550a0-452b-4f28-b5a5-d5364aa450f6 +readonly linux_entry=gnulinux-simple-9e4550a0-452b-4f28-b5a5-d5364aa450f6 +readonly master_entry=guanghu-jd-master-20260816 +readonly install_path=/usr/local/libexec/guanghu/guanghu-master-init +readonly grub_source=/etc/grub.d/42_guanghu_master +readonly receipt_root=/guanghu/recovery/JD-FD-PRIMARY-master-20260816 + +[[ $source_commit =~ ^[0-9a-f]{40}$ ]] +[[ -f $source_file && ! -L $source_file ]] +bash -n "$source_file" +[[ $(tr A-F a-f "$grub_source" </dev/null + +grep -Fq "'${master_entry}'" /boot/grub/grub.cfg +grep -Fq "guanghu.master=1 init=${install_path}" /boot/grub/grub.cfg +grep -Eq "^GRUB_DEFAULT=${linux_entry}$" /etc/default/grub +"$install_path" preflight + +installed_sha=$(sha256sum "$install_path" | awk '{print $1}') +source_sha=$(sha256sum "$source_file" | awk '{print $1}') +[[ $installed_sha == "$source_sha" ]] +boot_id=$(cat /proc/sys/kernel/random/boot_id) +cat >"$receipt_root/CANDIDATE-INSTALL-RECEIPT.hldp" <"$receipt_root/CANDIDATE-INSTALL-RECEIPT.hldp.sha256" +sync +printf 'GUANGHU_MASTER_CANDIDATE_INSTALLED entry=%s sha256=%s default=%s\n' "$master_entry" "$installed_sha" "$linux_entry" diff --git a/product-source/hololake-platform/guanghu-os/scripts/test-guanghu-master-init.sh b/product-source/hololake-platform/guanghu-os/scripts/test-guanghu-master-init.sh new file mode 100644 index 000000000..0c765d9af --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/scripts/test-guanghu-master-init.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +source_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +subject=${source_root}/scripts/guanghu-master-init.sh + +bash -n "$subject" +grep -Fq 'readonly NODE_ID=JD-FD-PRIMARY' "$subject" +grep -Fq 'readonly INSTANCE_ID=f3d4b730-7f02-452f-975b-7091a4800431' "$subject" +grep -Fq '[[ $$ -eq 1 ]]' "$subject" +grep -Fq "if [[ \$\$ -ne 1 ]]" "$subject" +grep -Fq "GUANGHU_MASTER_REFUSED: run mode requires PID 1" "$subject" +grep -Fq '\"control\":\"GUANGHU_OS_MASTER\"' "$subject" +grep -Fq '\"full_linux_userspace\":\"DORMANT\"' "$subject" +grep -Fq '/usr/bin/grub-editenv /boot/grub/grubenv set next_entry=' "$subject" +grep -Fq 'start_root sshd /usr/sbin/sshd -D -e' "$subject" +grep -Fq 'mount -o remount,rw /' "$subject" +grep -Fq 'start_guanghu repository-bridge' "$subject" +grep -Fq 'start_guanghu app-hub' "$subject" +grep -Fq 'start_guanghu navigation-bridge' "$subject" +grep -Fq 'wait_http code-projection http://127.0.0.1:8088/code/ 303' "$subject" +grep -Fq 'wait_http navigation-bridge http://127.0.0.1:3922/v1/anchor 200' "$subject" +if grep -Eq '(^|[[:space:]])(systemd|/sbin/init)([[:space:]]|$)' "$subject"; then + echo 'full Linux init must remain dormant' >&2 + exit 1 +fi +echo GUANGHU_MASTER_INIT_CONTRACT_OK diff --git a/product-source/hololake-platform/guanghu-os/scripts/test-install-jd-guanghu-master-candidate.sh b/product-source/hololake-platform/guanghu-os/scripts/test-install-jd-guanghu-master-candidate.sh new file mode 100644 index 000000000..6c426b87d --- /dev/null +++ b/product-source/hololake-platform/guanghu-os/scripts/test-install-jd-guanghu-master-candidate.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +source_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +subject=${source_root}/scripts/install-jd-guanghu-master-candidate.sh +bash -n "$subject" +grep -Fq 'readonly node_id=JD-FD-PRIMARY' "$subject" +grep -Fq 'grep -Eq "^GRUB_DEFAULT=${linux_entry}$" /etc/default/grub' "$subject" +grep -Fq "menuentry 'Guanghu OS master · JD repository bridge'" "$subject" +grep -Fq 'guanghu.master=1 init=${install_path} panic=10' "$subject" +grep -Fq 'unattended_default=${linux_entry}' "$subject" +grep -Fq 'linux_rescue=PRESERVED' "$subject" +grep -Fq '"$install_path" preflight' "$subject" +echo GUANGHU_MASTER_CANDIDATE_INSTALL_CONTRACT_OK