= $timeout) { // Stale — kill and take over if ($pid > 0) { posix_kill($pid, 9); } } else { // Still fresh — another instance is running exit('Running...'); } } } // Write our PID $lockDir = dirname($lockFile); if (!is_dir($lockDir)) { @mkdir($lockDir, 0775, true); } file_put_contents($lockFile, getmypid()); return true; } /** * Release a cron lock * * @param string $lockFile Path to PID lock file */ public static function releaseCronLock($lockFile) { if (file_exists($lockFile)) { $pid = (int)trim(file_get_contents($lockFile)); // Only remove if it's our lock if ($pid === getmypid()) { unlink($lockFile); } } } // ─────────────────────────────────────────────────────────── // Internal Helpers // ─────────────────────────────────────────────────────────── /** * Check if /proc/PID exists with caching * * @param int $pid * @return bool */ protected static function procExists($pid) { $now = microtime(true); $key = (int)$pid; if (isset(self::$procCache[$key]) && ($now - self::$procCache[$key]['time']) < self::$cacheTtl) { return self::$procCache[$key]['exists']; } $exists = file_exists('/proc/' . $pid); self::$procCache[$key] = ['exists' => $exists, 'time' => $now]; return $exists; } /** * Clear the proc cache * * Useful before critical checks where stale cache could be dangerous. */ public static function clearCache() { self::$procCache = []; } // ─────────────────────────────────────────────────────────── // Streaming-specific Process Methods // Extracted from StreamingUtilities // ─────────────────────────────────────────────────────────── /** * Check if a stream process is alive (simplified cmdline search). * * Extracted from ProcessManager::isStreamAlive(). * Searches for $streamID anywhere in /proc/PID/cmdline (case-insensitive). * * @param int $pid Process ID * @param int|string $streamID Stream identifier to search for * @return bool */ public static function isStreamAlive($pid, $streamID) { $pid = (int)$pid; if ($pid <= 1) { return false; } if (!self::procExists($pid)) { return false; } if (!is_link('/proc/' . $pid . '/exe')) { return false; } static $cache = []; $cacheKey = $pid . '|' . $streamID; if (isset($cache[$cacheKey]) && $cache[$cacheKey]['time'] > time() - 4) { return $cache[$cacheKey]['alive']; } $cmd = @file_get_contents('/proc/' . $pid . '/cmdline'); if ($cmd === false) { $alive = false; } else { $cmd = str_replace("\0", ' ', $cmd); $alive = stripos($cmd, $streamID) !== false; } $cache[$cacheKey] = ['alive' => $alive, 'time' => time()]; return $alive; } /** * Check if a monitor/proxy process is running. * * Extracted from ProcessManager::isMonitorAlive(). * Checks for XC_VM[streamID] OR XC_VMProxy[streamID] in cmdline. * * @param int $pid Process ID * @param int|string $streamID Stream identifier * @param string|null $exe Expected executable (default: PHP_BIN) * @return bool */ public static function isMonitorAlive($pid, $streamID, $exe = null) { $pid = (int)$pid; if ($pid <= 0) { return false; } if ($exe === null && defined('PHP_BIN')) { $exe = PHP_BIN; } if (!self::procExists($pid)) { return false; } if (!$exe || !is_readable('/proc/' . $pid . '/exe')) { return false; } if (strpos(basename(@readlink('/proc/' . $pid . '/exe')), basename($exe)) !== 0) { return false; } $cmdline = trim(@file_get_contents('/proc/' . $pid . '/cmdline')); return ($cmdline == 'XC_VM[' . $streamID . ']' || $cmdline == 'XC_VMProxy[' . $streamID . ']'); } /** * Start a stream monitor process in background. * * Extracted from ProcessManager::startMonitor(). * * @param int $streamID * @param int $restart * @return bool */ public static function startMonitor($streamID, $restart = 0) { shell_exec(PHP_BIN . ' ' . CLI_PATH . 'monitor.php ' . intval($streamID) . ' ' . intval($restart) . ' >/dev/null 2>/dev/null &'); return true; } /** * Start a proxy process in background. * * Extracted from ProcessManager::startProxy(). * * @param int $streamID * @return bool */ public static function startProxy($streamID) { shell_exec(PHP_BIN . ' ' . CLI_PATH . 'proxy.php ' . intval($streamID) . ' >/dev/null 2>/dev/null &'); return true; } // ─────────────────────────────────────────────────────────── // Utility // ─────────────────────────────────────────────────────────── /** * Count running processes matching a pattern * * @param string $pattern grep pattern * @return int */ public static function countProcesses($pattern) { $pattern = escapeshellarg($pattern); return (int)trim(shell_exec("ps ax | grep -v grep | grep -c {$pattern}")); } /** * Check if nginx master process is running under xc_vm user * * Replaces CoreUtilities::isRunning(). * * @return bool */ public static function isNginxRunning() { $rNginx = 0; exec('ps -fp $(pgrep -u xc_vm)', $rOutput, $rReturnVar); foreach ($rOutput as $rProcess) { $rSplit = explode(' ', preg_replace('!\\s+!', ' ', trim($rProcess))); if (isset($rSplit[8]) && $rSplit[8] == 'nginx:' && isset($rSplit[9]) && $rSplit[9] == 'master') { $rNginx++; } } return 0 < $rNginx; } }