From 2b5ecb721b9188a5be1bb9d60123fad8b09f3cc8 Mon Sep 17 00:00:00 2001 From: Antarecke <2395326872@qq.com> Date: Tue, 26 May 2026 16:33:50 +0000 Subject: [PATCH 1/5] test(starry,nginx): implement alpine nginx CI --- apps/starry/README.md | 9 + apps/starry/nginx/README.md | 26 + .../nginx/build-x86_64-unknown-none.toml | 14 + apps/starry/nginx/nginx-alpine-mirror.sh | 53 ++ apps/starry/nginx/nginx-smoke-tests.sh | 285 ++++++++++ apps/starry/nginx/prebuild.sh | 13 + .../starry/nginx}/qemu-x86_64.toml | 4 +- .../nginx-smoke/sh/nginx-smoke-tests.sh | 507 ------------------ 8 files changed, 402 insertions(+), 509 deletions(-) create mode 100644 apps/starry/nginx/README.md create mode 100644 apps/starry/nginx/build-x86_64-unknown-none.toml create mode 100644 apps/starry/nginx/nginx-alpine-mirror.sh create mode 100644 apps/starry/nginx/nginx-smoke-tests.sh create mode 100644 apps/starry/nginx/prebuild.sh rename {test-suit/starryos/normal/qemu-smp1/nginx-smoke => apps/starry/nginx}/qemu-x86_64.toml (76%) delete mode 100644 test-suit/starryos/normal/qemu-smp1/nginx-smoke/sh/nginx-smoke-tests.sh diff --git a/apps/starry/README.md b/apps/starry/README.md index ec21856503..3e9c8c73f4 100644 --- a/apps/starry/README.md +++ b/apps/starry/README.md @@ -93,6 +93,15 @@ cargo xtask starry qemu \ See `jcode/README.md` for interactive usage and troubleshooting. +## Nginx + +The `nginx` case is a QEMU app integration workflow. It installs Alpine nginx +packages in a staging root during prebuild, injects runtime artifacts to the +app overlay, then runs smoke tests inside StarryOS. + +```bash +cargo xtask starry app run -t nginx --arch x86_64 +``` ## Orange Pi 5 Plus UVC diff --git a/apps/starry/nginx/README.md b/apps/starry/nginx/README.md new file mode 100644 index 0000000000..ee7587c0ae --- /dev/null +++ b/apps/starry/nginx/README.md @@ -0,0 +1,26 @@ +# Starry Nginx App + +This app runs Nginx integration tests inside StarryOS QEMU. + +## Run Smoke + +```bash +cargo xtask starry app run -t nginx --arch x86_64 +``` + +## Build/Prepare Logic + +`prebuild.sh` injects reusable nginx test scripts into the app overlay. + +Package installation happens in the guest prepare stage before test execution: + +- packages: `nginx`, `curl`, `busybox-extras`, `coreutils` +- mirror priority: domestic mirror first, then official global mirror +- each mirror attempt uses timeout (`NGINX_APK_MIRROR_TIMEOUT_SEC`, default `45`) +- mirror failure triggers automatic fallback +- if all mirrors fail, tests fail early at prepare stage and do not enter nginx + functional checks + +The shared helper logic is in: + +- `apps/starry/nginx/nginx-alpine-mirror.sh` diff --git a/apps/starry/nginx/build-x86_64-unknown-none.toml b/apps/starry/nginx/build-x86_64-unknown-none.toml new file mode 100644 index 0000000000..94d8986bbb --- /dev/null +++ b/apps/starry/nginx/build-x86_64-unknown-none.toml @@ -0,0 +1,14 @@ +target = "x86_64-unknown-none" +env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } +log = "Warn" +features = [ + "ax-hal/x86-pc", + "qemu", + "ax-driver/pci", + "ax-driver/virtio-blk", + "ax-driver/virtio-net", + "ax-driver/virtio-gpu", + "ax-driver/virtio-input", + "ax-driver/virtio-socket", +] +plat_dyn = false diff --git a/apps/starry/nginx/nginx-alpine-mirror.sh b/apps/starry/nginx/nginx-alpine-mirror.sh new file mode 100644 index 0000000000..6b9ccb330d --- /dev/null +++ b/apps/starry/nginx/nginx-alpine-mirror.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +set -eu + +NGINX_APK_MIRROR_TIMEOUT_SEC="${NGINX_APK_MIRROR_TIMEOUT_SEC:-45}" +NGINX_APK_REPO_FILE="/tmp/nginx-apk-repositories" +NGINX_APK_ATTEMPT_LOG="/tmp/nginx-apk-attempt.log" + +write_repo_file() { + mirror="$1" + cat >"$NGINX_APK_REPO_FILE" <"$NGINX_APK_ATTEMPT_LOG" 2>&1; then + echo "NGINX_APK_MIRROR_OK: $mirror" + return 0 + fi + + rc=$? + if [ "$rc" -eq 124 ]; then + echo "NGINX_APK_MIRROR_FAIL: $mirror (timeout ${NGINX_APK_MIRROR_TIMEOUT_SEC}s)" + else + echo "NGINX_APK_MIRROR_FAIL: $mirror (apk rc=$rc)" + fi + sed -n '1,120p' "$NGINX_APK_ATTEMPT_LOG" || true + return 1 +} + +nginx_apk_add_with_fallback() { + mirror_cn="https://mirrors.tuna.tsinghua.edu.cn/alpine/latest-stable" + mirror_global="https://dl-cdn.alpinelinux.org/alpine/latest-stable" + + if run_apk_add "$mirror_cn" "$@"; then + return 0 + fi + if run_apk_add "$mirror_global" "$@"; then + return 0 + fi + + echo "NGINX_APK_ALL_MIRRORS_FAILED" + return 1 +} diff --git a/apps/starry/nginx/nginx-smoke-tests.sh b/apps/starry/nginx/nginx-smoke-tests.sh new file mode 100644 index 0000000000..7006b1771c --- /dev/null +++ b/apps/starry/nginx/nginx-smoke-tests.sh @@ -0,0 +1,285 @@ +#!/bin/sh + +BASE=/tmp/nginx-tests +CONF="$BASE/conf/single-worker.conf" +MASTER_CONF="$BASE/conf/master-one-worker.conf" +SENDFILE_CONF="$BASE/conf/sendfile.conf" +WWW="$BASE/www" +LOGDIR="$BASE/logs" +OUT="$BASE/out" +NGINX_PID= +MASTER_PID= +FAILURES=0 + +. /usr/bin/nginx-alpine-mirror.sh + +log() { printf 'NGINX_APP_LOG: %s\n' "$*"; } +pass() { printf 'NGINX_APP_STEP_PASS: %s\n' "$*"; } +fail() { printf 'NGINX_APP_STEP_FAIL: %s\n' "$*"; FAILURES=$((FAILURES + 1)); } + +run_step() { + step_name=$1 + shift + log "BEGIN $step_name" + if "$@"; then + pass "$step_name" + return 0 + fi + fail "$step_name" + return 1 +} + +dump_file() { + dump_name=$1 + dump_path=$2 + printf -- '--- %s: %s ---\n' "$dump_name" "$dump_path" + if [ -f "$dump_path" ]; then + sed -n '1,220p' "$dump_path" 2>&1 + else + printf 'missing\n' + fi +} + +dump_diag() { + printf '=== NGINX_APP_DIAG_BEGIN ===\n' + date 2>&1 || true + uname -a 2>&1 || true + ps 2>&1 || true + ip addr 2>&1 || true + ip route 2>&1 || true + ss -ltnp 2>&1 || netstat -ltnp 2>&1 || true + ls -la "$BASE" "$LOGDIR" "$OUT" 2>&1 || true + dump_file "nginx config" "$CONF" + dump_file "nginx master config" "$MASTER_CONF" + dump_file "nginx sendfile config" "$SENDFILE_CONF" + dump_file "nginx stdout" "$LOGDIR/nginx-stdout.log" + dump_file "nginx error log" "$LOGDIR/error.log" + dump_file "nginx access log" "$LOGDIR/access.log" + printf '=== NGINX_APP_DIAG_END ===\n' +} + +cleanup() { + if [ -n "$MASTER_PID" ] && kill -0 "$MASTER_PID" 2>/dev/null; then + kill -TERM "$MASTER_PID" 2>/dev/null || true + sleep 1 + kill -KILL "$MASTER_PID" 2>/dev/null || true + fi + if [ -n "$NGINX_PID" ] && kill -0 "$NGINX_PID" 2>/dev/null; then + kill -TERM "$NGINX_PID" 2>/dev/null || true + sleep 1 + kill -KILL "$NGINX_PID" 2>/dev/null || true + fi +} + +finish() { + status=$? + cleanup + if [ "$FAILURES" -eq 0 ] && [ "$status" -eq 0 ]; then + printf 'NGINX_APP_SMOKE_PASSED\n' + exit 0 + fi + dump_diag + printf 'NGINX_APP_SMOKE_FAILED failures=%s status=%s\n' "$FAILURES" "$status" + exit 1 +} + +trap finish EXIT + +prepare_packages() { + nginx_apk_add_with_fallback nginx curl busybox-extras coreutils || { + printf 'NGINX_APP_PREPARE_FAILED: all mirrors failed\n' + return 1 + } +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW/dir" "$LOGDIR" "$OUT" "$BASE/client_temp" + printf 'NGINX_APP_INDEX_OK\n' > "$WWW/index.html" + printf 'small static file\n' > "$WWW/small.txt" + : > "$WWW/empty.txt" + printf 'NGINX_APP_DIR_INDEX_OK\n' > "$WWW/dir/index.html" + dd if=/dev/zero of="$WWW/large.bin" bs=1024 count=1024 + cat > "$CONF" <<'EOF_CONF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-tests/logs/error.log debug; +pid /tmp/nginx-tests/nginx.pid; + +events { worker_connections 64; } + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + access_log /tmp/nginx-tests/logs/access.log; + sendfile off; + keepalive_timeout 5; + client_body_temp_path /tmp/nginx-tests/client_temp; + server { + listen 127.0.0.1:8080; + server_name localhost; + root /tmp/nginx-tests/www; + location / { index index.html; } + } +} +EOF_CONF + + cat > "$MASTER_CONF" <<'EOF_MASTER_CONF' +daemon off; +master_process on; +worker_processes 1; +error_log /tmp/nginx-tests/logs/error-master.log debug; +pid /tmp/nginx-tests/nginx-master.pid; + +events { worker_connections 64; } + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + access_log /tmp/nginx-tests/logs/access-master.log; + sendfile off; + keepalive_timeout 5; + client_body_temp_path /tmp/nginx-tests/client_temp; + server { + listen 127.0.0.1:8081; + server_name localhost; + root /tmp/nginx-tests/www; + location / { index index.html; } + } +} +EOF_MASTER_CONF + + cat > "$SENDFILE_CONF" <<'EOF_SENDFILE_CONF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-tests/logs/error-sendfile.log debug; +pid /tmp/nginx-tests/nginx-sendfile.pid; + +events { worker_connections 64; } + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + access_log /tmp/nginx-tests/logs/access-sendfile.log; + sendfile on; + keepalive_timeout 5; + client_max_body_size 4k; + client_body_buffer_size 1k; + client_body_temp_path /tmp/nginx-tests/client_temp; + server { + listen 127.0.0.1:8082; + server_name localhost; + root /tmp/nginx-tests/www; + location / { index index.html; } + } +} +EOF_SENDFILE_CONF +} + +probe_environment() { + nginx -v + nginx -V + test -w /tmp + test -c /dev/null + test -c /dev/zero + test -r /proc/self/stat + test -r /proc/meminfo + ls -la /proc/self/fd +} + +test_config() { nginx -t -c "$CONF" -p "$BASE/"; } +test_master_config() { nginx -t -c "$MASTER_CONF" -p "$BASE/"; } +test_sendfile_config() { nginx -t -c "$SENDFILE_CONF" -p "$BASE/"; } + +start_nginx() { + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + NGINX_PID=$! + i=0 + while [ "$i" -lt 90 ]; do + if ! kill -0 "$NGINX_PID" 2>/dev/null; then return 1; fi + if curl -fsS -o "$OUT/startup.body" http://127.0.0.1:8080/ >/dev/null 2>&1; then return 0; fi + sleep 1 + i=$((i + 1)) + done + return 1 +} + +test_get_index() { curl -fsS -D "$OUT/index.headers" -o "$OUT/index.body" http://127.0.0.1:8080/ && grep -qx 'NGINX_APP_INDEX_OK' "$OUT/index.body"; } +test_get_missing() { code=$(curl -sS -o "$OUT/missing.body" -w '%{http_code}' http://127.0.0.1:8080/missing.txt || printf 'curl_failed'); [ "$code" = "404" ]; } +test_head_small() { code=$(curl -sS -I -o "$OUT/head.headers" -w '%{http_code}' http://127.0.0.1:8080/small.txt || printf 'curl_failed'); [ "$code" = "200" ] && grep -qi '^Content-Length: 18' "$OUT/head.headers"; } + +test_keepalive_two_requests() { + if command -v nc >/dev/null 2>&1; then NC=nc; elif busybox nc 2>&1 | grep -qi 'usage'; then NC='busybox nc'; else return 0; fi + if command -v timeout >/dev/null 2>&1; then TIMEOUT=timeout; elif busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT='busybox timeout'; else return 0; fi + { printf 'GET /small.txt HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n'; printf 'GET /empty.txt HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n'; } | $TIMEOUT 20 sh -c "$NC 127.0.0.1 8080" > "$OUT/keepalive.raw" + [ -s "$OUT/keepalive.raw" ] || return 0 + count=$(tr -d '\r' < "$OUT/keepalive.raw" | grep -c '^HTTP/1.1 200 OK') + [ "$count" -eq 2 ] +} + +test_logs() { test -s "$LOGDIR/access.log" && test -f "$LOGDIR/error.log"; } +stop_nginx() { kill -TERM "$NGINX_PID"; i=0; while kill -0 "$NGINX_PID" 2>/dev/null && [ "$i" -lt 30 ]; do sleep 1; i=$((i + 1)); done; ! kill -0 "$NGINX_PID" 2>/dev/null; } + +start_nginx_master() { + nginx -c "$MASTER_CONF" -p "$BASE/" > "$LOGDIR/nginx-master-stdout.log" 2>&1 & + MASTER_PID=$! + i=0 + while [ "$i" -lt 90 ]; do + if ! kill -0 "$MASTER_PID" 2>/dev/null; then return 1; fi + if curl -fsS -o "$OUT/master-startup.body" http://127.0.0.1:8081/ >/dev/null 2>&1; then return 0; fi + sleep 1 + i=$((i + 1)) + done + return 1 +} + +test_master_get_index() { curl -fsS -D "$OUT/master-index.headers" -o "$OUT/master-index.body" http://127.0.0.1:8081/ && grep -qx 'NGINX_APP_INDEX_OK' "$OUT/master-index.body"; } +test_master_reload() { nginx -s reload -c "$MASTER_CONF" -p "$BASE/" && sleep 2 && curl -fsS -o "$OUT/master-after-reload.body" http://127.0.0.1:8081/small.txt && grep -qx 'small static file' "$OUT/master-after-reload.body"; } +stop_nginx_master() { nginx -s quit -c "$MASTER_CONF" -p "$BASE/"; i=0; while kill -0 "$MASTER_PID" 2>/dev/null && [ "$i" -lt 30 ]; do sleep 1; i=$((i + 1)); done; ! kill -0 "$MASTER_PID" 2>/dev/null; } + +start_nginx_sendfile() { + nginx -c "$SENDFILE_CONF" -p "$BASE/" > "$LOGDIR/nginx-sendfile-stdout.log" 2>&1 & + NGINX_PID=$! + i=0 + while [ "$i" -lt 90 ]; do + if ! kill -0 "$NGINX_PID" 2>/dev/null; then return 1; fi + if curl -fsS -o "$OUT/sendfile-startup.body" http://127.0.0.1:8082/ >/dev/null 2>&1; then return 0; fi + sleep 1 + i=$((i + 1)) + done + return 1 +} + +test_large_sendfile() { curl -fsS -D "$OUT/large.headers" -o "$OUT/large.bin" http://127.0.0.1:8082/large.bin && [ "$(wc -c < "$OUT/large.bin")" -eq 1048576 ] && cmp "$WWW/large.bin" "$OUT/large.bin"; } +test_range() { curl -fsS -D "$OUT/range.headers" -H 'Range: bytes=0-15' -o "$OUT/range.bin" http://127.0.0.1:8082/large.bin && [ "$(wc -c < "$OUT/range.bin")" -eq 16 ] && grep -qi '^HTTP/1.1 206' "$OUT/range.headers" && grep -qi '^Content-Range: bytes 0-15/1048576' "$OUT/range.headers"; } +test_post_small() { code=$(curl -sS -D "$OUT/post.headers" -o "$OUT/post.body" -w '%{http_code}' -X POST --data 'abc' http://127.0.0.1:8082/ || printf 'curl_failed'); [ "$code" = "405" ] || [ "$code" = "404" ] || [ "$code" = "200" ]; } +test_post_too_large() { dd if=/dev/zero of="$OUT/post-large.bin" bs=1024 count=8 && code=$(curl -sS -D "$OUT/post-large.headers" -o "$OUT/post-large.body" -w '%{http_code}' -X POST --data-binary "@$OUT/post-large.bin" http://127.0.0.1:8082/ || printf 'curl_failed') && [ "$code" = "413" ]; } +test_post_too_large_known_issue() { if test_post_too_large; then return 0; fi; log "KNOWN_ISSUE: too large POST did not return 413"; return 0; } +test_short_connection_loop() { i=1; while [ "$i" -le 20 ]; do curl -fsS -o /dev/null http://127.0.0.1:8082/small.txt || return 1; i=$((i + 1)); done; } + +run_step "prepare packages" prepare_packages || exit 1 +run_step "prepare nginx files" prepare_tree || exit 1 +run_step "environment probe" probe_environment || exit 1 +run_step "nginx config test" test_config || exit 1 +run_step "start nginx single process" start_nginx || exit 1 +run_step "GET /" test_get_index +run_step "GET missing returns 404" test_get_missing +run_step "HEAD /small.txt" test_head_small +run_step "keepalive two requests" test_keepalive_two_requests +run_step "logs written" test_logs +run_step "stop nginx" stop_nginx +run_step "nginx master config test" test_master_config +run_step "start nginx master one worker" start_nginx_master +run_step "master GET /" test_master_get_index +run_step "master reload" test_master_reload +run_step "master quit" stop_nginx_master +run_step "nginx sendfile config test" test_sendfile_config +run_step "start nginx sendfile" start_nginx_sendfile +run_step "large file sendfile" test_large_sendfile +run_step "range request" test_range +run_step "small POST" test_post_small +run_step "too large POST known issue probe" test_post_too_large_known_issue +run_step "20 short connections" test_short_connection_loop +run_step "stop nginx sendfile" stop_nginx diff --git a/apps/starry/nginx/prebuild.sh b/apps/starry/nginx/prebuild.sh new file mode 100644 index 0000000000..42a7b410bf --- /dev/null +++ b/apps/starry/nginx/prebuild.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +app_dir="${STARRY_APP_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}" +overlay_dir="${STARRY_OVERLAY_DIR:-}" + +if [[ -z "$overlay_dir" ]]; then + echo "error: STARRY_OVERLAY_DIR is required" >&2 + exit 1 +fi + +install -Dm0755 "$app_dir/nginx-smoke-tests.sh" "$overlay_dir/usr/bin/nginx-smoke-tests.sh" +install -Dm0755 "$app_dir/nginx-alpine-mirror.sh" "$overlay_dir/usr/bin/nginx-alpine-mirror.sh" diff --git a/test-suit/starryos/normal/qemu-smp1/nginx-smoke/qemu-x86_64.toml b/apps/starry/nginx/qemu-x86_64.toml similarity index 76% rename from test-suit/starryos/normal/qemu-smp1/nginx-smoke/qemu-x86_64.toml rename to apps/starry/nginx/qemu-x86_64.toml index e8d4b8072e..15062c97e8 100644 --- a/test-suit/starryos/normal/qemu-smp1/nginx-smoke/qemu-x86_64.toml +++ b/apps/starry/nginx/qemu-x86_64.toml @@ -15,6 +15,6 @@ uefi = false to_bin = false shell_prefix = "root@starry:" shell_init_cmd = "/usr/bin/nginx-smoke-tests.sh" -success_regex = ["(?m)^STARRY_NGINX_SMOKE_PASSED\\s*$"] -fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^STARRY_NGINX_SMOKE_FAILED"] +success_regex = ["(?m)^NGINX_APP_SMOKE_PASSED\\s*$"] +fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^NGINX_APP_SMOKE_FAILED"] timeout = 3600 diff --git a/test-suit/starryos/normal/qemu-smp1/nginx-smoke/sh/nginx-smoke-tests.sh b/test-suit/starryos/normal/qemu-smp1/nginx-smoke/sh/nginx-smoke-tests.sh deleted file mode 100644 index f9ce11c827..0000000000 --- a/test-suit/starryos/normal/qemu-smp1/nginx-smoke/sh/nginx-smoke-tests.sh +++ /dev/null @@ -1,507 +0,0 @@ -#!/bin/sh - -BASE=/tmp/nginx-tests -CONF="$BASE/conf/single-worker.conf" -MASTER_CONF="$BASE/conf/master-one-worker.conf" -SENDFILE_CONF="$BASE/conf/sendfile.conf" -WWW="$BASE/www" -LOGDIR="$BASE/logs" -OUT="$BASE/out" -NGINX_PID= -MASTER_PID= -FAILURES=0 - -log() -{ - printf 'STARRY_NGINX_LOG: %s\n' "$*" -} - -pass() -{ - printf 'STARRY_NGINX_STEP_PASS: %s\n' "$*" -} - -fail() -{ - printf 'STARRY_NGINX_STEP_FAIL: %s\n' "$*" - FAILURES=$((FAILURES + 1)) -} - -run_step() -{ - step_name=$1 - shift - log "BEGIN $step_name" - if "$@"; then - pass "$step_name" - return 0 - fi - fail "$step_name" - return 1 -} - -dump_file() -{ - dump_name=$1 - dump_path=$2 - printf -- '--- %s: %s ---\n' "$dump_name" "$dump_path" - if [ -f "$dump_path" ]; then - sed -n '1,220p' "$dump_path" 2>&1 - else - printf 'missing\n' - fi -} - -dump_diag() -{ - printf '=== STARRY_NGINX_DIAG_BEGIN ===\n' - date 2>&1 || true - uname -a 2>&1 || true - pwd 2>&1 || true - printf -- '--- processes ---\n' - ps 2>&1 || true - printf -- '--- network ---\n' - ip addr 2>&1 || true - ip route 2>&1 || true - ss -ltnp 2>&1 || netstat -ltnp 2>&1 || true - printf -- '--- nginx files ---\n' - ls -la "$BASE" "$LOGDIR" "$OUT" 2>&1 || true - dump_file "nginx config" "$CONF" - dump_file "nginx master config" "$MASTER_CONF" - dump_file "nginx sendfile config" "$SENDFILE_CONF" - dump_file "nginx stdout" "$LOGDIR/nginx-stdout.log" - dump_file "nginx master stdout" "$LOGDIR/nginx-master-stdout.log" - dump_file "nginx sendfile stdout" "$LOGDIR/nginx-sendfile-stdout.log" - dump_file "nginx error log" "$LOGDIR/error.log" - dump_file "nginx master error log" "$LOGDIR/error-master.log" - dump_file "nginx sendfile error log" "$LOGDIR/error-sendfile.log" - dump_file "nginx access log" "$LOGDIR/access.log" - dump_file "nginx master access log" "$LOGDIR/access-master.log" - dump_file "nginx sendfile access log" "$LOGDIR/access-sendfile.log" - dump_file "curl index headers" "$OUT/index.headers" - dump_file "curl index body" "$OUT/index.body" - dump_file "curl missing body" "$OUT/missing.body" - dump_file "curl head headers" "$OUT/head.headers" - dump_file "keepalive raw" "$OUT/keepalive.raw" - dump_file "range headers" "$OUT/range.headers" - dump_file "post headers" "$OUT/post.headers" - printf '=== STARRY_NGINX_DIAG_END ===\n' -} - -cleanup() -{ - if [ -n "$MASTER_PID" ] && kill -0 "$MASTER_PID" 2>/dev/null; then - log "cleanup: terminating nginx master pid $MASTER_PID" - kill -TERM "$MASTER_PID" 2>/dev/null || true - i=0 - while kill -0 "$MASTER_PID" 2>/dev/null && [ "$i" -lt 10 ]; do - sleep 1 - i=$((i + 1)) - done - kill -KILL "$MASTER_PID" 2>/dev/null || true - fi - - if [ -n "$NGINX_PID" ] && kill -0 "$NGINX_PID" 2>/dev/null; then - log "cleanup: terminating nginx pid $NGINX_PID" - kill -TERM "$NGINX_PID" 2>/dev/null || true - i=0 - while kill -0 "$NGINX_PID" 2>/dev/null && [ "$i" -lt 10 ]; do - sleep 1 - i=$((i + 1)) - done - kill -KILL "$NGINX_PID" 2>/dev/null || true - fi -} - -finish() -{ - status=$? - cleanup - if [ "$FAILURES" -eq 0 ] && [ "$status" -eq 0 ]; then - printf 'STARRY_NGINX_SMOKE_PASSED\n' - exit 0 - fi - dump_diag - printf 'STARRY_NGINX_SMOKE_FAILED failures=%s status=%s\n' "$FAILURES" "$status" - exit 1 -} - -trap finish EXIT - -prepare_packages() -{ - apk update - apk add nginx curl busybox-extras coreutils -} - -prepare_tree() -{ - rm -rf "$BASE" - mkdir -p "$BASE/conf" "$WWW/dir" "$LOGDIR" "$OUT" "$BASE/client_temp" - printf 'STARRY_NGINX_INDEX_OK\n' > "$WWW/index.html" - printf 'small static file\n' > "$WWW/small.txt" - : > "$WWW/empty.txt" - printf 'STARRY_NGINX_DIR_INDEX_OK\n' > "$WWW/dir/index.html" - dd if=/dev/zero of="$WWW/large.bin" bs=1024 count=1024 - cat > "$CONF" <<'EOF_CONF' -daemon off; -master_process off; -worker_processes 1; -error_log /tmp/nginx-tests/logs/error.log debug; -pid /tmp/nginx-tests/nginx.pid; - -events { - worker_connections 64; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - access_log /tmp/nginx-tests/logs/access.log; - sendfile off; - keepalive_timeout 5; - client_body_temp_path /tmp/nginx-tests/client_temp; - - server { - listen 127.0.0.1:8080; - server_name localhost; - root /tmp/nginx-tests/www; - - location / { - index index.html; - } - } -} -EOF_CONF - - cat > "$MASTER_CONF" <<'EOF_MASTER_CONF' -daemon off; -master_process on; -worker_processes 1; -error_log /tmp/nginx-tests/logs/error-master.log debug; -pid /tmp/nginx-tests/nginx-master.pid; - -events { - worker_connections 64; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - access_log /tmp/nginx-tests/logs/access-master.log; - sendfile off; - keepalive_timeout 5; - client_body_temp_path /tmp/nginx-tests/client_temp; - - server { - listen 127.0.0.1:8081; - server_name localhost; - root /tmp/nginx-tests/www; - - location / { - index index.html; - } - } -} -EOF_MASTER_CONF - - cat > "$SENDFILE_CONF" <<'EOF_SENDFILE_CONF' -daemon off; -master_process off; -worker_processes 1; -error_log /tmp/nginx-tests/logs/error-sendfile.log debug; -pid /tmp/nginx-tests/nginx-sendfile.pid; - -events { - worker_connections 64; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - access_log /tmp/nginx-tests/logs/access-sendfile.log; - sendfile on; - keepalive_timeout 5; - client_max_body_size 4k; - client_body_buffer_size 1k; - client_body_temp_path /tmp/nginx-tests/client_temp; - - server { - listen 127.0.0.1:8082; - server_name localhost; - root /tmp/nginx-tests/www; - - location / { - index index.html; - } - } -} -EOF_SENDFILE_CONF -} - -probe_environment() -{ - nginx -v - nginx -V - test -w /tmp - test -c /dev/null - test -c /dev/zero - test -r /proc/self/stat - test -r /proc/meminfo - ls -la /proc/self/fd -} - -test_config() -{ - nginx -t -c "$CONF" -p "$BASE/" -} - -test_master_config() -{ - nginx -t -c "$MASTER_CONF" -p "$BASE/" -} - -test_sendfile_config() -{ - nginx -t -c "$SENDFILE_CONF" -p "$BASE/" -} - -start_nginx() -{ - nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & - NGINX_PID=$! - printf '%s\n' "$NGINX_PID" > "$BASE/nginx-shell.pid" - log "started nginx pid $NGINX_PID" - - i=0 - while [ "$i" -lt 90 ]; do - if ! kill -0 "$NGINX_PID" 2>/dev/null; then - log "nginx exited during startup" - return 1 - fi - if curl -fsS -o "$OUT/startup.body" http://127.0.0.1:8080/ >/dev/null 2>&1; then - return 0 - fi - sleep 1 - i=$((i + 1)) - done - return 1 -} - -test_get_index() -{ - curl -fsS -D "$OUT/index.headers" -o "$OUT/index.body" http://127.0.0.1:8080/ - grep -qx 'STARRY_NGINX_INDEX_OK' "$OUT/index.body" -} - -test_get_missing() -{ - code=$(curl -sS -o "$OUT/missing.body" -w '%{http_code}' http://127.0.0.1:8080/missing.txt || printf 'curl_failed') - [ "$code" = "404" ] -} - -test_head_small() -{ - code=$(curl -sS -I -o "$OUT/head.headers" -w '%{http_code}' http://127.0.0.1:8080/small.txt || printf 'curl_failed') - [ "$code" = "200" ] || return 1 - grep -qi '^Content-Length: 18' "$OUT/head.headers" -} - -test_keepalive_two_requests() -{ - if command -v nc >/dev/null 2>&1; then - NC=nc - elif busybox nc 2>&1 | grep -qi 'usage'; then - NC='busybox nc' - else - log "nc is unavailable for keepalive probe; skipping" - return 0 - fi - - if command -v timeout >/dev/null 2>&1; then - TIMEOUT=timeout - elif busybox timeout 2>&1 | grep -qi 'usage'; then - TIMEOUT='busybox timeout' - else - log "timeout is unavailable for keepalive probe; skipping" - return 0 - fi - - { - printf 'GET /small.txt HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n' - printf 'GET /empty.txt HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' - } | $TIMEOUT 20 sh -c "$NC 127.0.0.1 8080" > "$OUT/keepalive.raw" - - count=$(tr -d '\r' < "$OUT/keepalive.raw" | grep -c '^HTTP/1.1 200 OK') - if [ ! -s "$OUT/keepalive.raw" ]; then - log "nc keepalive probe produced no response; skipping" - return 0 - fi - [ "$count" -eq 2 ] -} - -test_logs() -{ - test -s "$LOGDIR/access.log" - test -f "$LOGDIR/error.log" -} - -stop_nginx() -{ - kill -TERM "$NGINX_PID" - i=0 - while kill -0 "$NGINX_PID" 2>/dev/null && [ "$i" -lt 30 ]; do - sleep 1 - i=$((i + 1)) - done - ! kill -0 "$NGINX_PID" 2>/dev/null -} - -start_nginx_master() -{ - nginx -c "$MASTER_CONF" -p "$BASE/" > "$LOGDIR/nginx-master-stdout.log" 2>&1 & - MASTER_PID=$! - printf '%s\n' "$MASTER_PID" > "$BASE/nginx-master-shell.pid" - log "started nginx master pid $MASTER_PID" - - i=0 - while [ "$i" -lt 90 ]; do - if ! kill -0 "$MASTER_PID" 2>/dev/null; then - log "nginx master exited during startup" - return 1 - fi - if curl -fsS -o "$OUT/master-startup.body" http://127.0.0.1:8081/ >/dev/null 2>&1; then - ps > "$OUT/master-startup.ps" 2>&1 || true - return 0 - fi - sleep 1 - i=$((i + 1)) - done - return 1 -} - -test_master_get_index() -{ - curl -fsS -D "$OUT/master-index.headers" -o "$OUT/master-index.body" http://127.0.0.1:8081/ - grep -qx 'STARRY_NGINX_INDEX_OK' "$OUT/master-index.body" -} - -test_master_reload() -{ - nginx -s reload -c "$MASTER_CONF" -p "$BASE/" - sleep 2 - curl -fsS -o "$OUT/master-after-reload.body" http://127.0.0.1:8081/small.txt - grep -qx 'small static file' "$OUT/master-after-reload.body" -} - -stop_nginx_master() -{ - nginx -s quit -c "$MASTER_CONF" -p "$BASE/" - i=0 - while kill -0 "$MASTER_PID" 2>/dev/null && [ "$i" -lt 30 ]; do - sleep 1 - i=$((i + 1)) - done - ! kill -0 "$MASTER_PID" 2>/dev/null -} - -start_nginx_sendfile() -{ - nginx -c "$SENDFILE_CONF" -p "$BASE/" > "$LOGDIR/nginx-sendfile-stdout.log" 2>&1 & - NGINX_PID=$! - printf '%s\n' "$NGINX_PID" > "$BASE/nginx-sendfile-shell.pid" - log "started nginx sendfile pid $NGINX_PID" - - i=0 - while [ "$i" -lt 90 ]; do - if ! kill -0 "$NGINX_PID" 2>/dev/null; then - log "nginx sendfile instance exited during startup" - return 1 - fi - if curl -fsS -o "$OUT/sendfile-startup.body" http://127.0.0.1:8082/ >/dev/null 2>&1; then - return 0 - fi - sleep 1 - i=$((i + 1)) - done - return 1 -} - -test_large_sendfile() -{ - curl -fsS -D "$OUT/large.headers" -o "$OUT/large.bin" http://127.0.0.1:8082/large.bin - size=$(wc -c < "$OUT/large.bin") - [ "$size" -eq 1048576 ] || return 1 - cmp "$WWW/large.bin" "$OUT/large.bin" -} - -test_range() -{ - curl -fsS -D "$OUT/range.headers" -H 'Range: bytes=0-15' -o "$OUT/range.bin" \ - http://127.0.0.1:8082/large.bin - size=$(wc -c < "$OUT/range.bin") - [ "$size" -eq 16 ] || return 1 - grep -qi '^HTTP/1.1 206' "$OUT/range.headers" || return 1 - grep -qi '^Content-Range: bytes 0-15/1048576' "$OUT/range.headers" -} - -test_post_small() -{ - code=$(curl -sS -D "$OUT/post.headers" -o "$OUT/post.body" -w '%{http_code}' \ - -X POST --data 'abc' http://127.0.0.1:8082/ || printf 'curl_failed') - [ "$code" = "405" ] || [ "$code" = "404" ] || [ "$code" = "200" ] -} - -test_post_too_large() -{ - dd if=/dev/zero of="$OUT/post-large.bin" bs=1024 count=8 - code=$(curl -sS -D "$OUT/post-large.headers" -o "$OUT/post-large.body" -w '%{http_code}' \ - -X POST --data-binary "@$OUT/post-large.bin" http://127.0.0.1:8082/ || printf 'curl_failed') - [ "$code" = "413" ] -} - -test_post_too_large_known_issue() -{ - if test_post_too_large; then - pass "known issue check: too large POST now returns 413" - return 0 - fi - log "KNOWN_ISSUE ISSUE-001: too large POST did not return 413; continuing" - dump_file "post-large headers" "$OUT/post-large.headers" - dump_file "post-large body" "$OUT/post-large.body" - return 0 -} - -test_short_connection_loop() -{ - i=1 - while [ "$i" -le 20 ]; do - curl -fsS -o /dev/null http://127.0.0.1:8082/small.txt || return 1 - i=$((i + 1)) - done -} - -run_step "apk install nginx curl busybox-extras coreutils" prepare_packages || exit 1 -run_step "prepare nginx files" prepare_tree || exit 1 -run_step "environment probe" probe_environment || exit 1 -run_step "nginx config test" test_config || exit 1 -run_step "start nginx single process" start_nginx || exit 1 -run_step "GET /" test_get_index -run_step "GET missing returns 404" test_get_missing -run_step "HEAD /small.txt" test_head_small -run_step "keepalive two requests" test_keepalive_two_requests -run_step "logs written" test_logs -run_step "stop nginx" stop_nginx -run_step "nginx master config test" test_master_config -run_step "start nginx master one worker" start_nginx_master -run_step "master GET /" test_master_get_index -run_step "master reload" test_master_reload -run_step "master quit" stop_nginx_master -run_step "nginx sendfile config test" test_sendfile_config -run_step "start nginx sendfile" start_nginx_sendfile -run_step "large file sendfile" test_large_sendfile -run_step "range request" test_range -run_step "small POST" test_post_small -run_step "too large POST known issue probe" test_post_too_large_known_issue -run_step "20 short connections" test_short_connection_loop -run_step "stop nginx sendfile" stop_nginx From f2d0b2c4804b4e305fd8516f2200e65d78c1060b Mon Sep 17 00:00:00 2001 From: Antarecke <2395326872@qq.com> Date: Thu, 28 May 2026 10:06:15 +0000 Subject: [PATCH 2/5] test(starry,nginx): reorganize nginx app CLI layout --- apps/starry/README.md | 5 +- apps/starry/nginx/README.md | 40 ++-- .../build-riscv64gc-unknown-none-elf.toml | 14 ++ apps/starry/nginx/debug/README.md | 12 ++ .../nginx/debug/nginx-http-basic-tests.sh | 183 ++++++++++++++++++ apps/starry/nginx/nginx-cli-tests.sh | 26 +++ apps/starry/nginx/phase/README.md | 13 ++ .../nginx/phase/nginx-1-3-lifecycle-tests.sh | 134 +++++++++++++ .../nginx/phase/nginx-2-0-http-basic-tests.sh | 75 +++++++ apps/starry/nginx/prebuild.sh | 2 +- apps/starry/nginx/qemu-riscv64.toml | 22 +++ .../nginx/{ => smoke}/nginx-smoke-tests.sh | 0 apps/starry/nginx/stress/README.md | 19 ++ 13 files changed, 529 insertions(+), 16 deletions(-) create mode 100644 apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml create mode 100644 apps/starry/nginx/debug/README.md create mode 100644 apps/starry/nginx/debug/nginx-http-basic-tests.sh create mode 100644 apps/starry/nginx/nginx-cli-tests.sh create mode 100644 apps/starry/nginx/phase/README.md create mode 100644 apps/starry/nginx/phase/nginx-1-3-lifecycle-tests.sh create mode 100644 apps/starry/nginx/phase/nginx-2-0-http-basic-tests.sh create mode 100644 apps/starry/nginx/qemu-riscv64.toml rename apps/starry/nginx/{ => smoke}/nginx-smoke-tests.sh (100%) create mode 100644 apps/starry/nginx/stress/README.md diff --git a/apps/starry/README.md b/apps/starry/README.md index 3e9c8c73f4..3d2b1f77bb 100644 --- a/apps/starry/README.md +++ b/apps/starry/README.md @@ -97,12 +97,15 @@ See `jcode/README.md` for interactive usage and troubleshooting. The `nginx` case is a QEMU app integration workflow. It installs Alpine nginx packages in a staging root during prebuild, injects runtime artifacts to the -app overlay, then runs smoke tests inside StarryOS. +app overlay, then runs nginx smoke tests inside StarryOS. ```bash cargo xtask starry app run -t nginx --arch x86_64 ``` +`apps/starry/nginx` maintains four directories: `smoke`, `phase`, `stress`, and +`debug`. Currently only smoke is connected as nginx test entry in tgoskits workflows. + ## Orange Pi 5 Plus UVC The `orangepi-5-plus-uvc` case needs `/usr/bin/uvc-fps` to be installed in the diff --git a/apps/starry/nginx/README.md b/apps/starry/nginx/README.md index ee7587c0ae..a307578f9b 100644 --- a/apps/starry/nginx/README.md +++ b/apps/starry/nginx/README.md @@ -1,26 +1,38 @@ # Starry Nginx App -This app runs Nginx integration tests inside StarryOS QEMU. +This app organizes nginx tests into four directories: -## Run Smoke +- `smoke/`: CI entry smoke test (the only connected nginx test in tgoskits) +- `phase/`: stage-unit tests named by `x-x`, such as `nginx-1-3-lifecycle-tests.sh` +- `stress/`: pressure test planning and future scripts +- `debug/`: flexible issue-focused scripts + +## CI-Connected Entry + +Only smoke is connected: ```bash -cargo xtask starry app run -t nginx --arch x86_64 +cargo xtask starry app run -t nginx --arch riscv64 ``` -## Build/Prepare Logic +`qemu-*.toml` starts `/usr/bin/nginx-smoke-tests.sh` only. + +## Local Development CLI -`prebuild.sh` injects reusable nginx test scripts into the app overlay. +Unified local CLI script: -Package installation happens in the guest prepare stage before test execution: +```bash +./apps/starry/nginx/nginx-cli-tests.sh smoke +./apps/starry/nginx/nginx-cli-tests.sh phase1 +./apps/starry/nginx/nginx-cli-tests.sh phase2 +./apps/starry/nginx/nginx-cli-tests.sh all +``` + +## Build/Prepare Logic -- packages: `nginx`, `curl`, `busybox-extras`, `coreutils` -- mirror priority: domestic mirror first, then official global mirror -- each mirror attempt uses timeout (`NGINX_APK_MIRROR_TIMEOUT_SEC`, default `45`) -- mirror failure triggers automatic fallback -- if all mirrors fail, tests fail early at prepare stage and do not enter nginx - functional checks +`prebuild.sh` injects only smoke entry and shared mirror helper into guest overlay: -The shared helper logic is in: +- `/usr/bin/nginx-smoke-tests.sh` +- `/usr/bin/nginx-alpine-mirror.sh` -- `apps/starry/nginx/nginx-alpine-mirror.sh` +Mirror helper: `apps/starry/nginx/nginx-alpine-mirror.sh`. diff --git a/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml b/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml new file mode 100644 index 0000000000..66c5d1e30d --- /dev/null +++ b/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml @@ -0,0 +1,14 @@ +target = "riscv64gc-unknown-none-elf" +env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } +log = "Warn" +features = [ + "ax-hal/riscv64-qemu-virt", + "qemu", + "ax-driver/pci", + "ax-driver/virtio-blk", + "ax-driver/virtio-net", + "ax-driver/virtio-gpu", + "ax-driver/virtio-input", + "ax-driver/virtio-socket", +] +plat_dyn = false diff --git a/apps/starry/nginx/debug/README.md b/apps/starry/nginx/debug/README.md new file mode 100644 index 0000000000..44f936a83f --- /dev/null +++ b/apps/starry/nginx/debug/README.md @@ -0,0 +1,12 @@ +# Nginx Debug Tests + +This directory stores flexible debug scripts for single issue reproduction and diagnosis. + +Current scripts: + +- `nginx-http-basic-tests.sh`: early HTTP basic script kept for issue-level debugging. + +Rule: + +- Debug scripts are free-form and can focus on one syscall or one behavior path. +- Debug scripts are not connected to tgoskits nginx CI entry. diff --git a/apps/starry/nginx/debug/nginx-http-basic-tests.sh b/apps/starry/nginx/debug/nginx-http-basic-tests.sh new file mode 100644 index 0000000000..b63f360915 --- /dev/null +++ b/apps/starry/nginx/debug/nginx-http-basic-tests.sh @@ -0,0 +1,183 @@ +#!/bin/sh + +BASE=/tmp/nginx-http-basic +CONF="$BASE/conf/http-basic.conf" +WWW="$BASE/www" +LOGDIR="$BASE/logs" +OUT="$BASE/out" +NGINX_PID= +FAILURES=0 +TIMEOUT_CMD= + +. /usr/bin/nginx-alpine-mirror.sh + +log() { printf 'NGINX_HTTP_BASIC_LOG: %s\n' "$*"; } +pass() { printf 'NGINX_HTTP_BASIC_STEP_PASS: %s\n' "$*"; } +fail() { printf 'NGINX_HTTP_BASIC_STEP_FAIL: %s\n' "$*"; FAILURES=$((FAILURES + 1)); } + +run_step() { + step_name=$1 + shift + log "BEGIN $step_name" + if "$@"; then + pass "$step_name" + return 0 + fi + fail "$step_name" + return 1 +} + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then + TIMEOUT_CMD='timeout' + return 0 + fi + if busybox timeout 2>&1 | grep -qi 'usage'; then + TIMEOUT_CMD='busybox timeout' + return 0 + fi + TIMEOUT_CMD= + return 0 +} + +run_with_timeout() { + timeout_sec=$1 + shift + if [ -n "$TIMEOUT_CMD" ]; then + $TIMEOUT_CMD "$timeout_sec" "$@" + return $? + fi + "$@" +} + +cleanup() { + if [ -n "$NGINX_PID" ] && kill -0 "$NGINX_PID" 2>/dev/null; then + kill -TERM "$NGINX_PID" 2>/dev/null || true + sleep 1 + kill -KILL "$NGINX_PID" 2>/dev/null || true + fi +} + +finish() { + status=$? + cleanup + if [ "$FAILURES" -eq 0 ] && [ "$status" -eq 0 ]; then + printf 'NGINX_HTTP_BASIC_PASSED\n' + exit 0 + fi + printf 'NGINX_HTTP_BASIC_FAILED failures=%s status=%s\n' "$FAILURES" "$status" + exit 1 +} + +trap finish EXIT + +prepare_packages() { + nginx_apk_add_with_fallback nginx curl busybox-extras || { + printf 'NGINX_HTTP_BASIC_PREPARE_FAILED: all mirrors failed\n' + return 1 + } +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW/dir" "$LOGDIR" "$OUT" "$BASE/client_temp" + printf 'HTTP_BASIC_INDEX_OK\n' > "$WWW/index.html" + printf 'small static file\n' > "$WWW/small.txt" + : > "$WWW/empty.txt" + printf 'HTTP_BASIC_DIR_INDEX_OK\n' > "$WWW/dir/index.html" + + cat > "$CONF" <<'EOF_CONF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-http-basic/logs/error.log debug; +pid /tmp/nginx-http-basic/nginx.pid; + +events { worker_connections 64; } + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + access_log /tmp/nginx-http-basic/logs/access.log; + sendfile off; + keepalive_timeout 5; + client_body_temp_path /tmp/nginx-http-basic/client_temp; + server { + listen 127.0.0.1:8080; + server_name localhost; + root /tmp/nginx-http-basic/www; + location / { index index.html; } + } +} +EOF_CONF +} + +test_config() { nginx -t -c "$CONF" -p "$BASE/"; } + +start_nginx() { + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + NGINX_PID=$! + i=0 + while [ "$i" -lt 90 ]; do + if ! kill -0 "$NGINX_PID" 2>/dev/null; then return 1; fi + if run_with_timeout 8 curl -fsS -o "$OUT/startup.body" http://127.0.0.1:8080/ >/dev/null 2>&1; then return 0; fi + sleep 1 + i=$((i + 1)) + done + return 1 +} + +test_get_small() { + code=$(run_with_timeout 12 curl -sS -D "$OUT/small.headers" -o "$OUT/small.body" -w '%{http_code}' http://127.0.0.1:8080/small.txt || printf 'curl_failed') + [ "$code" = "200" ] && grep -qx 'small static file' "$OUT/small.body" && grep -qi '^Content-Length: 18' "$OUT/small.headers" +} + +test_get_empty() { + code=$(run_with_timeout 12 curl -sS -D "$OUT/empty.headers" -o "$OUT/empty.body" -w '%{http_code}' http://127.0.0.1:8080/empty.txt || printf 'curl_failed') + [ "$code" = "200" ] && [ "$(wc -c < "$OUT/empty.body")" -eq 0 ] && grep -qi '^Content-Length: 0' "$OUT/empty.headers" +} + +test_get_dir_slash() { + code=$(run_with_timeout 12 curl -sS -D "$OUT/dir-slash.headers" -o "$OUT/dir-slash.body" -w '%{http_code}' http://127.0.0.1:8080/dir/ || printf 'curl_failed') + [ "$code" = "200" ] && grep -qx 'HTTP_BASIC_DIR_INDEX_OK' "$OUT/dir-slash.body" +} + +test_get_dir_redirect() { + code=$(run_with_timeout 12 curl -sS -D "$OUT/dir.headers" -o "$OUT/dir.body" -w '%{http_code}' http://127.0.0.1:8080/dir || printf 'curl_failed') + [ "$code" = "301" ] || [ "$code" = "302" ] +} + +test_bad_method() { + if command -v nc >/dev/null 2>&1; then + NC='nc' + elif busybox nc 2>&1 | grep -qi 'usage'; then + NC='busybox nc' + else + return 0 + fi + { printf 'BAD / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n'; } | run_with_timeout 12 sh -c "$NC 127.0.0.1 8080" > "$OUT/bad-method.raw" + tr -d '\r' < "$OUT/bad-method.raw" > "$OUT/bad-method.normalized" + grep -Eq '^HTTP/1.1 (400|405)' "$OUT/bad-method.normalized" +} + +stop_nginx() { + kill -TERM "$NGINX_PID" + i=0 + while kill -0 "$NGINX_PID" 2>/dev/null && [ "$i" -lt 30 ]; do + sleep 1 + i=$((i + 1)) + done + ! kill -0 "$NGINX_PID" 2>/dev/null +} + +run_step "prepare packages" prepare_packages || exit 1 +run_step "prepare nginx files" prepare_tree || exit 1 +run_step "init timeout helper" init_timeout_cmd || exit 1 +run_step "nginx config test" test_config || exit 1 +run_step "start nginx" start_nginx || exit 1 +run_step "GET /small.txt" test_get_small +run_step "GET /empty.txt" test_get_empty +run_step "GET /dir/" test_get_dir_slash +run_step "GET /dir redirect" test_get_dir_redirect +run_step "BAD method returns 400/405" test_bad_method +run_step "stop nginx" stop_nginx diff --git a/apps/starry/nginx/nginx-cli-tests.sh b/apps/starry/nginx/nginx-cli-tests.sh new file mode 100644 index 0000000000..21eeb50fe5 --- /dev/null +++ b/apps/starry/nginx/nginx-cli-tests.sh @@ -0,0 +1,26 @@ +#!/bin/sh +set -eu + +cmd=${1:-all} + +case "$cmd" in + smoke) + sh ./smoke/nginx-smoke-tests.sh + ;; + phase1) + sh ./phase/nginx-1-3-lifecycle-tests.sh + ;; + phase2) + sh ./phase/nginx-2-0-http-basic-tests.sh + ;; + all) + sh ./smoke/nginx-smoke-tests.sh + sh ./phase/nginx-1-3-lifecycle-tests.sh + sh ./phase/nginx-2-0-http-basic-tests.sh + printf 'NGINX_CLI_ALL_PASSED\n' + ;; + *) + printf 'usage: %s [smoke|phase1|phase2|all]\n' "$0" + exit 2 + ;; +esac diff --git a/apps/starry/nginx/phase/README.md b/apps/starry/nginx/phase/README.md new file mode 100644 index 0000000000..e25d2e0c55 --- /dev/null +++ b/apps/starry/nginx/phase/README.md @@ -0,0 +1,13 @@ +# Nginx Phase Tests + +Phase tests are organized by stage unit `x-x`. + +Current scripts: + +- `nginx-1-3-lifecycle-tests.sh`: covers phase 1.1, 1.2, and 1.3 lifecycle checks. +- `nginx-2-0-http-basic-tests.sh`: covers phase 2 HTTP basic semantics. + +Rule: + +- Each phase script must include all checks of that phase, including checks already covered by smoke. +- Phase scripts are managed for development iteration and are not connected as tgoskits nginx CI entry. diff --git a/apps/starry/nginx/phase/nginx-1-3-lifecycle-tests.sh b/apps/starry/nginx/phase/nginx-1-3-lifecycle-tests.sh new file mode 100644 index 0000000000..e6a75ecac4 --- /dev/null +++ b/apps/starry/nginx/phase/nginx-1-3-lifecycle-tests.sh @@ -0,0 +1,134 @@ +#!/bin/sh +set -eu + +BASE=/tmp/nginx-phase1 +WWW="$BASE/www" +CONF_DIR="$BASE/conf" +OUT="$BASE/out" +LOG_DIR="$BASE/logs" +TIMEOUT_CMD= + +log() { printf 'NGINX_PHASE1_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE1_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then + TIMEOUT_CMD='timeout' + return + fi + if busybox timeout 2>&1 | grep -qi 'usage'; then + TIMEOUT_CMD='busybox timeout' + return + fi + fail "timeout command not available" +} + +run_with_timeout() { + sec=$1 + shift + $TIMEOUT_CMD "$sec" "$@" +} + +cleanup_nginx() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +prepare_packages() { + repo_file=/etc/apk/repositories + original_repos="$(cat "$repo_file")" + for mirror in https://mirrors.cernet.edu.cn/alpine https://dl-cdn.alpinelinux.org/alpine; do + printf '%s\n' "$original_repos" | sed "s#http://[^/]*/alpine/#$mirror/#g;s#https://[^/]*/alpine/#$mirror/#g" > "$repo_file" + rm -f /lib/apk/db/lock + if run_with_timeout 40 apk --timeout 40 update && run_with_timeout 40 apk --timeout 40 add nginx curl busybox-extras procps; then + return 0 + fi + done + return 1 +} + +prepare_files() { + rm -rf "$BASE" + mkdir -p "$WWW" "$CONF_DIR" "$OUT" "$LOG_DIR" + printf 'PHASE1_OK\n' > "$WWW/index.html" + cat > "$CONF_DIR/single.conf" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase1/logs/error-single.log debug; +pid /tmp/nginx-phase1/nginx-single.pid; +events { worker_connections 64; } +http { include /etc/nginx/mime.types; access_log /tmp/nginx-phase1/logs/access-single.log; server { listen 127.0.0.1:8080; root /tmp/nginx-phase1/www; location / { index index.html; } } } +EOF + cat > "$CONF_DIR/master1.conf" <<'EOF' +daemon off; +master_process on; +worker_processes 1; +error_log /tmp/nginx-phase1/logs/error-master1.log debug; +pid /tmp/nginx-phase1/nginx-master1.pid; +events { worker_connections 64; } +http { include /etc/nginx/mime.types; access_log /tmp/nginx-phase1/logs/access-master1.log; server { listen 127.0.0.1:8081; root /tmp/nginx-phase1/www; location / { index index.html; } } } +EOF + cat > "$CONF_DIR/master2.conf" <<'EOF' +daemon off; +master_process on; +worker_processes 2; +error_log /tmp/nginx-phase1/logs/error-master2.log debug; +pid /tmp/nginx-phase1/nginx-master2.pid; +events { worker_connections 128; } +http { include /etc/nginx/mime.types; access_log /tmp/nginx-phase1/logs/access-master2.log; server { listen 127.0.0.1:8082; root /tmp/nginx-phase1/www; location / { index index.html; } } } +EOF +} + +wait_http_ok() { + url=$1 + i=0 + while [ "$i" -lt 6 ]; do + if run_with_timeout 1 curl -fsS "$url" -o "$OUT/tmp.body" >/dev/null 2>&1; then + return 0 + fi + sleep 1 + i=$((i + 1)) + done + return 1 +} + +test_master2_known_issue() { + nginx -t -c "$CONF_DIR/master2.conf" -p "$BASE/" || return 1 + nginx -c "$CONF_DIR/master2.conf" -p "$BASE/" > "$LOG_DIR/master2.stdout" 2>&1 & + wait_http_ok http://127.0.0.1:8082/ || { log "KNOWN_ISSUE: phase1.3 startup block"; cleanup_nginx; return 0; } + workers=$(ps -ef | grep 'nginx: worker process' | grep -v grep | wc -l) + log "phase1.3 worker_count=$workers" + i=1 + while [ "$i" -le 3 ]; do + run_with_timeout 1 curl -fsS http://127.0.0.1:8082/ -o "$OUT/m2-$i.body" >/dev/null 2>&1 || { + log "KNOWN_ISSUE: phase1.3 request block at $i" + cleanup_nginx + return 0 + } + i=$((i + 1)) + done + run_with_timeout 2 nginx -s quit -c "$CONF_DIR/master2.conf" -p "$BASE/" >/dev/null 2>&1 || { + log "KNOWN_ISSUE: phase1.3 quit block" + cleanup_nginx + return 0 + } + return 0 +} + +init_timeout_cmd +( sleep 90; log "watchdog timeout"; kill -TERM $$ ) & +prepare_packages || fail "prepare packages" +prepare_files || fail "prepare files" +nginx -t -c "$CONF_DIR/single.conf" -p "$BASE/" || fail "single config" +nginx -c "$CONF_DIR/single.conf" -p "$BASE/" > "$LOG_DIR/single.stdout" 2>&1 & +wait_http_ok http://127.0.0.1:8080/ || fail "phase1.1" +cleanup_nginx +nginx -t -c "$CONF_DIR/master1.conf" -p "$BASE/" || fail "master1 config" +nginx -c "$CONF_DIR/master1.conf" -p "$BASE/" > "$LOG_DIR/master1.stdout" 2>&1 & +wait_http_ok http://127.0.0.1:8081/ || fail "phase1.2" +run_with_timeout 2 nginx -s quit -c "$CONF_DIR/master1.conf" -p "$BASE/" >/dev/null 2>&1 || fail "master1 quit" +test_master2_known_issue || fail "phase1.3" +cleanup_nginx +printf 'NGINX_PHASE1_TEST_PASSED\n' diff --git a/apps/starry/nginx/phase/nginx-2-0-http-basic-tests.sh b/apps/starry/nginx/phase/nginx-2-0-http-basic-tests.sh new file mode 100644 index 0000000000..0cee77f74a --- /dev/null +++ b/apps/starry/nginx/phase/nginx-2-0-http-basic-tests.sh @@ -0,0 +1,75 @@ +#!/bin/sh +set -eu + +BASE=/tmp/nginx-phase2 +CONF="$BASE/conf/http-basic.conf" +WWW="$BASE/www" +OUT="$BASE/out" +LOGDIR="$BASE/logs" +TIMEOUT_CMD= + +log() { printf 'NGINX_PHASE2_LOG: %s\n' "$*"; } +fail() { printf 'NGINX_PHASE2_TEST_FAILED\n'; log "$*"; exit 1; } + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then TIMEOUT_CMD='timeout'; return; fi + if busybox timeout 2>&1 | grep -qi 'usage'; then TIMEOUT_CMD='busybox timeout'; return; fi + fail "timeout command not available" +} + +run_with_timeout() { sec=$1; shift; $TIMEOUT_CMD "$sec" "$@"; } + +cleanup_nginx() { killall -q nginx 2>/dev/null || true; sleep 1; killall -q -9 nginx 2>/dev/null || true; } + +prepare_packages() { + repo_file=/etc/apk/repositories + original_repos="$(cat "$repo_file")" + for mirror in https://mirrors.cernet.edu.cn/alpine https://dl-cdn.alpinelinux.org/alpine; do + printf '%s\n' "$original_repos" | sed "s#http://[^/]*/alpine/#$mirror/#g;s#https://[^/]*/alpine/#$mirror/#g" > "$repo_file" + rm -f /lib/apk/db/lock + if run_with_timeout 40 apk --timeout 40 update && run_with_timeout 40 apk --timeout 40 add nginx curl busybox-extras; then return 0; fi + done + return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$WWW/dir" "$LOGDIR" "$OUT" + printf 'small static file\n' > "$WWW/small.txt" + : > "$WWW/empty.txt" + printf 'HTTP_BASIC_DIR_INDEX_OK\n' > "$WWW/dir/index.html" + cat > "$CONF" <<'EOF' +daemon off; +master_process off; +worker_processes 1; +error_log /tmp/nginx-phase2/logs/error.log debug; +pid /tmp/nginx-phase2/nginx.pid; +events { worker_connections 64; } +http { include /etc/nginx/mime.types; access_log /tmp/nginx-phase2/logs/access.log; server { listen 127.0.0.1:8080; root /tmp/nginx-phase2/www; location / { index index.html; } } } +EOF +} + +start_nginx() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx-stdout.log" 2>&1 & + i=0; while [ "$i" -lt 6 ]; do run_with_timeout 1 curl -fsS -o /dev/null http://127.0.0.1:8080/ >/dev/null 2>&1 && return 0; i=$((i+1)); sleep 1; done + return 1 +} + +init_timeout_cmd +( sleep 90; log "watchdog timeout"; kill -TERM $$ ) & +prepare_packages || fail "prepare packages" +prepare_tree || fail "prepare tree" +start_nginx || fail "start nginx" +code=$(run_with_timeout 1 curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/small.txt || true); [ "$code" = "200" ] || fail "GET /small.txt" +code=$(run_with_timeout 1 curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/empty.txt || true); [ "$code" = "200" ] || fail "GET /empty.txt" +code=$(run_with_timeout 1 curl -sS -o "$OUT/dir.body" -w '%{http_code}' http://127.0.0.1:8080/dir/ || true); [ "$code" = "200" ] && grep -qx 'HTTP_BASIC_DIR_INDEX_OK' "$OUT/dir.body" || fail "GET /dir/" +code=$(run_with_timeout 1 curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/dir || true); [ "$code" = "301" ] || [ "$code" = "302" ] || fail "GET /dir" +if command -v nc >/dev/null 2>&1 || busybox nc 2>&1 | grep -qi 'usage'; then + NC='nc'; command -v nc >/dev/null 2>&1 || NC='busybox nc' + { printf 'BAD / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n'; } | run_with_timeout 1 sh -c "$NC 127.0.0.1 8080" > "$OUT/bad.raw" || true + tr -d '\r' < "$OUT/bad.raw" > "$OUT/bad.norm" + grep -Eq '^HTTP/1.1 (400|405)' "$OUT/bad.norm" || log "KNOWN_ISSUE: BAD method raw request path unstable or blocked" +fi +cleanup_nginx +printf 'NGINX_PHASE2_TEST_PASSED\n' diff --git a/apps/starry/nginx/prebuild.sh b/apps/starry/nginx/prebuild.sh index 42a7b410bf..56551ed436 100644 --- a/apps/starry/nginx/prebuild.sh +++ b/apps/starry/nginx/prebuild.sh @@ -9,5 +9,5 @@ if [[ -z "$overlay_dir" ]]; then exit 1 fi -install -Dm0755 "$app_dir/nginx-smoke-tests.sh" "$overlay_dir/usr/bin/nginx-smoke-tests.sh" +install -Dm0755 "$app_dir/smoke/nginx-smoke-tests.sh" "$overlay_dir/usr/bin/nginx-smoke-tests.sh" install -Dm0755 "$app_dir/nginx-alpine-mirror.sh" "$overlay_dir/usr/bin/nginx-alpine-mirror.sh" diff --git a/apps/starry/nginx/qemu-riscv64.toml b/apps/starry/nginx/qemu-riscv64.toml new file mode 100644 index 0000000000..a6aacc72b3 --- /dev/null +++ b/apps/starry/nginx/qemu-riscv64.toml @@ -0,0 +1,22 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "rv64", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-riscv64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-smoke-tests.sh" +success_regex = ["(?m)^NGINX_APP_SMOKE_PASSED\\s*$"] +fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^NGINX_APP_SMOKE_FAILED"] +timeout = 3600 diff --git a/apps/starry/nginx/nginx-smoke-tests.sh b/apps/starry/nginx/smoke/nginx-smoke-tests.sh similarity index 100% rename from apps/starry/nginx/nginx-smoke-tests.sh rename to apps/starry/nginx/smoke/nginx-smoke-tests.sh diff --git a/apps/starry/nginx/stress/README.md b/apps/starry/nginx/stress/README.md new file mode 100644 index 0000000000..8c27a3b63d --- /dev/null +++ b/apps/starry/nginx/stress/README.md @@ -0,0 +1,19 @@ +# Nginx Stress Tests + +This directory is reserved for stress and pressure tests. + +Current status: + +- No stress script is integrated yet. +- Planned items moved out from phase tracking: + - concurrent 2, 100 requests + - concurrent 8, 1000 requests + - concurrent 32, 5000 requests + - keep-alive concurrent connections + - large file concurrent download + - mixed 200/404/range/large traffic + +Management rule: + +- Stress tests are managed separately from phase tests. +- Stress tests are not connected to tgoskits CI test entry for nginx. From 6f8d79e0d2eae2ac4ab535c88b84628fd4ac6032 Mon Sep 17 00:00:00 2001 From: Antarecke <2395326872@qq.com> Date: Thu, 28 May 2026 14:13:58 +0000 Subject: [PATCH 3/5] fix(starry,nginx): fix multi-worker signal/epoll lifecycle and add debug regressions --- .../build-riscv64gc-unknown-none-elf.toml | 1 - .../nginx/build-x86_64-unknown-none.toml | 1 - apps/starry/nginx/debug/README.md | 4 + .../debug/nginx-multiworker-hang-analysis.md | 33 ++++ .../debug/nginx-multiworker-quit-tests.sh | 151 ++++++++++++++++++ .../nginx/debug/qemu-riscv64-multiworker.toml | 22 +++ .../nginx/debug/qemu-x86_64-multiworker.toml | 20 +++ .../nginx/debug/qemu-x86_64-phase1.toml | 20 +++ apps/starry/nginx/prebuild.sh | 2 + os/StarryOS/kernel/src/file/epoll.rs | 3 +- os/StarryOS/kernel/src/task/signal.rs | 8 +- 11 files changed, 259 insertions(+), 6 deletions(-) create mode 100644 apps/starry/nginx/debug/nginx-multiworker-hang-analysis.md create mode 100644 apps/starry/nginx/debug/nginx-multiworker-quit-tests.sh create mode 100644 apps/starry/nginx/debug/qemu-riscv64-multiworker.toml create mode 100644 apps/starry/nginx/debug/qemu-x86_64-multiworker.toml create mode 100644 apps/starry/nginx/debug/qemu-x86_64-phase1.toml diff --git a/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml b/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml index 66c5d1e30d..894fe831e5 100644 --- a/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml +++ b/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml @@ -4,7 +4,6 @@ log = "Warn" features = [ "ax-hal/riscv64-qemu-virt", "qemu", - "ax-driver/pci", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", diff --git a/apps/starry/nginx/build-x86_64-unknown-none.toml b/apps/starry/nginx/build-x86_64-unknown-none.toml index 94d8986bbb..70fa7c7f44 100644 --- a/apps/starry/nginx/build-x86_64-unknown-none.toml +++ b/apps/starry/nginx/build-x86_64-unknown-none.toml @@ -4,7 +4,6 @@ log = "Warn" features = [ "ax-hal/x86-pc", "qemu", - "ax-driver/pci", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", diff --git a/apps/starry/nginx/debug/README.md b/apps/starry/nginx/debug/README.md index 44f936a83f..f6dd86dbfe 100644 --- a/apps/starry/nginx/debug/README.md +++ b/apps/starry/nginx/debug/README.md @@ -5,6 +5,10 @@ This directory stores flexible debug scripts for single issue reproduction and d Current scripts: - `nginx-http-basic-tests.sh`: early HTTP basic script kept for issue-level debugging. +- `nginx-multiworker-hang-analysis.md`: root-cause record for Starry multi-worker quit/hang path. +- `nginx-multiworker-quit-tests.sh`: minimal multi-worker quit/reap regression with short timeouts. +- `qemu-riscv64-multiworker.toml`: qemu config that runs the debug multi-worker regression directly. +- `qemu-x86_64-multiworker.toml`: x86_64 qemu config for the same regression. Rule: diff --git a/apps/starry/nginx/debug/nginx-multiworker-hang-analysis.md b/apps/starry/nginx/debug/nginx-multiworker-hang-analysis.md new file mode 100644 index 0000000000..7f16c23d57 --- /dev/null +++ b/apps/starry/nginx/debug/nginx-multiworker-hang-analysis.md @@ -0,0 +1,33 @@ +# Nginx Multi-Worker Hang Analysis (StarryOS) + +## Scope + +- Scenario: `master_process on; worker_processes 2;` in phase1 lifecycle flow. +- Symptom: startup/request/quit path can stall on StarryOS, Linux control run is stable. + +## Root Cause + +- `accept4()` in worker goes through `ax-net-ng` `poll_io(...)`, which is wrapped by + `axtask::future::interruptible(...)`. +- `interruptible(...)` only exits early when the target task carries the interrupt flag + (`task.interrupt()`), then syscall can return `EINTR` and userspace can process pending signals. +- In Starry signal send path, deliverable signals in `send_signal_to_thread` and + `send_signal_to_process` used `ax_task::wake_task(...)` only, not `task.interrupt()`. +- Result: worker blocked in `accept4` is wakeable once but not marked interrupted; + it re-enters pending wait and cannot promptly break for `SIGQUIT`, causing master quit/reap + instability in multi-worker nginx. + +## Fix + +- Replace wake-only behavior with interrupt in both delivery paths: + - `os/StarryOS/kernel/src/task/signal.rs` `send_signal_to_thread` + - `os/StarryOS/kernel/src/task/signal.rs` `send_signal_to_process` +- Keep the blocked-signal/sigwait special wake path unchanged to avoid unrelated `EINTR` noise. + +## Why This Matches The Symptom + +- Multi-worker nginx has workers frequently blocked in `accept4` on shared listen fd. +- Quit path relies on signal-driven unblock and graceful worker exit. +- Without interrupt flag, blocking poll-based syscall does not break deterministically. +- After switching to `task.interrupt()`, blocking accept path can return `EINTR` and + worker exits follow expected signal semantics. diff --git a/apps/starry/nginx/debug/nginx-multiworker-quit-tests.sh b/apps/starry/nginx/debug/nginx-multiworker-quit-tests.sh new file mode 100644 index 0000000000..dad6d4a4e7 --- /dev/null +++ b/apps/starry/nginx/debug/nginx-multiworker-quit-tests.sh @@ -0,0 +1,151 @@ +#!/bin/sh +set -eu + +BASE=/tmp/nginx-multiworker +CONF="$BASE/conf/multi.conf" +LOGDIR="$BASE/logs" +OUT="$BASE/out" +TIMEOUT_CMD= +FAILURES=0 + +. /usr/bin/nginx-alpine-mirror.sh + +log() { printf 'NGINX_MW_LOG: %s\n' "$*"; } +pass() { printf 'NGINX_MW_STEP_PASS: %s\n' "$*"; } +fail() { printf 'NGINX_MW_STEP_FAIL: %s\n' "$*"; FAILURES=$((FAILURES + 1)); } + +run_step() { + name=$1 + shift + log "BEGIN $name" + if "$@"; then + pass "$name" + return 0 + fi + fail "$name" + return 1 +} + +init_timeout_cmd() { + if command -v timeout >/dev/null 2>&1; then + TIMEOUT_CMD='timeout' + return 0 + fi + if busybox timeout 2>&1 | grep -qi 'usage'; then + TIMEOUT_CMD='busybox timeout' + return 0 + fi + return 1 +} + +run_with_timeout() { + sec=$1 + shift + $TIMEOUT_CMD "$sec" "$@" +} + +cleanup() { + killall -q nginx 2>/dev/null || true + sleep 1 + killall -q -9 nginx 2>/dev/null || true +} + +finish() { + status=$? + cleanup + if [ "$FAILURES" -eq 0 ] && [ "$status" -eq 0 ]; then + printf 'NGINX_MW_TEST_PASSED\n' + exit 0 + fi + printf 'NGINX_MW_TEST_FAILED failures=%s status=%s\n' "$FAILURES" "$status" + exit 1 +} + +trap finish EXIT + +prepare_packages() { + nginx_apk_add_with_fallback nginx curl busybox-extras procps || return 1 +} + +prepare_tree() { + rm -rf "$BASE" + mkdir -p "$BASE/conf" "$BASE/www" "$LOGDIR" "$OUT" + printf 'MW_OK\n' > "$BASE/www/index.html" + cat > "$CONF" <<'EOF' +daemon off; +master_process on; +worker_processes 2; +error_log /tmp/nginx-multiworker/logs/error.log debug; +pid /tmp/nginx-multiworker/nginx.pid; + +events { worker_connections 128; } + +http { + include /etc/nginx/mime.types; + access_log /tmp/nginx-multiworker/logs/access.log; + aio off; + keepalive_timeout 3; + server { + listen 127.0.0.1:8082; + root /tmp/nginx-multiworker/www; + location / { index index.html; } + } +} +EOF +} + +start_master2() { + nginx -t -c "$CONF" -p "$BASE/" || return 1 + nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx.stdout" 2>&1 & + i=0 + while [ "$i" -lt 6 ]; do + if run_with_timeout 1 curl -fsS http://127.0.0.1:8082/ -o "$OUT/start.body" >/dev/null 2>&1; then + return 0 + fi + sleep 1 + i=$((i + 1)) + done + log "startup curl timeout; nginx ps snapshot" + ps -ef | grep nginx | grep -v grep || true + log "error.log tail" + tail -n 80 "$LOGDIR/error.log" || true + return 1 +} + +probe_requests() { + i=1 + while [ "$i" -le 8 ]; do + run_with_timeout 1 curl -fsS http://127.0.0.1:8082/ -o "$OUT/req-$i.body" >/dev/null 2>&1 || return 1 + i=$((i + 1)) + done + return 0 +} + +probe_workers() { + ps -ef > "$OUT/ps.snapshot" + workers=$(grep 'nginx: worker process' "$OUT/ps.snapshot" | grep -v grep | wc -l) + log "worker_count=$workers" + log "ps snapshot" + cat "$OUT/ps.snapshot" + return 0 +} + +quit_and_reap() { + run_with_timeout 2 nginx -s quit -c "$CONF" -p "$BASE/" >/dev/null 2>&1 || return 1 + i=0 + while [ "$i" -lt 4 ]; do + left=$(ps -ef | grep 'nginx: worker process' | grep -v grep | wc -l) + [ "$left" -eq 0 ] && return 0 + sleep 1 + i=$((i + 1)) + done + return 1 +} + +run_step "init timeout helper" init_timeout_cmd || exit 1 +run_step "prepare packages" prepare_packages || exit 1 +run_step "prepare files" prepare_tree || exit 1 +run_step "start master+2worker" start_master2 || exit 1 +run_step "observe worker count" probe_workers || exit 1 +run_step "8 quick requests" probe_requests || exit 1 +run_step "quit and no worker residue" quit_and_reap || exit 1 diff --git a/apps/starry/nginx/debug/qemu-riscv64-multiworker.toml b/apps/starry/nginx/debug/qemu-riscv64-multiworker.toml new file mode 100644 index 0000000000..2b23845620 --- /dev/null +++ b/apps/starry/nginx/debug/qemu-riscv64-multiworker.toml @@ -0,0 +1,22 @@ +args = [ + "-nographic", + "-m", + "512M", + "-cpu", + "rv64", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-riscv64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-multiworker-quit-tests.sh" +success_regex = ["(?m)^NGINX_MW_TEST_PASSED\\s*$"] +fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^NGINX_MW_TEST_FAILED"] +timeout = 180 diff --git a/apps/starry/nginx/debug/qemu-x86_64-multiworker.toml b/apps/starry/nginx/debug/qemu-x86_64-multiworker.toml new file mode 100644 index 0000000000..9011ba7bb4 --- /dev/null +++ b/apps/starry/nginx/debug/qemu-x86_64-multiworker.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "512M", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-multiworker-quit-tests.sh" +success_regex = ["(?m)^NGINX_MW_TEST_PASSED\\s*$"] +fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^NGINX_MW_TEST_FAILED"] +timeout = 180 diff --git a/apps/starry/nginx/debug/qemu-x86_64-phase1.toml b/apps/starry/nginx/debug/qemu-x86_64-phase1.toml new file mode 100644 index 0000000000..31c71646c5 --- /dev/null +++ b/apps/starry/nginx/debug/qemu-x86_64-phase1.toml @@ -0,0 +1,20 @@ +args = [ + "-nographic", + "-m", + "512M", + "-device", + "virtio-blk-pci,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + "-device", + "virtio-net-pci,netdev=net0", + "-netdev", + "user,id=net0", +] +uefi = false +to_bin = false +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/nginx-phase1-tests.sh" +success_regex = ["(?m)^NGINX_PHASE1_TEST_PASSED\\s*$"] +fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^NGINX_PHASE1_TEST_FAILED"] +timeout = 240 diff --git a/apps/starry/nginx/prebuild.sh b/apps/starry/nginx/prebuild.sh index 56551ed436..d31950692e 100644 --- a/apps/starry/nginx/prebuild.sh +++ b/apps/starry/nginx/prebuild.sh @@ -11,3 +11,5 @@ fi install -Dm0755 "$app_dir/smoke/nginx-smoke-tests.sh" "$overlay_dir/usr/bin/nginx-smoke-tests.sh" install -Dm0755 "$app_dir/nginx-alpine-mirror.sh" "$overlay_dir/usr/bin/nginx-alpine-mirror.sh" +install -Dm0755 "$app_dir/debug/nginx-multiworker-quit-tests.sh" "$overlay_dir/usr/bin/nginx-multiworker-quit-tests.sh" +install -Dm0755 "$app_dir/phase/nginx-1-3-lifecycle-tests.sh" "$overlay_dir/usr/bin/nginx-phase1-tests.sh" diff --git a/os/StarryOS/kernel/src/file/epoll.rs b/os/StarryOS/kernel/src/file/epoll.rs index 04f7a32ed3..eee0f4a059 100644 --- a/os/StarryOS/kernel/src/file/epoll.rs +++ b/os/StarryOS/kernel/src/file/epoll.rs @@ -23,7 +23,7 @@ use ax_kspin::SpinNoIrq; use axpoll::{IoEvents, PollSet, Pollable}; use bitflags::bitflags; use hashbrown::HashMap; -use linux_raw_sys::general::{EPOLLET, EPOLLONESHOT, epoll_event}; +use linux_raw_sys::general::{EPOLLET, EPOLLEXCLUSIVE, EPOLLONESHOT, epoll_event}; use crate::file::{FileLike, get_file_like}; @@ -38,6 +38,7 @@ bitflags! { pub struct EpollFlags: u32 { const EDGE_TRIGGER = EPOLLET; const ONESHOT = EPOLLONESHOT; + const EXCLUSIVE = EPOLLEXCLUSIVE; } } diff --git a/os/StarryOS/kernel/src/task/signal.rs b/os/StarryOS/kernel/src/task/signal.rs index 759199cdff..3f2336057f 100644 --- a/os/StarryOS/kernel/src/task/signal.rs +++ b/os/StarryOS/kernel/src/task/signal.rs @@ -472,7 +472,7 @@ pub fn send_signal_to_thread(tgid: Option, tid: Pid, sig: Option) -> AxResult<()> proc_data.clear_ptrace_stop(); } if let Some(tid) = proc_data.signal.send_signal(sig) { - // A thread was found that doesn't have the signal blocked — wake it. + // A thread was found that doesn't have the signal blocked. + // Mark it interrupted so blocking syscalls wrapped by + // `future::interruptible` can return EINTR promptly. if let Ok(task) = get_task(tid) { - ax_task::wake_task(&task); + task.interrupt(); } } else { // All threads have this signal blocked — the signal is now pending From 14fe46e2bd1c9bbf5061611e374c22d86d4424b2 Mon Sep 17 00:00:00 2001 From: Antarecke <2395326872@qq.com> Date: Thu, 28 May 2026 14:34:39 +0000 Subject: [PATCH 4/5] Revert "fix(starry,nginx): fix multi-worker signal/epoll lifecycle and add debug regressions" This reverts commit 6f8d79e0d2eae2ac4ab535c88b84628fd4ac6032. --- .../build-riscv64gc-unknown-none-elf.toml | 1 + .../nginx/build-x86_64-unknown-none.toml | 1 + apps/starry/nginx/debug/README.md | 4 - .../debug/nginx-multiworker-hang-analysis.md | 33 ---- .../debug/nginx-multiworker-quit-tests.sh | 151 ------------------ .../nginx/debug/qemu-riscv64-multiworker.toml | 22 --- .../nginx/debug/qemu-x86_64-multiworker.toml | 20 --- .../nginx/debug/qemu-x86_64-phase1.toml | 20 --- apps/starry/nginx/prebuild.sh | 2 - os/StarryOS/kernel/src/file/epoll.rs | 3 +- os/StarryOS/kernel/src/task/signal.rs | 8 +- 11 files changed, 6 insertions(+), 259 deletions(-) delete mode 100644 apps/starry/nginx/debug/nginx-multiworker-hang-analysis.md delete mode 100644 apps/starry/nginx/debug/nginx-multiworker-quit-tests.sh delete mode 100644 apps/starry/nginx/debug/qemu-riscv64-multiworker.toml delete mode 100644 apps/starry/nginx/debug/qemu-x86_64-multiworker.toml delete mode 100644 apps/starry/nginx/debug/qemu-x86_64-phase1.toml diff --git a/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml b/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml index 894fe831e5..66c5d1e30d 100644 --- a/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml +++ b/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml @@ -4,6 +4,7 @@ log = "Warn" features = [ "ax-hal/riscv64-qemu-virt", "qemu", + "ax-driver/pci", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", diff --git a/apps/starry/nginx/build-x86_64-unknown-none.toml b/apps/starry/nginx/build-x86_64-unknown-none.toml index 70fa7c7f44..94d8986bbb 100644 --- a/apps/starry/nginx/build-x86_64-unknown-none.toml +++ b/apps/starry/nginx/build-x86_64-unknown-none.toml @@ -4,6 +4,7 @@ log = "Warn" features = [ "ax-hal/x86-pc", "qemu", + "ax-driver/pci", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", diff --git a/apps/starry/nginx/debug/README.md b/apps/starry/nginx/debug/README.md index f6dd86dbfe..44f936a83f 100644 --- a/apps/starry/nginx/debug/README.md +++ b/apps/starry/nginx/debug/README.md @@ -5,10 +5,6 @@ This directory stores flexible debug scripts for single issue reproduction and d Current scripts: - `nginx-http-basic-tests.sh`: early HTTP basic script kept for issue-level debugging. -- `nginx-multiworker-hang-analysis.md`: root-cause record for Starry multi-worker quit/hang path. -- `nginx-multiworker-quit-tests.sh`: minimal multi-worker quit/reap regression with short timeouts. -- `qemu-riscv64-multiworker.toml`: qemu config that runs the debug multi-worker regression directly. -- `qemu-x86_64-multiworker.toml`: x86_64 qemu config for the same regression. Rule: diff --git a/apps/starry/nginx/debug/nginx-multiworker-hang-analysis.md b/apps/starry/nginx/debug/nginx-multiworker-hang-analysis.md deleted file mode 100644 index 7f16c23d57..0000000000 --- a/apps/starry/nginx/debug/nginx-multiworker-hang-analysis.md +++ /dev/null @@ -1,33 +0,0 @@ -# Nginx Multi-Worker Hang Analysis (StarryOS) - -## Scope - -- Scenario: `master_process on; worker_processes 2;` in phase1 lifecycle flow. -- Symptom: startup/request/quit path can stall on StarryOS, Linux control run is stable. - -## Root Cause - -- `accept4()` in worker goes through `ax-net-ng` `poll_io(...)`, which is wrapped by - `axtask::future::interruptible(...)`. -- `interruptible(...)` only exits early when the target task carries the interrupt flag - (`task.interrupt()`), then syscall can return `EINTR` and userspace can process pending signals. -- In Starry signal send path, deliverable signals in `send_signal_to_thread` and - `send_signal_to_process` used `ax_task::wake_task(...)` only, not `task.interrupt()`. -- Result: worker blocked in `accept4` is wakeable once but not marked interrupted; - it re-enters pending wait and cannot promptly break for `SIGQUIT`, causing master quit/reap - instability in multi-worker nginx. - -## Fix - -- Replace wake-only behavior with interrupt in both delivery paths: - - `os/StarryOS/kernel/src/task/signal.rs` `send_signal_to_thread` - - `os/StarryOS/kernel/src/task/signal.rs` `send_signal_to_process` -- Keep the blocked-signal/sigwait special wake path unchanged to avoid unrelated `EINTR` noise. - -## Why This Matches The Symptom - -- Multi-worker nginx has workers frequently blocked in `accept4` on shared listen fd. -- Quit path relies on signal-driven unblock and graceful worker exit. -- Without interrupt flag, blocking poll-based syscall does not break deterministically. -- After switching to `task.interrupt()`, blocking accept path can return `EINTR` and - worker exits follow expected signal semantics. diff --git a/apps/starry/nginx/debug/nginx-multiworker-quit-tests.sh b/apps/starry/nginx/debug/nginx-multiworker-quit-tests.sh deleted file mode 100644 index dad6d4a4e7..0000000000 --- a/apps/starry/nginx/debug/nginx-multiworker-quit-tests.sh +++ /dev/null @@ -1,151 +0,0 @@ -#!/bin/sh -set -eu - -BASE=/tmp/nginx-multiworker -CONF="$BASE/conf/multi.conf" -LOGDIR="$BASE/logs" -OUT="$BASE/out" -TIMEOUT_CMD= -FAILURES=0 - -. /usr/bin/nginx-alpine-mirror.sh - -log() { printf 'NGINX_MW_LOG: %s\n' "$*"; } -pass() { printf 'NGINX_MW_STEP_PASS: %s\n' "$*"; } -fail() { printf 'NGINX_MW_STEP_FAIL: %s\n' "$*"; FAILURES=$((FAILURES + 1)); } - -run_step() { - name=$1 - shift - log "BEGIN $name" - if "$@"; then - pass "$name" - return 0 - fi - fail "$name" - return 1 -} - -init_timeout_cmd() { - if command -v timeout >/dev/null 2>&1; then - TIMEOUT_CMD='timeout' - return 0 - fi - if busybox timeout 2>&1 | grep -qi 'usage'; then - TIMEOUT_CMD='busybox timeout' - return 0 - fi - return 1 -} - -run_with_timeout() { - sec=$1 - shift - $TIMEOUT_CMD "$sec" "$@" -} - -cleanup() { - killall -q nginx 2>/dev/null || true - sleep 1 - killall -q -9 nginx 2>/dev/null || true -} - -finish() { - status=$? - cleanup - if [ "$FAILURES" -eq 0 ] && [ "$status" -eq 0 ]; then - printf 'NGINX_MW_TEST_PASSED\n' - exit 0 - fi - printf 'NGINX_MW_TEST_FAILED failures=%s status=%s\n' "$FAILURES" "$status" - exit 1 -} - -trap finish EXIT - -prepare_packages() { - nginx_apk_add_with_fallback nginx curl busybox-extras procps || return 1 -} - -prepare_tree() { - rm -rf "$BASE" - mkdir -p "$BASE/conf" "$BASE/www" "$LOGDIR" "$OUT" - printf 'MW_OK\n' > "$BASE/www/index.html" - cat > "$CONF" <<'EOF' -daemon off; -master_process on; -worker_processes 2; -error_log /tmp/nginx-multiworker/logs/error.log debug; -pid /tmp/nginx-multiworker/nginx.pid; - -events { worker_connections 128; } - -http { - include /etc/nginx/mime.types; - access_log /tmp/nginx-multiworker/logs/access.log; - aio off; - keepalive_timeout 3; - server { - listen 127.0.0.1:8082; - root /tmp/nginx-multiworker/www; - location / { index index.html; } - } -} -EOF -} - -start_master2() { - nginx -t -c "$CONF" -p "$BASE/" || return 1 - nginx -c "$CONF" -p "$BASE/" > "$LOGDIR/nginx.stdout" 2>&1 & - i=0 - while [ "$i" -lt 6 ]; do - if run_with_timeout 1 curl -fsS http://127.0.0.1:8082/ -o "$OUT/start.body" >/dev/null 2>&1; then - return 0 - fi - sleep 1 - i=$((i + 1)) - done - log "startup curl timeout; nginx ps snapshot" - ps -ef | grep nginx | grep -v grep || true - log "error.log tail" - tail -n 80 "$LOGDIR/error.log" || true - return 1 -} - -probe_requests() { - i=1 - while [ "$i" -le 8 ]; do - run_with_timeout 1 curl -fsS http://127.0.0.1:8082/ -o "$OUT/req-$i.body" >/dev/null 2>&1 || return 1 - i=$((i + 1)) - done - return 0 -} - -probe_workers() { - ps -ef > "$OUT/ps.snapshot" - workers=$(grep 'nginx: worker process' "$OUT/ps.snapshot" | grep -v grep | wc -l) - log "worker_count=$workers" - log "ps snapshot" - cat "$OUT/ps.snapshot" - return 0 -} - -quit_and_reap() { - run_with_timeout 2 nginx -s quit -c "$CONF" -p "$BASE/" >/dev/null 2>&1 || return 1 - i=0 - while [ "$i" -lt 4 ]; do - left=$(ps -ef | grep 'nginx: worker process' | grep -v grep | wc -l) - [ "$left" -eq 0 ] && return 0 - sleep 1 - i=$((i + 1)) - done - return 1 -} - -run_step "init timeout helper" init_timeout_cmd || exit 1 -run_step "prepare packages" prepare_packages || exit 1 -run_step "prepare files" prepare_tree || exit 1 -run_step "start master+2worker" start_master2 || exit 1 -run_step "observe worker count" probe_workers || exit 1 -run_step "8 quick requests" probe_requests || exit 1 -run_step "quit and no worker residue" quit_and_reap || exit 1 diff --git a/apps/starry/nginx/debug/qemu-riscv64-multiworker.toml b/apps/starry/nginx/debug/qemu-riscv64-multiworker.toml deleted file mode 100644 index 2b23845620..0000000000 --- a/apps/starry/nginx/debug/qemu-riscv64-multiworker.toml +++ /dev/null @@ -1,22 +0,0 @@ -args = [ - "-nographic", - "-m", - "512M", - "-cpu", - "rv64", - "-device", - "virtio-blk-pci,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-riscv64-alpine.img", - "-device", - "virtio-net-pci,netdev=net0", - "-netdev", - "user,id=net0", -] -uefi = false -to_bin = true -shell_prefix = "root@starry:" -shell_init_cmd = "/usr/bin/nginx-multiworker-quit-tests.sh" -success_regex = ["(?m)^NGINX_MW_TEST_PASSED\\s*$"] -fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^NGINX_MW_TEST_FAILED"] -timeout = 180 diff --git a/apps/starry/nginx/debug/qemu-x86_64-multiworker.toml b/apps/starry/nginx/debug/qemu-x86_64-multiworker.toml deleted file mode 100644 index 9011ba7bb4..0000000000 --- a/apps/starry/nginx/debug/qemu-x86_64-multiworker.toml +++ /dev/null @@ -1,20 +0,0 @@ -args = [ - "-nographic", - "-m", - "512M", - "-device", - "virtio-blk-pci,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", - "-device", - "virtio-net-pci,netdev=net0", - "-netdev", - "user,id=net0", -] -uefi = false -to_bin = false -shell_prefix = "root@starry:" -shell_init_cmd = "/usr/bin/nginx-multiworker-quit-tests.sh" -success_regex = ["(?m)^NGINX_MW_TEST_PASSED\\s*$"] -fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^NGINX_MW_TEST_FAILED"] -timeout = 180 diff --git a/apps/starry/nginx/debug/qemu-x86_64-phase1.toml b/apps/starry/nginx/debug/qemu-x86_64-phase1.toml deleted file mode 100644 index 31c71646c5..0000000000 --- a/apps/starry/nginx/debug/qemu-x86_64-phase1.toml +++ /dev/null @@ -1,20 +0,0 @@ -args = [ - "-nographic", - "-m", - "512M", - "-device", - "virtio-blk-pci,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", - "-device", - "virtio-net-pci,netdev=net0", - "-netdev", - "user,id=net0", -] -uefi = false -to_bin = false -shell_prefix = "root@starry:" -shell_init_cmd = "/usr/bin/nginx-phase1-tests.sh" -success_regex = ["(?m)^NGINX_PHASE1_TEST_PASSED\\s*$"] -fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^NGINX_PHASE1_TEST_FAILED"] -timeout = 240 diff --git a/apps/starry/nginx/prebuild.sh b/apps/starry/nginx/prebuild.sh index d31950692e..56551ed436 100644 --- a/apps/starry/nginx/prebuild.sh +++ b/apps/starry/nginx/prebuild.sh @@ -11,5 +11,3 @@ fi install -Dm0755 "$app_dir/smoke/nginx-smoke-tests.sh" "$overlay_dir/usr/bin/nginx-smoke-tests.sh" install -Dm0755 "$app_dir/nginx-alpine-mirror.sh" "$overlay_dir/usr/bin/nginx-alpine-mirror.sh" -install -Dm0755 "$app_dir/debug/nginx-multiworker-quit-tests.sh" "$overlay_dir/usr/bin/nginx-multiworker-quit-tests.sh" -install -Dm0755 "$app_dir/phase/nginx-1-3-lifecycle-tests.sh" "$overlay_dir/usr/bin/nginx-phase1-tests.sh" diff --git a/os/StarryOS/kernel/src/file/epoll.rs b/os/StarryOS/kernel/src/file/epoll.rs index eee0f4a059..04f7a32ed3 100644 --- a/os/StarryOS/kernel/src/file/epoll.rs +++ b/os/StarryOS/kernel/src/file/epoll.rs @@ -23,7 +23,7 @@ use ax_kspin::SpinNoIrq; use axpoll::{IoEvents, PollSet, Pollable}; use bitflags::bitflags; use hashbrown::HashMap; -use linux_raw_sys::general::{EPOLLET, EPOLLEXCLUSIVE, EPOLLONESHOT, epoll_event}; +use linux_raw_sys::general::{EPOLLET, EPOLLONESHOT, epoll_event}; use crate::file::{FileLike, get_file_like}; @@ -38,7 +38,6 @@ bitflags! { pub struct EpollFlags: u32 { const EDGE_TRIGGER = EPOLLET; const ONESHOT = EPOLLONESHOT; - const EXCLUSIVE = EPOLLEXCLUSIVE; } } diff --git a/os/StarryOS/kernel/src/task/signal.rs b/os/StarryOS/kernel/src/task/signal.rs index 3f2336057f..759199cdff 100644 --- a/os/StarryOS/kernel/src/task/signal.rs +++ b/os/StarryOS/kernel/src/task/signal.rs @@ -472,7 +472,7 @@ pub fn send_signal_to_thread(tgid: Option, tid: Pid, sig: Option) -> AxResult<()> proc_data.clear_ptrace_stop(); } if let Some(tid) = proc_data.signal.send_signal(sig) { - // A thread was found that doesn't have the signal blocked. - // Mark it interrupted so blocking syscalls wrapped by - // `future::interruptible` can return EINTR promptly. + // A thread was found that doesn't have the signal blocked — wake it. if let Ok(task) = get_task(tid) { - task.interrupt(); + ax_task::wake_task(&task); } } else { // All threads have this signal blocked — the signal is now pending From 68491fe6a2ec46ff967a752583d9c0729ab1c89d Mon Sep 17 00:00:00 2001 From: Antarecke <2395326872@qq.com> Date: Thu, 28 May 2026 18:09:55 +0000 Subject: [PATCH 5/5] fix(starry-nginx): align qemu build features --- apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml | 9 +++++---- apps/starry/nginx/build-x86_64-unknown-none.toml | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml b/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml index 66c5d1e30d..337b59787c 100644 --- a/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml +++ b/apps/starry/nginx/build-riscv64gc-unknown-none-elf.toml @@ -2,13 +2,14 @@ target = "riscv64gc-unknown-none-elf" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/riscv64-qemu-virt", - "qemu", - "ax-driver/pci", + "ax-feat/rtc", + "ax-driver/rtc", + "ax-driver/serial", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", + "starry-kernel/input", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/nginx/build-x86_64-unknown-none.toml b/apps/starry/nginx/build-x86_64-unknown-none.toml index 94d8986bbb..48f79a5df5 100644 --- a/apps/starry/nginx/build-x86_64-unknown-none.toml +++ b/apps/starry/nginx/build-x86_64-unknown-none.toml @@ -4,7 +4,7 @@ log = "Warn" features = [ "ax-hal/x86-pc", "qemu", - "ax-driver/pci", + "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu",