fix(cron): make daemon liveness checks precise so cron:servers actually revives them

Two false positives/negatives in process detection kept the panel's
self-healing loop dead on any real installation:

- isNginxRunning() looked for "nginx: master" only among xc_vm-owned
  processes, but the master runs as root on typical installs (workers run
  as xc_vm), so cron:servers/cron:streams bailed out with "XC_VM not
  running..." before reaching the daemon revival block. Now scans
  /proc/*/cmdline user-agnostically, same as RootSignalsCronJob.

- The per-daemon "is it alive" checks piped ps through grep by bare words:
  every live-stream ffmpeg carries -thread_queue_size/-max_muxing_queue_size
  in its command line, so the "queue" check matched any running stream and
  the encode queue daemon was never started while at least one channel was
  up — created channels sat at "0% DONE" until console.php queue was run by
  hand. All seven checks (signals, cache_handler, network, watchdog, queue,
  ondemand, scanner) now use ProcessManager::findProcessPIDs(), a /proc
  cmdline scan matching the daemon title (XC_VM[...]) or its exact
  console.php invocation. Kill branches use the same PID list — the old
  bare "ondemand"/"scanner" greps could kill an innocent ffmpeg whose
  source URL contained those words.
This commit is contained in:
Divarion-D
2026-07-10 19:36:15 +03:00
parent 603ee3c8b6
commit b33e944cfd
2 changed files with 79 additions and 37 deletions
+56 -5
View File
@@ -493,20 +493,71 @@ class ProcessManager {
}
/**
* Check if nginx master process is running under xc_vm user
* Check if an nginx master process is running.
*
* Replaces CoreUtilities::isRunning().
*
* The master is owned by root on typical installs (workers run as
* xc_vm), so the scan must not be restricted to the xc_vm user —
* a false negative here makes cron:servers/cron:streams bail out
* every run and no daemon ever gets revived.
*
* @return bool
*/
public static function isNginxRunning() {
$rOutput = [];
@exec('pgrep -u xc_vm -a 2>/dev/null', $rOutput);
foreach ($rOutput as $rProcess) {
if (preg_match('/nginx:\s+master/', $rProcess)) {
foreach (glob('/proc/*/cmdline') ?: [] as $rCmdFile) {
$rRaw = @file_get_contents($rCmdFile);
if ($rRaw && strpos(str_replace("\0", ' ', $rRaw), 'nginx: master') !== false) {
return true;
}
}
return false;
}
/**
* Find PIDs of processes whose command line contains one of the given
* substrings. Reads /proc directly instead of ps|grep pipelines, which
* match unrelated processes (e.g. ffmpeg's -thread_queue_size satisfied
* the "queue" daemon check, so the encode queue was never revived).
*
* @param array $rTerms Cmdline substrings to match (exact, case-sensitive)
* @param int $rLimit Stop after this many matches (0 = no limit)
* @return array<int> Matching PIDs (own PID excluded)
*/
public static function findProcessPIDs(array $rTerms, $rLimit = 0) {
$rPIDs = array();
$rSelf = getmypid();
foreach (glob('/proc/[0-9]*/cmdline') ?: [] as $rCmdFile) {
$rPID = intval(basename(dirname($rCmdFile)));
if ($rPID == $rSelf) {
continue;
}
$rRaw = @file_get_contents($rCmdFile);
if (!$rRaw) {
continue;
}
$rCmd = str_replace("\0", ' ', $rRaw);
foreach ($rTerms as $rTerm) {
if (strpos($rCmd, $rTerm) !== false) {
$rPIDs[] = $rPID;
if ($rLimit > 0 && count($rPIDs) >= $rLimit) {
return $rPIDs;
}
break;
}
}
}
return $rPIDs;
}
/**
* Check whether any process (any user) matches one of the given
* cmdline substrings.
*
* @param array $rTerms Cmdline substrings to match
* @return bool
*/
public static function isAnyProcessRunning(array $rTerms) {
return count(self::findProcessPIDs($rTerms, 1)) > 0;
}
}