Rebrand project as Forkop

This commit is contained in:
ushan0v
2026-07-11 18:23:20 +03:00
parent dc052f03a7
commit b5f2eb3724
362 changed files with 14228 additions and 13950 deletions
+10 -10
View File
@@ -2,12 +2,12 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
CLI_UC="$PODKOP_BIN"
BYEDPI_RUNTIME_SH="$PODKOP_LIB/byedpi.sh"
LIFECYCLE_UC="$PODKOP_LIB/service/lifecycle.uc"
BYEDPI_RUNTIME_UC="$PODKOP_LIB/providers/byedpi/runtime.uc"
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
CLI_UC="$FORKOP_BIN"
BYEDPI_RUNTIME_SH="$FORKOP_LIB/byedpi.sh"
LIFECYCLE_UC="$FORKOP_LIB/service/lifecycle.uc"
BYEDPI_RUNTIME_UC="$FORKOP_LIB/providers/byedpi/runtime.uc"
fail() {
printf 'FAIL: %s\n' "$1" >&2
@@ -17,15 +17,15 @@ fail() {
[ ! -e "$BYEDPI_RUNTIME_SH" ] ||
fail "byedpi.sh shell owner must be removed"
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
fail "podkop entrypoint must be a direct ucode executable"
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
fail "forkop entrypoint must be a direct ucode executable"
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
fail "service/cli.uc must dispatch lifecycle orchestration through service/lifecycle.uc"
grep -Fq 'providers/byedpi/runtime.uc' "$LIFECYCLE_UC" ||
fail "service/lifecycle.uc must call providers/byedpi/runtime.uc for ByeDPI runtime operations"
if grep -R -n -E 'start_byedpi_runtime|stop_byedpi_runtime|get_byedpi_status_json|check_byedpi_runtime_json|is_byedpi_installed|get_byedpi_package_version|get_byedpi_rule_|run_byedpi_supervisor' \
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
fail "ByeDPI runtime shell symbols must not remain"
fi
if grep -n -E 'require\("uci"\)\.cursor|uci -q|"uci", "-q"' "$BYEDPI_RUNTIME_UC" >/dev/null 2>&1; then
@@ -48,7 +48,7 @@ grep -Fq 'mode == "check"' "$BYEDPI_RUNTIME_UC" ||
fail "providers/byedpi/runtime.uc must own ByeDPI check"
BYEDPI_BIN="$ROOT_DIR/tests/missing-ciadpi" \
ucode -L "$PODKOP_LIB" "$BYEDPI_RUNTIME_UC" check |
ucode -L "$FORKOP_LIB" "$BYEDPI_RUNTIME_UC" check |
node -e '
const fs = require("fs");
const value = JSON.parse(fs.readFileSync(0, "utf8"));
+3 -3
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
VALIDATOR="$ROOT_DIR/podkop/files/usr/lib/providers/byedpi/validator.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
VALIDATOR="$ROOT_DIR/forkop/files/usr/lib/providers/byedpi/validator.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -73,6 +73,6 @@ if (validator.strategy_or_default("", "--default\t1") != "--default 1")
exit(1);
UCODE
ucode -L "$PODKOP_LIB" "$WORK_DIR/require-byedpi-validator.uc"
ucode -L "$FORKOP_LIB" "$WORK_DIR/require-byedpi-validator.uc"
printf 'ByeDPI validator checks passed\n'
+24 -24
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -16,23 +16,23 @@ fail() {
exit 1
}
[ ! -e "$PODKOP_LIB/service/cli.uc" ] ||
[ ! -e "$FORKOP_LIB/service/cli.uc" ] ||
fail "service/cli.uc must be removed after /usr/bin ucode entrypoint takeover"
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
fail "podkop entrypoint must be a direct ucode executable"
if grep -Fq '#!/bin/sh' "$PODKOP_BIN" ||
grep -Fq '#!/bin/ash' "$PODKOP_BIN" ||
grep -Fq 'exec ucode' "$PODKOP_BIN"; then
fail "podkop entrypoint must not keep a shell loader"
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
fail "forkop entrypoint must be a direct ucode executable"
if grep -Fq '#!/bin/sh' "$FORKOP_BIN" ||
grep -Fq '#!/bin/ash' "$FORKOP_BIN" ||
grep -Fq 'exec ucode' "$FORKOP_BIN"; then
fail "forkop entrypoint must not keep a shell loader"
fi
if grep -Fq 'PODKOP_COMMAND' "$PODKOP_BIN" ||
grep -Fq 'run_module()' "$PODKOP_BIN"; then
fail "podkop entrypoint must not keep legacy shell routing symbols"
if grep -Fq 'FORKOP_COMMAND' "$FORKOP_BIN" ||
grep -Fq 'run_module()' "$FORKOP_BIN"; then
fail "forkop entrypoint must not keep legacy shell routing symbols"
fi
grep -Fq 'function command_spec(command)' "$PODKOP_BIN" ||
fail "podkop ucode entrypoint must own command routing"
grep -Fq 'function show_help()' "$PODKOP_BIN" ||
fail "podkop ucode entrypoint must own help text"
grep -Fq 'function command_spec(command)' "$FORKOP_BIN" ||
fail "forkop ucode entrypoint must own command routing"
grep -Fq 'function show_help()' "$FORKOP_BIN" ||
fail "forkop ucode entrypoint must own help text"
fake_lib="$WORK_DIR/lib"
mkdir -p "$fake_lib/service" "$fake_lib/diagnostics" "$fake_lib/components" "$fake_lib/dns"
@@ -55,35 +55,35 @@ UCODE
cat >"$fake_lib/dns/apply.uc" <<'UCODE'
#!/usr/bin/env ucode
let fs = require("fs");
let marker = getenv("PODKOP_TEST_DNS_RESTORE_MARKER");
let marker = getenv("FORKOP_TEST_DNS_RESTORE_MARKER");
if (marker != null && marker != "")
fs.writefile(marker, ARGV[0] || "");
UCODE
show_version_out="$(PODKOP_LIB="$fake_lib" ucode "$PODKOP_BIN" show_version)"
show_version_out="$(FORKOP_LIB="$fake_lib" ucode "$FORKOP_BIN" show_version)"
[ "$show_version_out" = $'diagnostics\tshow-version\t\t\t' ] ||
fail "show_version must dispatch through diagnostics/runtime.uc"
subscription_out="$(PODKOP_LIB="$fake_lib" ucode "$PODKOP_BIN" subscription_update proxy 2)"
subscription_out="$(FORKOP_LIB="$fake_lib" ucode "$FORKOP_BIN" subscription_update proxy 2)"
[ "$subscription_out" = $'updates\tsubscription-update\tproxy\t2' ] ||
fail "subscription_update must dispatch through components/updates.uc with arguments"
component_out="$(PODKOP_LIB="$fake_lib" ucode "$PODKOP_BIN" component_action sing_box update)"
component_out="$(FORKOP_LIB="$fake_lib" ucode "$FORKOP_BIN" component_action sing_box update)"
[ "$component_out" = $'action\tcomponent-action\tsing_box\tupdate' ] ||
fail "component_action must dispatch through components/action.uc with arguments"
component_check_cache_out="$(PODKOP_LIB="$fake_lib" ucode "$PODKOP_BIN" component_update_check_cache)"
component_check_cache_out="$(FORKOP_LIB="$fake_lib" ucode "$FORKOP_BIN" component_update_check_cache)"
[ "$component_check_cache_out" = $'updates\tcomponent-update-check-cache\t\t' ] ||
fail "component_update_check_cache must dispatch through components/updates.uc"
component_updates_out="$(PODKOP_LIB="$fake_lib" ucode "$PODKOP_BIN" component_updates_if_due)"
component_updates_out="$(FORKOP_LIB="$fake_lib" ucode "$FORKOP_BIN" component_updates_if_due)"
[ "$component_updates_out" = $'updates\tcomponent-updates-if-due\t\t' ] ||
fail "component_updates_if_due must dispatch through components/updates.uc"
rm -f "$fake_lib/service/lifecycle.uc"
set +e
PODKOP_TEST_DNS_RESTORE_MARKER="$WORK_DIR/dns-restore.marker" PODKOP_LIB="$fake_lib" \
ucode "$PODKOP_BIN" start >/dev/null 2>"$WORK_DIR/missing.err"
FORKOP_TEST_DNS_RESTORE_MARKER="$WORK_DIR/dns-restore.marker" FORKOP_LIB="$fake_lib" \
ucode "$FORKOP_BIN" start >/dev/null 2>"$WORK_DIR/missing.err"
status="$?"
set -e
[ "$status" -ne 0 ] ||
+9 -9
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
UPDATES_UC="$PODKOP_LIB/components/updates.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
UPDATES_UC="$FORKOP_LIB/components/updates.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -59,7 +59,7 @@ UCODE
cat >"$fake_lib/components/action.uc" <<'UCODE'
#!/usr/bin/env ucode
let component = ARGV[1] || "podkop";
let component = ARGV[1] || "forkop";
let action = ARGV[2] || "check_update";
print(sprintf("%J\n", {
success: true,
@@ -89,11 +89,11 @@ UCODE
updates_ucode() {
TEST_COMPONENT_UPDATE_CHECK_ENABLED="${TEST_COMPONENT_UPDATE_CHECK_ENABLED:-1}" \
TEST_COMPONENT_UPDATE_CHECK_INTERVAL="${TEST_COMPONENT_UPDATE_CHECK_INTERVAL:-1d}" \
PODKOP_LIB="$fake_lib" \
PODKOP_COMPONENT_UPDATE_CHECK_CACHE_DIR="$cache_dir" \
PODKOP_COMPONENT_UPDATE_CHECK_STATE_FILE="$timestamp_file" \
FORKOP_LIB="$fake_lib" \
FORKOP_COMPONENT_UPDATE_CHECK_CACHE_DIR="$cache_dir" \
FORKOP_COMPONENT_UPDATE_CHECK_STATE_FILE="$timestamp_file" \
UPDATES_JOB_DIR="$job_dir" \
ucode -L "$fake_lib" -L "$PODKOP_LIB" "$UPDATES_UC" "$@"
ucode -L "$fake_lib" -L "$FORKOP_LIB" "$UPDATES_UC" "$@"
}
updates_ucode component-action-worker "$state_file" "$output_file" zapret check_update
@@ -117,8 +117,8 @@ TEST_COMPONENT_UPDATE_CHECK_ENABLED=0 updates_ucode \
rm -f "$state_file" "$timestamp_file"
updates_ucode component-updates-if-due
[ -s "$cache_dir/podkop.json" ] ||
fail "automatic checks must cache the Podkop Plus result"
[ -s "$cache_dir/forkop.json" ] ||
fail "automatic checks must cache the Forkop result"
[ -s "$timestamp_file" ] ||
fail "automatic checks must record their last run"
+54 -44
View File
@@ -2,12 +2,12 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
CLI_UC="$PODKOP_BIN"
UPDATER="$ROOT_DIR/podkop/files/usr/lib/components/updater.uc"
UPDATES_UC="$ROOT_DIR/podkop/files/usr/lib/components/updates.uc"
ACTION_UC="$ROOT_DIR/podkop/files/usr/lib/components/action.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
CLI_UC="$FORKOP_BIN"
UPDATER="$ROOT_DIR/forkop/files/usr/lib/components/updater.uc"
UPDATES_UC="$ROOT_DIR/forkop/files/usr/lib/components/updates.uc"
ACTION_UC="$ROOT_DIR/forkop/files/usr/lib/components/action.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -70,7 +70,7 @@ if ucode "$UPDATER" updates-check-result-row component 1.0 2.0 invalid >/dev/nul
fail "invalid check result status should be rejected"
fi
[ ! -e "$ROOT_DIR/podkop/files/usr/lib/updater.sh" ] ||
[ ! -e "$ROOT_DIR/forkop/files/usr/lib/updater.sh" ] ||
fail "updater.sh shell owner must be removed"
grep -Fq 'component_action: [ "components/action.uc", "component-action", 2 ]' "$CLI_UC" ||
fail "service/cli.uc must dispatch direct component_action through components/action.uc"
@@ -97,15 +97,15 @@ grep -Fq 'require("core.uci")' "$ACTION_UC" ||
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"|command_exists\("uci"\)' "$ACTION_UC" >/dev/null 2>&1; then
fail "components/action.uc must not own direct UCI cursor or CLI calls"
fi
grep -Fq 'podkop_status_running_with_timeout()' "$ACTION_UC" ||
fail "components/action.uc must use bounded Podkop status checks for component actions"
grep -Fq 'forkop_status_running_with_timeout()' "$ACTION_UC" ||
fail "components/action.uc must use bounded Forkop status checks for component actions"
if grep -Fq 'command_success_from_args([ SERVICE_INIT, "status" ])' "$ACTION_UC"; then
fail "components/action.uc must not call init.d status without a timeout"
fi
status_timeout_line="$(grep -n '^function podkop_status_running_with_timeout()' "$ACTION_UC" | cut -d: -f1)"
capture_state_line="$(grep -n '^function capture_podkop_running_state()' "$ACTION_UC" | cut -d: -f1)"
status_timeout_line="$(grep -n '^function forkop_status_running_with_timeout()' "$ACTION_UC" | cut -d: -f1)"
capture_state_line="$(grep -n '^function capture_forkop_running_state()' "$ACTION_UC" | cut -d: -f1)"
[ -n "$status_timeout_line" ] && [ -n "$capture_state_line" ] && [ "$status_timeout_line" -lt "$capture_state_line" ] ||
fail "components/action.uc must declare bounded status helper before capture_podkop_running_state for OpenWrt ucode"
fail "components/action.uc must declare bounded status helper before capture_forkop_running_state for OpenWrt ucode"
init_line="$(grep -n '^function init_tmp_dir()' "$ACTION_UC" | cut -d: -f1)"
tmp_file_line="$(grep -n '^function make_tmp_file' "$ACTION_UC" | cut -d: -f1)"
[ -n "$init_line" ] && [ -n "$tmp_file_line" ] && [ "$init_line" -lt "$tmp_file_line" ] ||
@@ -183,10 +183,10 @@ chmod +x "$package_runtime_bin/opkg" "$package_runtime_bin/logger"
set +e
PATH="$package_runtime_bin:$PATH" \
PODKOP_LIB="$package_runtime_lib" \
PODKOP_RUNTIME_STATE_DIR="$WORK_DIR/package-runtime" \
PODKOP_BIN="$WORK_DIR/missing-podkop" \
PODKOP_SERVICE_INIT="$WORK_DIR/missing-init" \
FORKOP_LIB="$package_runtime_lib" \
FORKOP_RUNTIME_STATE_DIR="$WORK_DIR/package-runtime" \
FORKOP_BIN="$WORK_DIR/missing-forkop" \
FORKOP_SERVICE_INIT="$WORK_DIR/missing-init" \
FAKE_OPKG_LOG="$WORK_DIR/opkg.log" \
FAKE_OPKG_UPDATED="$WORK_DIR/opkg.updated" \
ucode -L "$package_runtime_lib" "$ACTION_UC" component-action sing_box install_stable >/dev/null
@@ -210,48 +210,58 @@ NODE
release_json="$(cat <<'JSON'
{
"tag_name": "v1.2.3",
"tag_name": "1.2.3",
"html_url": "https://example.com/release",
"assets": [
{"name": "podkop-plus_1.2.3_all.ipk", "browser_download_url": "https://example.com/backend.ipk"},
{"name": "luci-app-podkop-plus_1.2.3_all.ipk", "browser_download_url": "https://example.com/app.ipk"},
{"name": "luci-i18n-podkop-plus-ru_1.2.3_all.ipk", "browser_download_url": "https://example.com/i18n.ipk"}
{"name": "forkop_1.2.3.ipk", "browser_download_url": "https://example.com/backend.ipk"},
{"name": "luci-app-forkop_1.2.3.ipk", "browser_download_url": "https://example.com/app.ipk"},
{"name": "luci-i18n-forkop-ru_1.2.3.ipk", "browser_download_url": "https://example.com/i18n.ipk"}
]
}
JSON
)"
assert_eq "$(printf 'https://example.com/release\tpodkop-plus_1.2.3_all.ipk\thttps://example.com/backend.ipk\tluci-app-podkop-plus_1.2.3_all.ipk\thttps://example.com/app.ipk\tluci-i18n-podkop-plus-ru_1.2.3_all.ipk\thttps://example.com/i18n.ipk')" \
"$(printf '%s' "$release_json" | ucode "$UPDATER" podkop-plus-release-plan v1.2.3 ipk 1)" \
"podkop release plan with i18n"
assert_eq "$(printf 'https://example.com/release\tpodkop-plus_1.2.3_all.ipk\thttps://example.com/backend.ipk\tluci-app-podkop-plus_1.2.3_all.ipk\thttps://example.com/app.ipk\t\t')" \
"$(printf '%s' "$release_json" | ucode "$UPDATER" podkop-plus-release-plan v1.2.3 ipk 0)" \
"podkop release plan without i18n"
if printf '%s' "$release_json" | ucode "$UPDATER" podkop-plus-release-plan v9.9.9 ipk 0 >/dev/null 2>&1; then
fail "podkop release plan should reject tag mismatch"
assert_eq "$(printf 'https://example.com/release\tforkop_1.2.3.ipk\thttps://example.com/backend.ipk\tluci-app-forkop_1.2.3.ipk\thttps://example.com/app.ipk\tluci-i18n-forkop-ru_1.2.3.ipk\thttps://example.com/i18n.ipk')" \
"$(printf '%s' "$release_json" | ucode "$UPDATER" forkop-release-plan 1.2.3 ipk 1)" \
"forkop release plan with i18n"
assert_eq "$(printf 'https://example.com/release\tforkop_1.2.3.ipk\thttps://example.com/backend.ipk\tluci-app-forkop_1.2.3.ipk\thttps://example.com/app.ipk\t\t')" \
"$(printf '%s' "$release_json" | ucode "$UPDATER" forkop-release-plan 1.2.3 ipk 0)" \
"forkop release plan without i18n"
if printf '%s' "$release_json" | ucode "$UPDATER" forkop-release-plan 9.9.9 ipk 0 >/dev/null 2>&1; then
fail "forkop release plan should reject tag mismatch"
fi
missing_i18n_json="$(cat <<'JSON'
{
"tag_name": "v1.2.3",
"tag_name": "1.2.3",
"html_url": "https://example.com/release",
"assets": [
{"name": "podkop-plus_1.2.3_all.ipk", "browser_download_url": "https://example.com/backend.ipk"},
{"name": "luci-app-podkop-plus_1.2.3_all.ipk", "browser_download_url": "https://example.com/app.ipk"}
{"name": "forkop_1.2.3.ipk", "browser_download_url": "https://example.com/backend.ipk"},
{"name": "luci-app-forkop_1.2.3.ipk", "browser_download_url": "https://example.com/app.ipk"}
]
}
JSON
)"
if printf '%s' "$missing_i18n_json" | ucode "$UPDATER" podkop-plus-release-plan v1.2.3 ipk 1 >/dev/null 2>&1; then
fail "podkop release plan should require i18n asset when requested"
if printf '%s' "$missing_i18n_json" | ucode "$UPDATER" forkop-release-plan 1.2.3 ipk 1 >/dev/null 2>&1; then
fail "forkop release plan should require i18n asset when requested"
fi
ucode "$UPDATER" forkop-release-version-valid 1.2.3 ||
fail "three-part Forkop release version should be valid"
for invalid_version in v1.2.3 1.2 1.2.3.4 1.2.3-1 1.2.3-r1; do
if ucode "$UPDATER" forkop-release-version-valid "$invalid_version" >/dev/null 2>&1; then
fail "invalid Forkop release version was accepted: $invalid_version"
fi
done
assert_eq "-1" "$(ucode "$UPDATER" forkop-release-version-compare 1.2.3 1.2.4)" \
"three-part Forkop release version comparison"
component_actions_dir="$WORK_DIR/component-actions"
fake_lib="$WORK_DIR/lib"
mkdir -p "$fake_lib/components" "$fake_lib/config" "$fake_lib/core"
cp "$UPDATES_UC" "$fake_lib/components/updates.uc"
cp "$PODKOP_LIB/config/connections.uc" "$fake_lib/config/connections.uc"
cp "$PODKOP_LIB/core/uci.uc" "$fake_lib/core/uci.uc"
cp "$PODKOP_LIB/core/common.uc" "$fake_lib/core/common.uc"
cp "$FORKOP_LIB/config/connections.uc" "$fake_lib/config/connections.uc"
cp "$FORKOP_LIB/core/uci.uc" "$fake_lib/core/uci.uc"
cp "$FORKOP_LIB/core/common.uc" "$fake_lib/core/common.uc"
cat >"$fake_lib/components/action.uc" <<'UCODE'
#!/usr/bin/env ucode
if ((ARGV[0] || "") == "component-action") {
@@ -261,8 +271,8 @@ if ((ARGV[0] || "") == "component-action") {
exit(1);
UCODE
start_json="$(PODKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
ucode -L "$PODKOP_LIB" "$UPDATES_UC" component-action-async sing_box check_update)"
start_json="$(FORKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
ucode -L "$FORKOP_LIB" "$UPDATES_UC" component-action-async sing_box check_update)"
job_id="$(JSON_VALUE="$start_json" node - <<'NODE'
const value = JSON.parse(process.env.JSON_VALUE);
if (!value.success || !value.job_id) {
@@ -275,8 +285,8 @@ NODE
status_json=""
for _ in 1 2 3 4 5; do
status_json="$(PODKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
ucode -L "$PODKOP_LIB" "$UPDATES_UC" component-action-status "$job_id")"
status_json="$(FORKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
ucode -L "$FORKOP_LIB" "$UPDATES_UC" component-action-status "$job_id")"
JSON_VALUE="$status_json" node - <<'NODE' && break || true
const value = JSON.parse(process.env.JSON_VALUE);
process.exit(value.running === false ? 0 : 1);
@@ -293,8 +303,8 @@ if (value.running !== false || value.success !== true || value.component !== "si
}
NODE
start_json="$(PODKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
ucode -L "$PODKOP_LIB" "$UPDATES_UC" component-action-async sing-box check_update)"
start_json="$(FORKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
ucode -L "$FORKOP_LIB" "$UPDATES_UC" component-action-async sing-box check_update)"
job_id="$(JSON_VALUE="$start_json" node - <<'NODE'
const value = JSON.parse(process.env.JSON_VALUE);
if (!value.success || !value.job_id) {
@@ -307,8 +317,8 @@ NODE
status_json=""
for _ in 1 2 3 4 5; do
status_json="$(PODKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
ucode -L "$PODKOP_LIB" "$UPDATES_UC" component-action-status "$job_id")"
status_json="$(FORKOP_LIB="$fake_lib" UPDATES_JOB_DIR="$component_actions_dir" \
ucode -L "$FORKOP_LIB" "$UPDATES_UC" component-action-status "$job_id")"
JSON_VALUE="$status_json" node - <<'NODE' && break || true
const value = JSON.parse(process.env.JSON_VALUE);
process.exit(value.running === false ? 0 : 1);
+7 -4
View File
@@ -2,10 +2,11 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
STABLE_REF="${PODKOP_STABLE_REF:-0.7.19.9}"
STABLE_REPO="${PODKOP_STABLE_REPO:-}"
STABLE_REF="${FORKOP_STABLE_REF:-0.7.19.9}"
STABLE_REPO="${FORKOP_STABLE_REPO:-}"
MATRIX_SCRIPT="$ROOT_DIR/tests/helpers/config_contract_matrix.js"
WORK_DIR="$(mktemp -d)"
LEGACY_STEM="$(printf '\160\157\144\153\157\160')"
cleanup() {
rm -rf "$WORK_DIR"
@@ -28,8 +29,10 @@ ensure_stable_ref() {
prepare_stable_repo() {
if [ -n "$STABLE_REPO" ]; then
[ -r "$STABLE_REPO/podkop/files/etc/config/podkop" ] ||
fail "stable repo is missing Podkop config template: $STABLE_REPO"
if [ ! -r "$STABLE_REPO/forkop/files/etc/config/forkop" ] &&
[ ! -r "$STABLE_REPO/$LEGACY_STEM/files/etc/config/$LEGACY_STEM" ]; then
fail "stable repo is missing the expected config template: $STABLE_REPO"
fi
return 0
fi
+56 -41
View File
@@ -2,9 +2,11 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
MIGRATION="$PODKOP_LIB/config/migration.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
RUNTIME_MIGRATION="$FORKOP_LIB/config/migration.uc"
INSTALLER="$ROOT_DIR/install.sh"
WORK_DIR="$(mktemp -d)"
MIGRATION="$WORK_DIR/config-migration.uc"
cleanup() {
rm -rf "$WORK_DIR"
@@ -16,13 +18,26 @@ fail() {
exit 1
}
awk '
/cat > "\$helper_path" <<'\''FORKOP_CONFIG_MIGRATION_EOF'\''/ { capture = 1; next }
capture && /^FORKOP_CONFIG_MIGRATION_EOF$/ { exit }
capture { print }
' "$INSTALLER" > "$MIGRATION"
[ -s "$MIGRATION" ] || fail "failed to extract config migration from install.sh"
grep -Fq 'Reserved for future Forkop configuration migrations.' "$RUNTIME_MIGRATION" ||
fail "runtime config/migration.uc must remain a placeholder"
if grep -n -E 'require\("core\.uci"\)|function migrate_|migrate_runtime|migrate-fixture' "$RUNTIME_MIGRATION" >/dev/null 2>&1; then
fail "runtime config/migration.uc must not contain migration logic"
fi
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"' "$MIGRATION" >/dev/null 2>&1; then
fail "config/migration.uc must use core.uci instead of direct UCI cursor or CLI access"
fail "installer config migration must use core.uci instead of direct UCI cursor or CLI access"
fi
grep -Fq 'require("core.uci")' "$MIGRATION" ||
fail "config/migration.uc must import core.uci"
fail "installer config migration must import core.uci"
eval "$(ucode -L "$PODKOP_LIB" "$PODKOP_LIB/core/constants.uc" shell-env)"
eval "$(ucode -L "$FORKOP_LIB" "$FORKOP_LIB/core/constants.uc" shell-env)"
export ZAPRET_LEGACY_DEFAULT_NFQWS_OPT ZAPRET_DEFAULT_NFQWS_OPT
node >"$WORK_DIR/fixture.json" <<'NODE'
@@ -133,7 +148,7 @@ const fixture = {
process.stdout.write(`${JSON.stringify(fixture, null, 2)}\n`);
NODE
PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$MIGRATION" migrate-fixture "$WORK_DIR/fixture.json" >"$WORK_DIR/output.json"
FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$MIGRATION" migrate-fixture "$WORK_DIR/fixture.json" >"$WORK_DIR/output.json"
node - "$WORK_DIR/output.json" <<'NODE'
const fs = require('fs');
@@ -313,65 +328,65 @@ absent(legacyOutbound, 'outbound_detour_enabled', 'legacy-outbound');
absent(legacyOutbound, 'outbound_detour_section', 'legacy-outbound');
assert(out.removed_caches.includes('/tmp/sing-box/subscriptions/legacy-sub.json'), 'subscription runtime cache removal');
assert(out.removed_caches.includes('/var/run/podkop-plus/section-cache/legacy-sub.json'), 'section cache removal');
assert(out.removed_caches.includes('/var/run/forkop/section-cache/legacy-sub.json'), 'section cache removal');
NODE
cat >"$WORK_DIR/runtime-migrate.state" <<'EOF_UCI'
podkop-plus.settings=settings
podkop-plus.settings.routing_excluded_ips=192.0.2.0/24
podkop-plus.legacy=rule
podkop-plus.legacy.enabled=1
podkop-plus.legacy.connection_type=proxy
podkop-plus.legacy.proxy_config_type=url
podkop-plus.legacy.proxy_string=vless://one
podkop-plus.old_direct=section
podkop-plus.old_direct.enabled=1
podkop-plus.old_direct.action=direct
forkop.settings=settings
forkop.settings.routing_excluded_ips=192.0.2.0/24
forkop.legacy=rule
forkop.legacy.enabled=1
forkop.legacy.connection_type=proxy
forkop.legacy.proxy_config_type=url
forkop.legacy.proxy_string=vless://one
forkop.old_direct=section
forkop.old_direct.enabled=1
forkop.old_direct.action=direct
EOF_UCI
mkdir -p "$WORK_DIR/runtime" "$WORK_DIR/persistent-cache"
: >"$WORK_DIR/runtime-migrate.log"
PODKOP_UCI_STATE_FILE="$WORK_DIR/runtime-migrate.state" \
PODKOP_UCI_LOG_FILE="$WORK_DIR/runtime-migrate.log" \
PODKOP_CONFIG_NAME="podkop-plus" \
FORKOP_UCI_STATE_FILE="$WORK_DIR/runtime-migrate.state" \
FORKOP_UCI_LOG_FILE="$WORK_DIR/runtime-migrate.log" \
FORKOP_CONFIG_NAME="forkop" \
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/tmp-subscriptions" \
PODKOP_RUNTIME_STATE_DIR="$WORK_DIR/runtime" \
PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent-cache" \
PODKOP_INTERNAL_CONFIG_TRIGGER_GUARD="$WORK_DIR/internal-config-change" \
ucode -L "$PODKOP_LIB" "$MIGRATION" migrate
FORKOP_RUNTIME_STATE_DIR="$WORK_DIR/runtime" \
FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent-cache" \
FORKOP_INTERNAL_CONFIG_TRIGGER_GUARD="$WORK_DIR/internal-config-change" \
ucode -L "$FORKOP_LIB" "$MIGRATION" migrate
grep -Fxq 'podkop-plus.legacy=section' "$WORK_DIR/runtime-migrate.state" ||
grep -Fxq 'forkop.legacy=section' "$WORK_DIR/runtime-migrate.state" ||
fail "runtime migration must convert legacy rule type through core.uci"
grep -Fxq 'podkop-plus.legacy.action=connection' "$WORK_DIR/runtime-migrate.state" ||
grep -Fxq 'forkop.legacy.action=connection' "$WORK_DIR/runtime-migrate.state" ||
fail "runtime migration must write migrated action through core.uci"
grep -Fxq 'podkop-plus.old_direct.action=bypass' "$WORK_DIR/runtime-migrate.state" ||
grep -Fxq 'forkop.old_direct.action=bypass' "$WORK_DIR/runtime-migrate.state" ||
fail "runtime migration must convert direct action to bypass through core.uci"
grep -Eq '^podkop-plus\\.cfg[0-9a-f]+=$' "$WORK_DIR/runtime-migrate.state" && fail "anonymous fixture section should include a type"
grep -Fxq 'podkop-plus.legacy.selector_proxy_links=vless://one' "$WORK_DIR/runtime-migrate.state" ||
grep -Eq '^forkop\\.cfg[0-9a-f]+=$' "$WORK_DIR/runtime-migrate.state" && fail "anonymous fixture section should include a type"
grep -Fxq 'forkop.legacy.selector_proxy_links=vless://one' "$WORK_DIR/runtime-migrate.state" ||
fail "runtime migration must keep connection URLs in the parent section"
if grep -Fq '=connection_url' "$WORK_DIR/runtime-migrate.state"; then
fail "runtime migration must not create connection_url child sections"
fi
if grep -Fq 'podkop-plus.legacy.urltest_enabled=' "$WORK_DIR/runtime-migrate.state"; then
if grep -Fq 'forkop.legacy.urltest_enabled=' "$WORK_DIR/runtime-migrate.state"; then
fail "runtime migration must delete migrated urltest_enabled through core.uci"
fi
if grep -Fq 'podkop-plus.settings.routing_excluded_ips=' "$WORK_DIR/runtime-migrate.state"; then
if grep -Fq 'forkop.settings.routing_excluded_ips=' "$WORK_DIR/runtime-migrate.state"; then
fail "runtime migration must delete removed routing_excluded_ips through core.uci"
fi
if grep -Fq 'podkop-plus.legacy.proxy_string=' "$WORK_DIR/runtime-migrate.state"; then
if grep -Fq 'forkop.legacy.proxy_string=' "$WORK_DIR/runtime-migrate.state"; then
fail "runtime migration must delete legacy proxy_string through core.uci"
fi
grep -Fxq 'commit podkop-plus' "$WORK_DIR/runtime-migrate.log" ||
grep -Fxq 'commit forkop' "$WORK_DIR/runtime-migrate.log" ||
fail "runtime migration must commit through core.uci"
: >"$WORK_DIR/uci-commit.log"
: >"$WORK_DIR/uci-commit.state"
PODKOP_UCI_STATE_FILE="$WORK_DIR/uci-commit.state" \
PODKOP_UCI_LOG_FILE="$WORK_DIR/uci-commit.log" \
PODKOP_CONFIG_NAME="podkop-plus" \
PODKOP_INTERNAL_CONFIG_TRIGGER_GUARD="$WORK_DIR/internal-config-change" \
ucode -L "$PODKOP_LIB" "$MIGRATION" commit
FORKOP_UCI_STATE_FILE="$WORK_DIR/uci-commit.state" \
FORKOP_UCI_LOG_FILE="$WORK_DIR/uci-commit.log" \
FORKOP_CONFIG_NAME="forkop" \
FORKOP_INTERNAL_CONFIG_TRIGGER_GUARD="$WORK_DIR/internal-config-change" \
ucode -L "$FORKOP_LIB" "$MIGRATION" commit
grep -Fxq 'commit podkop-plus' "$WORK_DIR/uci-commit.log" ||
fail "commit mode must commit podkop-plus through core.uci"
grep -Fxq 'commit forkop' "$WORK_DIR/uci-commit.log" ||
fail "commit mode must commit forkop through core.uci"
printf 'config migration checks passed\n'
printf 'installer config migration checks passed\n'
+17 -15
View File
@@ -2,27 +2,27 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_FILES="$ROOT_DIR/podkop/files"
PODKOP_BIN="$PODKOP_FILES/usr/bin/podkop"
PODKOP_LIB="$PODKOP_FILES/usr/lib"
CLI_UC="$PODKOP_BIN"
VALIDATOR="$PODKOP_LIB/config/validator.uc"
LIFECYCLE="$PODKOP_LIB/service/lifecycle.uc"
FORKOP_FILES="$ROOT_DIR/forkop/files"
FORKOP_BIN="$FORKOP_FILES/usr/bin/forkop"
FORKOP_LIB="$FORKOP_FILES/usr/lib"
CLI_UC="$FORKOP_BIN"
VALIDATOR="$FORKOP_LIB/config/validator.uc"
LIFECYCLE="$FORKOP_LIB/service/lifecycle.uc"
fail() {
printf 'FAIL: %s\n' "$1" >&2
exit 1
}
[ ! -e "$PODKOP_LIB/config_validation.sh" ] ||
[ ! -e "$FORKOP_LIB/config_validation.sh" ] ||
fail "config_validation.sh shell owner must be removed"
if grep -R -n "config_validation.sh" "$PODKOP_FILES" >/dev/null 2>&1; then
if grep -R -n "config_validation.sh" "$FORKOP_FILES" >/dev/null 2>&1; then
fail "runtime files must not reference config_validation.sh"
fi
legacy_symbols='(^|[^A-Za-z0-9_])(config_validate_runtime|check_requirements|commit_podkop_config|mwan3_is_active|get_inline_remote_ruleset_format|detect_inline_ruleset_reference_kind)([^A-Za-z0-9_]|$)'
if grep -R -n -E "$legacy_symbols" "$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
legacy_symbols='(^|[^A-Za-z0-9_])(config_validate_runtime|check_requirements|commit_forkop_config|mwan3_is_active|get_inline_remote_ruleset_format|detect_inline_ruleset_reference_kind)([^A-Za-z0-9_]|$)'
if grep -R -n -E "$legacy_symbols" "$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
fail "runtime shell must not keep config_validation.sh symbols"
fi
@@ -35,13 +35,15 @@ grep -Fq 'require("core.uci")' "$VALIDATOR" ||
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"|command_output\("uci' "$VALIDATOR" >/dev/null 2>&1; then
fail "config validator must not own direct UCI cursor or CLI access"
fi
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
fail "podkop entrypoint must be a direct ucode executable"
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
fail "forkop entrypoint must be a direct ucode executable"
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
fail "service/cli.uc must dispatch service lifecycle through service/lifecycle.uc"
grep -Fq 'MIGRATION_UC' "$LIFECYCLE" &&
grep -Fq '"migrate"' "$LIFECYCLE" ||
fail "service/lifecycle.uc must run config migration directly through ucode"
if grep -n -E 'MIGRATION_UC|config[./]migration|"migrate"' "$LIFECYCLE" >/dev/null 2>&1; then
fail "service/lifecycle.uc must not run configuration migration"
fi
grep -Fq 'mark_internal_config_guard();' "$LIFECYCLE" ||
fail "service/lifecycle.uc must preserve the internal config trigger guard"
grep -Fq 'VALIDATOR_UC' "$LIFECYCLE" &&
grep -Fq '"validate-runtime"' "$LIFECYCLE" ||
fail "service/lifecycle.uc must run runtime validation directly through ucode"
+4 -4
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
VALIDATOR="$ROOT_DIR/podkop/files/usr/lib/config/validator.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
VALIDATOR="$ROOT_DIR/forkop/files/usr/lib/config/validator.uc"
fail() {
printf 'FAIL: %s\n' "$1" >&2
@@ -20,7 +20,7 @@ assert_rejects() {
shift 2
local output
if output="$(rows "$@" | ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-outbound-detours 2>/dev/null)"; then
if output="$(rows "$@" | ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-outbound-detours 2>/dev/null)"; then
fail "$label should be rejected"
fi
@@ -31,7 +31,7 @@ assert_rejects() {
rows \
source 1 connection 1 target \
target 1 connection 0 '' |
ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-outbound-detours
ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-outbound-detours
assert_rejects "unsupported source action" "supported only for Connection rules" \
source 1 zapret 1 target \
+4 -4
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
VALIDATOR="$ROOT_DIR/podkop/files/usr/lib/config/validator.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
VALIDATOR="$ROOT_DIR/forkop/files/usr/lib/config/validator.uc"
fail() {
printf 'FAIL: %s\n' "$1" >&2
@@ -21,7 +21,7 @@ assert_accepts() {
local zapret2="$4"
shift 4
rows "$@" | ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-download-section "$target" "$byedpi" "$zapret" "$zapret2"
rows "$@" | ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-download-section "$target" "$byedpi" "$zapret" "$zapret2"
}
assert_rejects() {
@@ -34,7 +34,7 @@ assert_rejects() {
shift 6
local output
if output="$(rows "$@" | ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-download-section "$target" "$byedpi" "$zapret" "$zapret2" 2>/dev/null)"; then
if output="$(rows "$@" | ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-download-section "$target" "$byedpi" "$zapret" "$zapret2" 2>/dev/null)"; then
fail "$label should be rejected"
fi
+4 -4
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
VALIDATOR="$ROOT_DIR/podkop/files/usr/lib/config/validator.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
VALIDATOR="$ROOT_DIR/forkop/files/usr/lib/config/validator.uc"
COMMUNITY_SERVICES="youtube twitter telegram"
fail() {
@@ -15,7 +15,7 @@ assert_accepts() {
local label="$1"
shift
ucode -L "$PODKOP_LIB" "$VALIDATOR" "$@" >/dev/null ||
ucode -L "$FORKOP_LIB" "$VALIDATOR" "$@" >/dev/null ||
fail "$label should be accepted"
}
@@ -23,7 +23,7 @@ assert_rejects() {
local label="$1"
shift
if ucode -L "$PODKOP_LIB" "$VALIDATOR" "$@" >/dev/null 2>&1; then
if ucode -L "$FORKOP_LIB" "$VALIDATOR" "$@" >/dev/null 2>&1; then
fail "$label should be rejected"
fi
}
+10 -10
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
VALIDATOR="$PODKOP_LIB/config/validator.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
VALIDATOR="$FORKOP_LIB/config/validator.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -33,7 +33,7 @@ if (input.settings.dns_server === undefined) input.settings.dns_server = ['77.88
if (input.settings.bootstrap_dns_server === undefined) input.settings.bootstrap_dns_server = ['77.88.8.8'];
fs.writeFileSync(process.argv[3], JSON.stringify(input));
JS
PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$normalized" "$context"
FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$normalized" "$context"
}
assert_rejects() {
@@ -354,7 +354,7 @@ cat >"$WORK_DIR/bad-byedpi.json" <<'JSON'
]
}
JSON
if output="$(PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/bad-byedpi.json" "$provider_context" 2>/dev/null)"; then
if output="$(FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/bad-byedpi.json" "$provider_context" 2>/dev/null)"; then
fail "bad byedpi should be rejected"
fi
printf '%s\n' "$output" | grep -Fq "ByeDPI listen address and port are assigned" ||
@@ -362,10 +362,10 @@ printf '%s\n' "$output" | grep -Fq "ByeDPI listen address and port are assigned"
runtime_lib="$WORK_DIR/runtime-lib"
mkdir -p "$runtime_lib"
ln -s "$PODKOP_LIB/core" "$runtime_lib/core"
ln -s "$PODKOP_LIB/config" "$runtime_lib/config"
ln -s "$PODKOP_LIB/subscription" "$runtime_lib/subscription"
ln -s "$PODKOP_LIB/providers" "$runtime_lib/providers"
ln -s "$FORKOP_LIB/core" "$runtime_lib/core"
ln -s "$FORKOP_LIB/config" "$runtime_lib/config"
ln -s "$FORKOP_LIB/subscription" "$runtime_lib/subscription"
ln -s "$FORKOP_LIB/providers" "$runtime_lib/providers"
touch "$WORK_DIR/ciadpi-provider"
cat >"$WORK_DIR/bad-byedpi-runtime-state.json" <<'JSON'
{
@@ -391,7 +391,7 @@ env \
ZAPRET2_QUEUE_RANGE_SIZE="16" \
NFT_FAKEIP_MARK="0x00000800" \
NFT_OUTBOUND_MARK="0x00000400" \
PODKOP_LIB="$runtime_lib" \
FORKOP_LIB="$runtime_lib" \
ucode -L "$runtime_lib" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/bad-byedpi-runtime-state.json" "{}"
chmod 755 "$WORK_DIR/ciadpi-provider"
if output="$(env \
@@ -409,7 +409,7 @@ if output="$(env \
ZAPRET2_QUEUE_RANGE_SIZE="16" \
NFT_FAKEIP_MARK="0x00000800" \
NFT_OUTBOUND_MARK="0x00000400" \
PODKOP_LIB="$runtime_lib" \
FORKOP_LIB="$runtime_lib" \
ucode -L "$runtime_lib" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/bad-byedpi-runtime-state.json" "{}" 2>/dev/null)"; then
fail "executable byedpi provider should enable strategy validation"
fi
+4 -4
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
GENERATOR="$PODKOP_LIB/singbox/generator.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
GENERATOR="$FORKOP_LIB/singbox/generator.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -49,7 +49,7 @@ cat >"$WORK_DIR/subscriptions/source-subscription-1.json" <<'JSON'
"outbounds": [ "nested-entry", "independent" ],
"url": "https://www.gstatic.com/generate_204",
"interval": "3m",
"__podkop_allow_group": true
"__forkop_allow_group": true
}
]
}
@@ -100,7 +100,7 @@ cat >"$WORK_DIR/fixture.json" <<'JSON'
JSON
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/subscriptions" \
ucode -L "$PODKOP_LIB" "$GENERATOR" generate-config-fixture \
ucode -L "$FORKOP_LIB" "$GENERATOR" generate-config-fixture \
"$WORK_DIR/fixture.json" "$WORK_DIR/output.json" "127.0.0.1"
ucode -e '
+20 -20
View File
@@ -2,14 +2,14 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
CLI_UC="$PODKOP_BIN"
PODKOP_MAKEFILE="$ROOT_DIR/podkop/Makefile"
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
CLI_UC="$FORKOP_BIN"
FORKOP_MAKEFILE="$ROOT_DIR/forkop/Makefile"
BUILD_SCRIPT="$ROOT_DIR/build.sh"
CONSTANTS_SH="$PODKOP_LIB/constants.sh"
LIFECYCLE_UC="$PODKOP_LIB/service/lifecycle.uc"
CONSTANTS_UC="$PODKOP_LIB/core/constants.uc"
CONSTANTS_SH="$FORKOP_LIB/constants.sh"
LIFECYCLE_UC="$FORKOP_LIB/service/lifecycle.uc"
CONSTANTS_UC="$FORKOP_LIB/core/constants.uc"
fail() {
printf 'FAIL: %s\n' "$1" >&2
@@ -19,36 +19,36 @@ fail() {
[ ! -e "$CONSTANTS_SH" ] ||
fail "constants.sh shell owner must be removed"
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
fail "podkop entrypoint must be a direct ucode executable"
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
fail "forkop entrypoint must be a direct ucode executable"
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
fail "service/cli.uc must dispatch lifecycle orchestration through service/lifecycle.uc"
grep -Fq 'core.constants' "$LIFECYCLE_UC" ||
fail "service/lifecycle.uc must load constants from core/constants.uc"
if grep -R -n -E 'constants\.sh|read_shell_constants|expand_shell_constants|unquote_shell_value' \
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' --include='*.uc' >/dev/null 2>&1; then
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' --include='*.uc' >/dev/null 2>&1; then
fail "shell constants owner or parser references must not remain"
fi
if grep -n 'constants\.sh' "$PODKOP_MAKEFILE" "$BUILD_SCRIPT" >/dev/null 2>&1; then
if grep -n 'constants\.sh' "$FORKOP_MAKEFILE" "$BUILD_SCRIPT" >/dev/null 2>&1; then
fail "package build must not patch removed constants.sh"
fi
grep -Fq 'core/constants.uc' "$PODKOP_MAKEFILE" ||
fail "podkop/Makefile must patch core/constants.uc"
grep -Fq 'core/constants.uc' "$FORKOP_MAKEFILE" ||
fail "forkop/Makefile must patch core/constants.uc"
grep -Fq 'core/constants.uc' "$BUILD_SCRIPT" ||
fail "manual WSL build must patch core/constants.uc"
config_name="$(ucode -L "$PODKOP_LIB" "$CONSTANTS_UC" get PODKOP_CONFIG_NAME)"
[ "$config_name" = "podkop-plus" ] ||
fail "core/constants.uc get returned unexpected PODKOP_CONFIG_NAME"
config_name="$(ucode -L "$FORKOP_LIB" "$CONSTANTS_UC" get FORKOP_CONFIG_NAME)"
[ "$config_name" = "forkop" ] ||
fail "core/constants.uc get returned unexpected FORKOP_CONFIG_NAME"
eval "$(ucode -L "$PODKOP_LIB" "$CONSTANTS_UC" shell-env)"
[ "$PODKOP_CONFIG" = "/etc/config/podkop-plus" ] ||
fail "core/constants.uc shell-env did not derive PODKOP_CONFIG"
eval "$(ucode -L "$FORKOP_LIB" "$CONSTANTS_UC" shell-env)"
[ "$FORKOP_CONFIG" = "/etc/config/forkop" ] ||
fail "core/constants.uc shell-env did not derive FORKOP_CONFIG"
[ "$TMP_RULESET_FOLDER" = "/tmp/sing-box/rulesets" ] ||
fail "core/constants.uc shell-env did not derive TMP_RULESET_FOLDER"
[ "$BYEDPI_PID_DIR" = "/var/run/podkop-plus/byedpi/pid" ] ||
[ "$BYEDPI_PID_DIR" = "/var/run/forkop/byedpi/pid" ] ||
fail "core/constants.uc shell-env did not derive BYEDPI_PID_DIR"
printf 'constants ownership checks passed\n'
+1 -1
View File
@@ -2,7 +2,7 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
HELPERS_UC="$ROOT_DIR/podkop/files/usr/lib/core/helpers.uc"
HELPERS_UC="$ROOT_DIR/forkop/files/usr/lib/core/helpers.uc"
fail() {
printf 'FAIL: %s\n' "$1" >&2
+1 -1
View File
@@ -2,7 +2,7 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
UCODE_LIB="$ROOT_DIR/podkop/files/usr/lib"
UCODE_LIB="$ROOT_DIR/forkop/files/usr/lib"
WORK_DIR="$(mktemp -d)"
cleanup() {
+3 -3
View File
@@ -2,7 +2,7 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -95,9 +95,9 @@ UCODE
FAKE_COUNTRY_PAYLOAD="$WORK_DIR/payload.json" \
FAKE_COUNTRY_RESOLVE="$WORK_DIR/resolve.txt" \
PODKOP_COUNTRY_IS_URL="https://country.invalid/" \
FORKOP_COUNTRY_IS_URL="https://country.invalid/" \
PATH="$WORK_DIR/bin:$PATH" \
ucode -L "$PODKOP_LIB" "$WORK_DIR/runner.uc"
ucode -L "$FORKOP_LIB" "$WORK_DIR/runner.uc"
grep -Fq '8.8.8.8' "$WORK_DIR/payload.json" ||
fail "country.is request did not contain the first resolved IP"
+24 -24
View File
@@ -2,15 +2,15 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DIAGNOSTICS="$ROOT_DIR/podkop/files/usr/lib/diagnostics/status.uc"
DIAGNOSTICS_RUNTIME="$ROOT_DIR/podkop/files/usr/lib/diagnostics/runtime.uc"
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
CLI_UC="$PODKOP_BIN"
DIAGNOSTICS="$ROOT_DIR/forkop/files/usr/lib/diagnostics/status.uc"
DIAGNOSTICS_RUNTIME="$ROOT_DIR/forkop/files/usr/lib/diagnostics/runtime.uc"
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
CLI_UC="$FORKOP_BIN"
WORK_DIR="$(mktemp -d)"
status_ucode() {
ucode -L "$PODKOP_LIB" "$DIAGNOSTICS" "$@"
ucode -L "$FORKOP_LIB" "$DIAGNOSTICS" "$@"
}
cleanup() {
@@ -47,17 +47,17 @@ assert_status 1 0 0 "running but disabled"
assert_status 0 1 1 "stopped but enabled"
assert_status 0 0 0 "stopped & disabled"
[ ! -e "$PODKOP_LIB/status_diagnostics.sh" ] ||
[ ! -e "$FORKOP_LIB/status_diagnostics.sh" ] ||
fail "status_diagnostics.sh shell owner must be removed"
grep -Fq 'get_system_info: [ "diagnostics/runtime.uc", "get-system-info", 0 ]' "$CLI_UC" ||
fail "service/cli.uc must dispatch get_system_info through diagnostics/runtime.uc"
[ "$(PODKOP_VERSION=runtime-test ucode -L "$PODKOP_LIB" "$DIAGNOSTICS_RUNTIME" show-version)" = "runtime-test" ] ||
[ "$(FORKOP_VERSION=runtime-test ucode -L "$FORKOP_LIB" "$DIAGNOSTICS_RUNTIME" show-version)" = "runtime-test" ] ||
fail "diagnostics/runtime.uc show-version mode failed"
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "show"|uci", "-q"' "$DIAGNOSTICS_RUNTIME" >/dev/null 2>&1; then
fail "diagnostics/runtime.uc must use core.uci instead of owning direct UCI cursor or CLI calls"
fi
grep -Fq '"podkop-stably-running", RT_TABLE_NAME, NFT_TABLE_NAME, NFT_FAKEIP_MARK, RUNTIME_STABLE_MIN_AGE' "$DIAGNOSTICS_RUNTIME" ||
fail "diagnostics Podkop status must use stable runtime state to avoid crash-loop flicker"
grep -Fq '"forkop-stably-running", RT_TABLE_NAME, NFT_TABLE_NAME, NFT_FAKEIP_MARK, RUNTIME_STABLE_MIN_AGE' "$DIAGNOSTICS_RUNTIME" ||
fail "diagnostics Forkop status must use stable runtime state to avoid crash-loop flicker"
grep -Fq '"sing-box-service-stable",' "$DIAGNOSTICS_RUNTIME" ||
fail "diagnostics sing-box status must use stable runtime state to avoid crash-loop flicker"
@@ -71,13 +71,13 @@ if (value.status !== "running but disabled" || value.dns_configured !== 1) {
NODE
{
printf 'Tue Jun 30 11:00:00 2026 user.notice podkop-plus: [info] Starting Podkop Plus\n'
printf 'Tue Jun 30 11:00:00 2026 user.notice forkop: [info] Starting Forkop\n'
for i in $(seq 1 4500); do
printf 'Tue Jun 30 11:00:%02d 2026 daemon.info unrelated[%04d]: filler filler filler filler filler filler filler filler filler filler\n' "$((i % 60))" "$i"
done
printf 'Tue Jun 30 11:01:00 2026 user.notice podkop-plus: [info] large logread marker survived stdin transport\n'
printf 'Tue Jun 30 11:01:00 2026 user.notice forkop: [info] large logread marker survived stdin transport\n'
} >"$WORK_DIR/large-logread.txt"
large_logs="$(PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$DIAGNOSTICS_RUNTIME" podkop-logs-fixture <"$WORK_DIR/large-logread.txt")" ||
large_logs="$(FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$DIAGNOSTICS_RUNTIME" forkop-logs-fixture <"$WORK_DIR/large-logread.txt")" ||
fail "diagnostics/runtime.uc must process large logread payloads through stdin without shell argument limits"
case "$large_logs" in
*"large logread marker survived stdin transport"*) ;;
@@ -94,14 +94,14 @@ SH
chmod +x "$fake_bin/curl"
uci_state="$WORK_DIR/uci-state.txt"
cat >"$uci_state" <<'EOF'
podkop-plus.settings=settings
podkop-plus.settings.latency_test_url=https://latency.example/generate_204
forkop.settings=settings
forkop.settings.latency_test_url=https://latency.example/generate_204
EOF
FAKE_CURL_LOG="$WORK_DIR/fake-curl.log" \
PODKOP_UCI_STATE_FILE="$uci_state" \
PODKOP_LIB="$PODKOP_LIB" \
FORKOP_UCI_STATE_FILE="$uci_state" \
FORKOP_LIB="$FORKOP_LIB" \
PATH="$fake_bin:$PATH" \
ucode -L "$PODKOP_LIB" "$DIAGNOSTICS_RUNTIME" clash-api get_proxy_latency proxy-out 5000 >/dev/null ||
ucode -L "$FORKOP_LIB" "$DIAGNOSTICS_RUNTIME" clash-api get_proxy_latency proxy-out 5000 >/dev/null ||
fail "clash-api get_proxy_latency should use fake curl successfully"
grep -Fq "url=https://latency.example/generate_204" "$WORK_DIR/fake-curl.log" ||
fail "clash-api latency check must use settings.latency_test_url"
@@ -111,11 +111,11 @@ mkdir -p "$latency_action_dir"
latency_state="$latency_action_dir/latency-1.json"
printf '%s\n' '{"success":true,"running":true,"kind":"latency","latency_type":"proxy_list","section":"main","tag":"[]","started_at":100}' >"$latency_state"
FAKE_CURL_LOG="$WORK_DIR/fake-curl-latencies.log" \
PODKOP_UCI_STATE_FILE="$uci_state" \
PODKOP_LIB="$PODKOP_LIB" \
PODKOP_UI_LATENCY_ACTION_DIR="$latency_action_dir" \
FORKOP_UCI_STATE_FILE="$uci_state" \
FORKOP_LIB="$FORKOP_LIB" \
FORKOP_UI_LATENCY_ACTION_DIR="$latency_action_dir" \
PATH="$fake_bin:$PATH" \
ucode -L "$PODKOP_LIB" "$DIAGNOSTICS_RUNTIME" clash-api get_proxy_latencies '["proxy-a","proxy-b"]' 5000 "$latency_state" >/dev/null ||
ucode -L "$FORKOP_LIB" "$DIAGNOSTICS_RUNTIME" clash-api get_proxy_latencies '["proxy-a","proxy-b"]' 5000 "$latency_state" >/dev/null ||
fail "clash-api get_proxy_latencies should update latency progress"
JOB_STATE="$latency_state" node - <<'NODE'
const fs = require("fs");
@@ -226,10 +226,10 @@ EOF
)"
printf '%s\n' "$sing_box_netstat" |
PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$DIAGNOSTICS_RUNTIME" sing-box-standard-ports-listening-fixture >/dev/null ||
FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$DIAGNOSTICS_RUNTIME" sing-box-standard-ports-listening-fixture >/dev/null ||
fail "sing-box standard listeners should satisfy diagnostics"
if printf '%s\n' "$sing_box_netstat" | sed '/0.0.0.0:1602/d' |
PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$DIAGNOSTICS_RUNTIME" sing-box-standard-ports-listening-fixture >/dev/null 2>&1; then
FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$DIAGNOSTICS_RUNTIME" sing-box-standard-ports-listening-fixture >/dev/null 2>&1; then
fail "missing sing-box tproxy listener should fail diagnostics"
fi
+27 -27
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
APPLY="$ROOT_DIR/podkop/files/usr/lib/dns/apply.uc"
UCODE_LIB="$ROOT_DIR/podkop/files/usr/lib"
APPLY="$ROOT_DIR/forkop/files/usr/lib/dns/apply.uc"
UCODE_LIB="$ROOT_DIR/forkop/files/usr/lib"
WORK_DIR="$(mktemp -d)"
STATE="$WORK_DIR/uci.state"
LOG="$WORK_DIR/uci.log"
@@ -131,7 +131,7 @@ export UCI_STATE="$STATE"
export UCI_LOG="$LOG"
export DNSMASQ_LOG
export DNSMASQ_INIT="$WORK_DIR/dnsmasq-init"
export PODKOP_CONFIG_NAME="podkop-plus"
export FORKOP_CONFIG_NAME="forkop"
export SB_DNS_INBOUND_ADDRESS="127.0.0.42"
if grep -E 'uci -q|command -v uci' "$APPLY" >/dev/null; then
@@ -182,18 +182,18 @@ cat >"$STATE" <<'EOF_STATE'
dhcp.@dnsmasq[0].server=1.1.1.1 8.8.8.8
dhcp.@dnsmasq[0].noresolv=0
dhcp.@dnsmasq[0].cachesize=150
podkop-plus.settings.shutdown_correctly=1
forkop.settings.shutdown_correctly=1
EOF_STATE
: > "$DNSMASQ_LOG"
: > "$LOG"
ucode -L "$UCODE_LIB" "$APPLY" configure force
assert_value 'dhcp.@dnsmasq[0].server' '127.0.0.42'
assert_value 'dhcp.@dnsmasq[0].podkop_server' '1.1.1.1 8.8.8.8'
assert_value 'dhcp.@dnsmasq[0].forkop_server' '1.1.1.1 8.8.8.8'
assert_value 'dhcp.@dnsmasq[0].noresolv' '1'
assert_value 'dhcp.@dnsmasq[0].podkop_noresolv' '0'
assert_value 'dhcp.@dnsmasq[0].forkop_noresolv' '0'
assert_value 'dhcp.@dnsmasq[0].cachesize' '0'
assert_value 'dhcp.@dnsmasq[0].podkop_cachesize' '150'
assert_value 'dhcp.@dnsmasq[0].forkop_cachesize' '150'
assert_log_contains 'commit dhcp'
assert_dnsmasq_restarted
@@ -203,21 +203,21 @@ ucode -L "$UCODE_LIB" "$APPLY" restore force
assert_value 'dhcp.@dnsmasq[0].server' '1.1.1.1 8.8.8.8'
assert_value 'dhcp.@dnsmasq[0].noresolv' '0'
assert_value 'dhcp.@dnsmasq[0].cachesize' '150'
assert_absent 'dhcp.@dnsmasq[0].podkop_server'
assert_absent 'dhcp.@dnsmasq[0].podkop_noresolv'
assert_absent 'dhcp.@dnsmasq[0].podkop_cachesize'
assert_absent 'dhcp.@dnsmasq[0].forkop_server'
assert_absent 'dhcp.@dnsmasq[0].forkop_noresolv'
assert_absent 'dhcp.@dnsmasq[0].forkop_cachesize'
assert_log_contains 'commit dhcp'
assert_dnsmasq_restarted
cat >"$STATE" <<'EOF_STATE'
dhcp.@dnsmasq[0].server=127.0.0.42
dhcp.@dnsmasq[0].notinterface=br-lan guest
dhcp.@dnsmasq[0].podkop_server=1.1.1.1 8.8.8.8
dhcp.@dnsmasq[0].podkop_notinterface=wan docker
dhcp.@dnsmasq[0].podkop_noresolv=1
dhcp.@dnsmasq[0].podkop_cachesize=0
dhcp.podkop_plus.interface=br-lan guest
podkop-plus.settings.dont_touch_dhcp=1
dhcp.@dnsmasq[0].forkop_server=1.1.1.1 8.8.8.8
dhcp.@dnsmasq[0].forkop_notinterface=wan docker
dhcp.@dnsmasq[0].forkop_noresolv=1
dhcp.@dnsmasq[0].forkop_cachesize=0
dhcp.forkop.interface=br-lan guest
forkop.settings.dont_touch_dhcp=1
EOF_STATE
run_restore
@@ -225,16 +225,16 @@ assert_value 'dhcp.@dnsmasq[0].server' '1.1.1.1 8.8.8.8'
assert_value 'dhcp.@dnsmasq[0].notinterface' 'wan docker'
assert_value 'dhcp.@dnsmasq[0].noresolv' '1'
assert_value 'dhcp.@dnsmasq[0].cachesize' '0'
assert_absent 'dhcp.@dnsmasq[0].podkop_server'
assert_absent 'dhcp.@dnsmasq[0].podkop_notinterface'
assert_absent 'dhcp.@dnsmasq[0].podkop_noresolv'
assert_absent 'dhcp.@dnsmasq[0].podkop_cachesize'
assert_absent 'dhcp.podkop_plus.interface'
assert_absent 'dhcp.@dnsmasq[0].forkop_server'
assert_absent 'dhcp.@dnsmasq[0].forkop_notinterface'
assert_absent 'dhcp.@dnsmasq[0].forkop_noresolv'
assert_absent 'dhcp.@dnsmasq[0].forkop_cachesize'
assert_absent 'dhcp.forkop.interface'
assert_log_contains 'commit dhcp'
cat >"$STATE" <<'EOF_STATE'
dhcp.@dnsmasq[0].server=9.9.9.9
podkop-plus.settings.dont_touch_dhcp=1
forkop.settings.dont_touch_dhcp=1
EOF_STATE
run_restore
@@ -243,17 +243,17 @@ assert_log_empty
cat >"$STATE" <<'EOF_STATE'
dhcp.@dnsmasq[0].server=127.0.0.42
dhcp.@dnsmasq[0].podkop_noresolv=1
dhcp.@dnsmasq[0].podkop_cachesize=0
podkop-plus.settings.dont_touch_dhcp=0
dhcp.@dnsmasq[0].forkop_noresolv=1
dhcp.@dnsmasq[0].forkop_cachesize=0
forkop.settings.dont_touch_dhcp=0
EOF_STATE
run_restore
assert_absent 'dhcp.@dnsmasq[0].server'
assert_value 'dhcp.@dnsmasq[0].noresolv' '1'
assert_value 'dhcp.@dnsmasq[0].cachesize' '0'
assert_absent 'dhcp.@dnsmasq[0].podkop_noresolv'
assert_absent 'dhcp.@dnsmasq[0].podkop_cachesize'
assert_absent 'dhcp.@dnsmasq[0].forkop_noresolv'
assert_absent 'dhcp.@dnsmasq[0].forkop_cachesize'
assert_log_contains 'commit dhcp'
printf 'DNS apply checks passed\n'
+9 -9
View File
@@ -2,9 +2,9 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
GENERATOR="$PODKOP_LIB/singbox/generator.uc"
FAILOVER="$PODKOP_LIB/singbox/dns_failover.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
GENERATOR="$FORKOP_LIB/singbox/generator.uc"
FAILOVER="$FORKOP_LIB/singbox/dns_failover.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -21,9 +21,9 @@ generate() {
local fixture="$1"
local output="$2"
local state="${3:-$WORK_DIR/missing-state.json}"
PODKOP_LIB="$PODKOP_LIB" \
PODKOP_DNS_FAILOVER_STATE_FILE="$state" \
ucode -L "$PODKOP_LIB" "$GENERATOR" generate-config-fixture "$fixture" "$output" 192.168.1.1 0
FORKOP_LIB="$FORKOP_LIB" \
FORKOP_DNS_FAILOVER_STATE_FILE="$state" \
ucode -L "$FORKOP_LIB" "$GENERATOR" generate-config-fixture "$fixture" "$output" 192.168.1.1 0
}
cat >"$WORK_DIR/single.json" <<'JSON'
@@ -141,11 +141,11 @@ cat >"$WORK_DIR/alive-recovery.json" <<'JSON'
{ "0": true, "1": true }
JSON
selected="$(ucode -L "$PODKOP_LIB" "$FAILOVER" select-fixture "$WORK_DIR/select-state.json" "$WORK_DIR/alive-failover.json" main 0)"
selected="$(ucode -L "$FORKOP_LIB" "$FAILOVER" select-fixture "$WORK_DIR/select-state.json" "$WORK_DIR/alive-failover.json" main 0)"
printf '%s' "$selected" | grep -Eq '"index"[[:space:]]*:[[:space:]]*1' || fail "dead active DNS must select the first live server below"
printf '%s' "$selected" | grep -Eq '"reason"[[:space:]]*:[[:space:]]*"active_dead"' || fail "failover reason"
selected="$(ucode -L "$PODKOP_LIB" "$FAILOVER" select-fixture "$WORK_DIR/select-state.json" "$WORK_DIR/alive-recovery.json" bootstrap 1)"
selected="$(ucode -L "$FORKOP_LIB" "$FAILOVER" select-fixture "$WORK_DIR/select-state.json" "$WORK_DIR/alive-recovery.json" bootstrap 1)"
printf '%s' "$selected" | grep -Eq '"index"[[:space:]]*:[[:space:]]*0' || fail "recovery must return to the highest live priority"
printf '%s' "$selected" | grep -Eq '"reason"[[:space:]]*:[[:space:]]*"recovery"' || fail "recovery reason"
@@ -156,7 +156,7 @@ cat >"$WORK_DIR/verify-bootstrap.json" <<'JSON'
{ "main_index": 0, "bootstrap_index": 1, "bootstrap_servers": [ "a", "b" ] }
JSON
verification="$(ucode -L "$PODKOP_LIB" "$FAILOVER" verification-plan-fixture "$WORK_DIR/verify-previous.json" "$WORK_DIR/verify-bootstrap.json")"
verification="$(ucode -L "$FORKOP_LIB" "$FAILOVER" verification-plan-fixture "$WORK_DIR/verify-previous.json" "$WORK_DIR/verify-bootstrap.json")"
printf '%s' "$verification" | grep -Eq '"main"[[:space:]]*:[[:space:]]*false' || fail "bootstrap-only switch must not require a dead main DNS to recover"
printf '%s' "$verification" | grep -Eq '"bootstrap"[[:space:]]*:[[:space:]]*true' || fail "bootstrap-only switch must verify the selected bootstrap DNS"
+21 -9
View File
@@ -5,6 +5,9 @@ const fs = require("fs");
const path = require("path");
const { spawnSync } = require("child_process");
const legacyStem = String.fromCharCode(112, 111, 100, 107, 111, 112);
const legacyAppDir = `luci-app-${legacyStem}-plus`;
function usage() {
console.error("Usage: config_contract_matrix.js --current <repo> --stable <repo> [--check]");
process.exit(2);
@@ -145,15 +148,17 @@ function enrichUiValues(fileData, option, block, values) {
}
function extractUi(repo, fields) {
const viewDir = path.join(
const currentViewDir = path.join(
repo,
"luci-app-podkop-plus",
"luci-app-forkop",
"htdocs",
"luci-static",
"resources",
"view",
"podkop",
"forkop",
);
const legacyViewDir = path.join(repo, legacyAppDir, "htdocs", "luci-static", "resources", "view", legacyStem);
const viewDir = fs.existsSync(currentViewDir) ? currentViewDir : legacyViewDir;
const files = walkFiles(viewDir).filter((file) => file.endsWith(".js"));
for (const file of files) {
@@ -200,7 +205,9 @@ function extractUi(repo, fields) {
}
function extractConfigDefaults(repo, fields) {
const file = path.join(repo, "podkop", "files", "etc", "config", "podkop");
const currentFile = path.join(repo, "forkop", "files", "etc", "config", "forkop");
const legacyFile = path.join(repo, legacyStem, "files", "etc", "config", legacyStem);
const file = fs.existsSync(currentFile) ? currentFile : legacyFile;
const data = readFileIfExists(file);
for (const line of data.split(/\n/)) {
const match = line.match(/^\s*(#\s*)?(option|list)\s+([A-Za-z0-9_]+)\s+['"]?([^'"]*)/);
@@ -212,17 +219,22 @@ function extractConfigDefaults(repo, fields) {
}
function extractBackend(repo, fields) {
const backendRoot = fs.existsSync(path.join(repo, "forkop")) ? "forkop" : legacyStem;
const roots = [
path.join(repo, "podkop", "files", "usr", "lib"),
path.join(repo, "podkop", "files", "usr", "bin"),
path.join(repo, "podkop", "files", "etc", "init.d"),
path.join(repo, backendRoot, "files", "usr", "lib"),
path.join(repo, backendRoot, "files", "usr", "bin"),
path.join(repo, backendRoot, "files", "etc", "init.d"),
];
const files = roots.flatMap((root) => walkFiles(root)).filter((file) => /\.(sh|uc)$/.test(file) || /[\\/]podkop$/.test(file));
const files = roots
.flatMap((root) => walkFiles(root))
.filter((file) => /\.(sh|uc)$/.test(file) || ["forkop", legacyStem].includes(path.basename(file)));
const installer = path.join(repo, "install.sh");
if (fs.existsSync(installer)) files.push(installer);
const shellOptionRe = /\bconfig_(?:get|get_bool|list_foreach)\s+\S+\s+(?:"[^"]+"|'[^']+'|\$[A-Za-z_][A-Za-z0-9_]*|\$\{[^}]+\})\s+["']([A-Za-z0-9_]+)["']/g;
const ucodeOptionRe = /\b(?:option|list_option|bool_option|int_option)\(\s*[^,\n]+,\s*["']([A-Za-z0-9_]+)["']/g;
const ucodeStaticOptionKeyArrayRe = /^\s*\[\s*["']([A-Za-z0-9_]+)["']\s*,/gm;
const migrationRe = /\bpodkop_uci_(?:set_option|set_option_if_missing|delete_option|add_list_unique)\s+["'$A-Za-z0-9_{}.-]+\s+["']([A-Za-z0-9_]+)["']/g;
const migrationRe = new RegExp(`\\b(?:forkop|${legacyStem})_uci_(?:set_option|set_option_if_missing|delete_option|add_list_unique)\\s+["'$A-Za-z0-9_{}.-]+\\s+["']([A-Za-z0-9_]+)["']`, "g");
const ucodeMigrationRe = /\b(?:set_option|set_option_if_missing|delete_option|add_list_unique)\(\s*[^,\n]+,\s*[^,\n]+,\s*["']([A-Za-z0-9_]+)["']/g;
for (const file of files) {
+36 -36
View File
@@ -2,16 +2,16 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
CLI_UC="$PODKOP_BIN"
HELPERS_SH="$PODKOP_LIB/helpers.sh"
LIFECYCLE_UC="$PODKOP_LIB/service/lifecycle.uc"
PACKAGES_UC="$PODKOP_LIB/core/packages.uc"
RULES_UC="$PODKOP_LIB/providers/rules.uc"
SINGBOX_RUNTIME_UC="$PODKOP_LIB/singbox/runtime.uc"
COMPONENT_ACTION_UC="$PODKOP_LIB/components/action.uc"
DIAGNOSTICS_RUNTIME_UC="$PODKOP_LIB/diagnostics/runtime.uc"
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
CLI_UC="$FORKOP_BIN"
HELPERS_SH="$FORKOP_LIB/helpers.sh"
LIFECYCLE_UC="$FORKOP_LIB/service/lifecycle.uc"
PACKAGES_UC="$FORKOP_LIB/core/packages.uc"
RULES_UC="$FORKOP_LIB/providers/rules.uc"
SINGBOX_RUNTIME_UC="$FORKOP_LIB/singbox/runtime.uc"
COMPONENT_ACTION_UC="$FORKOP_LIB/components/action.uc"
DIAGNOSTICS_RUNTIME_UC="$FORKOP_LIB/diagnostics/runtime.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -26,16 +26,16 @@ fail() {
[ ! -e "$HELPERS_SH" ] ||
fail "helpers.sh shell owner must be removed"
if grep -R -n -F 'helpers.sh' "$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
if grep -R -n -F 'helpers.sh' "$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
fail "runtime shell must not reference helpers.sh"
fi
if grep -R -n -E 'helpers_ucode\(|get_(inbound|server_inbound|tailscale_dns_server|outbound)_tag_by_section\(|get_domain_resolver_tag\(|provider_status_ucode\(|is_ipv4\(|is_min_package_version\(|url_get_(scheme|userinfo|host|port|path|query_param)\(' \
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
fail "helpers.sh wrapper symbols must not remain in runtime shell"
fi
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
fail "podkop entrypoint must be a direct ucode executable"
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
fail "forkop entrypoint must be a direct ucode executable"
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
fail "service/cli.uc must dispatch lifecycle through service/lifecycle.uc"
grep -Fq 'core/packages.uc' "$LIFECYCLE_UC" ||
@@ -45,19 +45,19 @@ for shell_owner_pattern in \
'config_get' \
'uci_set'
do
if grep -Fq "$shell_owner_pattern" "$PODKOP_BIN"; then
fail "podkop shell entrypoint must not own $shell_owner_pattern logic"
if grep -Fq "$shell_owner_pattern" "$FORKOP_BIN"; then
fail "forkop shell entrypoint must not own $shell_owner_pattern logic"
fi
done
if grep -E -n '(^|[;&|[:space:]])nft[[:space:]]+(list|add|delete|flush)' "$PODKOP_BIN" >/dev/null 2>&1; then
fail "podkop shell entrypoint must not own nft command logic"
if grep -E -n '(^|[;&|[:space:]])nft[[:space:]]+(list|add|delete|flush)' "$FORKOP_BIN" >/dev/null 2>&1; then
fail "forkop shell entrypoint must not own nft command logic"
fi
if grep -E -n '(^|[;&|[:space:]])ip[[:space:]]+(rule|route)' "$PODKOP_BIN" >/dev/null 2>&1; then
fail "podkop shell entrypoint must not own ip rule/route logic"
if grep -E -n '(^|[;&|[:space:]])ip[[:space:]]+(rule|route)' "$FORKOP_BIN" >/dev/null 2>&1; then
fail "forkop shell entrypoint must not own ip rule/route logic"
fi
[ ! -e "$PODKOP_LIB/updater.sh" ] ||
[ ! -e "$FORKOP_LIB/updater.sh" ] ||
fail "updater.sh shell owner must be removed"
[ ! -e "$PODKOP_LIB/status_diagnostics.sh" ] ||
[ ! -e "$FORKOP_LIB/status_diagnostics.sh" ] ||
fail "status_diagnostics.sh shell owner must be removed"
grep -Fq 'diagnostics/runtime.uc' "$CLI_UC" ||
fail "service/cli.uc must dispatch diagnostics through diagnostics/runtime.uc"
@@ -86,52 +86,52 @@ do
fail "singbox/runtime.uc missing $mode"
done
if grep -R -n -E 'get_sing_box_version\(|sing_box_version_from_output\(|sing_box_version_output\(|sing_box_output_has_build_tag\(|sing_box_has_build_tag\(|is_sing_box_extended\(|is_sing_box_tiny_package_installed\(|is_sing_box_full_package_installed\(|is_sing_box_compressed_marker_set\(|is_sing_box_extended_marker_set\(|read_sing_box_version_state\(|is_sing_box_tiny_marker_set\(|is_sing_box_tiny\(|sing_box_supports_tailscale\(|get_sing_box_variant\(|updates_(write|read|clear|restore)_sing_box_(variant_marker|version_state)\(' \
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
fail "sing-box helper/state shell symbols must not remain"
fi
if ucode -L "$PODKOP_LIB" "$PACKAGES_UC" installed podkop-plus-definitely-missing >/dev/null 2>&1; then
if ucode -L "$FORKOP_LIB" "$PACKAGES_UC" installed forkop-definitely-missing >/dev/null 2>&1; then
fail "missing package must not be reported installed"
fi
mark_hex="$(ucode -L "$PODKOP_LIB" "$RULES_UC" mark-hex 0x01000000 2)"
mark_hex="$(ucode -L "$FORKOP_LIB" "$RULES_UC" mark-hex 0x01000000 2)"
[ "$mark_hex" = "0x01000002" ] ||
fail "providers/rules.uc mark math changed"
version="$(printf 'sing-box version 1.12.4-extended\nEnvironment: test\n' |
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" version-from-output)"
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" version-from-output)"
[ "$version" = "1.12.4-extended" ] ||
fail "singbox/runtime.uc version-from-output changed"
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" is-extended "1.12.4-extended" >/dev/null ||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" is-extended "1.12.4-extended" >/dev/null ||
fail "extended sing-box version should be detected"
if ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" is-extended "1.12.4" >/dev/null 2>&1; then
if ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" is-extended "1.12.4" >/dev/null 2>&1; then
fail "stable sing-box version must not be detected as extended"
fi
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" supports-tailscale "" "$(printf 'Tags: with_quic,with_tailscale\n')" >/dev/null ||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" supports-tailscale "" "$(printf 'Tags: with_quic,with_tailscale\n')" >/dev/null ||
fail "with_tailscale build tag should be detected"
SB_VERSION_STATE_FILE="$WORK_DIR/version" \
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" write-version-state 1.2.3 >/dev/null ||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" write-version-state 1.2.3 >/dev/null ||
fail "version state write failed"
[ "$(SB_VERSION_STATE_FILE="$WORK_DIR/version" ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" read-version-state)" = "1.2.3" ] ||
[ "$(SB_VERSION_STATE_FILE="$WORK_DIR/version" ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" read-version-state)" = "1.2.3" ] ||
fail "version state read failed"
SB_VERSION_STATE_FILE="$WORK_DIR/version" \
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" restore-version-state "" >/dev/null ||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" restore-version-state "" >/dev/null ||
fail "version state restore-clear failed"
[ ! -e "$WORK_DIR/version" ] ||
fail "empty version state restore must clear the state file"
SB_VARIANT_STATE_FILE="$WORK_DIR/variant" \
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" write-variant-marker extended-compressed >/dev/null ||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" write-variant-marker extended-compressed >/dev/null ||
fail "variant marker write failed"
SB_VARIANT_STATE_FILE="$WORK_DIR/variant" \
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" marker-is extended-compressed >/dev/null ||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" marker-is extended-compressed >/dev/null ||
fail "variant marker check failed"
[ "$(SB_VARIANT_STATE_FILE="$WORK_DIR/variant" ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" read-variant-marker)" = "extended-compressed" ] ||
[ "$(SB_VARIANT_STATE_FILE="$WORK_DIR/variant" ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" read-variant-marker)" = "extended-compressed" ] ||
fail "variant marker read failed"
SB_VARIANT_STATE_FILE="$WORK_DIR/variant" \
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" restore-variant-marker "" >/dev/null ||
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" restore-variant-marker "" >/dev/null ||
fail "variant marker restore-clear failed"
[ ! -e "$WORK_DIR/variant" ] ||
fail "empty variant marker restore must clear the marker file"
+11 -11
View File
@@ -2,10 +2,10 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
INITD_UC="$PODKOP_LIB/service/initd.uc"
STATE_UC="$PODKOP_LIB/service/state.uc"
INITD="$ROOT_DIR/podkop/files/etc/init.d/podkop"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
INITD_UC="$FORKOP_LIB/service/initd.uc"
STATE_UC="$FORKOP_LIB/service/state.uc"
INITD="$ROOT_DIR/forkop/files/etc/init.d/forkop"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -19,10 +19,10 @@ fail() {
}
initd_ucode() {
ucode -L "$PODKOP_LIB" "$INITD_UC" "$@"
ucode -L "$FORKOP_LIB" "$INITD_UC" "$@"
}
config_file="$WORK_DIR/podkop-plus"
config_file="$WORK_DIR/forkop"
guard_file="$WORK_DIR/internal-config-change"
sync_file="$WORK_DIR/service-triggers.sync"
@@ -89,7 +89,7 @@ if initd_ucode initd-should-queue-config-change-reload on_config_change on_confi
fi
pending_file="$WORK_DIR/reload.pending"
if PODKOP_PENDING_RELOAD_FILE="$pending_file" \
if FORKOP_PENDING_RELOAD_FILE="$pending_file" \
initd_ucode reload-begin-fixture on_config_change 123 0 1 start >/dev/null 2>&1; then
fail "queued config-change reload should not run immediately while service is starting"
fi
@@ -99,7 +99,7 @@ grep -Fq 'reason=on_config_change' "$pending_file" ||
fail "pending reload should preserve config-change reason"
rm -f "$pending_file"
if PODKOP_PENDING_RELOAD_FILE="$pending_file" \
if FORKOP_PENDING_RELOAD_FILE="$pending_file" \
initd_ucode reload-begin-fixture pending 123 1 1 start >/dev/null 2>&1; then
fail "pending reload should not run while another service action is active"
fi
@@ -229,7 +229,7 @@ fi
if grep -Fq 'runtime_is_running' "$INITD"; then
fail "init.d must not keep shell runtime status decisions"
fi
if grep -E -n 'PODKOP_(CONFIG_FILE|RELOAD_LOCK_DIR|RUNTIME_STATE_DIR|PENDING_RELOAD_FILE|SERVICE_TRIGGER_SYNC_FILE|INTERNAL_CONFIG_TRIGGER_GUARD|CONFIG_CHANGE_REASON|BIN|SERVICE_INIT)=' "$INITD" >/dev/null 2>&1; then
if grep -E -n 'FORKOP_(CONFIG_FILE|RELOAD_LOCK_DIR|RUNTIME_STATE_DIR|PENDING_RELOAD_FILE|SERVICE_TRIGGER_SYNC_FILE|INTERNAL_CONFIG_TRIGGER_GUARD|CONFIG_CHANGE_REASON|BIN|SERVICE_INIT)=' "$INITD" >/dev/null 2>&1; then
fail "init.d must not pass internal orchestration paths through shell env"
fi
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"' "$INITD_UC" >/dev/null 2>&1; then
@@ -238,10 +238,10 @@ fi
if grep -E -n '(^|[[:space:]])config_(load|get|get_bool|set)[[:space:]]' "$INITD" >/dev/null 2>&1; then
fail "init.d must not read UCI directly"
fi
if grep -Fq 'PODKOP_STATE_UC' "$INITD" || grep -Fq 'PODKOP_UI_UC' "$INITD" || grep -Fq 'PODKOP_STATUS_UC' "$INITD"; then
if grep -Fq 'FORKOP_STATE_UC' "$INITD" || grep -Fq 'FORKOP_UI_UC' "$INITD" || grep -Fq 'FORKOP_STATUS_UC' "$INITD"; then
fail "init.d must not orchestrate state/UI/status modules directly"
fi
if grep -Fq 'procd_set_param command "$PODKOP_BIN" start' "$INITD"; then
if grep -Fq 'procd_set_param command "$FORKOP_BIN" start' "$INITD"; then
fail "init.d must not hand the one-shot start operation to procd as a daemon"
fi
if grep -Fq 'initd-should-' "$STATE_UC"; then
+202 -66
View File
@@ -4,6 +4,9 @@ set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
INSTALLER="$ROOT_DIR/install.sh"
WORK_DIR="$(mktemp -d)"
LEGACY_BRAND="$(printf '\160\157\144\153\157\160')"
LEGACY_BACKEND="${LEGACY_BRAND}-plus"
LEGACY_CONFIG_ALT="${LEGACY_BRAND}_plus"
cleanup() {
rm -rf "$WORK_DIR"
@@ -21,7 +24,7 @@ if grep -n -E '(^|[;&|[:space:]])uci[[:space:]]+-q|command_exists[[:space:]]+uci
fail "install.sh must not own UCI reads/writes through shell"
fi
shell_sing_box_owner_symbols='REQUIRED_SING_BOX_VERSION|SING_BOX_EXTENDED_|SING_BOX_MANAGED_SERVICE_MARKER|remove_old_sing_box_if_needed|install_managed_sing_box_service_script|remove_managed_sing_box_service_script|disable_sing_box_service_config|prepare_sing_box_service_disabled|prepare_sing_box_package_service_install|stop_podkop_for_sing_box_install|pkg_install_sing_box_variant|pkg_install_name_downgrade|pkg_remove_sing_box_conflict|resolve_sing_box_extended_release|install_sing_box_extended_(package|binary)|restore_sing_box_after_failed|move_file_to_backup|validate_extended_sing_box_binary|archive-member-path|sing-box-extended-arch-suffix|sing-box-extended-asset-url|sing-box-extended-package-asset-url'
shell_sing_box_owner_symbols='REQUIRED_SING_BOX_VERSION|SING_BOX_EXTENDED_|SING_BOX_MANAGED_SERVICE_MARKER|remove_old_sing_box_if_needed|install_managed_sing_box_service_script|remove_managed_sing_box_service_script|disable_sing_box_service_config|prepare_sing_box_service_disabled|prepare_sing_box_package_service_install|stop_forkop_for_sing_box_install|pkg_install_sing_box_variant|pkg_install_name_downgrade|pkg_remove_sing_box_conflict|resolve_sing_box_extended_release|install_sing_box_extended_(package|binary)|restore_sing_box_after_failed|move_file_to_backup|validate_extended_sing_box_binary|archive-member-path|sing-box-extended-arch-suffix|sing-box-extended-asset-url|sing-box-extended-package-asset-url'
if grep -n -E "$shell_sing_box_owner_symbols" "$INSTALLER" >/dev/null; then
fail "install.sh must not contain shell sing-box install/runtime ownership"
fi
@@ -31,47 +34,57 @@ grep -Fq 'ensure_bootstrap_package "ucode-mod-fs"' "$INSTALLER" ||
grep -Fq 'ensure_bootstrap_package "ucode-mod-uci"' "$INSTALLER" ||
fail "install.sh must bootstrap ucode-mod-uci before embedded UCI helper use"
grep -Fq '/usr/bin/podkop-plus component_action sing_box "$action"' "$INSTALLER" ||
fail "selected sing-box install must delegate to podkop-plus component_action"
grep -Fq '/usr/bin/forkop component_action sing_box "$action"' "$INSTALLER" ||
fail "selected sing-box install must delegate to forkop component_action"
for action in install_stable install_tiny install_extended install_extended_compressed; do
grep -Fq "action=\"$action\"" "$INSTALLER" ||
fail "selected sing-box installer is missing action mapping: $action"
done
grep -Fq 'run_args([ INSTALLER_PODKOP_PLUS_BIN, "restore_dnsmasq" ])' "$INSTALLER" ||
fail "installer dnsmasq restore must prefer installed podkop-plus ucode entrypoint"
grep -Fq 'run_args([ bin_path, "restore_dnsmasq" ])' "$INSTALLER" ||
fail "installer dnsmasq restore must prefer the active backend entrypoint"
grep -Fq 'else if (mode == "dnsmasq-failsafe-restore")' "$INSTALLER" ||
fail "dnsmasq restore fallback mode must remain available in embedded ucode helper"
grep -Fq 'else if (mode == "installer-cleanup-legacy")' "$INSTALLER" ||
fail "installer cleanup must be exposed as an embedded ucode mode"
grep -Fq 'else if (mode == "installer-finalize-legacy")' "$INSTALLER" ||
fail "installer legacy file cleanup must be exposed as an embedded ucode mode"
grep -Fq 'else if (mode == "installer-post-install")' "$INSTALLER" ||
fail "installer post-install must be exposed as an embedded ucode mode"
grep -Fq 'install_json_ucode installer-cleanup-legacy' "$INSTALLER" ||
fail "install.sh cleanup must delegate to embedded ucode"
grep -Fq 'install_json_ucode installer-post-install' "$INSTALLER" ||
fail "install.sh post-install must delegate to embedded ucode"
grep -Fq 'installer_config_migration_path()' "$INSTALLER" ||
fail "install.sh must embed the one-time configuration migration"
grep -Fq 'ucode -L /usr/lib/forkop "$(installer_config_migration_path)" migrate' "$INSTALLER" ||
fail "install.sh must execute the embedded migration only during the legacy transition"
if grep -n -E 'restore_podkop_dnsmasq_failsafe|remember_service_state|stop_conflicting_services|deactivate_original_podkop_if_present|remove_conflicting_dns_proxy|pkg_remove_if_installed|pkg_remove_matching_prefix|pkg_list_installed_names' "$INSTALLER" >/dev/null 2>&1; then
if grep -n -E 'restore_forkop_dnsmasq_failsafe|remember_service_state|stop_conflicting_services|deactivate_original_forkop_if_present|remove_conflicting_dns_proxy|pkg_remove_if_installed|pkg_remove_matching_prefix|pkg_list_installed_names' "$INSTALLER" >/dev/null 2>&1; then
fail "install.sh must not keep shell cleanup/remove service owners"
fi
if grep -n -E 'rm -f /var/luci-indexcache|rm -f /tmp/luci-indexcache|/etc/init\.d/rpcd[[:space:]]+reload|/etc/init\.d/podkop-plus[[:space:]]+(start|stop|disable|enable|restart)|/etc/init\.d/podkop[[:space:]]+(stop|disable)' "$INSTALLER" >/dev/null 2>&1; then
if grep -n -E 'rm -f /var/luci-indexcache|rm -f /tmp/luci-indexcache|/etc/init\.d/rpcd[[:space:]]+reload|/etc/init\.d/forkop[[:space:]]+(start|stop|disable|enable|restart)|/etc/init\.d/forkop[[:space:]]+(stop|disable)' "$INSTALLER" >/dev/null 2>&1; then
fail "install.sh shell must not own service/cache lifecycle actions"
fi
awk '
/^[[:space:]]*main\(\)[[:space:]]*\{/ { in_main = 1 }
in_main && /ensure_bootstrap_ucode_runtime/ { ensure = NR }
in_main && /detect_legacy_installation/ { detect = NR }
in_main && /decide_i18n_installation/ { i18n = NR }
in_main && /install_packages/ { install_packages = NR }
in_main && /cleanup_legacy_installation/ { cleanup = NR }
in_main && /install_backend_package/ { backend = NR }
in_main && /migrate_legacy_configuration/ { migration = NR }
in_main && /install_ui_packages/ { ui = NR }
in_main && /install_selected_sing_box/ { sing_box = NR }
in_main && /^[[:space:]]*\}/ { in_main = 0 }
END {
if (ensure > 0 && i18n > 0 && ensure < i18n &&
install_packages > 0 && sing_box > 0 && install_packages < sing_box)
if (ensure > 0 && detect > ensure && i18n > detect && cleanup > i18n &&
backend > cleanup && migration > backend && ui > migration && sing_box > ui)
exit 0
exit 1
}
' "$INSTALLER" || fail "install.sh main order must bootstrap ucode before UCI reads and install backend before sing-box component_action"
' "$INSTALLER" || fail "install.sh main order must remove the legacy packages, install the backend, migrate config, and then finish installation"
helper="$WORK_DIR/install-json.uc"
awk '
@@ -81,8 +94,15 @@ awk '
' "$INSTALLER" > "$helper"
[ -s "$helper" ] || fail "failed to extract embedded installer ucode helper"
printf '%s\n' '{"tag_name":"v-test"}' | ucode "$helper" release-tag | grep -Fxq 'v-test' ||
printf '%s\n' '{"tag_name":"0.0.1"}' | ucode "$helper" release-tag | grep -Fxq '0.0.1' ||
fail "embedded helper release-tag mode must parse release JSON"
release_json='{"tag_name":"0.0.1","assets":[{"name":"forkop_0.0.1.ipk","browser_download_url":"https://example.com/forkop.ipk"}]}'
printf '%s' "$release_json" | ucode "$helper" release-asset-url backend ipk | grep -Fxq 'https://example.com/forkop.ipk' ||
fail "embedded helper must resolve the exact three-part Forkop package name"
if printf '%s' '{"tag_name":"0.0.1","assets":[{"name":"forkop_0.0.1_all.ipk","browser_download_url":"https://example.com/old.ipk"}]}' |
ucode "$helper" release-asset-url backend ipk | grep -q .; then
fail "embedded helper must reject package names outside the Forkop release format"
fi
cat >"$WORK_DIR/opkg" <<'SH'
#!/usr/bin/env sh
@@ -92,11 +112,18 @@ case "$1" in
'https-dns-proxy - 1.0' \
'luci-app-https-dns-proxy - 1.0' \
'luci-i18n-https-dns-proxy-ru - 1.0'
if [ -n "${FORKOP_INSTALLER_FAKE_LEGACY_BACKEND:-}" ]; then
printf '%s\n' \
"$FORKOP_INSTALLER_FAKE_LEGACY_BACKEND - 1.0" \
"luci-app-$FORKOP_INSTALLER_FAKE_LEGACY_BACKEND - 1.0" \
"luci-i18n-$FORKOP_INSTALLER_FAKE_LEGACY_BACKEND-ru - 1.0"
fi
;;
remove)
shift
[ "$1" = "--force-depends" ] && shift
printf '%s\n' "$1" >> "$PODKOP_INSTALLER_OPKG_LOG"
printf '%s\n' "$1" >> "$FORKOP_INSTALLER_OPKG_LOG"
[ "${FORKOP_INSTALLER_FAIL_REMOVE:-}" != "$1" ] || exit 1
;;
esac
exit 0
@@ -106,20 +133,22 @@ chmod 0755 "$WORK_DIR/opkg"
: >"$WORK_DIR/opkg.log"
printf '%s\n' '1' |
PATH="$WORK_DIR:$PATH" \
PODKOP_INSTALLER_OPKG_LOG="$WORK_DIR/opkg.log" \
PODKOP_INSTALLER_PODKOP_PLUS_INIT="$WORK_DIR/missing-podkop-plus-init" \
PODKOP_INSTALLER_PODKOP_PLUS_BIN="$WORK_DIR/missing-podkop-bin" \
PODKOP_INSTALLER_PODKOP_PLUS_LIB="$WORK_DIR/missing-podkop-lib" \
PODKOP_INSTALLER_PODKOP_PLUS_UCI_DEFAULTS="$WORK_DIR/missing-uci-defaults" \
PODKOP_INSTALLER_PODKOP_PLUS_LUCI_VIEW="$WORK_DIR/missing-luci-view" \
PODKOP_INSTALLER_MENU_JSON="$WORK_DIR/missing-menu.json" \
PODKOP_INSTALLER_ACL_JSON="$WORK_DIR/missing-acl.json" \
PODKOP_INSTALLER_RU_LMO="$WORK_DIR/missing-ru.lmo" \
PODKOP_INSTALLER_EN_LMO="$WORK_DIR/missing-en.lmo" \
PODKOP_INSTALLER_RU_LUA="$WORK_DIR/missing-ru.lua" \
PODKOP_INSTALLER_EN_LUA="$WORK_DIR/missing-en.lua" \
PODKOP_INSTALLER_ORIGINAL_PODKOP_INIT="$WORK_DIR/missing-original-init" \
PODKOP_UCI_STATE_FILE="$WORK_DIR/empty-uci.state" \
FORKOP_INSTALLER_OPKG_LOG="$WORK_DIR/opkg.log" \
FORKOP_INSTALLER_INIT="$WORK_DIR/missing-forkop-init" \
FORKOP_INSTALLER_BIN="$WORK_DIR/missing-forkop-bin" \
FORKOP_INSTALLER_LIB="$WORK_DIR/missing-forkop-lib" \
FORKOP_INSTALLER_UCI_DEFAULTS="$WORK_DIR/missing-uci-defaults" \
FORKOP_INSTALLER_LUCI_VIEW="$WORK_DIR/missing-luci-view" \
FORKOP_INSTALLER_MENU_JSON="$WORK_DIR/missing-menu.json" \
FORKOP_INSTALLER_ACL_JSON="$WORK_DIR/missing-acl.json" \
FORKOP_INSTALLER_RU_LMO="$WORK_DIR/missing-ru.lmo" \
FORKOP_INSTALLER_EN_LMO="$WORK_DIR/missing-en.lmo" \
FORKOP_INSTALLER_RU_LUA="$WORK_DIR/missing-ru.lua" \
FORKOP_INSTALLER_EN_LUA="$WORK_DIR/missing-en.lua" \
FORKOP_INSTALLER_LEGACY_BASE_INIT="$WORK_DIR/missing-original-init" \
FORKOP_INSTALLER_LEGACY_BRAND="$LEGACY_BRAND" \
FORKOP_INSTALLER_LEGACY_BACKEND="$LEGACY_BACKEND" \
FORKOP_UCI_STATE_FILE="$WORK_DIR/empty-uci.state" \
ucode "$helper" installer-cleanup-legacy >"$WORK_DIR/conflict-state.env" 2>"$WORK_DIR/conflict.err"
grep -Fxq 'https-dns-proxy' "$WORK_DIR/opkg.log" ||
@@ -129,76 +158,183 @@ grep -Fxq 'luci-app-https-dns-proxy' "$WORK_DIR/opkg.log" ||
grep -Fxq 'luci-i18n-https-dns-proxy-ru' "$WORK_DIR/opkg.log" ||
fail "installer cleanup must remove confirmed https-dns-proxy i18n packages"
write_fake_podkop_init() {
cat >"$WORK_DIR/podkop-plus-init" <<'SH'
write_fake_service_init() {
service_path="$1"
cat >"$service_path" <<'SH'
#!/usr/bin/env sh
case "$1" in
enabled) exit 0 ;;
status) printf '%s\n' running; exit 0 ;;
stop|disable|enable|start|restart) printf '%s\n' "$1" >> "$PODKOP_INSTALLER_INIT_LOG"; exit 0 ;;
stop|disable|enable|start|restart) printf '%s\n' "$1" >> "$FORKOP_INSTALLER_INIT_LOG"; exit 0 ;;
esac
exit 0
SH
chmod 0755 "$WORK_DIR/podkop-plus-init"
chmod 0755 "$service_path"
}
write_fake_podkop_init
write_fake_service_init "$WORK_DIR/legacy-init"
cat >"$WORK_DIR/podkop-bin" <<'SH'
cat >"$WORK_DIR/legacy-bin" <<'SH'
#!/usr/bin/env sh
printf '%s\n' "$*" >> "$PODKOP_INSTALLER_BIN_LOG"
printf '%s\n' "$*" >> "$FORKOP_INSTALLER_BIN_LOG"
case "$1" in
get_status) printf '%s\n' '{"running":1}' ;;
esac
exit 0
SH
chmod 0755 "$WORK_DIR/podkop-bin"
chmod 0755 "$WORK_DIR/legacy-bin"
: >"$WORK_DIR/init.log"
: >"$WORK_DIR/bin.log"
state="$WORK_DIR/state.env"
PODKOP_INSTALLER_PODKOP_PLUS_INIT="$WORK_DIR/podkop-plus-init" \
PODKOP_INSTALLER_PODKOP_PLUS_BIN="$WORK_DIR/podkop-bin" \
PODKOP_INSTALLER_PODKOP_PLUS_LIB="$WORK_DIR/podkop-lib" \
PODKOP_INSTALLER_PODKOP_PLUS_UCI_DEFAULTS="$WORK_DIR/uci-defaults" \
PODKOP_INSTALLER_PODKOP_PLUS_LUCI_VIEW="$WORK_DIR/luci-view" \
PODKOP_INSTALLER_MENU_JSON="$WORK_DIR/menu.json" \
PODKOP_INSTALLER_ACL_JSON="$WORK_DIR/acl.json" \
PODKOP_INSTALLER_RU_LMO="$WORK_DIR/ru.lmo" \
PODKOP_INSTALLER_EN_LMO="$WORK_DIR/en.lmo" \
PODKOP_INSTALLER_RU_LUA="$WORK_DIR/ru.lua" \
PODKOP_INSTALLER_EN_LUA="$WORK_DIR/en.lua" \
PODKOP_INSTALLER_ORIGINAL_PODKOP_INIT="$WORK_DIR/missing-original-init" \
PODKOP_INSTALLER_INIT_LOG="$WORK_DIR/init.log" \
PODKOP_INSTALLER_BIN_LOG="$WORK_DIR/bin.log" \
PODKOP_UCI_STATE_FILE="$WORK_DIR/empty-uci.state" \
PATH="$WORK_DIR:$PATH" \
FORKOP_INSTALLER_INIT="$WORK_DIR/missing-forkop-init" \
FORKOP_INSTALLER_BIN="$WORK_DIR/missing-forkop-bin" \
FORKOP_INSTALLER_LIB="$WORK_DIR/missing-forkop-lib" \
FORKOP_INSTALLER_UCI_DEFAULTS="$WORK_DIR/uci-defaults" \
FORKOP_INSTALLER_LUCI_VIEW="$WORK_DIR/luci-view" \
FORKOP_INSTALLER_MENU_JSON="$WORK_DIR/menu.json" \
FORKOP_INSTALLER_ACL_JSON="$WORK_DIR/acl.json" \
FORKOP_INSTALLER_RU_LMO="$WORK_DIR/ru.lmo" \
FORKOP_INSTALLER_EN_LMO="$WORK_DIR/en.lmo" \
FORKOP_INSTALLER_RU_LUA="$WORK_DIR/ru.lua" \
FORKOP_INSTALLER_EN_LUA="$WORK_DIR/en.lua" \
FORKOP_INSTALLER_LEGACY_BASE_INIT="$WORK_DIR/missing-original-init" \
FORKOP_INSTALLER_LEGACY_BRAND="$LEGACY_BRAND" \
FORKOP_INSTALLER_LEGACY_BACKEND="$LEGACY_BACKEND" \
FORKOP_INSTALLER_LEGACY_CONFIG_ALT="$LEGACY_CONFIG_ALT" \
FORKOP_INSTALLER_LEGACY_INIT="$WORK_DIR/legacy-init" \
FORKOP_INSTALLER_LEGACY_BIN="$WORK_DIR/legacy-bin" \
FORKOP_INSTALLER_LEGACY_LIB="$WORK_DIR/legacy-lib" \
FORKOP_INSTALLER_FAKE_LEGACY_BACKEND="$LEGACY_BACKEND" \
FORKOP_INSTALLER_OPKG_LOG="$WORK_DIR/opkg.log" \
FORKOP_INSTALLER_INIT_LOG="$WORK_DIR/init.log" \
FORKOP_INSTALLER_BIN_LOG="$WORK_DIR/bin.log" \
FORKOP_UCI_STATE_FILE="$WORK_DIR/empty-uci.state" \
ucode "$helper" installer-cleanup-legacy >"$state"
grep -Fxq 'PODKOP_WAS_ENABLED=1' "$state" ||
grep -Fxq 'FORKOP_WAS_ENABLED=1' "$state" ||
fail "installer cleanup must export previous enabled state"
grep -Fxq 'PODKOP_WAS_RUNNING=1' "$state" ||
grep -Fxq 'FORKOP_WAS_RUNNING=1' "$state" ||
fail "installer cleanup must export previous running state"
grep -Fxq 'FORKOP_LEGACY_DETECTED=1' "$state" ||
fail "installer cleanup must report the legacy package transition"
grep -Fxq 'stop' "$WORK_DIR/init.log" ||
fail "installer cleanup must stop old Podkop Plus through ucode owner"
fail "installer cleanup must stop the legacy service through ucode owner"
grep -Fxq 'disable' "$WORK_DIR/init.log" ||
fail "installer cleanup must disable old Podkop Plus through ucode owner"
fail "installer cleanup must disable the legacy service through ucode owner"
grep -Fxq 'restore_dnsmasq' "$WORK_DIR/bin.log" ||
fail "installer cleanup must prefer backend restore_dnsmasq"
grep -Fxq "$LEGACY_BACKEND" "$WORK_DIR/opkg.log" ||
fail "installer cleanup must remove the legacy backend package"
grep -Fxq "luci-app-$LEGACY_BACKEND" "$WORK_DIR/opkg.log" ||
fail "installer cleanup must remove the legacy LuCI package"
write_fake_podkop_init
if printf '%s\n' '1' |
PATH="$WORK_DIR:$PATH" \
FORKOP_INSTALLER_OPKG_LOG="$WORK_DIR/opkg.log" \
FORKOP_INSTALLER_FAIL_REMOVE="$LEGACY_BACKEND" \
FORKOP_INSTALLER_FAKE_LEGACY_BACKEND="$LEGACY_BACKEND" \
FORKOP_INSTALLER_LEGACY_BRAND="$LEGACY_BRAND" \
FORKOP_INSTALLER_LEGACY_BACKEND="$LEGACY_BACKEND" \
FORKOP_UCI_STATE_FILE="$WORK_DIR/empty-uci.state" \
ucode "$helper" installer-cleanup-legacy >/dev/null 2>&1; then
fail "installer cleanup must stop when the package manager cannot remove the legacy backend"
fi
legacy_config="$WORK_DIR/legacy-config"
legacy_config_alt="$WORK_DIR/legacy-config-alt"
legacy_persistent="$WORK_DIR/legacy-persistent"
legacy_runtime="$WORK_DIR/legacy-runtime"
legacy_tmp="$WORK_DIR/legacy-tmp"
legacy_tmp_alt="$WORK_DIR/legacy-tmp-alt"
legacy_base_config="$WORK_DIR/legacy-base-config"
legacy_base_persistent="$WORK_DIR/legacy-base-persistent"
legacy_base_runtime="$WORK_DIR/legacy-base-runtime"
legacy_base_tmp="$WORK_DIR/legacy-base-tmp"
legacy_base_init="$WORK_DIR/legacy-base-init"
legacy_base_bin="$WORK_DIR/legacy-base-bin"
legacy_base_lib="$WORK_DIR/legacy-base-lib"
legacy_base_uci_defaults="$WORK_DIR/legacy-base-uci-defaults"
legacy_base_luci_view="$WORK_DIR/legacy-base-luci-view"
legacy_base_menu="$WORK_DIR/legacy-base-menu"
legacy_base_acl="$WORK_DIR/legacy-base-acl"
legacy_base_i18n="$WORK_DIR/legacy-base-i18n"
legacy_tmp_package="$WORK_DIR/luci-app-${LEGACY_BRAND}.ipk"
legacy_scan_root="$WORK_DIR/legacy-scan-root"
legacy_nested_uci="$legacy_scan_root/.uci/${LEGACY_BACKEND}"
legacy_nested_lock="$legacy_scan_root/lock/procd_${LEGACY_BACKEND}.lock"
legacy_nested_backup="$legacy_scan_root/audit/${LEGACY_BACKEND}.config"
mkdir -p "$legacy_persistent" "$legacy_runtime" "$legacy_tmp" "$legacy_tmp_alt"
mkdir -p "$(dirname "$legacy_nested_uci")" "$(dirname "$legacy_nested_lock")" "$(dirname "$legacy_nested_backup")"
touch "$legacy_config" "$legacy_config_alt" "$legacy_config-opkg"
touch \
"$legacy_base_config.backup" \
"$legacy_base_persistent.cache" \
"$legacy_base_runtime.internal" \
"$legacy_base_tmp.log" \
"$legacy_base_init.old" \
"$legacy_base_bin.bak" \
"$legacy_base_lib.prev" \
"$legacy_base_uci_defaults.done" \
"$legacy_base_luci_view.old" \
"$legacy_base_menu.json" \
"$legacy_base_acl.json" \
"$legacy_base_i18n.ru.lmo" \
"$legacy_tmp_package" \
"$legacy_nested_uci" \
"$legacy_nested_lock" \
"$legacy_nested_backup"
FORKOP_INSTALLER_LEGACY_BRAND="$LEGACY_BRAND" \
FORKOP_INSTALLER_LEGACY_BACKEND="$LEGACY_BACKEND" \
FORKOP_INSTALLER_LEGACY_CONFIG_ALT="$LEGACY_CONFIG_ALT" \
FORKOP_INSTALLER_LEGACY_CONFIG="$legacy_config" \
FORKOP_INSTALLER_LEGACY_CONFIG_FILE_ALT="$legacy_config_alt" \
FORKOP_INSTALLER_LEGACY_PERSISTENT_DIR="$legacy_persistent" \
FORKOP_INSTALLER_LEGACY_RUNTIME_DIR="$legacy_runtime" \
FORKOP_INSTALLER_LEGACY_TMP_DIR="$legacy_tmp" \
FORKOP_INSTALLER_LEGACY_TMP_ALT_DIR="$legacy_tmp_alt" \
FORKOP_INSTALLER_LEGACY_BASE_CONFIG="$legacy_base_config" \
FORKOP_INSTALLER_LEGACY_BASE_PERSISTENT_DIR="$legacy_base_persistent" \
FORKOP_INSTALLER_LEGACY_BASE_RUNTIME_DIR="$legacy_base_runtime" \
FORKOP_INSTALLER_LEGACY_BASE_TMP_DIR="$legacy_base_tmp" \
FORKOP_INSTALLER_LEGACY_BASE_INIT="$legacy_base_init" \
FORKOP_INSTALLER_LEGACY_BASE_BIN="$legacy_base_bin" \
FORKOP_INSTALLER_LEGACY_BASE_LIB="$legacy_base_lib" \
FORKOP_INSTALLER_LEGACY_BASE_UCI_DEFAULTS="$legacy_base_uci_defaults" \
FORKOP_INSTALLER_LEGACY_BASE_LUCI_VIEW="$legacy_base_luci_view" \
FORKOP_INSTALLER_LEGACY_BASE_MENU_JSON="$legacy_base_menu" \
FORKOP_INSTALLER_LEGACY_BASE_ACL_JSON="$legacy_base_acl" \
FORKOP_INSTALLER_LEGACY_BASE_I18N="$legacy_base_i18n" \
FORKOP_INSTALLER_LEGACY_TMP_PACKAGE_GLOB="$WORK_DIR/*${LEGACY_BRAND}*" \
FORKOP_INSTALLER_LEGACY_SCAN_ROOTS="$legacy_scan_root" \
ucode "$helper" installer-finalize-legacy
for path in "$legacy_config" "$legacy_config_alt" "$legacy_config-opkg" \
"$legacy_persistent" "$legacy_runtime" "$legacy_tmp" "$legacy_tmp_alt" \
"$legacy_base_config.backup" "$legacy_base_persistent.cache" \
"$legacy_base_runtime.internal" "$legacy_base_tmp.log" \
"$legacy_base_init.old" "$legacy_base_bin.bak" "$legacy_base_lib.prev" \
"$legacy_base_uci_defaults.done" "$legacy_base_luci_view.old" \
"$legacy_base_menu.json" "$legacy_base_acl.json" "$legacy_base_i18n.ru.lmo" \
"$legacy_tmp_package" "$legacy_nested_uci" "$legacy_nested_lock" \
"$legacy_nested_backup"; do
[ ! -e "$path" ] || fail "installer finalization left a legacy path behind: $path"
done
write_fake_service_init "$WORK_DIR/forkop-init"
touch "$WORK_DIR/luci-indexcache.one" "$WORK_DIR/luci-indexcache.two"
: >"$WORK_DIR/init.log"
PODKOP_INSTALLER_PODKOP_PLUS_INIT="$WORK_DIR/podkop-plus-init" \
PODKOP_INSTALLER_RPCD_INIT="$WORK_DIR/missing-rpcd" \
PODKOP_INSTALLER_LUCI_CACHE_GLOBS="$WORK_DIR/luci-indexcache*" \
PODKOP_INSTALLER_LATEST_VERSION_CACHE="$WORK_DIR/latest.cache" \
PODKOP_INSTALLER_SYSTEM_INFO_CACHE="$WORK_DIR/system-info.json" \
PODKOP_INSTALLER_SERVER_COUNTRY_CACHE="$WORK_DIR/server-country.json" \
PODKOP_INSTALLER_SING_BOX_VERSION_CACHE="$WORK_DIR/sing-box-version" \
PODKOP_INSTALLER_TMP_SYSTEM_INFO_CACHE="$WORK_DIR/tmp-system-info.json" \
PODKOP_WAS_ENABLED=1 \
PODKOP_WAS_RUNNING=1 \
PODKOP_INSTALLER_INIT_LOG="$WORK_DIR/init.log" \
FORKOP_INSTALLER_INIT="$WORK_DIR/forkop-init" \
FORKOP_INSTALLER_RPCD_INIT="$WORK_DIR/missing-rpcd" \
FORKOP_INSTALLER_LUCI_CACHE_GLOBS="$WORK_DIR/luci-indexcache*" \
FORKOP_INSTALLER_LATEST_VERSION_CACHE="$WORK_DIR/latest.cache" \
FORKOP_INSTALLER_SYSTEM_INFO_CACHE="$WORK_DIR/system-info.json" \
FORKOP_INSTALLER_SERVER_COUNTRY_CACHE="$WORK_DIR/server-country.json" \
FORKOP_INSTALLER_SING_BOX_VERSION_CACHE="$WORK_DIR/sing-box-version" \
FORKOP_INSTALLER_TMP_SYSTEM_INFO_CACHE="$WORK_DIR/tmp-system-info.json" \
FORKOP_WAS_ENABLED=1 \
FORKOP_WAS_RUNNING=1 \
FORKOP_INSTALLER_INIT_LOG="$WORK_DIR/init.log" \
ucode "$helper" installer-post-install
if compgen -G "$WORK_DIR/luci-indexcache*" >/dev/null; then
+5 -5
View File
@@ -12,8 +12,8 @@ reject_runtime_pattern() {
local pattern="$1"
local label="$2"
if rg -n --fixed-strings "$pattern" "$ROOT_DIR/podkop/files" >/dev/null 2>&1; then
rg -n --fixed-strings "$pattern" "$ROOT_DIR/podkop/files" >&2
if rg -n --fixed-strings "$pattern" "$ROOT_DIR/forkop/files" >/dev/null 2>&1; then
rg -n --fixed-strings "$pattern" "$ROOT_DIR/forkop/files" >&2
fail "$label"
fi
}
@@ -22,8 +22,8 @@ reject_runtime_regex() {
local pattern="$1"
local label="$2"
if rg -n "$pattern" "$ROOT_DIR/podkop/files" >/dev/null 2>&1; then
rg -n "$pattern" "$ROOT_DIR/podkop/files" >&2
if rg -n "$pattern" "$ROOT_DIR/forkop/files" >/dev/null 2>&1; then
rg -n "$pattern" "$ROOT_DIR/forkop/files" >&2
fail "$label"
fi
}
@@ -44,7 +44,7 @@ reject_runtime_pattern "Current sing-box config hash:" \
"sing-box config logs must not emit duplicate hash lines"
reject_runtime_pattern "Temporary sing-box config hash:" \
"sing-box config logs must not emit duplicate hash lines"
reject_runtime_pattern "Podkop Plus did not reach a stable running state after start" \
reject_runtime_pattern "Forkop did not reach a stable running state after start" \
"startup rollback logs must not be fatal secondary errors"
reject_runtime_pattern "Flush nft" \
"stop logs must not expose routine nft cleanup as user-facing info"
+5 -5
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
VALIDATOR="$PODKOP_LIB/config/validator.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
VALIDATOR="$FORKOP_LIB/config/validator.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -42,7 +42,7 @@ assert_mark_range_no_overlap() {
done
}
eval "$(ucode -L "$PODKOP_LIB" "$PODKOP_LIB/core/constants.uc" shell-env)"
eval "$(ucode -L "$FORKOP_LIB" "$FORKOP_LIB/core/constants.uc" shell-env)"
assert_mark_range_no_overlap "Zapret" "$((ZAPRET_ROUTE_MARK_BASE))" "$ZAPRET_QUEUE_RANGE_SIZE" "$((NFT_FAKEIP_MARK))" "FakeIP"
assert_mark_range_no_overlap "Zapret" "$((ZAPRET_ROUTE_MARK_BASE))" "$ZAPRET_QUEUE_RANGE_SIZE" "$((NFT_OUTBOUND_MARK))" "outbound"
@@ -78,9 +78,9 @@ context_json() {
JSON
}
PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/fixture.json" "$(context_json)"
FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/fixture.json" "$(context_json)"
if PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/fixture.json" "$(context_json "0x01100000")" >/dev/null 2>&1; then
if FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/fixture.json" "$(context_json "0x01100000")" >/dev/null 2>&1; then
fail "legacy Zapret2 route mark base should overlap FakeIP mark"
fi
+117 -117
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
NFT_RUNTIME="$ROOT_DIR/podkop/files/usr/lib/nft/apply.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
NFT_RUNTIME="$ROOT_DIR/forkop/files/usr/lib/nft/apply.uc"
WORK_DIR="$(mktemp -d)"
NFT_LOG="$WORK_DIR/nft.log"
LOGGER_LOG="$WORK_DIR/logger.log"
@@ -11,7 +11,7 @@ IP_LOG="$WORK_DIR/ip.log"
SYSCTL_LOG="$WORK_DIR/sysctl.log"
nft_ucode() {
ucode -L "$PODKOP_LIB" "$NFT_RUNTIME" "$@"
ucode -L "$FORKOP_LIB" "$NFT_RUNTIME" "$@"
}
cleanup() {
@@ -171,10 +171,10 @@ export LOGGER_LOG
export IP_LOG
export SYSCTL_LOG
assert_eq "/tmp/podkop-cache/condition_rule_1_domain_domains" \
"$(nft_ucode cache-path 1 /tmp/podkop-cache condition rule_1 domain domains)" \
assert_eq "/tmp/forkop-cache/condition_rule_1_domain_domains" \
"$(nft_ucode cache-path 1 /tmp/forkop-cache condition rule_1 domain domains)" \
"cache path"
if nft_ucode cache-path 1 /tmp/podkop-cache condition 'bad-name' domain domains >/dev/null 2>&1; then
if nft_ucode cache-path 1 /tmp/forkop-cache condition 'bad-name' domain domains >/dev/null 2>&1; then
fail "unsafe cache key should fail"
fi
@@ -206,55 +206,55 @@ assert_eq "^xn--80aswg[.]xn--p1ai$" \
"$(nft_ucode rule-condition-csv domain_regex generic 0 0 '' '' 'сайт.рф full:пример.испытание keyword:пример regex:^сайт[.]рф$' '')" \
"combined IDN regex is punycoded"
nft_ucode nft-create-runtime-base PodkopPlusTable localv4 podkop_plus_subnets podkop_plus_ports podkop_plus_ip_ports podkop_plus_interfaces "br-lan tun0" 0x00100000 0x00200000 198.18.0.0/15 1602 1
assert_contains "$NFT_LOG" $'nft\tadd\ttable\tinet\tPodkopPlusTable' "runtime table"
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tlocalv4\t{ type ipv4_addr; flags interval; auto-merge; }' "runtime localv4 set"
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tlocalv6\t{ type ipv6_addr; flags interval; auto-merge; }' "runtime localv6 set"
nft_ucode nft-create-runtime-base ForkopTable localv4 forkop_subnets forkop_ports forkop_ip_ports forkop_interfaces "br-lan tun0" 0x00100000 0x00200000 198.18.0.0/15 1602 1
assert_contains "$NFT_LOG" $'nft\tadd\ttable\tinet\tForkopTable' "runtime table"
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tForkopTable\tlocalv4\t{ type ipv4_addr; flags interval; auto-merge; }' "runtime localv4 set"
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tForkopTable\tlocalv6\t{ type ipv6_addr; flags interval; auto-merge; }' "runtime localv6 set"
assert_contains "$NFT_LOG" '0.0.0.0/8,10.0.0.0/8,127.0.0.0/8' "runtime localv4 elements"
assert_contains "$NFT_LOG" '::/128,::1/128,64:ff9b::/96' "runtime localv6 elements"
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tpodkop_plus_interfaces\t{ type ifname; flags interval; }' "runtime interface set"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_interfaces\t{ br-lan }' "runtime br-lan interface"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_interfaces\t{ tun0 }' "runtime tun0 interface"
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tPodkopPlusTable\tmangle\t{ type filter hook prerouting priority -150; policy accept; }' "runtime mangle chain"
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tPodkopPlusTable\tpriority_rules\t{ }' "runtime priority chain"
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tPodkopPlusTable\tpriority_output_rules\t{ }' "runtime priority output chain"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle\tjump\tpriority_rules' "runtime priority jump"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t@podkop_plus_subnets\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime common tcp rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip6\tdaddr\t@podkop_plus_subnets6\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime common6 tcp rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tproxy\tmeta\tmark\t&\t0x00100000\t==\t0x00100000\tmeta\tl4proto\ttcp\ttproxy\tip\tto\t:1602\tcounter' "runtime proxy tcp rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tproxy\tmeta\tmark\t&\t0x00100000\t==\t0x00100000\tmeta\tl4proto\ttcp\ttproxy\tip6\tto\t[::1]:1602\tcounter' "runtime proxy6 tcp rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tmeta\tmark\t0x00200000\tcounter\treturn' "runtime outbound return"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tjump\tpriority_output_rules' "runtime priority output jump"
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tPodkopPlusTable\tmangle\tudp\tdport\t123\treturn' "runtime ntp exclusion"
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tForkopTable\tforkop_interfaces\t{ type ifname; flags interval; }' "runtime interface set"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_interfaces\t{ br-lan }' "runtime br-lan interface"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_interfaces\t{ tun0 }' "runtime tun0 interface"
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tForkopTable\tmangle\t{ type filter hook prerouting priority -150; policy accept; }' "runtime mangle chain"
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tForkopTable\tpriority_rules\t{ }' "runtime priority chain"
assert_contains "$NFT_LOG" $'nft\tadd\tchain\tinet\tForkopTable\tpriority_output_rules\t{ }' "runtime priority output chain"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle\tjump\tpriority_rules' "runtime priority jump"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle\tiifname\t@forkop_interfaces\tip\tdaddr\t@forkop_subnets\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime common tcp rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle\tiifname\t@forkop_interfaces\tip6\tdaddr\t@forkop_subnets6\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime common6 tcp rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tproxy\tmeta\tmark\t&\t0x00100000\t==\t0x00100000\tmeta\tl4proto\ttcp\ttproxy\tip\tto\t:1602\tcounter' "runtime proxy tcp rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tproxy\tmeta\tmark\t&\t0x00100000\t==\t0x00100000\tmeta\tl4proto\ttcp\ttproxy\tip6\tto\t[::1]:1602\tcounter' "runtime proxy6 tcp rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tmeta\tmark\t0x00200000\tcounter\treturn' "runtime outbound return"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tjump\tpriority_output_rules' "runtime priority output jump"
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tForkopTable\tmangle\tudp\tdport\t123\treturn' "runtime ntp exclusion"
cat >"$WORK_DIR/runtime-base-uci.state" <<'EOF_UCI'
podkop-plus.settings=settings
podkop-plus.settings.source_network_interfaces=br-lan tun0
podkop-plus.settings.exclude_ntp=1
forkop.settings=settings
forkop.settings.source_network_interfaces=br-lan tun0
forkop.settings.exclude_ntp=1
EOF_UCI
: > "$NFT_LOG"
PODKOP_UCI_STATE_FILE="$WORK_DIR/runtime-base-uci.state" \
nft_ucode nft-create-runtime-base-from-uci PodkopPlusTable localv4 podkop_plus_subnets podkop_plus_ports podkop_plus_ip_ports podkop_plus_interfaces 0x00100000 0x00200000 198.18.0.0/15 1602
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_interfaces\t{ br-lan }' "runtime base from UCI br-lan interface"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_interfaces\t{ tun0 }' "runtime base from UCI tun0 interface"
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tPodkopPlusTable\tmangle\tudp\tdport\t123\treturn' "runtime base from UCI ntp exclusion"
FORKOP_UCI_STATE_FILE="$WORK_DIR/runtime-base-uci.state" \
nft_ucode nft-create-runtime-base-from-uci ForkopTable localv4 forkop_subnets forkop_ports forkop_ip_ports forkop_interfaces 0x00100000 0x00200000 198.18.0.0/15 1602
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_interfaces\t{ br-lan }' "runtime base from UCI br-lan interface"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_interfaces\t{ tun0 }' "runtime base from UCI tun0 interface"
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tForkopTable\tmangle\tudp\tdport\t123\treturn' "runtime base from UCI ntp exclusion"
: > "$NFT_LOG"
nft_ucode nft-create-runtime-output-rules PodkopPlusTable localv4 podkop_plus_subnets podkop_plus_ports podkop_plus_ip_ports 0x00100000 198.18.0.0/15
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tip\tdaddr\t@podkop_plus_subnets\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output common tcp"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tip6\tdaddr\t@podkop_plus_subnets6\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output common6 tcp"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tip\tdaddr\t.\ttcp\tdport\t@podkop_plus_ip_ports\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output ip-port tcp"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tip6\tdaddr\t.\ttcp\tdport\t@podkop_plus_ip6_ports\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output ip6-port tcp"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\ttcp\tdport\t@podkop_plus_ports\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output port tcp"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tip\tdaddr\t198.18.0.0/15\tmeta\tl4proto\tudp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output fakeip udp"
nft_ucode nft-create-runtime-output-rules ForkopTable localv4 forkop_subnets forkop_ports forkop_ip_ports 0x00100000 198.18.0.0/15
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tip\tdaddr\t@forkop_subnets\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output common tcp"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tip6\tdaddr\t@forkop_subnets6\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output common6 tcp"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tip\tdaddr\t.\ttcp\tdport\t@forkop_ip_ports\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output ip-port tcp"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tip6\tdaddr\t.\ttcp\tdport\t@forkop_ip6_ports\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output ip6-port tcp"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\ttcp\tdport\t@forkop_ports\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output port tcp"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tip\tdaddr\t198.18.0.0/15\tmeta\tl4proto\tudp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output fakeip udp"
: > "$NFT_LOG"
if NFT_LIST_TABLE_FAIL=1 nft_ucode nft-table-present-fixture PodkopPlusTable 2>"$WORK_DIR/nft-table-present.err"; then
if NFT_LIST_TABLE_FAIL=1 nft_ucode nft-table-present-fixture ForkopTable 2>"$WORK_DIR/nft-table-present.err"; then
fail "missing nft table should return false"
fi
[ ! -s "$WORK_DIR/nft-table-present.err" ] ||
fail "missing nft table predicate must suppress nft stderr"
assert_contains "$NFT_LOG" $'nft\tlist\ttable\tinet\tPodkopPlusTable' "nft table presence check"
assert_contains "$NFT_LOG" $'nft\tlist\ttable\tinet\tForkopTable' "nft table presence check"
cat >"$WORK_DIR/provider-rules.json" <<'JSON'
{
@@ -272,16 +272,16 @@ printf '#!/usr/bin/env sh\nexit 0\n' >"$provider_bin"
chmod 0755 "$provider_bin"
: > "$NFT_LOG"
nft_ucode nft-create-provider-output-rules-fixture "$WORK_DIR/provider-rules.json" PodkopPlusTable zapret "$provider_bin" 0x01000000 4000 0x40000000 0x20000000
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tmeta\tmark\t&\t0x40000000\t==\t0x40000000\treturn' "zapret desync return"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tmeta\tmark\t0x01000001\tmeta\tl4proto\ttcp\tcounter\tqueue\tnum\t4000\tbypass' "zapret first tcp queue rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tmangle_output\tmeta\tmark\t0x01000002\tmeta\tl4proto\tudp\tcounter\tqueue\tnum\t4001\tbypass' "zapret second udp queue rule"
nft_ucode nft-create-provider-output-rules-fixture "$WORK_DIR/provider-rules.json" ForkopTable zapret "$provider_bin" 0x01000000 4000 0x40000000 0x20000000
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tmeta\tmark\t&\t0x40000000\t==\t0x40000000\treturn' "zapret desync return"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tmeta\tmark\t0x01000001\tmeta\tl4proto\ttcp\tcounter\tqueue\tnum\t4000\tbypass' "zapret first tcp queue rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tmangle_output\tmeta\tmark\t0x01000002\tmeta\tl4proto\tudp\tcounter\tqueue\tnum\t4001\tbypass' "zapret second udp queue rule"
if grep -Fq 'zapret_disabled' "$NFT_LOG"; then
fail "disabled provider section should not create nft rule"
fi
: > "$NFT_LOG"
nft_ucode nft-create-provider-output-rules-fixture "$WORK_DIR/provider-rules.json" PodkopPlusTable zapret "$WORK_DIR/bin/missing-provider" 0x01000000 4000 0x40000000 0x20000000
nft_ucode nft-create-provider-output-rules-fixture "$WORK_DIR/provider-rules.json" ForkopTable zapret "$WORK_DIR/bin/missing-provider" 0x01000000 4000 0x40000000 0x20000000
if [ -s "$NFT_LOG" ]; then
fail "missing provider binary should skip provider nft output rules"
fi
@@ -315,26 +315,26 @@ cat >"$WORK_DIR/priority-rules.json" <<'JSON'
JSON
: > "$NFT_LOG"
nft_ucode nft-add-section-priority-rules-fixture "$WORK_DIR/priority-rules.json" PodkopPlusTable podkop_plus_interfaces localv4 localv6 0x00100000
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tpodkop_plus_rule_bypass_first_subnets\t{ type ipv4_addr; flags interval; auto-merge; }' "bypass priority subnet set"
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tPodkopPlusTable\tpodkop_plus_rule_wide_proxy_subnets\t{ type ipv4_addr; flags interval; auto-merge; }' "proxy priority subnet set"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@podkop_plus_rule_bypass_first_subnets\tcounter\taccept' "bypass priority accept rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@podkop_plus_rule_wide_proxy_subnets\tmeta\tmark\tset\t0x00100000\tcounter\taccept' "proxy priority capture rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\ttcp\tdport\t@podkop_plus_rule_port_bypass_ports\tcounter\taccept' "port-only bypass priority rule"
nft_ucode nft-add-section-priority-rules-fixture "$WORK_DIR/priority-rules.json" ForkopTable forkop_interfaces localv4 localv6 0x00100000
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tForkopTable\tforkop_rule_bypass_first_subnets\t{ type ipv4_addr; flags interval; auto-merge; }' "bypass priority subnet set"
assert_contains "$NFT_LOG" $'nft\tadd\tset\tinet\tForkopTable\tforkop_rule_wide_proxy_subnets\t{ type ipv4_addr; flags interval; auto-merge; }' "proxy priority subnet set"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tpriority_rules\tiifname\t@forkop_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@forkop_rule_bypass_first_subnets\tcounter\taccept' "bypass priority accept rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tpriority_rules\tiifname\t@forkop_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@forkop_rule_wide_proxy_subnets\tmeta\tmark\tset\t0x00100000\tcounter\taccept' "proxy priority capture rule"
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tForkopTable\tpriority_rules\tiifname\t@forkop_interfaces\tip\tdaddr\t!=\t@localv4\ttcp\tdport\t@forkop_rule_port_bypass_ports\tcounter\taccept' "port-only bypass priority rule"
assert_line_before "$NFT_LOG" \
$'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@podkop_plus_rule_bypass_first_subnets\tcounter\taccept' \
$'nft\tadd\trule\tinet\tPodkopPlusTable\tpriority_rules\tiifname\t@podkop_plus_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@podkop_plus_rule_wide_proxy_subnets\tmeta\tmark\tset\t0x00100000\tcounter\taccept' \
$'nft\tadd\trule\tinet\tForkopTable\tpriority_rules\tiifname\t@forkop_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@forkop_rule_bypass_first_subnets\tcounter\taccept' \
$'nft\tadd\trule\tinet\tForkopTable\tpriority_rules\tiifname\t@forkop_interfaces\tip\tdaddr\t!=\t@localv4\tip\tdaddr\t@forkop_rule_wide_proxy_subnets\tmeta\tmark\tset\t0x00100000\tcounter\taccept' \
"bypass priority order"
: > "$IP_LOG"
: > "$LOGGER_LOG"
rt_tables="$WORK_DIR/rt_tables"
IP_ROUTE_OUTPUT='' IP_ROUTE6_OUTPUT='' IP_RULE_OUTPUT='' IP_RULE6_OUTPUT='' nft_ucode ensure-tproxy-route-rule podkop 0x00100000 "$rt_tables"
assert_contains "$rt_tables" "105 podkop" "tproxy route table registry"
assert_contains "$IP_LOG" $'ip\troute\tadd\tlocal\t0.0.0.0/0\tdev\tlo\ttable\tpodkop' "tproxy route add"
assert_contains "$IP_LOG" $'ip\t-6\troute\tadd\tlocal\t::/0\tdev\tlo\ttable\tpodkop' "tproxy route6 add"
assert_contains "$IP_LOG" $'ip\t-4\trule\tadd\tfwmark\t0x00100000/0x00100000\ttable\tpodkop\tpriority\t105' "tproxy marking rule add"
assert_contains "$IP_LOG" $'ip\t-6\trule\tadd\tfwmark\t0x00100000/0x00100000\ttable\tpodkop\tpriority\t105' "tproxy marking rule6 add"
IP_ROUTE_OUTPUT='' IP_ROUTE6_OUTPUT='' IP_RULE_OUTPUT='' IP_RULE6_OUTPUT='' nft_ucode ensure-tproxy-route-rule forkop 0x00100000 "$rt_tables"
assert_contains "$rt_tables" "105 forkop" "tproxy route table registry"
assert_contains "$IP_LOG" $'ip\troute\tadd\tlocal\t0.0.0.0/0\tdev\tlo\ttable\tforkop' "tproxy route add"
assert_contains "$IP_LOG" $'ip\t-6\troute\tadd\tlocal\t::/0\tdev\tlo\ttable\tforkop' "tproxy route6 add"
assert_contains "$IP_LOG" $'ip\t-4\trule\tadd\tfwmark\t0x00100000/0x00100000\ttable\tforkop\tpriority\t105' "tproxy marking rule add"
assert_contains "$IP_LOG" $'ip\t-6\trule\tadd\tfwmark\t0x00100000/0x00100000\ttable\tforkop\tpriority\t105' "tproxy marking rule6 add"
assert_contains "$LOGGER_LOG" "[debug] Added IPv4 TPROXY route" "tproxy route creation log"
assert_contains "$LOGGER_LOG" "[debug] Added IPv6 TPROXY route" "tproxy route6 creation log"
assert_contains "$LOGGER_LOG" "[debug] Creating IPv4 TPROXY marking rule" "tproxy marking rule creation log"
@@ -342,12 +342,12 @@ assert_contains "$LOGGER_LOG" "[debug] Creating IPv6 TPROXY marking rule" "tprox
: > "$IP_LOG"
: > "$LOGGER_LOG"
printf '%s\n' '105 podkop' >"$rt_tables"
printf '%s\n' '105 forkop' >"$rt_tables"
IP_ROUTE_OUTPUT='local default dev lo scope host' \
IP_ROUTE6_OUTPUT='local default dev lo metric 1024 pref medium' \
IP_RULE_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup podkop' \
IP_RULE6_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup podkop' \
nft_ucode ensure-tproxy-route-rule podkop 0x00100000 "$rt_tables"
IP_RULE_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup forkop' \
IP_RULE6_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup forkop' \
nft_ucode ensure-tproxy-route-rule forkop 0x00100000 "$rt_tables"
if grep -Fq $'\tadd\t' "$IP_LOG"; then
fail "existing tproxy route/rule should not be added again"
fi
@@ -357,14 +357,14 @@ assert_contains "$LOGGER_LOG" "[debug] IPv4 TPROXY marking rule already exists"
assert_contains "$LOGGER_LOG" "[debug] IPv6 TPROXY marking rule already exists" "existing tproxy marking rule6 log"
IP_ROUTE_OUTPUT='local default dev lo scope host' \
IP_ROUTE6_OUTPUT='local default dev lo metric 1024 pref medium' \
IP_RULE_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup podkop' \
IP_RULE6_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup podkop' \
nft_ucode tproxy-route-rule-present podkop 0x00100000
IP_RULE_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup forkop' \
IP_RULE6_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup forkop' \
nft_ucode tproxy-route-rule-present forkop 0x00100000
if IP_ROUTE_OUTPUT='local default dev lo scope host' \
IP_ROUTE6_OUTPUT='local default dev lo metric 1024 pref medium' \
IP_RULE_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup other' \
IP_RULE6_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup podkop' \
nft_ucode tproxy-route-rule-present podkop 0x00100000 >/dev/null 2>&1; then
IP_RULE6_OUTPUT='105: from all fwmark 0x100000/0x100000 lookup forkop' \
nft_ucode tproxy-route-rule-present forkop 0x00100000 >/dev/null 2>&1; then
fail "tproxy route/rule presence should require matching lookup table"
fi
@@ -389,10 +389,10 @@ bad-value
203.0.113.0/24
EOF_INPUT
nft_ucode nft-add-file-chunks-to-set "$input" PodkopPlusTable podkop_plus_subnets ips "" 2
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_subnets\t{ 198.51.100.1,203.0.113.0/24 }' "nft chunked ips"
nft_ucode nft-add-file-chunks-to-set "$input" ForkopTable forkop_subnets ips "" 2
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_subnets\t{ 198.51.100.1,203.0.113.0/24 }' "nft chunked ips"
assert_contains "$LOGGER_LOG" "[debug] 'bad-value' is not IP or CIDR" "invalid element log"
assert_contains "$LOGGER_LOG" "[debug] Adding 2 elements to nft set podkop_plus_subnets" "chunk count log"
assert_contains "$LOGGER_LOG" "[debug] Adding 2 elements to nft set forkop_subnets" "chunk count log"
: > "$NFT_LOG"
ports_input="$WORK_DIR/ip-ports.txt"
@@ -401,7 +401,7 @@ cat >"$ports_input" <<'EOF_INPUT'
203.0.113.0/24
EOF_INPUT
nft_ucode nft-add-file-chunks-to-set "$ports_input" PodkopPlusTable podkop_plus_ip_ports ip-port-from-ip "80,443-444" 3
nft_ucode nft-add-file-chunks-to-set "$ports_input" ForkopTable forkop_ip_ports ip-port-from-ip "80,443-444" 3
assert_contains "$NFT_LOG" $'198.51.100.1 . 80,198.51.100.1 . 443-444,203.0.113.0/24 . 80' "ip-port chunk"
assert_contains "$NFT_LOG" $'203.0.113.0/24 . 443-444' "ip-port second chunk"
@@ -470,12 +470,12 @@ cat >"$plain_subnets" <<'EOF_INPUT'
EOF_INPUT
: > "$NFT_LOG"
nft_ucode nft-add-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports 5000
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets\t{ 198.51.100.210,203.0.113.0/24 }' "plain subnet import without ports"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets6\t{ 2001:db8::210 }' "plain subnet6 import without ports"
nft_ucode nft-add-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports "$plain_subnets" ForkopTable forkop_subnets forkop_ip_ports 5000
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets\t{ 198.51.100.210,203.0.113.0/24 }' "plain subnet import without ports"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets6\t{ 2001:db8::210 }' "plain subnet6 import without ports"
: > "$NFT_LOG"
nft_ucode nft-add-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" ports_only "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports 3
nft_ucode nft-add-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" ports_only "$plain_subnets" ForkopTable forkop_subnets forkop_ip_ports 3
assert_contains "$NFT_LOG" $'198.51.100.210 . 53,198.51.100.210 . 853,198.51.100.210 . 5353' "plain subnet import scoped first chunk"
assert_contains "$NFT_LOG" $'203.0.113.0/24 . 53,203.0.113.0/24 . 853,203.0.113.0/24 . 5353' "plain subnet import scoped second chunk"
assert_contains "$NFT_LOG" $'2001:db8::210 . 53,2001:db8::210 . 853,2001:db8::210 . 5353' "plain subnet6 import scoped chunk"
@@ -502,56 +502,56 @@ JSON
: > "$NFT_LOG"
unscoped_json="$WORK_DIR/unscoped-json.txt"
scoped_json="$WORK_DIR/scoped-json.txt"
nft_ucode nft-add-json-ruleset-subnets-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports "$json_ruleset" "fixture json" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports "$unscoped_json" "$scoped_json" 5000
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets\t{ 198.51.100.220 }' "json ruleset unscoped import"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets6\t{ 2001:db8::220 }' "json ruleset unscoped6 import"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_ip_ports\t{ 198.51.100.221 . 443,198.51.100.222 . 1000-1002 }' "json ruleset own port filters"
nft_ucode nft-add-json-ruleset-subnets-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports "$json_ruleset" "fixture json" ForkopTable forkop_subnets forkop_ip_ports "$unscoped_json" "$scoped_json" 5000
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets\t{ 198.51.100.220 }' "json ruleset unscoped import"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets6\t{ 2001:db8::220 }' "json ruleset unscoped6 import"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_ip_ports\t{ 198.51.100.221 . 443,198.51.100.222 . 1000-1002 }' "json ruleset own port filters"
: > "$NFT_LOG"
unscoped_json="$WORK_DIR/unscoped-json-ports.txt"
scoped_json="$WORK_DIR/scoped-json-ports.txt"
nft_ucode nft-add-json-ruleset-subnets-for-section-fixture "$WORK_DIR/populate-fixture.json" ports_only "$json_ruleset" "fixture json ports" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports "$unscoped_json" "$scoped_json" 4
nft_ucode nft-add-json-ruleset-subnets-for-section-fixture "$WORK_DIR/populate-fixture.json" ports_only "$json_ruleset" "fixture json ports" ForkopTable forkop_subnets forkop_ip_ports "$unscoped_json" "$scoped_json" 4
assert_contains "$NFT_LOG" $'198.51.100.220 . 53,198.51.100.220 . 5353,198.51.100.220 . 853' "json ruleset scoped import"
if grep -Fq '198.51.100.221' "$NFT_LOG" || grep -Fq '198.51.100.222' "$NFT_LOG"; then
fail "json ruleset section ports should intersect rule-owned port filters"
fi
: > "$NFT_LOG"
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports discord "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports podkop_plus_interfaces podkop_plus_discord_subnets 0x00100000 5000
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets\t{ 198.51.100.210,203.0.113.0/24 }' "discord community subnet import"
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports discord "$plain_subnets" ForkopTable forkop_subnets forkop_ip_ports forkop_interfaces forkop_discord_subnets 0x00100000 5000
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets\t{ 198.51.100.210,203.0.113.0/24 }' "discord community subnet import"
: > "$NFT_LOG"
NFT_MANGLE_CHAIN_OUTPUT=$'iifname @podkop_plus_interfaces ip daddr @podkop_plus_discord_subnets udp dport { 19000-20000, 50000-65535 } meta mark set 0x00100000 counter\niifname @podkop_plus_interfaces ip6 daddr @podkop_plus_discord_subnets6 udp dport { 19000-20000, 50000-65535 } meta mark set 0x00100000 counter' \
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports discord "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports podkop_plus_interfaces podkop_plus_discord_subnets 0x00100000 5000
NFT_MANGLE_CHAIN_OUTPUT=$'iifname @forkop_interfaces ip daddr @forkop_discord_subnets udp dport { 19000-20000, 50000-65535 } meta mark set 0x00100000 counter\niifname @forkop_interfaces ip6 daddr @forkop_discord_subnets6 udp dport { 19000-20000, 50000-65535 } meta mark set 0x00100000 counter' \
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" inline_no_ports discord "$plain_subnets" ForkopTable forkop_subnets forkop_ip_ports forkop_interfaces forkop_discord_subnets 0x00100000 5000
if grep -Fq $'nft\tadd\trule' "$NFT_LOG"; then
fail "discord community existing rule should not insert"
fi
: > "$NFT_LOG"
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" ports_only discord "$plain_subnets" PodkopPlusTable podkop_plus_subnets podkop_plus_ip_ports podkop_plus_interfaces podkop_plus_discord_subnets 0x00100000 3
if grep -Fq 'podkop_plus_discord_subnets' "$NFT_LOG"; then
nft_ucode nft-add-community-subnet-file-for-section-fixture "$WORK_DIR/populate-fixture.json" ports_only discord "$plain_subnets" ForkopTable forkop_subnets forkop_ip_ports forkop_interfaces forkop_discord_subnets 0x00100000 3
if grep -Fq 'forkop_discord_subnets' "$NFT_LOG"; then
fail "discord community with section ports should not use the discord nft set"
fi
assert_contains "$NFT_LOG" $'198.51.100.210 . 53,198.51.100.210 . 853,198.51.100.210 . 5353' "discord community with ports uses scoped import"
: > "$NFT_LOG"
nft_ucode nft-populate-runtime-sets-fixture "$WORK_DIR/populate-fixture.json" 1 "deferred" PodkopPlusTable podkop_plus_subnets podkop_plus_ports podkop_plus_ip_ports podkop_plus_interfaces localv4 0x00100000
nft_ucode nft-populate-runtime-sets-fixture "$WORK_DIR/populate-fixture.json" 1 "deferred" ForkopTable forkop_subnets forkop_ports forkop_ip_ports forkop_interfaces localv4 0x00100000
assert_contains "$NFT_LOG" $'198.51.100.1 . 80,198.51.100.1 . 443-444,203.0.113.0/24 . 80' "populate inline ip-port first chunk"
assert_contains "$NFT_LOG" $'203.0.113.0/24 . 443-444' "populate inline ip-port second chunk"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_ip_ports\t{ 198.51.100.1 . 80,198.51.100.1 . 443-444,203.0.113.0/24 . 80,203.0.113.0/24 . 443-444 }' "populate inline priority ip-port set"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_ip6_ports\t{ 2001:db8::1 . 80,2001:db8::1 . 443-444 }' "populate inline priority ip6-port set"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets\t{ 198.51.100.200 }' "populate inline ip without ports"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_inline_no_ports_subnets6\t{ 2001:db8::200 }' "populate inline ip6 without ports"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_ports_only_ports\t{ 53,853,5353 }' "populate ports-only set"
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip\tsaddr\t192.168.1.20/32\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "populate fully routed tcp"
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tPodkopPlusTable\tmangle\tiifname\t@podkop_plus_interfaces\tip6\tsaddr\t2001:db8::20/128\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "populate fully routed6 tcp"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_ip_ports\t{ 198.51.100.1 . 80,198.51.100.1 . 443-444,203.0.113.0/24 . 80,203.0.113.0/24 . 443-444 }' "populate inline priority ip-port set"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_ip6_ports\t{ 2001:db8::1 . 80,2001:db8::1 . 443-444 }' "populate inline priority ip6-port set"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets\t{ 198.51.100.200 }' "populate inline ip without ports"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_inline_no_ports_subnets6\t{ 2001:db8::200 }' "populate inline ip6 without ports"
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_ports_only_ports\t{ 53,853,5353 }' "populate ports-only set"
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tForkopTable\tmangle\tiifname\t@forkop_interfaces\tip\tsaddr\t192.168.1.20/32\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "populate fully routed tcp"
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tForkopTable\tmangle\tiifname\t@forkop_interfaces\tip6\tsaddr\t2001:db8::20/128\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "populate fully routed6 tcp"
if grep -Fq '192.0.2.1' "$NFT_LOG"; then
fail "disabled section should not populate nft"
fi
if grep -Fq '192.0.2.2' "$NFT_LOG"; then
fail "deferred section should not populate nft"
fi
if grep -Fq $'nft\tadd\telement\tinet\tPodkopPlusTable\tpodkop_plus_rule_ports_with_domain_ports\t{ 8443 }' "$NFT_LOG"; then
if grep -Fq $'nft\tadd\telement\tinet\tForkopTable\tforkop_rule_ports_with_domain_ports\t{ 8443 }' "$NFT_LOG"; then
fail "ports with destination matchers should not populate global port set"
fi
if [ "$(grep -F $'ip\tsaddr\t192.168.1.20/32' "$NFT_LOG" | wc -l | tr -d ' ')" != "3" ]; then
@@ -650,23 +650,23 @@ assert_eq "$expected_signature" \
"nft runtime signature fixture"
cat >"$WORK_DIR/signature-uci.state" <<'EOF_UCI'
podkop-plus.settings=settings
podkop-plus.settings.source_network_interfaces=br-lan tun0
podkop-plus.settings.exclude_ntp=1
podkop-plus.disabled=section
podkop-plus.disabled.enabled=0
podkop-plus.disabled.ip_cidr=192.0.2.10
podkop-plus.enabled=section
podkop-plus.enabled.enabled=1
podkop-plus.enabled.action=bypass
podkop-plus.enabled.ip_cidr=10.0.0.0/8 192.0.2.1
podkop-plus.enabled.ports=443 bad-value
podkop-plus.enabled.ports_text=80 443-444 65536
podkop-plus.enabled.fully_routed_ips=192.168.1.10/32
podkop-plus.enabled.community_lists=geoblock meta telegram youtube discord
podkop-plus.enabled.remote_subnet_lists=https://example.com/subnets.lst
podkop-plus.enabled.rule_set_with_subnets=/tmp/local.json
podkop-plus.enabled.domain_ip_lists=https://example.com/mixed.lst
forkop.settings=settings
forkop.settings.source_network_interfaces=br-lan tun0
forkop.settings.exclude_ntp=1
forkop.disabled=section
forkop.disabled.enabled=0
forkop.disabled.ip_cidr=192.0.2.10
forkop.enabled=section
forkop.enabled.enabled=1
forkop.enabled.action=bypass
forkop.enabled.ip_cidr=10.0.0.0/8 192.0.2.1
forkop.enabled.ports=443 bad-value
forkop.enabled.ports_text=80 443-444 65536
forkop.enabled.fully_routed_ips=192.168.1.10/32
forkop.enabled.community_lists=geoblock meta telegram youtube discord
forkop.enabled.remote_subnet_lists=https://example.com/subnets.lst
forkop.enabled.rule_set_with_subnets=/tmp/local.json
forkop.enabled.domain_ip_lists=https://example.com/mixed.lst
EOF_UCI
cat >"$WORK_DIR/signature-uci-expected.txt" <<'EOF_EXPECTED'
[settings.source_network_interfaces]
@@ -694,7 +694,7 @@ https://example.com/mixed.lst
EOF_EXPECTED
expected_signature="$(md5sum "$WORK_DIR/signature-uci-expected.txt" | awk '{print $1}')"
assert_eq "$expected_signature" \
"$(PODKOP_UCI_STATE_FILE="$WORK_DIR/signature-uci.state" nft_ucode nft-runtime-signature)" \
"$(FORKOP_UCI_STATE_FILE="$WORK_DIR/signature-uci.state" nft_ucode nft-runtime-signature)" \
"nft runtime signature from UCI state"
cat >"$WORK_DIR/signature-defaults-fixture.json" <<'JSON'
@@ -712,12 +712,12 @@ assert_eq "$expected_signature" \
"nft runtime signature defaults"
: > "$NFT_LOG"
nft_ucode nft-populate-runtime-sets-fixture "$WORK_DIR/populate-fixture.json" 0 "" PodkopPlusTable podkop_plus_subnets podkop_plus_ports podkop_plus_ip_ports podkop_plus_interfaces localv4 0x00100000
nft_ucode nft-populate-runtime-sets-fixture "$WORK_DIR/populate-fixture.json" 0 "" ForkopTable forkop_subnets forkop_ports forkop_ip_ports forkop_interfaces localv4 0x00100000
[ ! -s "$NFT_LOG" ] || fail "disabled nft population should not call nft"
: > "$NFT_LOG"
NFT_MANGLE_CHAIN_OUTPUT='ip saddr 192.168.1.20 meta l4proto tcp meta mark set 0x00100000 counter' \
nft_ucode nft-populate-runtime-sets-fixture "$WORK_DIR/populate-fixture.json" 1 "disabled deferred inline inline_no_ports ports_only ports_with_domain" PodkopPlusTable podkop_plus_subnets podkop_plus_ports podkop_plus_ip_ports podkop_plus_interfaces localv4 0x00100000
nft_ucode nft-populate-runtime-sets-fixture "$WORK_DIR/populate-fixture.json" 1 "disabled deferred inline inline_no_ports ports_only ports_with_domain" ForkopTable forkop_subnets forkop_ports forkop_ip_ports forkop_interfaces localv4 0x00100000
if grep -Fq $'nft\tinsert\trule' "$NFT_LOG"; then
fail "existing nft-normalized /32 fully routed source should not insert"
fi
+7 -7
View File
@@ -2,10 +2,10 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
GENERATOR_UC="$PODKOP_LIB/singbox/generator.uc"
PARSER_UC="$PODKOP_LIB/subscription/parser.uc"
VALIDATOR_UC="$PODKOP_LIB/config/validator.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
GENERATOR_UC="$FORKOP_LIB/singbox/generator.uc"
PARSER_UC="$FORKOP_LIB/subscription/parser.uc"
VALIDATOR_UC="$FORKOP_LIB/config/validator.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -109,7 +109,7 @@ JSON
runtime_config="$WORK_DIR/runtime-config.json"
mkdir -p "$runtime_config.section-cache"
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/subscriptions" \
ucode -L "$PODKOP_LIB" "$GENERATOR_UC" generate-config-fixture \
ucode -L "$FORKOP_LIB" "$GENERATOR_UC" generate-config-fixture \
"$WORK_DIR/runtime-tags.json" "$runtime_config" "127.0.0.1" "0"
ucode -e '
@@ -225,7 +225,7 @@ input.settings.dns_server = ['77.88.8.8'];
input.settings.bootstrap_dns_server = ['77.88.8.8'];
fs.writeFileSync(process.argv[3], JSON.stringify(input));
JS
if output="$(PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR_UC" \
if output="$(FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$VALIDATOR_UC" \
validate-runtime-fixture "$normalized" '{}' 2>/dev/null)"; then
fail "validator accepted $fixture"
fi
@@ -259,7 +259,7 @@ cat >"$WORK_DIR/xray-duplicates.json" <<'JSON'
]
JSON
ucode -L "$PODKOP_LIB" "$PARSER_UC" normalize-content \
ucode -L "$FORKOP_LIB" "$PARSER_UC" normalize-content \
"$WORK_DIR/xray-duplicates.json" "$WORK_DIR/xray-normalized.json"
ucode -e '
let fs = require("fs");
+17 -12
View File
@@ -2,9 +2,9 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_MAKEFILE="$ROOT_DIR/podkop/Makefile"
FORKOP_MAKEFILE="$ROOT_DIR/forkop/Makefile"
BUILD_SCRIPT="$ROOT_DIR/build.sh"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
fail() {
printf 'FAIL: %s\n' "$1" >&2
@@ -20,8 +20,8 @@ require_file() {
require_make_dep() {
local package="$1"
grep -Eq "DEPENDS:=.*(^|[[:space:]])\\+$package([[:space:]]|$)" "$PODKOP_MAKEFILE" ||
fail "podkop/Makefile DEPENDS is missing +$package"
grep -Eq "DEPENDS:=.*(^|[[:space:]])\\+$package([[:space:]]|$)" "$FORKOP_MAKEFILE" ||
fail "forkop/Makefile DEPENDS is missing +$package"
}
require_build_dep() {
@@ -40,22 +40,27 @@ require_package_dependency() {
require_build_dep "BACKEND_DEPENDS_APK" "$package"
}
require_file "$PODKOP_MAKEFILE"
require_file "$FORKOP_MAKEFILE"
require_file "$BUILD_SCRIPT"
require_file "$PODKOP_LIB"
require_file "$FORKOP_LIB"
if grep -Rqs 'require("uci")' "$PODKOP_LIB"; then
grep -Fq "must use x.y.z format" "$FORKOP_MAKEFILE" ||
fail "forkop/Makefile must enforce the three-part release version contract"
grep -Fq 'APK_INTERNAL_VERSION="$RELEASE_VERSION"' "$BUILD_SCRIPT" ||
fail "build.sh must use the exact three-part release version for APK metadata"
if grep -Rqs 'require("uci")' "$FORKOP_LIB"; then
require_package_dependency "ucode-mod-uci"
fi
if grep -Rqs 'require("fs")' "$PODKOP_LIB"; then
if grep -Rqs 'require("fs")' "$FORKOP_LIB"; then
require_package_dependency "ucode-mod-fs"
fi
if grep -Rqs 'podkop_dnsmasq_failsafe_restore_raw' \
"$ROOT_DIR/podkop/files/usr/bin" \
"$ROOT_DIR/podkop/files/usr/lib" \
"$ROOT_DIR/podkop/files/etc/init.d"; then
if grep -Rqs 'forkop_dnsmasq_failsafe_restore_raw' \
"$ROOT_DIR/forkop/files/usr/bin" \
"$ROOT_DIR/forkop/files/usr/lib" \
"$ROOT_DIR/forkop/files/etc/init.d"; then
fail "duplicated raw dnsmasq failsafe restore shell owner is present"
fi
+55 -65
View File
@@ -2,11 +2,11 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
PACKAGE_UC="$PODKOP_LIB/service/package.uc"
PODKOP_MAKEFILE="$ROOT_DIR/podkop/Makefile"
LUCI_UCI_DEFAULTS="$ROOT_DIR/luci-app-podkop-plus/root/etc/uci-defaults/50_luci-podkop-plus"
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
PACKAGE_UC="$FORKOP_LIB/service/package.uc"
FORKOP_MAKEFILE="$ROOT_DIR/forkop/Makefile"
LUCI_UCI_DEFAULTS="$ROOT_DIR/luci-app-forkop/root/etc/uci-defaults/50_luci-forkop"
BUILD_SCRIPT="$ROOT_DIR/build.sh"
WORK_DIR="$(mktemp -d)"
@@ -27,102 +27,92 @@ if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"' "$PACKAGE_UC" >/dev/n
fi
grep -Fq 'require("core.uci")' "$PACKAGE_UC" ||
fail "service/package.uc must import core.uci"
grep -Fq 'package_prerm: [ "service/package.uc", "prerm", 0 ]' "$PODKOP_BIN" ||
fail "podkop entrypoint must dispatch package prerm cleanup through service/package.uc"
grep -Fq 'luci_postinst: [ "service/package.uc", "luci-postinst", 0 ]' "$PODKOP_BIN" ||
fail "podkop entrypoint must dispatch LuCI postinstall cleanup through service/package.uc"
grep -Fq 'package_prerm: [ "service/package.uc", "prerm", 0 ]' "$FORKOP_BIN" ||
fail "forkop entrypoint must dispatch package prerm cleanup through service/package.uc"
grep -Fq 'luci_postinst: [ "service/package.uc", "luci-postinst", 0 ]' "$FORKOP_BIN" ||
fail "forkop entrypoint must dispatch LuCI postinstall cleanup through service/package.uc"
grep -Fq '#!/bin/sh' "$LUCI_UCI_DEFAULTS" ||
fail "LuCI uci-defaults must remain a shell script because OpenWrt default_postinst runs it through shell"
grep -Fq '/usr/bin/podkop-plus luci_postinst' "$LUCI_UCI_DEFAULTS" ||
grep -Fq '/usr/bin/forkop luci_postinst' "$LUCI_UCI_DEFAULTS" ||
fail "LuCI uci-defaults must delegate cache/rpcd handling to ucode"
if grep -E 'rm -f /var/luci-indexcache|rm -f /tmp/luci-indexcache|logger -t "podkop-plus"' "$LUCI_UCI_DEFAULTS" >/dev/null; then
if grep -E 'rm -f /var/luci-indexcache|rm -f /tmp/luci-indexcache|logger -t "forkop"' "$LUCI_UCI_DEFAULTS" >/dev/null; then
fail "LuCI uci-defaults must not own cache/logger shell logic"
fi
if grep -n -E 'grep -q "105 podkopplus"|sed -i "/105 podkopplus|podkop_dont_touch_dhcp=.*uci|cp /etc/config/podkop_plus|rm -f /tmp/luci-indexcache|killall -HUP rpcd' "$PODKOP_MAKEFILE" "$BUILD_SCRIPT" >/dev/null; then
if grep -n -E 'grep -q "105 forkop"|sed -i "/105 forkop|forkop_dont_touch_dhcp=.*uci|cp /etc/config/forkop|rm -f /tmp/luci-indexcache|killall -HUP rpcd' "$FORKOP_MAKEFILE" "$BUILD_SCRIPT" >/dev/null; then
fail "package scripts must not keep backend/LuCI lifecycle business logic in shell"
fi
grep -Fq '#!/usr/bin/ucode' "$PODKOP_MAKEFILE" ||
fail "podkop Makefile package hooks must use ucode entrypoints"
grep -Fq '/usr/bin/podkop-plus package_prerm' "$PODKOP_MAKEFILE" ||
fail "podkop Makefile prerm must delegate cleanup to package_prerm"
grep -Fq '/usr/bin/podkop-plus luci_postinst' "$BUILD_SCRIPT" ||
grep -Fq '#!/usr/bin/ucode' "$FORKOP_MAKEFILE" ||
fail "forkop Makefile package hooks must use ucode entrypoints"
grep -Fq '/usr/bin/forkop package_prerm' "$FORKOP_MAKEFILE" ||
fail "forkop Makefile prerm must delegate cleanup to package_prerm"
grep -Fq '/usr/bin/forkop luci_postinst' "$BUILD_SCRIPT" ||
fail "manual package builder must delegate LuCI cache/rpcd handling to luci_postinst"
legacy_config="$WORK_DIR/podkop_plus"
new_config="$WORK_DIR/podkop-plus"
printf 'config podkop_plus legacy\n' >"$legacy_config"
PODKOP_CONFIG="$new_config" PODKOP_LEGACY_CONFIG="$legacy_config" \
ucode -L "$PODKOP_LIB" "$PACKAGE_UC" preinst
[ "$(cat "$new_config")" = "config podkop_plus legacy" ] ||
fail "package preinst must copy legacy config when new config is absent"
printf 'existing config\n' >"$new_config"
PODKOP_CONFIG="$new_config" PODKOP_LEGACY_CONFIG="$legacy_config" \
ucode -L "$PODKOP_LIB" "$PACKAGE_UC" preinst
[ "$(cat "$new_config")" = "existing config" ] ||
fail "package preinst must not overwrite existing config"
if grep -n -E 'Package/forkop/preinst|copy_legacy_config|FORKOP_LEGACY_CONFIG|mode == "preinst"' \
"$FORKOP_MAKEFILE" "$BUILD_SCRIPT" "$PACKAGE_UC" >/dev/null 2>&1; then
fail "package hooks and runtime service must not own configuration migration"
fi
rt_tables="$WORK_DIR/rt_tables"
cat >"$rt_tables" <<'EOF'
100 main
105 podkopplus
105 forkop
200 custom
EOF
PODKOP_PACKAGE_TEST_MODE=1 PODKOP_RT_TABLES="$rt_tables" \
ucode -L "$PODKOP_LIB" "$PACKAGE_UC" prerm
if grep -Fq '105 podkopplus' "$rt_tables"; then
fail "package prerm must remove the legacy podkopplus routing table entry"
FORKOP_PACKAGE_TEST_MODE=1 FORKOP_RT_TABLES="$rt_tables" \
ucode -L "$FORKOP_LIB" "$PACKAGE_UC" prerm
if grep -Fq '105 forkop' "$rt_tables"; then
fail "package prerm must remove the Forkop routing table entry"
fi
grep -Fq '200 custom' "$rt_tables" ||
fail "package prerm must preserve unrelated rt_tables entries"
touch "$WORK_DIR/luci-indexcache.one" "$WORK_DIR/luci-indexcache.two"
PODKOP_PACKAGE_TEST_MODE=1 PODKOP_LUCI_CACHE_GLOBS="$WORK_DIR/luci-indexcache*" \
ucode -L "$PODKOP_LIB" "$PACKAGE_UC" luci-postinst
FORKOP_PACKAGE_TEST_MODE=1 FORKOP_LUCI_CACHE_GLOBS="$WORK_DIR/luci-indexcache*" \
ucode -L "$FORKOP_LIB" "$PACKAGE_UC" luci-postinst
if compgen -G "$WORK_DIR/luci-indexcache*" >/dev/null; then
fail "luci-postinst must remove LuCI index cache files"
fi
cat >"$WORK_DIR/podkop-bin" <<'SH'
cat >"$WORK_DIR/forkop-bin" <<'SH'
#!/usr/bin/env bash
printf '%s\n' "$*" >> "${PODKOP_RESTORE_LOG:?}"
printf '%s\n' "$*" >> "${FORKOP_RESTORE_LOG:?}"
SH
chmod 0755 "$WORK_DIR/podkop-bin"
chmod 0755 "$WORK_DIR/forkop-bin"
cat >"$WORK_DIR/dont-touch.state" <<'EOF_UCI'
podkop-plus.settings=settings
podkop-plus.settings.dont_touch_dhcp=1
forkop.settings=settings
forkop.settings.dont_touch_dhcp=1
EOF_UCI
printf '105 podkopplus\n' >"$WORK_DIR/rt_tables_dont_touch"
printf '105 forkop\n' >"$WORK_DIR/rt_tables_dont_touch"
: >"$WORK_DIR/restore-dont-touch.log"
PODKOP_UCI_STATE_FILE="$WORK_DIR/dont-touch.state" \
PODKOP_RESTORE_LOG="$WORK_DIR/restore-dont-touch.log" \
PODKOP_BIN="$WORK_DIR/podkop-bin" \
PODKOP_DNS_APPLY_UC="$WORK_DIR/missing-dns-apply.uc" \
PODKOP_SING_BOX_INIT="$WORK_DIR/missing-sing-box-init" \
PODKOP_SING_BOX_BIN="$WORK_DIR/missing-sing-box-bin" \
PODKOP_SING_BOX_CRONET="$WORK_DIR/missing-cronet" \
PODKOP_RT_TABLES="$WORK_DIR/rt_tables_dont_touch" \
ucode -L "$PODKOP_LIB" "$PACKAGE_UC" prerm
FORKOP_UCI_STATE_FILE="$WORK_DIR/dont-touch.state" \
FORKOP_RESTORE_LOG="$WORK_DIR/restore-dont-touch.log" \
FORKOP_BIN="$WORK_DIR/forkop-bin" \
FORKOP_DNS_APPLY_UC="$WORK_DIR/missing-dns-apply.uc" \
FORKOP_SING_BOX_INIT="$WORK_DIR/missing-sing-box-init" \
FORKOP_SING_BOX_BIN="$WORK_DIR/missing-sing-box-bin" \
FORKOP_SING_BOX_CRONET="$WORK_DIR/missing-cronet" \
FORKOP_RT_TABLES="$WORK_DIR/rt_tables_dont_touch" \
ucode -L "$FORKOP_LIB" "$PACKAGE_UC" prerm
[ ! -s "$WORK_DIR/restore-dont-touch.log" ] ||
fail "package prerm must skip dnsmasq restore when dont_touch_dhcp is enabled"
cat >"$WORK_DIR/restore.state" <<'EOF_UCI'
podkop-plus.settings=settings
podkop-plus.settings.dont_touch_dhcp=0
forkop.settings=settings
forkop.settings.dont_touch_dhcp=0
EOF_UCI
printf '105 podkopplus\n' >"$WORK_DIR/rt_tables_restore"
printf '105 forkop\n' >"$WORK_DIR/rt_tables_restore"
: >"$WORK_DIR/restore.log"
PODKOP_UCI_STATE_FILE="$WORK_DIR/restore.state" \
PODKOP_RESTORE_LOG="$WORK_DIR/restore.log" \
PODKOP_BIN="$WORK_DIR/podkop-bin" \
PODKOP_DNS_APPLY_UC="$WORK_DIR/missing-dns-apply.uc" \
PODKOP_SING_BOX_INIT="$WORK_DIR/missing-sing-box-init" \
PODKOP_SING_BOX_BIN="$WORK_DIR/missing-sing-box-bin" \
PODKOP_SING_BOX_CRONET="$WORK_DIR/missing-cronet" \
PODKOP_RT_TABLES="$WORK_DIR/rt_tables_restore" \
ucode -L "$PODKOP_LIB" "$PACKAGE_UC" prerm
FORKOP_UCI_STATE_FILE="$WORK_DIR/restore.state" \
FORKOP_RESTORE_LOG="$WORK_DIR/restore.log" \
FORKOP_BIN="$WORK_DIR/forkop-bin" \
FORKOP_DNS_APPLY_UC="$WORK_DIR/missing-dns-apply.uc" \
FORKOP_SING_BOX_INIT="$WORK_DIR/missing-sing-box-init" \
FORKOP_SING_BOX_BIN="$WORK_DIR/missing-sing-box-bin" \
FORKOP_SING_BOX_CRONET="$WORK_DIR/missing-cronet" \
FORKOP_RT_TABLES="$WORK_DIR/rt_tables_restore" \
ucode -L "$FORKOP_LIB" "$PACKAGE_UC" prerm
grep -Fxq 'restore_dnsmasq' "$WORK_DIR/restore.log" ||
fail "package prerm must restore dnsmasq when dont_touch_dhcp is disabled"
+10 -10
View File
@@ -2,10 +2,10 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
GENERATOR_UC="$PODKOP_LIB/singbox/generator.uc"
VALIDATOR_UC="$PODKOP_LIB/config/validator.uc"
PRIORITY_UC="$PODKOP_LIB/singbox/priority.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
GENERATOR_UC="$FORKOP_LIB/singbox/generator.uc"
VALIDATOR_UC="$FORKOP_LIB/config/validator.uc"
PRIORITY_UC="$FORKOP_LIB/singbox/priority.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -23,7 +23,7 @@ generate_config() {
local output="$2"
mkdir -p "${output}.section-cache"
ucode -L "$PODKOP_LIB" "$GENERATOR_UC" generate-config-fixture \
ucode -L "$FORKOP_LIB" "$GENERATOR_UC" generate-config-fixture \
"$fixture" "$output" "127.0.0.1"
}
@@ -38,7 +38,7 @@ input.settings.dns_server ??= ['77.88.8.8'];
input.settings.bootstrap_dns_server ??= ['77.88.8.8'];
fs.writeFileSync(process.argv[3], JSON.stringify(input));
JS
PODKOP_LIB="$PODKOP_LIB" ucode -L "$PODKOP_LIB" "$VALIDATOR_UC" \
FORKOP_LIB="$FORKOP_LIB" ucode -L "$FORKOP_LIB" "$VALIDATOR_UC" \
validate-runtime-fixture "$normalized" "{}"
}
@@ -366,7 +366,7 @@ JSON
cat >"$WORK_DIR/select-first-live-latency.json" <<'JSON'
{ "a": -1, "b": 200, "c": 50 }
JSON
first_live="$(ucode -L "$PODKOP_LIB" "$PRIORITY_UC" select-fixture \
first_live="$(ucode -L "$FORKOP_LIB" "$PRIORITY_UC" select-fixture \
"$WORK_DIR/select-first-live-group.json" "$WORK_DIR/select-first-live-latency.json" 0 1)"
printf '%s\n' "$first_live" | grep -Fq '"tag": "b"' ||
fail "priority selection should choose the first live outbound when pick_fastest=0"
@@ -383,7 +383,7 @@ JSON
cat >"$WORK_DIR/select-fastest-latency.json" <<'JSON'
{ "a": 80, "b": 20, "c": 50 }
JSON
fastest="$(ucode -L "$PODKOP_LIB" "$PRIORITY_UC" select-fixture \
fastest="$(ucode -L "$FORKOP_LIB" "$PRIORITY_UC" select-fixture \
"$WORK_DIR/select-fastest-group.json" "$WORK_DIR/select-fastest-latency.json" 0 0)"
printf '%s\n' "$fastest" | grep -Fq '"tag": "b"' ||
fail "priority selection should choose the fastest live outbound when pick_fastest=1"
@@ -652,7 +652,7 @@ cat >"$WORK_DIR/bad-filter-mode.json" <<'JSON'
JSON
assert_rejects "priority level bad filter mode" "$WORK_DIR/bad-filter-mode.json" "Invalid Priority filter mode"
skip_dead="$(ucode -L "$PODKOP_LIB" "$PRIORITY_UC" select-fixture \
skip_dead="$(ucode -L "$FORKOP_LIB" "$PRIORITY_UC" select-fixture \
"$WORK_DIR/select-first-live-group.json" "$WORK_DIR/select-fastest-latency.json" 0 1 a)"
printf '%s\n' "$skip_dead" | grep -Fq '"tag": "b"' ||
fail "replacement selection should skip the just-failed active outbound"
@@ -660,7 +660,7 @@ printf '%s\n' "$skip_dead" | grep -Fq '"tag": "b"' ||
cat >"$WORK_DIR/select-faster-current-latency.json" <<'JSON'
{ "a": 100, "b": 50, "c": 80 }
JSON
same_level="$(ucode -L "$PODKOP_LIB" "$PRIORITY_UC" select-faster-fixture \
same_level="$(ucode -L "$FORKOP_LIB" "$PRIORITY_UC" select-faster-fixture \
"$WORK_DIR/select-fastest-group.json" "$WORK_DIR/select-faster-current-latency.json" 0 a)"
printf '%s\n' "$same_level" | grep -Fq '"tag": "b"' ||
fail "same-level switching should compare candidates with the active delay from the same pass"
+3 -3
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
RULES_UC="$ROOT_DIR/podkop/files/usr/lib/providers/rules.uc"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
RULES_UC="$ROOT_DIR/forkop/files/usr/lib/providers/rules.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
fail() {
printf 'FAIL: %s\n' "$1" >&2
@@ -28,7 +28,7 @@ rows() {
}
rules_ucode() {
ucode -L "$PODKOP_LIB" "$RULES_UC" "$@"
ucode -L "$FORKOP_LIB" "$RULES_UC" "$@"
}
if grep -n -E 'require\("uci"\)\.cursor|cursor\.foreach' "$RULES_UC" >/dev/null 2>&1; then
+28 -28
View File
@@ -2,15 +2,15 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
PODKOP_INIT="$ROOT_DIR/podkop/files/etc/init.d/podkop"
CLI_UC="$PODKOP_BIN"
RUNTIME_STATE="$ROOT_DIR/podkop/files/usr/lib/runtime_state.sh"
UPDATES_RUNTIME="$ROOT_DIR/podkop/files/usr/lib/updates_runtime.sh"
LIFECYCLE_UC="$ROOT_DIR/podkop/files/usr/lib/service/lifecycle.uc"
UPDATES_UC="$ROOT_DIR/podkop/files/usr/lib/components/updates.uc"
STATE_UC="$ROOT_DIR/podkop/files/usr/lib/service/state.uc"
NFT_UC="$ROOT_DIR/podkop/files/usr/lib/nft/apply.uc"
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
FORKOP_INIT="$ROOT_DIR/forkop/files/etc/init.d/forkop"
CLI_UC="$FORKOP_BIN"
RUNTIME_STATE="$ROOT_DIR/forkop/files/usr/lib/runtime_state.sh"
UPDATES_RUNTIME="$ROOT_DIR/forkop/files/usr/lib/updates_runtime.sh"
LIFECYCLE_UC="$ROOT_DIR/forkop/files/usr/lib/service/lifecycle.uc"
UPDATES_UC="$ROOT_DIR/forkop/files/usr/lib/components/updates.uc"
STATE_UC="$ROOT_DIR/forkop/files/usr/lib/service/state.uc"
NFT_UC="$ROOT_DIR/forkop/files/usr/lib/nft/apply.uc"
fail() {
printf 'FAIL: %s\n' "$1" >&2
@@ -22,30 +22,30 @@ fail() {
[ ! -e "$UPDATES_RUNTIME" ] ||
fail "updates_runtime.sh shell owner must be removed"
grep -Fq 'runtime_state.sh' "$PODKOP_BIN" &&
fail "podkop must not source runtime_state.sh"
grep -Fq 'runtime_state.sh' "$FORKOP_BIN" &&
fail "forkop must not source runtime_state.sh"
service_predicates='process_age_seconds|sing_box_service_pid|pid_is_sing_box|sing_box_service_is_running|sing_box_service_is_stable|podkop_runtime_network_is_configured|podkop_is_running|podkop_is_stably_running|wait_for_podkop_stable_start'
if grep -R -n -E "$service_predicates" "$PODKOP_BIN" "$ROOT_DIR/podkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
service_predicates='process_age_seconds|sing_box_service_pid|pid_is_sing_box|sing_box_service_is_running|sing_box_service_is_stable|forkop_runtime_network_is_configured|forkop_is_running|forkop_is_stably_running|wait_for_forkop_stable_start'
if grep -R -n -E "$service_predicates" "$FORKOP_BIN" "$ROOT_DIR/forkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
fail "service running/stability predicates must not remain in shell"
fi
pending_state='mark_pending_reload|consume_pending_reload|run_pending_reload_if_requested|sync_time_if_needed'
if grep -R -n -E "$pending_state" "$PODKOP_BIN" "$PODKOP_INIT" "$ROOT_DIR/podkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
if grep -R -n -E "$pending_state" "$FORKOP_BIN" "$FORKOP_INIT" "$ROOT_DIR/forkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
fail "pending reload/time-sync ownership must not remain in shell"
fi
runtime_state_shell_symbols='runtime_state_ucode|acquire_runtime_dir_lock|release_runtime_dir_lock|capture_reload_state|write_reload_state_file|write_reload_state|clear_reload_state|populate_nft_runtime_sets|rebuild_nft_runtime|reload_sing_box_runtime|apply_pending_urltest_selector_switches|close_inherited_service_lock_fd|PODKOP_URLTEST_SELECTOR_SWITCHES'
if grep -R -n -E "$runtime_state_shell_symbols" "$PODKOP_BIN" "$PODKOP_INIT" "$ROOT_DIR/podkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
runtime_state_shell_symbols='runtime_state_ucode|acquire_runtime_dir_lock|release_runtime_dir_lock|capture_reload_state|write_reload_state_file|write_reload_state|clear_reload_state|populate_nft_runtime_sets|rebuild_nft_runtime|reload_sing_box_runtime|apply_pending_urltest_selector_switches|close_inherited_service_lock_fd|FORKOP_URLTEST_SELECTOR_SWITCHES'
if grep -R -n -E "$runtime_state_shell_symbols" "$FORKOP_BIN" "$FORKOP_INIT" "$ROOT_DIR/forkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
fail "runtime_state.sh symbols must not remain in shell"
fi
if grep -Fq 'acquire_reload_lock' "$PODKOP_INIT" || grep -Fq 'release_reload_lock' "$PODKOP_INIT"; then
if grep -Fq 'acquire_reload_lock' "$FORKOP_INIT" || grep -Fq 'release_reload_lock' "$FORKOP_INIT"; then
fail "init.d must not own reload lock decisions"
fi
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
fail "podkop entrypoint must be a direct ucode executable"
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
fail "forkop entrypoint must be a direct ucode executable"
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
fail "service/cli.uc must dispatch lifecycle orchestration through service/lifecycle.uc"
grep -Fq 'refresh-cron-from-uci' "$LIFECYCLE_UC" ||
@@ -56,7 +56,7 @@ grep -Fq '"list-update"' "$LIFECYCLE_UC" ||
fail "service/lifecycle.uc start/reload must run list updates through components/updates.uc"
grep -Fq 'subscription_update: [ "components/updates.uc", "subscription-update", 2 ]' "$CLI_UC" ||
fail "service/cli.uc must run subscription updates through components/updates.uc"
if grep -R -n -F 'updates_runtime.sh' "$PODKOP_BIN" "$ROOT_DIR/podkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
if grep -R -n -F 'updates_runtime.sh' "$FORKOP_BIN" "$ROOT_DIR/forkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
fail "runtime shell must not reference updates_runtime.sh"
fi
grep -Fq 'mode == "refresh-cron-from-uci"' "$UPDATES_UC" ||
@@ -67,11 +67,11 @@ grep -Fq 'mode == "list-update"' "$UPDATES_UC" ||
fail "components/updates.uc must own list update"
grep -Fq 'mode == "subscription-update"' "$UPDATES_UC" ||
fail "components/updates.uc must own subscription update"
grep -Fq 'mode == "podkop-running"' "$STATE_UC" ||
fail "service/state.uc must own Podkop running predicate"
grep -Fq 'mode == "forkop-running"' "$STATE_UC" ||
fail "service/state.uc must own Forkop running predicate"
grep -Fq 'mode == "sing-box-service-stable"' "$STATE_UC" ||
fail "service/state.uc must expose stable sing-box predicate"
grep -Fq 'mode == "wait-podkop-stable-start"' "$STATE_UC" ||
grep -Fq 'mode == "wait-forkop-stable-start"' "$STATE_UC" ||
fail "service/state.uc must own stable-start waiting"
grep -Fq 'mode == "mark-pending-reload"' "$STATE_UC" ||
fail "service/state.uc must own pending reload writes"
@@ -83,12 +83,12 @@ grep -Fq 'mode == "write-captured-reload-state"' "$STATE_UC" ||
fail "service/state.uc must own captured reload state writes"
grep -Fq 'mode == "reload-sing-box-runtime"' "$STATE_UC" ||
fail "service/state.uc must own sing-box runtime reload"
grep -Fq '"wait-podkop-stable-start"' "$LIFECYCLE_UC" ||
grep -Fq '"wait-forkop-stable-start"' "$LIFECYCLE_UC" ||
fail "service/lifecycle.uc must verify stable runtime after sing-box start/reload"
grep -Fq 'SING_BOX_START_STABLE_MIN_AGE' "$LIFECYCLE_UC" ||
fail "service/lifecycle.uc must use a dedicated sing-box start stability window"
sing_box_start_line="$(grep -nF 'command_success_from_args([ "/etc/init.d/sing-box", "start" ])' "$LIFECYCLE_UC" | head -n1 | cut -d: -f1)"
early_stable_line="$(awk -v start="$sing_box_start_line" 'NR > start && /"wait-podkop-stable-start"/ { print NR; exit }' "$LIFECYCLE_UC")"
early_stable_line="$(awk -v start="$sing_box_start_line" 'NR > start && /"wait-forkop-stable-start"/ { print NR; exit }' "$LIFECYCLE_UC")"
start_stable_min_age_line="$(awk -v start="$sing_box_start_line" 'NR > start && /SING_BOX_START_STABLE_MIN_AGE/ { print NR; exit }' "$LIFECYCLE_UC")"
deferred_bootstrap_line="$(grep -nF '"run-deferred-bootstrap"' "$LIFECYCLE_UC" | head -n1 | cut -d: -f1)"
[ -n "$sing_box_start_line" ] ||
@@ -105,9 +105,9 @@ reload_stable_min_age_line="$(awk -v start="$sing_box_reload_line" 'NR > start &
fail "service/lifecycle.uc must reload sing-box through service/state.uc"
[ -n "$reload_stable_min_age_line" ] ||
fail "service/lifecycle.uc must use the dedicated sing-box start stability window after sing-box reload"
grep -Fq 'Reload verification failed after sing-box was reloaded; stopping Podkop Plus runtime' "$LIFECYCLE_UC" ||
grep -Fq 'Reload verification failed after sing-box was reloaded; stopping Forkop runtime' "$LIFECYCLE_UC" ||
fail "service/lifecycle.uc must fail reload when sing-box does not stay stable"
grep -Fq 'Reload runtime restart verification failed after Podkop Plus was started; rolling back DNS changes' "$LIFECYCLE_UC" ||
grep -Fq 'Reload runtime restart verification failed after Forkop was started; rolling back DNS changes' "$LIFECYCLE_UC" ||
fail "service/lifecycle.uc must fail full runtime restart when sing-box does not stay stable"
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"|uci_get_cli|uci_exists_cli|mwan3_has_enabled_interface_from_uci_show' "$STATE_UC" >/dev/null 2>&1; then
fail "service/state.uc must not own direct UCI CLI/cursor access"
@@ -115,7 +115,7 @@ fi
grep -Fq 'nft-rebuild-runtime-from-uci' "$LIFECYCLE_UC" ||
fail "service/lifecycle.uc reload must call nft rebuild through ucode"
if grep -Fq 'nft-create-full-runtime-from-uci' "$LIFECYCLE_UC"; then
fail "podkop start/reload must rebuild nft runtime instead of appending to stale state"
fail "forkop start/reload must rebuild nft runtime instead of appending to stale state"
fi
grep -Fq 'nft-populate-runtime-sets-from-uci' "$LIFECYCLE_UC" ||
fail "service/lifecycle.uc reload must call nft populate through ucode"
+16 -16
View File
@@ -2,14 +2,14 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
STATE_UC="$ROOT_DIR/podkop/files/usr/lib/service/state.uc"
NFT_UC="$ROOT_DIR/podkop/files/usr/lib/nft/apply.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
STATE_UC="$ROOT_DIR/forkop/files/usr/lib/service/state.uc"
NFT_UC="$ROOT_DIR/forkop/files/usr/lib/nft/apply.uc"
WORK_DIR="$(mktemp -d)"
export ZAPRET_DEFAULT_NFQWS_OPT="--default-zapret"
export ZAPRET2_DEFAULT_NFQWS2_OPT="--default-zapret2"
export BYEDPI_DEFAULT_CMD_OPTS="--default-bye"
export PODKOP_FAKE_INIT_CAPTURE="$WORK_DIR/pending-reload-init.args"
export FORKOP_FAKE_INIT_CAPTURE="$WORK_DIR/pending-reload-init.args"
cleanup() {
rm -rf "$WORK_DIR"
@@ -34,11 +34,11 @@ md5_file() {
}
state_ucode() {
ucode -L "$PODKOP_LIB" "$STATE_UC" "$@"
ucode -L "$FORKOP_LIB" "$STATE_UC" "$@"
}
nft_ucode() {
ucode -L "$PODKOP_LIB" "$NFT_UC" "$@"
ucode -L "$FORKOP_LIB" "$NFT_UC" "$@"
}
state_ucode list-has-remote-references "local.lst https://example.com/list.txt" >/dev/null ||
@@ -114,17 +114,17 @@ fi
cat >"$WORK_DIR/fake-init" <<'SH'
#!/bin/sh
printf '%s\n' "$1" >"$PODKOP_FAKE_INIT_CAPTURE"
printf '%s\n' "$1" >"$FORKOP_FAKE_INIT_CAPTURE"
SH
chmod +x "$WORK_DIR/fake-init"
state_ucode mark-pending-reload "$PENDING_RELOAD_FILE" "reload_busy"
state_ucode run-pending-reload-if-requested "$PENDING_RELOAD_FILE" "$WORK_DIR/fake-init"
for _ in $(seq 1 20); do
[ -s "$PODKOP_FAKE_INIT_CAPTURE" ] && break
[ -s "$FORKOP_FAKE_INIT_CAPTURE" ] && break
sleep 0.1
done
assert_eq "reload" \
"$(cat "$PODKOP_FAKE_INIT_CAPTURE")" \
"$(cat "$FORKOP_FAKE_INIT_CAPTURE")" \
"pending reload should invoke init.d reload"
[ ! -e "$PENDING_RELOAD_FILE" ] ||
fail "pending reload should be consumed when worker is started"
@@ -191,9 +191,9 @@ cat >"$WORK_DIR/service-dns-state.json" <<'JSON'
"server": [ "1.1.1.1#53", "8.8.8.8" ],
"noresolv": "1",
"cachesize": "0",
"podkop_server": [ "127.0.0.42#53" ],
"podkop_noresolv": "0",
"podkop_cachesize": "1500"
"forkop_server": [ "127.0.0.42#53" ],
"forkop_noresolv": "0",
"forkop_cachesize": "1500"
},
"legacy_dnsmasq_present": true
}
@@ -217,13 +217,13 @@ cat >"$WORK_DIR/dnsmasq-signature.expected" <<'EOF_DNSMASQ_SIG'
1
[dhcp.@dnsmasq[0].cachesize]
0
[dhcp.@dnsmasq[0].podkop_server]
[dhcp.@dnsmasq[0].forkop_server]
127.0.0.42#53
[dhcp.@dnsmasq[0].podkop_noresolv]
[dhcp.@dnsmasq[0].forkop_noresolv]
0
[dhcp.@dnsmasq[0].podkop_cachesize]
[dhcp.@dnsmasq[0].forkop_cachesize]
1500
[dhcp.podkop_plus.present]
[dhcp.forkop.present]
1
EOF_DNSMASQ_SIG
+4 -4
View File
@@ -2,8 +2,8 @@
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
LIFECYCLE_UC="$PODKOP_LIB/service/lifecycle.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
LIFECYCLE_UC="$FORKOP_LIB/service/lifecycle.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -17,8 +17,8 @@ fail() {
}
lifecycle_ucode() {
PODKOP_RUNTIME_STATE_DIR="$WORK_DIR/run" \
ucode -L "$PODKOP_LIB" "$LIFECYCLE_UC" "$@"
FORKOP_RUNTIME_STATE_DIR="$WORK_DIR/run" \
ucode -L "$FORKOP_LIB" "$LIFECYCLE_UC" "$@"
}
json_flatten() {
+2 -2
View File
@@ -62,11 +62,11 @@ cat >"$WORK_DIR/different-array-order.json" <<'JSON'
}
JSON
if node "$COMPARE" "$WORK_DIR/expected.json" "$WORK_DIR/different-array-order.json" >/tmp/podkop-semantic-json.out 2>&1; then
if node "$COMPARE" "$WORK_DIR/expected.json" "$WORK_DIR/different-array-order.json" >/tmp/forkop-semantic-json.out 2>&1; then
fail "array order mismatch should be rejected"
fi
grep -Fq '$.outbounds[0].tag: "first" != "second"' /tmp/podkop-semantic-json.out ||
grep -Fq '$.outbounds[0].tag: "first" != "second"' /tmp/forkop-semantic-json.out ||
fail "array order mismatch reported unexpected diff"
printf 'semantic JSON compare checks passed\n'
+33 -33
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SERVER_RUNTIME="$ROOT_DIR/podkop/files/usr/lib/server/service.uc"
UCODE_LIB="$ROOT_DIR/podkop/files/usr/lib"
SERVER_RUNTIME="$ROOT_DIR/forkop/files/usr/lib/server/service.uc"
UCODE_LIB="$ROOT_DIR/forkop/files/usr/lib"
WORK_DIR="$(mktemp -d)"
STATE="$WORK_DIR/uci.state"
LOG="$WORK_DIR/uci.log"
@@ -59,26 +59,26 @@ LOGGER
chmod 0755 "$WORK_DIR/bin/logger"
export PATH="$WORK_DIR/bin:$PATH"
export PODKOP_UCI_STATE_FILE="$STATE"
export PODKOP_UCI_LOG_FILE="$LOG"
export PODKOP_CONFIG_NAME="podkop-plus"
export PODKOP_SERVER_RUNTIME_UC="$SERVER_RUNTIME"
export FORKOP_UCI_STATE_FILE="$STATE"
export FORKOP_UCI_LOG_FILE="$LOG"
export FORKOP_CONFIG_NAME="forkop"
export FORKOP_SERVER_RUNTIME_UC="$SERVER_RUNTIME"
if grep -E 'uci -q|command -v uci' "$SERVER_RUNTIME" >/dev/null; then
fail "server/service.uc must use ucode UCI access instead of shelling out to uci"
fi
cat >"$STATE" <<'EOF_STATE'
podkop-plus.vless=server
podkop-plus.vless.protocol=vless
podkop-plus.vless.server_users=client|22222222-2222-4222-8222-222222222222|xtls-rprx-vision
podkop-plus.socks=server
podkop-plus.socks.protocol=socks
podkop-plus.socks.label=desk
podkop-plus.tailscale=server
podkop-plus.tailscale.protocol=tailscale
podkop-plus.json=server
podkop-plus.json.protocol=json_inbound
forkop.vless=server
forkop.vless.protocol=vless
forkop.vless.server_users=client|22222222-2222-4222-8222-222222222222|xtls-rprx-vision
forkop.socks=server
forkop.socks.protocol=socks
forkop.socks.label=desk
forkop.tailscale=server
forkop.tailscale.protocol=tailscale
forkop.json=server
forkop.json.protocol=json_inbound
EOF_STATE
ucode -L "$UCODE_LIB" "$SERVER_RUNTIME" prepare-all-defaults
@@ -93,22 +93,22 @@ assert_value() {
[ "$actual" = "$expected" ] || fail "$path: expected '$expected', got '$actual'"
}
assert_value podkop-plus.vless.security reality
assert_value podkop-plus.vless.listen 0.0.0.0
assert_value podkop-plus.vless.listen_port 443
assert_value podkop-plus.vless.server_uuid 22222222-2222-4222-8222-222222222222
assert_value podkop-plus.vless.vless_flow xtls-rprx-vision
assert_value podkop-plus.vless.reality_short_id abcd1234
assert_value podkop-plus.vless.reality_private_key private-key
assert_value podkop-plus.vless.reality_public_key public-key
assert_value podkop-plus.socks.security none
assert_value podkop-plus.socks.server_username desk
assert_value podkop-plus.socks.server_password generated-password
assert_value podkop-plus.tailscale.security none
assert_value podkop-plus.tailscale.tailscale_control_url https://controlplane.tailscale.com
assert_value podkop-plus.tailscale.tailscale_hostname podkop-tailscale
assert_value podkop-plus.tailscale.tailscale_advertise_exit_node 1
assert_value podkop-plus.json.security none
grep -Fxq 'commit podkop-plus' "$LOG" || fail 'expected config commit'
assert_value forkop.vless.security reality
assert_value forkop.vless.listen 0.0.0.0
assert_value forkop.vless.listen_port 443
assert_value forkop.vless.server_uuid 22222222-2222-4222-8222-222222222222
assert_value forkop.vless.vless_flow xtls-rprx-vision
assert_value forkop.vless.reality_short_id abcd1234
assert_value forkop.vless.reality_private_key private-key
assert_value forkop.vless.reality_public_key public-key
assert_value forkop.socks.security none
assert_value forkop.socks.server_username desk
assert_value forkop.socks.server_password generated-password
assert_value forkop.tailscale.security none
assert_value forkop.tailscale.tailscale_control_url https://controlplane.tailscale.com
assert_value forkop.tailscale.tailscale_hostname forkop-tailscale
assert_value forkop.tailscale.tailscale_advertise_exit_node 1
assert_value forkop.json.security none
grep -Fxq 'commit forkop' "$LOG" || fail 'expected config commit'
printf 'server runtime checks passed\n'
+1 -1
View File
@@ -2,7 +2,7 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
RELOAD_UC="$ROOT_DIR/podkop/files/usr/lib/service/reload.uc"
RELOAD_UC="$ROOT_DIR/forkop/files/usr/lib/service/reload.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
+21 -21
View File
@@ -2,18 +2,18 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
PODKOP_INIT="$ROOT_DIR/podkop/files/etc/init.d/podkop"
CLI_UC="$PODKOP_BIN"
LIFECYCLE_UC="$ROOT_DIR/podkop/files/usr/lib/service/lifecycle.uc"
INITD_UC="$ROOT_DIR/podkop/files/usr/lib/service/initd.uc"
STATE_UC="$ROOT_DIR/podkop/files/usr/lib/service/state.uc"
UI_UC="$ROOT_DIR/podkop/files/usr/lib/service/ui.uc"
UPDATES_UC="$ROOT_DIR/podkop/files/usr/lib/components/updates.uc"
SUBSCRIPTION_CACHE_UC="$ROOT_DIR/podkop/files/usr/lib/subscription/cache.uc"
NFQUEUE_RUNTIME_UC="$ROOT_DIR/podkop/files/usr/lib/providers/nfqueue/runtime.uc"
BYEDPI_RUNTIME_UC="$ROOT_DIR/podkop/files/usr/lib/providers/byedpi/runtime.uc"
PRIORITY_UC="$ROOT_DIR/podkop/files/usr/lib/singbox/priority.uc"
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
FORKOP_INIT="$ROOT_DIR/forkop/files/etc/init.d/forkop"
CLI_UC="$FORKOP_BIN"
LIFECYCLE_UC="$ROOT_DIR/forkop/files/usr/lib/service/lifecycle.uc"
INITD_UC="$ROOT_DIR/forkop/files/usr/lib/service/initd.uc"
STATE_UC="$ROOT_DIR/forkop/files/usr/lib/service/state.uc"
UI_UC="$ROOT_DIR/forkop/files/usr/lib/service/ui.uc"
UPDATES_UC="$ROOT_DIR/forkop/files/usr/lib/components/updates.uc"
SUBSCRIPTION_CACHE_UC="$ROOT_DIR/forkop/files/usr/lib/subscription/cache.uc"
NFQUEUE_RUNTIME_UC="$ROOT_DIR/forkop/files/usr/lib/providers/nfqueue/runtime.uc"
BYEDPI_RUNTIME_UC="$ROOT_DIR/forkop/files/usr/lib/providers/byedpi/runtime.uc"
PRIORITY_UC="$ROOT_DIR/forkop/files/usr/lib/singbox/priority.uc"
fail() {
printf 'FAIL: %s\n' "$1" >&2
@@ -31,7 +31,7 @@ reject_pattern() {
local pattern="$1"
local label="$2"
! grep -Fq "$pattern" "$PODKOP_BIN" || fail "$label"
! grep -Fq "$pattern" "$FORKOP_BIN" || fail "$label"
}
require_file_pattern() {
@@ -42,18 +42,18 @@ require_file_pattern() {
grep -Fq "$pattern" "$file" || fail "$label"
}
[ -r "$PODKOP_BIN" ] || fail "podkop binary source is missing"
[ -r "$PODKOP_BIN" ] || fail "podkop entrypoint is missing"
[ -r "$FORKOP_BIN" ] || fail "forkop binary source is missing"
[ -r "$FORKOP_BIN" ] || fail "forkop entrypoint is missing"
[ -r "$LIFECYCLE_UC" ] || fail "service/lifecycle.uc is missing"
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
fail "podkop entrypoint must be a direct ucode executable"
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
fail "forkop entrypoint must be a direct ucode executable"
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
fail "service/cli.uc must dispatch startup through service/lifecycle.uc"
reject_pattern "trap " \
"podkop shell entrypoint must not own startup fail-safe traps"
"forkop shell entrypoint must not own startup fail-safe traps"
reject_pattern "clear_startup_failsafe_trap" \
"podkop shell entrypoint must not keep old trap helper"
"forkop shell entrypoint must not keep old trap helper"
require_pattern 'module_background(UPDATES_UC, [ "list-update" ])' \
"startup list_update background job must be owned by service/lifecycle.uc"
require_pattern 'module_background(DIAGNOSTICS_UC, [ "get-system-info" ])' \
@@ -91,9 +91,9 @@ require_file_pattern "$INITD_UC" 'reload pending >/dev/null 2>&1 1000>&- &' \
"service/initd.uc pending reload worker must close inherited procd lock fd"
require_file_pattern "$STATE_UC" 'reload pending >/dev/null 2>&1 1000>&- &' \
"service/state.uc pending reload worker must close inherited procd lock fd"
require_file_pattern "$PODKOP_INIT" 'PODKOP_LAST_START_STATUS="$?"' \
require_file_pattern "$FORKOP_INIT" 'FORKOP_LAST_START_STATUS="$?"' \
"init.d start_service must preserve backend start status for rc.common"
require_file_pattern "$PODKOP_INIT" 'service_started()' \
require_file_pattern "$FORKOP_INIT" 'service_started()' \
"init.d must return preserved start status through rc.common service_started hook"
require_file_pattern "$UI_UC" '>/dev/null 2>&1 1000>&- & echo $!' \
"service/ui.uc service action workers must close inherited procd lock fd"
+31 -30
View File
@@ -2,34 +2,34 @@
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_FILES="$ROOT_DIR/podkop/files"
PODKOP_BIN="$PODKOP_FILES/usr/bin/podkop"
PODKOP_LIB="$PODKOP_FILES/usr/lib"
PODKOP_INIT="$PODKOP_FILES/etc/init.d/podkop"
LUCI_ROOT="$ROOT_DIR/luci-app-podkop-plus/root"
LUCI_UCI_DEFAULTS="$LUCI_ROOT/etc/uci-defaults/50_luci-podkop-plus"
FORKOP_FILES="$ROOT_DIR/forkop/files"
FORKOP_BIN="$FORKOP_FILES/usr/bin/forkop"
FORKOP_LIB="$FORKOP_FILES/usr/lib"
FORKOP_INIT="$FORKOP_FILES/etc/init.d/forkop"
LUCI_ROOT="$ROOT_DIR/luci-app-forkop/root"
LUCI_UCI_DEFAULTS="$LUCI_ROOT/etc/uci-defaults/50_luci-forkop"
fail() {
printf 'FAIL: %s\n' "$1" >&2
exit 1
}
[ -d "$PODKOP_LIB" ] || fail "runtime library directory is missing"
[ -r "$PODKOP_BIN" ] || fail "podkop ucode entrypoint is missing"
[ -r "$PODKOP_INIT" ] || fail "podkop init.d entrypoint is missing"
[ -d "$FORKOP_LIB" ] || fail "runtime library directory is missing"
[ -r "$FORKOP_BIN" ] || fail "forkop ucode entrypoint is missing"
[ -r "$FORKOP_INIT" ] || fail "forkop init.d entrypoint is missing"
[ -r "$LUCI_UCI_DEFAULTS" ] || fail "LuCI uci-defaults entrypoint is missing"
runtime_shell_files="$(find "$PODKOP_LIB" -type f -name '*.sh' -print)"
runtime_shell_files="$(find "$FORKOP_LIB" -type f -name '*.sh' -print)"
[ -z "$runtime_shell_files" ] ||
fail "runtime library must not contain shell owners: $runtime_shell_files"
legacy_shell_owners='runtime_state\.sh|rules_nft_runtime\.sh|config_validation\.sh|sing_box_runtime\.sh|updates_runtime\.sh|updater\.sh|status_diagnostics\.sh|helpers\.sh|constants\.sh|subscription_runtime\.sh|byedpi\.sh|zapret\.sh|zapret2\.sh'
if find "$PODKOP_FILES" -type f -print | grep -E "$legacy_shell_owners" >/dev/null 2>&1; then
fail "legacy runtime shell owner file returned under podkop/files"
if find "$FORKOP_FILES" -type f -print | grep -E "$legacy_shell_owners" >/dev/null 2>&1; then
fail "legacy runtime shell owner file returned under forkop/files"
fi
shell_scripts="$(
find "$PODKOP_FILES" "$LUCI_ROOT" -type f -print |
find "$FORKOP_FILES" "$LUCI_ROOT" -type f -print |
while IFS= read -r file; do
first_line="$(sed -n '1p' "$file")"
case "$first_line" in
@@ -38,13 +38,14 @@ shell_scripts="$(
;;
esac
done |
sort
LC_ALL=C sort
)"
expected_shell_scripts="$(
printf '%s\n' \
'luci-app-podkop-plus/root/etc/uci-defaults/50_luci-podkop-plus' \
'podkop/files/etc/init.d/podkop'
'luci-app-forkop/root/etc/uci-defaults/50_luci-forkop' \
'forkop/files/etc/init.d/forkop' |
LC_ALL=C sort
)"
[ "$shell_scripts" = "$expected_shell_scripts" ] ||
@@ -54,33 +55,33 @@ $expected_shell_scripts
actual:
$shell_scripts"
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
fail "/usr/bin/podkop must remain a direct ucode executable"
grep -Fq 'function command_spec(command)' "$PODKOP_BIN" ||
fail "/usr/bin/podkop must own command routing in ucode"
if grep -n -E '#!/bin/(ba)?sh|exec[[:space:]]+ucode|run_module\(|PODKOP_COMMAND' "$PODKOP_BIN" >/dev/null 2>&1; then
fail "/usr/bin/podkop must not regress to a shell loader or shell router"
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
fail "/usr/bin/forkop must remain a direct ucode executable"
grep -Fq 'function command_spec(command)' "$FORKOP_BIN" ||
fail "/usr/bin/forkop must own command routing in ucode"
if grep -n -E '#!/bin/(ba)?sh|exec[[:space:]]+ucode|run_module\(|FORKOP_COMMAND' "$FORKOP_BIN" >/dev/null 2>&1; then
fail "/usr/bin/forkop must not regress to a shell loader or shell router"
fi
grep -Fq 'PODKOP_INITD_UC="$PODKOP_LIB/service/initd.uc"' "$PODKOP_INIT" ||
grep -Fq 'FORKOP_INITD_UC="$FORKOP_LIB/service/initd.uc"' "$FORKOP_INIT" ||
fail "init.d must delegate service orchestration to service/initd.uc"
grep -Fq 'initd_ucode start-service' "$PODKOP_INIT" ||
grep -Fq 'initd_ucode start-service' "$FORKOP_INIT" ||
fail "init.d start path must delegate to ucode"
grep -Fq 'initd_ucode stop-service' "$PODKOP_INIT" ||
grep -Fq 'initd_ucode stop-service' "$FORKOP_INIT" ||
fail "init.d stop path must delegate to ucode"
grep -Fq 'initd_ucode reload-service' "$PODKOP_INIT" ||
grep -Fq 'initd_ucode reload-service' "$FORKOP_INIT" ||
fail "init.d reload path must delegate to ucode"
grep -Fq 'initd_ucode trigger-plan' "$PODKOP_INIT" ||
grep -Fq 'initd_ucode trigger-plan' "$FORKOP_INIT" ||
fail "init.d trigger decisions must be produced by ucode"
if grep -n -E '(^|[^[:alnum:]_])(uci|config_load|config_get|config_foreach|jsonfilter|nft|iptables|ip6?tables|sing-box|dnsmasq|curl|wget|opkg|apk)([[:space:]]|$)' "$PODKOP_INIT" >/dev/null 2>&1; then
if grep -n -E '(^|[^[:alnum:]_])(uci|config_load|config_get|config_foreach|jsonfilter|nft|iptables|ip6?tables|sing-box|dnsmasq|curl|wget|opkg|apk)([[:space:]]|$)' "$FORKOP_INIT" >/dev/null 2>&1; then
fail "init.d must not own UCI, routing, download, package, dnsmasq, nft, or sing-box decisions"
fi
if grep -n -E 'PODKOP_RELOAD_LOCK|PODKOP_URLTEST_SELECTOR_SWITCHES|capture_reload_state|populate_nft_runtime_sets|rebuild_nft_runtime|apply_pending_urltest_selector_switches' "$PODKOP_INIT" >/dev/null 2>&1; then
if grep -n -E 'FORKOP_RELOAD_LOCK|FORKOP_URLTEST_SELECTOR_SWITCHES|capture_reload_state|populate_nft_runtime_sets|rebuild_nft_runtime|apply_pending_urltest_selector_switches' "$FORKOP_INIT" >/dev/null 2>&1; then
fail "init.d must not own runtime state or reload decisions"
fi
grep -Fq '/usr/bin/podkop-plus luci_postinst' "$LUCI_UCI_DEFAULTS" ||
grep -Fq '/usr/bin/forkop luci_postinst' "$LUCI_UCI_DEFAULTS" ||
fail "LuCI uci-defaults must delegate postinstall work to ucode"
if grep -n -E '(^|[^[:alnum:]_])(uci|rm|logger|rpcd|killall|jsonfilter|config_load|config_get)([[:space:]]|$)' "$LUCI_UCI_DEFAULTS" >/dev/null 2>&1; then
fail "LuCI uci-defaults must not own cache, rpcd, logging, or UCI logic"
+33 -33
View File
@@ -2,13 +2,13 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
CLI_UC="$PODKOP_BIN"
SING_BOX_RUNTIME_SH="$PODKOP_LIB/sing_box_runtime.sh"
LIFECYCLE_UC="$PODKOP_LIB/service/lifecycle.uc"
SINGBOX_RUNTIME_UC="$PODKOP_LIB/singbox/runtime.uc"
SINGBOX_GENERATOR_UC="$PODKOP_LIB/singbox/generator.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
CLI_UC="$FORKOP_BIN"
SING_BOX_RUNTIME_SH="$FORKOP_LIB/sing_box_runtime.sh"
LIFECYCLE_UC="$FORKOP_LIB/service/lifecycle.uc"
SINGBOX_RUNTIME_UC="$FORKOP_LIB/singbox/runtime.uc"
SINGBOX_GENERATOR_UC="$FORKOP_LIB/singbox/generator.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -23,13 +23,13 @@ fail() {
[ ! -e "$SING_BOX_RUNTIME_SH" ] ||
fail "sing_box_runtime.sh shell owner must be removed"
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
fail "podkop entrypoint must be a direct ucode executable"
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
fail "forkop entrypoint must be a direct ucode executable"
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
fail "service/cli.uc must dispatch lifecycle orchestration through service/lifecycle.uc"
grep -Fq 'singbox/runtime.uc' "$LIFECYCLE_UC" ||
fail "service/lifecycle.uc must call singbox/runtime.uc for sing-box runtime operations"
if grep -R -n -E 'sing_box_runtime_ucode|rulesets_ucode|sing_box_configure_service|sing_box_init_config|get_service_listen_address|get_device_ipv4_address|get_download_detour_tag' "$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
if grep -R -n -E 'sing_box_runtime_ucode|rulesets_ucode|sing_box_configure_service|sing_box_init_config|get_service_listen_address|get_device_ipv4_address|get_download_detour_tag' "$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
fail "sing-box runtime shell symbols must not remain"
fi
grep -Fq 'mode == "configure-service"' "$SINGBOX_RUNTIME_UC" ||
@@ -54,7 +54,7 @@ if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"' "$SINGBOX_GENERATOR_U
fi
grep -Fq 'require("core.uci")' "$SINGBOX_GENERATOR_UC" ||
fail "singbox/generator.uc must import core.uci"
grep -Fq 'PODKOP_RULE_CONDITION_CACHE_DIR' "$LIFECYCLE_UC" ||
grep -Fq 'FORKOP_RULE_CONDITION_CACHE_DIR' "$LIFECYCLE_UC" ||
fail "service/lifecycle.uc must pass rule-condition cache dir through module environment"
if awk '
/"write-current-reload-state-clean"|"write-captured-reload-state"/ {
@@ -79,7 +79,7 @@ fi
mkdir -p "$WORK_DIR/etc" "$WORK_DIR/tmp"
printf 'old config\n' >"$WORK_DIR/etc/config.json"
printf 'new config\n' >"$WORK_DIR/tmp/config.json"
ucode -L "$PODKOP_LIB" "$SINGBOX_RUNTIME_UC" save-config-file-fixture \
ucode -L "$FORKOP_LIB" "$SINGBOX_RUNTIME_UC" save-config-file-fixture \
"$WORK_DIR/tmp/config.json" "$WORK_DIR/etc/config.json"
[ "$(cat "$WORK_DIR/etc/config.json")" = "new config" ] ||
fail "singbox/runtime.uc must replace an existing config from a temp file"
@@ -92,7 +92,7 @@ generate_config() {
local mwan3_active="${3:-0}"
mkdir -p "$output.section-cache" "$output.rulesets"
ucode -L "$PODKOP_LIB" "$PODKOP_LIB/singbox/generator.uc" generate-config-fixture \
ucode -L "$FORKOP_LIB" "$FORKOP_LIB/singbox/generator.uc" generate-config-fixture \
"$fixture" "$output" "127.0.0.1" "$mwan3_active"
}
@@ -102,8 +102,8 @@ generate_config_with_subscription_cache() {
mkdir -p "$output.section-cache" "$output.rulesets"
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/subscriptions" \
PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent-subscription-cache" \
ucode -L "$PODKOP_LIB" "$PODKOP_LIB/singbox/generator.uc" generate-config-fixture \
FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent-subscription-cache" \
ucode -L "$FORKOP_LIB" "$FORKOP_LIB/singbox/generator.uc" generate-config-fixture \
"$fixture" "$output" "127.0.0.1" "0"
}
@@ -120,7 +120,7 @@ cat >"$WORK_DIR/no-enabled-fixture.json" <<'JSON'
}
JSON
if ucode -L "$PODKOP_LIB" "$SINGBOX_GENERATOR_UC" generate-config-fixture \
if ucode -L "$FORKOP_LIB" "$SINGBOX_GENERATOR_UC" generate-config-fixture \
"$WORK_DIR/no-enabled-fixture.json" "$WORK_DIR/no-enabled.json" "127.0.0.1" "0" \
>"$WORK_DIR/no-enabled.stdout" 2>"$WORK_DIR/no-enabled.stderr"; then
fail "generator should reject a config without enabled sections"
@@ -132,21 +132,21 @@ grep -Fxq 'no enabled sections' "$WORK_DIR/no-enabled.stderr" ||
fail "generator failure reason should be concise"
cat >"$WORK_DIR/generator-uci.state" <<'EOF_UCI'
podkop-plus.settings=settings
podkop-plus.settings.dns_server=1.1.1.1
podkop-plus.settings.bootstrap_dns_server=1.1.1.1
podkop-plus.settings.config_path=/tmp/sing-box/config.json
podkop-plus.settings.cache_path=/tmp/sing-box/cache.db
podkop-plus.settings.log_level=warn
podkop-plus.uci_proxy=section
podkop-plus.uci_proxy.enabled=1
podkop-plus.uci_proxy.action=connection
podkop-plus.uci_proxy.outbound_jsons={"type":"direct"}
podkop-plus.uci_proxy.domain_suffix=example.org
forkop.settings=settings
forkop.settings.dns_server=1.1.1.1
forkop.settings.bootstrap_dns_server=1.1.1.1
forkop.settings.config_path=/tmp/sing-box/config.json
forkop.settings.cache_path=/tmp/sing-box/cache.db
forkop.settings.log_level=warn
forkop.uci_proxy=section
forkop.uci_proxy.enabled=1
forkop.uci_proxy.action=connection
forkop.uci_proxy.outbound_jsons={"type":"direct"}
forkop.uci_proxy.domain_suffix=example.org
EOF_UCI
PODKOP_UCI_STATE_FILE="$WORK_DIR/generator-uci.state" \
PODKOP_SECTION_CACHE_DIR="$WORK_DIR/generated-from-uci.section-cache" \
ucode -L "$PODKOP_LIB" "$SINGBOX_GENERATOR_UC" generate-config "$WORK_DIR/generated-from-uci.json" "127.0.0.1" "0"
FORKOP_UCI_STATE_FILE="$WORK_DIR/generator-uci.state" \
FORKOP_SECTION_CACHE_DIR="$WORK_DIR/generated-from-uci.section-cache" \
ucode -L "$FORKOP_LIB" "$SINGBOX_GENERATOR_UC" generate-config "$WORK_DIR/generated-from-uci.json" "127.0.0.1" "0"
grep -Fq '"example.org"' "$WORK_DIR/generated-from-uci.json" ||
fail "singbox/generator.uc must read section matchers from core.uci"
grep -Fq '"uci_proxy-out"' "$WORK_DIR/generated-from-uci.json" ||
@@ -644,7 +644,7 @@ cat >"$WORK_DIR/subscriptions/grouped-subscription-1.json" <<'JSON'
"interval": "10m",
"tolerance": 50,
"remark": "Provider Group",
"__podkop_allow_group": true
"__forkop_allow_group": true
},
{
"type": "vless",
@@ -653,7 +653,7 @@ cat >"$WORK_DIR/subscriptions/grouped-subscription-1.json" <<'JSON'
"server_port": 443,
"uuid": "00000000-0000-4000-8000-000000000001",
"tls": { "enabled": true, "server_name": "example.com" },
"__podkop_hidden": true
"__forkop_hidden": true
},
{
"type": "vless",
@@ -662,7 +662,7 @@ cat >"$WORK_DIR/subscriptions/grouped-subscription-1.json" <<'JSON'
"server_port": 443,
"uuid": "00000000-0000-4000-8000-000000000002",
"tls": { "enabled": true, "server_name": "example.org" },
"__podkop_hidden": true
"__forkop_hidden": true
},
{
"type": "direct",
+12 -12
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
RULESETS_UC="$ROOT_DIR/podkop/files/usr/lib/singbox/rulesets.uc"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
RULESETS_UC="$ROOT_DIR/forkop/files/usr/lib/singbox/rulesets.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
fail() {
printf 'FAIL: %s\n' "$1" >&2
@@ -19,32 +19,32 @@ assert_eq() {
}
assert_eq srs \
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" file-extension 'https://example.com/path/rule.srs?token=1#x')" \
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" file-extension 'https://example.com/path/rule.srs?token=1#x')" \
"ruleset file extension"
ucode -L "$PODKOP_LIB" "$RULESETS_UC" is-community youtube >/dev/null ||
ucode -L "$FORKOP_LIB" "$RULESETS_UC" is-community youtube >/dev/null ||
fail "youtube should be a community ruleset"
if ucode -L "$PODKOP_LIB" "$RULESETS_UC" is-community unknown_service >/dev/null 2>&1; then
if ucode -L "$FORKOP_LIB" "$RULESETS_UC" is-community unknown_service >/dev/null 2>&1; then
fail "unknown service should not be a community ruleset"
fi
assert_eq domains \
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" kind-from-reference-hint 'https://example.com/geosite-custom.srs')" \
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" kind-from-reference-hint 'https://example.com/geosite-custom.srs')" \
"domain ruleset hint"
assert_eq subnets \
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" kind-from-reference-hint '/tmp/geoip-cidr.json')" \
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" kind-from-reference-hint '/tmp/geoip-cidr.json')" \
"subnet ruleset hint"
assert_eq unknown \
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" kind-from-reference-hint '/tmp/custom.srs')" \
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" kind-from-reference-hint '/tmp/custom.srs')" \
"unknown ruleset hint"
assert_eq source \
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" remote-format 'https://example.com/rules.json')" \
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" remote-format 'https://example.com/rules.json')" \
"json remote ruleset format"
assert_eq binary \
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" remote-format 'https://example.com/rules.srs')" \
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" remote-format 'https://example.com/rules.srs')" \
"srs remote ruleset format"
assert_eq binary \
"$(ucode -L "$PODKOP_LIB" "$RULESETS_UC" remote-format 'https://example.com/rules.unknown')" \
"$(ucode -L "$FORKOP_LIB" "$RULESETS_UC" remote-format 'https://example.com/rules.unknown')" \
"unknown remote ruleset format"
ucode -L "$PODKOP_LIB" -e 'let rulesets = require("singbox.rulesets"); if (rulesets.kind_from_reference_hint("geoip") != "subnets") exit(1);'
ucode -L "$FORKOP_LIB" -e 'let rulesets = require("singbox.rulesets"); if (rulesets.kind_from_reference_hint("geoip") != "subnets") exit(1);'
printf 'singbox rulesets checks passed\n'
+1 -1
View File
@@ -2,7 +2,7 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PARSER="$ROOT_DIR/podkop/files/usr/lib/subscription/parser.uc"
PARSER="$ROOT_DIR/forkop/files/usr/lib/subscription/parser.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
+20 -20
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CACHE_UC="$ROOT_DIR/podkop/files/usr/lib/subscription/cache.uc"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
CACHE_UC="$ROOT_DIR/forkop/files/usr/lib/subscription/cache.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -25,7 +25,7 @@ assert_eq() {
}
cache_ucode() {
ucode -L "$PODKOP_LIB" "$CACHE_UC" "$@"
ucode -L "$FORKOP_LIB" "$CACHE_UC" "$@"
}
assert_eq "first second" \
@@ -44,24 +44,24 @@ if cache_ucode state-list-contains "first second" third >/dev/null 2>&1; then
fail "state list should not contain third"
fi
SUBSCRIPTION_RUNTIME_SH="$ROOT_DIR/podkop/files/usr/lib/subscription_runtime.sh"
UPDATES_RUNTIME_SH="$ROOT_DIR/podkop/files/usr/lib/updates_runtime.sh"
SUBSCRIPTION_RUNTIME_SH="$ROOT_DIR/forkop/files/usr/lib/subscription_runtime.sh"
UPDATES_RUNTIME_SH="$ROOT_DIR/forkop/files/usr/lib/updates_runtime.sh"
[ ! -e "$SUBSCRIPTION_RUNTIME_SH" ] ||
fail "subscription_runtime.sh shell owner must be removed"
[ ! -e "$UPDATES_RUNTIME_SH" ] ||
fail "updates_runtime.sh shell owner must be removed"
if grep -R -n "subscription_runtime.sh" "$ROOT_DIR/podkop/files" >/dev/null 2>&1; then
if grep -R -n "subscription_runtime.sh" "$ROOT_DIR/forkop/files" >/dev/null 2>&1; then
fail "runtime files must not reference subscription_runtime.sh"
fi
subscription_runtime_shell_symbols='subscription_runtime_|prepare_subscription_caches_for_startup|prepare_subscription_caches_for_runtime_generation|run_deferred_subscription_bootstrap|stop_deferred_subscription_bootstrap_retry_worker'
if grep -R -n -E "$subscription_runtime_shell_symbols" "$ROOT_DIR/podkop/files/usr/bin/podkop" "$ROOT_DIR/podkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
if grep -R -n -E "$subscription_runtime_shell_symbols" "$ROOT_DIR/forkop/files/usr/bin/forkop" "$ROOT_DIR/forkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
fail "subscription_runtime shell symbols must not remain in runtime shell"
fi
if grep -R -n -E 'get_subscription_metadata_path|get_outbound_metadata_path' "$ROOT_DIR/podkop/files/usr/bin/podkop" "$ROOT_DIR/podkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
if grep -R -n -E 'get_subscription_metadata_path|get_outbound_metadata_path' "$ROOT_DIR/forkop/files/usr/bin/forkop" "$ROOT_DIR/forkop/files/usr/lib" --include='*.sh' >/dev/null 2>&1; then
fail "subscription metadata path helpers must be owned by subscription/cache.uc"
fi
@@ -94,10 +94,10 @@ grep -q 'mode == "stop-deferred-bootstrap-worker"' "$CACHE_UC" ||
assert_eq "proxy-subscription-2" \
"$(cache_ucode source-id proxy 2)" \
"source id mode"
assert_eq "/var/run/podkop-plus/subscription-metadata/proxy.json" \
assert_eq "/var/run/forkop/subscription-metadata/proxy.json" \
"$(cache_ucode subscription-metadata-path proxy)" \
"subscription metadata path mode"
assert_eq "/var/run/podkop-plus/outbound-metadata/proxy.json" \
assert_eq "/var/run/forkop/outbound-metadata/proxy.json" \
"$(cache_ucode outbound-metadata-path proxy)" \
"outbound metadata path mode"
if cache_ucode subscription-metadata-path '../bad' >/dev/null 2>&1; then
@@ -129,16 +129,16 @@ runtime_env() {
TMP_SING_BOX_FOLDER="$WORK_DIR/runtime/tmp-sing-box" \
TMP_RULESET_FOLDER="$WORK_DIR/runtime/tmp-sing-box/rulesets" \
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/runtime/tmp-sing-box/subscriptions" \
PODKOP_RUNTIME_STATE_DIR="$WORK_DIR/runtime/run" \
PODKOP_SUBSCRIPTION_UPDATE_STATE_DIR="$WORK_DIR/runtime/run/subscription-update" \
PODKOP_SUBSCRIPTION_LINKS_DIR="$WORK_DIR/runtime/run/subscription-links" \
PODKOP_SUBSCRIPTION_METADATA_DIR="$WORK_DIR/runtime/run/subscription-metadata" \
PODKOP_OUTBOUND_METADATA_DIR="$WORK_DIR/runtime/run/outbound-metadata" \
PODKOP_SECTION_CACHE_DIR="$WORK_DIR/runtime/run/section-cache" \
PODKOP_RUNTIME_CACHE_FORMAT_FILE="$WORK_DIR/runtime/run/cache-format" \
PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/runtime/persistent" \
PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE="$WORK_DIR/runtime/persistent/cache-format" \
PODKOP_SUBSCRIPTION_BOOTSTRAP_RETRY_PID_FILE="$WORK_DIR/runtime/run/bootstrap.pid" \
FORKOP_RUNTIME_STATE_DIR="$WORK_DIR/runtime/run" \
FORKOP_SUBSCRIPTION_UPDATE_STATE_DIR="$WORK_DIR/runtime/run/subscription-update" \
FORKOP_SUBSCRIPTION_LINKS_DIR="$WORK_DIR/runtime/run/subscription-links" \
FORKOP_SUBSCRIPTION_METADATA_DIR="$WORK_DIR/runtime/run/subscription-metadata" \
FORKOP_OUTBOUND_METADATA_DIR="$WORK_DIR/runtime/run/outbound-metadata" \
FORKOP_SECTION_CACHE_DIR="$WORK_DIR/runtime/run/section-cache" \
FORKOP_RUNTIME_CACHE_FORMAT_FILE="$WORK_DIR/runtime/run/cache-format" \
FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/runtime/persistent" \
FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE="$WORK_DIR/runtime/persistent/cache-format" \
FORKOP_SUBSCRIPTION_BOOTSTRAP_RETRY_PID_FILE="$WORK_DIR/runtime/run/bootstrap.pid" \
"$@"
}
+4 -4
View File
@@ -2,9 +2,9 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
PARSER="$ROOT_DIR/podkop/files/usr/lib/subscription/parser.uc"
GENERATOR="$ROOT_DIR/podkop/files/usr/lib/singbox/generator.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
PARSER="$ROOT_DIR/forkop/files/usr/lib/subscription/parser.uc"
GENERATOR="$ROOT_DIR/forkop/files/usr/lib/singbox/generator.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -119,7 +119,7 @@ cat >"$generator_fixture" <<'JSON'
}
JSON
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/subscriptions" \
ucode -L "$PODKOP_LIB" "$GENERATOR" generate-config-fixture \
ucode -L "$FORKOP_LIB" "$GENERATOR" generate-config-fixture \
"$generator_fixture" "$generator_output" "127.0.0.1" "0"
assert_contains "$generator_output" '"type": "hysteria2"' "generated stale HY2 type"
if grep -Fq '"utls"' "$generator_output"; then
+3 -3
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
PARSER="$ROOT_DIR/podkop/files/usr/lib/subscription/parser.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
PARSER="$ROOT_DIR/forkop/files/usr/lib/subscription/parser.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -69,6 +69,6 @@ if (invalid.valid || invalid.error != "Subscription URL must start with http://
exit(1);
UCODE
ucode -L "$PODKOP_LIB" "$WORK_DIR/require-subscription-parser.uc"
ucode -L "$FORKOP_LIB" "$WORK_DIR/require-subscription-parser.uc"
printf 'subscription source entry checks passed\n'
+20 -20
View File
@@ -2,9 +2,9 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
UPDATES_UC="$ROOT_DIR/podkop/files/usr/lib/components/updates.uc"
UPDATES_RUNTIME="$ROOT_DIR/podkop/files/usr/lib/updates_runtime.sh"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
UPDATES_UC="$ROOT_DIR/forkop/files/usr/lib/components/updates.uc"
UPDATES_RUNTIME="$ROOT_DIR/forkop/files/usr/lib/updates_runtime.sh"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -34,7 +34,7 @@ write_state() {
}
updates_ucode() {
ucode -L "$PODKOP_LIB" "$UPDATES_UC" "$@"
ucode -L "$FORKOP_LIB" "$UPDATES_UC" "$@"
}
[ ! -e "$UPDATES_RUNTIME" ] ||
@@ -79,7 +79,7 @@ if (value.running !== false || value.success !== false || value.section !== "pro
NODE
mkdir -p "$WORK_DIR/bin"
cat >"$WORK_DIR/bin/podkop-plus" <<'SH'
cat >"$WORK_DIR/bin/forkop" <<'SH'
#!/usr/bin/env sh
if [ "$1" != "subscription_update" ]; then
exit 64
@@ -87,27 +87,27 @@ fi
printf 'fake update for %s/%s\n' "$2" "$3"
printf 'Subscription update completed by fake worker\n'
SH
chmod +x "$WORK_DIR/bin/podkop-plus"
chmod +x "$WORK_DIR/bin/forkop"
ASYNC_ENV=(
"PODKOP_LIB=$ROOT_DIR/podkop/files/usr/lib"
"PODKOP_BIN=$WORK_DIR/bin/podkop-plus"
"FORKOP_LIB=$ROOT_DIR/forkop/files/usr/lib"
"FORKOP_BIN=$WORK_DIR/bin/forkop"
"TMP_SING_BOX_FOLDER=$WORK_DIR/tmp/sing-box"
"TMP_RULESET_FOLDER=$WORK_DIR/tmp/sing-box/rulesets"
"TMP_SUBSCRIPTION_FOLDER=$WORK_DIR/tmp/sing-box/subscriptions"
"PODKOP_RUNTIME_STATE_DIR=$WORK_DIR/run"
"PODKOP_SUBSCRIPTION_UPDATE_STATE_DIR=$WORK_DIR/run/subscription-update"
"PODKOP_SUBSCRIPTION_UPDATE_JOB_DIR=$WORK_DIR/run/subscription-update-jobs"
"PODKOP_SUBSCRIPTION_LINKS_DIR=$WORK_DIR/run/subscription-links"
"PODKOP_SUBSCRIPTION_METADATA_DIR=$WORK_DIR/run/subscription-metadata"
"PODKOP_OUTBOUND_METADATA_DIR=$WORK_DIR/run/outbound-metadata"
"PODKOP_SECTION_CACHE_DIR=$WORK_DIR/run/section-cache"
"PODKOP_RUNTIME_CACHE_FORMAT_FILE=$WORK_DIR/run/cache-format"
"PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR=$WORK_DIR/persistent/subscription-cache"
"PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE=$WORK_DIR/persistent/subscription-cache/cache-format"
"FORKOP_RUNTIME_STATE_DIR=$WORK_DIR/run"
"FORKOP_SUBSCRIPTION_UPDATE_STATE_DIR=$WORK_DIR/run/subscription-update"
"FORKOP_SUBSCRIPTION_UPDATE_JOB_DIR=$WORK_DIR/run/subscription-update-jobs"
"FORKOP_SUBSCRIPTION_LINKS_DIR=$WORK_DIR/run/subscription-links"
"FORKOP_SUBSCRIPTION_METADATA_DIR=$WORK_DIR/run/subscription-metadata"
"FORKOP_OUTBOUND_METADATA_DIR=$WORK_DIR/run/outbound-metadata"
"FORKOP_SECTION_CACHE_DIR=$WORK_DIR/run/section-cache"
"FORKOP_RUNTIME_CACHE_FORMAT_FILE=$WORK_DIR/run/cache-format"
"FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR=$WORK_DIR/persistent/subscription-cache"
"FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE=$WORK_DIR/persistent/subscription-cache/cache-format"
)
async_response="$(env "${ASYNC_ENV[@]}" ucode -L "$ROOT_DIR/podkop/files/usr/lib" "$UPDATES_UC" subscription-update-async proxy 1)"
async_response="$(env "${ASYNC_ENV[@]}" ucode -L "$ROOT_DIR/forkop/files/usr/lib" "$UPDATES_UC" subscription-update-async proxy 1)"
job_id="$(JSON_VALUE="$async_response" node - <<'NODE'
const value = JSON.parse(process.env.JSON_VALUE);
if (value.success !== true || !value.job_id) {
@@ -120,7 +120,7 @@ NODE
final_status=""
for _ in $(seq 1 30); do
final_status="$(env "${ASYNC_ENV[@]}" ucode -L "$ROOT_DIR/podkop/files/usr/lib" "$UPDATES_UC" subscription-update-status "$job_id")"
final_status="$(env "${ASYNC_ENV[@]}" ucode -L "$ROOT_DIR/forkop/files/usr/lib" "$UPDATES_UC" subscription-update-status "$job_id")"
if JSON_VALUE="$final_status" node -e 'process.exit(JSON.parse(process.env.JSON_VALUE).running ? 1 : 0)' >/dev/null 2>&1; then
break
fi
+18 -23
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
UPDATES_UC="$ROOT_DIR/podkop/files/usr/lib/components/updates.uc"
REAL_LIB="$ROOT_DIR/podkop/files/usr/lib"
UPDATES_UC="$ROOT_DIR/forkop/files/usr/lib/components/updates.uc"
REAL_LIB="$ROOT_DIR/forkop/files/usr/lib"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -44,11 +44,6 @@ function record(line) {
FAKE_LIB="$WORK_DIR/lib"
write_stub "$FAKE_LIB/config/migration.uc" "$stub_header"'
record("config/migration:" + as_string(ARGV[0]));
exit(ARGV[0] == "migrate" ? 0 : 64);
'
write_stub "$FAKE_LIB/subscription/cache.uc" "$stub_header"'
let mode = as_string(ARGV[0]);
record("subscription/cache:" + mode);
@@ -117,22 +112,22 @@ run_update() {
: >"$log"
env \
PODKOP_LIB="$FAKE_LIB" \
PODKOP_RUNTIME_STATE_DIR="$WORK_DIR/run" \
PODKOP_SUBSCRIPTION_UPDATE_LOCK_DIR="$WORK_DIR/run/subscription-update.lock" \
PODKOP_RELOAD_LOCK_DIR="$WORK_DIR/run/reload.lock" \
PODKOP_SUBSCRIPTION_UPDATE_STATE_DIR="$WORK_DIR/run/subscription-update" \
PODKOP_SUBSCRIPTION_UPDATE_JOB_DIR="$WORK_DIR/run/subscription-update-jobs" \
PODKOP_SUBSCRIPTION_LINKS_DIR="$WORK_DIR/run/subscription-links" \
PODKOP_SUBSCRIPTION_METADATA_DIR="$WORK_DIR/run/subscription-metadata" \
PODKOP_OUTBOUND_METADATA_DIR="$WORK_DIR/run/outbound-metadata" \
PODKOP_SECTION_CACHE_DIR="$WORK_DIR/run/section-cache" \
PODKOP_RUNTIME_CACHE_FORMAT_FILE="$WORK_DIR/run/cache-format" \
PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent/subscription-cache" \
PODKOP_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE="$WORK_DIR/persistent/subscription-cache/cache-format" \
PODKOP_PENDING_RELOAD_FILE="$WORK_DIR/run/reload.pending" \
PODKOP_RELOAD_STATE_FILE="$WORK_DIR/run/reload-state" \
PODKOP_RULE_CONDITION_CACHE_DIR="$WORK_DIR/run/rule-condition-cache" \
FORKOP_LIB="$FAKE_LIB" \
FORKOP_RUNTIME_STATE_DIR="$WORK_DIR/run" \
FORKOP_SUBSCRIPTION_UPDATE_LOCK_DIR="$WORK_DIR/run/subscription-update.lock" \
FORKOP_RELOAD_LOCK_DIR="$WORK_DIR/run/reload.lock" \
FORKOP_SUBSCRIPTION_UPDATE_STATE_DIR="$WORK_DIR/run/subscription-update" \
FORKOP_SUBSCRIPTION_UPDATE_JOB_DIR="$WORK_DIR/run/subscription-update-jobs" \
FORKOP_SUBSCRIPTION_LINKS_DIR="$WORK_DIR/run/subscription-links" \
FORKOP_SUBSCRIPTION_METADATA_DIR="$WORK_DIR/run/subscription-metadata" \
FORKOP_OUTBOUND_METADATA_DIR="$WORK_DIR/run/outbound-metadata" \
FORKOP_SECTION_CACHE_DIR="$WORK_DIR/run/section-cache" \
FORKOP_RUNTIME_CACHE_FORMAT_FILE="$WORK_DIR/run/cache-format" \
FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent/subscription-cache" \
FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE="$WORK_DIR/persistent/subscription-cache/cache-format" \
FORKOP_PENDING_RELOAD_FILE="$WORK_DIR/run/reload.pending" \
FORKOP_RELOAD_STATE_FILE="$WORK_DIR/run/reload-state" \
FORKOP_RULE_CONDITION_CACHE_DIR="$WORK_DIR/run/rule-condition-cache" \
FAKE_CALL_LOG="$log" \
FAKE_SUBSCRIPTION_UPDATE_SUMMARY="$summary" \
ucode -L "$REAL_LIB" "$UPDATES_UC" subscription-update-if-due
+3 -3
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CACHE_UC="$ROOT_DIR/podkop/files/usr/lib/subscription/cache.uc"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
CACHE_UC="$ROOT_DIR/forkop/files/usr/lib/subscription/cache.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -42,7 +42,7 @@ preferred_candidates="$(printf '%s\n%s\n%s\n%s\n%s\n%s' \
Clash.Meta)"
cache_ucode() {
ucode -L "$PODKOP_LIB" "$CACHE_UC" "$@"
ucode -L "$FORKOP_LIB" "$CACHE_UC" "$@"
}
cache_ucode write-user-agent-candidates "$WORK_DIR/default.txt" "" "" "$default_ua"
+30 -30
View File
@@ -2,11 +2,11 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
UI_UC="$ROOT_DIR/podkop/files/usr/lib/service/ui.uc"
PODKOP_FILES="$ROOT_DIR/podkop/files"
PODKOP_BIN="$PODKOP_FILES/usr/bin/podkop"
PODKOP_INIT="$PODKOP_FILES/etc/init.d/podkop"
UI_RUNTIME_SH="$PODKOP_FILES/usr/lib/ui_runtime.sh"
UI_UC="$ROOT_DIR/forkop/files/usr/lib/service/ui.uc"
FORKOP_FILES="$ROOT_DIR/forkop/files"
FORKOP_BIN="$FORKOP_FILES/usr/bin/forkop"
FORKOP_INIT="$FORKOP_FILES/etc/init.d/forkop"
UI_RUNTIME_SH="$FORKOP_FILES/usr/lib/ui_runtime.sh"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -20,7 +20,7 @@ fail() {
}
ui_ucode() {
ucode -L "$PODKOP_FILES/usr/lib" "$UI_UC" "$@"
ucode -L "$FORKOP_FILES/usr/lib" "$UI_UC" "$@"
}
assert_eq() {
@@ -42,16 +42,16 @@ write_state() {
[ ! -e "$UI_RUNTIME_SH" ] ||
fail "ui_runtime.sh shell owner must be removed"
if grep -R -n "ui_runtime.sh" "$PODKOP_FILES" >/dev/null 2>&1; then
if grep -R -n "ui_runtime.sh" "$FORKOP_FILES" >/dev/null 2>&1; then
fail "runtime files must not reference ui_runtime.sh"
fi
ui_runtime_shell_symbols='ui_runtime_|podkop_fast_get_ui_state|PODKOP_UI_RUNTIME|load_ui_runtime'
if grep -R -n -E "$ui_runtime_shell_symbols" "$PODKOP_BIN" "$PODKOP_INIT" "$PODKOP_FILES/usr/lib" --include='*.sh' >/dev/null 2>&1; then
ui_runtime_shell_symbols='ui_runtime_|forkop_fast_get_ui_state|FORKOP_UI_RUNTIME|load_ui_runtime'
if grep -R -n -E "$ui_runtime_shell_symbols" "$FORKOP_BIN" "$FORKOP_INIT" "$FORKOP_FILES/usr/lib" --include='*.sh' >/dev/null 2>&1; then
fail "ui_runtime shell symbols must not remain in runtime shell"
fi
if grep -R -n '^get_ui_capabilities()' "$PODKOP_FILES/usr/lib" --include='*.sh' >/dev/null 2>&1; then
if grep -R -n '^get_ui_capabilities()' "$FORKOP_FILES/usr/lib" --include='*.sh' >/dev/null 2>&1; then
fail "UI capabilities must be owned by service/ui.uc, not shell"
fi
grep -Fq 'require("core.uci")' "$UI_UC" ||
@@ -59,8 +59,8 @@ grep -Fq 'require("core.uci")' "$UI_UC" ||
if grep -n -E 'require\("uci"\)\.cursor|uci -q|uci", "-q"' "$UI_UC" >/dev/null 2>&1; then
fail "service/ui.uc must not own direct UCI cursor or CLI reads"
fi
grep -Fq '"podkop-stably-running"' "$UI_UC" ||
fail "UI Podkop status must use stable runtime state to avoid crash-loop flicker"
grep -Fq '"forkop-stably-running"' "$UI_UC" ||
fail "UI Forkop status must use stable runtime state to avoid crash-loop flicker"
grep -Fq '"sing-box-service-stable"' "$UI_UC" ||
fail "UI sing-box status must use stable runtime state to avoid crash-loop flicker"
grep -Fq 'run_pending_reload_after_service_action(action, success)' "$UI_UC" ||
@@ -127,7 +127,7 @@ latency_action_dir="$WORK_DIR/latency-actions"
mkdir -p "$latency_action_dir"
latency_state="$latency_action_dir/latency-1.json"
printf '%s\n' '{"success":true,"running":true,"kind":"latency","latency_type":"proxy_list","section":"main","tag":"[]","started_at":100}' >"$latency_state"
PODKOP_UI_LATENCY_ACTION_DIR="$latency_action_dir" \
FORKOP_UI_LATENCY_ACTION_DIR="$latency_action_dir" \
ui_ucode latency-progress-state "$latency_state" 2 5 1 >/dev/null ||
fail "latency-progress-state should update running latency jobs"
JOB_STATE="$latency_state" node - <<'NODE'
@@ -207,21 +207,21 @@ ui_ucode cleanup-action-dir-fixture "$cleanup_dir"
[ ! -e "$cleanup_dir/done.out.json" ] ||
fail "expired acknowledged action JSON sidecar should be cleaned"
export PODKOP_UI_STATE_DIR="$WORK_DIR/ui-state"
export PODKOP_UI_SERVICE_ACTION_DIR="$PODKOP_UI_STATE_DIR/service-actions"
export PODKOP_UI_SERVICE_ACTION_LOCK_DIR="$PODKOP_UI_STATE_DIR/service-actions.lock"
export PODKOP_UI_LATENCY_ACTION_DIR="$PODKOP_UI_STATE_DIR/latency-actions"
export PODKOP_UI_COMPONENT_ACTION_DIR="$PODKOP_UI_STATE_DIR/component-actions"
export PODKOP_UI_SUBSCRIPTION_ACTION_DIR="$PODKOP_UI_STATE_DIR/subscription-actions"
export FORKOP_UI_STATE_DIR="$WORK_DIR/ui-state"
export FORKOP_UI_SERVICE_ACTION_DIR="$FORKOP_UI_STATE_DIR/service-actions"
export FORKOP_UI_SERVICE_ACTION_LOCK_DIR="$FORKOP_UI_STATE_DIR/service-actions.lock"
export FORKOP_UI_LATENCY_ACTION_DIR="$FORKOP_UI_STATE_DIR/latency-actions"
export FORKOP_UI_COMPONENT_ACTION_DIR="$FORKOP_UI_STATE_DIR/component-actions"
export FORKOP_UI_SUBSCRIPTION_ACTION_DIR="$FORKOP_UI_STATE_DIR/subscription-actions"
latency_start="$(
PODKOP_BIN=/bin/true \
PODKOP_LIB="$PODKOP_FILES/usr/lib" \
FORKOP_BIN=/bin/true \
FORKOP_LIB="$FORKOP_FILES/usr/lib" \
ui_ucode latency-test-async proxy_list main '["proxy-a","proxy-b"]' 5000
)"
latency_job_id="$(printf '%s' "$latency_start" | sed -n 's/.*"job_id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')"
[ -n "$latency_job_id" ] || fail "latency-test-async should return a job id"
latency_async_state="$PODKOP_UI_LATENCY_ACTION_DIR/$latency_job_id.json"
latency_async_state="$FORKOP_UI_LATENCY_ACTION_DIR/$latency_job_id.json"
[ -f "$latency_async_state" ] || fail "latency-test-async should create a state file"
JOB_STATE="$latency_async_state" node - <<'NODE'
const fs = require("fs");
@@ -234,7 +234,7 @@ NODE
job_id="$(ui_ucode service-action-begin-if-idle reload test)"
[ -n "$job_id" ] || fail "service-action-begin-if-idle should create a job"
service_state="$PODKOP_UI_SERVICE_ACTION_DIR/$job_id.json"
service_state="$FORKOP_UI_SERVICE_ACTION_DIR/$job_id.json"
[ -f "$service_state" ] || fail "service action state file should be created"
assert_eq "reload" \
"$(ui_ucode active-service-action)" \
@@ -247,11 +247,11 @@ fi
ui_ucode service-action-update-pid "$job_id" "$$" >/dev/null ||
fail "service-action-update-pid should update running job"
PODKOP_UI_SERVICE_ACTION_TIMEOUT_SECONDS=1 \
PODKOP_UI_SERVICE_ACTION_SETTLE_SECONDS=0 \
PODKOP_SERVICE_INIT=/bin/true \
PODKOP_BIN=/bin/true \
PODKOP_LIB="$PODKOP_FILES/usr/lib" \
FORKOP_UI_SERVICE_ACTION_TIMEOUT_SECONDS=1 \
FORKOP_UI_SERVICE_ACTION_SETTLE_SECONDS=0 \
FORKOP_SERVICE_INIT=/bin/true \
FORKOP_BIN=/bin/true \
FORKOP_LIB="$FORKOP_FILES/usr/lib" \
ui_ucode service-action-finish-after-command reload "$job_id" 0 >/dev/null ||
fail "service-action-finish-after-command should spawn waiter without ucode declaration-order failure"
@@ -272,8 +272,8 @@ if (value.running !== false || value.success !== true || value.message !== "done
}
NODE
component_job="$PODKOP_UI_COMPONENT_ACTION_DIR/component-1.json"
mkdir -p "$PODKOP_UI_COMPONENT_ACTION_DIR"
component_job="$FORKOP_UI_COMPONENT_ACTION_DIR/component-1.json"
mkdir -p "$FORKOP_UI_COMPONENT_ACTION_DIR"
printf '%s\n' '{"running":true,"component":"sing_box","started_at":100}' >"$component_job"
ui_ucode component-action-running-for sing_box >/dev/null ||
fail "component-action-running-for should detect running sing-box component action"
+25 -25
View File
@@ -2,8 +2,8 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
UPDATES_UC="$ROOT_DIR/podkop/files/usr/lib/components/updates.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
UPDATES_UC="$ROOT_DIR/forkop/files/usr/lib/components/updates.uc"
fail() {
printf 'FAIL: %s\n' "$1" >&2
@@ -22,7 +22,7 @@ assert_status() {
}
updates_ucode() {
ucode -L "$PODKOP_LIB" "$UPDATES_UC" "$@"
ucode -L "$FORKOP_LIB" "$UPDATES_UC" "$@"
}
if grep -n -E 'require\("uci"\)\.cursor|uci -q' "$UPDATES_UC" >/dev/null 2>&1; then
@@ -83,11 +83,11 @@ assert_eq "0 0 * * *" \
"$(updates_ucode due-check-cron-schedule 86400)" \
"daily cron schedule"
assert_eq "*/30 * * * * /usr/bin/podkop list_update_if_due # list" \
"$(updates_ucode list-update-cron-job 30m /usr/bin/podkop '# list')" \
assert_eq "*/30 * * * * /usr/bin/forkop list_update_if_due # list" \
"$(updates_ucode list-update-cron-job 30m /usr/bin/forkop '# list')" \
"list update cron job"
assert_eq "0 */2 * * * /usr/bin/podkop subscription_update_if_due # subscription" \
"$(updates_ucode subscription-update-cron-job 7200 /usr/bin/podkop '# subscription')" \
assert_eq "0 */2 * * * /usr/bin/forkop subscription_update_if_due # subscription" \
"$(updates_ucode subscription-update-cron-job 7200 /usr/bin/forkop '# subscription')" \
"subscription update cron job"
assert_eq $'error\trule3\tinvalid\nmin\t1800' \
"$(printf 'rule1\t2h\nrule2\t30m\nrule3\tinvalid\nrule4\t\n' | updates_ucode subscription-update-interval-plan)" \
@@ -95,10 +95,10 @@ assert_eq $'error\trule3\tinvalid\nmin\t1800' \
assert_eq $'min\t0' \
"$(printf 'rule1\t\n' | updates_ucode subscription-update-interval-plan)" \
"empty subscription interval plan"
if updates_ucode list-update-cron-job invalid /usr/bin/podkop '# list' >/dev/null 2>&1; then
if updates_ucode list-update-cron-job invalid /usr/bin/forkop '# list' >/dev/null 2>&1; then
fail "invalid list update interval should fail"
fi
if updates_ucode subscription-update-cron-job 0 /usr/bin/podkop '# subscription' >/dev/null 2>&1; then
if updates_ucode subscription-update-cron-job 0 /usr/bin/forkop '# subscription' >/dev/null 2>&1; then
fail "invalid subscription update interval should fail"
fi
@@ -150,8 +150,8 @@ cat >"$WORK_DIR/cron-plan.json" <<'JSON'
}
JSON
assert_eq $'list\t*/30 * * * * /usr/bin/podkop list_update_if_due # list\nsubscription-error\tbad_sub\tbad\nsubscription\t*/45 * * * * /usr/bin/podkop subscription_update_if_due # subscription\ncomponent\t0 */2 * * * /usr/bin/podkop component_updates_if_due # component' \
"$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan.json" /usr/bin/podkop '# list' '# subscription' '# component')" \
assert_eq $'list\t*/30 * * * * /usr/bin/forkop list_update_if_due # list\nsubscription-error\tbad_sub\tbad\nsubscription\t*/45 * * * * /usr/bin/forkop subscription_update_if_due # subscription\ncomponent\t0 */2 * * * /usr/bin/forkop component_updates_if_due # component' \
"$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan.json" /usr/bin/forkop '# list' '# subscription' '# component')" \
"cron refresh plan"
grep -Fq 'fs.writefile(tmp, as_string(text)) == null' "$UPDATES_UC" ||
@@ -175,7 +175,7 @@ cat >"$WORK_DIR/cron-plan-list-disabled.json" <<'JSON'
JSON
assert_eq "list-disabled" \
"$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan-list-disabled.json" /usr/bin/podkop '# list' '# subscription' '# component')" \
"$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan-list-disabled.json" /usr/bin/forkop '# list' '# subscription' '# component')" \
"disabled list cron plan"
cat >"$WORK_DIR/cron-plan-invalid-list.json" <<'JSON'
@@ -196,26 +196,26 @@ cat >"$WORK_DIR/cron-plan-invalid-list.json" <<'JSON'
JSON
status=0
invalid_plan="$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan-invalid-list.json" /usr/bin/podkop '# list' '# subscription' '# component')" || status="$?"
invalid_plan="$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan-invalid-list.json" /usr/bin/forkop '# list' '# subscription' '# component')" || status="$?"
assert_eq 1 "$status" "invalid list cron plan status"
assert_eq $'list-error\tbad' "$invalid_plan" "invalid list cron plan output"
cat >"$WORK_DIR/existing.cron" <<'CRON'
0 1 * * * /bin/true # keep
* * * * * /usr/bin/podkop list_update_if_due # list
* * * * * /usr/bin/podkop subscription_update_if_due # subscription
* * * * * /usr/bin/podkop component_updates_if_due # component
* * * * * /usr/bin/forkop list_update_if_due # list
* * * * * /usr/bin/forkop subscription_update_if_due # subscription
* * * * * /usr/bin/forkop component_updates_if_due # component
CRON
updates_ucode refresh-cron-fixture "$WORK_DIR/cron-plan.json" "$WORK_DIR/existing.cron" /usr/bin/podkop '# list' '# subscription' '# component' >"$WORK_DIR/cron-apply.json"
updates_ucode refresh-cron-fixture "$WORK_DIR/cron-plan.json" "$WORK_DIR/existing.cron" /usr/bin/forkop '# list' '# subscription' '# component' >"$WORK_DIR/cron-apply.json"
node - "$WORK_DIR/cron-apply.json" <<'JS'
const fs = require("fs");
const value = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
const expected = [
"0 1 * * * /bin/true # keep",
"*/30 * * * * /usr/bin/podkop list_update_if_due # list",
"*/45 * * * * /usr/bin/podkop subscription_update_if_due # subscription",
"0 */2 * * * /usr/bin/podkop component_updates_if_due # component",
"*/30 * * * * /usr/bin/forkop list_update_if_due # list",
"*/45 * * * * /usr/bin/forkop subscription_update_if_due # subscription",
"0 */2 * * * /usr/bin/forkop component_updates_if_due # component",
""
].join("\n");
if (value.crontab !== expected) {
@@ -224,22 +224,22 @@ if (value.crontab !== expected) {
}
const messages = value.logs.map(item => item.message);
if (!messages.includes("The cron job removed") ||
!messages.includes("The cron job has been created: */30 * * * * /usr/bin/podkop list_update_if_due # list") ||
!messages.includes("The subscription cron job has been created: */45 * * * * /usr/bin/podkop subscription_update_if_due # subscription") ||
!messages.includes("The component update check cron job has been created: 0 */2 * * * /usr/bin/podkop component_updates_if_due # component")) {
!messages.includes("The cron job has been created: */30 * * * * /usr/bin/forkop list_update_if_due # list") ||
!messages.includes("The subscription cron job has been created: */45 * * * * /usr/bin/forkop subscription_update_if_due # subscription") ||
!messages.includes("The component update check cron job has been created: 0 */2 * * * /usr/bin/forkop component_updates_if_due # component")) {
console.error("unexpected cron apply logs", JSON.stringify(value.logs));
process.exit(1);
}
JS
status=0
updates_ucode refresh-cron-fixture "$WORK_DIR/cron-plan-invalid-list.json" "$WORK_DIR/existing.cron" /usr/bin/podkop '# list' '# subscription' '# component' >"$WORK_DIR/cron-apply-invalid.json" || status="$?"
updates_ucode refresh-cron-fixture "$WORK_DIR/cron-plan-invalid-list.json" "$WORK_DIR/existing.cron" /usr/bin/forkop '# list' '# subscription' '# component' >"$WORK_DIR/cron-apply-invalid.json" || status="$?"
assert_eq 1 "$status" "invalid cron apply status"
node - "$WORK_DIR/cron-apply-invalid.json" <<'JS'
const fs = require("fs");
const value = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
if (value.crontab !== "0 1 * * * /bin/true # keep\n") {
console.error("invalid plan should remove old podkop cron jobs only", JSON.stringify(value.crontab));
console.error("invalid plan should remove old forkop cron jobs only", JSON.stringify(value.crontab));
process.exit(1);
}
if (!value.logs.some(item => item.level === "error" && item.message === "Invalid update_interval value: bad")) {
+10 -10
View File
@@ -2,10 +2,10 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
PARSER_UC="$PODKOP_LIB/subscription/parser.uc"
GENERATOR_UC="$PODKOP_LIB/singbox/generator.uc"
CACHE_UC="$PODKOP_LIB/subscription/cache.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
PARSER_UC="$FORKOP_LIB/subscription/parser.uc"
GENERATOR_UC="$FORKOP_LIB/singbox/generator.uc"
CACHE_UC="$FORKOP_LIB/subscription/cache.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -21,7 +21,7 @@ fail() {
normalize_subscription() {
local input="$1"
local output="$2"
ucode -L "$PODKOP_LIB" "$PARSER_UC" normalize-content "$input" "$output"
ucode -L "$FORKOP_LIB" "$PARSER_UC" normalize-content "$input" "$output"
}
prepare_subscription_cache() {
@@ -42,7 +42,7 @@ generate_config() {
local output="$2"
mkdir -p "${output}.section-cache"
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/subscriptions" \
ucode -L "$PODKOP_LIB" "$GENERATOR_UC" generate-config-fixture \
ucode -L "$FORKOP_LIB" "$GENERATOR_UC" generate-config-fixture \
"$fixture" "$output" "127.0.0.1"
}
@@ -286,7 +286,7 @@ if (!reveal_selector || length(reveal_selector.outbounds || []) != 4)
' "$xray_config" "$xray_config.section-cache/proxy.json" "$xray_reveal_urltest_config" "$xray_group_name_filter_config" || fail "xray generated URLTest behavior"
xray_metadata="$WORK_DIR/xray-ui-outbound-metadata.json"
ucode -L "$PODKOP_LIB" "$CACHE_UC" get-outbound-metadata "$xray_config.section-cache" proxy "$WORK_DIR/missing-outbound-metadata.json" >"$xray_metadata"
ucode -L "$FORKOP_LIB" "$CACHE_UC" get-outbound-metadata "$xray_config.section-cache" proxy "$WORK_DIR/missing-outbound-metadata.json" >"$xray_metadata"
ucode -e '
let fs = require("fs");
@@ -356,8 +356,8 @@ let value = json(fs.readfile(ARGV[0]));
let flags = {};
for (let outbound in value.outbounds || [])
flags[outbound.tag] = {
allow: outbound.__podkop_allow_group === true,
hidden: outbound.__podkop_hidden === true
allow: outbound.__forkop_allow_group === true,
hidden: outbound.__forkop_hidden === true
};
if (!flags["Native Group"].allow)
die("native sing-box URLTest group was not allowed\n");
@@ -591,7 +591,7 @@ if (!contains(candidates, "Native A") || !contains(candidates, "Detour Only"))
' "$singbox_config" "$singbox_config.section-cache/proxy.json" "$singbox_reveal_urltest_config" "$singbox_reveal_detour_config" || fail "native sing-box generated URLTest behavior"
singbox_metadata="$WORK_DIR/singbox-ui-outbound-metadata.json"
ucode -L "$PODKOP_LIB" "$CACHE_UC" get-outbound-metadata "$singbox_config.section-cache" proxy "$WORK_DIR/missing-outbound-metadata.json" >"$singbox_metadata"
ucode -L "$FORKOP_LIB" "$CACHE_UC" get-outbound-metadata "$singbox_config.section-cache" proxy "$WORK_DIR/missing-outbound-metadata.json" >"$singbox_metadata"
ucode -e '
let fs = require("fs");
+4 -4
View File
@@ -2,7 +2,7 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -69,7 +69,7 @@ JSON
output="$WORK_DIR/config.json"
mkdir -p "$output.section-cache"
ucode -L "$PODKOP_LIB" "$PODKOP_LIB/singbox/generator.uc" generate-config-fixture \
ucode -L "$FORKOP_LIB" "$FORKOP_LIB/singbox/generator.uc" generate-config-fixture \
"$WORK_DIR/fixture.json" "$output" "127.0.0.1"
urltest_count="$(ucode -e '
@@ -148,7 +148,7 @@ JSON
multi_output="$WORK_DIR/multi-config.json"
mkdir -p "$multi_output.section-cache"
ucode -L "$PODKOP_LIB" "$PODKOP_LIB/singbox/generator.uc" generate-config-fixture \
ucode -L "$FORKOP_LIB" "$FORKOP_LIB/singbox/generator.uc" generate-config-fixture \
"$WORK_DIR/multi-fixture.json" "$multi_output" "127.0.0.1"
ucode -e '
@@ -222,7 +222,7 @@ JSON
hide_added_output="$WORK_DIR/hide-added-config.json"
mkdir -p "$hide_added_output.section-cache"
ucode -L "$PODKOP_LIB" "$PODKOP_LIB/singbox/generator.uc" generate-config-fixture \
ucode -L "$FORKOP_LIB" "$FORKOP_LIB/singbox/generator.uc" generate-config-fixture \
"$WORK_DIR/hide-added-fixture.json" "$hide_added_output" "127.0.0.1"
ucode -e '
+26 -26
View File
@@ -2,32 +2,32 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_BIN="$ROOT_DIR/podkop/files/usr/bin/podkop"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
CLI_UC="$PODKOP_BIN"
LIFECYCLE_UC="$PODKOP_LIB/service/lifecycle.uc"
ZAPRET_RUNTIME="$PODKOP_LIB/providers/zapret/runtime.uc"
ZAPRET2_RUNTIME="$PODKOP_LIB/providers/zapret2/runtime.uc"
ZAPRET_COMMON="$PODKOP_LIB/providers/zapret/common.uc"
ZAPRET2_COMMON="$PODKOP_LIB/providers/zapret2/common.uc"
NFQUEUE_RUNTIME="$PODKOP_LIB/providers/nfqueue/runtime.uc"
NFQUEUE_CHECK="$PODKOP_LIB/providers/nfqueue/check.uc"
NFQUEUE_VALIDATOR="$PODKOP_LIB/providers/nfqueue/validator.uc"
ZAPRET2_CHECK="$PODKOP_LIB/providers/zapret2/check.uc"
ZAPRET2_VALIDATOR="$PODKOP_LIB/providers/zapret2/validator.uc"
FORKOP_BIN="$ROOT_DIR/forkop/files/usr/bin/forkop"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
CLI_UC="$FORKOP_BIN"
LIFECYCLE_UC="$FORKOP_LIB/service/lifecycle.uc"
ZAPRET_RUNTIME="$FORKOP_LIB/providers/zapret/runtime.uc"
ZAPRET2_RUNTIME="$FORKOP_LIB/providers/zapret2/runtime.uc"
ZAPRET_COMMON="$FORKOP_LIB/providers/zapret/common.uc"
ZAPRET2_COMMON="$FORKOP_LIB/providers/zapret2/common.uc"
NFQUEUE_RUNTIME="$FORKOP_LIB/providers/nfqueue/runtime.uc"
NFQUEUE_CHECK="$FORKOP_LIB/providers/nfqueue/check.uc"
NFQUEUE_VALIDATOR="$FORKOP_LIB/providers/nfqueue/validator.uc"
ZAPRET2_CHECK="$FORKOP_LIB/providers/zapret2/check.uc"
ZAPRET2_VALIDATOR="$FORKOP_LIB/providers/zapret2/validator.uc"
fail() {
printf 'FAIL: %s\n' "$1" >&2
exit 1
}
[ ! -e "$PODKOP_LIB/zapret.sh" ] ||
[ ! -e "$FORKOP_LIB/zapret.sh" ] ||
fail "zapret.sh shell owner must be removed"
[ ! -e "$PODKOP_LIB/zapret2.sh" ] ||
[ ! -e "$FORKOP_LIB/zapret2.sh" ] ||
fail "zapret2.sh shell owner must be removed"
grep -Fq '#!/usr/bin/ucode' "$PODKOP_BIN" ||
fail "podkop entrypoint must be a direct ucode executable"
grep -Fq '#!/usr/bin/ucode' "$FORKOP_BIN" ||
fail "forkop entrypoint must be a direct ucode executable"
grep -Fq 'service/lifecycle.uc' "$CLI_UC" ||
fail "service/cli.uc must dispatch lifecycle orchestration through service/lifecycle.uc"
grep -Fq 'providers/zapret/runtime.uc' "$LIFECYCLE_UC" ||
@@ -64,21 +64,21 @@ if grep -n -E 'function (start_runtime|stop_runtime|status_json|create_nft_rules
fail "provider common modules must stay thin config wrappers, not runtime engines"
fi
if grep -R -n -E 'providers/zapret/runtime\.uc" (start-runtime|stop-runtime|status|check|installed|package-installed|package-version|enabled-rule-count) zapret2' \
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
fail "zapret2 must not be multiplexed through providers/zapret/runtime.uc"
fi
if grep -R -n -E 'providers\.zapret\.|providers/zapret/' "$PODKOP_LIB/providers/zapret2" >/dev/null 2>&1; then
if grep -R -n -E 'providers\.zapret\.|providers/zapret/' "$FORKOP_LIB/providers/zapret2" >/dev/null 2>&1; then
fail "zapret2 provider modules must not import or execute providers/zapret modules"
fi
if grep -n 'zapret2' "$ZAPRET_COMMON" >/dev/null 2>&1; then
fail "providers/zapret/common.uc must not contain zapret2 ownership"
fi
if grep -R -n -E 'start_zapret2?_runtime|stop_zapret2?_runtime|get_zapret2?_status_json|check_zapret2?_runtime_json|create_zapret2?_nft_rules|validate_rule_nfqws2?_opt|has_enabled_zapret2?_rules|get_zapret2?_rule_' \
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
fail "zapret runtime shell symbols must not remain"
fi
if grep -R -n -E 'is_zapret2?_installed|get_zapret2?_package_version' \
"$PODKOP_BIN" "$PODKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
"$FORKOP_BIN" "$FORKOP_LIB" --include='*.sh' >/dev/null 2>&1; then
fail "zapret installed/version shell predicates must not remain"
fi
if grep -n -E 'require\("uci"\)\.cursor|uci -q|"uci", "-q"|command_output\(command_from_args\(\[ "uci"' "$NFQUEUE_RUNTIME" "$ZAPRET_COMMON" "$ZAPRET2_COMMON" >/dev/null 2>&1; then
@@ -119,7 +119,7 @@ grep -Fq 'providers/zapret2/check.uc' "$ZAPRET2_COMMON" ||
grep -Fq 'providers.zapret2.validator' "$ZAPRET2_COMMON" ||
fail "providers/zapret2/common.uc must use its own validator module"
status_json="$(PODKOP_CONFIG_NAME=podkop-plus-definitely-missing ucode -L "$PODKOP_LIB" "$ZAPRET_RUNTIME" status)"
status_json="$(FORKOP_CONFIG_NAME=forkop-definitely-missing ucode -L "$FORKOP_LIB" "$ZAPRET_RUNTIME" status)"
JSON_VALUE="$status_json" node - <<'NODE'
const value = JSON.parse(process.env.JSON_VALUE);
if (value.configured !== false || value.enabled_rule_count !== 0 || typeof value.provider_path !== 'string') {
@@ -128,7 +128,7 @@ if (value.configured !== false || value.enabled_rule_count !== 0 || typeof value
}
NODE
check_json="$(ucode -L "$PODKOP_LIB" "$ZAPRET2_RUNTIME" check)"
check_json="$(ucode -L "$FORKOP_LIB" "$ZAPRET2_RUNTIME" check)"
JSON_VALUE="$check_json" node - <<'NODE'
const value = JSON.parse(process.env.JSON_VALUE);
if (!Object.prototype.hasOwnProperty.call(value, 'zapret2_installed') ||
@@ -139,14 +139,14 @@ if (!Object.prototype.hasOwnProperty.call(value, 'zapret2_installed') ||
}
NODE
ucode -L "$PODKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws2 '--name podkop --intercept=1' >/dev/null ||
ucode -L "$FORKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws2 '--name forkop --intercept=1' >/dev/null ||
fail "providers/zapret2/validator.uc must validate nfqws2 strategies"
if ucode -L "$PODKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws '--dpi-desync=fake' >/dev/null 2>&1; then
if ucode -L "$FORKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws '--dpi-desync=fake' >/dev/null 2>&1; then
fail "providers/zapret2/validator.uc must not validate nfqws strategies"
fi
printf 'table inet x { chain y { queue num 4301 bypass } }\n' |
ucode -L "$PODKOP_LIB" "$ZAPRET2_CHECK" nft-queue-overlap PodkopPlusTable 4300 4555 >/dev/null ||
ucode -L "$FORKOP_LIB" "$ZAPRET2_CHECK" nft-queue-overlap ForkopTable 4300 4555 >/dev/null ||
fail "providers/zapret2/check.uc must own zapret2 queue overlap checks"
printf 'zapret runtime ownership checks passed\n'
+16 -16
View File
@@ -2,9 +2,9 @@
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
VALIDATOR="$ROOT_DIR/podkop/files/usr/lib/providers/zapret/validator.uc"
ZAPRET2_VALIDATOR="$ROOT_DIR/podkop/files/usr/lib/providers/zapret2/validator.uc"
FORKOP_LIB="$ROOT_DIR/forkop/files/usr/lib"
VALIDATOR="$ROOT_DIR/forkop/files/usr/lib/providers/zapret/validator.uc"
ZAPRET2_VALIDATOR="$ROOT_DIR/forkop/files/usr/lib/providers/zapret2/validator.uc"
WORK_DIR="$(mktemp -d)"
cleanup() {
@@ -43,42 +43,42 @@ assert_json_field() {
[ "$actual" = "$expected" ] || fail "expected $field=$expected, got $actual"
}
valid_nfqws="$(ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-json nfqws '--dpi-desync=fake --dpi-desync-repeats 2')"
valid_nfqws="$(ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-json nfqws '--dpi-desync=fake --dpi-desync-repeats 2')"
assert_json_field "$valid_nfqws" valid true
configured="$(ucode -L "$PODKOP_LIB" "$VALIDATOR" strategy-or-default "$(printf -- '--dpi-desync=fake\t--dpi-desync-repeats 2')" '--default')"
configured="$(ucode -L "$FORKOP_LIB" "$VALIDATOR" strategy-or-default "$(printf -- '--dpi-desync=fake\t--dpi-desync-repeats 2')" '--default')"
[ "$configured" = "--dpi-desync=fake --dpi-desync-repeats 2" ] ||
fail "configured zapret strategy should be normalized, got '$configured'"
defaulted="$(ucode -L "$PODKOP_LIB" "$VALIDATOR" strategy-or-default "" "$(printf -- '--default\t1')")"
defaulted="$(ucode -L "$FORKOP_LIB" "$VALIDATOR" strategy-or-default "" "$(printf -- '--default\t1')")"
[ "$defaulted" = "--default 1" ] ||
fail "empty zapret strategy should use normalized default, got '$defaulted'"
if invalid_nfqws="$(ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-json nfqws '--hostlist domains.txt' 2>/dev/null)"; then
if invalid_nfqws="$(ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-json nfqws '--hostlist domains.txt' 2>/dev/null)"; then
fail "hostlist selection should be rejected for nfqws"
fi
assert_json_field "$invalid_nfqws" valid false
assert_json_field "$invalid_nfqws" needle --hostlist
if ucode -L "$PODKOP_LIB" "$VALIDATOR" validate nfqws '--qnum=200' >/tmp/zapret-validator.out 2>/dev/null; then
if ucode -L "$FORKOP_LIB" "$VALIDATOR" validate nfqws '--qnum=200' >/tmp/zapret-validator.out 2>/dev/null; then
fail "qnum override should be rejected for nfqws"
fi
grep -q 'NFQUEUE number is assigned by Podkop Plus' /tmp/zapret-validator.out ||
grep -q 'NFQUEUE number is assigned by Forkop' /tmp/zapret-validator.out ||
fail "qnum rejection should explain ownership"
valid_nfqws2="$(ucode -L "$PODKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws2 '--name podkop --intercept=1')"
valid_nfqws2="$(ucode -L "$FORKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws2 '--name forkop --intercept=1')"
assert_json_field "$valid_nfqws2" valid true
if invalid_nfqws2="$(ucode -L "$PODKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws2 '--intercept=0' 2>/dev/null)"; then
if invalid_nfqws2="$(ucode -L "$FORKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws2 '--intercept=0' 2>/dev/null)"; then
fail "disabled nfqws2 intercept should be rejected"
fi
assert_json_field "$invalid_nfqws2" valid false
assert_json_field "$invalid_nfqws2" needle --intercept
if ucode -L "$PODKOP_LIB" "$VALIDATOR" validate-json nfqws2 '--name podkop --intercept=1' >/dev/null 2>&1; then
if ucode -L "$FORKOP_LIB" "$VALIDATOR" validate-json nfqws2 '--name forkop --intercept=1' >/dev/null 2>&1; then
fail "zapret validator must not own nfqws2 validation"
fi
if ucode -L "$PODKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws '--dpi-desync=fake' >/dev/null 2>&1; then
if ucode -L "$FORKOP_LIB" "$ZAPRET2_VALIDATOR" validate-json nfqws '--dpi-desync=fake' >/dev/null 2>&1; then
fail "zapret2 validator must not own nfqws validation"
fi
@@ -94,11 +94,11 @@ let invalid_nfqws = validator.validate_strategy("nfqws", "--hostlist domains.txt
if (invalid_nfqws.valid || invalid_nfqws.needle != "--hostlist")
exit(1);
let valid_nfqws2 = zapret2_validator.validate_strategy("nfqws2", "--name podkop --intercept=1", "");
let valid_nfqws2 = zapret2_validator.validate_strategy("nfqws2", "--name forkop --intercept=1", "");
if (!valid_nfqws2.valid)
exit(1);
if (validator.validate_strategy("nfqws2", "--name podkop --intercept=1", "").valid)
if (validator.validate_strategy("nfqws2", "--name forkop --intercept=1", "").valid)
exit(1);
if (zapret2_validator.validate_strategy("nfqws", "--dpi-desync=fake", "").valid)
exit(1);
@@ -107,6 +107,6 @@ if (validator.strategy_or_default("", "--default\t1") != "--default 1")
exit(1);
UCODE
ucode -L "$PODKOP_LIB" "$WORK_DIR/require-zapret-validator.uc"
ucode -L "$FORKOP_LIB" "$WORK_DIR/require-zapret-validator.uc"
printf 'Zapret validator checks passed\n'