servers = config('servers.server.ports'); } /** * @return bool * @throws */ public function isRunner(): bool { if ($this->getPidFilePid() !== null || $this->getNamedServerPids() !== []) { return true; } $ports = $this->sortService($this->servers); foreach ($ports as $config) { if (checkPortIsAlready($config->port)) { return true; } } return false; } /** * @param $port * @throws */ public function exit($port): void { $pids = $this->collectStopPids((int)$port); if ($pids === []) { return; } $this->terminatePids($pids, SIGTERM, 30); $remaining = array_values(array_filter($pids, [$this, 'isProcessAlive'])); if ($remaining !== []) { $this->terminatePids($remaining, SIGKILL, 10); } if (!checkPortIsAlready((int)$port)) { @unlink(storage('.swoole.pid')); } } private function collectStopPids(int $port): array { $pids = []; $pidFilePid = $this->getPidFilePid(); if ($pidFilePid !== null) { $pids[] = $pidFilePid; $pids = array_merge($pids, $this->getDescendantPids($pidFilePid)); } $pids = array_merge($pids, $this->getNamedServerPids()); $portPid = checkPortIsAlready($port); if ($portPid !== false) { $pids[] = (int)$portPid; $pids = array_merge($pids, $this->getDescendantPids((int)$portPid)); } $currentPid = getmypid(); $pids = array_values(array_unique(array_filter(array_map('intval', $pids), function (int $pid) use ($currentPid) { return $pid > 1 && $pid !== $currentPid; }))); rsort($pids); return $pids; } private function terminatePids(array $pids, int $signal, int $rounds): void { for ($i = 0; $i < $rounds; $i++) { $alive = false; foreach ($pids as $pid) { if (!$this->isProcessAlive($pid)) { continue; } $alive = true; @Process::kill($pid, $signal); } if (!$alive) { return; } usleep(100000); } } private function getPidFilePid(): ?int { $pidFile = storage('.swoole.pid'); if (!is_file($pidFile)) { return null; } $pid = (int)trim((string)file_get_contents($pidFile)); if ($pid <= 1 || !$this->isProcessAlive($pid)) { return null; } return $pid; } private function getDescendantPids(int $parentPid): array { $processes = $this->listProcesses(); $children = []; foreach ($processes as $process) { $children[(int)$process['ppid']][] = (int)$process['pid']; } $result = []; $stack = [$parentPid]; while ($stack !== []) { $pid = array_pop($stack); foreach ($children[$pid] ?? [] as $childPid) { $result[] = $childPid; $stack[] = $childPid; } } return $result; } private function getNamedServerPids(): array { $siteId = '[' . config('site.id', 'system-service') . ']'; $pids = []; foreach ($this->listProcesses() as $process) { $command = $process['command']; if (!str_contains($command, $siteId)) { continue; } if (!preg_match('/\\]\\..+\\[[0-9]+\\]/', $command)) { continue; } $pids[] = (int)$process['pid']; } return $pids; } private function listProcesses(): array { $lines = []; exec('ps -eo pid=,ppid=,args=', $lines); $processes = []; foreach ($lines as $line) { $line = trim($line); if ($line === '') { continue; } $parts = preg_split('/\s+/', $line, 3); if (count($parts) < 3) { continue; } $processes[] = [ 'pid' => (int)$parts[0], 'ppid' => (int)$parts[1], 'command' => $parts[2], ]; } return $processes; } private function isProcessAlive(int $pid): bool { return $pid > 1 && @Process::kill($pid, 0); } }