fix(service): prevent recursive flock deadlock and crontab wipe; increase component RPC timeout
- initd.uc: retry_start_on_wan_up now calls BIN_PATH directly instead of SERVICE_INIT to avoid the rc.common flock-1000 self-deadlock that was hanging Backend CI for the full 1-hour runner timeout - initd.uc: schedule_start_retry uses background_command() so inherited fd 1000 (procd lock) is closed before the sleep starts - updates.uc, parental_quota.uc: read /etc/crontabs/root via fs.readfile instead of crontab -l — BusyBox exits 1 on an empty file causing command_output_from_args to return '' and wipe all user cron entries - shell/index.ts: COMPONENT_ACTION_RPC_TIMEOUT_MS 10s -> 45s to prevent false RPC timeout errors during opkg installs on slow routers
This commit is contained in:
@@ -11,7 +11,7 @@ const SERVICE_ACTION_TIMEOUT_MS = 2 * 60 * 1000;
|
||||
const SERVICE_ACTION_POLL_INTERVAL_MS = 1000;
|
||||
const LATENCY_TEST_TIMEOUT_MS = 30 * 1000;
|
||||
const LATENCY_TEST_POLL_INTERVAL_MS = 1000;
|
||||
const COMPONENT_ACTION_RPC_TIMEOUT_MS = 10000;
|
||||
const COMPONENT_ACTION_RPC_TIMEOUT_MS = 45000;
|
||||
const COMPONENT_ACTION_POLL_INTERVAL_MS = 1000;
|
||||
const COMPONENT_ACTION_SELF_UPDATE_SETTLE_MS = 2000;
|
||||
const COMPONENT_ACTION_TRANSIENT_RPC_GRACE_MS = 30000;
|
||||
|
||||
@@ -3157,7 +3157,7 @@ var SERVICE_ACTION_TIMEOUT_MS = 2 * 60 * 1e3;
|
||||
var SERVICE_ACTION_POLL_INTERVAL_MS = 1e3;
|
||||
var LATENCY_TEST_TIMEOUT_MS = 30 * 1e3;
|
||||
var LATENCY_TEST_POLL_INTERVAL_MS = 1e3;
|
||||
var COMPONENT_ACTION_RPC_TIMEOUT_MS = 1e4;
|
||||
var COMPONENT_ACTION_RPC_TIMEOUT_MS = 45e3;
|
||||
var COMPONENT_ACTION_POLL_INTERVAL_MS = 1e3;
|
||||
var COMPONENT_ACTION_SELF_UPDATE_SETTLE_MS = 2e3;
|
||||
var COMPONENT_ACTION_TRANSIENT_RPC_GRACE_MS = 3e4;
|
||||
|
||||
@@ -923,7 +923,11 @@ function log_cron_apply_result(result) {
|
||||
}
|
||||
|
||||
function remove_cron_jobs(list_marker, subscription_marker, component_marker) {
|
||||
let crontab = command_output_from_args([ "crontab", "-l" ]);
|
||||
// BusyBox `crontab -l` exits with code 1 when the crontab is empty or
|
||||
// absent; command_output_from_args returns "" in that case and a
|
||||
// subsequent write would wipe all user cron lines. Read the file
|
||||
// directly to avoid this.
|
||||
let crontab = as_string(fs.readfile("/etc/crontabs/root") || "");
|
||||
let markers = [ list_marker, subscription_marker, component_marker ];
|
||||
let result = {
|
||||
crontab: strip_unmarked_tachyon_cron_lines(
|
||||
@@ -938,10 +942,12 @@ function remove_cron_jobs(list_marker, subscription_marker, component_marker) {
|
||||
}
|
||||
|
||||
function refresh_cron_from_sources(settings, sections, bin, list_marker, subscription_marker, component_marker) {
|
||||
// Same as remove_cron_jobs: read /etc/crontabs/root directly to avoid
|
||||
// BusyBox's `crontab -l` exit-code-1 on an empty or absent file.
|
||||
let result = cron_refresh_apply_result(
|
||||
settings,
|
||||
sections,
|
||||
command_output_from_args([ "crontab", "-l" ]),
|
||||
as_string(fs.readfile("/etc/crontabs/root") || ""),
|
||||
bin,
|
||||
list_marker,
|
||||
subscription_marker,
|
||||
|
||||
@@ -284,10 +284,19 @@ function schedule_start_retry(path, delay_seconds) {
|
||||
if (!ensure_parent_dir(path))
|
||||
return false;
|
||||
|
||||
// Build the retry worker. The inner command sleeps, removes the pid file,
|
||||
// then runs the retry action. We wrap everything in background_command() so
|
||||
// that all inherited descriptors (in particular procd's flock fd 1000) are
|
||||
// closed before the sleep starts — without that the background subshell
|
||||
// would pin the lock file and prevent any subsequent service operation that
|
||||
// also tries to acquire flock 1000.
|
||||
let worker = command_from_args([ "sleep", delay_seconds ]) +
|
||||
"; " + command_from_args([ "rm", "-f", path ]) +
|
||||
"; exec " + command_from_args([ SERVICE_INIT, "retry_start_on_wan_up" ]);
|
||||
let result = command_capture(command_from_args([ "sh", "-c", worker ]) + " >/dev/null 2>&1 & echo $!");
|
||||
"; " + command_from_args([ SERVICE_INIT, "retry_start_on_wan_up" ]);
|
||||
let bg = background_command(worker);
|
||||
// background_command() already appends " &"; we need $! so run it inside a
|
||||
// subshell that prints the pid back to us.
|
||||
let result = command_capture("{ " + bg + " } ; echo $!");
|
||||
let pid = trim(result.output);
|
||||
if (result.status != 0 || !numeric_text(pid))
|
||||
return false;
|
||||
@@ -494,7 +503,11 @@ function retry_start_on_wan_up(owner_pid) {
|
||||
return 0;
|
||||
|
||||
command_success_from_args([ "logger", "-t", SERVICE_NAME, "[info] Retrying failed Tachyon start after WAN came up" ]);
|
||||
return command_status_from_args([ SERVICE_INIT, "restart", "triggered" ]);
|
||||
// Do NOT call SERVICE_INIT here: rc.common holds flock 1000 for the whole
|
||||
// duration of the init.d script, so a nested /etc/init.d/tachyon call would
|
||||
// block forever waiting for that same lock (recursive flock self-deadlock).
|
||||
// Call BIN_PATH directly, the same way start_service and stop_service do.
|
||||
return command_status_from_args([ BIN_PATH, "restart" ]);
|
||||
}
|
||||
|
||||
function active_service_action_value() {
|
||||
|
||||
@@ -347,7 +347,11 @@ function sync_enforcement(blocked_macs, blocked_ips) {
|
||||
}
|
||||
|
||||
function crontab_lines() {
|
||||
return split(command_output_from_args([ "crontab", "-l" ]), "\n");
|
||||
// BusyBox's `crontab -l` exits with code 1 when the file is empty or
|
||||
// absent, making command_output_from_args() return "" and causing a
|
||||
// subsequent crontab write to discard every user cron line. Read the
|
||||
// file directly instead; null (no file yet) is treated as empty string.
|
||||
return split(as_string(fs.readfile("/etc/crontabs/root") || ""), "\n");
|
||||
}
|
||||
|
||||
function cron_line() {
|
||||
|
||||
Reference in New Issue
Block a user