#!/usr/bin/env ucode let fs = require("fs"); let common = require("core.common"); let as_string = common.as_string; let write_json = common.write_json; let read_json_file = common.read_json_file; let read_stdin = common.read_stdin; let array_or_empty = common.array_or_empty; function int_arg(value) { value = as_string(value); return value == "" ? 0 : int(value, 10); } function bool_arg(value) { value = as_string(value); return value == "1" || value == "true"; } function first_field(line) { let fields = split(trim(as_string(line)), /[ \t\r\n]+/); return length(fields) > 0 ? as_string(fields[0]) : ""; } function stdin_first_nonempty_field() { for (let line in split(read_stdin(), "\n")) { let field = first_field(line); if (field != "") { print(field, "\n"); return; } } } function stdin_first_line_version_field() { let data = read_stdin(); let newline = index(data, "\n"); let line = newline >= 0 ? substr(data, 0, newline) : data; if (match(line, /^.*version[ \t]*/) == null) return; let field = first_field(replace(line, /^.*version[ \t]*/, "")); if (field != "") print(field, "\n"); } function byedpi_restart_count_file(path) { let data = fs.readfile(path); let count = 0; if (data != null) for (let line in split(data, "\n")) if (match(as_string(line), /ciadpi for rule .* exited with code/) != null) count++; print(count, "\n"); } function byedpi_validation_result(valid, message, needle) { let needles = []; if (!bool_arg(valid)) { for (let line in split(read_stdin(), "\n")) { if (line != "") push(needles, line); } } write_json({ valid: bool_arg(valid), message: as_string(message), needle: as_string(needle), needles }); } function has_socks_outbound(config_path, tag, address, port) { let config = read_json_file(config_path); port = int_arg(port); for (let outbound in array_or_empty(config && config.outbounds)) { if (type(outbound) == "object" && outbound.type == "socks" && outbound.tag == tag && outbound.server == address && int(outbound.server_port || 0) == port) return true; } return false; } function has_direct_mark_outbound(config_path, tag, routing_mark) { let config = read_json_file(config_path); routing_mark = int_arg(routing_mark); for (let outbound in array_or_empty(config && config.outbounds)) { if (type(outbound) == "object" && outbound.type == "direct" && outbound.tag == tag && int(outbound.routing_mark || 0) == routing_mark) return true; } return false; } function has_route_rule(config_path, inbound, outbound_tag) { let config = read_json_file(config_path); for (let rule in array_or_empty(config && config.route && config.route.rules)) { if (type(rule) == "object" && rule.action == "route" && rule.inbound == inbound && rule.outbound == outbound_tag) return true; } return false; } function value_contains(value, needle) { if (type(value) == "array") { for (let item in value) { if (as_string(item) == needle) return true; } return false; } return as_string(value) == needle; } function find_tagged_item(items, tag) { for (let item in array_or_empty(items)) { if (type(item) == "object" && item.tag == tag) return item; } return null; } function tagged_runtime_summary(config_path, section_name, tag) { let config = read_json_file(config_path); let item = find_tagged_item(config && config[section_name], tag); if (item == null) { write_json({ exists: 0 }); return; } write_json({ exists: 1, type: as_string(item.type), listen: as_string(item.listen), listen_port: int_arg(item.listen_port) }); } function has_route_rule_for_inbound(config_path, inbound) { let config = read_json_file(config_path); for (let rule in array_or_empty(config && config.route && config.route.rules)) { if (type(rule) == "object" && (rule.action == "route" || rule.action == "reject") && value_contains(rule.inbound, inbound)) return true; } return false; } function byedpi_status(args) { write_json({ installed: bool_arg(args[0]), package_installed: bool_arg(args[1]), provider_available: bool_arg(args[2]), provider_path: as_string(args[3]), version: as_string(args[4]), configured: bool_arg(args[5]), enabled_rule_count: int_arg(args[6]), expected_process_count: int_arg(args[7]), running_process_count: int_arg(args[8]), supervisor_process_count: int_arg(args[9]), restart_count: int_arg(args[10]), runtime_unstable: bool_arg(args[11]), standalone_service_enabled: bool_arg(args[12]), standalone_service_running: bool_arg(args[13]), listen_address: as_string(args[14]), port_base: int_arg(args[15]), outbounds_configured: bool_arg(args[16]), routes_configured: bool_arg(args[17]), ready: bool_arg(args[18]), conflict: bool_arg(args[19]), status_message: as_string(args[20]) }); } function byedpi_check(installed, package_installed, provider_path) { write_json({ byedpi_installed: bool_arg(installed), byedpi_package_installed: bool_arg(package_installed), byedpi_provider_path: as_string(provider_path) }); } function zapret_status(args) { write_json({ installed: bool_arg(args[0]), package_installed: bool_arg(args[1]), provider_available: bool_arg(args[2]), provider_path: as_string(args[3]), files_available: bool_arg(args[4]), ipset_available: bool_arg(args[5]), version: as_string(args[6]), configured: bool_arg(args[7]), enabled_rule_count: int_arg(args[8]), expected_process_count: int_arg(args[9]), running_process_count: int_arg(args[10]), supervisor_process_count: int_arg(args[11]), standalone_service_enabled: bool_arg(args[12]), standalone_service_running: bool_arg(args[13]), standalone_config_present: bool_arg(args[14]), standalone_conflict: bool_arg(args[15]), luci_app_installed: bool_arg(args[16]), queue_base: int_arg(args[17]), queue_range_end: int_arg(args[18]), queue_overlap: bool_arg(args[19]), legacy_runtime_present: bool_arg(args[20]), ready: bool_arg(args[21]), conflict: bool_arg(args[22]), outbounds_configured: bool_arg(args[23]), routes_configured: bool_arg(args[24]), status_message: as_string(args[25]) }); } function zapret_check(installed, package_installed, provider_path) { write_json({ zapret_installed: bool_arg(installed), zapret_package_installed: bool_arg(package_installed), zapret_provider_path: as_string(provider_path) }); } function zapret2_status(args) { write_json({ installed: bool_arg(args[0]), package_installed: bool_arg(args[1]), provider_available: bool_arg(args[2]), provider_path: as_string(args[3]), files_available: bool_arg(args[4]), ipset_available: bool_arg(args[5]), version: as_string(args[6]), configured: bool_arg(args[7]), enabled_rule_count: int_arg(args[8]), expected_process_count: int_arg(args[9]), running_process_count: int_arg(args[10]), supervisor_process_count: int_arg(args[11]), standalone_service_enabled: bool_arg(args[12]), standalone_service_running: bool_arg(args[13]), standalone_config_present: bool_arg(args[14]), standalone_conflict: bool_arg(args[15]), luci_app_installed: bool_arg(args[16]), queue_base: int_arg(args[17]), queue_range_end: int_arg(args[18]), queue_overlap: bool_arg(args[19]), ready: bool_arg(args[20]), conflict: bool_arg(args[21]), outbounds_configured: bool_arg(args[22]), routes_configured: bool_arg(args[23]), status_message: as_string(args[24]) }); } function zapret2_check(installed, package_installed, provider_path) { write_json({ zapret2_installed: bool_arg(installed), zapret2_package_installed: bool_arg(package_installed), zapret2_provider_path: as_string(provider_path) }); } let mode = ARGV[0] || ""; if (mode == "byedpi-validation") byedpi_validation_result(ARGV[1], ARGV[2], ARGV[3]); else if (mode == "stdin-first-nonempty-field") stdin_first_nonempty_field(); else if (mode == "stdin-first-line-version-field") stdin_first_line_version_field(); else if (mode == "byedpi-restart-count-file") byedpi_restart_count_file(ARGV[1]); else if (mode == "has-socks-outbound") exit(has_socks_outbound(ARGV[1], ARGV[2], ARGV[3], ARGV[4]) ? 0 : 1); else if (mode == "has-direct-mark-outbound") exit(has_direct_mark_outbound(ARGV[1], ARGV[2], ARGV[3]) ? 0 : 1); else if (mode == "has-route-rule") exit(has_route_rule(ARGV[1], ARGV[2], ARGV[3]) ? 0 : 1); else if (mode == "inbound-summary") tagged_runtime_summary(ARGV[1], "inbounds", ARGV[2]); else if (mode == "endpoint-summary") tagged_runtime_summary(ARGV[1], "endpoints", ARGV[2]); else if (mode == "has-route-rule-for-inbound") exit(has_route_rule_for_inbound(ARGV[1], ARGV[2]) ? 0 : 1); else if (mode == "byedpi-status") byedpi_status(slice(ARGV, 1)); else if (mode == "byedpi-check") byedpi_check(ARGV[1], ARGV[2], ARGV[3]); else if (mode == "zapret-status") zapret_status(slice(ARGV, 1)); else if (mode == "zapret-check") zapret_check(ARGV[1], ARGV[2], ARGV[3]); else if (mode == "zapret2-status") zapret2_status(slice(ARGV, 1)); else if (mode == "zapret2-check") zapret2_check(ARGV[1], ARGV[2], ARGV[3]); else { warn("Usage: providers/status.uc ...\n"); exit(1); }