directoryList->getPath(DirectoryList::VAR_DIR) . '/magesail.pid'; } public function getKeyFilePath(): string { return $this->directoryList->getPath(DirectoryList::VAR_DIR) . '/magesail.key'; } public function getLogFilePath(): string { return $this->directoryList->getPath(DirectoryList::VAR_DIR) . '/log/magesail_tunnel.log'; } /** * @return array{running: bool, pid: int|null, key: string|null} */ public function getStatus(): array { $pidFile = $this->getPidFilePath(); $keyFile = $this->getKeyFilePath(); if (!\is_file($pidFile)) { return ['running' => false, 'pid' => null, 'key' => null]; } $pid = (int) trim((string) file_get_contents($pidFile)); if ($pid <= 0) { return ['running' => false, 'pid' => null, 'key' => null]; } if (!$this->isProcessAlive($pid)) { @unlink($pidFile); $kf = $this->getKeyFilePath(); if (\is_file($kf)) { @unlink($kf); } return ['running' => false, 'pid' => null, 'key' => null]; } $key = null; if (\is_file($keyFile)) { $key = trim((string) file_get_contents($keyFile)); if ($key === '') { $key = null; } } return ['running' => true, 'pid' => $pid, 'key' => $key]; } public function isRunning(): bool { return $this->getStatus()['running']; } public function isProcessAlive(int $pid): bool { if ($pid <= 0) { return false; } try { $this->shell->execute("ps -p %s > /dev/null 2>&1", [$pid]); return true; } catch (\Exception $e) { return false; } } /** * If PID file exists but process is dead, remove pid/key files. */ public function cleanupStaleState(): void { $pidFile = $this->getPidFilePath(); if (!\is_file($pidFile)) { return; } $pid = (int) trim((string) file_get_contents($pidFile)); if ($pid <= 0 || !$this->isProcessAlive($pid)) { @unlink($pidFile); $keyFile = $this->getKeyFilePath(); if (\is_file($keyFile)) { @unlink($keyFile); } } } }