💋 Sync 2026-10-11 00:16:14
Merge-upstream / merge (push) Canceled after 0s

This commit is contained in:
github-actions[bot] committed 2026-10-11 00:16:14 +08:00
1 parent c7bafc635d
commit c71da84035
264 files changed
+8169 -3944

No files matched your search

@@ -8,7 +8,6 @@ let status = require('status');
let config = require('config');
let refresh = require('refresh');
let rules = require('rules');
let license = require('license');
let statistics = require('statistics');
return {
@@ -66,17 +65,7 @@ return {
refresh: true
},
call: rules.delete
},
license_get: {
call: license.get
},
license_update: {
args: {
license_key: ''
},
call: license.update
}
}
};
@@ -12,7 +12,6 @@ let reload_uci = core.reload_uci;
let cfg = core.cfg;
let to_bool = core.to_bool;
let to_int = core.to_int;
let mask_secret = core.mask_secret;
let api_error = core.api_error;
let uci_commit_option = core.uci_commit_option;
let bool_uci = core.bool_uci;
@@ -46,10 +45,6 @@ function build_config() {
version: CONFIG_SCHEMA_VERSION
},
values: values,
license: {
configured: !!cfg('license_key', ''),
key_masked: mask_secret(cfg('license_key', ''))
},
device: {
vendor: cfg('device_vendor', ''),
model: cfg('device_model', ''),
@@ -67,9 +62,6 @@ function validate_config_value(name, value) {
return api_error('dedicated_method_required', 'Use set_enabled to change enabled state', name);
}
if (name == 'license_key') {
return api_error('dedicated_method_required', 'Use license_update to change the license key', name);
}
return api_error('unknown_option', 'Unsupported SafeShield configuration option', name);
}
@@ -149,20 +149,6 @@ function identity_cfg(name, def) {
return (v == null) ? def : v;
}
function mask_secret(v) {
let s = sprintf('%s', v || '');
let n = length(s);
if (n == 0) {
return '';
}
if (n <= 8) {
return '********';
}
return sprintf('%s...%s', substr(s, 0, 4), substr(s, n - 4));
}
function api_error(code, message, field) {
let error = {
@@ -242,7 +228,6 @@ return {
reload_uci: reload_uci,
cfg: cfg,
identity_cfg: identity_cfg,
mask_secret: mask_secret,
api_error: api_error,
uci_commit_option: uci_commit_option,
uci_delete_option: uci_delete_option,
@@ -1,90 +0,0 @@
'use strict';
let core = require('core');
let runtime = require('runtime');
let reload_uci = core.reload_uci;
let cfg = core.cfg;
let trim = core.trim;
let mask_secret = core.mask_secret;
let api_error = core.api_error;
let uci_commit_option = core.uci_commit_option;
let uci_delete_option = core.uci_delete_option;
let start_refresh_async = runtime.start_refresh_async;
let reset_statistics_upload_state = runtime.reset_statistics_upload_state;
function license_get_call() {
reload_uci();
let key = cfg('license_key', '');
return {
ok: true,
license: {
configured: !!key,
key: key
}
};
}
function license_update_call(request) {
reload_uci();
if (request.args.license_key == null) {
return api_error('missing_argument', 'license_key is required; use an empty string to clear it', 'license_key');
}
let key = trim(request.args.license_key);
if (length(key) > 512 || match(key, /[\r\n]/)) {
return api_error('invalid_license_key', 'license_key must be at most 512 characters and contain no line breaks', 'license_key');
}
let current = cfg('license_key', '');
if (current == key) {
return {
ok: true,
changed: false,
license: {
configured: !!key,
key_masked: mask_secret(key)
},
refresh: {
requested: false,
accepted: false,
reason: 'unchanged'
}
};
}
let saved = key == ''
? uci_delete_option('license_key')
: uci_commit_option('license_key', key);
if (!saved) {
return api_error('uci_commit_failed', 'Failed to update the SafeShield license key');
}
reset_statistics_upload_state();
let refresh = start_refresh_async();
return {
ok: true,
changed: true,
license: {
configured: !!key,
key_masked: mask_secret(key)
},
refresh: {
requested: true,
accepted: refresh.accepted,
reason: refresh.reason || ''
}
};
}
return {
get: license_get_call,
update: license_update_call
};
@@ -2,7 +2,6 @@
let core = require('core');
let fs = require('fs');
let ubus = require('ubus').connect();
let PKG_NAME = core.PKG_NAME;
let STATUS_FILE = core.STATUS_FILE;
@@ -12,43 +11,72 @@ let to_bool = core.to_bool;
let reload_uci = core.reload_uci;
let cfg = core.cfg;
function service_running(name) {
let r = ubus.call('service', 'list', { name: name });
if (!r || !r[name] || !r[name].instances) {
return false;
// Do not call ubus synchronously from an rpcd handler. In particular,
// service.list may require the same event loop currently serving the caller.
// Use an independent ubus client with its built-in -t timeout (seconds).
// BusyBox/OpenWrt installations do not necessarily provide coreutils timeout.
// Only successful procd lookups are briefly reused. A failed lookup must not
// poison subsequent requests or be mistaken for a stopped service.
let service_cache = {};
function service_status(name, use_cache) {
if (name != PKG_NAME && name != 'dnsmasq') {
return { state: 'unknown', running: false, lookup_ok: false };
}
for (let inst_name, inst in r[name].instances) {
if (inst.running) {
return true;
let now = time();
let cached = service_cache[name];
if (use_cache && cached && cached.at == now) {
return cached.value;
}
let pipe = fs.popen(sprintf("ubus -t 2 call service list '{\"name\":\"%s\"}' 2>/dev/null", name), 'r');
if (!pipe) {
return { state: 'unknown', running: false, lookup_ok: false };
}
let output = pipe.read('all');
pipe.close();
if (!output) {
return { state: 'unknown', running: false, lookup_ok: false };
}
let result = json(output);
// procd returns an empty object when the named service is absent.
// That is a valid stopped response, not a transport failure.
if (type(result) != 'object') {
return { state: 'unknown', running: false, lookup_ok: false };
}
let instances = result[name] && result[name].instances;
let running = false;
if (type(instances) == 'object') {
for (let instance_name, instance in instances) {
if (instance && instance.running) {
running = true;
break;
}
}
}
return false;
let value = { state: running ? 'running' : 'stopped', running: running, lookup_ok: true, instances: instances || {} };
if (use_cache) {
service_cache[name] = { at: now, value: value };
}
return value;
}
function service_running(name) {
return service_status(name, false).running;
}
function service_instance_running(name, instance_name) {
let r = ubus.call('service', 'list', { name: name });
if (!r || !r[name] || !r[name].instances || !r[name].instances[instance_name]) {
return false;
}
return !!r[name].instances[instance_name].running;
let state = service_status(name, false);
return !!(state.instances && state.instances[instance_name] && state.instances[instance_name].running);
}
function dnsmasq_running() {
let r = ubus.call('service', 'list', { name: 'dnsmasq' });
if (!r || !r.dnsmasq || !r.dnsmasq.instances) {
return false;
}
for (let inst_name, inst in r.dnsmasq.instances) {
if (inst.running) {
return true;
}
}
return false;
return service_running('dnsmasq');
}
function run_service_action(action, timeout_ms) {
@@ -85,10 +113,11 @@ function start_refresh_async() {
};
}
if (!service_running(PKG_NAME)) {
let service = service_status(PKG_NAME, false);
if (!service.running) {
return {
accepted: false,
reason: 'service_stopped'
reason: service.lookup_ok ? 'service_stopped' : 'service_unavailable'
};
}
@@ -129,10 +158,11 @@ function start_local_apply_async() {
};
}
if (!service_running(PKG_NAME)) {
let service = service_status(PKG_NAME, false);
if (!service.running) {
return {
accepted: false,
reason: 'service_stopped'
reason: service.lookup_ok ? 'service_stopped' : 'service_unavailable'
};
}
@@ -155,6 +185,7 @@ function start_local_apply_async() {
return {
service_running: service_running,
service_status: service_status,
service_instance_running: service_instance_running,
dnsmasq_running: dnsmasq_running,
run_service_action: run_service_action,
@@ -91,7 +91,8 @@ function build_statistics() {
let snapshot_interval_s = to_int(cfg('statistics_snapshot_interval_s', '60'), 60);
let data = read_json_file(STATISTICS_FILE, {});
let totals = (type(data.totals) == 'object') ? data.totals : {};
let collector_running = service_instance_running(PKG_NAME, 'statistics');
let collector_service = runtime.service_status(PKG_NAME, true);
let collector_running = !!(collector_service.instances && collector_service.instances.statistics && collector_service.instances.statistics.running);
let effective_enabled = enabled && collector_running;
let source = sanitize_source(data.source);
if (!effective_enabled) {
@@ -106,6 +107,8 @@ function build_statistics() {
effective_enabled: effective_enabled,
available: !!data.schema,
collector_running: collector_running,
collector_state: collector_service.lookup_ok ? (collector_running ? 'running' : 'stopped') : 'unknown',
collector_lookup_ok: collector_service.lookup_ok,
volatile: to_bool(data.volatile, true),
storage: sprintf('%s', data.storage || 'tmpfs'),
persistent: to_bool(data.persistent, false),
@@ -20,7 +20,6 @@ let file_size_kb = core.file_size_kb;
let to_bool = core.to_bool;
let to_optional_bool = core.to_optional_bool;
let to_int = core.to_int;
let mask_secret = core.mask_secret;
let service_running = runtime.service_running;
let dnsmasq_running = runtime.dnsmasq_running;
@@ -134,8 +133,6 @@ function build_status() {
let refresh_interval_s = to_int(cfg('refresh_interval_s', '28800'), 28800);
let boot_start_delay_s = to_int(cfg('boot_start_delay_s', '30'), 30);
let license_key = cfg('license_key', '');
let license_configured = !!license_key;
let apply_local_overrides = to_bool(cfg('apply_local_overrides', '1'), true);
let cfg_physical_fingerprint = identity_cfg('physical_fingerprint', '');
@@ -186,8 +183,8 @@ function build_status() {
let artifact_block_source_count = to_int(data.artifact_block_source_count || (artifact_download_url_present ? 1 : 0), 0);
let artifact_allow_source_count = to_int(data.artifact_allow_source_count || 0, 0);
let license_plan = data.license_plan || '';
let license_status = data.license_status || '';
let entitlement_plan = data.entitlement_plan || '';
let entitlement_status = data.entitlement_status || '';
let physical_fingerprint = data.physical_fingerprint || cfg_physical_fingerprint || '';
let fingerprint_version = to_int(data.fingerprint_version || cfg_fingerprint_version || 1, 1);
let identity_provider = data.identity_provider || cfg_identity_provider || '';
@@ -217,8 +214,10 @@ function build_status() {
let hub_source = build_api_source(data, enabled);
let installed = blocklist_installed || file_exists(BLOCKLIST_FILE);
let refreshd_running = service_running(PKG_NAME);
let dns_running = dnsmasq_running();
let refresh_service = runtime.service_status(PKG_NAME, true);
let dns_service = runtime.service_status('dnsmasq', true);
let refreshd_running = refresh_service.running;
let dns_running = dns_service.running;
let generated_at = time();
let next_refresh_in_s = 0;
@@ -245,7 +244,11 @@ function build_status() {
runtime: {
refreshd_running: refreshd_running,
refreshd_state: refresh_service.state,
refreshd_lookup_ok: refresh_service.lookup_ok,
dnsmasq_running: dns_running,
dnsmasq_state: dns_service.state,
dnsmasq_lookup_ok: dns_service.lookup_ok,
dnsmasq_version: dnsmasq_version,
dnsmasq_min_version: dnsmasq_min_version,
dns_runtime_ok: health_dns_runtime || dns_running,
@@ -255,11 +258,9 @@ function build_status() {
last_result: last_result,
last_error_code: last_error_code
},
license: {
configured: license_configured,
key_masked: mask_secret(license_key),
plan: license_plan,
status: license_status
entitlement: {
plan: entitlement_plan,
status: entitlement_status
},
device: {